@ZeroGeek
2015-08-19T06:13:45.000000Z
字数 10212
阅读 804
view
android
http://developer.android.com/intl/zh-cn/reference/android/graphics/Canvas.html
http://developer.android.com/intl/zh-cn/reference/android/graphics/Paint.html
结合官网详细学习一遍Canvas和Paint的用法,再做一个验证码生成器
提供绘制几何图形,文本,位图的样式和颜色。
- Paint()
- Paint(int flags) flags涉及一些图形学概念,如锯齿,抖动,平滑等绘制效果
- Paint(Paint paint)
通常我们使用第一种就好
重头戏(这里我没一一列举,列举一些常用的,详细自行查阅)
- void clearShadowLayer() //清除阴影层
- int getAlpha()
- int getColor()
- boolean getFillPath(Path src, Path dst)
- int getFlags()
- int getHinting()
- PathEffect getPathEffect()
- Paint.Style getStyle()
- Paint.Align getTextAlign()
- void getTextBounds(char[] text, int index, int count, Rect bounds)
- void getTextBounds(String text, int start, int end, Rect bounds)
- void getTextPath(char[] text, int index, int count, float x, float y, Path path)
- int getTextWidths(String text, int start, int end, float[] widths)
- void reset()
- void set(Paint src)
- void setARGB(int a, int r, int g, int b)
- void setAlpha(int a)
- void setColor(int color)
- void setFlags(int flags)
- void setHinting(int mode)
- void setStyle(Paint.Style style)
看着看着就不想翻译了=。= ! 建议先去复习下计算机图形学...
参考 : http://www.cnblogs.com/angeldevil/p/3479431.html
如果在Code中实例化一个View会调用第一个构造函数,如果在xml中定义会调用第二个构造函数,而第三个函数系统是不调用的,要由View显式调用,比如我们可以在第二个构造函数中调用了第三个构造函数。
第三个参数的意义就如同它的名字所说的,是默认的Style,只是这里没有说清楚,这里的默认的Style是指它在当前Application或Activity所用的Theme中的默认Style。
参考:http://blog.csdn.net/lmj623565791/article/details/24252901
首先贴出核心的代码,重要地方都标有注释:
package com.zero.apptest;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* 生成数字验证码
* Created by zero on 15-8-18.
*/
public class RandomView extends View implements View.OnClickListener{
private static final String TAG = "RandomView";
private String mTitleText; //验证码内容
private int mTitleTextColor; //验证码数字颜色
private int mTitleTextSize; //数字大小
private Rect mBound; //内容大小
private Paint mPaint;
public static final int LEVEL_NO = 0;
public static final int LEVEL_ONE = 1;
public static final int LEVEL_TWO = 2;
private int mNumber; //验证码数字个数
private int mLevel; //识别度
public RandomView(Context context,AttributeSet attrs) {
this(context, attrs, 0);
}
public RandomView(Context context) {
this(context,null);
}
/**
* 获得自定义样式属性
*/
public RandomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//设置默认属性
mNumber = 4;
mLevel = LEVEL_ONE;
//读取自定义属性
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,R.styleable.RandomView, defStyleAttr, 0);
int n = typedArray.getIndexCount();
//遍历所有属性
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.RandomView_myText:
mTitleText = typedArray.getString(attr);
break;
case R.styleable.RandomView_myTextColor:
mTitleTextColor = typedArray.getColor(attr, Color.BLACK); // 设置默认颜色
break;
case R.styleable.RandomView_myTextSize:
//默认设置为16sp,TypeValue可以把sp转化为px
mTitleTextSize = typedArray.getDimensionPixelSize(attr,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()));
break;
}
}
typedArray.recycle(); //需要回收
mPaint = new Paint();
mPaint.setTextSize(mTitleTextSize);
mBound = new Rect();
mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mBound);
setOnClickListener(this); //注意绑定监听点击事件
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//分宽,高来测量,先学习三种模式MeasureSpec.EXACTLY,MeasureSpec.AT_MOST,MeasureSpec.UNSPECIFIED
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 {
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
float textWidth = mBound.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
float textHeight = mBound.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
}
//注意必须设置
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final float textWith = mBound.width();
final float textHeight = mBound.height();
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
mPaint.setColor(Color.parseColor("#BDC3C7"));
canvas.drawRect(0, 0, width, height, mPaint);
mPaint.setColor(mTitleTextColor);
canvas.drawText(mTitleText, width / 2 - textWith / 2, height / 2 + textHeight / 2, mPaint);
switch (mLevel) {
case LEVEL_NO:
break;
case LEVEL_ONE:
paintComplex(20,20,width,height,canvas);
break;
case LEVEL_TWO:
paintComplex(40,40,width,height,canvas);
break;
default:
break;
}
}
@Override
public void onClick(View v) {
mTitleText = getRandomNumbers(mNumber);
requestLayout(); //请求重新设置布局调用onMeasure()
invalidate(); //请求重绘,调用onDraw()
}
public void setNumber(int number) {
if (number > 0 && number < 10) {
mNumber = number;
}
}
/**
* 生成n位的数字串
* @param n
* @return
*/
private String getRandomNumbers(int n) {
String str = " ";
for (int i = 0; i < n; i++) {
str += (int)(Math.random()*10);
}
Log.d(TAG,"str="+str);
return str.trim();
}
//得到小于10的随机正整数
private int getRandomTen() {
return (int)(Math.random()*10);
}
/**
* 设置验证码识别度
* @param level
*/
public void setLevel(int level) {
if (level == LEVEL_NO) {
mLevel = LEVEL_NO;
} else if (level == LEVEL_ONE) {
mLevel = LEVEL_ONE;
} else if (level == LEVEL_TWO) {
mLevel = LEVEL_TWO;
} else {
mLevel = LEVEL_ONE; //默认样式
}
}
/**
*获得在宽w,高h矩形中随机n个点
*/
private List<Point> getRandomPoint(int n, int w, int h) {
List<Point> list = new ArrayList<>();
for (int i = 0; i < n; i++ ) {
int x = (int)(Math.random()*1000) % w;
int y = (int)(Math.random()*1000) % h;
Point point = new Point(x,y);
list.add(point);
}
return list;
}
/**
* 画上一些散落的圆点和线段
*/
private void paintComplex (int circleTotal,int lineTotal,int width,int height,Canvas canvas) {
List<Point> circles= getRandomPoint(circleTotal,width,height); //得到15个圆心
mPaint.setColor(Color.parseColor("#34495E"));
for (int i = 0 ; i < circles.size() ; i++) {
canvas.drawCircle(circles.get(i).x,circles.get(i).y,4,mPaint);
}
List<Point> lines= getRandomPoint(lineTotal,width,height); //得到10个点,作为弧的左顶点
mPaint.setColor(Color.GREEN);
for (int i = 0 ; i < lines.size() ; i++) {
int x = lines.get(i).x;
int y = lines.get(i).y;
canvas.drawLine(x,y,x+getRandomTen(),y+getRandomTen(),mPaint);
}
}
}
在res/value/下创建attrs.xml,设置自定义属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RandomView" >
<attr name="myText" format="string" />
<attr name="myTextColor" format="color" />
<attr name="myTextSize" format="dimension" />
</declare-styleable>
</resources>
main.xml :
注意 引入 xmlns:test="http://schemas.android.com/apk/res-auto" (在gradle构建下),
如果是在eclipse则引入xmlns:test="http://schemas.android.com/apk/res/<包名>"
其中 ‘test’ ,是自己取个名字,与下面定义保持一致就行。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:test="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<android.support.design.widget.TextInputLayout
android:id="@+id/textInput"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"/>
</android.support.design.widget.TextInputLayout>
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="80dp">
</Spinner>
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Go"/>
<com.zero.apptest.RandomView
android:id="@+id/random"
android:layout_width="wrap_content"
android:layout_height="50dp"
test:myText="6890"
test:myTextColor="#ECF0F1"
test:myTextSize="26sp"
android:layout_gravity="center"
android:paddingLeft="5dp"
android:paddingRight="5dp"
/>
</LinearLayout>
然后是MainActivity中,主要代码
RandomView mRandomView;
TextInputLayout mTIL;
EditText mEt;
Button mBtn;
Spinner mSpinner;
int mLevel;
private static final String[] mRes={"无效果","等级1","等级2"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLevel = RandomView.LEVEL_ONE;
mRandomView = (RandomView) findViewById(R.id.random);
mTIL = (TextInputLayout) findViewById(R.id.textInput);
mBtn = (Button) findViewById(R.id.btn);
mSpinner = (Spinner) findViewById(R.id.spinner);
mTIL.setHint("请输入验证码个数:");
mEt = mTIL.getEditText();
mEt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!isNumeric(s.toString())) {
mTIL.setErrorEnabled(true);
mTIL.setError("只能输入数字");
mBtn.setEnabled(false);
} else {
mTIL.setErrorEnabled(false);
mBtn.setEnabled(true);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,android.R.layout.simple_spinner_item,mRes);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinner.setAdapter(adapter);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
mLevel = RandomView.LEVEL_NO;
break;
case 1:
mLevel = RandomView.LEVEL_ONE;
break;
case 2:
mLevel = RandomView.LEVEL_TWO;
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String str = mEt.getText().toString().trim();
if (!str.equals(null) && !str.equals("")) {
int n = Integer.parseInt(str);
if (n > 0 && n < 10) {
mRandomView.setNumber(n);
mRandomView.setLevel(mLevel);
Toast.makeText(getApplicationContext(),"设置成功",Toast.LENGTH_SHORT).show();
} else {
closeInput();
final Snackbar snackbar = Snackbar.make(mTIL,"不能超过10",Snackbar.LENGTH_LONG);
snackbar.show();
snackbar.setDuration(2000);
snackbar.setAction("取消",new View.OnClickListener() {
@Override
public void onClick(View v) {
snackbar.dismiss();
}
});
}
}
}
});
}
private boolean isNumeric(String str) {
Pattern pattern = Pattern.compile("[0-9]*");
return pattern.matcher(str).matches();
}
private void closeInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mTIL.getWindowToken(), 0);
}
运行效果: