简单自定义View复习

一、介绍

为什么要自定义view?明明Android系统内置的控件和布局已经很多了。但是,有了产品经理就不一样了,那些控件根本不够用呀,有些效果利用原生的控件无法实现,所以就需要自定义View.

二、自定义View

自定义View我们大部分时候只需重写两个函数:onMeasure()、onDraw()。onMeasure负责对当前View的尺寸进行测量,onDraw负责把当前这个View绘制出来。当然了,你还得写至少写2个构造函数:

 public CView(Context context) {
        super(context);
    }

    public CView(Context context, AttributeSet attrs) {
        super(context, attrs); 
    }
1.onMeasure

首先我们要测量View的宽高尺寸。可以通过重写父类的onMeasure方法,在onMeasure中完成自定义的尺寸。
下面是onMeasure方法的原型

     @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

解释一下:widthMeasureSpec和heightMeasureSpec包含了view的宽高信息,我们可以通过MeasureSpec中的方法获取view的宽高信息

int width = MeasureSpec.getSize(widthMeasureSpec); //获取自定义view的宽
int height = MeasureSpec.getSize(heightMeasureSpec);//获取自定义view的高
int widthMode = MeasureSpec.getMode(widthMeasureSpec);//获取自定义view的宽的测量模式
int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获取自定义view的高的测量模式

接下来我们看一下测量模式

测量模式 表示意思
UNSPECIFIED 父容器没有对当前View有任何限制,当前View可以任意取尺寸
EXACTLY 当前的尺寸就是当前View应该取的尺寸
AT_MOST 当前尺寸是当前View能取的最大尺寸

而上面的测量模式跟我们的布局时的wrap_content、match_parent以及写成固定的尺寸有什么对应关系呢?

match_parent--->EXACTLY。怎么理解呢?match_parent就是要利用父View给我们提供的所有剩余空间,而父View剩余空间是确定的,也就是这个测量模式的整数里面存放的尺寸。

wrap_content--->AT_MOST。怎么理解:就是我们想要将大小设置为包裹我们的view内容,那么尺寸大小就是父View给我们作为参考的尺寸,只要不超过这个尺寸就可以啦,具体尺寸就根据我们的需求去设定。

固定尺寸(如100dp)--->EXACTLY。用户自己指定了尺寸大小,我们就不用再去干涉了,当然是以指定的大小为主啦。
2.onDraw

首先我们先深入了解两个类Paint和Canvas。

2.1.paint
/**  
     * Paint类介绍  
     *   
     * Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色,  
     * 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法,  
     * 大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。         
     *   
     * 1.图形绘制  
     * setARGB(int a,int r,int g,int b);  
     * 设置绘制的颜色,a代表透明度,r,g,b代表颜色值。  
     *   
     * setAlpha(int a);  
     * 设置绘制图形的透明度。  
     *   
     * setColor(int color);  
     * 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。  
     *   
    * setAntiAlias(boolean aa);  
     * 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。  
     *   
     * setDither(boolean dither);  
     * 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰  
     *   
     * setFilterBitmap(boolean filter);  
     * 如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示  
     * 速度,本设置项依赖于dither和xfermode的设置  
     *   
     * setMaskFilter(MaskFilter maskfilter);  
     * 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等       *   
     * setColorFilter(ColorFilter colorfilter);  
     * 设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果  
     *   
     * setPathEffect(PathEffect effect);  
     * 设置绘制路径的效果,如点画线等  
     *   
     * setShader(Shader shader);  
     * 设置图像效果,使用Shader可以绘制出各种渐变效果  
     *  
     * setShadowLayer(float radius ,float dx,float dy,int color);  
     * 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色  
     *   
     * setStyle(Paint.Style style);  
     * 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE    Style.FILL: 实心   STROKE:空心   FILL_OR_STROKE:同时实心与空心

        
     *   
     * setStrokeCap(Paint.Cap cap);  
     * 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式  
     * Cap.ROUND,或方形样式Cap.SQUARE  
     *   
     * setSrokeJoin(Paint.Join join);  
     * 设置绘制时各图形的结合方式,如平滑效果等  
     *   
     * setStrokeWidth(float width);  
     * 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度  
     *   
     * setXfermode(Xfermode xfermode);  
     * 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果  
     *   
     * 2.文本绘制  
     * setFakeBoldText(boolean fakeBoldText);  
     * 模拟实现粗体文字,设置在小字体上效果会非常差  
     *   
     * setSubpixelText(boolean subpixelText);  
     * 设置该项为true,将有助于文本在LCD屏幕上的显示效果  
     *   
     * setTextAlign(Paint.Align align);  
     * 设置绘制文字的对齐方向  
     *   
   * setTextScaleX(float scaleX);  
    * 设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果  
     *   
     * setTextSize(float textSize);  
     * 设置绘制文字的字号大小  
     *   
     * setTextSkewX(float skewX);  
     * 设置斜体文字,skewX为倾斜弧度  
     *   
     * setTypeface(Typeface typeface);  
     * 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等  
     *   
     * setUnderlineText(boolean underlineText);  
     * 设置带有下划线的文字效果  
     *   
     * setStrikeThruText(boolean strikeThruText);  
     * 设置带有删除线的效果  
     *   
     */  

画笔这里, setStyle(Paint.Style style) 

这里通过http://www.cnblogs.com/aibuli/p/efef9d774df97c553a8a0c0c3495ba35.html?utm_source=tuicool&utm_medium=referral学习了paint的用法
2.2.Canvas

Canvas类简单理解就是表示一块画布,可以在上面画我们想画的东西

Canvas中的方法很多,Canvas可以绘制的对象有:

弧线(arcs) canvas.
填充颜色(argb和color)
Bitmap
圆(circle和oval)
点(point)
线(line)
矩形(Rect)
图片(Picture)
圆角矩形 (RoundRect)
文本(text)
顶点(Vertices)
路径(path)

canvas.save():把当前的绘制的图像保存起来,让后续的操作相当于是在一个新的图层上的操作。
canvas.restore(); 把当前画布返回(调整)到上一个save()状态之前
canvas.translate(dx, dy); //把当前画布的原点移到(dx,dy),后面的操作都以(dx,dy)作为参照点,默认原点为(0,0)

canvas.scale(x,y);扩大。x为水平方向的放大倍数,y为竖直方向的放大倍数
canvas.rotate(angel):旋转.angle指旋转的角度,顺时针旋转。
canvas.transform():切变。所谓切变,其实就是把图像的顶部或底部推到一边。
canvas.saveLayer(bounds, paint, saveFlags);

学会了这两个方法就可以通过onDrow(Canvas canvas)绘制我们想要的图形了

  protected void onDraw(Canvas canvas) {              //重载onDraw方法  
            // TODO Auto-generated method stub  
            super.onDraw(canvas);  
            paint.setColor(Color.RED);                      //设置画笔颜色  
            paint.setStyle(Style.FILL);   //设置画笔为空心     如果将这里改为Style.STROKE  这个图中的实线圆柱体就变成了空心的圆柱体
            canvas.drawColor(Color.WHITE);  
            canvas.drawLine(50, 50, 450, 50, paint);        //绘制直线  
            canvas.drawRect(100, 100, 200, 600, paint);     //绘制矩形  
            canvas.drawRect(300, 100, 400, 600, paint);     //绘制矩形  
        } 
        

三、自定义ViewGroup

我们都知道ViewGroup是个View容器,它装纳child View并且负责把child View放入指定的位置

1.首先,我们得知道各个子View的大小,只有先知道子View的大小,我们才知道当前的ViewGroup该设置为多大去容纳它们。


2.根据子View的大小,以及我们的ViewGroup要实现的功能,决定出ViewGroup的大小


3.ViewGroup和子View的大小算出来了之后,接下来就是去摆放了吧,具体怎么去摆放呢?这得根据你定制的需求去摆放了,比如,你想让子View按照垂直顺序一个挨着一个放,或者是按照先后顺序一个叠一个去放,这是你自己决定的。


4.已经知道怎么去摆放还不行啊,决定了怎么摆放就是相当于把已有的空间"分割"成大大小小的空间,每个空间对应一个子View,我们接下来就是把子View对号入座了,把它们放进它们该放的地方去。

首先重写onMeasure,实现测量子View大小以及设定ViewGroup的大小;

上面的onMeasure将子View测量好了,以及把自己的尺寸也设置好了,接下来我们去摆放子View吧~

 @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    //此方法中写控件的摆放逻辑
        int count = getChildCount();
        //记录当前的高度位置
        int curHeight = t;
        //将子View逐个摆放
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            int height = child.getMeasuredHeight();
            int width = child.getMeasuredWidth();
            //摆放子View,参数分别是子View矩形区域的左、上、右、下边
            child.layout(l, curHeight, l + width, curHeight + height);
            curHeight += height;
        }
    }

然后就可以去布局文件中使用了

四、自定义布局属性

如果有些属性我们希望由用户指定,只有当用户不指定的时候才用我们硬编码的值,比如上面的默认尺寸,我们想要由用户自己在布局文件里面指定该怎么做呢?那当然是通我们自定属性,让用户用我们定义的属性啦~
首先我们需要在res/values/styles.xml文件(如果没有请自己新建)里面声明一个我们自定义的属性:



    
    
        
        
    

接下来就是在布局文件用上我们的自定义的属性啦~




    



注意:需要在根标签(LinearLayout)里面设定命名空间,命名空间名称可以随便取,比如hc,命名空间后面取得值是固定的:"http://schemas.android.com/apk/res-auto"
最后就是在我们的自定义的View里面把我们自定义的属性的值取出来,在构造函数中,还记得有个AttributeSet属性吗?就是靠它帮我们把布局里面的属性取出来:

private int defalutSize;

 public CView(Context context, AttributeSet attrs) {
     super(context, attrs);
     //第二个参数就是我们在styles.xml文件中的标签
       //即属性集合的标签,在R文件中名称为R.styleable+name
       TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView);
       
       //第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称
       //第二个参数为,如果没有设置这个属性,则设置的默认的值
       defalutSize = a.getDimensionPixelSize(R.styleable.MyView_default_size, 100);
       
       //最后记得将TypedArray对象回收
       a.recycle();
  }

自定义view暂时记录,还需深入学习。。。

写在最后: 本系列的文章旨在学习过程中的总结,如果对你也有帮助,荣幸之至。

你可能感兴趣的:(简单自定义View复习)