效果预览
本次的图片效果是基于上一篇博客的三种方法进行扩展实现图片的外边框效果。上一篇博客的传送门。
下面开始进行到整体,针对上次实现圆形图片的三个方式添加一个外边框效果。
使用PorterDuffXfermode方式来实现
我们使用这个方式实现圆形图片的原理是通过图片的设置图片的叠加显示效果。还是这张图:
思路
实现圆形图片,我们只需要绘制一个圆,并将模式设置为SRC_IN
即可。但是我们需要在外围添加一个边框的话,就不会那么简单了。我们需要绘制三次图片,通过合理的设置他们的Xfermode
模式来达到我们的要求。具体步骤为:
- 将原图片绘制出来(如果有需要,进行必要的比例缩放处理,使其是一个正方形)
- 绘制一个等大的正方形,颜色为边框要显示的颜色。(这一步的目的是为了将外围的边框也绘制出来)
- 绘制一个半径为二分之一边长减去边框长度的圆,模式为
DST_OUT
- 绘制一个半径为二分之一边长的圆,模式为
DST_IN
通过上面四步,基本就实现了我们想要的外边框的效果。
用图表示为:
具体细节可以参考代码实现。代码
自定义viewCircleImageView1
的代码如下。在代码中使用到了自定义的属性,所以需要在1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134public class CircleImageView1 extends ImageView {
public static final int DEFAULT_BORDER_WIDTH = 0;
private Paint mPaint;
private Paint mBorderPaint;
private Shape mShape;
private Shape mBorderShape;
private float mRadius;
private float mBorderWidth;
private int mBorderColor;
private PorterDuffXfermode mBorderDuffMode;
private Bitmap mBorderBitmap;
private float[] outerRadii = new float[8];
public CircleImageView1(Context context) {
this(context, null);
}
public CircleImageView1(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleImageView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(attrs);
}
private void initView(AttributeSet attrs) {
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CircleImageView1);
mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView1_circle_border_width1, DEFAULT_BORDER_WIDTH);
mBorderColor = a.getInt(R.styleable.CircleImageView1_circle_border_color1, Color.BLACK);
a.recycle();
}
setLayerType(LAYER_TYPE_HARDWARE, null);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLACK);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
if (mBorderWidth != DEFAULT_BORDER_WIDTH) {
mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setFilterBitmap(true);
mBorderDuffMode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int min = Math.min(width, height);
mRadius = min / 2;
setMeasuredDimension(min, min);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
if (mShape == null) {
Arrays.fill(outerRadii, mRadius);
mShape = new RoundRectShape(outerRadii, null, null);
if (mBorderWidth != DEFAULT_BORDER_WIDTH && mBorderShape == null) {
mBorderShape = new RoundRectShape(outerRadii, null, null);
}
}
mShape.resize(getWidth(), getHeight());
if (mBorderWidth != DEFAULT_BORDER_WIDTH && mBorderShape != null) {
mBorderShape.resize(getWidth() - mBorderWidth * 2, getHeight() - mBorderWidth * 2);
makeStrokeBitmap();
}
}
}
@Override
protected void onDraw(Canvas canvas) {
int saveCount = canvas.getSaveCount();
canvas.save();
super.onDraw(canvas);
if (mBorderWidth != DEFAULT_BORDER_WIDTH && mBorderShape != null && mBorderBitmap != null) {
int i = canvas.saveLayer(0, 0, getMeasuredWidth(), getMeasuredHeight(), null, Canvas.ALL_SAVE_FLAG);
//1.绘制外边框颜色的正方形
mBorderPaint.setXfermode(null);
canvas.drawBitmap(mBorderBitmap, 0, 0, mBorderPaint);
//2.适当画布的中心位置
canvas.translate(mBorderWidth, mBorderWidth);
//3.绘制减去边框长度的图片,模式为DST_OUT
mBorderPaint.setXfermode(mBorderDuffMode);
mBorderShape.draw(canvas, mBorderPaint);
canvas.restoreToCount(i);
}
if (mShape != null) {
mShape.draw(canvas, mPaint);
}
canvas.restoreToCount(saveCount);
}
private void makeStrokeBitmap() {
if (mBorderWidth <= 0) return;
int w = getMeasuredWidth();
int h = getMeasuredHeight();
if (w == 0 || h == 0) {
return;
}
releaseBorderBitmap();
mBorderBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(mBorderBitmap);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(mBorderColor);
c.drawRect(new RectF(0, 0, w, h), p);
}
private void releaseBorderBitmap() {
if (mBorderBitmap != null) {
mBorderBitmap.recycle();
mBorderBitmap = null;
}
}
public void build(String url) {
Glide.with(getContext()).load(url).into(this);
}
}value/attrs
文件中添加支持,代码如下:使用也很简单,在xml中使用的代码如下:1
2
3
4
5
6
7
8
9
10<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleImageView1">
<attr name="circle_border_width1" format="dimension" />
<attr name="circle_border_color1" format="color" />
<attr name="circle_border_overlay1" format="boolean" />
<attr name="circle_fill_color1" format="color" />
<attr name="circle_circle_background_color1" format="color" />
</declare-styleable>
</resources>通过java代码设置图片来源,代码如下:1
2
3
4
5
6
7
8<com.example.cm.circleview.view2.CircleImageView1
android:id="@+id/image2_id1"
app:circle_border_width1="5dp"
app:circle_border_color1="#fff"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp" />1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class MainActivity extends AppCompatActivity {
public static final String URL = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533911636&di=04ec95ec44a1623d52b20ff08727ac78&imgtype=jpg&er=1&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fitem%2F201312%2F05%2F20131205172342_P3nwv.jpeg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CircleImageView1 circleImageView21 = findViewById(R.id.image2_id1);
circleImageView21.build(URL);
}
}
优点
能够实现对图片不同边框形状的支持。
缺点
实现较复杂,性能代价也比较高,且不支持设置外边框透明度。
使用BitmapShader重新绘制实现圆形图片
思路
和上一种方式比起来,第二种方式就显得简单很多了。主要思路就是利用bitmapShader
将原图绘制出来,然后通过canvas.drawCircle
绘制我们想要的边框即可。
代码
这里只贴出自定义viewCircleImageView2
的代码,完整代码可以参考我后面给出的代码链接。
1 |
|
与用这种方式实现没有边框的圆形图片相比,并添加复杂的逻辑,只是再次基础上画了一个圆,我们将画圆的画笔setStrokeWidth
设置我们要实现的边框即可。
优点
实现比较简单,支持复杂的图片处理。
缺点
需要两次处理drawable
,性能还是有一点的开销。
通过clipPath裁剪的方式
思路
我们在裁剪好的图片上画一个圆边框即可。与第二种方式实现圆类似。
代码
1 | public class CircleImageView3 extends ImageView { |
优点
实现简单,不需要额外操作drawble
,简单粗暴。
缺点
不支持复杂的图片处理。
源代码链接:https://github.com/Reoger/CurtomViewTest
上一篇链接:https://blog.csdn.net/Reoger/article/details/81394579