上篇文章Android圆形图像的绘制(二)介绍了单人圆形头像的绘制,这篇文章也是圆形头像的最后一篇。多人头像存在的场景有很多,像一些社交软件,只要涉及到群聊的功能,基本上都会存在多人头像。下面介绍多人头像是怎样实现的,最多支持五人。

在进行多人头像绘制的过程中,CircleImageView自定义控件中应该有个列表保存多人头像的相关信息,每个人的信息应该封装为一个bean对象,上篇文章介绍圆形头像涉及到位图和随机图像的展示,所以我们bean对象包含的数据有位图、随机背景、文本,代码如下:

public class CircleImageViewBean {/*** 位图*/private Bitmap bitmap;/*** 随机背景*/private int randomBg;/*** 文本信息*/private String text;public CircleImageViewBean(Bitmap bitmap, int randomBg, String text) {this.bitmap = bitmap;this.randomBg = randomBg;this.text = text;}public int getRandomBg() {return randomBg;}public void setRandomBg(int randomBg) {this.randomBg = randomBg;}public String getText() {return text;}public void setText(String text) {this.text = text;}public Bitmap getBitmap() {return bitmap;}public void setBitmap(Bitmap bitmap) {this.bitmap = bitmap;}
}

在定义好bean对象后,CircleImageView自定义控件应该为外面提供一个公用的方法,方法如下:

    /*** 设置多人头像* @param circleImageViewBeanList*/public void setImageBitmaps(List<CircleImageViewBean> circleImageViewBeanList) {mCircleImageViewBeanList = circleImageViewBeanList;invalidate();}

接下来看下onDraw方法的实现,代码如下:

    private static final int MAX = 5;
    protected void onDraw(Canvas canvas) {//一定要注释掉,否则圆形图像没法生效
//        super.onDraw(canvas);if (mWidth > 0 && mHeight > 0) {
//            Bitmap bitmap = createCircleBitmapForSRC_IN(canvas);
//            if (bitmap != null) {
//                canvas.drawBitmap(bitmap, 0, 0, new Paint());
//            }if (mCircleImageViewBeanList != null && mCircleImageViewBeanList.size() > 0) {mCount = Math.min(mCircleImageViewBeanList.size(), MAX);//绘制多人头像createCircleBitmap(canvas);}}}

在绘制之前,需要获取当前多人头像为几人头像,最大值为5,状态用mCount变量保存。下面计算每个小圆相对于大圆的缩放比例,缩放比例的获取用到的是基本的三角函数的相关知识,代码如下:

    public float getScale(int count) {int angle = getAngle(count);if (angle == 360) {return 1f;}double cot = getCot(angle);float scale = (float) (1f / (Math.sqrt(1 + Math.pow(cot, 2)) + 1));return scale;}
    private static final int[] sAngleArray = {360, 90, 60, 45, 36};private int getAngle(int count) {return count > 0 && count <= sAngleArray.length ? sAngleArray[count - 1] : null;}
   private double getCot(int angle) {double radian = Math.toRadians(angle);double sin = Math.sin(radian);double cos = Math.cos(radian);return  cos / sin;}

下面获取与大圆最顶部相切的小圆的左上角位置,主要是为了方便计算其他小圆的左上角的位置,因为找到这个小圆的对应的位置是在旋转角度为零的位置,如果想得到其他小圆的位置,只需要根据旋转角度,运用三角函数知识就可以得到。最上面小圆左上角位置获取的代码如下:

    public float[] getTopPosition(float radius, float scale) {float x = radius * (1 - scale);float y = 0;return new float[] { x, y };}

然后,我们获取每个小圆左上角的位置,这里需要知道大圆半径、旋转角度、缩放比例、最上面小圆左上角的位置,旋转角度决定的小头像的摆放位置,它的值定义了一个数据,代码如下:

    private static final float[][] sRotationArray = { new float[] { 360 }, new float[] { 45, -135 },new float[] { 120, 0, -120 }, new float[] { 45, 135, -135, -45 },new float[] { 144, 72, 0, -72, -144 }};public float[] getRotation(int count) {return count > 0 && count <= sRotationArray.length ? sRotationArray[count - 1] : null;}  

小头像位置计算的代码如下:

  public float[] getTranslatePosition(float radius, float rotation, float scale, float topX, float topY) {float smallRadius = radius * (1 - scale);double radian = Math.toRadians(rotation);double cos = Math.cos(radian);double sin = Math.sin(radian);float x = (float) (topX - smallRadius * sin);float y = (float) (topY + smallRadius * (1 - cos));return new float[] { x, y };}

得到每个小圆左上角的位置后,需要对目标canvas进行相应的平移,为了能准备画出每个小圆展示在哪个位置,在进行变换之前,需要对目标canvas进行状态的保存,防止这里的变换操作,对其他小圆产生影响,代码如下:

    targetCanvas.save();float[] radianPosition = mPresenter.getTranslatePosition(viewSize / 2, rotation[i], scale, topPosition[0], topPosition[1]);targetCanvas.translate(radianPosition[0], radianPosition[1]);targetCanvas.restore();
当canvas移动到具体的位置之后,我们需要获取位图,将其在canvas上绘制出来。先处理位图相关的绘制逻辑,因为位图的宽高,跟视图的宽高肯定是不一致的,此时需要对位图进行相应的缩放操作,这里涉及到两次缩放的逻辑,第一次是位图缩放到大圆的逻辑,第二次是大圆缩放到小圆的逻辑,代码如下:
    int width = bitmap.getWidth();int height = bitmap.getHeight();if (width > 0 && height > 0) {//对位图进行缩放float scaleX = (float) mWidth / width;float scaleY = (float) mHeight / height;Matrix bitmapMatrix = new Matrix();bitmapMatrix.postScale(scaleX, scaleY);bitmapMatrix.postConcat(matrix);newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,height, bitmapMatrix, true);}

随机头像的绘制逻辑跟上篇文章的逻辑基本上是一样的,只是需要注意下,绘制的时候需要在小圆的基础上绘制,需要对字体的大小进行相应的缩放,代码如下:

    int size = (int) (viewSize * scale);private Bitmap createRandomMaskBitmap(int size, float scale, String text) {Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);final Paint paint = new Paint();paint.setAntiAlias(true);// 抗锯齿paint.setFilterBitmap(true);paint.setColor(mBackground);int center = size / 2;//获取画布的中心位置//创建canvas对象,绘制随机背景Canvas canvas = new Canvas(output);canvas.drawCircle(center, center, center, paint);//绘制随机背景上的文字setText(canvas, size, paint, scale, text);return output;}
    private void setText(Canvas canvas, int size, Paint paint, float scale, String text) {Rect targetRect = new Rect(0, 0, size, size);//设置绘制文本字体的颜色paint.setColor(Color.WHITE);//设置绘制文本的大小paint.setTextSize(mTextSize * scale);//获取文本展示的居中位置Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;paint.setTextAlign(Paint.Align.CENTER);canvas.drawText(text, targetRect.centerX(), baseline, paint);}

到这里,多人头像已经基本是实现了,效果图如下:

单人头像的展示,跟上篇文章的效果图也是一样的,完整的代码如下:

CircleImageView.java

package com.dylan.circleimageview;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;import java.util.ArrayList;
import java.util.List;/*** Description* author   Dylan.zhuang* Date:    16/6/30-下午2:43*/
public class CircleImageView extends ImageView {private static final String TAG = "CircleImageView";private static final int MAX = 5;private static final float DEFAULT_SCALE = 0.9f;/*** 绘制图片的位图*/private Bitmap mBitmap;/*** 圆形图像边框宽度*/private int mStrokeWidth;/*** 圆形图像边框颜色*/private int mStrokeColor;/*** 随机背景文本大小*/private int mTextSize;/*** 随机背景颜色*/private int mBackground;/*** 随机背景要展示的文本*/private String mText;/*** 视图宽度*/private int mWidth;/*** 视图高度*/private int mHeight;/*** 存放多人头像的列表*/private List<CircleImageViewBean> mCircleImageViewBeanList;/*** 逻辑层*/private CircleImageViewPresenter mPresenter;/*** 记录头像的个数*/private int mCount;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);//#5AC3B2int defaultColor = getResources().getColor(R.color.colorGreen);//14spint defaultTextSize = getResources().getDimensionPixelSize(R.dimen.dimen_default_text_size);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyleAttr, 0);mStrokeWidth = typedArray.getDimensionPixelSize(R.styleable.CircleImageView_stroke_width, 0);mStrokeColor = typedArray.getColor(R.styleable.CircleImageView_stroke_color, defaultColor);mTextSize = typedArray.getDimensionPixelSize(R.styleable.CircleImageView_text_size, defaultTextSize);mBackground = typedArray.getColor(R.styleable.CircleImageView_random_backgroud, defaultColor);mText = typedArray.getString(R.styleable.CircleImageView_text);//一定要记得回收typedArray.recycle();mPresenter = new CircleImageViewPresenter();}/*** 设置边缘宽度** @param width*/public void setStrokeWidth(int width) {mStrokeWidth = width;}/*** 设置边缘颜色** @param color*/public void setStrokeColor(int color) {mStrokeColor = color;}/*** 设置文本大小** @param textSize*/public void setTextSize(int textSize) {mTextSize = textSize;}/*** 设置背景颜色** @param background*/public void setBackground(int background) {mBackground = background;}/*** 设置文本** @param text*/public void setText(String text) {mText = text;}/*** 绘制随机背景*/public void drawRandomBackground() {readyInvalidate(mBitmap, mBackground, mText);}/*** 设置多人头像* @param circleImageViewBeanList*/public void setImageBitmaps(List<CircleImageViewBean> circleImageViewBeanList) {mCircleImageViewBeanList = circleImageViewBeanList;invalidate();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mWidth = w;mHeight = h;}@Overridepublic void setImageResource(int resId) {super.setImageResource(resId);mBitmap = getBitmapFromDrawable(getDrawable());readyInvalidate(mBitmap, mBackground, mText);}@Overridepublic void setImageDrawable(Drawable drawable) {super.setImageDrawable(drawable);mBitmap = getBitmapFromDrawable(drawable);readyInvalidate(mBitmap, mBackground, mText);}@Overridepublic void setImageBitmap(Bitmap bm) {super.setImageBitmap(bm);mBitmap = bm;readyInvalidate(mBitmap, mBackground, mText);}@Overrideprotected void onDraw(Canvas canvas) {//一定要注释掉,否则圆形图像没法生效
//        super.onDraw(canvas);if (mWidth > 0 && mHeight > 0) {
//            Bitmap bitmap = createCircleBitmapForSRC_IN(canvas);
//            if (bitmap != null) {
//                canvas.drawBitmap(bitmap, 0, 0, new Paint());
//            }if (mCircleImageViewBeanList != null && mCircleImageViewBeanList.size() > 0) {mCount = Math.min(mCircleImageViewBeanList.size(), MAX);//绘制多人头像createCircleBitmap(canvas);}}}/*** 为调用onDraw之前准备数据* @param bitmap* @param randomBg* @param text*/private void readyInvalidate(Bitmap bitmap, int randomBg, String text) {if (mCircleImageViewBeanList == null) {mCircleImageViewBeanList = new ArrayList<CircleImageViewBean>();}mCircleImageViewBeanList.clear();CircleImageViewBean bean = new CircleImageViewBean(bitmap, randomBg, text);mCircleImageViewBeanList.add(bean);invalidate();}/*** 创建圆形位图* @param targetCanvas*/private void createCircleBitmap(Canvas targetCanvas) {//初始化画笔Paint paint = new Paint();paint.setAntiAlias(true);//获取缩放比例float scale = mPresenter.getScale(mCount);int viewSize = Math.min(mWidth, mHeight);//获取与大圆最上面相切小圆的左上角位置,为了方便计算每个圆具体展示在哪个位置float[] topPosition = mPresenter.getTopPosition(viewSize / 2, scale);//获取旋转角度float[] rotation = mPresenter.getRotation(mCount);Matrix matrix = new Matrix();matrix.postScale(scale, scale);for (int i = 0; i < mCount; i++) {CircleImageViewBean bean = mCircleImageViewBeanList.get(i);//对canvas进行状态保存,防止被变换引起状态的改变targetCanvas.save();float[] radianPosition = mPresenter.getTranslatePosition(viewSize / 2, rotation[i], scale, topPosition[0], topPosition[1]);targetCanvas.translate(radianPosition[0], radianPosition[1]);Bitmap scaleBitmap = getScaleBitmap(bean, matrix, viewSize, scale);if (scaleBitmap == null) {return;}int newSize = Math.min(scaleBitmap.getWidth(), scaleBitmap.getHeight());int center = newSize / 2;//画边缘圈boolean isDrawBorder = drawCircleBorder(targetCanvas, center, paint);Bitmap circleBitmap = drawCircleBitmap(scaleBitmap, newSize, isDrawBorder);paint.setStyle(Paint.Style.FILL);targetCanvas.drawBitmap(circleBitmap, 0, 0, paint);targetCanvas.restore();}}/*** 获取缩放后的位图* @param bean* @param matrix* @param viewSize* @param scale* @return*/private Bitmap getScaleBitmap(CircleImageViewBean bean, Matrix matrix, int viewSize, float scale) {if (bean == null) {return null;}Bitmap newBitmap = null;Bitmap bitmap = bean.getBitmap();String text = bean.getText();if (bitmap != null) {int width = bitmap.getWidth();int height = bitmap.getHeight();if (width > 0 && height > 0) {//对位图进行缩放float scaleX = (float) mWidth / width;float scaleY = (float) mHeight / height;Matrix bitmapMatrix = new Matrix();bitmapMatrix.postScale(scaleX, scaleY);bitmapMatrix.postConcat(matrix);newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,height, bitmapMatrix, true);}} else {int size = (int) (viewSize * scale);newBitmap = createRandomMaskBitmap(size, scale, text);}return newBitmap;}/**** @param newBitmap* @param newSize* @param isDrawBorder* @return*/private Bitmap drawCircleBitmap(Bitmap newBitmap, int newSize, boolean isDrawBorder) {Bitmap bitmap = Bitmap.createBitmap(newSize, newSize, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);Paint paint = new Paint();paint.setAntiAlias(true);int center = newSize / 2;if (isDrawBorder) {canvas.scale(DEFAULT_SCALE, DEFAULT_SCALE, center, center);}//在矩阵中心画圆,与矩阵的四边相切canvas.drawCircle(center, center, center, paint);//设置Xfermode为SRC_INpaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//绘制图片canvas.drawBitmap(newBitmap, 0, 0, paint);return bitmap;}/*** 创建圆形图像** @param targetCanvas* @return*/private Bitmap createCircleBitmapForSRC_IN(Canvas targetCanvas) {//创建一个和图片大小差不多的正方形矩阵int size = Math.min(mWidth, mHeight);Bitmap newBitmap = null;if (mBitmap != null) {int width = mBitmap.getWidth();int height = mBitmap.getHeight();// 对bitmap进行缩放,缩放到指定view的大小Matrix matrix = new Matrix();matrix.postScale((float) mWidth / width, (float) mHeight / height);newBitmap = Bitmap.createBitmap(mBitmap, 0, 0, width,height, matrix, true);} else {newBitmap = createRandomMaskBitmap(size, 1.0f, mText);}if (newBitmap == null) {return null;}int center = size / 2;Paint paint = new Paint();boolean isDrawBorder = drawCircleBorder(targetCanvas, center, paint);Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);if (isDrawBorder) {paint.setColor(Color.WHITE);paint.setStyle(Paint.Style.FILL);canvas.scale(DEFAULT_SCALE, DEFAULT_SCALE, center, center);}//在矩阵中心画圆,与矩阵的四边相切canvas.drawCircle(center, center, center, paint);//设置Xfermode为SRC_INpaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//绘制图片canvas.drawBitmap(newBitmap, 0, 0, paint);return bitmap;}/*** 获取bitmap** @param drawable* @return*/private Bitmap getBitmapFromDrawable(Drawable drawable) {if (drawable == null) {return null;}if (drawable instanceof BitmapDrawable) {return ((BitmapDrawable) drawable).getBitmap();}try {Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());drawable.draw(canvas);return bitmap;} catch (OutOfMemoryError e) {return null;}}/*** 绘制边界圆** @param canvas* @param size* @param paint* @return*/private boolean drawCircleBorder(Canvas canvas, int size, Paint paint) {if (mStrokeWidth > 0) {paint.setAntiAlias(true);paint.setColor(mStrokeColor);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(mStrokeWidth);canvas.drawCircle(size, size, size - mStrokeWidth, paint);return true;}return false;}/*** 创建随机背景** @param size* @return*/private Bitmap createRandomMaskBitmap(int size, float scale, String text) {Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);final Paint paint = new Paint();paint.setAntiAlias(true);// 抗锯齿paint.setFilterBitmap(true);paint.setColor(mBackground);int center = size / 2;//获取画布的中心位置//创建canvas对象,绘制随机背景Canvas canvas = new Canvas(output);canvas.drawCircle(center, center, center, paint);//绘制随机背景上的文字setText(canvas, size, paint, scale, text);return output;}/*** 绘制文本** @param canvas* @param size* @param paint*/private void setText(Canvas canvas, int size, Paint paint, float scale, String text) {Rect targetRect = new Rect(0, 0, size, size);//设置绘制文本字体的颜色paint.setColor(Color.WHITE);//设置绘制文本的大小paint.setTextSize(mTextSize * scale);//获取文本展示的居中位置Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;paint.setTextAlign(Paint.Align.CENTER);canvas.drawText(text, targetRect.centerX(), baseline, paint);}
}

CircleImageViewPresenter.java

package com.dylan.circleimageview;/*** Description* author   Dylan.zhuang* Date:    16/7/7-下午2:51*/
public class CircleImageViewPresenter {/*** 每种头像对应的旋转角度*/private static final float[][] sRotationArray = { new float[] { 360 }, new float[] { 45, -135 },new float[] { 120, 0, -120 }, new float[] { 45, 135, -135, -45 },new float[] { 144, 72, 0, -72, -144 }};/*** 经过小圆圆心的两条直线ab,和经过大圆圆心的两条直线cd,a和c垂直,b和d垂直(对两个圆的条件不成立)* 公式为360/(n*2),n代表小圆个数,分割策略如下* 基数圆经过每个圆的圆心画直线,1个圆除外;* 偶数圆经过每个圆的圆心画直线,并在公切线也画直线;* 分割策略提到的直线都经过大圆圆心*/private static final int[] sAngleArray = {360, 90, 60, 45, 36};/*** 获取旋转角度* @param count* @return*/public float[] getRotation(int count) {return count > 0 && count <= sRotationArray.length ? sRotationArray[count - 1] : null;}/*** 获取缩放比例* @param count* @return*/public float getScale(int count) {int angle = getAngle(count);if (angle == 360) {return 1f;}double cot = getCot(angle);float scale = (float) (1f / (Math.sqrt(1 + Math.pow(cot, 2)) + 1));return scale;}/*** 获取最上面圆的左上角的值,方便计算每个圆的位置* @param radius* @param scale* @return*/public float[] getTopPosition(float radius, float scale) {float x = radius * (1 - scale);float y = 0;return new float[] { x, y };}/*** 获取每个小头像应该平移的距离,找圆心* @param radius* @param rotation* @param scale* @param topX* @param topY* @return*/public float[] getTranslatePosition(float radius, float rotation, float scale, float topX, float topY) {float smallRadius = radius * (1 - scale);double radian = Math.toRadians(rotation);double cos = Math.cos(radian);double sin = Math.sin(radian);float x = (float) (topX - smallRadius * sin);float y = (float) (topY + smallRadius * (1 - cos));return new float[] { x, y };}/*** 获取角度* @param count* @return*/private int getAngle(int count) {return count > 0 && count <= sAngleArray.length ? sAngleArray[count - 1] : null;}/*** 获取cot值* @param angle* @return*/private double getCot(int angle) {double radian = Math.toRadians(angle);double sin = Math.sin(radian);double cos = Math.cos(radian);return  cos / sin;}
}

MainActivity.java

package com.dylan.circleimageview;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;import java.util.ArrayList;
import java.util.List;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);CircleImageView circleImageView = (CircleImageView) findViewById(R.id.random_icon);circleImageView.drawRandomBackground();CircleImageView circleImageView1 = (CircleImageView) findViewById(R.id.random_icon1);circleImageView1.drawRandomBackground();List<CircleImageViewBean> list2 = new ArrayList<>();List<CircleImageViewBean> list3 = new ArrayList<>();List<CircleImageViewBean> list4 = new ArrayList<>();List<CircleImageViewBean> list5 = new ArrayList<>();Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);CircleImageViewBean bean1 = new CircleImageViewBean(bitmap, -1, "");CircleImageViewBean bean2 = new CircleImageViewBean(null, R.color.colorGreen, "A");CircleImageViewBean bean3 = new CircleImageViewBean(bitmap, R.color.colorGreen, "A");CircleImageViewBean bean4 = new CircleImageViewBean(null, R.color.colorAccent, "B");CircleImageViewBean bean5 = new CircleImageViewBean(bitmap, R.color.colorGreen, "A");list2.add(bean1);list2.add(bean2);CircleImageView circleImageView2 = (CircleImageView) findViewById(R.id.icon_two);circleImageView2.setImageBitmaps(list2);list3.add(bean3);list3.addAll(list2);CircleImageView circleImageView3 = (CircleImageView) findViewById(R.id.icon_three);circleImageView3.setImageBitmaps(list3);list4.add(bean4);list4.addAll(list3);CircleImageView circleImageView4 = (CircleImageView) findViewById(R.id.icon_four);circleImageView4.setImageBitmaps(list4);list5.add(bean5);list5.addAll(list4);CircleImageView circleImageView5 = (CircleImageView) findViewById(R.id.icon_five);circleImageView5.setImageBitmaps(list5);}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_margin="10dp"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><com.dylan.circleimageview.CircleImageView xmlns:circle_icon="http://schemas.android.com/apk/res/com.dylan.circleimageview"android:layout_width="100dp"android:layout_height="100dp"android:layout_margin="10dp"android:src="@drawable/test"circle_icon:stroke_color="#ff0000"circle_icon:stroke_width="1dp" /><com.dylan.circleimageview.CircleImageView xmlns:circle_icon="http://schemas.android.com/apk/res/com.dylan.circleimageview"android:id="@+id/icon_two"android:layout_width="100dp"android:layout_height="100dp"android:layout_margin="10dp"android:src="@drawable/test"circle_icon:stroke_color="#ff0000"circle_icon:stroke_width="1dp" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><com.dylan.circleimageview.CircleImageView xmlns:circle_icon="http://schemas.android.com/apk/res/com.dylan.circleimageview"android:id="@+id/random_icon"android:layout_width="100dp"android:layout_height="100dp"android:layout_margin="10dp"circle_icon:random_backgroud="@color/colorAccent"circle_icon:stroke_color="#000000"circle_icon:stroke_width="1dp"circle_icon:text="A"circle_icon:text_size="25sp" /><com.dylan.circleimageview.CircleImageView xmlns:circle_icon="http://schemas.android.com/apk/res/com.dylan.circleimageview"android:id="@+id/icon_three"android:layout_width="100dp"android:layout_height="100dp"android:layout_margin="10dp"circle_icon:random_backgroud="@color/colorAccent"circle_icon:stroke_color="#000000"circle_icon:stroke_width="1dp"circle_icon:text="A"circle_icon:text_size="25sp" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><com.dylan.circleimageview.CircleImageView xmlns:circle_icon="http://schemas.android.com/apk/res/com.dylan.circleimageview"android:layout_width="100dp"android:layout_height="100dp"android:layout_margin="10dp"android:src="@drawable/test" /><com.dylan.circleimageview.CircleImageView xmlns:circle_icon="http://schemas.android.com/apk/res/com.dylan.circleimageview"android:id="@+id/icon_four"android:layout_width="100dp"android:layout_height="100dp"android:layout_margin="10dp"android:src="@drawable/test" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><com.dylan.circleimageview.CircleImageView xmlns:circle_icon="http://schemas.android.com/apk/res/com.dylan.circleimageview"android:id="@+id/random_icon1"android:layout_width="100dp"android:layout_height="100dp"android:layout_margin="10dp"circle_icon:random_backgroud="@color/colorPrimary"circle_icon:text="B"circle_icon:text_size="25sp" /><com.dylan.circleimageview.CircleImageView xmlns:circle_icon="http://schemas.android.com/apk/res/com.dylan.circleimageview"android:id="@+id/icon_five"android:layout_width="100dp"android:layout_height="100dp"android:layout_margin="10dp"circle_icon:random_backgroud="@color/colorPrimary"circle_icon:text="B"circle_icon:text_size="25sp" /></LinearLayout></LinearLayout>

github地址

Android圆形头像的绘制(三)之多人头像的实现相关推荐

  1. Android Studio OpenGL ES绘制三棱锥/四面体的多纹理贴图 每个面使用一张图片渲染

    本文参考了王刚的<疯狂Android讲义(第3版)>P554-P559 要求:利用OpenGL ES绘制一个三棱锥,并对每个面进行纹理贴图,每个面使用不同的图片进行渲染. 环境:Andro ...

  2. android圆形头像 demo,Android图像处理之绘制圆形头像

    在Android中,绘制圆形和绘制图片都是很容易的事情,但是绘制圆形图片就有点难倒人了.以前为了偷懒就直接去github上找一个开源项目,后来才发现绘制圆形图片其实也是很简单的事.绘制圆形图片也需要两 ...

  3. Android 圆形头像的两种实现方式

    Android 圆形头像的两种实现方式 前言 这篇博客只是为了做一个记录而已,方便而后查询,核心代码都是直接采用鸿洋博客里面的代码的. 圆形头像在实际开发中实际很常见,一般来说,主要有两种实现方式: ...

  4. Android应用性能优化之优化列表头像过度绘制[一]

    为什么80%的码农都做不了架构师?>>>    操作的是否顺畅.卡顿,决定着整体的流畅程度. 事实上android跟iphone的差别,个人觉得很大程度上决定于流畅程度,无论是动画, ...

  5. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  6. Android 仿PhotoShop调色板应用(三) 主体界面绘制

    版权声明:本文为博主原创文章,未经博主允许不得转载. Android 仿PhotoShop调色板应用(三) 主体界面绘制    关于PhotoShop调色板应用的实现我总结了两个最核心的部分:   1 ...

  7. Android O: View的绘制流程(三):布局和绘制

    前一篇文章Android O: View的绘制流程(二):测量中,  我们分析了View的测量流程.  当View测量完毕后,就要开始进行布局和绘制相关的工作,  本篇文章就来分析下这部分流程. 一. ...

  8. Android圆形头像图Circle ImageView

    <Android圆形头像图Circle ImageView> 需要处理的原始图(pic): 使用CircleImageView处理后的图(作为头像): 现在很多的应用都有设置头像的功能,如 ...

  9. Android 开发使用OpenGL ES绘制三棱锥并进行纹理贴图

    效果图: 直接上代码 MainActivity.java的代码 package com.zzu.shiyan3;import androidx.appcompat.app.AppCompatActiv ...

最新文章

  1. 解决[warn] _default_ VirtualHost overlap on port 80, the first has precedence问题
  2. Struts2拦截器之FileUploadInterceptor
  3. 目标检测——从RCNN到Faster RCNN 串烧
  4. [2021-09-09 T3] 序列/luogu P3943 星空(异或差分+bfs最短路+状压dp)
  5. bash shell 命令行选项的用法
  6. .NET Remoting Security使用小结 – TcpChannel
  7. python索引字符串_Python:通过索引删除子字符串
  8. WordPress 修改固定链接出错(apache2)
  9. c语言输出最小值流程图,C语言实用程序设计100例流程图
  10. 初中计算机考试ppt操作视频,初中信息技术PPT操作题课件.ppt
  11. Flask蓝本与子域名
  12. OPENWRT---SWITCH设置
  13. 外贸独立站和外贸电商平台优劣势分析
  14. qt Redis使用
  15. 多层感知机预测电池SOH值
  16. xss payload大全
  17. vxe-table vxe-pager 如何使用分页,自定义分页
  18. 带bitlocker解密的pe_Win10使用BitLocker加密U盘|Win10自带BitLocker加密U盘
  19. 1989-2020中国省级平均受教育年限与学历结构数据
  20. 权责发生制与收付实现制在企业与事业单位会计处理的比较

热门文章

  1. 软件项目经理需要具备的资质
  2. 计算机是如何进行计算的?(一)
  3. Ultimate Rope Editor,绳索插件教程
  4. SCSS--SCSS相关介绍
  5. 苹果商业广告--倡导绿色环保
  6. 【Git 教程系列第 27 篇】ssh: connect to host github.com port 22: Connection refused 的解决方案
  7. display属性介绍
  8. 01-微发试验台-如何打开tdms文件?以及转换成matlab格式
  9. linux优先级继承和优先级天花板,关于Linux操作系统内核原理.ppt
  10. 数字IC前端设计入门----之环境搭建