自定义view基础
分类
自定义View按照组合方式大体分为如下两大类,四小类:
- 自定义单一View(不含子view)
- 继承View
- 继承View的子类(重写、增加功能,如:SurfaceView、TextView、Editext等)
- 自定义ViewGroup(包含子view)
- 继承ViewGroup
- 继承ViewGroup的子类(如:LinearLayout、RelativeLayout)
注:实际上ViewGroup也是继承View,但因为二者的组合方式不同,故分开处理。
流程
- 自定义单一View
- 重写构造函数
- 自定义View的属性
- 获取自定义的属性
- 重新onMeasure(可省)
- 重写onDraw
- 使用自定义View
- 自定义ViewGroup
重写构造函数
构造函数一共有4个,一般来说用前面3个即可:
- View(Context context):Java代码中new后调用。
- View(Context context, AttributeSet attrs):xml中定义后调用。
- View(Context context, AttributeSet attrs, int defStyleAttr):可通过第一、二个构造函数间接调用,主要用来切换style的主题风格。
- View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes):可通过第一、二个构造函数间接调用,而且需要在API 21之后才能使用,主要用来切换style的主题风格,同时需要defStyleRes的资源。
注意:为了方便获取自定义的属性,建议修改第一、二两个构造函数的super为this,让它们皆可调用第三个构造函数。第四个构造函数一般用不到,可以不生成。
//1.Java代码中创建的,则调用第一个构造函数
public CustomView(Context context) {
//默认:super(context);
//调用第二个构造函数
this(context, null);
}
//2.当在xml中使用时,则调用第二个构造函数
public CustomView(Context context, AttributeSet attrs) {
//默认:super(context, attrs);
//调用第三个构造函数
this(context, attrs, 0);
}
//3.不会主动调用,一般是由第一第二个构造函数主动调用
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);
a.recycle(); // 解析后释放资源
init();
}
//4.不会主动调用,并且仅在API 21后才能使用,因此除非你的minSdkVersion为21,否则不要使用它。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
自定义View的属性
declare-styleable
format 属性
format的作用就是对View的属性取值进行格式化,目前支持11种类型,已经涵盖了我们平时所需属性。
- color 颜色
- boolean 布尔
- dimension 尺寸
- enum 枚举
- flags 位或运算
- float 浮点型
- fraction 百分数
- integer 整型
- reference 参考资源ID
- String 字符串
- 混合类型
属性定义
values中新建一个cv_view_styles.xml文件,定义一下属性
<resources>
<declare-styleable name="CustomView">
<!--颜色-->
<attr name="cv_color" format="color" />
<!--布尔-->
<attr name="cv_boolean" format="boolean" />
<!--尺寸-->
<attr name="cv_dimension" format="dimension" />
<!--枚举-->
<attr name="cv_enum">
<enum name="cv_enum0" value="0" />
<enum name="cv_enum1" value="1" />
</attr>
<!--位或运算-->
<attr name="cv_flag">
<flag name="flag0" value="0x1" />
<flag name="flag1" value="0x2" />
<flag name="flag2" value="0x3" />
<flag name="flag3" value="0x4" />
</attr>
<!--浮点-->
<attr name="cv_float" format="float" />
<!--百分数-->
<attr name="cv_" format="fraction" />
<!--整型-->
<attr name="cv_integer" format="integer" />
<!--参考资源ID-->
<attr name="cv_reference" format="reference" />
<!--字符串-->
<attr name="cv_string" format="string" />
<!--混合-->
<attr name="cv_hybrid" format="reference|color"/>
</declare-styleable>
</resources>
属性获取
自定义CustomView.java中,获取以下属性
// TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView,defStyleAttr,0);
//获取颜色
int mColor = a.getColor(R.styleable.CustomView_cv_color, Color.BLACK);
//获取颜色(#D81B60或者selector类型的drawable资源)
ColorStateList colorStateList = a.getColorStateList(R.styleable.CustomView_cv_color);
//获取布尔
boolean mBoolean = a.getBoolean(R.styleable.CustomView_cv_boolean, false);
//获取尺寸
float mDimension = a.getDimension(R.styleable.CustomView_cv_dimension, 3f);
int mDimensionPixelSize = a.getDimensionPixelSize(R.styleable.CustomView_cv_dimension, 0);
//获取浮点型
float mFloat = a.getFloat(R.styleable.CustomView_cv_float, (float) 0.22);
//获取枚举
int mInt = a.getInt(R.styleable.CustomView_cv_enum, 0);
//获取整型
int mInteger = a.getInteger(R.styleable.CustomView_cv_integer, 0);
//获取字符
String mString = a.getString(R.styleable.CustomView_cv_string);
//获取资源文件
int resourceId = a.getResourceId(R.styleable.CustomView_cv_reference, 0);
//获取drawable
Drawable drawable = a.getDrawable(R.styleable.CustomView_cv_color);
属性使用
在使用自定View的属性之前,我们需要引入自定义的命名空间,有2种方式可行:
xmlns:app=“http://schemas.android.com/apk/res-auto” 或者xmlns:app="http://schemas.android.com/apk/com.brainbg.customview ,其中com.brainbg.customview为我们的应用程序包名,建议使用第一种方式,让系统自动查找。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<!--或xmlns:app="http://schemas.android.com/apk/com.brainbg.customview"-->
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.brainbg.customview.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="30dp"
app:cv_color="#3a819b"
app:cv_boolean="true"
app:cv_dimension="11dp"
app:cv_enum="cv_enum0"
app:cv_flag="flag0"
<!--或app:cv_flag="flag0|flag1"-->
app:cv_float="1.1"
app:cv_fraction="90%"
app:cv_integer="11"
app:cv_reference="@mipmap/ic_launcher"
app:cv_string="字符串"
app:cv_hybrid="@mipmap/ic_launcher"
<!--或app:cv_hybrid="#3a819b"-->
/>
</LinearLayout>
需要注意的是:枚举属性不能同时使用两个值,而位或运算则可以。所以定属性的时候,需要根据自己的需求,选择枚举、位或运算。
- 有兴趣的话,可以去查看TextView源码的declare-styleable,路径:sdk/platforms/android-x/data/res/values/attrs.xml,以下是关于Textview的部分源码:
<attr name="textColor" format="reference|color" />
<attr name="typeface">
<enum name="normal" value="0" />
<enum name="sans" value="1" />
<enum name="serif" value="2" />
<enum name="monospace" value="3" />
</attr>
<attr name="textStyle">
<flag name="normal" value="0" />
<flag name="bold" value="1" />
<flag name="italic" value="2" />
</attr>
------------------------可以直接引用以上共用attr的值-------------------------
<declare-styleable name="TextView">
<attr name="bufferType">
<enum name="normal" value="0" />
<enum name="spannable" value="1" />
<enum name="editable" value="2" />
</attr>
<attr name="textColor" />
<attr name="textScaleX" format="float" />
<attr name="typeface" />
<attr name="textStyle" />
<attr name="numeric">
<flag name="integer" value="0x01" />
<flag name="signed" value="0x03" />
<flag name="decimal" value="0x05" />
</attr>
<attr name="phoneNumber" format="boolean" />
<attr name="inputMethod" format="string" />
<attr name="drawableTop" format="reference|color" />
.....
.....
</declare-styleable>
注意:
- 属性定义时可以指定多种类型值:
<declare-styleable name="CustomView">
<attr name="cv_background" format="reference|color" />
</declare-styleable>
// 使用
<ImageView
android:layout_width="42dip"
android:layout_height="42dip"
android:background="@drawable/图片ID|#00FF00" />
- attr的name不可和系统及第三方的重复,否则会报错,如:Android resource compilation failed;尽量改用:自定义view的驼峰字母+属性的格式命名,如CustomView的背景色可以这么写:cv_background
<declare-styleable name="CustomView">
<-- 切记不可写成 name="background" -->
<attr name="cv_background" format="reference|color" />
</declare-styleable>
- 将公用的属性定义在declare-styleable标签之外,即可被多个自定义控件使用,可参考Textview的declare-styleable源码
注意事项
支持wrap_content、padding、margin
参考资料
https://www.jianshu.com/p/e9d8420b1b9c
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0806/4575.html
作者:Brainbg(白雨)
GitHub:https://github.com/Brainbg
博客:https://www.brainbg.com/
简书:https://www.jianshu.com/u/94518ede7100
CSDN:https://blog.csdn.net/u014720022