Search code examples
androidandroid-layoutcanvasandroid-imageview

Draw on Picture in android


I was working on a custom imageview that can draw line on it, the problem is that the drawing area size is not exactly with the bitmap size.

For example, in the other app, it looks like:

enter image description here

However, in my app, it looks like

enter image description here

Here is my program, which seems the bitmap is not fit with the canvas. Thanks for helping

   public class DrawingView extends View {

    //drawing path
    private Path drawPath;
    //drawing and canvas paint
    private Paint drawPaint, canvasPaint;
    //initial color
    private int paintColor = 0xFF660000;
    //canvas
    private Canvas drawCanvas;
    //canvas bitmap
    private Bitmap canvasBitmap;


    public DrawingView(Context context, AttributeSet attrs){
        super(context, attrs);
        setupDrawing();
    }

    //setup drawing
    private void setupDrawing(){

        //prepare for drawing and setup paint stroke properties
        drawPath = new Path();
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(15.0f);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        canvasPaint = new Paint(Paint.DITHER_FLAG);
    }

    //size assigned 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);
    }

    //draw the view - will be called after touch event
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher), 0, 0, canvasPaint);
        canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
        canvas.drawPath(drawPath, drawPaint);
    }

    //register user touches as drawing action
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float touchX = event.getX();
        float touchY = event.getY();
        //respond to down, move and up events
        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;
        }
        //redraw
        invalidate();
        return true;

    }

    //update color
    public void setColor(String newColor){
        invalidate();
        paintColor = Color.parseColor(newColor);
        drawPaint.setColor(paintColor);
    }

    //start new drawing
    public void startNew(){
        drawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        invalidate();
    }
}

And I found the similar tutorial code, the problem is that the touch event has bug, it count in the action bar and the rest UI element so the touching is weird. Recommend to have a look

http://www.java2s.com/Code/Android/2D-Graphics/DrawonPictureandsave.htm

So, the goal of this topic , is the solution of

1) draw-able image

2) zoom-able and pan-able(can use library) (when active zoom , zoom, when inactive zoom , draw)

can reference the screenshot 1 for the idea

Thanks a lot for helping


Solution

  • You can avoid counting coordinates of other UI items in your sample by creation your own ImageView. Try this code for Activity:

    public class DrawOnBitmapActivity extends Activity implements OnClickListener
        {
    
    DrawableImageView choosenImageView;
    Button choosePicture;
    Button savePicture;
    
    Bitmap bmp;
    Bitmap alteredBitmap;
    
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        choosenImageView = (DrawableImageView) this.findViewById(R.id.ChoosenImageView);
        choosePicture = (Button) this.findViewById(R.id.ChoosePictureButton);
        savePicture = (Button) this.findViewById(R.id.SavePictureButton);
    
        savePicture.setOnClickListener(this);
        choosePicture.setOnClickListener(this);
    }
    
    public void onClick(View v) 
    {
        if (v == choosePicture) 
        {
            Intent choosePictureIntent = new Intent(
                    Intent.ACTION_PICK,
                    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            startActivityForResult(choosePictureIntent, 0);
        } 
        else if (v == savePicture) 
        {
            if (alteredBitmap != null) 
            {
                ContentValues contentValues = new ContentValues(3);
                contentValues.put(Media.DISPLAY_NAME, "Draw On Me");
    
                Uri imageFileUri = getContentResolver().insert(
                        Media.EXTERNAL_CONTENT_URI, contentValues);
                try {
                    OutputStream imageFileOS = getContentResolver()
                            .openOutputStream(imageFileUri);
                    alteredBitmap
                            .compress(CompressFormat.JPEG, 90, imageFileOS);
                    Toast t = Toast
                            .makeText(this, "Saved!", Toast.LENGTH_SHORT);
                    t.show();
    
                } catch (Exception e) {
                    Log.v("EXCEPTION", e.getMessage());
                }
            }
        }
    }
    
    protected void onActivityResult(int requestCode, int resultCode,
            Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
    
        if (resultCode == RESULT_OK) {
            Uri imageFileUri = intent.getData();
            try {
                BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
                bmpFactoryOptions.inJustDecodeBounds = true;
                bmp = BitmapFactory
                        .decodeStream(
                                getContentResolver().openInputStream(
                                        imageFileUri), null, bmpFactoryOptions);
    
                bmpFactoryOptions.inJustDecodeBounds = false;
                bmp = BitmapFactory
                        .decodeStream(
                                getContentResolver().openInputStream(
                                        imageFileUri), null, bmpFactoryOptions);
    
                alteredBitmap = Bitmap.createBitmap(bmp.getWidth(),
                        bmp.getHeight(), bmp.getConfig());
    
                choosenImageView.setNewImage(alteredBitmap, bmp);
            } 
            catch (Exception e) {
                Log.v("ERROR", e.toString());
            }
        }
    }
    }
    

    You have to change activity_main layout slightly:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <Button
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Choose Picture" android:id="@+id/ChoosePictureButton"/>
        <ru.pristalovpavel.drawonimage.DrawableImageView android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:id="@+id/ChoosenImageView">
        </ru.pristalovpavel.drawonimage.DrawableImageView>
         <Button
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Save Picture" android:id="@+id/SavePictureButton"/>
    </LinearLayout>
    

    and your custom ImageView:

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

    All source code of the project in eclipse: link

    UPDATE:

    new source code for DrawableImageView!

    More information is in my blog.