你的位置:首页 > 操作系统

[操作系统]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 view127     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 view137     invalidate();138   }139 140   @Override141   protected void onDraw(Canvas canvas) {142     super.onDraw(canvas);143     //draw arc144     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 text202     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.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.

 1 <??> 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   @Override19   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   @Override35   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   ="http://schemas.android.com/apk/res/android" 3   ="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>

注意,在布局文件中引入自定义属性时需要加入一行代码:

好了,需求搞定,剩下的就是搬到实际的项目当中去了。实现效果如下:

总结一下,自定义View一般就是通过重写onDraw、onMeasure()、onLayout()等方法来进行测量、绘制,绘制的时候一般会用到Canvas、Paint、Bitmap等类,测量和绘制的过程其实就是对现实生活中绘图工作的抽象和实现,我们利用面向对象的思想将画板、画纸、画笔等工具以及绘画的动作用一行行代码加以描述就OK啦!

由于实现过程比较简单,我就不贴源码了,大家如果对2D绘图还不是很了解,可以去搜一下相关资料或查阅相关书籍!