What would be the best way to determine screen size in an external class and then adjust the stroke width of a handwriting capture view, so that it scales nicely?
I've got this SignatureCanvas.java class, in an external library. As you can see I've set the stroke width to a constant, which works (nothing strange there!). However, as I've been testing on various devices and emaulators (of differing screen size), the stroke width isn't translated to any kind of under-the-hood pixel->dip (which is understandable!). So, on older/smaller screen sizes, this stroke width of 8, actually looks like it's 14, and the signature becomes unreadable.
I've brainstormed a bit and have come up with the idea of generalising screen resolutions and having a default stroke width for each 'bracket' (so to speak), and applying it as and when I need to. However, that seems a bit rubbish and I was wondering if anyone has had this dilemma previously and how you solved it?
The class is here purely for demonstration purposes, there is nothing wrong with the code per-se.
package com.goosesys.gooselib.Views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class SignatureCanvas extends View {
private final float STROKE_WIDTH = 8f;
private final boolean ANTI_ALIAS = true;
private final int DEFAULT_PEN_COLOUR = 0xFF000000;
private Path drawPath;
private Paint drawPaint;
private Paint canvasPaint;
private int paintColour = DEFAULT_PEN_COLOUR;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
/*
* Constructors
*/
// Main in-code constructor //
public SignatureCanvas(Context context) {
super(context);
setupDrawing();
}
// Constructor for use in UI layout tool - Custom Views
public SignatureCanvas(Context context, AttributeSet attributeSet){
super(context, attributeSet);
setupDrawing();
}
/*
* Methods
*/
private void setupDrawing(){
drawPath = new Path();
drawPaint = new Paint();
// set initial colour for drawing
drawPaint.setColor(paintColour);
// setup paths
drawPaint.setAntiAlias(ANTI_ALIAS);
drawPaint.setStrokeWidth(STROKE_WIDTH);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
// finally create a new canvas paint object
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
public void clearCanvas(){
drawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
}
public Bitmap saveSignature(){
return Bitmap.createBitmap(canvasBitmap);
}
/*
* (non-Javadoc)
* #see android.view.View#onSizeChanged(int, int, int, int)
*/
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh){
super.onSizeChanged(w, h, oldw, oldh);
// view given size
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
#Override
protected void onDraw(Canvas canvas){
// draw view
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);
}
#Override
public boolean onTouchEvent(MotionEvent event){
// detect user touch
float touchX = event.getX();
float touchY = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
default:
return false;
}
invalidate();
return true;
}
}
public static int convertDpsToPixels(Context context, int dps) {
// http://developer.android.com/guide/practices/screens_support.html
// Convert the dps to pixels
final float scale = context.getResources().getDisplayMetrics().density;
final float dpsFloat = dps;
return (int) (dpsFloat * scale + 0.5f);
}
Related
I am developing an app in which I am showing canvas above pdf file so I can annotate it with free hand drawing to give remarks. My problem is that my canvas is zoomable but not scrollable. I am able to scale the canvas but unable to scroll it. Zoomed area is not scrollable please help how I can achieve it. My code is below
import android.content.Context;
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.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import com.github.barteksc.pdfviewer.PDFView;
import com.example.smartschool.ViewPDFFileActivity;
public class PaintView extends PDFView {
static Path drawPath;
//drawing and canvas paint
private static Paint drawPaint;
private static Paint canvasPaint;
//initial color
static int paintColor = 0xFFFF0000;
//stroke width
private static float STROKE_WIDTH = 5f;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;
//eraser mode
private static boolean erase=false;
boolean scaling=false;
private float scaleFactor = 1.f;
public ScaleGestureDetector detector = new ScaleGestureDetector(getContext(), new ScaleListener());
//constructor
public PaintView(Context context, AttributeSet attrs){
super(context, attrs);
setupDrawing();
setErase(erase);
}
private static void setupDrawing(){
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(STROKE_WIDTH);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
//*************************************** View assigned size ****************************************************
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
public static void setErase(boolean isErase){
erase=isErase;
drawPaint = new Paint();
if(erase) {
setupDrawing();
int srcColor= 0x00000000;
PorterDuff.Mode mode = PorterDuff.Mode.CLEAR;
PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(srcColor, mode);
drawPaint.setStrokeWidth(40);
drawPaint.setColorFilter(porterDuffColorFilter);
drawPaint.setColor(srcColor);
drawPaint.setXfermode(new PorterDuffXfermode(mode));
}
else {
setupDrawing();
}
}
//************************************ draw view *************************************************************
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawCanvas.drawColor(Color.TRANSPARENT);
canvas.save();
canvas.scale(scaleFactor, scaleFactor);
canvas.drawPath(drawPath, drawPaint);
canvas.drawBitmap(canvasBitmap,0,0, canvasPaint);
canvas.restore();
}
//*************************** respond to touch interaction **************************************************
#Override
public boolean onTouchEvent(MotionEvent event) {
detector.onTouchEvent(event);
float touchX = event.getX()/scaleFactor;
float touchY = event.getY()/scaleFactor;
//respond to down, move and up events
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(ViewPDFFileActivity.isDrawEnabled)
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
if(ViewPDFFileActivity.isDrawEnabled) {
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.lineTo(touchX, touchY);
}
break;
case MotionEvent.ACTION_UP:
if(ViewPDFFileActivity.isDrawEnabled) {
drawPath.lineTo(touchX, touchY);
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
}
break;
default:
return false;
}
//redraw
invalidate();
return true;
}
//*********************************** return current alpha ***********************************************
public int getPaintAlpha(){
return Math.round((float)STROKE_WIDTH/255*100);
}
//************************************** set alpha ******************************************************
public void setPaintAlpha(int newAlpha){
STROKE_WIDTH=Math.round((float)newAlpha/100*255);
drawPaint.setStrokeWidth(newAlpha);
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
if(ViewPDFFileActivity.isDrawEnabled) return true;
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(1f, Math.min(scaleFactor, 5f));
invalidate();
ViewPDFFileActivity.pdfView.setMaxZoom(scaleFactor);
ViewPDFFileActivity.pdfView.zoomTo(scaleFactor);
ViewPDFFileActivity.pdfView.invalidate();
return true;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
scaling=true;
return super.onScaleBegin(detector);
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
super.onScaleEnd(detector);
scaling=false;
}
}
}
This is my first question here! I hope that you can help me!
I'm explain:
I'm trying to develop an app like a paint in android studio to work with Paint,Canvas,and this class...
I have a class call Lienzo.java; This is the code of my class:
package com.example.pedro.paint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.graphics.Path;
/**
* Created by Pedro on 26/02/2018.
*/
public class Lienzo extends View {
//PATH TO DRAW THE LINES
private Path drawPath;
//PAINT DRAWING AND PAINT CANVAS
private Paint drawPaint,canvasPaint;
//COLOR INITIAL
private int paintColor=0xFFFF0000;
//CANVAS
private Canvas drawCanvas;
//CANVAS TO SAVE
private Bitmap canvasBitmap;
public Lienzo(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
private void setupDrawing(){
//CONFIGURATION AREA TO DRAW
drawPath = new Path();
drawPaint=new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(20);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint= new Paint(Paint.DITHER_FLAG);
}
//ASIGN SIZE TO VIEW
#Override
protected void onSizeChanged(int w, int h,int oldw, int oldh){
super.onSizeChanged(w,h,oldw,oldh);
canvasBitmap=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
drawCanvas=new Canvas(canvasBitmap);
}
//PAINT THE VIEW.CALL BY ONTHOUCHEVENT
#Override
protected void onDraw(Canvas canvas){
canvas.drawBitmap(canvasBitmap,0,0,canvasPaint);
canvas.drawPath(drawPath,drawPaint);
}
//REGISTER USER TOUCH
public boolean OnTouchEvent(MotionEvent event)
{
float touchX =event.getX();
float touchY=event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX,touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX,touchY);
break;
case MotionEvent.ACTION_UP:
drawPath.lineTo(touchX,touchY);
drawCanvas.drawPath(drawPath,drawPaint);
drawPath.reset();
break;
default:
return false;
}
//REPAINT
invalidate();
return true;
}
}
I have no error but I try to paint but do nothing.
This is a copy of an example.The original https://www.youtube.com/watch?v=GAr_agEokr8 works but my code,its equal and dont work.
Anybody knows why?
Thanks and regards!
OnTouchEvent change to onTouchEvent and add #Override
I am new in Android coding, but I'd like to make an app for tablets with a pen to be able to handwrite (I know there are already plenty out there but I want my own one)
I collected some code from all over the Internet and now I am at the point that I'd like to add an eraser mode. For that, I am creating a Bitmap which is then added on a Canvas.
It all works fine, but the Bitmap has the Bitmap.Configuration.ARGB_8888 and that is slow when drawing, so it lags behind. I tried Bitmap.Configuration.RGB_565 and had no performance issues, but other problems with erasing.
I also found out, that it obviously has to do with the size of the Bitmap. My tablet has a screen resolution of 1920x1200. When I am creating the Bitmap with only 1557x1000 I have no performance problems, but as I increase it to 1558x1000 or higher it starts to lag again.
Here is a part of my code:
class DrawView:
public class DrawView extends SurfaceView implements View.OnTouchListener{
private Paint paint;
private Paint bitmapPaint;
private Path path;
private Canvas canvas;
private Bitmap mBitmap;
private int currentColor;
private float currentStrokeWidth;
private boolean eraserMode = false;
public DrawView(Context context, AttributeSet attr) {
super(context, attr);
path = new Path();
paint = new Paint();
bitmapPaint = new Paint(Paint.DITHER_FLAG);
this.setOnTouchListener(this);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setFilterBitmap(false);
// ... setting Color and StorkeWidth to paint
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas c){
c.drawBitmap(mBitmap, 0, 0, bitmapPaint);
c.drawPath(path, paint);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(eventX, eventY);
break;
case MotionEvent.ACTION_MOVE:
touch_move(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
touch_up();
break;
default:
return false;
}
invalidate();
}
return true;
}
private float mX, mY;
private void touch_start(float x, float y) {
path.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
canvas.drawPath(path, paint);
mX = x;
mY = y;
}
private void touch_up() {
canvas.drawPath(path, paint);
path.reset();
}
}
I am very glad if someone could help me fixing this issue.
I fixed it by myself.
I realized that my background image I was setting with setBackgroundResource(R.drawable.background) had a too high resolution, I lowered it and now it works. I don't know why this affects drawing performance but that explains why I haven't found any answer on the internet
I'm new to Android development, and trying to render a button to a Canvas object in an onDraw method, basically text over a backfield. It's a good way to get my feet wet with some of the rendering commands.
I am able to fill a solid rectangle and then draw centered text over it, but when I try to fill a gradient rectangle, and then draw text over it, the text doesn't draw.
Code is below, cobbled together from various examples. Basically:
DO_PAINT=0, DO_GRADIENT=0 -> text renders
DO_PAINT=1, DO_GRADIENT=0 -> solid rectangle with text on top
DO_PAINT=0, DO_GRADIENT=1 -> gradient rectangle (no text) !!!
So something about my gradient drawing interferes with my text rendering. I'm guessing that I'm leaving something in a bad state in the Paint object, but I'm not sure what property that would be...
Any insight or thoughts are greatly appreciated...
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.view.View;
import android.graphics.Paint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Canvas;
import android.util.Log;
import android.view.MotionEvent;
import android.app.Activity;
public class cMyView extends View
{
public cMyView(Context context, Activity owner_activity)
{
super(context);
}
final Paint m_paint = new Paint();
public String m_Text = "Button";
private final Rect textBounds = new Rect();
public Typeface m_TypeFace = Typeface.create("Arial",Typeface.NORMAL);
public int m_TextColor = Color.argb(255,0,0,0);
public int m_TextSize = 32;
#Override
protected void onDraw(Canvas canvas) {
Rect m_Bounds = new Rect(100,100,500,200);
boolean DO_PAINT = false;
boolean DO_GRADIENT = true;
if ( DO_PAINT) {
m_paint.setStyle(Paint.Style.FILL);
m_paint.setColor(Color.GREEN);
canvas.drawRect(m_Bounds, m_paint);
}
if (DO_GRADIENT) {
m_paint.setShader(new LinearGradient(0, m_Bounds.top, 0, m_Bounds.bottom, Color.BLACK, Color.WHITE, Shader.TileMode.MIRROR));
canvas.drawRect(m_Bounds.left, m_Bounds.top, m_Bounds.right, m_Bounds.bottom, m_paint);
}
m_paint.setColor(m_TextColor);
m_paint.setTextSize(m_TextSize);
m_paint.setTypeface(m_TypeFace);
m_paint.getTextBounds(m_Text, 0, m_Text.length(), textBounds);
double x = m_Bounds.left + m_Bounds.width()/2 - textBounds.exactCenterX();
double y = m_Bounds.top + m_Bounds.height()/2 - textBounds.exactCenterY();
canvas.drawText(m_Text, (float) x, (float) y, m_paint);
}
}
Just add an another Paint for text, this is worked for me, and I found the reason, if you comment 2-nd row in DO_GRADIENT case (in your code), then will see that text is gradient, it mean that it draw, but have same gradient as background have, and becomes invisible.
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
final Paint m_paint = new Paint();
public String m_Text = "Button";
private final Rect textBounds = new Rect();
public Typeface m_TypeFace = Typeface.create("Arial",Typeface.NORMAL);
public int m_TextColor = Color.argb(255, 0, 0, 0);
public int m_TextSize = 32;
private final Paint textPaint = new Paint();
#Override
protected void onDraw(Canvas canvas) {
Rect m_Bounds = new Rect(100,100,500,200);
boolean DO_PAINT = true;
boolean DO_GRADIENT = true;
if ( DO_PAINT) {
m_paint.setStyle(Paint.Style.FILL);
m_paint.setColor(Color.GREEN);
canvas.drawRect(m_Bounds, m_paint);
}
if (DO_GRADIENT) {
m_paint.setShader(new LinearGradient(0, m_Bounds.top, 0, m_Bounds.bottom, Color.BLACK, Color.WHITE, Shader.TileMode.MIRROR));
canvas.drawRect(m_Bounds.left, m_Bounds.top, m_Bounds.right, m_Bounds.bottom, m_paint);
}
m_paint.setColor(m_TextColor);
m_paint.setTextSize(m_TextSize);
m_paint.setTypeface(m_TypeFace);
m_paint.getTextBounds(m_Text, 0, m_Text.length(), textBounds);
double x = m_Bounds.left + m_Bounds.width()/2 - textBounds.exactCenterX();
double y = m_Bounds.top + m_Bounds.height()/2 - textBounds.exactCenterY();
textPaint.setColor(m_TextColor);
textPaint.setTextSize(m_TextSize);
textPaint.setTypeface(m_TypeFace);
canvas.drawText(m_Text, (float) x, (float) y, textPaint);
}
I've got one bitmap on a canvas (backgroundBitmap) that I want to remain unchanged and another smaller bitmap (draggableBitmap) that I want the user to be able to drag above backgroundBitmap. (And I do mean "above" as in z-axis).
My thinking is that I just redraw the background with each ACTION_MOVE. When I do this with a solid color, it works perfectly. When I redraw the backgroundBitmap instead of the color, the backgroundBitmap remains visible but the draggableBitmap just repeats itself along the dragged path. Why is the solid color working to "clear" the image and a bitmap won't?
EDIT:
Many thanks to Steve K for helping out with this. I've updated the code below to reflect the progress so far. The only issue at this point is that the image goes completely black after dragging begins. Does this have something to do with the background bitmap being set to mutable? I was getting a mutability error without it.
package com.example.drawing;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class DrawingView extends View {
// drawing and canvas paint
private Paint canvasPaint;
// canvas
private Canvas drawCanvas;
// canvas bitmap
private Bitmap backgroundBitmap, draggableBitmap;
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
private void setupDrawing() {
canvasPaint = new Paint();
canvasPaint.setColor(Color.TRANSPARENT);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
BitmapFactory.Options opt1 = new BitmapFactory.Options();
opt1.inMutable = true;
opt1.inSampleSize = 4;
BitmapFactory.Options opt2 = new BitmapFactory.Options();
opt2.inMutable = true;
backgroundBitmap = BitmapFactory.decodeFile("/storage/emulated/0/DCIM/Camera/20140901_223343.jpg", opt1);
draggableBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.draggable_image, opt2);
drawCanvas = new Canvas(backgroundBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(backgroundBitmap, 0, 0, null);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Nothing here
break;
case MotionEvent.ACTION_MOVE:
drawCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
drawCanvas.drawBitmap(backgroundBitmap, 0, 0, null);
drawCanvas.drawBitmap(draggableBitmap, touchX, touchY, null);
break;
case MotionEvent.ACTION_UP:
// Nothing here
break;
default:
return false;
}
invalidate();
return true;
}
}
The color has no defined boundaries, so it 'clears' the canvas by writing to every pixel. Your floating bitmap doesn't - it only redraws in the place where it is. The whole canvas needs to be redrawn, first cleared with a color, then drawn over.
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Nothing here
break;
case MotionEvent.ACTION_MOVE:
drawCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
drawCanvas.drawBitmap(backgroundBitmap, 0, 0, null);
drawCanvas.drawBitmap(draggableBitmap, touchX, touchY, null);
break;
case MotionEvent.ACTION_UP:
// Nothing here
break;
default:
return false;
}
invalidate();
return true;
}
Yes, when you create the canvas, it must have a mutable bitmap to draw into. When you tried to draw backgroundBitmap to the canvas which had been initialized with backgroundBitmap as its target, it drew the backgroundBitmap, but the important thing to understand is that since it's your target drawing surface, what backgroundBitmap contains is literally the picture that you see (the sum total of everything drawn to the canvas), so when you draw it again, nothing happens. By initializing the canvas with backgroundBitmap, everything you drew to the canvas CHANGED backgroundBitmap. You need a clean surface to draw into that's not one of your sprites.
I ended up adding a third bitmap. While I confess I don't entirely understand how this works, I did remember reading something about the necessity of a bitmap to which all other drawings were applied in a Canvas.
drawCanvas is constructed with canvasBitmap and then drawCanvas is used to redraw the bgBitmap and draggableBitmap. I'm assuming my error in the original code was that the canvasBitmap was constructed using the bitmap I was trying to draw over it?
package com.example.drawing;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class DrawingView extends View {
// drawing and canvas paint
private Paint canvasPaint;
// canvas
private Canvas drawCanvas;
// canvas bitmap
private Bitmap canvasBitmap, draggableBitmap, bgBitmap;
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
private void setupDrawing() {
canvasPaint = new Paint();
canvasPaint.setColor(Color.TRANSPARENT);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
BitmapFactory.Options opt1 = new BitmapFactory.Options();
opt1.inMutable = true;
opt1.inSampleSize = 4;
BitmapFactory.Options opt2 = new BitmapFactory.Options();
opt2.inMutable = true;
bgBitmap = BitmapFactory.decodeFile("/storage/emulated/0/DCIM/Camera/20140901_223343.jpg", opt1);
draggableBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.draggable_image, opt2);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
drawCanvas.drawBitmap(bgBitmap, 0, 0, null);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(canvasBitmap, 0, 0, null);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Nothing here
break;
case MotionEvent.ACTION_MOVE:
drawCanvas.drawBitmap(bgBitmap, 0, 0, null);
drawCanvas.drawBitmap(draggableBitmap, touchX, touchY, null);
break;
case MotionEvent.ACTION_UP:
// Nothing here
break;
default:
return false;
}
invalidate();
return true;
}
}