如果你还没了解过自定义View的一些原理,建议先看一下这几篇博客,做个了解。
在开始之前,我们先来看下自定义View的步骤:
1.创建自定义View类
2.自定义View的属性
3.在自定义View的构造方法中获得自定义View的属性
4.重写onMesure方法(可选)
5.重写onLayout方法(可选)
6.重写onDraw方法
7.在布局文件中使用
1.创建自定义View类,类继承View或View的子类
package com.mailiang.diyview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
public class CustomView extends View{
private String text;
private int color;
private int background;
private int size;
/**
* 绘制时控制文本绘制的范围
*/
private Rect rect;
private Paint paint;
public CustomView(Context context) {
this(context,null);
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
/**
* 获取自定义属性样式
* @param context
* @param attrs
* @param defStyleAttr
*/
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
2.自定义View属性
在res/values/文件夹下建立attrs.xml文件,在该xml中定义属性及声明样式:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--定义属性-->
<attr name="text" format="string"/>
<attr name="color" format="color"/>
<attr name="size" format="dimension"/>
<attr name="backcolor" format="color"/>
<!--定义样式声明-->
<declare-styleable name="CustomView">
<attr name="text"/>
<attr name="color"/>
<attr name="size"/>
<attr name="backcolor"/>
</declare-styleable>
</resources>
format的类型介绍如下:
1. reference:参考某一资源ID。
属性定义:
<declare-styleable name = "名称">
<attr name = "background" format = "reference" />
</declare-styleable>
属性使用:
<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/图片ID"/>
2. color:颜色值。
属性定义:
<declare-styleable name = "名称">
<attr name = "textColor" format = "color" />
</declare-styleable>
属性使用:
<TextView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:textColor = "#00FF00"/>
3. boolean:布尔值。
属性定义:
<declare-styleable name = "名称">
<attr name = "focusable" format = "boolean" />
</declare-styleable>
属性使用:
<Button
android:layout_width = "42dip"
android:layout_height = "42dip"
android:focusable = "true"/>
4. dimension:尺寸值。
属性定义:
<declare-styleable name = "名称">
<attr name = "layout_width" format = "dimension" />
</declare-styleable>
属性使用:
<Button
android:layout_width = "42dip"
android:layout_height = "42dip"/>
5. float:浮点值。
属性定义:
<declare-styleable name = "AlphaAnimation">
<attr name = "fromAlpha" format = "float" />
<attr name = "toAlpha" format = "float" />
</declare-styleable>
属性使用:
<alpha
android:fromAlpha = "1.0"
android:toAlpha = "0.7"/>
6. integer:整型值。
属性定义:
<declare-styleable name = "AnimatedRotateDrawable">
<attr name = "visible" />
<attr name = "frameDuration" format="integer" />
<attr name = "framesCount" format="integer" />
<attr name = "pivotX" />
<attr name = "pivotY" />
<attr name = "drawable" />
</declare-styleable>
属性使用:
<animated-rotate
xmlns:android ="http://schemas.android.com/apk/res/android"
android:drawable = "@drawable/图片ID"
android:pivotX = "50%"
android:pivotY = "50%"
android:framesCount = "12"
android:frameDuration = "100"/>
7. string:字符串。
属性定义:
<declare-styleable name = "MapView">
<attr name = "apiKey" format = "string" />
</declare-styleable>
属性使用:
<com.google.android.maps.MapView
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:apiKey ="0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"/>
8. fraction:百分数。
属性定义:
<declare-styleable name="RotateDrawable">
<attr name = "visible" />
<attr name = "fromDegrees" format = "float" />
<attr name = "toDegrees" format = "float" />
<attr name = "pivotX" format = "fraction" />
<attr name = "pivotY" format = "fraction" />
<attr name = "drawable" />
</declare-styleable>
属性使用:
<rotate
xmlns:android ="http://schemas.android.com/apk/res/android"
android:interpolator = "@anim/动画ID"
android:fromDegrees = "0"
android:toDegrees = "360"
android:pivotX = "200%"
android:pivotY = "300%"
android:duration = "5000"
android:repeatMode = "restart"
android:repeatCount = "infinite"
/>
9. enum:枚举值。
属性定义:
<declare-styleable name="名称">
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
</declare-styleable>
属性使用:
<LinearLayout
xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent" >
</LinearLayout>
10. flag:位或运算。
属性定义:
<declare-styleable name="名称">
<attr name="windowSoftInputMode">
<flag name = "stateUnspecified" value = "0" />
<flag name = "stateUnchanged" value = "1" />
<flag name = "stateHidden" value = "2" />
<flag name = "stateAlwaysHidden" value = "3" />
<flag name = "stateVisible" value = "4" />
<flag name = "stateAlwaysVisible" value = "5" />
<flag name = "adjustUnspecified" value = "0x00" />
<flag name = "adjustResize" value = "0x10" />
<flag name = "adjustPan" value = "0x20" />
<flag name = "adjustNothing" value = "0x30" />
</attr>
</declare-styleable>
属性使用:
<activity
android:name = ".StyleAndThemeActivity"
android:label = "@string/app_name"
android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
注意:
属性定义时可以指定多种类型值。
属性定义:
<declare-styleable name = "名称">
<attr name = "background" format = "reference|color" />
</declare-styleable>
属性使用:
<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/图片ID|#00FF00"/>
3.在自定义View的构造方法中获得自定义View的属性,补充完整构造函数
/**
* 获取自定义属性样式
* @param context
* @param attrs
* @param defStyleAttr
*/
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 n = typedArray.getIndexCount();
for (int i=0;i<n;i++){
int attr = typedArray.getIndex(i);
switch (attr){
case R.styleable.CustomView_text:
text = typedArray.getString(attr);
break;
case R.styleable.CustomView_color:
color = typedArray.getColor(attr, Color.RED);
break;
case R.styleable.CustomView_backcolor:
background = typedArray.getColor(attr,Color.GRAY);
break;
case R.styleable.CustomView_size:
size = typedArray.getDimensionPixelSize(attr,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()));
break;
}
}
typedArray.recycle();
//获取绘制文本的宽与高
paint = new Paint();
paint.setTextSize(size);
rect = new Rect();
paint.getTextBounds(text,0,text.length(),rect);
}
4.重写onMesure方法(可选):适配match_parent、wrap_content和确定值
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY){//明确值或match_parent
width = widthSize;
}
else {//wrap_content
paint.setTextSize(size);
paint.getTextBounds(text,0,text.length(),rect);
float textWidth = rect.width();
int desired = (int)(getPaddingLeft()+textWidth+getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY){//明确值或match_parent
height = heightSize;
}
else {//wrap_content
paint.setTextSize(size);
paint.getTextBounds(text,0,text.length(),rect);
float textHeight = rect.height();
int desired = (int)(getPaddingTop()+textHeight+getPaddingBottom());
height = desired;
}
setMeasuredDimension(width,height);
}
5.重写onLayout方法(可选):此处没用到,省略此步骤
6.重写onDraw方法,绘制View视图
@Override
protected void onDraw(Canvas canvas) {
paint.setColor(background);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint);
paint.setColor(color);
canvas.drawText(text,getWidth()/2-rect.width()/2,getHeight()/2+rect.height()/2,paint);
}
7.在布局文件中使用
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/apk"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.mailiang.diyview.CustomView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:padding="15dp"
android:layout_centerInParent="true"
app:text="文字"
app:color="#f0cd5c"
app:size="30sp"
app:backcolor="#ff0022"
/>
</RelativeLayout>
展示效果如下:
github源码下载:点击进入