美文网首页
简单自定义View复习

简单自定义View复习

作者: Ma_小鹏 | 来源:发表于2019-03-03 21:15 被阅读0次

    一、介绍

    为什么要自定义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文件(如果没有请自己新建)里面声明一个我们自定义的属性:

    <resources>
    
        <!--name为声明的"属性集合"名,可以随便取,但是最好是设置为跟我们的View一样的名称-->
        <declare-styleable name="MyView">
            <!--声明我们的属性,名称为default_size,取值类型为尺寸类型(dp,px等)-->
            <attr name="default_size" format="dimension" />
        </declare-styleable>
    </resources>
    

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

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:hc="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.hc.studyview.MyView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            hc:default_size="100dp" />
    
    </LinearLayout>
    
    

    注意:需要在根标签(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文件中的<declare-styleable>标签
           //即属性集合的标签,在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复习

          本文链接:https://www.haomeiwen.com/subject/mjomuqtx.html