自定义View以及自定义属性

Posted 安辉就是我

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义View以及自定义属性相关的知识,希望对你有一定的参考价值。

转载请注明出处:http://blog.csdn.net/lowprofile_coding/article/details/53558247

1.最终效果图.

效果图看起来蛮简单的,就是画一个圆的过程。

2.实现步骤

1).重写onDraw方法

@Override
    protected void onDraw(Canvas canvas)
        Log.i("MyView","onDraw");
        if(null==myThread)
            myThread=new MyThread();
            myThread.start();
        else
            //第一个参数是RectF   左上的x y坐标   右下的x y坐标
            //第二个参数是 弧形的开始角度
            //第三个参数是 弧形的结束角度
            //第四个参数是 true:画扇形   false:画弧线
            //第五个参数是 画笔
            canvas.drawArc(rectF, 0, sweepAngle, true, paint);
        
    

    //开启一个子线程绘制ui
    private class MyThread extends Thread
        @Override
        public void run() 
            while(running)
                logic();
                postInvalidate();//重新绘制,会调用onDraw
                try 
                    Thread.sleep(200);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        
    

    protected void logic() 
        sweepAngle+=sweepAngleAdd;//每次增加弧度

        //随机设置画笔的颜色
        int r=random.nextInt(255);
        int g=random.nextInt(255);
        int b=random.nextInt(255);
        paint.setARGB(255, r, g, b);

        if(sweepAngle>=360)//如果弧度大于360°  从头开始
            sweepAngle=0;
        
    

当第一次执行onDraw方法的时候,启动了一个线程,在线程里面改变我们画圆的参数,然后调用postInvalidate方法,这个方法的作用是重新绘制,继续调用onDraw。然后第二次调用onDraw方法的时候,线程不为空,根据角度画扇形。

2).重写onMeasure方法

讲到View的measure测量,会涉及到View的一个静态内部类MeasureSpec,MeasureSpec类封装了父View传递给子View的布局(layout)要求,每个MeasureSpec实例代表宽度或者高度(只能是其一)要求。MeasureSpec字面意思是测量规格或者测量属性,在measure方法中有两个参数widthMeasureSpec和heightMeasureSpec,如果使用widthMeasureSpec,我们就可以通过MeasureSpec计算出宽的模式Mode和宽度的实际值。

测量的模式分以下三种:
EXACTLY 精确值模式
当我们的View的layout_width 或者 layout_height属性设置为具体的数值(例: android:layout_width = “100dp”)或者指定为 “match_parent” 时(这时候系统会自动分配为父布局的大小)
就是使用的这个模式

AT_MOST 最大值模式
当我们的View的layout_width 或者 layout_height属性设置为 “wrap_content”

UNSPECIFIED
表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到

我们为什么要重写onMeasure方法?

给大家看一下View在布局里面的属性:

    <com.ansen.view.MyView
        android:background="@android:color/holo_green_dark"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        custom:sweepAngleAdd="10"/>

运行截图如下:

我明明设置了包裹内容,但是背景颜色的宽高跟屏幕一样大。因为我们不重写的话,View在布局文件中设置android:layout_width=”wrap_content”时会起到match_parent的效果.

接下来我们重写onMeasure方法:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 获得它的父容器为它设置的测量模式和大小
        int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        if(modeWidth==MeasureSpec.EXACTLY)//指定宽度/match_parent

        else if(modeWidth==MeasureSpec.AT_MOST)//指定为WRAP_CONTENT时
            measuredWidth=380;
        

        if(modeHeight==MeasureSpec.EXACTLY)//指定高度/match_parent

        else if(modeHeight==MeasureSpec.AT_MOST)//指定为WRAP_CONTENT时
            measuredHeight=380;
        

        setMeasuredDimension(measuredWidth,measuredHeight);
        Log.i("MyView","onMeasure");
    

我们通过MeasureSpec获得它的父容器为它设置的测量模式和大小,根据不同的宽高模式做处理,我们这边对模式为AT_MOST的时候做了处理,设置高度380.最后调用setMeasuredDimension方法,把处理过的实际宽高传递进去。

现在我们可以看到宽高就正常了。。。。

3).自定义属性.

首先在res/values下新建一个attrs.xml文件。内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="customStyleView">
        <attr name="sweepAngleAdd" format="integer"/>
    </declare-styleable>
</resources>

需要用包围所有属性。其中name为该属性集的名字,主要用途是标识该属性集。
属性的类型也有很多: string,integer,dimension , reference , color , enum.

在布局xml文件中引用该属性:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.ansen.view.MyView
        android:background="@android:color/holo_green_dark"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        custom:sweepAngleAdd="10"/>
</RelativeLayout>

在自定义View的构造方法里面获取该自定义属性的值。我们这边用来控制扇形的弧度增加,每次画扇形的时候,弧度的增加数量如果有变化就不需要修改java代码了,只要在xml中修改一下就行。

    public MyView(Context context) 
        this(context,null);
    

    public MyView(Context context,AttributeSet attrs) 
        super(context, attrs);
        init(context,attrs);
    

    //初始化
    private void  init(Context context,AttributeSet attrs)
        //获取自定义属性的值
        TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.customStyleView);
        sweepAngleAdd=typedArray.getInt(R.styleable.customStyleView_sweepAngleAdd,0);
        typedArray.recycle();

        paint=new Paint();
        paint.setTextSize(60);
    

学了这么多,想不想下载源码自己运行一下呢。。。点击我下载源码

如果你想第一时间看我们的后期文章,扫码关注公众号,每周不定期推送Android开发实战教程文章,你还等什么,赶快关注吧,学好技术,出任ceo,赢取白富美。。。。

      Android开发666 - 安卓开发技术分享
            扫描二维码加关注

以上是关于自定义View以及自定义属性的主要内容,如果未能解决你的问题,请参考以下文章

Android自定义View 基础篇

Android自定义View 基础篇

android自定义View: 饼状图绘制

android自定义View: 饼状图绘制

android自定义View: 饼状图绘制

Android技术分享|自定义View实现Material Design的Loading效果