android 自定义控件 (二) 初步认识

  最近一直在忙项目,也在不断的面试,每次问道这个自定义控件,好多人云里雾绕的,今天就这个机会,简单做个完全自定义控件的入门吧。上一篇讲了继承已有控件的过程,发现我们只是简答的在封装的布局里操作,并没有重写onDraw,onMeasure,onLayout这些方法。其实继承控件这种形式基本能满足我们大部分的功能,但对于现有控件无法满足的怎么办,那就让我们重写上述三个方法,自己写特定需求的控件。完全自定义控件一般有两种,一种继承View,一种继承Viewgroup。根据view树结构,我们知道Viewgroup可以包含view,也可以包含viewgroup。View也可以包含Viewgroup。不能一看到Viewgroup,望文生义。今天先以一个简单的例子,来打开完全自定义之门。

经常在项目中遇到进度条的问题,比如圆形进度条,显示百分比进度等。请看代码。

1.分析:

从图中我们看到,分三个部分:大圆,红色的进度,中间的文字。而圆形还有一定的宽度,颜色,进度显示也有颜色,文字有颜色,同时还有大小等细节。

2.实现思路:

刚才说到字体大小等细节,这些可以作为属性,绘制大圆,文字,进度等可以在onDraw里重绘(画圆用到drawCircle,绘制文字用到drawText,进度用到drawArc),自定义view就完全自己来嘛。

3.额外的细节(添加监听等):

上述绘制完view暂时还是精致的,想动起来,需要不断的更新进度,不断刷新重绘view。这时候就用到了invalidate()或postinvalidate();当invalidate()方法调用时会重新调用onDraw() 方法。当进度达到100%时,还要有个回调,执行完毕等。针对其他的自定义我们可能还要重新onTouchEvent,onSroll等触摸,滑动等,这样才算是一个完整的控件。

好了,基本的思路都告诉大家了。以后无外乎就这几个思路。重要的还是把自己掌握的技能融会贯通,多练。下面就看实现代码吧:

(1)values里的attrs代码

<resources>
    <declare-styleable name="CircleView">
        <attr name="roundColor" format="color"/>
        <attr name="roundProgressColor" format="color"/>
        <attr name="roundWidth" format="dimension"></attr>
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
        <attr name="max" format="integer"></attr>
        <attr name="textIsDisplayable" format="boolean"></attr>
        <attr name="style">
            <enum name="STROKE" value="0"></enum>
            <enum name="FILL" value="1"></enum>
        </attr>
    </declare-styleable>
</resources>

(2)自定义控件核心代码

  1 public class CircleView extends View  {
  2
  3     private int mCirlceViewColor;
  4     private int mCirlceViweProgressColor;
  5     private  int mProgress;
  6     private float mTextSize;
  7     private int mTextColor;
  8     private  int mStyle;
  9     private  float mRoudWidth;
 10     private  int  max;
 11     private Paint mPaint ;
 12
 13     private  static final int STROKE = 0;
 14     private  static final int FILL = 1;
 15
 16     public CircleView(Context context) {
 17         this(context, null);
 18     }
 19
 20     public CircleView(Context context, AttributeSet attrs) {
 21         this(context, attrs, 0);
 22     }
 23
 24     public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
 25         super(context, attrs, defStyleAttr);
 26         mPaint = new Paint();
 27
 28         TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
 29         mCirlceViewColor = array.getColor(R.styleable.CircleView_roundColor, Color.BLACK);
 30         mCirlceViweProgressColor = array.getColor(R.styleable.CircleView_roundProgressColor,Color.RED);
 31         mTextColor = array.getColor(R.styleable.CircleView_textColor,Color.BLUE);
 32         mTextSize = array.getDimension(R.styleable.CircleView_textSize,40);
 33         mRoudWidth = array.getDimension(R.styleable.CircleView_roundWidth,6);
 34         max = array.getInteger(R.styleable.CircleView_max,100);
 35         mStyle = array.getInt(R.styleable.CircleView_style, 0);
 36
 37         array.recycle();
 38     }
 39
 40     @Override
 41     protected void onDraw(Canvas canvas) {
 42         super.onDraw(canvas);
 43
 44         /**
 45          * 先画圆,直接使用draw Circle
 46          * Draw the specified circle using the specified paint. If radius is <= 0,
 47          * then nothing will be drawn. The circle will be filled or framed based
 48          * on the Style in the paint.
 49          *
 50           public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
 51          * @param cx     The x-coordinate of the center of the cirle to be drawn
 52          * @param cy     The y-coordinate of the center of the cirle to be drawn
 53          * @param radius The radius of the cirle to be drawn
 54          * @param paint  The paint used to draw the circle
 55          */
 56
 57         int cx = getWidth()/2;
 58
 59         int radius =(int) (cx -mRoudWidth/2);//设置圆环的半径
 60         mPaint.setColor(mCirlceViewColor); //设置圆环的颜色
 61         mPaint.setStyle(Paint.Style.STROKE); //设置空心
 62         mPaint.setStrokeWidth(mRoudWidth); //设置圆环的宽度
 63         mPaint.setAntiAlias(true);  //消除锯齿
 64         canvas.drawCircle(cx,cx,radius,mPaint);
 65
 66
 67         /**
 68          * 有了外层的圆,现在绘制圆内的文字.使用drawText
 69
 70          * Draw the text, with origin at (x,y), using the specified paint. The
 71          * origin is interpreted based on the Align setting in the paint.
 72          *
 73          public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
 74          * @param text  The text to be drawn
 75          * @param x     The x-coordinate of the origin of the text being drawn
 76          * @param y     The y-coordinate of the baseline of the text being drawn
 77          * @param paint The paint used for the text (e.g. color, size, style)
 78          */
 79
 80         mPaint.setStrokeWidth(0);
 81         mPaint.setColor(mTextColor);
 82         mPaint.setTextSize(mTextSize);
 83         int percent = (int)(((float)mProgress/(float) max) *100);
 84         float textWidth  = mPaint.measureText (percent+"%");
 85         canvas.drawText(percent+"%",cx-textWidth/2,cx+mTextSize/2,mPaint);
 86
 87         /**
 88          * 画圆弧 ,画圆环的进度
 89          */
 90
 91         mPaint.setStrokeWidth(mRoudWidth); //设置圆环的宽度
 92         mPaint.setColor(mCirlceViweProgressColor);  //设置进度的颜色
 93         RectF oval = new RectF(cx - radius, cx - radius, cx
 94                 + radius, cx + radius);  //用于定义的圆弧的形状和大小的界限
 95
 96         switch (mStyle) {
 97             case STROKE:{
 98                 mPaint.setStyle(Paint.Style.STROKE);
 99                 canvas.drawArc(oval, 0, 360 * mProgress / max, false, mPaint);  //根据进度画圆弧
100                 break;
101             }
102             case FILL:{
103                 mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
104                 if(mProgress !=0)
105                     canvas.drawArc(oval, 0, 360 * mProgress / max, true, mPaint);  //根据进度画圆弧
106                 break;
107             }
108         }
109     }
110
111     public  void  setProgress(int progress){
112         this.mProgress = progress;
113     }
114
115     private  int getProgress(){
116         return this.mProgress;
117
118
119     }
120
121     private  void setMax(int max){
122         this.max = max;
123     }
124     private  int getMax(){
125         return  max;
126     }

说明几个点:

a.public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)的几个参数

cx和cy是圆心坐标,这个圆心坐标是相对当前绘制的圆心大小的,x和y,而不是屏幕左上角的。

radius是半径,paint是当前的所持有的画笔。

b.public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)

x:是文本内容最开始绘制的位置。在此view中可以看出应该是(中心点的x-文字宽度的/2)

y是基线的值。在view中应该是中心点的y+文字大小/2.

(3)监听事件的添加如下

上述只是个初步重写view的大致过程,实际项目中更复杂的自定义,设计的内容会则会更加庞大,比如滑动效果,比如事件分发,比如点击事件等等。接下来就从view 的基本知识点讲起,逐步完成一个复杂的自定义view。敬请期待,当然我的博客都比较简单,旨在给大家提供一个思路。后续我会把我的心得转成代码形式放到github上,希望能帮到大家。

时间: 2024-10-14 19:26:13

android 自定义控件 (二) 初步认识的相关文章

android 自定义控件二之仿QQ长按删除

自定义Dialog 1.先上个效果图: 虽然效果丑了点,但主要学习修改已有的控件,例如修改Dialog控件 2.一些基本的只是进行了解 Dialog: theme是Dialog的样式,常用样式为: <style name="MyDialogStyle" parent="@android:Theme.Dialog"> <item name="android:windowFrame">@null</item> &l

android自定义控件(二) 入门,继承View

转载请注明地址:http://blog.csdn.net/ethan_xue/article/details/7313788 ps: 可根据apidemo里LableView,list4,list6学习 文档在dev guide/Framework Topics/User Interface/Building Custom Components 自定义控件的步骤: 1 View的工作原理  2 编写View类  3 为View类增加属性  4 绘制屏幕  5 响应用户消息  6 自定义回调函数

玩转android自定义控件二——自定义索引栏listview

带索引栏的listview,在android开发非常普遍,方便用户进行字母索引,就像微信通讯录这样: 今天,我们就从零到一实现这个具有索引栏的listview. 怎么实现这个控件了,我们应当梳理出一个思路. ①首先应当将字母的索引栏继承与一个控件,通过ondraw方法将字母画出来. ②然后我们应该监听这个字母控件的ontouch事件,来判断用户到底是按了那个字母. ③就是实现这个索引栏与listview的联动,就是将listview滑动到按下字母的位置. 大体流程图如下: 有了前面铺垫,我们引出

老猪带你玩转android自定义控件二——自定义索引栏listview

带索引栏的listview,在android开发非常普遍,方便用户进行字母索引,就像微信通讯录这样: 今天,我们就从零到一实现这个具有索引栏的listview. 怎么实现这个控件了,我们应当梳理出一个思路. ①首先应当将字母的索引栏继承与一个控件,通过ondraw方法将字母画出来. ②然后我们应该监听这个字母控件的ontouch事件,来判断用户到底是按了那个字母. ③就是实现这个索引栏与listview的联动,就是将listview滑动到按下字母的位置. 大体流程图如下: 有了前面铺垫,我们引出

Android 自定义控件开发入门(二)

上一次我们讲了一堆实现自定义控件的理论基础,列举了View类一些可以重写的方法,我们对这些方法的重写是我们继承View类来派生自定义控件的关键 我通过一个最简单的例子给大家展示了这一个过程,无论是多么复杂的自定义控件,思路总是这样子的,但是因为我们仅仅重写了onDraw方法使得大家觉得怪怪的,作为一个控件,我们居然还要为了他的实现为其增加麻烦的监听,这就不能叫做控件了. 下面再给大家介绍一个经常重写的方法法:publicboolean onTouchEvent (MotionEvent even

android自定义控件(二)Canvas

一.重要方法 1.translate2.scale3.rotate 二.注意 1.明确顺序 canvas.rotate(45); canvas.drawRect(new Rect(50, 50, 100, 100), paint); 如果顺序调换,则没有旋转的效果 2.转换的时候,需要把转换的中心点移到shape自身的中心 int left=50,top=50, right = 100,bottom = 100; canvas.translate(right/2, bottom/2); canv

Android自定义控件的实现步骤(二)

前天已经写了一个关于自定义控件的实现步骤的博客,这个是附上代码的详细版本 首先,我们得新建一个attrs.xml的资源文件,在上面添加我们将要自定义控件的额外属性,就是自定义控件的自定义属性,具体代码如下: <resources> <declare-styleable name="TestView"> <attr name="textColor" format="color"></attr> <

从头学Android之Service初步二

在上一篇,我们学习了通过startService来启动Service,由于篇幅过长,所以这一篇是接上一篇的 二.bindService方法启动Service 先看bindSerivce(Intent service,ServiceConnection conn,int flags)函数 参数说明: service:通过该参数也就是Intent我们可以启动指定的Service conn:该参数是一个ServiceConnection对象,这个对角用于监听访问者(也可以说成是客户端)与Service

Android自定义控件View(二)

在前一篇博客中学习了Android自定义控件View的流程步骤和注意点,不了解的童鞋可以参考Android自定义控件View(一).这一节开始学习自定义控件View(二)之继承系统已有的控件.我们来自定义一个圆形ImageView. RoundImageView 随着Android UI效果越来越炫,很多系统自带的控件已经无法满足日常开发需求,比如很多应用的头像是圆形的,QQ头像就是圆形的图片.但是Android系统提供的控件当中没有一个是圆形的.那么怎么才能实现圆形头像效果呢?两种方法: 图片