上一篇向我们展示了WaveLoadingView的效果,本篇及后续几篇来讲解它的实现原理,先从控件的初始化开始。
创建自定义控件的java文件
首先我们为自定义控件创建一个java文件,并让它继承某个自定义控件,以WaveLoadingView为例,此外还需要继承控件的构造函数,默认有四个,都继承下来。
public class WaveLoadingView extends View {
//1
public WaveLoadingView(Context context) {
super(context);
}
//2
public WaveLoadingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context,attrs);
}
//3
public WaveLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context,attrs);
}
//4
public WaveLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
...
}
...
}
其实1~3构造函数最终都调用了4构造函数,在构造函数里我们需要获取到布局文件的参数并完成初始化,下面会讲解。
自定义参数
<com.pyjtlk.waveloadview.WaveLoadingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:text="Loading"
app:textSize="20sp"
app:waveLength="3"
app:waveHeight="normal"/>
以上是WaveLoadingView的基本参数设置,以app:开头的参数都是WaveLoadingView自定义的。
要想自定义控件参数类型,首先需要在res/values文件夹下新建一个xml文件,命名格式为attr_xxx.xml,命名其实没有硬性要求,但推荐以这样的格式命名。
创建完毕后会得到标签,里面什么也没有,我们需要自己写一些东西。
<resources>
<declare-styleable name="WaveLoadingView">
<attr format="string" name="text"/>
<attr format="dimension" name="textSize"/>
<attr format="integer" name="length"/>
<attr format="color" name="color"/>
<attr format="reference" name="customImage"/>
<attr format="enum" name="waveHeight">
<enum name="slight" value="1"/>
<enum name="normal" value="2"/>
<enum name="big" value="3"/>
<enum name="large" value="4"/>
</attr>
...
</declare-styleable>
</resources>
首先是declare-styleable标签的name,这个是用来和刚刚的java文件绑定的,也就是自定义控件java文件的名字。
attr标签就是自定义参数,format是参数类型,name是参数的名字。
比如text是字符串类型的参数,使用的时候只能输入字符串。
textSize是尺寸类型的参数,使用的时候能输入sp、px、dp等尺寸参数
length是整型参数,使用的时候可以输入整型数据
color是颜色类型的参数,使用的时候只能输入#AABBCCDD
其中AA BB CC DD分别指A(透明度)R(红)G(绿)B(蓝)的十六进制数
透明度可以省略不写,但前面的#不能省略,如#E142D3
customImage是引用类型的参数,使用的时候可以输入引用的资源文件
举个例子,ImageView的src参数就是引用类型的
waveHeight是枚举类型的参数,枚举值必须写整数且不能重复。
获取参数
定义好了参数,就需要在java文件上获取它们了。
//4
public WaveLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context,attrs);
}
private void init(Context context, @Nullable AttributeSet attrs){
//1
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.WaveLoadingView);
...
//2
typedArray.recycle();
}
我们通过TypedArray这个类来获取布局文件的数据(代码1),获取完后要记得回收它。而中间的部分就是获取xml数据的,看看下面的例子。
//1
mText = typedArray.getString(R.styleable.WaveLoadingView_text);
//2
mColor = typedArray.getColor(R.styleable.WaveLoadingView_color, Color.BLUE);
//3
mImageSize = typedArray.getDimensionPixelSize(R.styleable.WaveLoadingView_imageSize,dp2px(DEFAULT_IMAGE_SIZE_DP));
//4.1
mWaveHeight = typedArray.getInt(R.styleable.WaveLoadingView_waveHeight,WAVE_HEIGHT_NORMAL);
//4.2
mWaveLength = typedArray.getInt(R.styleable.WaveLoadingView_waveLength,1);
//5
mCustomWaveDrawable = typedArray.getDrawable(R.styleable.WaveLoadingView_customImage);
代码1 getString方法可以获取到xml文件的字符串类型数据。
R.styleable.WaveLoadingView_text这个值是自动生成的。
代码2getColor可以获取到颜色值,注意getColor返回值的是整型。
代码3 getDimensionPixelSize可以获取尺寸的像素值,如果xml文件上使用的是sp或dp返回的像素值会有所不同。比如xml上写了10dp,px与dp是3:1的关系,则getDimensionPixelSize返回的是10 * 3。
代码4.1和 代码4.2 getInt获取的都是整型值 ,也就是说枚举值也是通过getInt来获取的。
代码5 getDrawable可以获取到图片的drawable
最后
限于文章篇幅,文章只贴了关键代码,想深入了解此控件测量流程的朋友可以到Github项目上阅读完整代码。
下一篇讲解控件的测量,感兴趣的朋友可以看看。