Java Android:如何移动我的画布可绘制形状 我正在尝试在Android中实现一个绘图应用程序,用户应该能够选择和移动绘制的形状。 目前,我已经在绘图画布上静态绘制了一些矩形和文本 我想选择(在所选形状上绘制边框)并在绘图画布的onTouch事件中移动绘制的

Java Android:如何移动我的画布可绘制形状 我正在尝试在Android中实现一个绘图应用程序,用户应该能够选择和移动绘制的形状。 目前,我已经在绘图画布上静态绘制了一些矩形和文本 我想选择(在所选形状上绘制边框)并在绘图画布的onTouch事件中移动绘制的,java,android,Java,Android,Android:如何移动我的画布可绘制形状 我正在尝试在Android中实现一个绘图应用程序,用户应该能够选择和移动绘制的形状。 目前,我已经在绘图画布上静态绘制了一些矩形和文本 我想选择(在所选形状上绘制边框)并在绘图画布的onTouch事件中移动绘制的形状 package com.android.graphics; 导入android.content.Context; 导入android.graphics.Bitmap; 导入android.graphics.Bitmap.CompressF

Android:如何移动我的画布可绘制形状
  • 我正在尝试在Android中实现一个绘图应用程序,用户应该能够选择和移动绘制的形状。 目前,我已经在绘图画布上静态绘制了一些矩形和文本
  • 我想选择(在所选形状上绘制边框)并在绘图画布的
    onTouch
    事件中移动绘制的形状
  • package com.android.graphics;
    导入android.content.Context;
    导入android.graphics.Bitmap;
    导入android.graphics.Bitmap.CompressFormat;
    导入android.graphics.BitmapFactory;
    导入android.graphics.Canvas;
    导入android.graphics.Color;
    导入android.graphics.Paint;
    导入android.graphics.Path;
    导入android.graphics.PathEffect;
    导入android.graphics.PorterDuff;
    导入android.graphics.PorterDuffXfermode;
    导入android.graphics.RectF;
    导入android.graphics.Typeface;
    导入android.util.AttributeSet;
    导入android.view.MotionEvent;
    导入android.view.view;
    导入java.io.ByteArrayOutputStream;
    导入java.util.ArrayList;
    导入java.util.List;
    公共类画布视图扩展视图{
    公共枚举模式{
    画
    文本,
    橡皮擦;
    }
    公共枚举抽屉{
    笔,
    行,,
    矩形,
    圆圈
    椭圆,
    二次贝塞尔,
    库比克·贝塞尔;
    }
    私有画布=null;
    私有位图=空;
    私有列表路径列表=新的ArrayList();
    私有列表paintlist=new ArrayList();
    私有最终油漆清空油漆=新油漆();
    private int baseColor=Color.WHITE;
    //要撤消,请重做
    私有int历史指针=0;
    //旗帜
    私有模式=模式.DRAW;
    私人抽屉=Drawer.PEN;
    私有布尔值isDown=false;
    //油漆
    private Paint.Style paintStyle=Paint.Style.STROKE;
    private int paintStrokeColor=Color.BLACK;
    私有int paintFillColor=Color.BLACK;
    专用浮子油漆冲程宽度=3F;
    私有整数不透明度=255;
    私有浮动模糊=0F;
    private Paint.Cap lineCap=Paint.Cap.ROUND;
    private PathEffect drawPathEffect=null;
    //用于文本
    私有字符串text=“”;
    private Typeface fontFamily=Typeface.DEFAULT;
    私有浮动字体大小=32F;
    private Paint.Align text Align=Paint.Align.RIGHT;//已修复
    私有绘画文本绘画=新绘画();
    私有浮动文本x=0F;
    私有浮动文本y=0F;
    //抽屉
    专用浮点数startX=0F;
    私人浮动起点=0F;
    专用浮点控制x=0F;
    私有浮动控制y=0F;
    公共画布视图(上下文、属性集属性、int-defStyle){
    超级(上下文、属性、定义样式);
    这个.setup();
    }
    公共画布视图(上下文、属性集属性){
    超级(上下文,attrs);
    这个.setup();
    }
    公共画布视图(上下文){
    超级(上下文);
    这个.setup();
    }
    私有无效设置(){
    this.pathLists.add(新路径());
    this.paintlist.add(this.createPaint());
    这个.historyPointer++;
    this.textPaint.setARGB(0,255,255,255);
    }
    私有绘制createPaint(){
    油漆=新油漆();
    paint.setAntiAlias(真);
    paint.setStyle(这个paintStyle);
    paint.setStrokeWidth(此paintStrokeWidth);
    油漆.固定行程盖(此.线盖);
    paint.setStrokeJoin(paint.Join.MITER);//已修复
    //用于文本
    if(this.mode==mode.TEXT){
    paint.setTypeface(此.fontFamily);
    paint.setTextSize(此.fontSize);
    paint.setTextAlign(this.textAlign);
    油漆。设置行程宽度(0F);
    }
    if(this.mode==mode.橡皮擦){
    //橡皮擦
    setXfermode(新的PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    setARGB(0,0,0,0);
    }否则{
    //否则
    paint.setColor(此.paintStrokeColor);
    paint.setShadowLayer(this.blur,0F,0F,this.paintStrokeColor);
    paint.setAlpha(这个是不透明度);
    paint.setPathEffect(this.drawPathEffect);
    }
    返漆;
    }
    专用路径createPath(MotionEvent事件){
    路径路径=新路径();
    //保存以进行操作\u移动
    this.startX=event.getX();
    this.startY=event.getY();
    path.moveTo(this.startX,this.startY);
    返回路径;
    }
    私有void updateHistory(路径){
    if(this.historyPointer==this.pathLists.size()){
    this.pathLists.add(路径);
    this.paintlist.add(this.createPaint());
    这个.historyPointer++;
    }否则{
    //在撤消或重做的过程中
    this.pathLists.set(this.historyPointer,path);
    this.paintLists.set(this.historyPointer,this.createPaint());
    这个.historyPointer++;
    for(int i=this.historyPointer,size=this.paintLists.size();i=0)和&(不透明度=0){
    this.blur=模糊;
    }否则{
    此.blur=0F;
    }
    }
    public Paint.Cap getLineCap(){
    返回此.lineCap;
    }
    公共无效设置linecap(Paint.Cap){
    this.lineCap=cap;
    }
    公共路径效果getDrawPathEffect(){
    回归效应;
    }
    公共无效setDrawPathEffect(PathEffect drawPathEffect){
    这种效应
    
    package com.android.graphics;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Bitmap.CompressFormat;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.PathEffect;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffXfermode;
    import android.graphics.RectF;
    import android.graphics.Typeface;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    
    import java.io.ByteArrayOutputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    public class CanvasView extends View {
        public enum Mode {
            DRAW,
            TEXT,
            ERASER;
        }
    
        public enum Drawer {
            PEN,
            LINE,
            RECTANGLE,
            CIRCLE,
            ELLIPSE,
            QUADRATIC_BEZIER,
            QUBIC_BEZIER;
        }
    
        private Canvas canvas = null;
        private Bitmap bitmap = null;
        private List<Path> pathLists = new ArrayList<Path>();
        private List<Paint> paintLists = new ArrayList<Paint>();
        private final Paint emptyPaint = new Paint();
        private int baseColor = Color.WHITE;
        // for Undo, Redo
        private int historyPointer = 0;
        // Flags
        private Mode mode = Mode.DRAW;
        private Drawer drawer = Drawer.PEN;
        private boolean isDown = false;
        // for Paint
        private Paint.Style paintStyle = Paint.Style.STROKE;
        private int paintStrokeColor = Color.BLACK;
        private int paintFillColor = Color.BLACK;
        private float paintStrokeWidth = 3F;
        private int opacity = 255;
        private float blur = 0F;
        private Paint.Cap lineCap = Paint.Cap.ROUND;
        private PathEffect drawPathEffect = null;
        // for Text
        private String text = "";
        private Typeface fontFamily = Typeface.DEFAULT;
        private float fontSize = 32F;
        private Paint.Align textAlign = Paint.Align.RIGHT;  // fixed
        private Paint textPaint = new Paint();
        private float textX = 0F;
        private float textY = 0F;
        // for Drawer
        private float startX = 0F;
        private float startY = 0F;
        private float controlX = 0F;
        private float controlY = 0F;
    
        public CanvasView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            this.setup();
        }
    
        public CanvasView(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.setup();
        }
    
        public CanvasView(Context context) {
            super(context);
            this.setup();
        }
    
        private void setup() {
            this.pathLists.add(new Path());
            this.paintLists.add(this.createPaint());
            this.historyPointer++;
            this.textPaint.setARGB(0, 255, 255, 255);
        }
    
        private Paint createPaint() {
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setStyle(this.paintStyle);
            paint.setStrokeWidth(this.paintStrokeWidth);
            paint.setStrokeCap(this.lineCap);
            paint.setStrokeJoin(Paint.Join.MITER);  // fixed
            // for Text
            if (this.mode == Mode.TEXT) {
                paint.setTypeface(this.fontFamily);
                paint.setTextSize(this.fontSize);
                paint.setTextAlign(this.textAlign);
                paint.setStrokeWidth(0F);
            }
            if (this.mode == Mode.ERASER) {
                // Eraser
                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
                paint.setARGB(0, 0, 0, 0);
    
            } else {
                // Otherwise
                paint.setColor(this.paintStrokeColor);
                paint.setShadowLayer(this.blur, 0F, 0F, this.paintStrokeColor);
                paint.setAlpha(this.opacity);
                paint.setPathEffect(this.drawPathEffect);
            }
            return paint;
        }
    
        private Path createPath(MotionEvent event) {
            Path path = new Path();
            // Save for ACTION_MOVE
            this.startX = event.getX();
            this.startY = event.getY();
            path.moveTo(this.startX, this.startY);
            return path;
        }
    
        private void updateHistory(Path path) {
            if (this.historyPointer == this.pathLists.size()) {
                this.pathLists.add(path);
                this.paintLists.add(this.createPaint());
                this.historyPointer++;
            } else {
                // On the way of Undo or Redo
                this.pathLists.set(this.historyPointer, path);
                this.paintLists.set(this.historyPointer, this.createPaint());
                this.historyPointer++;
    
                for (int i = this.historyPointer, size = this.paintLists.size(); i < size; i++) {
                    this.pathLists.remove(this.historyPointer);
                    this.paintLists.remove(this.historyPointer);
                }
            }
        }
    
        private Path getCurrentPath() {
            return this.pathLists.get(this.historyPointer - 1);
        }
    
        private void drawText(Canvas canvas) {
            if (this.text.length() <= 0) {
                return;
            }
            if (this.mode == Mode.TEXT) {
                this.textX = this.startX;
                this.textY = this.startY;
                this.textPaint = this.createPaint();
            }
            float textX = this.textX;
            float textY = this.textY;
            Paint paintForMeasureText = new Paint();
            // Line break automatically
            float textLength = paintForMeasureText.measureText(this.text);
            float lengthOfChar = textLength / (float) this.text.length();
            float restWidth = this.canvas.getWidth() - textX;  // text-align : right
            int numChars = (lengthOfChar <= 0) ? 1 : (int) Math.floor((double) (restWidth / lengthOfChar));  // The number of characters at 1 line
            int modNumChars = (numChars < 1) ? 1 : numChars;
            float y = textY;
            for (int i = 0, len = this.text.length(); i < len; i += modNumChars) {
                String substring = "";
                if ((i + modNumChars) < len) {
                    substring = this.text.substring(i, (i + modNumChars));
                } else {
                    substring = this.text.substring(i, len);
                }
                y += this.fontSize;
                canvas.drawText(substring, textX, y, this.textPaint);
            }
        }
    
        private void onActionDown(MotionEvent event) {
            switch (this.mode) {
                case DRAW:
                case ERASER:
                    if ((this.drawer != Drawer.QUADRATIC_BEZIER) && (this.drawer != Drawer.QUBIC_BEZIER)) {
                        // Oherwise
                        this.updateHistory(this.createPath(event));
                        this.isDown = true;
                    } else {
                        // Bezier
                        if ((this.startX == 0F) && (this.startY == 0F)) {
                            // The 1st tap
                            this.updateHistory(this.createPath(event));
                        } else {
                            // The 2nd tap
                            this.controlX = event.getX();
                            this.controlY = event.getY();
    
                            this.isDown = true;
                        }
                    }
                    break;
                case TEXT:
                    this.startX = event.getX();
                    this.startY = event.getY();
    
                    break;
                default:
                    break;
            }
        }
    
    
        private void onActionMove(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
            switch (this.mode) {
                case DRAW:
                case ERASER:
                    if ((this.drawer != Drawer.QUADRATIC_BEZIER) && (this.drawer != Drawer.QUBIC_BEZIER)) {
                        if (!isDown) {
                            return;
                        }
                        Path path = this.getCurrentPath();
                        switch (this.drawer) {
                            case PEN:
                                path.lineTo(x, y);
                                break;
                            case LINE:
                                path.reset();
                                path.moveTo(this.startX, this.startY);
                                path.lineTo(x, y);
                                break;
                            case RECTANGLE:
                                path.reset();
                                float left = Math.min(this.startX, x);
                                float right = Math.max(this.startX, x);
                                float top = Math.min(this.startY, y);
                                float bottom = Math.max(this.startY, y);
                                path.addRect(left, top, right, bottom, Path.Direction.CCW);
                                break;
                            case CIRCLE:
                                double distanceX = Math.abs((double) (this.startX - x));
                                double distanceY = Math.abs((double) (this.startX - y));
                                double radius = Math.sqrt(Math.pow(distanceX, 2.0) + Math.pow(distanceY, 2.0));
                                path.reset();
                                path.addCircle(this.startX, this.startY, (float) radius, Path.Direction.CCW);
                                break;
                            case ELLIPSE:
                                RectF rect = new RectF(this.startX, this.startY, x, y);
                                path.reset();
                                path.addOval(rect, Path.Direction.CCW);
                                break;
                            default:
                                break;
                        }
                    } else {
                        if (!isDown) {
                            return;
                        }
                        Path path = this.getCurrentPath();
                        path.reset();
                        path.moveTo(this.startX, this.startY);
                        path.quadTo(this.controlX, this.controlY, x, y);
                    }
                    break;
                case TEXT:
                    this.startX = x;
                    this.startY = y;
                    break;
                default:
                    break;
            }
        }
    
        private void onActionUp(MotionEvent event) {
            if (isDown) {
                this.startX = 0F;
                this.startY = 0F;
                this.isDown = false;
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            // Before "drawPath"
            canvas.drawColor(this.baseColor);
            if (this.bitmap != null) {
                canvas.drawBitmap(this.bitmap, 0F, 0F, emptyPaint);
            }
            for (int i = 0; i < this.historyPointer; i++) {
                Path path = this.pathLists.get(i);
                Paint paint = this.paintLists.get(i);
                canvas.drawPath(path, paint);
            }
            this.drawText(canvas);
            this.canvas = canvas;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    this.onActionDown(event);
                    break;
                case MotionEvent.ACTION_MOVE:
                    this.onActionMove(event);
                    break;
                case MotionEvent.ACTION_UP:
                    this.onActionUp(event);
                    break;
                default:
                    break;
            }
            // Re draw
            this.invalidate();
            return true;
        }
    
        public Mode getMode() {
            return this.mode;
        }
    
        public void setMode(Mode mode) {
            this.mode = mode;
        }
    
        public Drawer getDrawer() {
            return this.drawer;
        }
    
        public void setDrawer(Drawer drawer) {
            this.drawer = drawer;
        }
    
        public boolean canUndo() {
            return this.historyPointer > 1;
        }
    
        public boolean canRedo() {
            return this.historyPointer < this.pathLists.size();
        }
    
        public boolean undo() {
            if (canUndo()) {
                this.historyPointer--;
                this.invalidate();
                return true;
            } else {
                return false;
            }
        }
    
        public boolean redo() {
            if (canRedo()) {
                this.historyPointer++;
                this.invalidate();
                return true;
            } else {
                return false;
            }
        }
    
        public void clear() {
            Path path = new Path();
            path.moveTo(0F, 0F);
            path.addRect(0F, 0F, 3000F, 3000F, Path.Direction.CCW);
            path.close();
            Paint paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setStyle(Paint.Style.FILL);
            if (this.historyPointer == this.pathLists.size()) {
                this.pathLists.add(path);
                this.paintLists.add(paint);
                this.historyPointer++;
            } else {
                // On the way of Undo or Redo
                this.pathLists.set(this.historyPointer, path);
                this.paintLists.set(this.historyPointer, paint);
                this.historyPointer++;
    
                for (int i = this.historyPointer, size = this.paintLists.size(); i < size; i++) {
                    this.pathLists.remove(this.historyPointer);
                    this.paintLists.remove(this.historyPointer);
                }
            }
            this.text = "";
            // Clear
            this.invalidate();
        }
    
        public int getBaseColor() {
            return this.baseColor;
        }
    
        public void setBaseColor(int color) {
            this.baseColor = color;
        }
    
        public String getText() {
            return this.text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    
        public Paint.Style getPaintStyle() {
            return this.paintStyle;
        }
    
        public void setPaintStyle(Paint.Style style) {
            this.paintStyle = style;
        }
    
        public int getPaintStrokeColor() {
            return this.paintStrokeColor;
        }
    
        public void setPaintStrokeColor(int color) {
            this.paintStrokeColor = color;
        }
    
        public int getPaintFillColor() {
            return this.paintFillColor;
        }
    
        public void setPaintFillColor(int color) {
            this.paintFillColor = color;
        }
    
        public float getPaintStrokeWidth() {
            return this.paintStrokeWidth;
        }
    
        public void setPaintStrokeWidth(float width) {
            if (width >= 0) {
                this.paintStrokeWidth = width;
            } else {
                this.paintStrokeWidth = 3F;
            }
        }
    
        public int getOpacity() {
            return this.opacity;
        }
    
        public void setOpacity(int opacity) {
            if ((opacity >= 0) && (opacity <= 255)) {
                this.opacity = opacity;
            } else {
                this.opacity = 255;
            }
        }
    
        public float getBlur() {
            return this.blur;
        }
    
        public void setBlur(float blur) {
            if (blur >= 0) {
                this.blur = blur;
            } else {
                this.blur = 0F;
            }
        }
    
        public Paint.Cap getLineCap() {
            return this.lineCap;
        }
    
        public void setLineCap(Paint.Cap cap) {
            this.lineCap = cap;
        }
    
        public PathEffect getDrawPathEffect() {
            return drawPathEffect;
        }
    
        public void setDrawPathEffect(PathEffect drawPathEffect) {
            this.drawPathEffect = drawPathEffect;
        }
    
        public float getFontSize() {
            return this.fontSize;
        }
    
        public void setFontSize(float size) {
            if (size >= 0F) {
                this.fontSize = size;
            } else {
                this.fontSize = 32F;
            }
        }
    
        public Typeface getFontFamily() {
            return this.fontFamily;
        }
    
        public void setFontFamily(Typeface face) {
            this.fontFamily = face;
        }
    
        public Bitmap getBitmap() {
            this.setDrawingCacheEnabled(false);
            this.setDrawingCacheEnabled(true);
    
            return Bitmap.createBitmap(this.getDrawingCache());
        }
    
        public Bitmap getScaleBitmap(int w, int h) {
            this.setDrawingCacheEnabled(false);
            this.setDrawingCacheEnabled(true);
            return Bitmap.createScaledBitmap(this.getDrawingCache(), w, h, true);
        }
    
        public void drawBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
            this.invalidate();
        }
    
        public void drawBitmap(byte[] byteArray) {
            this.drawBitmap(BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length));
        }
    
        public static byte[] getBitmapAsByteArray(Bitmap bitmap, CompressFormat format, int quality) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            bitmap.compress(format, quality, byteArrayOutputStream);
            return byteArrayOutputStream.toByteArray();
        }
    
        public byte[] getBitmapAsByteArray(CompressFormat format, int quality) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            this.getBitmap().compress(format, quality, byteArrayOutputStream);
    
            return byteArrayOutputStream.toByteArray();
        }
    
        public byte[] getBitmapAsByteArray() {
            return this.getBitmapAsByteArray(CompressFormat.PNG, 100);
        }
    }