头像 堆叠 效果

//自定义类

public class DiscussionAvatarView extends ViewGroup {
    /**
     * 头像的半径
     */
    private int mRadius;
    /**
     * 头像间的距离
     */
    private float mSpace;
    private Context mContext;
    private LayoutInflater mInflater;
    /**
     * 是否最后一个显示完全
     */
    private boolean mIsLastComplete;
    /**
     * 最大头像数目
     */
    private int mMaxCount;
    /**
     * 当前移动的偏移量
     */
    private int mCurrentOffset;
    /**
     * 移动的属性动画
     */
    private ValueAnimator animator;
    /**
     * 是否显示动画效果
     */
    private boolean mIsShowAnimation;
    /**
     * 监听
     */
    private DiscussionAvatarListener listener;
    private boolean mIsShowFrame;
    private int mFrameColor;

    public DiscussionAvatarView(Context context) {
        this(context, null);
    }

    public DiscussionAvatarView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DiscussionAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs);
    }

    private void initView(Context context, AttributeSet attrs) {
        this.mContext = context;
        this.mInflater = LayoutInflater.from(mContext);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DiscussionAvatarView);
        if (array != null) {
            int radius = array.getInteger(R.styleable.DiscussionAvatarView_radius, 13);
            mSpace = array.getFloat(R.styleable.DiscussionAvatarView_space, (float) 0.5);
            mMaxCount = array.getInteger(R.styleable.DiscussionAvatarView_maxCount, 6);
            mIsLastComplete = array.getBoolean(R.styleable.DiscussionAvatarView_isLastComplete, true);
            mIsShowAnimation = array.getBoolean(R.styleable.DiscussionAvatarView_isShowAnimation, true);
            mIsShowFrame = array.getBoolean(R.styleable.DiscussionAvatarView_isShowFrame, true);
            mFrameColor = array.getColor(R.styleable.DiscussionAvatarView_frameColor, 0);

            mRadius = CommonUtils.dip2px(context, radius);

            array.recycle();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int heiMeasure = MeasureSpec.getSize(heightMeasureSpec);
        int heiMode = MeasureSpec.getMode(heightMeasureSpec);
        int widMode = MeasureSpec.getMode(widthMeasureSpec);
        int widMeasure = MeasureSpec.getSize(widthMeasureSpec);

        int wid = 0;
        int hei = 0;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            LayoutParams lp = child.getLayoutParams();
            lp.width = 2 * mRadius;
            lp.height = lp.width;
            child.setLayoutParams(lp);
            // 测量子View的宽和高,系统提供的measureChild
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            // 子View占据的宽度
            int childWidth = child.getMeasuredWidth();
            // 子View占据的高度
            int childHeight = child.getMeasuredHeight();

            if (i < mMaxCount) {
                if (i == 0) {
                    wid = wid + childWidth;
                } else {
                    wid = (int) (wid + childWidth * mSpace);
                }
            }
            hei = Math.max(hei, childHeight);
        }
        //如果是exactly使用测量宽和高,否则使用自己设置的宽和高
        setMeasuredDimension((widMode == MeasureSpec.EXACTLY) ? widMeasure : wid,
                (heiMode == MeasureSpec.EXACTLY) ? heiMeasure : hei);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();

        int left = -mCurrentOffset;
        int top = 0;
        int right = -mCurrentOffset;
        for (int i = 0; i < count; i++) {
            View child;
            if (mIsLastComplete) {
                child = getChildAt(i);
            } else {
                child = getChildAt(count - i - 1);
            }
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            if (i == 0) {
                right = right + childWidth;
            } else {
                right = (int) (right + childWidth * mSpace);
            }
            child.layout(left, top, right, childHeight);
            left = (int) (left + childWidth * mSpace);
        }

    }

    /**
     * 初始化数据
     *
     * @param list
     */
    public void initDatas(ArrayList<String> list) {
        if (list == null) {
            return;
        }
        removeAllViews();
        int size = list.size();
        mMaxCount = size;
        for (int i = 0; i < size; i++) {
            ImageView iv = (CircleImageView) mInflater.inflate(R.layout.item_discussion_avatar, this, false);
            if (mIsLastComplete) {
                Glide.with(mContext).load(list.get(i)).into(iv);
            } else {
                loadCircleBorder(mContext, list.get(size - i - 1), iv, getResources().getColor(R.color.white));

            }
            this.addView(iv);
        }
    }


    /**
     * 头像带边框的时候
     *
     * @param context
     * @param url
     * @param iv
     * @param color
     */
    private static void loadCircleBorder(Context context, String url, ImageView iv, int color) {
        Glide.with(context).load(url).
                diskCacheStrategy(DiskCacheStrategy.RESOURCE).
                transform(new GlideCircleTransformWithBorder(context, 1, color)).
                into(iv);
    }

    /**
     * 添加头像,没有动画监听
     *
     * @param ava
     */
    public void addData(String ava) {
        addData(ava, null);
    }

    /**
     * 添加一个头像,有动画监听
     *
     * @param ava
     */
    public void addData(String ava, DiscussionAvatarListener listener1) {
        this.listener = listener1;
        if (mMaxCount <= 0) {
            return;
        }
        if (TextUtils.isEmpty(ava)) {
            return;
        }
        if (animator != null) {
            animator.cancel();
        }
        int childCount = getChildCount();
        final ImageView iv = (ImageView) mInflater.inflate(R.layout.item_discussion_avatar, this, false);
        Glide.with(mContext).load(ava).into(iv);
        if (mIsLastComplete) {
            this.addView(iv);
        } else {
            this.addView(iv, 0);
        }

        if (childCount >= mMaxCount) {
            if (mIsShowAnimation) {
                int countAft = getChildCount();
                final View child;
                if (mIsLastComplete) {
                    child = getChildAt(0);
                } else {
                    child = getChildAt(countAft - 1);
                }
                int childWid = child.getMeasuredWidth();
                animator = ValueAnimator.ofInt(0, (int) (childWid * mSpace));
                animator.setDuration(1000);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        mCurrentOffset = (int) animation.getAnimatedValue();
                        //使左侧渐变消失,右侧渐变显示
                        long duration = animation.getDuration();
                        long currentPlayTime = animation.getCurrentPlayTime();
                        float v = (float) currentPlayTime / duration;
                        iv.setAlpha(v);
                        child.setAlpha(1 - v);
                        requestLayout();
                    }
                });

                animator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                        if (listener != null) {
                            listener.onAnimationStart();
                        }
                    }

                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        mCurrentOffset = 0;
                        int count = getChildCount();
                        for (int i = 0; i < count; i++) {
                            View child = getChildAt(i);
                            child.setAlpha(1);
                        }
                        if (mIsLastComplete) {
                            removeViewAt(0);
                        } else {
                            removeViewAt(count - 1);
                        }
                        if (listener != null) {
                            listener.onAnimationEnd();
                        }
                    }

                });
                animator.start();
            } else {
                mCurrentOffset = 0;
                if (mIsLastComplete) {
                    removeViewAt(0);
                } else {
                    int count = getChildCount();
                    removeViewAt(count - 1);
                }
            }
        }
    }

    /**
     * 设置最大头像数
     *
     * @param count
     */
    public void setMaxCount(int count) {
        this.mMaxCount = count;
        int childCount = getChildCount();
        if (childCount > mMaxCount) {
            for (int i = 0; i < childCount - mMaxCount; i++) {
                if (mIsLastComplete) {
                    removeViewAt(0);
                } else {
                    int currentCount = getChildCount();
                    removeViewAt(currentCount - 1);
                }
            }
        }
    }

}



//头像的边框类
public class GlideCircleTransformWithBorder extends BitmapTransformation {
    private Paint mBorderPaint;
    private float mBorderWidth;

    public GlideCircleTransformWithBorder(Context context) {
        super();
    }

    public GlideCircleTransformWithBorder(Context context, int borderWidth, int borderColor) {
        super();
        mBorderWidth = Resources.getSystem().getDisplayMetrics().density * borderWidth;

        mBorderPaint = new Paint();
        mBorderPaint.setDither(true);
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(borderColor);
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setStrokeWidth(mBorderWidth);
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return circleCrop(pool, toTransform);
    }

    private Bitmap circleCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;

        int size = (int) (Math.min(source.getWidth(), source.getHeight()) - (mBorderWidth) / 2);
        int x = (source.getWidth() - size) / 2;
        int y = (source.getHeight() - size) / 2;
        // TODO this could be acquired from the pool too
        Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);
        Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        }
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        float r = size / 2f;
        canvas.drawCircle(r, r, r, paint);
        if (mBorderPaint != null) {
            float borderRadius = r - mBorderWidth / 2;
            canvas.drawCircle(r, r, borderRadius, mBorderPaint);
        }
        return result;
    }

    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
    } //
//
//    @Override
//    public String getId() {
//        return getClass().getName();
//    }
}



//监听类
public interface DiscussionAvatarListener {
    void onAnimationStart();
    void onAnimationEnd();
}

//工具类方法

public static int dip2px(Context context, float dpValue) {
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dpValue * scale + 0.5f);
}

//头像的布局文件 

item_discussion_avatar
<?xml version="1.0" encoding="utf-8"?>
<com.benben.funsocial.view.CircleImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

//属性文件   attrs下

<!--头像的堆叠-->
<declare-styleable name="DiscussionAvatarView">
    <!--头像的半径,dp为单位-->
    <attr name="radius" format="reference|integer" />
    <!--头像间的距离,为头像直径的长度的百分比,dp为单位-->
    <attr name="space" format="reference|float" />
    <!--最多显示多少个头像-->
    <attr name="maxCount" format="reference|integer" />
    <!--是否最后一个显示完全,默认是true-->
    <attr name="isLastComplete" format="reference|boolean" />
    <!--是否显示动画效果-->
    <attr name="isShowAnimation" format="reference|boolean" />
    <!--是否显示边框-->
    <attr name="isShowFrame" format="reference|boolean" />
    <!--边框颜色-->
    <attr name="frameColor" format="reference|color" />
</declare-styleable>

//CircleImageView

public class CircleImageView extends AppCompatImageView {
    private float width;
    private float height;
    private float radius;
    private Paint paint;
    private Matrix matrix;


    public CircleImageView(Context context) {
        this(context, null);

    }

    public CircleImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);

    }




    public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        paint.setAntiAlias(true);//设置抗锯齿
        matrix = new Matrix(); //初始化缩放矩阵
    }

    /**
     * 测量控件的宽高,并获取其内切圆的半径
     */


    @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight(); radius = Math.min(width, height) / 2;
    }


    @Override 
    protected void onDraw(Canvas canvas) {
        Drawable drawable = getDrawable();
        if (drawable == null) {
        super.onDraw(canvas);
            return;
        }
        if (drawable instanceof BitmapDrawable) {
             paint.setShader(initBitmapShader((BitmapDrawable) drawable));//将着色器设置给画笔
            canvas.drawCircle(width / 2, height / 2, radius, paint);//使用画笔在画布上画圆
             return;
         }
        super.onDraw(canvas);
    }
    /**
     * 获取ImageView中资源图片的Bitmap,利用Bitmap初始化图片着色器,通过缩放矩阵将原资源图片缩放到铺满整个绘制区域,避免边界填充
     */


    private BitmapShader initBitmapShader(BitmapDrawable drawable) {
        Bitmap bitmap = drawable.getBitmap();
    BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    float scale = Math.max(width / bitmap.getWidth(), height / bitmap.getHeight());
    matrix.setScale(scale, scale);//将图片宽高等比例缩放,避免拉伸
    bitmapShader.setLocalMatrix(matrix);
    return bitmapShader; }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值