Sunday, September 1, 2013

Detect touch and free draw on Bitmap

The previous exercise "Get bitmap color on touched position in ImageView" show how to project touch position on ImageView to underlying bitmap. In this exercise, we are going to draw something on the touch position.

Detect touch and free draw on Bitmap

package com.example.androiddrawbitmap;

import java.io.FileNotFoundException;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 Button btnLoadImage;
 TextView textSource, textInfo;
 ImageView imageResult;
 
 final int RQS_IMAGE1 = 1;

 Uri source;
 Bitmap bitmapMaster;
 Canvas canvasMaster;
 

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  btnLoadImage = (Button)findViewById(R.id.loadimage);
  textSource = (TextView)findViewById(R.id.sourceuri);
  imageResult = (ImageView)findViewById(R.id.result);
  
  btnLoadImage.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    Intent intent = new Intent(Intent.ACTION_PICK,
      android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(intent, RQS_IMAGE1);
   }});
  
  imageResult.setOnTouchListener(new OnTouchListener(){

   @Override
   public boolean onTouch(View v, MotionEvent event) {
    
    int action = event.getAction();
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch(action){
    case MotionEvent.ACTION_DOWN:
     textSource.setText("ACTION_DOWN- " + x + " : " + y);
     drawOnProjectedBitMap((ImageView)v, bitmapMaster, x, y);
     break;
    case MotionEvent.ACTION_MOVE:
     textSource.setText("ACTION_MOVE- " + x + " : " + y);
     drawOnProjectedBitMap((ImageView)v, bitmapMaster, x, y);
     break;
    case MotionEvent.ACTION_UP:
     textSource.setText("ACTION_UP- " + x + " : " + y);
     drawOnProjectedBitMap((ImageView)v, bitmapMaster, x, y);
     break;
    }
    /*
     * Return 'true' to indicate that the event have been consumed.
     * If auto-generated 'false', your code can detect ACTION_DOWN only,
     * cannot detect ACTION_MOVE and ACTION_UP.
     */
    return true;
   }});
  
 }
 
 /*
  * Project position on ImageView to position on Bitmap
  * draw on it
  */
 private void drawOnProjectedBitMap(ImageView iv, Bitmap bm, int x, int y){
  if(x<0 || y<0 || x > iv.getWidth() || y > iv.getHeight()){
   //outside ImageView
   return;
  }else{
   int projectedX = (int)((double)x * ((double)bm.getWidth()/(double)iv.getWidth()));
   int projectedY = (int)((double)y * ((double)bm.getHeight()/(double)iv.getHeight()));

   Paint   paint = new Paint();
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(Color.WHITE);
            paint.setStrokeWidth(3);
   canvasMaster.drawCircle(projectedX, projectedY, 5, paint);
   imageResult.invalidate();
   
   textSource.setText(x + ":" + y + "/" + iv.getWidth() + " : " + iv.getHeight() + "\n" +
     projectedX + " : " + projectedY + "/" + bm.getWidth() + " : " + bm.getHeight()
     );
  }
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  
  Bitmap tempBitmap;
  
  if(resultCode == RESULT_OK){
   switch (requestCode){
   case RQS_IMAGE1:
    source = data.getData();
    textSource.setText(source.toString());
    
    try {
     //tempBitmap is Immutable bitmap,
     //cannot be passed to Canvas constructor
     tempBitmap = BitmapFactory.decodeStream(
       getContentResolver().openInputStream(source));
     
     Config config;
     if(tempBitmap.getConfig() != null){
      config = tempBitmap.getConfig();
     }else{
      config = Config.ARGB_8888;
     }
     
     //bitmapMaster is Mutable bitmap
     bitmapMaster = Bitmap.createBitmap(
       tempBitmap.getWidth(),
       tempBitmap.getHeight(),
       config);
     
     canvasMaster = new Canvas(bitmapMaster);
     canvasMaster.drawBitmap(tempBitmap, 0, 0, null);
     
     imageResult.setImageBitmap(bitmapMaster);
    } catch (FileNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    
    break;
   }
  }
 }

}

Refer to the post "Get bitmap color on touched position in ImageView" for the layout file.

download filesDownload the files.

next: Detect touch and draw rect on bitmap


more: Something about processing images in Android

3 comments:

manoj said...

Thanks Eric! Very useful

Unknown said...

Its give me dashes line, what can I do to change the line into solid line. ?
regards Tahir.

Erik said...

Rizky Tahier,

This example drawCircle on the touch point.

In order to draw line, you have to keep track of last touch point and current touch point, then drawLine between them.