一、分类
1. 继承View重写onDraw方法
用于实现一些不规则的效果,即这种效果不方便通过布局的组合方式来达到,往往需要静态或动态地显示一些不规则的图形,需要通过绘制的方式来实现,即重写onDraw方法。
采用这种方式需要在onMeasure方法中支持wrap_content;在onDraw方法中处理padding。
2. 继承ViewGroup派生特殊的Layout
用于实现自定义的布局,即除了LinearLayout、RelativeLayout、FrameLayout这几种系统的布局之外,重新定义一种新布局,当某种效果看起来像几种View组合在一起的时候,可以采用这种方法来实现。
采用这种方式需要合适地处理ViewGroup以及子元素的测量和布局这两个过程。
在onMeasure方法中支持wrap_content并处理当前ViewGroup的padding以及子元素的margin;在onLayout方法中处理当前ViewGroup的padding以及子元素的margin。
3. 继承特定的View(比如TextView)
用于扩展已有的View的功能。
不需要自己支持wrap_content和padding等。
4. 继承特定的ViewGroup(比如LinearLayout)
当某种效果看起来像几种View组合在一起的时候,可以采用这种方法来实现。
不需要自己处理ViewGroup以及子元素的测量和布局这两个过程。
二、注意事项
1. 尽量不要在View中使用Handler
因为View内部本身提供了post系列的方法,完全可以替代Handler的作用。
2. 线程或者动画需要停止
(1) 当包含此View的Activity退出或者当前View被remove时,在View的onDetachedFromWindow方法中停止线程或者动画。
(2) View变得不可见时,需要停止线程或者动画,防止内存泄露。
当包含此View的Activity退出或者当前View被remove时,View的onDetachedFromWindow方法会被调用;当包含此View的Activity启动时,View的onAttachedToWindow方法会被调用。
3. View带有滑动嵌套情形时,需要处理好滑动冲突
推荐使用外部拦截法。
三、自定义属性
像android:layout_width、android:padding这种以android开头的属性是系统自带的属性,我们也可以自定义属性,方法如下:
1. 创建自定义属性的XML
在values目录下创建xml文件,比如attrs.xml,或者类似attrs_circle_view.xml等以atrrs_开头的文件名,当然也可以随便取名字。
//attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleView"> //CircleView为自定义属性集合
<attr name="circle_color" format="color" /> //circle_color为自定义属性集合中的一个属性
</declare-styleable>
</resources>
2. 在View的构造方法中解析自定义属性的值并做相应处理
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView); //使用自定义属性集合CircleView
mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED); //解析自定义属性集合CircleView中的circle_color属性,如果在使用时没有指定颜色则默认为红色
a.recycle(); //释放资源
init();
}
3. 在布局文件中使用自定义属性
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" //使用自定义属性时需要添加schemas声明
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical" >
<com.example.test.CircleView
android:id="@+id/circleView1"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_margin="20dp"
app:circle_color="@color/light_green" //使用自定义属性
android:padding="20dp"
android:background="#000000" />
</LinearLayout >
为了使用自定义属性,必须在布局文件中添加schemas声明:xmlns:app=http://schemas.android.com/apk/res-auto。(推荐使用方式)
在这个声明中,app是自定义属性的前缀,当然可以换其他名字,但是CircleView中的自定义属性的前缀必须和这里的一致。
也可以按照如下方式声明:xmlns:app=http://schemas.android.com/apk/res/com.example.test。
在这个声明中,在apk/res/后面附加应用的包名。但是这两种方式没有本质区别。
四、实例
自定义View实战一:打造仿系统TextView
自定义View实战二:计步器的实现
自定义View实战四:圆形进度条
自定义ViewGroup实战:一步步实现流式布局