user782104
user782104

Reputation: 13545

Drawing transparent line on canvas of imageview in android

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

enter image description here

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

Upvotes: 4

Views: 2588

Answers (4)

nandan dubey
nandan dubey

Reputation: 21

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

Upvotes: 0

Frank
Frank

Reputation: 2230

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.

Upvotes: 1

user782104
user782104

Reputation: 13545

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);
}

Upvotes: 1

Budius
Budius

Reputation: 39836

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.

Upvotes: 0

Related Questions