Android回顾--(二十九)View的事件传输与分发机制

自定义View

  在平时工作中,总会需要一些特别的需求,而这些需求是Android系统自带控件不能实现的,所以我们就需要自定义View来实现业务需求。

  1. 继承控件:主要完成的功能是在原来的控件基础上添加一些新的功能
  2. 组合控件:将多个的单一的控件组合成一个控件。
  3. 完全自定义控件:编写一个类直接继承View,重写里面的onMeasure()方法确定尺寸,重写onLayout()方法确定布局位置,重写onDraw()方法绘画控件的形状,再加上相应的回调函数和自定义属性来完成相应的控件的展示和数据的回调。

所有的自定义View都是重写的有两个参数的构造函数,因为系统默认就是调用的有两个参数的构造函数。

完全自定义View(控件)

  1. 自定义一个类继承于View,并重写里面包含两个参数的构造函数
  2. 重写onMeasure()方法,用于测量自身的尺寸

protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec)
widthMeasureSpec:
heightMeasureSpec:这两个参数是从父容器得来的,这两个参数分别实际上是两个32位的2进制数来表示的,这两个32位的2进制数的高两位是当前控件在xml中声明的测量模式,后30位是父容器根据当前的测量模式计算出来的建议的宽和高。(这个宽和高只是建议的,不是实际的)

    /**
     * 测量自身的宽高尺寸
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //下面设置默认的宽和高
        int describWidth = 200;
        int describHeight = 200;
        //获取宽高的测量模式
        int widthModel = MeasureSpec.getMode(widthMeasureSpec);
        int heightModel = MeasureSpec.getMode(heightMeasureSpec);
        //获取父类建议的宽高的值
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //通过测量模式确定宽高的最终值
        int width = 0;
        int height = 0;
        if (widthModel == MeasureSpec.AT_MOST) { //说明使用的是那个warp_content模式
            width = Math.min(describWidth,widthSize);
        }else if (widthModel == MeasureSpec.EXACTLY){ //说明使用的是具体的数值或者match_parent这种测量模式
            width = widthSize;
        }
        if (heightModel == MeasureSpec.AT_MOST){
            height = Math.min(describHeight,heightSize);
        }else if (heightModel == MeasureSpec.EXACTLY){
            height = heightSize;
        }
        setMeasuredDimension(width,height);
    }

测量模式:

  1. MeasureSpec.AT_MOST:--->对应咱们在xml布局文件中声明的wrap_content
  2. MeasureSpec.EXACTLY:--->对应布局文件中的match_parent/实际的数值
    默认情况下,1和2是相等的,也就是自定义View默认情况下是屏幕大小
  3. MeasureSpec.UNSPECIFIED:--->指的是控件的宽高不受父控件的影响,想有多高就有多高,一般用于系统组件,咱们用不着
  1. 重写onDraw()方法绘制控件的形状
    protected void onDraw(Canvas canvas) //用来画控件的长相的
    mPaint.setAntiAlias(true); //设置抗锯齿,为了让边缘平滑
    /**
     * 绘制控件的形状
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //获取画笔的对象
        Paint paint = new Paint();
        paint.setColor(getResources().getColor(R.color.colorAccent));
        paint.setStrokeWidth(1);//设置线条(画笔)的高度
        paint.setAntiAlias(true);  //设置抗锯齿,边缘平滑
        //获取控件测量出来的宽高
        //这个宽高指的是控件没有超出屏幕情况下的宽和高 如果超出了屏幕,那么这个值就是屏幕的宽高
        //getWidth:获取的是控件的宽度
        //getHeight:获取的是控件的高度
        //只有超出了屏幕的情况下,下面这个才能获取真正的宽和高。如果没有超出屏幕,那么两种方法获取到的值是相等的。
        getMeasuredHeight();
        getMeasuredWidth();
        //画出自定义View的整体轮廓和形状
        canvas.drawRect(0, 0, getWidth(), getHeight(), paint);

    }
  1. 如果需要添加事件的话就需要回调

自定义属性

  1. 在Values目录下面建立一个attr的xml文件
  2. 在里面添加属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="diyTextView">
        <attr name="diyText" format="string" />
        <attr name="diyColor" format="string" />
    </declare-styleable>
    <declare-styleable name="diyEditView">
        <attr name="diyEditColor" format="color" />
        <attr name="diyPadding" format="integer" />
    </declare-styleable>
</resources>
  1. 在需要使用自定义属性的地方引入

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

引入的时候,名字需要统一

diyTextView:diyText = "自定义属性"

  1. 在自定义View中获取属性
  • 第一种方式:

int count = attrs.getAttributeCount();
for (int i=0;i<count;i++){
String attrName=attrs.getAttributeName(i);
String attrValue=attrs.getAttributeValue(i);
Log.e("---名称:"+attrName,"---值:"+attrValue);
}

  • 第二个方式:

TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.bobo);
String text=array.getString(R.styleable.bobo_myText);
String color=array.getString(R.styleable.bobo_myColor);
/* Log.e("------------",text);
Log.e("------------",color);*/
array.recycle(); //表示循环的取出消息
尽量使用第二种方式,因为第一种方式在使用了资源引用的时候,获取的值是资源R文件里面的引用而不是值。

这两种获取属性方式的却别是什么?

第一种方式获取属性值的时候,如果存在资源的引用的话没有办法直接获取属性值。第二种方式相当于第一种方式的封装,能够直接通过属性的名称来获取属性的值。

刷新当前控件的方法:

invalidate();//刷新控件不能控件的宽和高
requestLayout();//刷新控件可以改变控件的宽高

继承控件

  一般情况下也即是继承一个已知控件然后完成相应的多功能操作。
实例:仿照笔记本样式

/**
 * 仿照笔记本
 */
public class MyEditText extends EditText{
    int color=Color.BLACK;    //这个呢是这个颜色
    int padding=50;    //这个呢是那个边距
    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        setGravity(Gravity.LEFT | Gravity.TOP );  //这里的|相当于是与的关系,将EditText设置为从左上角开始
        setPadding(60,0,60,0);//设置左上右下的内边距
        setBackgroundResource(R.drawable.background );
    }
    /**
     * 重新布局自己的EditText
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint mPaint=new Paint();
        mPaint.setColor(color);    //设置那个画笔的颜色
        mPaint.setAntiAlias(true); //设置那个抗锯齿
        mPaint.setStrokeWidth(1);  //设置那个线条的宽度
        //开始划线
        //从哪里划线  到哪里结束?
        int viewHeight=getHeight();
        int viewWidth=getWidth();
        //下一步应该获取一共应该画多少线
        int lineCount=viewHeight/getLineHeight(); //计算出了一共需要画出多少线
        for (int i=0;i<lineCount-1;i++){
            canvas.drawLine(60,(i+1)*getLineHeight(),viewWidth-60,(i+1)*getLineHeight(),mPaint);//参数分别是:起点x,起点y,终点x,终点y,画笔
      }
}

在需要使用该控件的地方声明

<com.qf.mobilephone.android1606_28.view2.MyEditText
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:hint="请输入内容"
    />

组合控件

  将多个已知的控件组合形成一个新的控件
案例:将ListView的一个item组合成一个

  1. 编写一个View继承于一个布局
  2. 在初始化这个控件的方法里面获取布局加载器
  3. 编写需要组合的控件的模板-->用xml文件描述
  4. 通过布局加载器加载布局文件
    //首先获取那个布局的加载器
    mLayoutInflater= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//将xml文件加载给当前对象
    View view = mLayoutInflater.inflate(R.layout.view3_layout, this);
  1. 找id,编写方法
  2. 在使用了该控件的地方实现该控件中的回调接口,并重写里面点击事件。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,983评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,354评论 3 403
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,216评论 0 367
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,061评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,073评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,541评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,906评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,881评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,428评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,460评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,578评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,176评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,913评论 3 339
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,348评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,490评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,142评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,650评论 2 366

推荐阅读更多精彩内容