一种自己写的多指触摸白板控件Demo

多点触摸方面的知识借鉴于Google的代码:

https://github.com/googlearchive/android-BasicMultitouch/blob/master/Application/src/main/java/com/example/android/basicmultitouch/TouchDisplayView.java

 

然后我自己写的这个Demo控件,包含随机颜色Paint构造的办法、Path的二阶贝塞尔曲线的使用办法、在Canvas上分行绘制文本的办法、多指书写的方法等。可以搭配我的仿地图的View来实现可平移、放大绘制上去的内容的白板

自定义控件DrawView:

package com.testFileSystem;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by cjz on 2019/11/25.
 * 绘图用的窗体
 */

public class DrawView extends View{
    private Curv currentCurv;

    /**当前绘制画布**/
    private Canvas canvas;

    /**进行过初始化了吗**/
    private boolean isInitFinished = false;

    /**数据读写根目录**/
    private String rootPath;

    /**控件长宽**/
    private int mWidth, mHeight;

    /**当前绘制画布**/
    private Bitmap canvasBitmap;

    /**是否绘制触摸**/
    private boolean isShowTouchEvent = true;

    /**事件累积**/
    private StringBuffer touchEventStringBuffer = new StringBuffer();



    /**当前正在绘制的线条组合**/
    private Map<Integer, BaseShape> currentDrawingMap = new HashMap<>();

    public DrawView(Context context) {
        super(context);
    }

    public DrawView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public DrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if(!isInitFinished){
            rootPath = getContext().getFilesDir().getAbsolutePath()  + File.separatorChar + "drawView";
            File rootDir = new File(rootPath);
            if(!rootDir.exists()){
                rootDir.mkdir();
            }
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            mWidth = width;
            mHeight = height;
            canvasBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
            canvas = new Canvas(canvasBitmap);
            isInitFinished = true;
        }
    }


    /**获取绘制笔**/
    private Paint makePaint(){
        Paint paint = new Paint();
        paint.setStrokeWidth(12f);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setAntiAlias(true);
        int color = 0xFF000000;
        //随机颜色
        color |= ((int) (Math.random() * 255 + 1) << 16);
        color |= ((int) (Math.random() * 255 + 1) << 8);
        color |= ((int) (Math.random() * 255 + 1));
        paint.setColor(color);
        return paint;
    }

    /**书写**/
    private void penDraw(MotionEvent event) {
        int actionType = event.getAction() & MotionEvent.ACTION_MASK;
        switch (actionType){
            case MotionEvent.ACTION_POINTER_DOWN: {
                Log.i("penDraw_AT", "MotionEvent.ACTION_POINTER_DOWN");
                int id = event.getPointerId(event.getActionIndex());
                touchEventStringBuffer.append("MotionEvent.ACTION_DOWN, id:" + id + "\n");
                Paint paint = makePaint();
                currentCurv = new Curv(paint);
                currentCurv.draw(event.getX(event.getActionIndex()), event.getY(event.getActionIndex()), event.getAction(), canvas);
                currentDrawingMap.put(id, currentCurv);
                break;
            }
            case MotionEvent.ACTION_DOWN: {
                int id = event.getPointerId(event.getActionIndex());
                touchEventStringBuffer.append("MotionEvent.ACTION_DOWN, id:" + id + "\n");
                Paint paint = makePaint();
                currentCurv = new Curv(paint);
                currentCurv.draw(event.getX(event.getActionIndex()), event.getY(event.getActionIndex()), event.getAction(), canvas);
                currentDrawingMap.put(id, currentCurv);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                for (int i = 0; i < event.getPointerCount(); i++) {
                    int id = event.getPointerId(i);
                    touchEventStringBuffer.append("MotionEvent.ACTION_MOVE, id:" + id + "\n");
                    Curv curv = (Curv) currentDrawingMap.get(id);
                    if (curv != null) {
                        curv.draw(event.getX(i), event.getY(i), event.getAction(), canvas);
                    }
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                int id = event.getPointerId(event.getActionIndex());
                currentDrawingMap.remove(id);
                break;
            }
            case MotionEvent.ACTION_POINTER_UP: {
                int id = event.getPointerId(event.getActionIndex());
                currentDrawingMap.remove(id);
                break;
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(getClass().getName(), event.toString());
        penDraw(event);
        invalidate();
        return true;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(canvasBitmap, 0, 0, null);
        if(isShowTouchEvent) {
            //顺便随手写个多行文本框示例
            float fontSize = 20f;
            Paint paint = new Paint();
            paint.setStyle(Paint.Style.STROKE);
            paint.setAntiAlias(true);
            paint.setColor(Color.RED);
            paint.setStrokeWidth(1f);
            paint.setTextSize(fontSize);
            //显示触摸事件
            String eventStr[] = touchEventStringBuffer.toString().split("\n");
            for(int i = 0; i < eventStr.length; i++){
                canvas.drawText(eventStr[i], 0, fontSize * (i + 1), paint);
            }
            touchEventStringBuffer = new StringBuffer();
        }
    }
}

绘制线条对象类: 

package com.testFileSystem;

import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.MotionEvent;

import java.util.ArrayList;
import java.util.List;

import javax.microedition.khronos.opengles.GL10;

/**
 *  曲线容器,一个容器的曲线>=1
 * Created by cjz on 2018/9/17.
 */

public class Curv extends BaseShape{
    public Paint paint;
    private List<PointF> touchPointList = new ArrayList<>();
    private List<Path> segPathList = new ArrayList<>(); //用于加速画布的一小段一小段的path
    private List<Path> pathList = new ArrayList<>(); //用于保存该容器里面有多少个path
    /**
     * 点信息
     */
    private PointF start;
    private PointF last;
    private PointF current;
    private PointF mid;
    private PointF end;
    private float cx;
    private float cy;
    private float midX;
    private float midY;
    private float startX;
    private float startY;
    /**
     * 绘制范围
     */
    public RectF range;
    private float width = 2 / 3.3f;

    private boolean isStart = false;
    public Path totalPath;
    private Path drawPath;

    private void init() {
        start = new PointF();
        last = new PointF();
        current = new PointF();
        mid = new PointF();
        end = new PointF();
        range = new RectF();
        totalPath = new Path();
        pathList.add(totalPath);
    }

    public boolean isStart() {
        return isStart;
    }


    public Curv(Paint paint) {
        this.paint = new Paint(paint);
        init();
    }

    /**
     * 处理范围
     *
     * @param x 判断点x
     * @param y 判断点y
     */
    private void handleRange(float x, float y) {
        if (x <= range.left)
            range.left = x;
        if (y <= range.top)
            range.top = y;
        if (x >= range.right)
            range.right = x;
        if (y >= range.bottom)
            range.bottom = y;
    }

    public Rect getRect() {
        int padding = (int) (paint.getStrokeWidth() / 2 + 5);
        RectF rectF = new RectF();
        drawPath.computeBounds(rectF, true);
        return new Rect((int) rectF.left - padding, (int) rectF.top - padding, (int) rectF.right + padding, (int) rectF.bottom + padding);
    }

    public void setCurrentRaw(float x, float y, int action) {
        //记录起始点
        if (!isStart) {
            start.set(x, y);
            mid.set(x,y);
            end.set(x,y);
            isStart = true;
        }


        //记录上一个点
        last.set(current.x, current.y);

        //记录当前点
        current.set(x, y);

        //处理范围
        handleRange(x, y);

    }
    /**
     * 落闸放点(狗),贝塞尔曲线化
     *
     * @param x
     * @param y
     * @param action
     */
    public void draw(float x, float y, int action, Canvas canvas) {
        if(!isStart()) {
            setCurrentRaw(x, y, action);

            totalPath.moveTo(x, y);
//            if(!isBuildPathAllDoing)
            touchPointList.add(new PointF(x, y));
            segPathList.add(new Path());
            canvas.drawPath(segPathList.get(segPathList.size() - 1), paint);

        } else {
            if (action == MotionEvent.ACTION_UP)
                System.out.println("setCurrent end " + x + " , " + y);
            touchPointList.add(new PointF(x, y));
            drawPath = new Path();
            segPathList.add(drawPath);
            setCurrentRaw(x, y, action);

            double distance = Math.sqrt(Math.pow(Math.abs(x - last.x), 2) + Math.pow(Math.abs(y - last.y), 2));
            /**如果两次点击之间的距离过大,就判断为该点报废,Current点回退到last点**/
            if (distance > 400) {  //如果距离突变过长,判断为无效点,直接current回退到上一次纪录的last的点,并且用UP时间结束这次path draw
                Log.i("NewCurv.SetCurrent", "超长" + distance);
//                super.setCurrent(getLast().x, getLast().y, MotionEvent.ACTION_UP);
                System.out.println("超长?");
                return;
            }
            cx = last.x;
            cy = last.y;

            midX = (x + cx) / 2;
            midY = (y + cy) / 2;

            startX = mid.x;
            startY = mid.y;

            mid.x = midX;
            mid.y = midY;

            drawPath.moveTo(startX, startY);

            double s = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2));
            if (action == MotionEvent.ACTION_UP){
                drawPath.lineTo(x,y);
                totalPath.lineTo(x, y);
            } else {
                if (s < 200) {
                    if (s < 2) {//1.10 //2.12 //3.15
                        drawPath.cubicTo(cx, cy, midX, midY, x, y);
                        totalPath.cubicTo(cx, cy, midX, midY, x, y);
                        System.out.println("cubicTo");
                    } else {
                        drawPath.quadTo(cx, cy, midX, midY);
                        totalPath.quadTo(cx, cy, midX, midY);
//                    System.out.println("quadTo");
                    }
                } else {
                    drawPath.quadTo(cx, cy, midX, midY);
                    totalPath.quadTo(cx, cy, midX, midY);
                }
            }
            canvas.drawPath(segPathList.get(segPathList.size() - 1), paint);

        }
        //抬起时把画好的线段生成OpenGL线段
//        if(action == MotionEvent.ACTION_UP) {
//            //OpenGL此时DPI和Canvas不一样,要放大再对景区
//            Path path = new Path();
//            Matrix matrix = new Matrix();
//            matrix.postScale(UITrees.openGLRenderer.scale / 2, UITrees.openGLRenderer.scale / 2, UITrees.panelView.scaleCenterPoint.x, UITrees.panelView.scaleCenterPoint.y);
//            totalPath.transform(matrix, path);
//
//            PathMeasure pathMeasure = new PathMeasure();
//            pathMeasure.setPath(path, false);
//            float step = 10f / paint.getStrokeWidth() > 1 ? 10f / paint.getStrokeWidth() : 1; //粗线条的点密度设置大一些咯
//
//            float[] point = new float[2];
//            for(float i = 0; i < pathMeasure.getLength(); i += step) {
//                pathMeasure.getPosTan(i, point, null);
//                //todo 缩放之后,Canvas再加Path的时候还是采用实际点,但OpenGL用了这个点就和Canvas的不对齐了,因为OpenGL缩放是把画布前后推,要做做换算,例如缩放小了,左上角的坐标是画布外的坐标
//
//                float realtiveX = point[0] / 1080 * 4f - UITrees.openGLRenderer.dx;  //4个象限
//                float realtiveY = -point[1] / 1080 * 4f + UITrees.openGLRenderer.dy ;
//
//                glLine.drawLine(realtiveX, realtiveY);
//            }
//        }
        if(action == MotionEvent.ACTION_UP) {
            if(totalPath != null && paint != null){
                PathMeasure pathMeasure = new PathMeasure(totalPath, false);
                if(pathMeasure.getLength() < 2f){
                    paint.setStyle(Paint.Style.FILL);
                    totalPath = new Path();
                    totalPath.addCircle(x + paint.getStrokeWidth() / 2f, y + paint.getStrokeWidth() / 2f, paint.getStrokeWidth() / 2f, Path.Direction.CCW);
                    canvas.drawPath(totalPath, paint);
                }
            }
        }
    }
}

使用效果:

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值