Android自定义组件03

    最近几天总是到处跑,办社保,搞体检准备入职,帮助老婆改论文,现在终于有时间静下心来继续进行Android自定义组件的学习,今天就简单的介绍一下自定义组件的最后一种方式:完全自定义组件,只有当现有的组件完全不符合项目的需求时才使用这种方式,我认为这也是最复杂的一种方式,因为不但要求我们具有开发的知识,也需要一定的审美和美工功底才能可能做出美观的组件。

    原生的Button、EditText,ImageView等组件是继承自View的,而各种Layout则是继承自ViewGroup的,这里我只讲一下View组件。这里我引用网络上的一篇文章进行介绍(http://www.apkbus.com/forum.php?mod=viewthread&tid=147117&highlight=%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6)。

    首先定义类继承自View,添加构造方法并重写基类的一些方法,例如onDraw(), onMeasure()等,此外还可能自定义一些组件的属性,属性中对颜色、字体、字体颜色进行设置,自定义的属性在values目录的attrs.xml中。文章中以一个圆形进度条为例进行了介绍。

    1. 在values下面新建一个attrs.xml,现在里面定义我们的属性,不同的属性对应不同的format,属性对应的format可以参考http://blog.csdn.net/pgalxx/article/details/6766677,介绍的还是比较详细,接下来我贴上我在自定义这个进度条所用到的属性

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <declare-styleable name="RoundProgressBar">  
        <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. 属性定义完成后接下来就是怎么获取属性和代码的编写了,我们需要在构造方法中获取我们自己定义的相关属性,我们先调用context.obtainStyledAttributes(attrs,R.styleable.RoundProgressBar)来获取TypedArray,然后从TypedArray获取我们定义的属性,例如

<span style="font-family:Comic Sans MS;">roundColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundColor, Color.RED);
roundProgressColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundProgressColor, Color.GREEN);
textColor = mTypedArray.getColor(R.styleable.RoundProgressBar_textColor, Color.GREEN);
textSize = mTypedArray.getDimension(R.styleable.RoundProgressBar_textSize, 15);
roundWidth = mTypedArray.getDimension(R.styleable.RoundProgressBar_roundWidth, 5);
max = mTypedArray.getInteger(R.styleable.RoundProgressBar_max, 100);
textIsDisplayable = mTypedArray.getBoolean(R.styleable.RoundProgressBar_textIsDisplayable, true);
style = mTypedArray.getInt(R.styleable.RoundProgressBar_style, 0);</span>
上面的代码中,如roundColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundColor, Color.RED); getColor方法的第一个参数是我们在XML文件中定义的颜色,如果我们没有给我们自定义的View定义颜色,他就会使用第二个参数中的默认值,即Color.RED
3.为了方便大家理解,我将自定义View的全部代码贴出来,里面的代码我也有详细的注释

  1. package com.apkbus.myprogressbar;

  2. import com.apkbus.myprogress.R;

  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.graphics.Canvas;
  6. import android.graphics.Color;
  7. import android.graphics.Paint;
  8. import android.graphics.RectF;
  9. import android.graphics.Typeface;
  10. import android.util.AttributeSet;
  11. import android.util.Log;
  12. import android.view.View;


  13. /**
  14. *仿iphone带进度的进度条,线程安全的View,可直接在线程中更新进度
  15. * @author David Wang
  16. * @version [版本号, 2013年11月6日]
  17. * [url=home.php?mod=space&uid=66080]@SINCE[/url] [产品/模块版本]
  18. */
  19. public class RoundProgressBar extends View {
  20.         /**
  21.          * 画笔对象的引用
  22.          */
  23.         private Paint paint;
  24.         
  25.         /**
  26.          * 圆环的颜色
  27.          */
  28.         private int roundColor;
  29.         
  30.         /**
  31.          * 圆环进度的颜色
  32.          */
  33.         private int roundProgressColor;
  34.         
  35.         /**
  36.          * 中间进度百分比的字符串的颜色
  37.          */
  38.         private int textColor;
  39.         
  40.         /**
  41.          *中间进度百分比的字符串的字体
  42.          */
  43.         private float textSize;
  44.         
  45.         /**
  46.          * 圆环的宽度
  47.          */
  48.         private float roundWidth;
  49.         
  50.         /**
  51.          * 最大进度
  52.          */
  53.         private int max;
  54.         
  55.         /**
  56.      * 当前进度
  57.      */
  58.     private int progress;
  59.     /**
  60.      * 是否显示中间的进度
  61.      */
  62.     private boolean textIsDisplayable;
  63.     
  64.     /**
  65.      * 进度的风格,实心或者空心
  66.      */
  67.     private int style;
  68.         
  69.         public static final int STROKE = 0;
  70.         public static final int FILL = 1;
  71.         
  72.         public RoundProgressBar(Context context) {
  73.                 this(context, null);
  74.         }

  75.         public RoundProgressBar(Context context, AttributeSet attrs) {
  76.                 this(context, attrs, 0);
  77.         }
  78.         
  79.         public RoundProgressBar(Context context, AttributeSet attrs, int defStyle) {
  80.                 super(context, attrs, defStyle);
  81.                 
  82.                 paint = new Paint();

  83.                 
  84.                 TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
  85.                                 R.styleable.RoundProgressBar);
  86.                 roundColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundColor, Color.RED);
  87.                 roundProgressColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundProgressColor, Color.GREEN);
  88.                 textColor = mTypedArray.getColor(R.styleable.RoundProgressBar_textColor, Color.GREEN);
  89.                 textSize = mTypedArray.getDimension(R.styleable.RoundProgressBar_textSize, 15);
  90.                 roundWidth = mTypedArray.getDimension(R.styleable.RoundProgressBar_roundWidth, 5);
  91.                 max = mTypedArray.getInteger(R.styleable.RoundProgressBar_max, 100);
  92.                 textIsDisplayable = mTypedArray.getBoolean(R.styleable.RoundProgressBar_textIsDisplayable, true);
  93.                 style = mTypedArray.getInt(R.styleable.RoundProgressBar_style, 0);
  94.                 
  95.                 mTypedArray.recycle();
  96.         }
  97.         

  98.         @Override
  99.         protected void onDraw(Canvas canvas) {
  100.                 super.onDraw(canvas);
  101.                 
  102.               /**
  103.          * 画最外层的大圆环
  104.          */
  105.         int centre = getWidth()/2; //获取圆心的x坐标
  106.         int radius = (int) (centre - roundWidth/2); //圆环的半径
  107.         paint.setColor(roundColor); //设置圆环的颜色
  108.         paint.setStyle(Paint.Style.STROKE); //设置空心
  109.         paint.setStrokeWidth(roundWidth); //设置圆环的宽度
  110.         paint.setAntiAlias(true);  //消除锯齿 
  111.         canvas.drawCircle(centre, centre, radius, paint); //画出圆环
  112.         
  113.         Log.e("log", centre + "");
  114.         
  115.         /**
  116.          * 画进度百分比
  117.          */
  118.         paint.setStrokeWidth(0); 
  119.         paint.setColor(textColor);
  120.         paint.setTextSize(textSize);
  121.         paint.setTypeface(Typeface.DEFAULT_BOLD); //设置字体
  122.         int percent = (int)(((float)progress / (float)max) * 100);  //中间的进度百分比,先转换成float在进行除法运算,不然都为0
  123.         float textWidth = paint.measureText(percent + "%");   //测量字体宽度,我们需要根据字体的宽度设置在圆环中间
  124.         
  125.         if(textIsDisplayable && percent != 0 && style == STROKE){
  126.             canvas.drawText(percent + "%", centre - textWidth / 2, centre + textSize/2, paint); //画出进度百分比
  127.                 }
  128.                 
  129.                 
  130.                   /**
  131.          * 画圆弧 ,画圆环的进度
  132.          */
  133.         
  134.         //设置进度是实心还是空心
  135.         paint.setStrokeWidth(roundWidth); //设置圆环的宽度
  136.         paint.setColor(roundProgressColor);  //设置进度的颜色
  137.         RectF oval = new RectF(centre - radius, centre - radius, centre
  138.                 + radius, centre + radius);  //用于定义的圆弧的形状和大小的界限
  139.         
  140.         switch (style) {
  141.         case STROKE:{
  142.             paint.setStyle(Paint.Style.STROKE);
  143.             canvas.drawArc(oval, 0, 360 * progress / max, false, paint);  //根据进度画圆弧
  144.             break;
  145.         }
  146.         case FILL:{
  147.             paint.setStyle(Paint.Style.FILL_AND_STROKE);
  148.             if(progress !=0)
  149.                 canvas.drawArc(oval, 0, 360 * progress / max, true, paint);  //根据进度画圆弧
  150.             break;
  151.         }
  152.         }
  153.         
  154.     }
  155.     
  156.     
  157.     public synchronized int getMax() {
  158.         return max;
  159.     }

  160.     /**
  161.      * 设置进度的最大值
  162.      * @param max
  163.      */
  164.     public synchronized void setMax(int max) {
  165.         if(max < 0){
  166.             throw new IllegalArgumentException("max not less than 0");
  167.         }
  168.         this.max = max;
  169.     }

  170.     /**
  171.      * 获取进度.需要同步
  172.      * @return
  173.      */
  174.     public synchronized int getProgress() {
  175.         return progress;
  176.     }

  177.     /**
  178.      * 设置进度,此为线程安全控件,由于考虑多线的问题,需要同步
  179.      * 刷新界面调用postInvalidate()能在非UI线程刷新
  180.      * @param progress
  181.      */
  182.         public synchronized void setProgress(int progress) {
  183.                 if(progress < 0){
  184.                         throw new IllegalArgumentException("progress not less than 0");
  185.                 }
  186.                 if(progress > max){
  187.                         progress = max;
  188.                 }
  189.                 if(progress <= max){
  190.                         this.progress = progress;
  191.                         postInvalidate();
  192.                 }
  193.                 
  194.         }
  195.         
  196.         
  197.         public int getCricleColor() {
  198.                 return roundColor;
  199.         }

  200.         public void setCricleColor(int cricleColor) {
  201.                 this.roundColor = cricleColor;
  202.         }

  203.         public int getCricleProgressColor() {
  204.                 return roundProgressColor;
  205.         }

  206.         public void setCricleProgressColor(int cricleProgressColor) {
  207.                 this.roundProgressColor = cricleProgressColor;
  208.         }

  209.         public int getTextColor() {
  210.                 return textColor;
  211.         }

  212.         public void setTextColor(int textColor) {
  213.                 this.textColor = textColor;
  214.         }

  215.         public float getTextSize() {
  216.                 return textSize;
  217.         }

  218.         public void setTextSize(float textSize) {
  219.                 this.textSize = textSize;
  220.         }

  221.         public float getRoundWidth() {
  222.                 return roundWidth;
  223.         }

  224.         public void setRoundWidth(float roundWidth) {
  225.                 this.roundWidth = roundWidth;
  226.         }



  227. }
    4.通过上面几步我们就实现了自定义View,和自定义View的属性,当然使用过程中还是有一点变化,我们必须在界面布局的最顶层加上 xmlns:android_custom="http://schemas.android.com/apk/res/com.example.circlepregress"这个即命名空间, 什么意思呢?对于android系统控件我们定义其控件属性是用android:XXX="XXXXXXX",而我们自己定义的就用android_custom:XXX = "XXXXXX"

通过上面这两步我们就能自己定义属性了,我贴出自定义View在XML中使用情况

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:android_custom="http://schemas.android.com/apk/res/com.apkbus.myprogress"
  3.     xmlns:tools="http://schemas.android.com/tools"
  4.     android:layout_width="match_parent"
  5.     android:layout_height="match_parent" >


  6.     <com.apkbus.myprogressbar.RoundProgressBar
  7.         android:id="@+id/roundProgressBar2"
  8.         android:layout_width="80dip"
  9.         android:layout_height="80dip"
  10.         android:layout_alignLeft="@+id/roundProgressBar1"
  11.         android:layout_alignParentBottom="true"
  12.         android:layout_marginBottom="78dp"
  13.         
  14.         
  15.         android_custom:roundColor="#D1D1D1"
  16.         android_custom:roundProgressColor="@android:color/black"
  17.         android_custom:textColor="#9A32CD"
  18.         android_custom:roundWidth="10dip"
  19.         android_custom:textSize="18sp" />

  20.     <com.apkbus.myprogressbar.RoundProgressBar
  21.         android:id="@+id/roundProgressBar4"
  22.         android_custom:style="FILL"
  23.         android:layout_width="80dip"
  24.         android:layout_height="80dip"
  25.         android:layout_alignParentRight="true"
  26.         android:layout_alignTop="@+id/roundProgressBar1"
  27.         android:layout_marginRight="32dp"
  28.         android_custom:roundWidth="1dip"
  29.         android_custom:roundProgressColor="#C2C2C2" />

  30.     <com.apkbus.myprogressbar.RoundProgressBar
  31.         android:id="@+id/roundProgressBar3"
  32.         android:layout_width="80dip"
  33.         android:layout_height="80dip"
  34.         android:layout_alignLeft="@+id/roundProgressBar4"
  35.         android:layout_alignTop="@+id/roundProgressBar2"
  36.         android_custom:roundColor="#C6E2FF"
  37.         android_custom:roundWidth="10dip"
  38.         android_custom:roundProgressColor="#CD3333"
  39.         android_custom:textIsDisplayable="false" />

  40.     <com.apkbus.myprogressbar.RoundProgressBar
  41.         android:id="@+id/roundProgressBar5"
  42.         android:layout_width="50dip"
  43.         android:layout_height="50dip"
  44.         android:layout_below="@+id/roundProgressBar1"
  45.         android:layout_marginLeft="22dp"
  46.         android:layout_toRightOf="@+id/roundProgressBar1" />
  47.     
  48.     
  49.         <Button
  50.         android:id="@+id/button1"
  51.         android:layout_width="wrap_content"
  52.         android:layout_height="wrap_content"
  53.         android:layout_alignParentLeft="true"
  54.         android:layout_alignParentRight="true"
  55.         android:layout_alignParentTop="true"
  56.         android:text="Button" />

  57.     <com.apkbus.myprogressbar.RoundProgressBar
  58.         android:id="@+id/roundProgressBar1"
  59.         android:layout_width="80dip"
  60.         android:layout_height="80dip"
  61.         android:layout_alignParentLeft="true"
  62.         android:layout_below="@+id/button1"
  63.         android:layout_marginLeft="16dp"
  64.         android:layout_marginTop="40dp" />
  65.     

  66. </RelativeLayout>

    5. 最终的效果图


    6. 在上面的例子当中并没有重写基类的onMeasure()方法,实际上,这个方法也是十分重要的,对它的具体说明请参考 http://blog.csdn.net/pi9nc/article/details/18764863,说的很详细。


今天为止,简单的介绍了三种自定义组件的方式,接下来就需要在实践中体会这些自定义组件方法了,我想今后的日子里会时不时的进行一些实例的制作,希望能够熟练的掌握!





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值