Android开发中,有时需要一个半透明的草稿界面,上面可当草稿纸一样画图,还可以撤销、恢复和清除。
为此做了个简单的DrawView控件,包含了以上的功能,代码如下DrawView.java:
package com.ldw.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* 说明:自定义画图控件,继承View
* @author ldw
* 日期:2017.12.13
*/
public class DrawView extends View {
//画笔
private Paint mPaint = null;
//绘制的记录
private List<Path> mPathList = new ArrayList<>();
//丢弃的记录,redo用
private List<Path> mRecycleList = new ArrayList<>();
//临时变量
private float preX;
private float preY;
private Path path;
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
//设置抖动处理
mPaint = new Paint(Paint.DITHER_FLAG);
//设置画笔颜色
mPaint.setColor(Color.BLACK);
//设置画笔风格
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(3);
//反锯齿
mPaint.setAntiAlias(true);
mPaint.setDither(true);
// 设置外边缘
mPaint.setStrokeJoin(Paint.Join.ROUND);
// 圆形样式
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
path = new Path();
mPathList.add(path);
mRecycleList.clear();
preX = x;
preY = y;
path.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(x - preX);
float dy = Math.abs(y - preY);
if(dx >= 2.0 || dy >= 2.0){
/*
* 二次贝塞尔曲线参数说明:
* 控制点:为每次event获得的点(x, y)
* 起点和终点:为获得的点(x, y)的一系列中间点。
* 说明:这样贝塞尔曲线一般是不会经过(x, y)(除非是直线的时候),而是经过所有中间点。
* 但却能保证曲线向(x, y)的方向弯曲。
* 如果终点设置为(x, y),控制点很难去计算。
* 缺点:根据贝塞尔曲线公式可以知道:控制点和启动终点共线的话,曲线段变成直线。
* 刚开始绘线的一小段会变成直线,因为起点和控制点重叠,不过效果很不明显。
* 如果是path.quadTo(preX, preY, x, y);也变成画直线,跟path.lineTo(x,y);一样效果
*/
//二次贝塞尔曲线:控制点设置为起点,终点设置为中间点
path.quadTo(preX, preY, (preX + x)/2, (preY + y)/2);
preX = x;
preY = y;
}
break;
case MotionEvent.ACTION_UP:
path.lineTo(x, y);
break;
}
invalidate();
return true;
}
@Override
public void onDraw(Canvas canvas){
for(Path path : mPathList){
canvas.drawPath(path, mPaint);
}
}
/**
* 能否撤销
*/
public boolean canUndo(){
return mPathList.size() > 0;
}
/**
* 能否恢复
*/
public boolean canRedo(){
return mRecycleList.size() > 0;
}
/**
* 撤销
*/
public void undo() {
if(canUndo()){
mRecycleList.add(mPathList.remove(mPathList.size() - 1));
invalidate();
}
}
/**
* 恢复
*/
public void redo(){
if(canRedo()){
mPathList.add(mRecycleList.remove(mRecycleList.size() - 1));
invalidate();
}
}
/**
* 清除全部
*/
public void clear() {
mRecycleList.clear();
if(canUndo()) {
mPathList.clear();
invalidate();
}
}
/**
* 设置画笔大小
*/
public void setPaintWidth(int width) {
mPaint.setStrokeWidth(width);
}
}
Activity中使用如下:
1、activity要设置半透明的背景,主题要加上:
<item name="android:windowBackground">@color/translucentWindow</item>//半透明值为#7fffffff
<item name="android:windowIsTranslucent">true</item>
2、把控件放进布局,使用提供的几个接口,很简单,不详细说了。