一:自定义view概述

1、自定义view

自定义view的一般步奏是:

1、 继承view、
2、 重写构造方法、
3、 重写onMeasure方法、
4、 重写onDraw方法。
5、 也可以使用自定义属性:

使用的优先级
直接在XML中定义>style定义>由defStyleAttr和defStyleRes指定的默认值>直接在Theme中指定的值
1、通过为自定义View添加属性
2、在xml中为相应的属性指定属性值

  • 在layout布局文件中
  • 设置style并在style中设置属性
  • 在theme中指定在当前Application或Activity中属性的默认值

3、在运行时(一般为构造函数)获取属性值
我们要获取的属性值都是通过这个函数返回的TypedArray获得的

TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes){}

四个参数:

  • set:属性值的集合
  • attrs:属性资源的ID
  • defStyleAttr:当前Theme中的一个attribute,是一个指向style的一个引用,当在layout xml中和style中都没有为View指定属性时,会从Theme中这个attribute指向的Style中查找相应的属性值。如果这个参数传入0表示不在Theme中搜索默认值
  • defStyleRes:指向一个Style的资源,但是仅在defStyleAttr为0或defStyleAttr不为0但Theme中没有为defStyleAttr属性赋值时起作用。也是指定自定义view的默认属性。

4、将获取到的属性值应用到View

1、构造函数
  • 一个参数 (Context context):
    直接new一个view的时候调用

  • 两个参数 (Context context, AttributeSet attrs):
    在layout布局文件中使用的时候会调用,关于它的所有属性(包括自定义属性,还有在这个layout中通过style为view添加的属性)都会包含在attrs中传递进来。

  • 三个参数 (Context context, AttributeSet attrs, int defStyleAttr):
    不会自动调用,一般在两个参数的构造函数中手动调用,重写这个构造函数的意义是,为自定义view提供一些默认的属性。
    其中defStyleAttr,是默认的属性,它的取值是,是在Theme中style的值。

  • 四个参数 (Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes):
    minSdkVersion>=21才可以使用

一般来说,你只需实现前两个

3、测量view的大小(onMeasure)

当宽高设置为match_parent、wrap_content时,系统帮我们测量的宽高都是match_parent的长度,所以在设置了wrap_content时,需要手动进行测量(重写onMeasure())。

在重新onMeasure方法时,需要注意 MeasureSpec 。 MeasureSpec封装了父布局传递给子布局的布局要求。一个MeasureSpec由大小和模式组成。

它有三种模式:
EXACTLY(精确): 父控件决定子控件的确切大小,子控件将被限定在给定的边界里而忽略它本身大小
AT_MOST(最多): 子控件至多达到指定大小的值,
UNSPECIFIED(未指定): 父控件不对子控件施加任何束缚,子元素可以得到任意想要的大小

最简单的映射关系是:

  • wrap_parent -> MeasureSpec.AT_MOST
  • match_parent -> MeasureSpec.EXACTLY
  • 具体值 -> MeasureSpec.EXACTLY
4、基本实现

自定义属性 定义(res/values/attrs.xml 下)



    
        
    
    

使用,(res/values/style.xml 下)



    
    

    

    


重写构造方法、onMeasure方法



public class SelfView extends View {
    private final int DEFAULT_COLOR= Color.RED;
    private final int DEFAULT_SIZE= 320;
    private int color;
    private int length;

    /**
     * 一般在直接New一个View的时候调用。
     */
    public SelfView(Context context) {
        this(context,null);
    }

    /**
     * 一般在layout文件中使用的时候会调用,关于它的所有属性(包括自定义属性)都会包含在attrs中传递进来。
     */
    public SelfView(Context context, AttributeSet attrs) {
        this(context, attrs,R.attr.defaultStyle);
    }

    public SelfView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //使用TypedArray 读取属性值
        TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.SelfView,defStyleAttr,R.style.defaultResStyle);
        color=typedArray.getColor(R.styleable.SelfView_innerColor,DEFAULT_COLOR);
        typedArray.recycle();
        init(context);
    }

/*    public SelfView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }*/
}

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getMeasureSpec(widthMeasureSpec),getMeasureSpec(heightMeasureSpec));
    }

    private int getMeasureSpec(int widthMeasureSpec) {
        int specMode=MeasureSpec.getMode(widthMeasureSpec);
        int specSize=MeasureSpec.getSize(widthMeasureSpec);
        int result=length;
        switch (specMode){
            case MeasureSpec.EXACTLY:
                result=specSize;
                break;
            case MeasureSpec.AT_MOST:
                result=Math.min(specSize,length);
                break;
            case MeasureSpec.UNSPECIFIED:
                break;
        }
        return result;
    }
}

参考:安卓自定义View进阶-分类与流程、深入理解View的构造函数、理解View的构造函数

2、绘图有关的类

1、Paint

Paint类用于定义绘图时的参数,主要包含颜色、文本、图形样式、位图模式、滤镜等几个方面。
1、图形样式包含绘制的图形是空心样式还是实心样式,同时还能指定落笔和收笔时的笔触效果。
Paint类与图形样式相关的方法有:

  • setStyle(Paint.Style style):设置绘制的图形是空心样式还是实心样式,默认为实心样式。style 的可选值有:

  • FILL 实心样式

  • FILL_AND_STROKE 同时使用实心样 式和空心样式

  • STROKE 空心样式,绘制时只有线条而无填充效果

  • setStrokeJoin(Paint.Join join):当绘图样式为 STROKE、FILL_AND_STROKE 时,该方法用于指定线条连接处的拐角样式,能使绘制的图形 更加平滑。默认值为 MITER。可选值如下:

  • MITER

  • BEVEL

  • ROUND

一:自定义view概述_第1张图片
MITER

一:自定义view概述_第2张图片
ROUND

一:自定义view概述_第3张图片
BEVEL(FILL_AND_STROKE )

一:自定义view概述_第4张图片
BEVEL(STROKE )

一:自定义view概述_第5张图片
BEVEL(FILL)
  • setStrokeCap(Paint.Cap cap):该方法用于设置落笔时的样式,控制我们的画笔在离开画板时留下的最后一点图形,可选值如下:
    BUTT,ROUND, SQUARE

参考:Android自定义组件开发详解 pdf

2、弧度和角度

角度=(弧度/Math.PI)*180
弧度转化为角度:Math.toDegrees()
角度转化为弧度:Math.toRadians()
反正切函數:Math.atan(),返回值是弧度
正切函数:Math.tan(),參數是弧度。

3、Canvas 的操作

⑴位移(translate)

 translate(float dx, float dy)

translate是坐标系的移动,是基于当前位置的移动,而不是每次基于屏幕左上角的(0,0)点移动

⑵缩放(scale)

scale(float sx, float sy)
scale(float sx, float sy, float px, float py)

缩放的中心默认为坐标原点 ,当缩放比例为负数的时候会根据缩放中心轴进行翻转

⑶旋转(rotate)

rotate(float degrees)
rotate(float degrees, float px, float py)

默认的旋转中心依旧是坐标原点。

所有的画布操作都只影响后续的绘制,对之前已经绘制过的内容没有影响。

(4)、保存save、回滚restore

大多数情况下只需要记住下面的步骤就可以了:

   save();      //保存状态
   ...          //具体操作
   restore();   //回滚到之前的状态

画布的操作是不可逆的,而且很多画布操作会影响后续的步骤,所以会对画布的一些状态进行保存和回滚。

通过旋转画布,可以得到如下效果:

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.GRAY);
        canvas.save();
        canvas.translate(width/2,height/2);
        canvas.drawCircle(0,0,outerRadius,linePaint);
        if(desBitmap!=null && !desBitmap.isRecycled()){
            for (int i=0;i<6;i++){
                canvas.drawBitmap(desBitmap,-innerRadius,outerRadius-innerRadius,null);
                canvas.rotate(60);
            }
        }
        canvas.restore();
    }
一:自定义view概述_第6张图片
旋转画布

参考:安卓自定义View进阶-Canvas之画布操作

4、图层

  • canvas默认就有一个layer,当我们平时调用canvas的各种drawXXX()方法时,其实是把所有的东西都绘制到canvas这个默认的layer上面。

  • 我们还可以通过canvas.saveLayer()新建一个layer,新建的layer放置在canvas默认layer的上部,当我们执行了canvas.saveLayer()之后,我们所有的绘制操作都绘制到了我们新建的layer上

  • 用canvas.saveLayer()方法产生的layer所有像素的ARGB值都是(0,0,0,0),即canvas.saveLayer()方法产生的layer初始时时完全透明的。

  • canvas.saveLayer()方法会返回一个int值,用于表示layer的ID,在我们对这个新layer绘制完成后可以通过调用canvas.restoreToCount(layer)或者canvas.restore()把这个layer绘制到canvas默认的layer上去,这样就完成了一个layer的绘制工作。

参考:Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解



你可能感兴趣的:(一:自定义view概述)