美文网首页
自定义View简单使用

自定义View简单使用

作者: 磊啊_ | 来源:发表于2018-04-28 03:30 被阅读0次

      1.创建一个包:weight

      2.创建一个类:ClicleView  extends  View

      四个构造方法

              Context context  上下文引用

          AttributeSet attrs    自定义属性集

                  int defStyleAttr  默认样式

          int defStyleRes  默认资源引用

      public SubView(Context context) {

              super(context);

          }

      调用自身构造函数

      public SubView(Context context) {

              this(context, null);

          }

     

          //AttributeSet attrs    自定义属性集

          //自定义属性从XML文件中进行获取

          public SubView(Context context, @Nullable AttributeSet attrs) {

              this(context, attrs, 0);

          }

     

          public SubView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

              super(context, attrs, defStyleAttr);

          }

       

        defStyleAttr Android默认样式

       

          xmlns:android="http://schemas.android.com/apk/res/android"  Android原生的命名空间集(删除,Android取值会取不到)

          xmlns:day="http://schemas.android.com/apk/res-auto"  Android命名空间

       

       

        自定义View:

      1、构造函数

      2、设置XML中的命名空间

      3、设置自定义属性

      1.创建自定义属性:(在res/values/attrs  创建一个属性集)

      2.声明自定义属性:

      <resources>

      <declare-styleable name="SubView">

      <attr name="myText" format="string"></attr>

      <attr name="myColor" format="color"></attr>

      <attr name="myTextSize" format="dimension"></attr>

      </declare-styleable>

      </resources>

      通过attr标签,声明属性值,并且规定接受类型

      3.自定义属性取值:

      ①从res/values/attrs.xml中去取属性

      ②需要对属性声明名称

      4.获取自定义属性集

      TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.CircleView)

      遍历自定义属性集,获取自定义属性(for循环遍历)

      获取完毕后对自定义属性集中的下标内容进行获取

      typedArray.getIndex(i);

      4、对自定义控件进行设置

      1.onLayout 控件位置的摆放(根据控件组摆放)

      2.onMeasure  测量大小

      3.onDraw  绘制出所显示的内容

      onDraw(Canvas canvas){//cancas画板对象

      //创建画笔对象

      Paint paint = new Paint();

      paint.setColor(mColor);//设置画笔颜色

      canvas.drawCircle(0,0,mRadius,paint);//画圆

      //参数:1.X轴坐标 2.Y轴坐标 3.圆的半径 4.画笔

      }

     

        setMeasuredDimesion();//根据控件的具体宽高进行设置

        invalidate();//重绘方法

        paint.setStyle(Paint.Style.STROKE);//设置画笔描边样式(空心圆)

     

     

     

     

        自定义View绘制流程:

        首先我们自定义view需要继承自android.View,能帮你处理android命名空间的属性,比如说android:layout_width=”match_parent”……,当然如果我们的控件需要自定的控件属性,则需要在attrs.xml文件中预定义样式格式,如下:

    attrs.xml

    <?xml version="1.0" encoding="utf-8"?>

    <resources>

        <declare-styleable name="MyImageView">

                <attr name="src" format="integer"/>

        </declare-styleable>

    </resources>

      1

     

    然后在使用过程中我们需要在layout文件中声明命名空间:

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"

        xmlns:tools="http://schemas.android.com/tools"

        xmlns:img="http://schemas.android.com/apk/res-auto"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        tools:context="com.example.sosky.skytalk.FriendFragment">

        <RelativeLayout

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:layout_marginBottom="85dp">

        <!-- TODO: Update blank fragment layout -->

        <com.example.sosky.skytalk.MyImageView

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            img:src="@drawable/road"/>

        </RelativeLayout>

    </FrameLayout>

     

    这样声明命令空间xmlns:img=”http://schemas.android.com/apk/res-auto”,还可以xmlns:img=”http://schemas.android.com/apk/res/包名”声明命名空间。

    接下来我们就需要测量大小和绘制view,下面是我自定义的一个图片控件:

    package com.example.sosky.skytalk;

    import android.content.Context;

    import android.content.res.TypedArray;

    import android.graphics.Bitmap;

    import android.graphics.Canvas;

    import android.graphics.Paint;

    import android.graphics.drawable.BitmapDrawable;

    import android.graphics.drawable.Drawable;

    import android.icu.util.Measure;

    import android.support.v4.content.ContextCompat;

    import android.util.AttributeSet;

    import android.view.View;

    import java.util.jar.Attributes;

    /**

    * Created by sOSky on 2017/11/21.

    */

    public class MyImageView extends View {

        private Paint mBitmapPaint;

        private Drawable mDrawable;

        private Bitmap mBitmap;

        private int mWidth;

        private int mHeight;

        public MyImageView(Context context){

            this(context, null);

        }

        public MyImageView(Context context, AttributeSet attributeSet){

            super(context,attributeSet);

            initAtters(attributeSet);

            mBitmapPaint = new Paint();

            mBitmapPaint.setAntiAlias(true);

        }

        private void initAtters(AttributeSet attributeSet){

            if (attributeSet != null) {

                TypedArray array = null;

                try {

                    array = getContext().obtainStyledAttributes(attributeSet,R.styleable.MyImageView);

                    mDrawable =array.getDrawable(R.styleable.MyImageView_src);

                    measureDrawable();

                }finally {

                    if (array != null){

                        array.recycle();

                    }

                }

            }

        }

        private void measureDrawable(){

            if (mDrawable == null) {

                throw new RuntimeException("图片不存在!!!");

            }

            mWidth = mDrawable.getIntrinsicWidth();

            mHeight = mDrawable.getIntrinsicWidth();

        }

        @Override

        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){

            int widthmod = MeasureSpec.getMode(widthMeasureSpec);

            int width = MeasureSpec.getSize(widthMeasureSpec);

            int heightmod = MeasureSpec.getMode(heightMeasureSpec);

            int height = MeasureSpec.getSize(heightMeasureSpec);

            setMeasuredDimension(measureWidth(widthmod,width),measureHeight(heightmod,height));

        }

        @Override

        protected void onDraw(Canvas canvas){

            if (mBitmap == null){

                mBitmap = Bitmap.createScaledBitmap(ImageUtils.drawableToBitmap(mDrawable),getMeasuredWidth(),getMeasuredHeight(),true);

            }

            canvas.drawBitmap(mBitmap,getLeft(),getTop(),mBitmapPaint);

        }

        private int measureWidth(int mod, int width){

            switch(mod){

                case MeasureSpec.UNSPECIFIED:

                case MeasureSpec.AT_MOST:

                    break;

                case MeasureSpec.EXACTLY:

                    mWidth = width;

            }

            return mWidth;

        }

        private int measureHeight(int mod , int height){

            switch(mod){

                case MeasureSpec.UNSPECIFIED:

                case MeasureSpec.AT_MOST:

                break;

                case MeasureSpec.EXACTLY:

                    mHeight = height;

            }

            return mHeight;

        }

    }

      1

     

    这里主要关注的两点是,获取开发人员设置属性值和根据父视图测量自身大小。

    获取属性值主要是通过Context.obtainStyledAttributes(attribueSet attrs,int styleableid);来获取开发人员设定的属性集中我们需要的属性,在这里我们需要的就是R.styleable.MyImageView。然后对获得的资源进行处理。

    测量自身的大小我们需要复写onMeasure(int measurewidth,int measureheight );这两个参数是整个android View绘制的关键。

    Measurespec是一个32位整形数据,前两位是测量模式(specMod),后30位父视图的窗口大小(specSize)。

    三个测量模式分别是

    MeasureSpec.EXACTLY:父视图希望子视图大小应该是由父视图窗口大小来决定,也可以设置成任意的大小。match_parent,具体的数值都是这个模式。

    MeasureSpec.AT_MOST:表示子视图只能是specSize中指定的大小,不能超过specSize的大小,一般来说warp_context对应这个模式。

    MeasureSpec.UNSPECIFIED:表示子视图能够设定为任意值。使用情况较少,系统开发会用到。

    这样我们的子视图就能获取到父视图的窗口大小,再通过开发人员设置的layout属性,就能计算出view的大小。

    我当时看到这里产生了一个疑问,这个measurespecwidth和Measurespecheight是从哪里来的。查了资料后,明白了其实是由一个ViewRootImpl类创建的,它是整个view绘制流程的控制类,所有view都是通过它绘制出来的。

    根视图的MeasureSpec是由窗口大小和自身LayoutParams决定的,一般来说都是EXACTLY模式和窗口大小,然后他会调用

    performMeasure(int childwidthSpec, int childheightSpec){

    …..

    mView.measure(childwidthSpec,childheightSpec)

    ……

    }

    可以看到它是调用的viewGroup的测量函数,遍历当中所有的子view,方式就是调用每一个view的onMeasure()函数。这样子view就获得了测绘模式,父视图大小和自身设定的layoutParams,通过这三个信息,就能计算出具体的大小。

       

       

    相关文章

      网友评论

          本文标题:自定义View简单使用

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