<TextView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="15dp" android:text="Hello World" android:textColor="#333333" android:textSize="15sp" />如上代码,我们在xml中写了一个控件TextView,并且设置了它的属性如width、height、text、textSize等,那我们自己定义一个View,如何实现这种可以直接在xml布局中设置View属性呢。其实自定义属性并不复杂,我们可以通过以下几个步骤,来完成View的自定义属性。A、res文件夹下的values文件夹中下建立一个xml,比如attr.xml,然后在这个xml里面自定义一个属性集合,就是存放指定控件的自定义属性<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="CircleView"> <attr name="circle_color" format="color"/> <attr name="fontColor" format="enum"> <enum name="blue" value="1"/> <enum name="red" value="2"/> </attr> </declare-styleable></resources>上述代码就创建了一个名为CircleView的自定义属性的集合,然后在这个集合里面创建我们想要设置的自定义属性项,比如circle_color就定义了一个名为circle_color,类型为color的自定义属性。这里的类型包括有reference(资源id)、dmension指尺寸、string、integer、color等,上述代码也创建了一个枚举类型的属性fontColor,开发者在设置xml属性的时候,可以在我们规定的范围内进行选择。B、在我们的自定义View中,对这些属性进行读取和解析public MyView(Context context, AttributeSet attrs) { super(context, attrs); //第二个参数就是我们在styles.xml文件中的<declare-styleable>标签 //即属性集合的标签,在R文件中名称为R.styleable+name TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView); //第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称 //第二个参数为,如果没有设置这个属性,则设置的默认的值 mFontColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED); //最后记得将TypedArray对象回收 a.recycle(); } C、 然后就是在xml文件中使用自定义属性首先为了可以使用自定义属性,我们需要添加schems声明。xmlns:app="http://schemas.android.com/apk/res-auto" 这样我们才能知道去资源文件中找自定义好的属性集合,然后就可以使用自定义属性了<com.example.lenovo.myapplication.MyView app:circle_color="#333333" android:layout_width="wrap_content" android:layout_height="wrap_content" />通过以上的步骤,我们就完成了一个View的自定义属性。怎么样?是不是很简单?四、Canvas的简单使用
我们在自定义View的分类以及View的绘制过程中讲到了通过Canvas和Paint来绘制我们想要的View。这一部分,我们就简单来介绍一下Canvas在自定义View中的简单实用。在自定义View的时候,我们可以通过draw方法获取到Canvas对象,所以不需要自己new对象了@Override PRotected void onDraw(Canvas canvas) { }可以通过查看api的方法的知,canvas能绘制的对象有弧线(arcs)、填充颜色(argb和color)、Bitmap、圆(circle和oval)、点(point)、线(line)、矩形(Rect)、图片(Picture)、圆角矩形(RoundRect)、文本(text)、顶点(Vertices)、路径(Path)。我们可以通过组合这些对象画出我们想要的图形出来。但是光有这些还不够,当我们想操作Canvas进行位置转换时,可以通过它提供的(rorate、scale、translate、skew(扭曲))等方法来完成一些操作。也可以通过getMatrix方法直接操作矩阵(关于矩阵操作,就涉及到另一个知识点了,因为动画里面也会涉及到这部分的知识点,所以详见另一片笔记)。做进行canvas的使用前,首先我们要知道RectF是做什么用的 RectF其实就是通过left、top、right、bottom四个坐标点来确定一个矩形下面就进行一些canvas的简单用法A、比如画一个圆@Override protected void onDraw(Canvas canvas) { Paint paint = new Paint();//new一个画笔对象,并且设置为蓝色 paint.setColor(Color.BLUE); canvas.drawCircle(100,100,100,paint);//通过canvas画圆 }效果如下B、比如画一段弧形
@Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLUE); RectF rectF = new RectF(0,0,100,100);//确定一个100 * 100的矩形 canvas.drawArc(rectF,//在这个矩形范围内画一段圆弧 0,//开始角度 90,//扫过的角度 true,//是否使用中心 paint); }效果如下C、通过path绘制一个五角星
@Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLUE); Path path = new Path(); path.moveTo(131,5); path.lineTo(88.357f,63.725f); path.lineTo(19,42); path.lineTo(62.002f,100); path.lineTo(19,159); path.lineTo(88.357f,136.275f); path.lineTo(131,195); path.lineTo(131,122.419f); path.lineTo(200,100); path.lineTo(131,77.581f); path.lineTo(131,5); canvas.drawPath(path,paint); }效果如下通过上述Canvas的介绍,我们就可以通过Canvas和Paint来绘制我们想要样子的自定义控件了。更多更复杂的用法,请大家自己去查询相关资料。
五、View的事件分发体系
View的事件分发是核心知识点,更是难点。所谓的事件就是指MotionEvent,当一个MotionEvent产生了之后,系统需要把这个事件传递给一个具体的View,而这个传递的过程就是事件的分发。事件的分发由三个很重要的方法来完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。public boolean dispatchTouchEvent(MotionEvent event):该方法用来进行事件的分发,如果该事件能传递到当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗该事件public boolean onInterceptTouchEvent(MotionEvent event):在上述方法内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列中,此方法不会被再次调用,返回结果表示是否拦截当前事件。public boolean onTouchEvent(MotionEvent event):在dispatichTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件他们的关系可以用下列伪代码来体现public boolean dispatchTouchEvent(MotionEvent event) { boolean consume = false; if (onInterceptTouchEvent(event)){ consume = onTouchEvent(event); }else { consume = child.dispatchTouchEvent(event); } return consume; }事件传递的大致规则如下:对于一个根ViewGroup来说,点击事件产生后,首先会传给它,此时它的dispatchTouchEvent就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true,就表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用;如果这个ViewGroup的oinInterceptTouchEvent方法返回false,就表示它不拦截当前事件,这时当前事件就会继续传递给它的子元素,接着子元素的dispatchToucEvent方法就会被调用,如此反复直到事件被最终处理。如果根ViewGroup的所有子View都不处理事件,即onTouchEvent返回false,那么它的父容器的onTouchEvent将会倍调用,以此类推,如果所有的View都不处理这个事件,那么这个事件将会最终传递给Activity。一些关键点:1、同一事件序列:是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件,这个事件序列以down事件开始,中间含有数量不定的move事件,最终以up事件结束。2、ViewGroup默认不拦截任何事件,onInterceptTouchEvent默认返回false。3、View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用。4、事件的传递过程总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法,可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外,如果DOWN事件被拦截了,那么该事件序列的其他事件都不会再传递下去。总结
到这里,本篇文件关于自定义View的内容,就讲解的差不多了。当然,本篇文章只是比较简单的从跟自定义View密切相关的几个方面,对View体系进行了一番讲解,如想自定义View必须先了解View的绘制过程,然后你得明白常见的自定义View是如何分类,当我们对我们想要的自定义控件进行分类之后,就能比较清晰的找到一个实现思路,比如是通过Canvas去画这个控件的样子、还是通过控件的组合。然后,你的自定义控件肯定需要自己的自定义属性,这样便于xml中直接使用。再然后,你得学会一点Canvas的简单使用。这样便于你绘制自己想要的View的形状。再然后,你通过常用控件组装起自己的自定义View时,你得知道点击、滑动等事件是如何进行分发的。这样才能清楚的让所有子控件各司其职,不会发生错乱。以上就是本文的全部内容,View的绘制、自定义View的分类、View的自定义属性、Canvas的简单使用、View的事件分发体系。本文所有内容来自于自己平时的总结以及其他人分享的经验,感谢所有无私奉献的同行。本文并没有对View的绘制、Canvas的使用等内容进行更深入的解析,如果有这方面需求的,请查阅相关内容。因个人水平水平有限,难免有不足和错误之处,请大家指正。谢谢。。。
新闻热点
疑难解答