Android基础小画板的实现(SurfaceView版)

首先,我们先明确步骤:
1.实现布局
2.定义一个类继承于SurfaceView,并实现其构造方法
3.定义画笔及path对象,通过path对象绘制;
4.开始绘制:
定义画布->绑定画布->绘制画布->解绑画布;
5.定义一个清除按钮。
(当然这其中包括Callback回掉、触摸事件等等,具体见下文详解。在文末有完整代码。)

效果图如下:
这里写图片描述

具体实现过程如下:
第一步:
因为是一个小画板,布局大部分空间要留出来作画,所以只允许按钮占用一小部分空间,实现代码如下:

    <TextView
        android:id="@+id/myView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="重新绘制"/>

由于此处我们还未实现画板,所以暂时使用TextView顶替,在后面再进行替换。

第二步:
新建一个MyView类,使其继承于SurfaceView,并实现其构造方法。此处要记住,因为MyView在后面要作为一个布局元素加入到layout中,所以此种情况下构造函数最好写全:

public MyView(Context context) {
        super(context);
        //初始化,后文实现
        initSurfaceView(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initSurfaceView(context);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initSurfaceView(context);
    }

这样做的原因是因为:从文档上的介绍来看,第二个和第三个构造函数对于XML这种自定义引用方式是必须实现的,这三个构造函数应该是在不同的应用场合来实例化。如果不写全,程序可能会报错。博主第一次就是因为只有第一个构造函数导致程序不能正常运行。

完成构造函数后,就要实现surface的回调接口了,这是为了自定义surfaceview所必须要做的,同时在这里也直接实现OnTouchListener的接口。代码如下:

public class MyView extends SurfaceView implements SurfaceHolder.Callback,View.OnTouchListener{
...
    @Override
    //当surfaceview刚刚被创建时调用
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
    }

    @Override
    //当发生变动时被调用
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
    return false
    }

第三步:
设置画笔、path等性质,代码如下:

void initSurfaceView(Context context){
        //取的surface对象并加载回调函数
        getHolder().addCallback(this);
        //初始化画笔
        paint.setColor(Color.RED);
        //设置画笔风格为空心,这一步不可缺,否则会默认画笔为实心,读者若感兴趣,可去之看效果
        paint.setStyle(Paint.Style.STROKE);
        //画笔粗细
        paint.setStrokeWidth(20);
        //设置触摸监听
        setOnTouchListener(this);
    }

第四步:
设置好画笔等内容,我们就开始写绘制方法,代码很简单,如下:

  public void draw(){
        //定义画布并且锁定画布
        Canvas canvas = getHolder().lockCanvas();
        //开始绘画
        //设置画布颜色
        canvas.drawColor(Color.WHITE);
        canvas.drawPath(path,paint);
        //解锁画布
        getHolder().unlockCanvasAndPost(canvas);
    }

完成draw方法后,开始完善触摸监听,代码如下:

@Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        switch (motionEvent.getAction()){
            case MotionEvent.ACTION_DOWN://摁下
                path.moveTo(motionEvent.getX(),motionEvent.getY());//改变后面操作的起始点位置
                draw();
                break;
            case MotionEvent.ACTION_MOVE://移动
                path.lineTo(motionEvent.getX(),motionEvent.getY());//添加当前点到目标点(x,y)构成的直线到path(注:lineTo起始点默认是屏幕左上角的坐标系原点(0,0)。)
                draw();
                break;
        }
        return true;//这里一定不要忘记改为true,否则只会执行down动作。
    }

同时,一定不要忘记在surfaceCreated中加上draw,因为此方法这是创建surface的第一步:

@Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        draw();
    }

第五步:
最后一步,设置清空。很简单,在myView中写一个方法clear:

public void clear(){
        path.reset();
        draw();
    }

至此,大的步骤已完成,接下来,完善一下layout与mainActivity中的内容,代码如下:
layout中只需替换标签即可:

 <com.example.paint.MyView
        android:id="@+id/myView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

MainActivity中的代码:

package com.example.paint;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final MyView myView = findViewById(R.id.myView);

        Button btn = findViewById(R.id.btn);
        //设置点击监听事件
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myView.clear();
            }
        });
    }
}

myView完整代码如下:

package com.example.paint;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;


public class MyView extends SurfaceView implements SurfaceHolder.Callback,View.OnTouchListener{

    private Paint paint = new Paint();
    private Path path = new Path();

    public MyView(Context context) {
        super(context);
        initSurfaceView(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initSurfaceView(context);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initSurfaceView(context);
    }
void initSurfaceView(Context context){

        getHolder().addCallback(this);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(20);
        setOnTouchListener(this);
    }
    public void draw(){
        //定义画布并且锁定画布
        Canvas canvas = getHolder().lockCanvas();
        //开始绘画
        canvas.drawColor(Color.WHITE);
        canvas.drawPath(path,paint);
        //解锁画布
        getHolder().unlockCanvasAndPost(canvas);
    }

    public void clear(){
        path.reset();
        draw();
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        draw();
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        switch (motionEvent.getAction()){
            case MotionEvent.ACTION_DOWN:
                path.moveTo(motionEvent.getX(),motionEvent.getY());
                draw();
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(motionEvent.getX(),motionEvent.getY());
                draw();
                break;
        }
        return true;
    }
}

写的可能有些繁琐,不过是为了让大家更容易理解,不足之处还请大家多多包涵,并提出宝贵意见!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值