如果图片质量要求不高 图片比较多的话 可以把 ARGB_8888改成 ARGB_4444
避免内存溢出
第一个是原图,第二个是圆形效果,第三第四设置了不同的圆角大小。
准备改变一个博客的风格,首先给大家讲一下原理,让大家明白了,然后再贴代码,不然可以直接看那么长的代码也比较痛苦,核心代码其实就那么几行:
核心代码分析:
复制代码
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
|
/**
* 根据原图和变长绘制圆形图片
*
* @param source
* @param min
* @return
*/
private
Bitmap createCircleImage(Bitmap source,
int
min)
{
final
Paint paint =
new
Paint();
paint.setAntiAlias(
true
);
Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);
/**
* 产生一个同样大小的画布
*/
Canvas canvas =
new
Canvas(target);
/**
* 首先绘制圆形
*/
canvas.drawCircle(min /
2
, min /
2
, min /
2
, paint);
/**
* 使用SRC_IN
*/
paint.setXfermode(
new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
/**
* 绘制图片
*/
canvas.drawBitmap(source,
0
,
0
, paint);
return
target;
}
|
其实主要靠:paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));这行代码,为什么呢,我给大家解释下,SRC_IN这种模式,两个绘制的效果叠加后取交集展现后图,怎么说呢,咱们第一个绘制的是个圆形,第二个绘制的是个Bitmap,于是交集为圆形,展现的是BItmap,就实现了圆形图片效果。圆角,其实就是先绘制圆角矩形,是不是很简单,以后别人再说实现圆角,你就把这一行代码给他就行了。
从Android的示例中,给大家证明一下:
下面有一张PorterDuff.Mode的16中效果图,咱们的只是其一:
源码咱们只关心谁先谁后绘制的:
复制代码
1
2
3
4
5
6
7
8
9
10
11
|
canvas.translate(x, y);
canvas.drawBitmap(mDstB,
0
,
0
, paint);
paint.setXfermode(sModes);
canvas.drawBitmap(mSrcB,
0
,
0
, paint);
paint.setXfermode(
null
);
canvas.restoreToCount(sc);
|
可以看出先绘制的Dst,再绘制的Src,最后的展示是SrcIn那个图:显示的区域是二者交集,展示的是Src(后者)。和咱们前面结论一致。效果16种,大家可以自由组合展示不同的效果。
好了,原理和核心代码解释完成。下面开始写自定义View。
1、自定义属性:
复制代码
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
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
resources
>
<
attr
name
=
"borderRadius"
format
=
"dimension"
/>
<
attr
name
=
"type"
>
<
enum
name
=
"circle"
value
=
"0"
/>
<
enum
name
=
"round"
value
=
"1"
/>
</
attr
>
<
attr
name
=
"src"
format
=
"reference"
></
attr
>
<
declare-styleable
name
=
"CustomImageView"
>
<
attr
name
=
"borderRadius"
/>
<
attr
name
=
"type"
/>
<
attr
name
=
"src"
/>
</
declare-styleable
>
</
resources
>
|
2、构造中获取自定义的属性:
复制代码
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
134
135
136
137
|
/**
* TYPE_CIRCLE / TYPE_ROUND
*/
private
int
type;
private
static
final
int
TYPE_CIRCLE =
0
;
private
static
final
int
TYPE_ROUND =
1
;
/**
* 图片
*/
private
Bitmap mSrc;
/**
* 圆角的大小
*/
private
int
mRadius;
/**
* 控件的宽度
*/
private
int
mWidth;
/**
* 控件的高度
*/
private
int
mHeight;
public
CustomImageView(Context context, AttributeSet attrs)
{
this
(context, attrs,
0
);
}
public
CustomImageView(Context context)
{
this
(context,
null
);
}
/**
* 初始化一些自定义的参数
*
* @param context
* @param attrs
* @param defStyle
*/
public
CustomImageView(Context context, AttributeSet attrs,
int
defStyle)
{
super
(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyle,
0
);
int
n = a.getIndexCount();
for
(
int
i =
0
; i < n; i++)
{
int
attr = a.getIndex(i);
switch
(attr)
{
case
R.styleable.CustomImageView_src:
mSrc = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr,
0
));
break
;
case
R.styleable.CustomImageView_type:
type = a.getInt(attr,
0
);
// 默认为Circle
break
;
case
R.styleable.CustomImageView_borderRadius:
mRadius= a.getDimensionPixelSize(attr, (
int
) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,
getResources().getDisplayMetrics()));
// 默认为10DP
break
;
}
}
a.recycle();
}
|
3、onMeasure中获取控件宽高:
复制代码
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
|
/**
* 计算控件的高度和宽度
*/
@Override
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec)
{
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置宽度
*/
int
specMode = MeasureSpec.getMode(widthMeasureSpec);
int
specSize = MeasureSpec.getSize(widthMeasureSpec);
if
(specMode == MeasureSpec.EXACTLY)
// match_parent , accurate
{
mWidth = specSize;
}
else
{
// 由图片决定的宽
int
desireByImg = getPaddingLeft() + getPaddingRight() + mSrc.getWidth();
if
(specMode == MeasureSpec.AT_MOST)
// wrap_content
{
mWidth = Math.min(desireByImg, specSize);
}
}
/***
* 设置高度
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if
(specMode == MeasureSpec.EXACTLY)
// match_parent , accurate
{
mHeight = specSize;
}
else
{
int
desire = getPaddingTop() + getPaddingBottom() + mSrc.getHeight();
if
(specMode == MeasureSpec.AT_MOST)
// wrap_content
{
mHeight = Math.min(desire, specSize);
}
}
setMeasuredDimension(mWidth, mHeight);
}
|
复制代码
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
/**
* 绘制
*/
@Override
protected
void
onDraw(Canvas canvas)
{
switch
(type)
{
// 如果是TYPE_CIRCLE绘制圆形
case
TYPE_CIRCLE:
int
min = Math.min(mWidth, mHeight);
/**
* 长度如果不一致,按小的值进行压缩
*/
mSrc = Bitmap.createScaledBitmap(mSrc, min, min,
false
);
canvas.drawBitmap(createCircleImage(mSrc, min),
0
,
0
,
null
);
break
;
case
TYPE_ROUND:
canvas.drawBitmap(createRoundConerImage(mSrc),
0
,
0
,
null
);
break
;
}
}
/**
* 根据原图和变长绘制圆形图片
*
* @param source
* @param min
* @return
*/
private
Bitmap createCircleImage(Bitmap source,
int
min)
{
final
Paint paint =
new
Paint();
paint.setAntiAlias(
true
);
Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);
/**
* 产生一个同样大小的画布
*/
Canvas canvas =
new
Canvas(target);
/**
* 首先绘制圆形
*/
canvas.drawCircle(min /
2
, min /
2
, min /
2
, paint);
/**
* 使用SRC_IN,参考上面的说明
*/
paint.setXfermode(
new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
/**
* 绘制图片
*/
canvas.drawBitmap(source,
0
,
0
, paint);
return
target;
}
/**
* 根据原图添加圆角
*
* @param source
* @return
*/
private
Bitmap createRoundConerImage(Bitmap source)
{
final
Paint paint =
new
Paint();
paint.setAntiAlias(
true
);
Bitmap target = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);
Canvas canvas =
new
Canvas(target);
RectF rect =
new
RectF(
0
,
0
, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rect, mRadius, mRadius, paint);
paint.setXfermode(
new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(source,
0
,
0
, paint);
return
target;
}
|
第一个是原图,第二个是圆形效果,第三第四设置了不同的圆角大小。
准备改变一个博客的风格,首先给大家讲一下原理,让大家明白了,然后再贴代码,不然可以直接看那么长的代码也比较痛苦,核心代码其实就那么几行:
核心代码分析:
复制代码
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
|
/**
* 根据原图和变长绘制圆形图片
*
* @param source
* @param min
* @return
*/
private
Bitmap createCircleImage(Bitmap source,
int
min)
{
final
Paint paint =
new
Paint();
paint.setAntiAlias(
true
);
Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);
/**
* 产生一个同样大小的画布
*/
Canvas canvas =
new
Canvas(target);
/**
* 首先绘制圆形
*/
canvas.drawCircle(min /
2
, min /
2
, min /
2
, paint);
/**
* 使用SRC_IN
*/
paint.setXfermode(
new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
/**
* 绘制图片
*/
canvas.drawBitmap(source,
0
,
0
, paint);
return
target;
}
|
其实主要靠:paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));这行代码,为什么呢,我给大家解释下,SRC_IN这种模式,两个绘制的效果叠加后取交集展现后图,怎么说呢,咱们第一个绘制的是个圆形,第二个绘制的是个Bitmap,于是交集为圆形,展现的是BItmap,就实现了圆形图片效果。圆角,其实就是先绘制圆角矩形,是不是很简单,以后别人再说实现圆角,你就把这一行代码给他就行了。
从Android的示例中,给大家证明一下:
下面有一张PorterDuff.Mode的16中效果图,咱们的只是其一:
源码咱们只关心谁先谁后绘制的:
复制代码
1
2
3
4
5
6
7
8
9
10
11
|
canvas.translate(x, y);
canvas.drawBitmap(mDstB,
0
,
0
, paint);
paint.setXfermode(sModes);
canvas.drawBitmap(mSrcB,
0
,
0
, paint);
paint.setXfermode(
null
);
canvas.restoreToCount(sc);
|
可以看出先绘制的Dst,再绘制的Src,最后的展示是SrcIn那个图:显示的区域是二者交集,展示的是Src(后者)。和咱们前面结论一致。效果16种,大家可以自由组合展示不同的效果。
好了,原理和核心代码解释完成。下面开始写自定义View。
1、自定义属性:
复制代码
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
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
resources
>
<
attr
name
=
"borderRadius"
format
=
"dimension"
/>
<
attr
name
=
"type"
>
<
enum
name
=
"circle"
value
=
"0"
/>
<
enum
name
=
"round"
value
=
"1"
/>
</
attr
>
<
attr
name
=
"src"
format
=
"reference"
></
attr
>
<
declare-styleable
name
=
"CustomImageView"
>
<
attr
name
=
"borderRadius"
/>
<
attr
name
=
"type"
/>
<
attr
name
=
"src"
/>
</
declare-styleable
>
</
resources
>
|
2、构造中获取自定义的属性:
复制代码
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
134
135
136
137
|
/**
* TYPE_CIRCLE / TYPE_ROUND
*/
private
int
type;
private
static
final
int
TYPE_CIRCLE =
0
;
private
static
final
int
TYPE_ROUND =
1
;
/**
* 图片
*/
private
Bitmap mSrc;
/**
* 圆角的大小
*/
private
int
mRadius;
/**
* 控件的宽度
*/
private
int
mWidth;
/**
* 控件的高度
*/
private
int
mHeight;
public
CustomImageView(Context context, AttributeSet attrs)
{
this
(context, attrs,
0
);
}
public
CustomImageView(Context context)
{
this
(context,
null
);
}
/**
* 初始化一些自定义的参数
*
* @param context
* @param attrs
* @param defStyle
*/
public
CustomImageView(Context context, AttributeSet attrs,
int
defStyle)
{
super
(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyle,
0
);
int
n = a.getIndexCount();
for
(
int
i =
0
; i < n; i++)
{
int
attr = a.getIndex(i);
switch
(attr)
{
case
R.styleable.CustomImageView_src:
mSrc = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr,
0
));
break
;
case
R.styleable.CustomImageView_type:
type = a.getInt(attr,
0
);
// 默认为Circle
break
;
case
R.styleable.CustomImageView_borderRadius:
mRadius= a.getDimensionPixelSize(attr, (
int
) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,
getResources().getDisplayMetrics()));
// 默认为10DP
break
;
}
}
a.recycle();
}
|
3、onMeasure中获取控件宽高:
复制代码
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
|
/**
* 计算控件的高度和宽度
*/
@Override
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec)
{
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置宽度
*/
int
specMode = MeasureSpec.getMode(widthMeasureSpec);
int
specSize = MeasureSpec.getSize(widthMeasureSpec);
if
(specMode == MeasureSpec.EXACTLY)
// match_parent , accurate
{
mWidth = specSize;
}
else
{
// 由图片决定的宽
int
desireByImg = getPaddingLeft() + getPaddingRight() + mSrc.getWidth();
if
(specMode == MeasureSpec.AT_MOST)
// wrap_content
{
mWidth = Math.min(desireByImg, specSize);
}
}
/***
* 设置高度
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if
(specMode == MeasureSpec.EXACTLY)
// match_parent , accurate
{
mHeight = specSize;
}
else
{
int
desire = getPaddingTop() + getPaddingBottom() + mSrc.getHeight();
if
(specMode == MeasureSpec.AT_MOST)
// wrap_content
{
mHeight = Math.min(desire, specSize);
}
}
setMeasuredDimension(mWidth, mHeight);
}
|
复制代码
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
/**
* 绘制
*/
@Override
protected
void
onDraw(Canvas canvas)
{
switch
(type)
{
// 如果是TYPE_CIRCLE绘制圆形
case
TYPE_CIRCLE:
int
min = Math.min(mWidth, mHeight);
/**
* 长度如果不一致,按小的值进行压缩
*/
mSrc = Bitmap.createScaledBitmap(mSrc, min, min,
false
);
canvas.drawBitmap(createCircleImage(mSrc, min),
0
,
0
,
null
);
break
;
case
TYPE_ROUND:
canvas.drawBitmap(createRoundConerImage(mSrc),
0
,
0
,
null
);
break
;
}
}
/**
* 根据原图和变长绘制圆形图片
*
* @param source
* @param min
* @return
*/
private
Bitmap createCircleImage(Bitmap source,
int
min)
{
final
Paint paint =
new
Paint();
paint.setAntiAlias(
true
);
Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);
/**
* 产生一个同样大小的画布
*/
Canvas canvas =
new
Canvas(target);
/**
* 首先绘制圆形
*/
canvas.drawCircle(min /
2
, min /
2
, min /
2
, paint);
/**
* 使用SRC_IN,参考上面的说明
*/
paint.setXfermode(
new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
/**
* 绘制图片
*/
canvas.drawBitmap(source,
0
,
0
, paint);
return
target;
}
/**
* 根据原图添加圆角
*
* @param source
* @return
*/
private
Bitmap createRoundConerImage(Bitmap source)
{
final
Paint paint =
new
Paint();
paint.setAntiAlias(
true
);
Bitmap target = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);
Canvas canvas =
new
Canvas(target);
RectF rect =
new
RectF(
0
,
0
, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rect, mRadius, mRadius, paint);
paint.setXfermode(
new
PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(source,
0
,
0
, paint);
return
target;
}
|