在android开发的过程中,你会发现,如果只是使用系统自带的那些控件是无法满足产品日益变化的需求的,所以我们经常会用到自定义view,自定义view有三种,第一种最基础的组合控件,将android现有的控件组合在一起,比如在页面头部做一个通用的头部,一个退出image+标题textview,这种最简单。第二种就是继承某个控件,比如继承某个button,让这个button有点击或长按效果,第三种就是直接继承View父类,自己画一个View出来,个人认为这是相对前两种更难实现的。但是通过一个demo下来,发现说难也不难的,主要要做以下几步:
1、设置attr自定义属性
2、自定义view构造函数中获取自定义view属性的值
3、重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)绘制控件高度和宽度
4、重写protected void onDraw(Canvas canvas)绘制控件
本实验做的是一个自定义的验证码生成器,字母和数字的组合,可以设置是否包含字母或数字,以及字母和数字的个数,先看看效果,设置字母和数字都包含,然后各有3个:
上面的验证码就是本次实验生成的自定义view。
好的,开始我们的实验。
第一步、设置attr自定义属性
在attrs.xml中增加
<declare-styleable name="ValidateView">
<attr name="showLetter" format="boolean" />
<attr name="showNumber" format="boolean" />
<attr name="LetterNum" format="integer"/>
<attr name="numberNum" format="integer"/>
<attr name="textSize" format="integer"/>
</declare-styleable>
第二步、自定义view构造函数中获取自定义view属性的值
第三步、重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)绘制控件高度和宽度
第四步、重写protected void onDraw(Canvas canvas)
以上几步见自定义ValidateView.java
package com.figo.study.view;
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.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import com.figo.study.R;
import com.figo.study.utils.CommonUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
/**
* Created by figo on 16/02/04.
*/
public class ValidateView extends View implements View.OnClickListener {
boolean mShowLetter = false;
boolean mShowNum = false;
int mLetterNum = 0;
int mNumberNum = 0;
String[] mLetters = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
String[] mNumbers = new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
ArrayList<String> mArrayList;
String mText;
private Rect mTextBounds;
Paint mPaint;
int mTextSize;
public ValidateView(Context context) {
super(context);
}
public ValidateView(Context context, AttributeSet attrs) {
super(context, attrs);
//获取自定义属性的值,在OnDraw的时候使用
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ValidateView,
0, 0);
try {
mShowLetter = a.getBoolean(R.styleable.ValidateView_showLetter, false);
mLetterNum = a.getInteger(R.styleable.ValidateView_LetterNum, 0);
mShowNum = a.getBoolean(R.styleable.ValidateView_showNumber, false);
mNumberNum = a.getInteger(R.styleable.ValidateView_numberNum, 0);
mTextSize = a.getInteger(R.styleable.ValidateView_textSize, 0);
} finally {
a.recycle();
}
//首次生成一串验证码
generateValidateCode();
mPaint = new Paint();
//设置画笔
mPaint.setColor(Color.parseColor("#80FF0000"));
mPaint.setTextSize(80);
mTextBounds = new Rect();
setOnClickListener(this);
}
/**
* @param widthMeasureSpec
* @param heightMeasureSpec 如果android:layout_width="200dp"
* android:layout_height="100dp"设置了具体的值这个方法不用重写
* wrap_content/match_parent,默认会全屏显示这个控件
* 所以设置成wrap_content需要重写这个方法,重新测量宽度和高度
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = (int) (getPaddingLeft() + mTextBounds.width() + getPaddingRight());
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = (int) (getPaddingTop() + mTextBounds.height() + getPaddingBottom());
}
//设置高度
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画背景颜色
canvas.drawColor(Color.parseColor("#80123456"));
//将文字绘在正中
mPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
float textWidth = mTextBounds.width();
float textHeight = mTextBounds.height();
canvas.drawText(mText, getWidth() / 2 - textWidth / 2, getHeight() / 2
+ textHeight / 2, mPaint);
}
@Override
public void onClick(View v) {
generateValidateCode();
//触发onDraw,重绘view
invalidate();
}
private void generateValidateCode() {
if (mArrayList == null) {
mArrayList = new ArrayList<String>();
} else {
mArrayList.clear();
}
if (mShowLetter) {
for (int a = 0; a < mLetterNum; a++) {
mArrayList.add(mLetters[new Random().nextInt(26)]);
}
}
if (mShowNum) {
for (int b = 0; b < mNumberNum; b++) {
mArrayList.add(mNumbers[new Random().nextInt(10)]);
}
}
//乱序排列
Collections.shuffle(mArrayList);
//写文字
StringBuffer buffer = new StringBuffer();
for (int a = 0; a < mArrayList.size(); a++) {
buffer.append(mArrayList.get(a));
}
mText = buffer.toString();
}
public String getText() {
return mText;
}
}
第五步、activity页面使用自定义view
<LinearLayout 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"
android:orientation="vertical"
android:gravity="center"
tools:context="com.figo.study.activity.CustomViewActivity">
<com.figo.study.view.ValidateView
android:id="@+id/validate_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
custom:LetterNum="3"
custom:numberNum="3"
custom:textSize="60"
custom:showLetter="true"
custom:showNumber="true" />
<Button
android:id="@+id/btn_current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取当前验证码" />
</LinearLayout>