转载请注明出处:http://blog.youkuaiyun.com/xiaohao0724/article/details/53994009
自定义控件是Android程序猿通向高手的必经之路,下面我们就从零开启自定义控件之旅。
自定义控件整体上分为自定义View和自定义ViewGroup,本文就义自定义View为例。
自定义View的一般步骤:
*1、自定义定义一个CustomView 继承Android系统控件View或View的某些子类(如:Button、TextView等)
2、在res/values/目录下新建自定义属性attr.xml
*3、重写构造方法。构造方法有三个不同的重载适用于不同的场景,如果有自定义属性需要重写CustomView (Context context, AttributeSet attrs, int defStyleAttr)构造方法
4、重写onMesure()方法测量自定义View的大小
*5、重写onDraw方法在绘制自定义的View
6、在xml布局里面引用(注意添加自定义命名空间)
注:带 " * "的条目为必须要做的步骤
OK,接下来我们按步骤来一一实现,先放上效果图如下:
1、新建一个CustomView继承View
public class CustomView extends View {
}
2、在values下面新建自定义属性attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CicleCustom">
<attr name="cicleColor" format="color" />
<attr name="strokeWidth" format="dimension"></attr>
<attr name="sleepTime" format="integer"></attr>
</declare-styleable>
</resources>
更多自定义属性知识请参考:http://blog.youkuaiyun.com/xiaohao0724/article/details/50679467
3、重写构造方法
public class CustomView extends View {
/**
* 代码中实例化控件new时候使用
*
* @param context
*/
public CustomView(Context context) {
this(context, null);
}
/**
* 从XML加载到代码中时使用
*
* @param context
* @param attrs
*/
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 获得自定义的样式属性
*
* @param context
* @param attrs
* @param defStyleAttr
*/
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
5、重写onDraw()方法绘制View
/**
* 在画布上绘制View
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 画圆
canvas.drawCircle(width, height, radius, paint);
}
package com.havorld.customview.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.View;
import com.havorld.customview.R;
import com.havorld.customview.utils.ScreenInfoUtil;
public class CustomView extends View {
private Paint paint;
private int radius;
private int width;
private int height;
private int cicleColor = Color.RED;
private float strokeWidth = 3;
private int sleepTime = 150;
/**
* 代码中实例化控件new时候使用
*
* @param context
*/
public CustomView(Context context) {
this(context, null);
}
/**
* 从XML加载到代码中时使用
*
* @param context
* @param attrs
*/
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 获得自定义的样式属性
*
* @param context
* @param attrs
* @param defStyleAttr
*/
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 获取我们的自定义属性
TypedArray obtainStyledAttributes = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.CicleCustom,
defStyleAttr, 0);
// 如果XML中不使用自定义属性,则obtainStyledAttributes.getIndexCount()为零
for (int i = 0; i < obtainStyledAttributes.getIndexCount(); i++) {
int attr = obtainStyledAttributes.getIndex(i);
switch (attr) {
case R.styleable.CicleCustom_cicleColor:
cicleColor = obtainStyledAttributes.getColor(attr, Color.RED);
break;
case R.styleable.CicleCustom_strokeWidth:
strokeWidth = obtainStyledAttributes.getDimension(attr, 3);
break;
case R.styleable.CicleCustom_sleepTime:
sleepTime = obtainStyledAttributes.getInt(attr, 100);
break;
default:
break;
}
}
obtainStyledAttributes.recycle();
getScreenInfo(context);
initPaint();
redrawView();
}
/**
* 重绘View圆环
*/
private void redrawView() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (radius <= 200) {
radius += 10;
} else {
radius = 0;
}
// Android中提供了两个的方法来重绘更新View
// invalidate(); // 在被调用的线程更新View(如果直接在子线程调用将报错)
postInvalidate(); // 在UI线程更新View
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* 获取屏幕的宽高
*
* @param context
*/
private void getScreenInfo(Context context) {
int[] screenMeasures = ScreenInfoUtil.getScreenMeasure(context);
width = screenMeasures[0] / 2;
height = screenMeasures[1] / 2;
}
/**
* 初始化画笔
*/
private void initPaint() {
paint = new Paint();
paint.setColor(cicleColor);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(strokeWidth);
paint.setAntiAlias(true);
}
/**
* 在画布上绘制View
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 画圆
canvas.drawCircle(width, height, radius, paint);
// 画出矩形
// canvas.drawRect(width - radius, height - radius, width + radius, height
// + radius, paint);
}
}
package com.havorld.customview.utils;
import android.content.Context;
import android.view.Display;
import android.view.WindowManager;
public class ScreenInfoUtil {
/**
* 获取屏幕的宽和高
*
* @param context
* @return
*/
@SuppressWarnings("deprecation")
public static int[] getScreenMeasure(Context context) {
// 1、得到设备屏幕的分辨率的宽和高:
// 得到系统提供的屏幕管理服务对象
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
// 得到默认分辨率对象
Display display = wm.getDefaultDisplay();
// 得到屏幕的宽和高
int screeWidth = display.getWidth();
int screeHeigth = display.getHeight();
return new int[] { screeWidth, screeHeigth };
}
}
程序入口MainActivity
MainACtivity布局activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:havorld="http://schemas.android.com/apk/res/com.havorld.customview"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.havorld.customview.MainActivity" >
<com.havorld.customview.widget.CustomView
android:id="@+id/customView"
android:layout_width="match_parent"
android:layout_height="match_parent"
havorld:cicleColor="#0000ff"
havorld:sleepTime="100"
havorld:strokeWidth="5px" />
</RelativeLayout>
引用自定义布局需要添加自定义命名空间:
xmlns:havorld="http://schemas.android.com/apk/res/com.havorld.customview"
其中com.havorld.customview是包名,也可以使用下面方式的命名空间
xmlns:havorld="http://schemas.android.com/apk/res-auto"
必须添加自定义命名空间后才可以使用下面的自定义属性
havorld:cicleColor="#0000ff"
havorld:sleepTime="100"
havorld:strokeWidth="5px"