//自定义类
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; } }