Android简单画板实现

299649.jpg
前言:

今天要给大家展示安卓画板的绘画效果,当然并非原创,仿写别人的,写得不好的地方,请见谅~
由于原来博客的地址无法访问,这里分享一下自己仿写过后同样的源码 提取码: 5jjm

概述:
  • 环境:Android Studio 3.42
  • 语言:Java
  • 特点:简单,易懂,效果爆炸
展示:
20180815171025918.gif
开始:
  • 界面布局
<?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/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


        <me.jrl.demo36.DrawingBoard
            android:id="@+id/draw_board"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@color/white"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="@color/mode_bg"
            android:layout_gravity="center_vertical">
            <ImageView
                android:id="@+id/iv_paint"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@drawable/draw_mode_background"
                android:padding="8dp"
                android:src="@drawable/paint_mode_src"/>
            <ImageView
                android:id="@+id/iv_eraser"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@drawable/draw_mode_background"
                android:padding="8dp"
                android:src="@drawable/eraser_mode_src"/>
            <ImageView
                android:id="@+id/iv_clean"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@drawable/draw_mode_background"
                android:padding="8dp"
                android:src="@drawable/clean"/>
            <ImageView
                android:id="@+id/iv_last"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@drawable/draw_mode_background"
                android:padding="8dp"
                android:src="@drawable/last"/>
            <ImageView
                android:id="@+id/iv_next"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@drawable/draw_mode_background"
                android:padding="8dp"
                android:src="@drawable/next"/>
        </LinearLayout>

    </LinearLayout>


</RelativeLayout>
  • MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private DrawingBoard mDrawingBoard;
    private ImageView mPaint;
    private ImageView mEraser;
    private ImageView mClean;
    private ImageView mLast;
    private ImageView mNext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initEvent();
    }
    //初始化控件
    private void initView(){
        mDrawingBoard = findViewById(R.id.draw_board);
        mPaint = findViewById(R.id.iv_paint);
        mEraser = findViewById(R.id.iv_eraser);
        mClean = findViewById(R.id.iv_clean);
        mLast = findViewById(R.id.iv_last);
        mNext = findViewById(R.id.iv_next);
    }
    //设置监听事件
    private void initEvent(){
        //设置默认选择背景,level值为1
        mPaint.getDrawable().setLevel(1);
        mPaint.getBackground().setLevel(1);
        mPaint.setOnClickListener(this);
        mEraser.setOnClickListener(this);
        mClean.setOnClickListener(this);
        mLast.setOnClickListener(this);
        mNext.setOnClickListener(this);
    }

    //实现监听事件效果
    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.iv_paint:
                if (mDrawingBoard.getMode() != DrawMode.PaintMode) {
                    mDrawingBoard.setMode(DrawMode.PaintMode);
                }
                    mPaint.getDrawable().setLevel(1);
                    mPaint.getBackground().setLevel(1);
                    mEraser.getDrawable().setLevel(0);
                    mEraser.getBackground().setLevel(0);

                break;
            case R.id.iv_eraser:
                if (mDrawingBoard.getMode() != DrawMode.EraserMode) {
                    mDrawingBoard.setMode(DrawMode.EraserMode);
                }
                    mPaint.getDrawable().setLevel(0);
                    mPaint.getBackground().setLevel(0);
                    mEraser.getDrawable().setLevel(1);
                    mEraser.getBackground().setLevel(1);
                break;
            case R.id.iv_clean:
                alertDialogClean();
                break;
            case R.id.iv_last:
                mDrawingBoard.lastStep();
                break;
            case R.id.iv_next:
                mDrawingBoard.nextStep();
                break;
        }
    }
    //设置画板清空对话框
    private void alertDialogClean(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("确定要请空画板吗?");
        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                mDrawingBoard.clean();
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {

            }
        });
        final  AlertDialog dialog = builder.show();
        dialog.show();
    }
}
  • 画板功能实现
    自定义控件DrawingBoard画板
    主要方法
  • 触摸事件监听,保存触摸点坐标,设置路径,保存路径
@Override
    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()){

            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(mLastX,mLastY);
                break;
            case MotionEvent.ACTION_MOVE:
                //画出路径
                mPath.quadTo(mLastX,mLastY,(mLastX+x)/2,(mLastY+y)/2);
                mBufferCanvas.drawPath(mPath,mPaint);
                invalidate();
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
                //保存路径
                saveDrawPaths();
                mPath.reset();
                break;
        }
        return true;
    }
  • onMeasure方法
 //重新测量宽高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(mWidth,mHeight);
        initCanvas();
    }
  • onDraw方法
 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //缓冲位图上的图形都会绘制在canvas上
        canvas.drawBitmap(mBufferBitmap,0,0,null);
    }

完整代码

public class DrawingBoard extends View {

    //画图的模式(默认是画笔)
    private DrawMode mDrawMode = DrawMode.PaintMode;

    //画笔
    private Paint mPaint;
    //画笔颜色
    private int mPaintColor = Color.RED;
    //画笔宽度
    private int mPaintSize = dip2px(5);
    //橡皮擦宽度
    private int mEraserSize = dip2px(36);
    //缓冲的位图
    private Bitmap mBufferBitmap;
    //缓冲的画布
    private Canvas mBufferCanvas;
    //当前控件的宽度
    private int mWidth;
    //当前控件的高度
    private int mHeight;
    //画布的颜色
    private int mCanvasColor = Color.WHITE;
    //上次的位置
    private float mLastX;
    private float mLastY;
    //路径
    private Path mPath;
    //设置图形混合模式为清除
    private PorterDuffXfermode mEraserMode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
    //保存的路径
    private List<DrawPathInfo> savePaths;
    //当前的路径
    private List<DrawPathInfo> currPaths;
    //最多保存20条路径
    private int MAX_PATH = 20;
    public DrawingBoard(Context context) {
        this(context,null);
    }

    public DrawingBoard(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public DrawingBoard(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        initPath();
    }
    //获取屏幕像素,实现外部赋值控制控件大小
    public int dip2px(float dipValue){
        final float scale = this.getResources().getDisplayMetrics().density;
        return (int)(dipValue*scale+0.5f);
    }

    //重新测量宽高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(mWidth,mHeight);
        initCanvas();
    }

    private void initPath(){
        mPath = new Path();
        savePaths = new ArrayList<>();
        currPaths = new ArrayList<>();
    }
    private void initCanvas(){
        //创建一个BITMAP
        mBufferBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
        mBufferCanvas = new Canvas(mBufferBitmap);
        mBufferCanvas.drawColor(mCanvasColor);
    }
    private void initPaint(){
        //设置画笔抗锯齿和抖动
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG|Paint.DITHER_FLAG);
        //设置画笔填充方式为只描边
        mPaint.setStyle(Paint.Style.STROKE);
        //设置画笔颜色
        mPaint.setColor(mPaintColor);
        //设置画笔宽度
        mPaint.setStrokeWidth(mPaintSize);
        //设置圆形线帽
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        //设置线段连接处圆角
        mPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mBufferBitmap,0,0,null);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()){

            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(mLastX,mLastY);
                break;
            case MotionEvent.ACTION_MOVE:
                //画出路径
                mPath.quadTo(mLastX,mLastY,(mLastX+x)/2,(mLastY+y)/2);
                mBufferCanvas.drawPath(mPath,mPaint);
                invalidate();
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
                //保存路径
                saveDrawPaths();
                mPath.reset();
                break;
        }
        return true;
    }
    private void saveDrawPaths(){
        if (savePaths.size() == MAX_PATH){
            savePaths.remove(0);
        }
        savePaths.clear();
        savePaths.addAll(currPaths);
        Path cachePath = new Path(mPath);
        Paint cachePaint = new Paint(mPaint);
        savePaths.add(new DrawPathInfo(cachePaint,cachePath));
        currPaths.add(new DrawPathInfo(cachePaint,cachePath));
    }
    /**
     * 设置画笔模式
     * */
    public void setMode(DrawMode mode){
        if (mode != mDrawMode){
            if (mode == DrawMode.EraserMode){
                mPaint.setStrokeWidth(mEraserSize);
                mPaint.setXfermode(mEraserMode);
                mPaint.setColor(mCanvasColor);
            }
            else{
                mPaint.setXfermode(null);
                mPaint.setColor(mPaintColor);
                mPaint.setStrokeWidth(mPaintSize);
            }
            mDrawMode = mode;
        }
    }
    public DrawMode getMode(){
        return mDrawMode;
    }

    /**
     * 下一步 反撤销
     * */
    public void nextStep(){
        if (currPaths != savePaths){
            if (savePaths.size()>currPaths.size()){
                currPaths.add(savePaths.get(currPaths.size()));
                redrawBitmap();
            }
        }
    }
    /**
     * 上一步 撤销
     * */
    public void lastStep(){
        if (currPaths.size()>0){
                currPaths.remove(currPaths.size()-1);
        }
    }
    /**
     * 重绘位图
     * */
    private void redrawBitmap(){
        mBufferBitmap.eraseColor(Color.TRANSPARENT);
        for (int i = 0;i<currPaths.size();i++){
            DrawPathInfo path = currPaths.get(i);
            mBufferCanvas.drawPath(path.getPath(),path.getPaint());
        }
        invalidate();
    }
    /**
     * 保存图片
     * */

    /**
     * 擦除画布
     * */
    public void clean(){
        savePaths.clear();
        currPaths.clear();
        //将位图擦成透明的
        mBufferBitmap.eraseColor(Color.TRANSPARENT);
        invalidate();
    }
}
  • DrawMode类,枚举保存绘制模式,画笔和橡皮擦
public enum DrawMode {
    //画笔模式
    PaintMode,
    //橡皮擦模式
    EraserMode,
}
  • DrawPathInfo类,保存绘制路径
public class DrawPathInfo {

    private Paint paint;

    private Path path;

    public DrawPathInfo(Paint paint,Path path){
        this.paint = paint;
        this.path = path;
    }

    public Paint getPaint() {
        return paint;
    }

    public void setPaint(Paint paint) {
        this.paint = paint;
    }

    public Path getPath() {
        return path;
    }

    public void setPath(Path path) {
        this.path = path;
    }
}
最后定义橡皮擦和画笔点击选中蓝色背景的xml文件实现
  • draw_mode_background.xml
<!--设置控件的背景颜色,通过设置maxLevel改变背景颜色-->
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@color/mode_bg"
        android:maxLevel="0"/>

    <item android:drawable="@color/select_mode_bg"
        android:maxLevel="1"/>
</level-list>
  • paint_mode_src.xml
<!--设置画笔的背景图片,通过设置maxLevel改变背景图片-->
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/paint"
        android:maxLevel="0"/>
    <item android:drawable="@drawable/paint_white"
        android:maxLevel="1"/>

</level-list>
  • eraser_mode_xml
<!--设置橡皮擦的背景图片,通过设置maxLevel改变背景图片-->
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/eraser"
        android:maxLevel="0"/>
    <item android:drawable="@drawable/eraser_white"
        android:maxLevel="1"/>
</level-list>

总结:这个demo画了一点点时间了解,自己运行效果和原博文显示效果一样,感觉很开心,感谢各位的阅读,谢谢大家,感谢博主博客的分享
博文地址:[https://blog.csdn.net/javaSXL/article/details/81708256]

推荐阅读更多精彩内容