Android-自定义View
一、为什么要自定义View
- Android原生控件,不能直接满足具体业务场景的特殊交互方式;
- 封装代码,方便项目适配与复用;
二、自定义View方式
- 组合控件
- 继承控件
- 自绘控件
三、自定义View实现
(一)组合控件
绘制一个CustomViewByGroup视图,解析xml布局,其中xml布局集成LinearLayout & TextView等相关Android原生控件。
public class CustomViewByGroup extends LinearLayout {
private TextView mTxt_title;
private Context mContext;
public CustomViewByGroup(Context context) {
super(context);
}
public CustomViewByGroup(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context);
initData();
}
public CustomViewByGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomViewByGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private void initView(Context context) {
mContext = context;
View view = LayoutInflater.from(mContext).inflate(R.layout.widget_by_group,this);
mTxt_title = view.findViewById(R.id.txt_title);
}
private void initData(){
mTxt_title.setText(mContext.getString(R.string.customview_by_group_title));
}
}
(二)继承控件
绘制一个CustomViewByExtend视图,继承Android原生控件TextView。
public class CustomViewByExtend extends androidx.appcompat.widget.AppCompatTextView {
private Context mContext;
public CustomViewByExtend(Context context) {
super(context);
}
public CustomViewByExtend(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
initData();
}
public CustomViewByExtend(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void initView(Context context) {
mContext = context;
}
private void initData(){
int padding = (int)mContext.getResources().getDimension(R.dimen.content_title_padding);
this.setPadding(padding,padding,padding,padding);
this.setText(mContext.getString(R.string.customview_by_extend_title));
float textSize = (float) (mContext.getResources().getDimension(R.dimen.content_title_size));
this.setTextSize(px2dip(mContext,textSize));
}
// 根据手机的分辨率从 px(像素) 的单位 转成为 dp
public static int px2dip(Context context, float pxValue) {
// 获取当前手机的像素密度(1个dp对应几个px)
float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f); // 四舍五入取整
}
}
(三)自绘控件
绘制一个CustomViewByDraw视图,继承View,重写onDraw方法。
public class CustomViewByDraw extends View {
// 定义画笔
private Paint mPaint;
// 用于获取文字的宽和高
private Rect mBounds;
// 计数值,每点击一次本控件,其值增加1
private Context mContext;
/**
* Java代码引用时,调用此构造函数
* @param context
*/
public CustomViewByDraw(Context context) {
super(context);
}
/**
* XML布局文件引用,调用此构造函数
* @param context
* @param attrs
*/
public CustomViewByDraw(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context);
initData();
}
/**
* 涉及主题变更时,调用此构造函数
* @param context
* @param attrs
* @param defStyleAttr
*/
public CustomViewByDraw(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 涉及主题变更时,调用此构造函数
* @param context
* @param attrs
* @param defStyleAttr
* @param defStyleRes
*/
public CustomViewByDraw(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private void initView(Context context) {
mContext = context;
// 初始化画笔、Rect
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBounds = new Rect();
}
private void initData(){
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int view_height = 80;
// 绘制一个填充色为蓝色的矩形
mPaint.setColor(Color.BLUE);
canvas.drawRect(0, 0, getWidth(), view_height, mPaint);
// 绘制字符串
mPaint.setColor(Color.YELLOW);
mPaint.setTextSize(45);
// 获取文字的宽和高
String text = String.valueOf(mContext.getString(R.string.customview_by_draw_title));
mPaint.getTextBounds(text, 0, text.length(), mBounds);
float textHeight = mBounds.height();
canvas.drawText(text, 12, view_height/2 + textHeight / 2, mPaint);
}
}
四、View相关问题分析
1. 四个构造函数参数代表什么?
参数 | 描述 |
---|---|
Context | 上下文 |
AttributeSet attrs | 从xml中定义的各种参数,如:textColor |
int defStyleAttr | 主题中优先级最高的属性,格式为reference(参考某一资源ID),只要在主题中对这个属性赋值,该View就会自动应用这个属性的值,如:textViewStyle |
int defStyleRes | 样式资源文件,如:R.style.GreenTextStyle |
2. 四个构造函数的引用时机?
构造函数 | 引用时机 |
---|---|
CustomViewByExtend(Context context) | Java代码引用时,调用此构造函数 |
CustomViewByExtend(Context context, AttributeSet attrs) | XML布局文件引用,调用此构造函数 |
CustomViewByExtend(Context context, AttributeSet attrs, int defStyleAttr) |
涉及主题等相关资源变更时,可主动调用此构造函数 |
CustomViewByExtend(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) |
涉及主题等相关资源变更时,可主动调用此构造函数 |
3. View资源属性赋值优先级?
- 优先级:Xml直接定义 > xml中style引用 > defStyleAttr > defStyleRes > theme直接定义
- 分析过程:目前是通过控制变量法,改变如上不同的属性,观察textview的字体颜色,可以看到属性赋值的优先级。详细可参考Demo代码(CustomViewActivity类),如下附上部分代码:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/content_tip_size"
android:paddingLeft="@dimen/content_title_padding"
android:text="优先级:xml直接定义 > xml的style定义 > theme直接定义"/>
<!-- 字体为theme定义颜色 -->
<com.lzq.mycustomdemo.widget.CustomViewByExtend
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- 字体为style定义颜色 -->
<com.lzq.mycustomdemo.widget.CustomViewByExtend
style="@style/RedTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- 字体为xml布局定义颜色 -->
<com.lzq.mycustomdemo.widget.CustomViewByExtend
style="@style/RedTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/holo_orange_light"/>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_margin="5dp"
android:layerType="software"
android:background="@drawable/dotted_line_shap"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/content_tip_size"
android:paddingLeft="@dimen/content_title_padding"
android:text="优先级: defStyleAttr > defStyleRes > theme直接定义"/>
<!-- defStyleAttr和defStyleRes都设置为0,字体为主题默认颜色 -->
<com.lzq.mycustomdemo.example.customview.CustomViewBlank
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/content_title_padding"
android:textSize="@dimen/content_title_size"
android:text="@string/customview_by_extend_title"/>
<!-- defStyleAttr为0 && defStyleRes设置为绿颜色,字体为绿颜色 -->
<com.lzq.mycustomdemo.example.customview.CustomViewGreen
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/content_title_padding"
android:textSize="@dimen/content_title_size"
android:text="@string/customview_by_extend_title"/>
Demo测试结果如下:
五、代码仓库地址
Demo地址: https://gitee.com/linzhiqin/custom-demo