一说起自定义view,似乎好高大上的样子,其实只是小马过河。
自定义view的入门相当的简单,让我想起了我的第一个程序,Hello World;
然后是自定义view的进阶,在我感觉就像我们常玩的积木,自需要根据一定的规律通过提供的方法、接口等把我们需要的东西组装在一起(当然,首先自己需要先了解要实现view的逻辑,否则该干嘛干嘛去吧,再者,需要知道实现view中功能的方法、接口,这些就需要自己平时的积累了)
自定义view入门
- 自定义属性(xml中设置view的属性)
- 获取设置的属性(获取xml中设置的属性)
- 重写onMeasure方法(设置view的宽高)
- 重写onDraw方法(绘制view的内容)
自定义属性
详细自定义属性
在res/values文件夹下创建attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--自定义控件文字内容-->
<attr name="customViewText" format="string"/>
<!--自定义控件文字内容颜色-->
<attr name="customViewcolor" format="color|reference"/>
<!--自定义控件文字内容大小-->
<attr name="customViewSize" format="dimension"/>
<declare-styleable name="CustomView">
<attr name="customViewText"/>
<attr name="customViewcolor"/>
<attr name="customViewSize"/>
</declare-styleable>
</resources>
在界面中使用定义的属性
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<bin.com.customviewone.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:customViewText="CustomView"
custom:customViewcolor="@color/colorPrimary"
custom:customViewSize="12sp" />
</LinearLayout>
获取设置的属性
创建CustomView类,继承View,在构造方法中获取自定义属性
public class CustomView extends View {
String text;
int color;
int size;
private Paint mPaint;
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);
int count = typedArray.getIndexCount();
for(int i = 0; i < count; i++){
int attr = typedArray.getIndex(i);
switch (attr)
{
case R.styleable.CustomView_customViewText:
text = typedArray.getString(attr);
break;
case R.styleable.CustomView_customViewcolor:
color = typedArray.getColor(attr, Color.BLACK);
break;
case R.styleable.CustomView_customViewSize:
size = typedArray.getDimensionPixelSize(attr, 20);
break;
}
}
//释放资源
typedArray.recycle();
mPaint = new Paint();
mPaint.setTextSize(size);
}
}
重写onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
widthMeasureSpec、heightMeasureSpec并不是控件的真实宽、高,它是由宽、高的模式(例如:wrap_content、match_parent代表设置不同的模式)和具体的宽、高2部分组成。
获取宽的模式:int specMode = MeasureSpec. getMode(widthMeasureSpec);
模式具体的值
MeasureSpec. UNSPECIFIED:(可以随意设置大小,可以超过父布局)
MeasureSpec. AT_MOST:(可以随意设置大小,不能超过父布局)(当宽、高设置wrap_content时)
MeasureSpec. EXACTLY:(表示设置具体的值)(当宽、高设置具体值或者match_parent时)
获取真实的宽度: int specSize = MeasureSpec. getSize(widthMeasureSpec);
当宽、高的模式为MeasureSpec. AT_MOST和MeasureSpec. EXACTLY时,则不需要重新设置宽、高;如果是MeasureSpec. UNSPECIFIED时,则需要计算view的宽、高,然后通过setMeasuredDimension()方法设置宽、高。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int specWidthMode = MeasureSpec. getMode(widthMeasureSpec);
int specWidthSize = MeasureSpec. getSize(widthMeasureSpec);
int specHeightMode = MeasureSpec. getMode(heightMeasureSpec);
int specHeightSize = MeasureSpec. getSize(heightMeasureSpec);
switch (specWidthMode){
case MeasureSpec.UNSPECIFIED:
//不确定什么时候会执行这个
break;
case MeasureSpec.AT_MOST:
//当宽、高设置wrap_content时)
//计算view的宽度,赋值给specWidthSize
break;
case MeasureSpec.EXACTLY:
//(当宽、高设置具体值或者match_parent时)
//specWidthSize不变
break;
}
switch (specHeightMode){
case MeasureSpec.UNSPECIFIED:
break;
case MeasureSpec.AT_MOST:
//计算view的高度,赋值给specHeightSize
break;
case MeasureSpec.EXACTLY:
//specHeightSize不变
break;
}
//设置view的宽、高
setMeasuredDimension(specWidthSize, specHeightSize);
}
重写onDraw()方法
@Override
protected void onDraw(Canvas canvas)
{
//mPaint在构造方法中初始化,相当于画笔;canvas相当于画布;getHeight 获取view的高
mPaint.setColor(color);
canvas.drawText(text, 20, getHeight(), mPaint);
}
主要在这个方法中绘制需要展示的东西,具体怎么绘制布局,可以自己了解;上面只是在界面中写字。