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绘图还不是很了解,可以去搜一下相关资料或查阅相关书籍!

posted @ 2016-05-13 14:09  三味码屋~  阅读(2743)  评论(0编辑  收藏  举报