一、自定义控件的实现思路
一个自定义控件的实质就是一个View,一个View的展示主要包含以下关键流程:
(1)View属性的初始化;
(2)View位置大小的量测 ,主要在onMeasure()中实现;
【(3)View内部的布局关系展示,主要在onLayout()中实现;】
(4)View的绘制,即是最后的展示效果;
【(5)View交互事件的处理,实现View与目标对象的沟通。】
其中3,5小点不是每个View都必须要自行实现的。
---------------------------------------------------------站在巨人的肩膀上,能够看的更远。
二、例举实现自定义控件
公共使用变量声明:
/**
* 显示文字
*/
private String textAttr;
/**
* 字体颜色
*/
private int textColorAttr;
/**
* 字体大小
*/
private int textSizeAttr;
/**
* 绘制工具--笔/绘制区域
*/
private Paint paint;
private Rect rect;
private String[] strings = new String[]{
"我就是我,不一样的烟火!",
"谁的青春不迷茫,谁的未来是肯定。",
"对自己好一点,和自己多一点的孤独相处。",
"明天会更好!",
"将来的你一定感谢现在拼命的自己。"
};
1,控件的属性声明及设置,res/values下新建attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--声明属性的名称及取值类型-->
<attr name="TextColor" format="color" />
<attr name="TextSize" format="dimension" />
<attr name="TextContent" format="string" />
<attr name="image" format="reference" />
<attr name="imageScaleType">
<enum name="fillXY" value="0" />
<enum name="center" value="1" />
</attr>
<!--声明该自定义控件所拥有的属性-->
<declare-styleable name="CustomControlTextView">
<attr name="TextColor" />
<attr name="TextSize" />
<attr name="TextContent" />
</declare-styleable>
</resources>
2,自定义控件的属性值获取:
public CustomControlTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* 获取自定义属性
* 【
* 预留:
* 1,属性的类型,及值类型;
* 2,属性的获取方式
* 3,属性默认值设置
* 】
*
*/
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomControlTextView, defStyleAttr, 0);
int count = typedArray.getIndexCount();
for (int i = 0; i < count; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.CustomControlTextView_TextColor:
textColorAttr = typedArray.getColor(attr, Color.BLACK);//默认字体颜色为黑色
break;
case R.styleable.CustomControlTextView_TextContent:
textAttr = typedArray.getString(attr);
break;
case R.styleable.CustomControlTextView_TextSize://像素转换细节注意
textSizeAttr = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
typedArray.recycle();
/**
* 设置基本属性
*/
paint = new Paint();
paint.setColor(textColorAttr);
paint.setTextSize(textSizeAttr);
rect = new Rect();
paint.getTextBounds(textAttr, 0, textAttr.length(), rect);
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = (int) (Math.random() * 4);
textAttr = strings[index];
postInvalidate();
}
});
}
细节点:其他类型构造方法,采用调用本身的方法,使得所有该View被使用时,都通过该定义的方法初始化。如实笨一点方法,可以将其他类型构造方法下需要初始化的分别实现也行。
public CustomControlTextView(Context context) {
// super(context);
this(context, null);
}
public CustomControlTextView(Context context, @Nullable AttributeSet attrs) {
// super(context, attrs);
/**
* 重写构造方法,都走第三构造方法
*/
this(context, attrs, 0);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 【
* 1,测量模式,测量的计算方式深度挖掘
* 2,文字绘制的详细优化【padding等一系列的重算】
* 】
*/
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
paint.setTextSize(textSizeAttr);
paint.getTextBounds(textAttr, 0, textAttr.length(), rect);
float textWidth = rect.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
paint.setTextSize(textSizeAttr);
paint.getTextBounds(textAttr, 0, textAttr.length(), rect);
float textHeight = rect.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
}
setMeasuredDimension(width, height);
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
使用定义控件:在主Activity的布局文件中添加控件,即可显示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.future.customcontrolsfirstdemo.MainActivity">
<com.future.customcontrolsfirstdemo.view.CustomControlTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
custom:TextColor="#f07829"
custom:TextContent="第一个自定义控件"
custom:TextSize="16sp" />
</RelativeLayout>
注意补充自定义控件的命名空间(现在AndroidStudio会自动提醒,多好!)
经过重新计算控件的大小,展示效果如图:
4,绘制显示
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.GREEN);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
paint.setColor(textColorAttr);
canvas.drawText(textAttr, getWidth() / 2 - rect.width() / 2, getHeight() / 2 + rect.height() / 2, paint);
}
5,实现控件的可交互性
在构造方法的后面实现了该控件的点击事件监听器。可也会发现,没有完整的调整整个View,它的交互事件给人最后的结果并不如意:
交互实践的代码:【注意一下她被添加的位置啦】
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = (int) (Math.random() * 4);
textAttr = strings[index];
postInvalidate();
}
});
芝麻开门
如果每个人的内心,都像是锁了很多秘密的仓库。那么如果你够幸运的话,在你一生当中,你会碰到几个握有可以打开你内心仓库的钥匙的人。但很多人终其一生,内心的仓库却始终未曾被开启。
------------------------------------------------ 第一次亲密接触