Created
June 26, 2018 09:10
-
-
Save CallumCoombes/aef8f8dbd8aa8a8c69b17b33e1618103 to your computer and use it in GitHub Desktop.
ImageView to allow users to paint on image with their finger and add a cross image on a painted area.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.graphics.Bitmap; | |
import android.graphics.BitmapFactory; | |
import android.graphics.Canvas; | |
import android.graphics.Color; | |
import android.graphics.Matrix; | |
import android.graphics.Paint; | |
import android.graphics.Path; | |
import android.graphics.RectF; | |
import android.graphics.drawable.BitmapDrawable; | |
import android.graphics.drawable.Drawable; | |
import android.support.annotation.Nullable; | |
import android.util.AttributeSet; | |
import android.util.Log; | |
import android.view.MotionEvent; | |
import android.widget.Toast; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.Map; | |
import static android.graphics.Bitmap.Config.ARGB_8888; | |
public class FingerPaintImageView extends android.support.v7.widget.AppCompatImageView { | |
private final String TAG = getClass().getSimpleName(); | |
private Bitmap mBitmap; | |
private Canvas mCanvas; | |
private Path mPath; | |
private Paint mBitmapPaint; | |
private Paint mPaint; | |
private boolean placingCross = false; | |
private Toast warningToast = Toast.makeText(getContext(), "You must select a shaded area", Toast.LENGTH_SHORT); | |
private final int gridSpaceCount = 20; | |
private Bitmap imageViewBitmap = this.getImageViewBitmapFromVector(/*this.getDrawable()*/getResources().getDrawable(R.drawable.front_bpi_man)); | |
private boolean crossPlaced = false; | |
private float crossX; | |
private float crossY; | |
public FingerPaintImageView(Context context, @Nullable AttributeSet attrs) { | |
super(context, attrs); | |
//initialise values | |
mPath = new Path(); | |
mBitmapPaint = new Paint(Paint.DITHER_FLAG); | |
mPaint = new Paint(); | |
mPaint.setAntiAlias(true); | |
mPaint.setDither(true); | |
mPaint.setColor(0xFFFF0000); | |
mPaint.setStyle(Paint.Style.STROKE); | |
mPaint.setStrokeJoin(Paint.Join.ROUND); | |
mPaint.setStrokeCap(Paint.Cap.ROUND); | |
mPaint.setStrokeWidth(20); | |
} | |
@Override | |
protected void onSizeChanged(int w, int h, int oldw, int oldh) { | |
super.onSizeChanged(w, h, oldw, oldh); | |
mBitmap = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888); //changed from w and h | |
mCanvas = new Canvas(mBitmap); | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
super.onMeasure(widthMeasureSpec, heightMeasureSpec); | |
//get image and scale it | |
int nh = (int) ( imageViewBitmap.getHeight() * (512.0 / imageViewBitmap.getWidth()) ); | |
imageViewBitmap = Bitmap.createScaledBitmap(imageViewBitmap, 512, nh, true); | |
//set bitmap as the imageview's bitmap | |
setImageBitmap(imageViewBitmap); | |
} | |
@Override | |
protected void onDraw(Canvas canvas) { | |
super.onDraw(canvas); | |
//draw the bitmap on the canvas | |
canvas.drawBitmap(imageViewBitmap, getMatrix(), null); | |
//do painting stuff | |
if (mBitmap != null) { | |
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); | |
canvas.drawPath(mPath, mPaint); //this draws path when held down | |
if (placingCross && isOnImage(mX, mY)) { | |
crossPlaced = true; | |
crossX = mX; | |
crossY = mY; | |
} | |
if (crossPlaced) { | |
Bitmap crossBitmap = getImageViewBitmapFromVector(getResources().getDrawable(R.drawable.cross_yellow_25px)); | |
int crossXCenter = crossBitmap.getWidth() / 2; | |
int crossYCenter = crossBitmap.getHeight() / 2; | |
canvas.drawBitmap(crossBitmap, crossX - crossXCenter, crossY - crossYCenter, null); | |
} | |
} | |
} | |
//get bitmap from vector drawable | |
private Bitmap getImageViewBitmapFromVector(Drawable d) { | |
try { | |
Bitmap bitmap; | |
bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(), ARGB_8888); | |
Canvas canvas = new Canvas(bitmap); | |
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); | |
d.draw(canvas); | |
return bitmap; | |
} catch (OutOfMemoryError e) { | |
// Handle the error | |
return null; | |
} | |
} | |
private float mX, mY; | |
private static final float TOUCH_TOLERANCE = 4; | |
private boolean isStarted = false; | |
//handle start touch | |
private void touch_start(float x, float y) { | |
mPath.reset(); | |
if (isOnImage(x, y)) { | |
if (!placingCross) { | |
mPath.moveTo(x, y); | |
} | |
mX = x; | |
mY = y; | |
// Log.d(TAG, "touch_start: started on white - mX=" + mX + ", mY=" + mY); | |
isStarted = true; | |
} | |
// Log.d(TAG, "touch_start: not started - mX=" + mX + ", mY=" + mY); | |
} | |
//handle moving touch | |
private void touch_move(float x, float y) { | |
if (isOnImage(x, y)) { | |
if (!isStarted) { | |
touch_start(x, y); | |
} | |
// Log.d(TAG, "touch_move: drawn on white - mX=" + mX + ", mY=" + mY); | |
float dx = Math.abs(x - mX); | |
float dy = Math.abs(y - mY); | |
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { | |
if (!placingCross) { | |
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); | |
} | |
mX = x; | |
mY = y; | |
} | |
} else { | |
if (isStarted) { | |
touch_up(); | |
} | |
} | |
} | |
//handle touch stopped | |
private void touch_up() { | |
if (isStarted) { | |
// Log.d(TAG, "touch_up- mX=" + mX + ", mY=" + mY); | |
isStarted = false; | |
if (!placingCross) { | |
mPath.lineTo(mX, mY); | |
// commit the path to our offscreen | |
mCanvas.drawPath(mPath, mPaint); //this draws the path once it is finished | |
} | |
// kill this so we don't double draw | |
mPath.reset(); | |
} | |
} | |
//check if x,y position is on the bitmap image | |
public boolean isOnImage(float x, float y) { | |
int pixel = imageViewBitmap.getPixel((int) x, (int) y); | |
int alphaValue = Color.alpha(pixel); | |
if (placingCross) { | |
int paintPixel = mBitmap.getPixel((int) x, (int) y); | |
//if placing cross, check for selection of a shaded red area | |
if (Color.red(paintPixel) == 255 & Color.blue(paintPixel) == 0 & Color.green(paintPixel) == 0) { | |
return true; | |
} else { | |
if (warningToast != null) warningToast.cancel(); | |
// warningToast = Toast.makeText(getContext(), "You must select a shaded area", Toast.LENGTH_SHORT); | |
warningToast.show(); | |
return false; | |
} | |
} else if (alphaValue > 0) { | |
//if normal shading mode, just check not transparent | |
return true; | |
} else { | |
return false; | |
} | |
} | |
@Override | |
public boolean onTouchEvent(MotionEvent event) { | |
//CHANGING THIS X Y MEANS THAT WHERE YOU CLICK ISN'T RIGHT, DON'T CHANGE IT HERE! | |
float x = event.getX(); | |
float y = event.getY(); | |
if (x < imageViewBitmap.getWidth() && x > 0 && | |
y < imageViewBitmap.getHeight() && y > 0 && | |
x != 0.0 && y != 0.0) { | |
switch (event.getAction()) { | |
case MotionEvent.ACTION_DOWN: | |
touch_start(x, y); | |
invalidate(); | |
break; | |
case MotionEvent.ACTION_MOVE: | |
touch_move(x, y); | |
invalidate(); | |
break; | |
case MotionEvent.ACTION_UP: | |
touch_up(); | |
invalidate(); | |
break; | |
} | |
return true; | |
} | |
return false; | |
} | |
public void clear() { | |
mBitmap.eraseColor(Color.TRANSPARENT); | |
invalidate(); | |
System.gc(); | |
crossPlaced = false; | |
crossX = 0.0f; | |
crossY = 0.0f; | |
} | |
public void setPlacingCross(boolean placingCross) { | |
this.placingCross = placingCross; | |
mX = 0; | |
mY = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment