Android Open Source - DNP Drawing View






From Project

Back to project page DNP.

License

The source code is released under:

GNU General Public License

If you think the Android project DNP listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.tacitus.dnp.widget;
//w  ww .j  a v a 2  s  .  c  om
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Handler;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import com.tacitus.dnp.R;

import junit.framework.Assert;

import java.util.ArrayList;

public class DrawingView extends View {

    private class DrawWatcher implements Runnable {

        @Override
        public void run() {
            resetColorOrder();
        }
    }

    private class DrawPath {
        private Path mDrawPath;
        private Paint mDrawPaint;
        private Paint mDrawPaintHollow;
        private float mSize;
        private float mSizeHollow;
        private float mPressure;


        private DrawPath(float size, float pressure, int color) {
            mDrawPath = new Path();
            mDrawPaint = createPaint();
            mDrawPaint.setColor(color);
            mSize = size;
            mPressure = pressure;
            if (!mTouchSizeMode) {
                mSize = mBrushSize;
            }
            // Create Hollow paint only if eraseMode is not enable
            if (mHollowMode && !mEraseMode) {
                mSizeHollow = mSize - (mSize * HOLLOW_LINE_THICKNESS_RATIO / 100);
                mDrawPaintHollow = createPaint();
                mDrawPaintHollow.setStrokeWidth(mSizeHollow);
                mDrawPaintHollow.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            }
            mDrawPaint.setStrokeWidth(mSize);
            if (mUnderlineMode && !mEraseMode) {
                mDrawPaint.setShadowLayer(1f, mSize / 2, mSize / 2, Color.BLACK);
            }
            // WARNING: When changing color needs to define alpha AFTER
            // Because color contains some alpha setting.
            mDrawPaint.setAlpha(mPaintAlpha);
            if (mPressureMode) {
                mDrawPaint.setAlpha((int) (mPressure * 255));
            }
        }

        public void moveTo(float x, float y) {
            mDrawPath.moveTo(x, y);
        }

        public void lineTo(float x, float y) {
            mDrawPath.lineTo(x, y);
        }

        public void drawCircle(float x, float y) {
            mDrawCanvas.drawCircle(x, y, mSize / 2, mDrawPaint);
            if (mHollowMode && !mEraseMode) {
                mDrawCanvas.drawCircle(x, y, mSizeHollow / 2, mDrawPaintHollow);
            }

        }

        public void drawPath() {
            mDrawCanvas.drawPath(mDrawPath, mDrawPaint);
            if (mDrawPaintHollow != null) {
                mDrawCanvas.drawPath(mDrawPath, mDrawPaintHollow);
            }
        }

        public void drawPath(Canvas canvas) {
            canvas.drawPath(mDrawPath, mDrawPaint);
            if (mDrawPaintHollow != null) {
                canvas.drawPath(mDrawPath, mDrawPaintHollow);
            }
        }

        public void resetPath() {
            mDrawPath.reset();
        }

    }

    private Handler mHandler;
    private DrawWatcher mDrawWatcher = new DrawWatcher();

    //drawing path & paint
    private SparseArray<DrawPath> mDrawPaths = new SparseArray<DrawPath>();
    private SparseArray<Integer> mDrawPaintColors = new SparseArray<Integer>();
    private ArrayList<DrawPath> mDrawPathsHistory = new ArrayList<DrawPath>();
    private ArrayList<DrawPath> mDrawPathsRedoable = new ArrayList<DrawPath>();
    private int mCurrentColorCursor;

    private final int HOLLOW_LINE_THICKNESS_RATIO = 20;

    private Paint mCanvasPaint;
    //initial color
    private int mPaintColor = 0xFF660000;
    //canvas
    private Canvas mDrawCanvas;
    private boolean mHollowMode = false;
    private boolean mUnderlineMode = false;
    private boolean mTouchSizeMode = true;
    private boolean mEraseMode = false;
    private boolean mPressureMode = false;
    private boolean mOldTabletMode = false;

    private Bitmap mCanvasBitmap;
    private Bitmap mLoadedBitmap;

    private float mBrushSize;
    private float mBrushSizeOldTablet;

    private int mPaintAlpha;

    public DrawingView(Context context, AttributeSet attrs){
        super(context, attrs);
        resetColorOrder();
        Resources resources = getResources();
        Assert.assertNotNull(resources);
        mCanvasPaint = new Paint(Paint.DITHER_FLAG);
        setBrushSize(resources.getInteger(R.integer.initial_size) * 2);
        mHandler = new Handler();
    }

    public void initDrawWatcherTimer() {
        mHandler.removeCallbacks(mDrawWatcher);
        mHandler.postDelayed(mDrawWatcher, 10000);
    }

    private Paint createPaint() {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);

        if (mEraseMode) {
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        }
        return paint;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldW, int oldH) {
        //view given size
        super.onSizeChanged(w, h, oldW, oldH);
        if (mCanvasBitmap != null) {
            mCanvasBitmap.recycle();
        }
        mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mDrawCanvas = new Canvas(mCanvasBitmap);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        // Draw the bitmap on hardware canvas
        canvas.drawBitmap(mCanvasBitmap, 0, 0, mCanvasPaint);
        // Temporary drawing current drawn paths on hardware canvas
        for (int i=0; i<mDrawPaths.size(); i++) {
            DrawPath drawPath = mDrawPaths.valueAt(i);
            drawPath.drawPath(canvas);
        }


    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float touchX = MotionEventCompat.getX(event, MotionEventCompat.getActionIndex(event));
        float touchY = MotionEventCompat.getY(event, MotionEventCompat.getActionIndex(event));

        // Index of multiple touch event:
        int index = MotionEventCompat.getActionIndex(event);
        // Id of multiple touch event
        int id = MotionEventCompat.getPointerId(event, index);


        InputDevice device = event.getDevice();
        Assert.assertNotNull(device);
        InputDevice.MotionRange motionRangeMajor = device.getMotionRange(MotionEvent.AXIS_TOUCH_MAJOR);
        InputDevice.MotionRange motionRangeMinor = device.getMotionRange(MotionEvent.AXIS_TOUCH_MINOR);
        Assert.assertNotNull(motionRangeMajor);
        Assert.assertNotNull(motionRangeMinor);
        float touchMajorMax = motionRangeMajor.getMax();
        float touchMinorMax = motionRangeMinor.getMax();

        float multiplier = 2700;
        if (mOldTabletMode) {
            multiplier *= mBrushSizeOldTablet;
        }
        float size = (((event.getTouchMajor(index) / touchMajorMax) + (event.getTouchMinor(index) / touchMinorMax)) / 2) * multiplier;
        float pressure = event.getPressure(index);
        DrawPath drawPath;

        switch (MotionEventCompat.getActionMasked(event)) {
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_DOWN:
//                Log.logEvent(event);

                // Create path and draw a small line of 1 pixel:
                Integer color = getColor(mCurrentColorCursor);
                if (color != null) {
                    drawPath = new DrawPath(size, pressure, color);
                    drawPath.moveTo(touchX, touchY);
                    drawPath.lineTo(touchX - 1, touchY - 1);
                    mDrawPaths.put(id, drawPath);
                    // This will call the onDraw callback to draw the path temporarily in hardware canvas:
                    invalidate();
                } else if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN){
                    // Toast only for ACTION_DOWNto avoid spamming.
                    Context context = getContext();
                    Assert.assertNotNull(context);
                    Toast noColorChosen = Toast.makeText(context,
                            R.string.no_color_chosen, Toast.LENGTH_SHORT);
                    noColorChosen.show();
                }
                initDrawWatcherTimer();
                break;

            case MotionEvent.ACTION_MOVE:
                // In case of ACTION_MOVE event we update all paths:
                for (int i = 0; i < MotionEventCompat.getPointerCount(event); i++) {
                    int currentId = MotionEventCompat.getPointerId(event, i);
                    drawPath = mDrawPaths.get(currentId);
                    if (drawPath != null) {
                        touchX = MotionEventCompat.getX(event, i);
                        touchY = MotionEventCompat.getY(event, i);
                        drawPath.lineTo(touchX, touchY);
                    }
                }
                initDrawWatcherTimer();
                // This will call the onDraw callback to draw the path temporarily in hardware canvas:
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                mCurrentColorCursor++;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_POINTER_UP:
                // Delete path:
                drawPath = mDrawPaths.get(id);
                if (drawPath != null) {
                    drawPath.lineTo(touchX, touchY);
                    // Draw path definitely on bitmap:
                    drawPath.drawPath();
                    mDrawPathsHistory.add(drawPath);
                    mDrawPathsRedoable.clear();
                    mDrawPaths.remove(id);
                    // This will call the onDraw callback to draw the bitmap on hardware canvas:
                    invalidate();
                }
                initDrawWatcherTimer();
                break;

            default:
                // Do not consume other events.
                return false;
        }
        // Consume handled event.
        return true;
    }

    private Integer getColor(int id) {
        Integer color;
        for (int i = id; i >= 0; i--) {
            color = mDrawPaintColors.get(i);
            if (color != null) {
                return color;
            }
        }
        return null;
    }


    public void setColor(String newColor){
        //set color
//        invalidate();
        mPaintColor = Color.parseColor(newColor);
    }

    public void setColor(String newColor, int id){
        //set color
//        invalidate();
        mDrawPaintColors.put(id, Color.parseColor(newColor));
    }

    public void clearColor(int id){
        //clear color
        mDrawPaintColors.remove(id);
    }


    public void setBrushSize(float brushSize){
        //update size
        mBrushSize = brushSize;
    }

    public void setPaintAlpha(int alphaValue) {
        mPaintAlpha = alphaValue;
    }


    public void setHollowMode(boolean hollowMode) {
        mHollowMode = hollowMode;
    }

    public void setUnderlineMode(boolean underlineMode) {
        mUnderlineMode = underlineMode;
    }


    public void setEraseMode(boolean eraseMode) {
        mEraseMode = eraseMode;
    }

    public void setTouchSizeMode(boolean touchSizeMode) {
        mTouchSizeMode = touchSizeMode;
    }

    public void setPressureMode(boolean pressureMode) {
        mPressureMode = pressureMode;
    }

    public void startNew(){
        mDrawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        mDrawPathsHistory.clear();
        resetColorOrder();
        invalidate();
    }

    public void loadImage(Bitmap bitmap) {
        if (mLoadedBitmap != null) {
            mLoadedBitmap.recycle();
        }
        mLoadedBitmap = Bitmap.createBitmap(bitmap);
        mDrawCanvas.drawBitmap(bitmap, 0, 0, null);
        mDrawPathsHistory.clear();
        resetColorOrder();
        invalidate();
    }

    public void setBrushSizeOldTablet(float brushSizeOldTablet){
        //update size for old tablet
        mBrushSizeOldTablet = brushSizeOldTablet;
    }

    public void setOldTabletMode(boolean oldTabletMode) {
        mOldTabletMode = oldTabletMode;
    }

    public void resetColorOrder() {
        mCurrentColorCursor = 0;
    }

    public void undo() {
        if (mDrawPathsHistory.size() > 0) {
            // Never set the color to 0, it would means that no color are chosen.
            if (mCurrentColorCursor > 1) {
                mCurrentColorCursor--;
            }
            DrawPath lastPath = mDrawPathsHistory.get(mDrawPathsHistory.size() - 1);
            mDrawPathsHistory.remove(lastPath);
            mDrawPathsRedoable.add(lastPath);
            resetCanvas();
            redrawAllPaths();
            invalidate();
        } else {
            Context context = getContext();
            Assert.assertNotNull(context);
            Toast nothingToUndo = Toast.makeText(context,
                    R.string.nothing_to_undo, Toast.LENGTH_SHORT);
            nothingToUndo.show();
        }
    }

    public void redo() {
        if (mDrawPathsRedoable.size() > 0) {
            DrawPath lastPath = mDrawPathsRedoable.get(mDrawPathsRedoable.size() - 1);
            mDrawPathsRedoable.remove(lastPath);
            mDrawPathsHistory.add(lastPath);
            lastPath.drawPath();
            invalidate();
        } else {
            Context context = getContext();
            Assert.assertNotNull(context);
            Toast nothingToRedo = Toast.makeText(context,
                    R.string.nothing_to_redo, Toast.LENGTH_SHORT);
            nothingToRedo.show();
        }

    }

    private void redrawAllPaths() {
        for (int i=0; i<mDrawPathsHistory.size(); ++i) {
            mDrawPathsHistory.get(i).drawPath();
        }
    }
    private void resetCanvas() {
        if (mLoadedBitmap != null) {
            mDrawCanvas.drawBitmap(mLoadedBitmap, 0, 0, null);
        } else {
            mDrawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
    }
}




Java Source Code List

com.tacitus.dnp.Dnp.java
com.tacitus.dnp.Log.java
com.tacitus.dnp.widget.ColorChooserDialog.java
com.tacitus.dnp.widget.DnpColor.java
com.tacitus.dnp.widget.DrawingView.java