I made a custom ImageView which has a canvas and gets two bitmaps so that I can draw on top of an existing image.
While the orientation changes the images is visible but the lines I've drawn disappear.
public class DrawableImageView extends ImageView implements View.OnTouchListener, DrawableImageViewControlListener {
float downx = 0;
float downy = 0;
float upx = 0;
float upy = 0;
Canvas canvas;
Paint paint;
Matrix matrix;
boolean isChanged;
public DrawableImageView(Context context) {
super(context);
setOnTouchListener(this);
}
public DrawableImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setOnTouchListener(this);
}
public DrawableImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOnTouchListener(this);
}
public void setNewImage(Bitmap alteredBitmap, Bitmap bmp)
{
canvas = new Canvas(alteredBitmap);
paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
paint.setStrokeCap(Paint.Cap.ROUND);
matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
isChanged = false;
setImageBitmap(alteredBitmap);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
isChanged = true;
switch (action)
{
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];//event.getX();
downy = getPointerCoords(event)[1];//event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];//event.getX();
upy = getPointerCoords(event)[1];//event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
upx = getPointerCoords(event)[0];//event.getX();
upy = getPointerCoords(event)[1];//event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
final float[] getPointerCoords(MotionEvent e)
{
final int index = e.getActionIndex();
final float[] coords = new float[] { e.getX(index), e.getY(index) };
Matrix matrix = new Matrix();
getImageMatrix().invert(matrix);
matrix.postTranslate(getScrollX(), getScrollY());
matrix.mapPoints(coords);
return coords;
}
#Override
public void colorChanged(int color) {
paint.setColor(color);
}
#Override
public void brushChanged(int size) {
paint.setStrokeWidth(size);
}
public boolean isChanged() {
return isChanged;
}
And here is the code where I initialize the ImageView in my OnCreate:
imageView = (DrawableImageView) findViewById(R.id.editImageView);
IStorage storage = BootLoader.resolve(this).getStorage();
imageFile = storage.getImageFile(fileName);
Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
if (alteredBitmap == null) {
alteredBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
}
imageView.setNewImage(alteredBitmap, bitmap);
Note: I am using two layout files for the two different orientations, and would like to keep it this way. So not manually handling configuration changes.
Android tears down the whole Activity when the screen orientation is changed and restarts it again, so you need to save the altered bitmap when this happens and restore it afterwards.
Different storage options are described here
When to save is described here,
Related
Android custom view drawing:
Screenshot:
This can't save the last trace, i want to redraw on a bitmap but the effect is not very good.
Screenshot:
code:
public class CustomView extends View {
private float sX, sY, eX, eY;
private Paint paint = new Paint();
private Canvas canvas = new Canvas();
private Bitmap bitmap;
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(bitmap);
setBackgroundColor(Color.WHITE);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
eX = event.getX();
eY = event.getY();
canvas.drawLine(sX, sY, eX, eY, paint);
break;
case MotionEvent.ACTION_DOWN:
sX = event.getX();
sY = event.getY();
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, getMatrix(), null);
}
}
Have you ever encountered the same problem?
You should update the sX sY variable, when your finger is moving
ACTION_DOWN point is [100, 100] sX = 100, sY = 100
ACTION_MOVE now finger is locating at [130, 150] eY = 130, eY = 150
invalidate() so will trigger draw() draw line [100, 100] to [130, 150]
finger continue to move
ACTION_MOVE now the start is [130, 150] sX = 130, sY = 150 not ACTION_DOWN point value
ACTION_MOVE now finger is locating at [160, 180]
invalidate() so will trigger draw() draw line [130, 150] to [160, 180]
continue do the same thing .....
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
eX = event.getX();
eY = event.getY();
invalidate()
sX = eX;
sY = eY;
break;
case MotionEvent.ACTION_DOWN:
sX = event.getX();
sY = event.getY();
break;
case MotionEvent.ACTION_UP:
canvas.drawLine(sX, sY, eX, eY, paint);
break;
}
//invalidate();
return true;
}
i have not Run the code, but maybe you have understood what i mean.
you should update both startX startY, and endX endY
I have found a solution, but I don't want to solve the problem in this way. My goal is to solve the problem with only the new canvas.
screenshot
public class CustomView extends View {
private float sX, sY, eX, eY;
private Paint paint = new Paint();
private Canvas canvas = new Canvas();
private Bitmap bitmap;
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint.setColor(Color.RED);
paint.setStrokeWidth(3);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(bitmap);
setBackgroundColor(Color.WHITE);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
eX = event.getX();
eY = event.getY();
break;
case MotionEvent.ACTION_DOWN:
sX = event.getX();
sY = event.getY();
break;
case MotionEvent.ACTION_UP:
canvas.drawLine(sX, sY, eX, eY, paint);
break;
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, getMatrix(), null);
canvas.drawLine(sX, sY, eX, eY, paint);
}
}
or
public class CustomView extends View {
private Paint paint = new Paint();
private List<Path> cache = new ArrayList<>();
private Path currentPath;
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint.setColor(Color.RED);
paint.setStrokeWidth(3);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (currentPath != null) {
currentPath.eX = event.getX();
currentPath.eY = event.getY();
}
break;
case MotionEvent.ACTION_DOWN:
currentPath = new Path();
currentPath.sX = event.getX();
currentPath.sY = event.getY();
break;
case MotionEvent.ACTION_UP:
if (currentPath != null) {
cache.add(currentPath);
currentPath = null;
}
break;
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Path path : cache) {
canvas.drawLine(path.sX, path.sY, path.eX, path.eY, paint);
}
if (currentPath == null) return;
canvas.drawLine(currentPath.sX, currentPath.sY, currentPath.eX, currentPath.eY, paint);
}
static class Path {
float sX;
float sY;
float eX;
float eY;
}
}
Edit :
my first part of question is unique, but the second part is similar to some other questions.
Main Problem:
this is my class code,I use this class to draw lines with custom stroke shape from drawable . I want to draw a line, but it just draw some dots as shown in the picture below. Is there any idea to fix this problem?
public class MainDrawingView extends View {
private Bitmap mBitmapBrush;
private Bitmap rBitmapBrush;
private Vector2 mBitmapBrushDimensions;
private List<Vector2> mPositions = new ArrayList<Vector2>(100);
private static final class Vector2 {
public Vector2(float x, float y) {
this.x = x;
this.y = y;
}
public final float x;
public final float y;
}
public MainDrawingView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
mBitmapBrush = BitmapFactory.decodeResource(context.getResources(), R.drawable.brush01);
rBitmapBrush = Bitmap.createScaledBitmap(mBitmapBrush, 10, 10, false);
mBitmapBrushDimensions = new Vector2(rBitmapBrush.getWidth(), rBitmapBrush.getHeight());
}
#Override
protected void onDraw(Canvas canvas) {
for (Vector2 pos : mPositions) {
canvas.drawBitmap(rBitmapBrush, pos.x, pos.y, null);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
final float posX = event.getX();
final float posY = event.getY();
mPositions.add(new Vector2(posX - mBitmapBrushDimensions.x / 2, posY - mBitmapBrushDimensions.y / 2));
invalidate();
}
return true;
}
}
Update:
I try another class to draw a line with my drawable, but I faced 2 new problem! the first; when I use setShader , there is no any line when touch and move my finger on the screen. and the second;when comment the setShader line in the code, I could draw a simple black line, but if I draw a curved line rapidly, the result is a broken line ! and not a true curve line.so what is your idea to solve these problems?
public class MainDrawingView extends View {
private Bitmap mBitmapBrush;
private Bitmap rBitmapBrush;
private BitmapShader mBitmapShader;
private Paint paint = new Paint();
private Path path = new Path();
public MainDrawingView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
mBitmapBrush = BitmapFactory.decodeResource(context.getResources(), R.drawable.brush01);
rBitmapBrush = Bitmap.createScaledBitmap(mBitmapBrush, 10, 10, false);
mBitmapShader = new BitmapShader(rBitmapBrush, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setAntiAlias(true);
paint.setStrokeWidth(5f);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
//paint.setShader(mBitmapShader);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(eventX, eventY);
break;
default:
return false;
}
invalidate();
return true;
}
}
I have a problem with canvas I have two options either to draw with a paint or to add an image but when I choose to add an image after drawing the drawing is gone but when I choose drawing again and start to draw the previous drawing show up and remove the image.
public class DrawCanvas extends View {
Bitmap bitmap;
private Paint paint = new Paint();
private Path path;
ArrayList<Path> paths = new ArrayList<>();
ArrayList<Path> undo = new ArrayList<>();
final Paint mBackgroundPaint;
ArrayList<Emotion> emotions;
private boolean mDrawingEnabled = true;
float currentX = 0, currentY = 0;
public DrawCanvas(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.launcher);
paint.setAntiAlias(false);
paint.setColor(Color.BLACK);
paint.setStrokeJoin(Paint.Join.MITER);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(30f);
emotions = new ArrayList<>();
path = new Path();
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(Color.WHITE);
}
#Override
protected void onDraw(Canvas canvas){
if (mDrawingEnabled ) {
canvas.drawPath(path, paint);
}
else{
canvas.drawBitmap(bitmap, currentX, currentY, null);
}
}
#Override
public boolean onTouchEvent(MotionEvent motionEvent){
int xPos =(int) motionEvent.getX(), yPos =(int) motionEvent.getY();
final int action = motionEvent.getAction();
if (mDrawingEnabled){
switch (action) {
case MotionEvent.ACTION_DOWN:
path.moveTo(xPos, yPos);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(xPos, yPos);
break;
case MotionEvent.ACTION_UP:
paths.add(path);
break;
default:
return false;
}
}
else {
final float me_x = motionEvent.getX();
final float me_y = motionEvent.getY();
switch ( action ) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
currentX = me_x;
currentY = me_y;
break;
}
}
invalidate();
return true;
}
public void undo(){
if (paths.size()>0){
undo.add(paths.remove(paths.size()-1));
}
}
public void redo(){
if (undo.size()>0){
paths.add(undo.remove(undo.size()-1));
}
}
final void enableDrawing() { mDrawingEnabled = true; }
final void disableDrawing() { mDrawingEnabled = false; }
public void changeBrushColor(int color){
paint.setColor(color);
}
public void changeBrushSize(int size){
paint.setStrokeWidth(size);
}
}
here is the drawing
and when I add image clears the drawing
I want to keep the drawn path alongside the image.
I'm trying to draw using onTouch events on a Canvas over an Imageview that is pinchable/zoomable. It works perfectly if the image is fullscreen (and proportioned to the size of the screen), but once you zoom in on the image, the drawing point is not the same place as where you touch the screen.
I am creating a canvas and an imageview like so:
canvas = new Canvas(bitmap);
image.setImageBitmap(bitmap);
and then performing a lot of pinch/zoom transformations by using setMatrix on image. By looking at bitmap.getHeight() and bitmap.getWidth(), it's evident that throughout all the transformations, neither Bitmap nor Image actually change in size. Image, for instance, stays at a constant 2560x1454 while Bitmap stays at 3264x1836 (using image.getHeight()/width() and bitmap.getHeight()/width() for those values).
For the drawing part, I'm converting MotionEvent to a series of Point objects (literally just int x, int y) and then translating that value through a formula I found somewhere on the internet. I really have no idea why it works, which is probably my first problem.
List<Point> points = new ArrayList<>();
private boolean handleDraw(View v, MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_UP) {
Point p = new Point();
p.x = event.getX();
p.y = event.getY();
convertPoint(p, v);
points.add(p);
//draw the converted points
drawOnBitmap();
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
points.clear();
return true;
}
return true;
}
and then they are converted into bitmap values here
//v here is the view passed by the ontouch listener.
private void convertPoint(Point point, View v) {
double divisor = ((double) bitmap.getWidth() / (double) v.getWidth());
point.x = (int) ((double) point.x * divisor);
point.y = (int) ((double) point.y * divisor);
}
and drawn onto the canvas here:
private void drawOnBitmap() {
Path path = new Path();
boolean first = true;
//uses quaddraw
for (int i = 0; i < points.size(); i += 2) {
Point point = points.get(i);
if (first) {
first = false;
path.moveTo(point.x, point.y);
} else if (i < points.size() - 1) {
Point next = points.get(i + 1);
path.quadTo(point.x, point.y, next.x, next.y);
} else {
path.lineTo(point.x, point.y);
}
}
canvas.drawPath(path, redPaint);
image.invalidate();
}
with Paint being
redPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setStyle(Paint.Style.STROKE);
redPaint.setStrokeWidth(STROKE_WIDTH);
redPaint.setColor(Color.RED);
In general, what's happening is that the touch event is being scaled from the size of the screen to the size of the bitmap (see the above image for an example). However, the answer isn't to just take the original getRaw() values; those are always incorrect.
I guess the question is, how do I get the canvas to respond to the ABSOLUTE position of the touch event instead of the proportional one? Failing that, how can I transform the base canvas with the same values as the imageview and get them both to scale proportionally?
You need to recalculate your coords using Matrix with code like this:
public class DrawableImageView extends ImageView implements OnTouchListener
{
float downx = 0;
float downy = 0;
float upx = 0;
float upy = 0;
Canvas canvas;
Paint paint;
Matrix matrix;
public DrawableImageView(Context context)
{
super(context);
setOnTouchListener(this);
}
public DrawableImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
setOnTouchListener(this);
}
public DrawableImageView(Context context, AttributeSet attrs,
int defStyleAttr)
{
super(context, attrs, defStyleAttr);
setOnTouchListener(this);
}
public void setNewImage(Bitmap alteredBitmap, Bitmap bmp)
{
canvas = new Canvas(alteredBitmap );
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(5);
matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
setImageBitmap(alteredBitmap);
}
#Override
public boolean onTouch(View v, MotionEvent event)
{
int action = event.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];//event.getX();
downy = getPointerCoords(event)[1];//event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];//event.getX();
upy = getPointerCoords(event)[1];//event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
upx = getPointerCoords(event)[0];//event.getX();
upy = getPointerCoords(event)[1];//event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
final float[] getPointerCoords(MotionEvent e)
{
final int index = e.getActionIndex();
final float[] coords = new float[] { e.getX(index), e.getY(index) };
Matrix matrix = new Matrix();
getImageMatrix().invert(matrix);
matrix.postTranslate(getScrollX(), getScrollY());
matrix.mapPoints(coords);
return coords;
}
}
The full sample you can get here.
The problem is, I attempt to change the opacity to 100 which should be transparent, but when I try to draw the line it has some circle on the line. (ref to the screenshot) Highly appreciate if provide some sample code. Thanks a lot for helping.
Code from MainActivity
// set image
bitmap = downScale(view.getTag().toString(),1280,1024);
altered_bitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), bitmap.getConfig());
draw_view.setNewImage(altered_bitmap,bitmap);
pen.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
draw_view.setAlpha(100);
}
}
});
And Code from the custom imageView
public ScaleImageView(Context context) {
super(context);
sharedConstructing(context);
}
public void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(width);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setAlpha(alpha);
drawListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (getDrawable() != null) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];// event.getX();
downy = getPointerCoords(event)[1];// event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
mPath = new Path();
paths.add(mPath);
invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
mPath = new Path();
paths.add(mPath);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
}
return true;
}
};
setOnTouchListener(drawListener);
}
//draw view start
public void setNewImage(Bitmap alteredBitmap, Bitmap bmp) {
canvas = new Canvas(alteredBitmap);
matrix_draw = new Matrix();
canvas.drawBitmap(bmp, matrix_draw, paint);
setImageBitmap(alteredBitmap);
mPath = new Path();
paths.add(mPath);
}
public void setBrushColor(int color) {
this.color = color;
paint.setColor(color);
paint.setAlpha(alpha);
}
public void setAlpha(int alpha) {
this.alpha = alpha;
paint.setAlpha(alpha);
}
public void setWidth(float width) {
this.width = width;
paint.setStrokeWidth(width);
}
final float[] getPointerCoords(MotionEvent e) {
final int index = e.getActionIndex();
final float[] coords = new float[] { e.getX(index), e.getY(index) };
Matrix matrix = new Matrix();
getImageMatrix().invert(matrix);
matrix.postTranslate(getScrollX(), getScrollY());
matrix.mapPoints(coords);
return coords;
}
public void setIsScale() {
isScale = !isScale;
setOnTouchListener(isScale ? zoomListener : drawListener);
}
And the screenshot
Update: Project code
Since it is quite difficult to figure out the problem though code extract , I have uplod the project (<1 mb) along with the used library:
if you have some spare time , you are welcome to take a look
it is a small drawing tool , first copy a folder with some images to the folder "HistoryTool" in your device
The path , for example, like:
sd card root/ HistoryTool/ folder1 / a.jpg
, then you can draw on it, but the draw transparent line has circle on it. thats all
https://drive.google.com/file/d/0B9mELZtUJp0LZncwQVM4alExalE/view?usp=sharing
I have complete the task by using On-draw and Path
drawListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (getDrawable() != null) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];// event.getX();
downy = getPointerCoords(event)[1];// event.getY();
holderList.add(new Holder(color, width, alpha));
holderList.get(holderList.size() - 1).path.moveTo(downx, downy);
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
holderList.get(holderList.size() - 1).path.lineTo(upx, upy);
invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
}
return true;
}
};
setOnTouchListener(drawListener);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (img != null) {
tmp_canvas.drawBitmap(img, 0, 0, null);
}
for (Holder holder : holderList) {
tmp_canvas.drawPath(holder.path, holder.paint);
}
}
//draw view start
public void setNewImage(Bitmap alteredBitmap, Bitmap bmp) {
tmp_canvas = new Canvas(alteredBitmap);
img = bmp;
tmp_canvas.drawBitmap(bmp, 0, 0, null);
setImageBitmap(alteredBitmap);
}
So, your problem is circles on the line. This comes from each new segment of the line chain being drawn individually and each end point overlapping the previously drawn segment of the chain. You want to draw the path of the line before you call stroke(); This will draw the entire chain of line segments once instead of individually and prevent those circular overlaps.
What you could do is something like this: Make a line chain class that draws lines all at once:
/* Feed the line chain an array of points to draw when you construct it. */
/* points_=[{"x":0,"y":0},{"x":10,"y":10}]; */
function LineChain(points_){
this.points=points_;
}
LineChain.prototype={
constructor:LineChain,
/* Draws this line chain to the specified context. */
drawTo:function(context_){
/* Get the first point in the chain. */
var point=this.points[0];
/* Start the path by moving to the first position on the chain. */
context_.beginPath();
context_.moveTo(point.x,point.y);
/* Loop through the remaining points in the chain and draw the rest of the path. */
for (var index=1;index<this.points.length-1;index++){
point=this.points[index];
context_.lineTo(point.x,point.y);
}
}
}
Now when you actually want to draw the chain to the canvas, just do this:
/* Define a line chain. */
var line_chain=new LineChain([{"x":0,"y":0},{"x":10,"y":20},{"x":30,"y":40}]);
/* Wherever you're drawing at... */
context.strokeStyle="rgba(255,0,0,0.5)";
context.lineWidth=20;
line_chain.drawTo(context);
context.stroke();
Basically, it doesn't matter how you go about implementing this technique, the only thing you need to do is ensure that your entire path is drawn before you call the stroke function.
from the docs Paint.setAlpha(int)
set the alpha component [0..255] of the paint's color.
that means that 0 is transparent and 255 is fully opaque. 100 you're just setting to something in the middle.
Please refer to BlendMode that will help you fix this problem.
Expectation: If we draw same stroke even on itself it should not overdraw and decrease transparency.
Refer to below links.
https://developer.android.com/reference/kotlin/androidx/compose/ui/graphics/BlendMode
https://medium.com/mobile-app-development-publication/practical-image-porterduff-mode-usage-in-android-3b4b5d2e8f5f