Android打造带透明圆弧的ImageView
这几天因为项目需求,需要在ImageView上面叠加一层透明圆弧,并且在沿着圆弧的方向显示相应的文字,效果如下图所示:

拿到这个需求,首先想到的是自定义一个ImageView来实现此功能,即在onDraw()中绘制圆弧和文字。同时因为要保证圆弧的位置可以任意摆放,圆弧的颜色、透明度以及文字大小、颜色等都是可控的,所以增加了一些自定义属性。实现代码非常简单,如下:
1.自定义ImageView:
1 package com.chunk.customviewsdemo.views.ArcImageView;
2
3 import android.content.Context;
4 import android.content.res.TypedArray;
5 import android.graphics.Canvas;
6 import android.graphics.Paint;
7 import android.graphics.Path;
8 import android.graphics.RectF;
9 import android.util.AttributeSet;
10 import android.widget.ImageView;
11
12 import com.chunk.customviewsdemo.R;
13
14 /**
15 * Description:A custom ImageView with circular arc and text
16 * Author: XiaoYu
17 * Date: 2016/5/10 13:55
18 */
19 public class ArcImageView extends ImageView {
20 /**
21 * The default text size.
22 */
23 private final float DEFAULT_TEXT_SIZE = 20;
24 /**
25 * The default scale value which decides the width of arc.
26 */
27 private final float DEFAULT_SCALE = 0.5f;
28 /**
29 * The default transparency of arc.
30 */
31 private final int DEFAULT_ARC_ALPHA =100;
32 /**
33 * The default width of arc.
34 */
35 private final int DEFAULT_ARC_WIDTH =160;
36 /**
37 * The default angle that the arc starts with.
38 */
39 private final int DEFAULT_START_ANGLE = 180;
40 /**
41 * The default angle that the arc.
42 */
43 private final int DEFAULT_SWEEP_ANGLE = 90;
44 /**
45 * The default distance along the path to add to the text's starting position.
46 */
47 private final int DEFAULT_H_OFFSET = 100;
48 /**
49 * The default distance above(-) or below(+) the path to position the text.
50 */
51 private final int DEFAULT_V_OFFSET = 20;
52 private Context mContext;
53 /**
54 * The text displayed on ImageView along arc.
55 */
56 private String mDrawStr;
57 /**
58 * The font size of text.
59 */
60 private float mTextSize = DEFAULT_TEXT_SIZE;
61 /**
62 * The scale value which decides the width of arc.
63 */
64 private float mScale = DEFAULT_SCALE;
65 /**
66 * The transparency of arc.
67 */
68 private int mArcAlpha = DEFAULT_ARC_ALPHA;
69 /**
70 * The width of arc.
71 */
72 private int mArcWidth = DEFAULT_ARC_WIDTH;
73 /**
74 * The start angle of angle.
75 */
76 private int mStartAngle = DEFAULT_START_ANGLE;
77 /**
78 * The swept angle of angle.
79 */
80 private int mSweepAngle = DEFAULT_SWEEP_ANGLE;
81 /**
82 * The default distance along the path to add to the text's starting position.
83 */
84 private float mHOffset = DEFAULT_H_OFFSET;
85 /**
86 * The default distance above(-) or below(+) the path to position the text.
87 */
88 private float mVOffset = DEFAULT_V_OFFSET;
89 /**
90 * The style of arc, all styles includes LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM, CENTER。
91 * of course, you can add your own style according to your demands.
92 */
93 private int mDrawStyle;
94 /**
95 * The color of arc.
96 */
97 private int mArcColor;
98 /**
99 * The color of text.
100 */
101 private int mTextColor;
102
103 public ArcImageView(Context context) {
104 super(context);
105 this.mContext = context;
106 }
107
108 public ArcImageView(Context context, AttributeSet attrs) {
109 super(context, attrs);
110 this.mContext = context;
111 obtainAttributes(attrs);
112 }
113
114 public ArcImageView(Context context, AttributeSet attrs, int defStyleAttr) {
115 super(context, attrs, defStyleAttr);
116 this.mContext = context;
117 obtainAttributes(attrs);
118 }
119
120 /**
121 * Set the text that will be drawn on arc.
122 * @param drawStr the text content.
123 */
124 public void setDrawStr(String drawStr) {
125 this.mDrawStr = drawStr;
126 //refresh this view
127 invalidate();
128 }
129
130 /**
131 * Set the transparency of arc.
132 * @param mArcAlpha the value of transparency.
133 */
134 public void setArcAlpha(int mArcAlpha) {
135 this.mArcAlpha = mArcAlpha;
136 //refresh this view
137 invalidate();
138 }
139
140 @Override
141 protected void onDraw(Canvas canvas) {
142 super.onDraw(canvas);
143 //draw arc
144 Paint arcPaint = new Paint();
145 arcPaint.setStrokeWidth(mArcWidth);
146 arcPaint.setStyle(Paint.Style.STROKE);
147 arcPaint.setColor(mArcColor);
148 arcPaint.setAlpha(mArcAlpha);
149 int width = getWidth();
150 int height = getHeight();
151 float radius;
152 if (width > height) {
153 radius = mScale * height;
154 } else {
155 radius = mScale * width;
156 }
157 RectF oval = new RectF();
158
159 int center_x = width;
160 int center_y = height;
161
162 switch (mDrawStyle) {
163 case 0:
164 center_x = 0;
165 center_y = 0;
166 mStartAngle = 90;
167 mSweepAngle = -90;
168 break;
169 case 1:
170 center_x = 0;
171 center_y = height;
172 mStartAngle = 270;
173 mSweepAngle = 90;
174 break;
175 case 2:
176 center_x = width;
177 center_y = 0;
178 mStartAngle = 180;
179 mSweepAngle = -90;
180 break;
181 case 3:
182 center_x = width;
183 center_y = height;
184 mStartAngle = 180;
185 mSweepAngle = 90;
186 break;
187 case 4:
188 center_x = width / 2;
189 center_y = height / 2;
190 mStartAngle = 270;
191 mSweepAngle = 90;
192 break;
193 }
194 float left = center_x - radius;
195 float top = center_y - radius;
196 float right = center_x + radius;
197 float bottom = center_y + radius;
198 oval.set(left, top, right, bottom);
199 canvas.drawArc(oval, mStartAngle, mSweepAngle, false, arcPaint);
200
201 //draw text
202 Paint textPaint = new Paint();
203 textPaint.setTextSize(mTextSize);
204 textPaint.setStyle(Paint.Style.FILL);
205 textPaint.setColor(mTextColor);
206 Path path = new Path();
207 path.addArc(oval, mStartAngle, mSweepAngle);
208 canvas.drawTextOnPath(mDrawStr, path, mHOffset, mVOffset, textPaint);
209 }
210
211 /**
212 * Obtain custom attributes that been defined in attrs.xml.
213 * @param attrs A collection of attributes.
214 */
215 private void obtainAttributes(AttributeSet attrs) {
216 TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.ArcImageView);
217 mDrawStr = ta.getString(R.styleable.ArcImageView_drawStr);
218 mTextSize = ta.getDimension(R.styleable.ArcImageView_textSize, DEFAULT_TEXT_SIZE);
219 mArcAlpha = ta.getInteger(R.styleable.ArcImageView_arcAlpha, DEFAULT_ARC_ALPHA);
220 mArcWidth = ta.getInteger(R.styleable.ArcImageView_arcWidth, DEFAULT_ARC_WIDTH);
221 mStartAngle = ta.getInteger(R.styleable.ArcImageView_startAngle, DEFAULT_START_ANGLE);
222 mSweepAngle = ta.getInteger(R.styleable.ArcImageView_startAngle, DEFAULT_SWEEP_ANGLE);
223 mHOffset = ta.getInteger(R.styleable.ArcImageView_hOffset, DEFAULT_H_OFFSET);
224 mVOffset = ta.getInteger(R.styleable.ArcImageView_vOffset, DEFAULT_V_OFFSET);
225 mArcColor = ta.getColor(R.styleable.ArcImageView_arcColor, 0XCCCCCC);
226 mTextColor = ta.getColor(R.styleable.ArcImageView_textColor, 0XFFFFFF);
227 mDrawStyle = ta.getInt(R.styleable.ArcImageView_drawStyle, 0);
228 ta.recycle();
229 }
230 }
2.在values文件夹下的attrs.xml中自定义属性:
1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 <declare-styleable name="ArcImageView"> 4 <attr name="drawStr" format="string" /> 5 <attr name="textSize" format="dimension" /> 6 <attr name="arcAlpha" format="integer" /> 7 <attr name="arcWidth" format="integer" /> 8 <attr name="startAngle" format="integer" /> 9 <attr name="sweepAngle" format="integer" /> 10 <attr name="scale" format="float" /> 11 <attr name="hOffset" format="float" /> 12 <attr name="vOffset" format="float" /> 13 <attr name="drawStyle" format="enum"> 14 <enum name="LEFT_TOP" value="0" /> 15 <enum name="LEFT_BOTTOM" value="1" /> 16 <enum name="RIGHT_TOP" value="2" /> 17 <enum name="RIGHT_BOTTOM" value="3" /> 18 <enum name="CENTER" value="4" /> 19 </attr> 20 <attr name="arcColor" format="color" /> 21 <attr name="textColor" format="color" /> 22 </declare-styleable> 23 </resources>
3.在MainActivity调用ArcImageView,实现代码如下:
1 package com.chunk.customviewsdemo;
2
3 import android.os.Bundle;
4 import android.support.v7.app.AppCompatActivity;
5 import android.view.View;
6 import android.widget.Button;
7
8 import com.chunk.customviewsdemo.views.ArcImageView.ArcImageView;
9
10 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
11 private ArcImageView aiv_one;
12 private ArcImageView aiv_two;
13 private ArcImageView aiv_three;
14 private ArcImageView aiv_four;
15 private Button btn_another_one;
16 private int mGroup = 1;
17
18 @Override
19 protected void onCreate(Bundle savedInstanceState) {
20 super.onCreate(savedInstanceState);
21 setContentView(R.layout.activity_main);
22 aiv_one = (ArcImageView) findViewById(R.id.aiv_one);
23 aiv_one.setArcAlpha(180);
24 aiv_two = (ArcImageView) findViewById(R.id.aiv_two);
25 aiv_two.setArcAlpha(180);
26 aiv_three = (ArcImageView) findViewById(R.id.aiv_three);
27 aiv_three.setArcAlpha(180);
28 aiv_four = (ArcImageView) findViewById(R.id.aiv_four);
29 aiv_four.setArcAlpha(180);
30 btn_another_one = (Button) findViewById(R.id.btn_another_one);
31 btn_another_one.setOnClickListener(this);
32 }
33
34 @Override
35 public void onClick(View v) {
36 switch (v.getId()) {
37 case R.id.btn_another_one:
38 if (mGroup == 1) {
39 aiv_one.setDrawStr("苹果");
40 aiv_one.setBackgroundResource(R.drawable.apple);
41 aiv_two.setDrawStr("柚子");
42 aiv_two.setBackgroundResource(R.drawable.pineapple);
43 aiv_three.setDrawStr("香蕉");
44 aiv_three.setBackgroundResource(R.drawable.banana);
45 aiv_four.setDrawStr("菠萝");
46 aiv_four.setBackgroundResource(R.drawable.pineapple);
47 mGroup = 2;
48 } else {
49 aiv_one.setDrawStr("牛排");
50 aiv_one.setBackgroundResource(R.drawable.steak);
51 aiv_two.setDrawStr("海鲜");
52 aiv_two.setBackgroundResource(R.drawable.seafood);
53 aiv_three.setDrawStr("奶酪");
54 aiv_three.setBackgroundResource(R.drawable.cheese);
55 aiv_four.setDrawStr("烧烤");
56 aiv_four.setBackgroundResource(R.drawable.barbecue);
57 mGroup = 1;
58 }
59 break;
60 }
61 }
62 }
4.MainActivity的布局文件如下:
1 <LinearLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:custom="http://schemas.android.com/apk/res-auto" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:layout_marginTop="100dp" 7 android:layout_marginBottom="100dp" 8 android:orientation="vertical" > 9 10 <Button 11 android:id="@+id/btn_another_one" 12 android:layout_width="wrap_content" 13 android:layout_height="wrap_content" 14 android:text="换一组" /> 15 16 <LinearLayout 17 android:layout_width="match_parent" 18 android:layout_height="0dp" 19 android:layout_weight="1" 20 android:orientation="horizontal" > 21 22 <RelativeLayout 23 android:layout_width="0dp" 24 android:layout_weight="1" 25 android:layout_height="match_parent" > 26 27 <com.chunk.customviewsdemo.views.ArcImageView.ArcImageView 28 android:id="@+id/aiv_one" 29 android:layout_width="match_parent" 30 android:layout_height="match_parent" 31 android:background="@drawable/steak" 32 custom:drawStyle="RIGHT_BOTTOM" 33 custom:drawStr="牛排" 34 custom:arcAlpha="100" 35 custom:arcColor="@color/gray" 36 custom:textColor="@color/black" 37 custom:textSize="20sp" /> 38 </RelativeLayout> 39 40 <RelativeLayout 41 android:layout_width="0dp" 42 android:layout_weight="1" 43 android:layout_height="match_parent" > 44 45 <com.chunk.customviewsdemo.views.ArcImageView.ArcImageView 46 android:id="@+id/aiv_two" 47 android:layout_width="match_parent" 48 android:layout_height="match_parent" 49 android:background="@drawable/seafood" 50 custom:drawStyle="LEFT_BOTTOM" 51 custom:drawStr="海鲜" 52 custom:arcAlpha="100" 53 custom:arcColor="@color/gray" 54 custom:textColor="@color/black" 55 custom:textSize="20sp" /> 56 57 </RelativeLayout> 58 </LinearLayout> 59 60 <LinearLayout 61 android:layout_width="match_parent" 62 android:layout_height="0dp" 63 android:layout_weight="1" 64 android:orientation="horizontal" > 65 66 <RelativeLayout 67 android:layout_width="0dp" 68 android:layout_weight="1" 69 android:layout_height="match_parent" > 70 71 <com.chunk.customviewsdemo.views.ArcImageView.ArcImageView 72 android:id="@+id/aiv_three" 73 android:layout_width="match_parent" 74 android:layout_height="match_parent" 75 android:background="@drawable/cheese" 76 custom:drawStyle="RIGHT_TOP" 77 custom:drawStr="奶酪" 78 custom:arcAlpha="100" 79 custom:arcColor="@color/gray" 80 custom:textColor="@color/black" 81 custom:textSize="20sp" /> 82 </RelativeLayout> 83 84 <RelativeLayout 85 android:layout_width="0dp" 86 android:layout_weight="1" 87 android:layout_height="match_parent" > 88 89 <com.chunk.customviewsdemo.views.ArcImageView.ArcImageView 90 android:id="@+id/aiv_four" 91 android:layout_width="match_parent" 92 android:layout_height="match_parent" 93 android:background="@drawable/barbecue" 94 custom:drawStyle="LEFT_TOP" 95 custom:drawStr="烧烤" 96 custom:arcAlpha="100" 97 custom:arcColor="@color/gray" 98 custom:textColor="@color/black" 99 custom:textSize="20sp" /> 100 101 </RelativeLayout> 102 </LinearLayout> 103 </LinearLayout>
注意,在布局文件中引入自定义属性时需要加入一行代码:xmlns:custom="http://schemas.android.com/apk/res-auto"。
好了,需求搞定,剩下的就是搬到实际的项目当中去了。实现效果如下:

总结一下,自定义View一般就是通过重写onDraw、onMeasure()、onLayout()等方法来进行测量、绘制,绘制的时候一般会用到Canvas、Paint、Bitmap等类,测量和绘制的过程其实就是对现实生活中绘图工作的抽象和实现,我们利用面向对象的思想将画板、画纸、画笔等工具以及绘画的动作用一行行代码加以描述就OK啦!
由于实现过程比较简单,我就不贴源码了,大家如果对2D绘图还不是很了解,可以去搜一下相关资料或查阅相关书籍!


浙公网安备 33010602011771号