首先,我们先明确步骤:
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;
}
}
写的可能有些繁琐,不过是为了让大家更容易理解,不足之处还请大家多多包涵,并提出宝贵意见!