Skip to content

Instantly share code, notes, and snippets.

@dmitriy-chernysh
Created April 4, 2018 15:32
Show Gist options
  • Save dmitriy-chernysh/ce83f28c2c2d4560c5914702dc3d1be5 to your computer and use it in GitHub Desktop.
Save dmitriy-chernysh/ce83f28c2c2d4560c5914702dc3d1be5 to your computer and use it in GitHub Desktop.
Custom view for drawing
public class DrawingView extends View implements IDrawingView {
private static final int TOUCH_TOLERANCE = 4;
private static final int PAINT_COLOR = 0xff1565C0;
private static final int PAINT_BRUSH_SIZE = 20;
// To hold the path that will be drawn.
private Path mDrawPath;
// Paint object to draw drawPath and drawCanvas.
private Paint mDrawPaint, mCanvasPaint;
// canvas on which drawing takes place.
private Canvas mDrawCanvas;
// canvas bitmap
private Bitmap mCanvasBitmap;
private float mX, mY;
private boolean mDrawPoint;
private ArrayList<MotionEvent> mCachedEventList = new ArrayList<>();
private ArrayList<MotionEvent> mEventList = new ArrayList<>();
public DrawingView(Context context) {
this(context, null, 0);
}
public DrawingView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mCanvasBitmap, 0, 0, mCanvasPaint);
canvas.drawPath(mDrawPath, mDrawPaint);
for (MotionEvent event : mCachedEventList) {
performTouchEvent(event);
}
mCachedEventList.clear();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
getParent().requestDisallowInterceptTouchEvent(true);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
performTouchEvent(event);
}
return true;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mDrawCanvas = new Canvas(mCanvasBitmap);
}
@Override
public void clearDrawing() {
mDrawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
mEventList.clear();
invalidate();
}
@Override
public Bitmap getDrawingBitmap() {
return mCanvasBitmap;
}
@Override
public boolean isDrawingEmpty() {
return mEventList == null || mEventList.isEmpty();
}
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
return new ParsableHelper(mEventList, super.onSaveInstanceState());
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
ParsableHelper helper = (ParsableHelper) state;
mCachedEventList = helper.events;
super.onRestoreInstanceState(helper.parcelable);
}
private void init() {
setSaveEnabled(true);
setUpDrawing();
}
private void setUpDrawing() {
mDrawPath = new Path();
mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDrawPaint.setColor(PAINT_COLOR);
mDrawPaint.setStyle(Paint.Style.STROKE);
mDrawPaint.setStrokeJoin(Paint.Join.ROUND);
mDrawPaint.setStrokeCap(Paint.Cap.ROUND);
mDrawPaint.setStrokeWidth(PAINT_BRUSH_SIZE);
mCanvasPaint = new Paint(Paint.DITHER_FLAG);
}
private void performTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStart(x, y);
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
break;
case MotionEvent.ACTION_UP:
touchUp();
break;
}
invalidate();
mEventList.add(MotionEvent.obtain(event));
}
private void touchStart(float x, float y) {
mDrawPath.reset();
mDrawPath.moveTo(x, y);
mX = x;
mY = y;
mDrawPoint = true;
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mDrawPoint = false;
mDrawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touchUp() {
if (mDrawPoint) {
mDrawCanvas.drawPoint(mX, mY, mDrawPaint);
} else {
mDrawPath.lineTo(mX, mY);
// commit the path to our offscreen
mDrawCanvas.drawPath(mDrawPath, mDrawPaint);
// kill this so we don't double draw
mDrawPath.reset();
}
}
private static class ParsableHelper implements Parcelable {
private ArrayList<MotionEvent> events;
private Parcelable parcelable;
ParsableHelper(ArrayList<MotionEvent> events, Parcelable parcelable) {
this.events = events;
this.parcelable = parcelable;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(this.events);
dest.writeParcelable(parcelable, flags);
}
protected ParsableHelper(Parcel in) {
this.events = in.createTypedArrayList(MotionEvent.CREATOR);
this.parcelable = in.readParcelable(ClassLoader.getSystemClassLoader());
}
public static final Parcelable.Creator<ParsableHelper> CREATOR = new Parcelable.Creator<ParsableHelper>() {
@Override
public ParsableHelper createFromParcel(Parcel source) {
return new ParsableHelper(source);
}
@Override
public ParsableHelper[] newArray(int size) {
return new ParsableHelper[size];
}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment