Showing posts with label Android code sample: detect touch. Show all posts
Showing posts with label Android code sample: detect touch. Show all posts

Detect touch and draw circle on Android WebView with Javascript

Last exercise show a simple WebView with touch detection on Android WebView, it's modify to draw circle when user touch and move on HTML canvas.


Modify /assets/mypage.html from last exercise.
<!DOCTYPE HTML>
<HTML>
<HEAD>
<script>

var p1;
var p2;
var canvas;
var context;
var cx;
var cy;
var x;
var y;
var canvasOffsetX;
var canvasOffsetY;

function init(){
p1 = document.getElementById('p1');
p2 = document.getElementById('p2');
p1.innerHTML=navigator.userAgent;

canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
canvasOffsetX = canvas.getBoundingClientRect().left;
canvasOffsetY = canvas.getBoundingClientRect().top;

canvas.width = window.innerWidth - (2 * canvasOffsetX);
canvas.height = window.innerHeight - canvasOffsetY - 100;
canvas.fillStyle="#a0a0a0";

canvas.addEventListener('touchstart', touchstartListener, false);
canvas.addEventListener('touchmove', touchmoveListener, false);
canvas.addEventListener('touchend', touchendListener, false);
canvas.addEventListener('touchenter', touchenterListener, false);
canvas.addEventListener('touchleave', touchleaveListener, false);
canvas.addEventListener('touchcancel', touchcancelListener, false);

}

function touchstartListener(event){
cx = event.changedTouches[0].pageX - canvasOffsetX;
cy = event.changedTouches[0].pageY - canvasOffsetY;
p2.innerHTML= "touchstart - <br/>"
+ cx + ":" + cy;
event.preventDefault();

context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle="white";
context.fill();
}

function touchmoveListener(event){

x = event.changedTouches[0].pageX - canvasOffsetX;
y = event.changedTouches[0].pageY - canvasOffsetY;
var deltax = x-cx;
var deltay = y-cy;
var radius = Math.sqrt(deltax*deltax + deltay*deltay);
context.beginPath();
context.arc(cx, cy, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = 1;
context.strokeStyle = '#003300';
context.stroke();

p2.innerHTML= "touchmove - <br/>"
+ x + ":" + y;
event.preventDefault();
}

function touchendListener(event){
p2.innerHTML= "touchend - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

function touchenterListener(event){
p2.innerHTML= "touchenter - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

function touchleaveListener(event){
p2.innerHTML= "touchleave - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

function touchcancelListener(event){
p2.innerHTML= "touchcancel - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

</script>
</HEAD>
<BODY onload="init()" style="border:5px solid #000000;">

<p id='p1'>un-init</p>

<canvas id='myCanvas' style="border:1px solid #FF0000;">
Canvas not support!
</canvas>

<p id='p2'></p>

</BODY>
</HTML>

Android WebView, detect touch events with Javascript.

This example show how to create hybrid web-app, using WebView. And detect the touch events with Javascript.


/assets/mypage.html, it will be loaded in our WebView, to perform the main function.
<!DOCTYPE HTML>
<HTML>
<HEAD>
<script>

var p1;
var p2;

function init(){
p1 = document.getElementById('p1');
p2 = document.getElementById('p2');
p1.innerHTML=navigator.userAgent;
var canvas = document.getElementById('myCanvas');
var canvasOffsetX = canvas.getBoundingClientRect().left;
var canvasOffsetY = canvas.getBoundingClientRect().top;

canvas.width = window.innerWidth - (2 * canvasOffsetX);
canvas.height = window.innerHeight - canvasOffsetY - 100;
canvas.fillStyle="#a0a0a0";

canvas.addEventListener('touchstart', touchstartListener, false);
canvas.addEventListener('touchmove', touchmoveListener, false);
canvas.addEventListener('touchend', touchendListener, false);
canvas.addEventListener('touchenter', touchenterListener, false);
canvas.addEventListener('touchleave', touchleaveListener, false);
canvas.addEventListener('touchcancel', touchcancelListener, false);

}

function touchstartListener(event){
p2.innerHTML= "touchstart - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

function touchmoveListener(event){
p2.innerHTML= "touchmove - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

function touchendListener(event){
p2.innerHTML= "touchend - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

function touchenterListener(event){
p2.innerHTML= "touchenter - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

function touchleaveListener(event){
p2.innerHTML= "touchleave - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

function touchcancelListener(event){
p2.innerHTML= "touchcancel - <br/>"
+ event.changedTouches[0].pageX + ":" + event.changedTouches[0].pageY;
event.preventDefault();
}

</script>
</HEAD>
<BODY onload="init()" style="border:5px solid #000000;">

<p id='p1'>un-init</p>

<canvas id='myCanvas' style="border:1px solid #FF0000;">
Canvas not support!
</canvas>

<p id='p2'></p>

</BODY>
</HTML>

/res/layout/activity_main.xml, with <WebView>.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
android:background="@android:color/background_dark"
tools:context="com.example.androidhybridwebview.MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<WebView
android:id="@+id/mybrowser"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>

</LinearLayout>

package com.example.androidhybridwebview;

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;

public class MainActivity extends Activity {

WebView myBrowser;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myBrowser = (WebView) findViewById(R.id.mybrowser);
myBrowser.getSettings().setJavaScriptEnabled(true);
myBrowser.loadUrl("file:///android_asset/mypage.html");
}

}

Next:
Detect touch and draw circle on Android WebView with Javascript

Get touch pressure

MotionEvent provide getPressure() and getPressure(int pointerIndex) to get the touch pressure.

Example:

MyView.java
package com.example.androiddrawinview;

import java.util.ArrayList;
import android.content.Context;
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 MyView extends View {

private ArrayList<xy> drawList;
private boolean touching = false;

Paint paintTouchPointer_ONE;
Paint paintTouchPointer;

public MyView(Context context) {
super(context);
init();
}

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}

private void init(){
paintTouchPointer = new Paint();
paintTouchPointer.setColor(Color.RED);
paintTouchPointer.setStyle(Paint.Style.FILL);

paintTouchPointer_ONE = new Paint();
paintTouchPointer_ONE.setColor(Color.BLUE);
paintTouchPointer_ONE.setStyle(Paint.Style.STROKE);
}

@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.GRAY);
if(touching){
for(xy pt : drawList){

float PRESSURE_ONE_RADIUS = 50;
float radius = pt.getPressure() * PRESSURE_ONE_RADIUS;

canvas.drawCircle(
pt.getX(),
pt.getY(),
radius,
paintTouchPointer);

canvas.drawCircle(
pt.getX(),
pt.getY(),
PRESSURE_ONE_RADIUS,
paintTouchPointer_ONE);
}
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {

int action = event.getAction() & MotionEvent.ACTION_MASK;
switch(action){
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_DOWN:

ArrayList<xy> touchList = new ArrayList<xy>();
int pointerCount = event.getPointerCount();

for(int i=0; i < pointerCount; i++){
touchList.add(
new xy(
event.getX(i),
event.getY(i),
event.getPressure(i)));
}
drawList = touchList;
touching = true;
break;
default:
touching = false;
}

invalidate();
return true;
}

class xy{
float x;
float y;
float pressure;

public xy(float x, float y, float pressure){
this.x = x;
this.y = y;
this.pressure = pressure;
}

public float getX(){
return x;
}

public float getY(){
return y;
}

public float getPressure(){
return pressure;
}
}

}

/res/layout/fragment_main.xml, to include MyView in layout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.androiddrawinview.MainActivity$PlaceholderFragment" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<com.example.androiddrawinview.MyView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_margin="10dp" />
</LinearLayout>

</LinearLayout>

MainActivity.java, actually it is the default generated by Eclipse.
package com.example.androiddrawinview;

import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;

public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {

public PlaceholderFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
return rootView;
}
}

}

/res/layout/activity_main.xml, auto generated by Eclipse.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.androiddrawinview.MainActivity"
tools:ignore="MergeRootFrame" />

download filesDownload the files.

Cannot detect MotionEvent.ACTION_MOVE and ACTION_UP

Refer to former exercises, "Detect single touch on custom View to draw icon on canvas" and "Detect multi touch on custom View to draw icons on canvas"; true is return from onTouchEvent() method of the custom view, indicate that the event was handled.

If false is returned from onTouchEvent() method, indicate that your code will not handle the events, and the system will not pass you the subsequent events such as MotionEvent.ACTION_MOVE and MotionEvent.ACTION_UP.

This example show two custome View, MyView on left side and MyView2 on right side. MyView handle user touch and display icon in onTouchEvent() method and return true. MyView2 do the same job by extending MyView, but return false in onTouchEvent() method. It can be noticed that MyView2 on right side can detect MotionEvent.ACTION_DOWN only, not MotionEvent.ACTION_MOVE and MotionEvent.ACTION_UP.


Modify from last the exercise "Detect multi touch on custom View to draw icons on canvas", create MyView2.java extends MyView, override onTouchEvent() to return false.

package com.example.androiddrawinview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;

public class MyView2 extends MyView {

public MyView2(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

public MyView2(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

public MyView2(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
}

@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
return false;
}

}

Modify /res/layout/fragment_main.xml to replace the MyView on right hand side to MyView2.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.androiddrawinview.MainActivity$PlaceholderFragment" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<com.example.androiddrawinview.MyView
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_margin="10dp" />
<com.example.androiddrawinview.MyView2
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_margin="10dp" />
</LinearLayout>

</LinearLayout>


download filesDownload the files.

Detect multi touch on custom View to draw icons on canvas

Last exercise show how to "Detect single touch on custom View to draw icon on canvas". It is now modified to detect multi touch, to display multi icons.


MyView.java
package com.example.androiddrawinview;

import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MyView extends View {

private ArrayList<xy> drawList;
private float offsetX, offsetY;
private boolean touching = false;
private Bitmap bm;

public MyView(Context context) {
super(context);
init();
}

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}

private void init(){
bm = BitmapFactory.decodeResource(
getResources(),
R.drawable.ic_launcher);
offsetX = bm.getWidth()/2;
offsetY = bm.getHeight()/2;
}

@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.GRAY);
if(touching){
for(xy pt : drawList){
canvas.drawBitmap(bm,
pt.getX()-offsetX,
pt.getY()-offsetY,
null);
}
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {

int action = event.getAction() & MotionEvent.ACTION_MASK;
switch(action){
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_DOWN:

ArrayList<xy> touchList = new ArrayList<xy>();
int pointerCount = event.getPointerCount();

for(int i=0; i < pointerCount; i++){
touchList.add(
new xy(
event.getX(i),
event.getY(i)));
}
drawList = touchList;
touching = true;
break;
default:
touching = false;
}

invalidate();
return true;
}

class xy{
float x;
float y;

public xy(float x, float y){
this.x = x;
this.y = y;
}

public float getX(){
return x;
}

public float getY(){
return y;
}
}

}

/res/layout/fragment_main.xml, MainActivity.java and /res/layout/activity_main.xml, refer to last exercise.

download filesDownload the files.

Related:
Cannot detect MotionEvent.ACTION_MOVE and ACTION_UP

Detect single touch on custom View to draw icon on canvas

This example implement custom View, override onTouchEvent() and onDraw() to draw bitmap on canvas when user touch. In this implementation, only one touch point is detected.


Our custom View, MyView.java
package com.example.androiddrawinview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MyView extends View {

private float x, y;
private float offsetX, offsetY;
private boolean touching = false;
private Bitmap bm;

public MyView(Context context) {
super(context);
init();
}

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

private void init(){
bm = BitmapFactory.decodeResource(
getResources(),
R.drawable.ic_launcher);
offsetX = bm.getWidth()/2;
offsetY = bm.getHeight()/2;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}


@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.GRAY);
if(touching){
canvas.drawBitmap(bm, x-offsetX, y-offsetY, null);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();

switch(action){
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
touching = true;
break;
default:
touching = false;
}
invalidate();

return true;
}

}

Modify /res/layout/fragment_main.xml to add <com.example.androiddrawinview.MyView>.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.androiddrawinview.MainActivity$PlaceholderFragment" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<com.example.androiddrawinview.MyView
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

</LinearLayout>

Keep using the auto generated MainActivity.java and /res/layout/activity_main.xml.

MainActivity.java
package com.example.androiddrawinview;

import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;

public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {

public PlaceholderFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
return rootView;
}
}

}

/res/layout/activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.androiddrawinview.MainActivity"
tools:ignore="MergeRootFrame" />


download filesDownload the files.

Multi single touch View

Modify /res/layout/fragment_main.xml to have two <com.example.androiddrawinview.MyView>.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.androiddrawinview.MainActivity$PlaceholderFragment" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<com.example.androiddrawinview.MyView
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_margin="10dp" />
<com.example.androiddrawinview.MyView
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_margin="10dp" />
</LinearLayout>

</LinearLayout>

Now we have two single touch view together.


Next:
Detect multi touch on custom View to draw icons on canvas
Cannot detect MotionEvent.ACTION_MOVE and ACTION_UP

Detect touch and draw rect on bitmap

In last example "Detect touch and free draw on Bitmap", the points are drawn on the canvas (also the bitmap) directly when user touch.

Detect touch and draw rect on bitmap


In case of drawing square, the user touch on the screen to mark the start position, and move, and release on the end position. We have to keep displaying the updated square. But if we draw the rect on the canvasMaster, it will full of rect when user touch and move.

In this example, we create two overlay ImageViews, have same dimension. Also additional bitmap and canvas for the extra ImagewView. When ACTION_DOWN detected, we mark the starting position. When ACTION_MOVE, we draw rect on the extra canvasDrawingPane, not the canvasMaster. Such that we can clear and re-draw the canvas everytime. And finally when ACTION_UP, simple draw the extra bitmap on canvasMaster.



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.graphics.PorterDuff.Mode;
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;
ImageView imageResult, imageDrawingPane;

final int RQS_IMAGE1 = 1;

Uri source;
Bitmap bitmapMaster;
Canvas canvasMaster;
Bitmap bitmapDrawingPane;
Canvas canvasDrawingPane;
projectPt startPt;

@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);
imageDrawingPane = (ImageView)findViewById(R.id.drawingpane);

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);
startPt = projectXY((ImageView)v, bitmapMaster, x, y);
break;
case MotionEvent.ACTION_MOVE:
textSource.setText("ACTION_MOVE- " + x + " : " + y);
drawOnRectProjectedBitMap((ImageView)v, bitmapMaster, x, y);
break;
case MotionEvent.ACTION_UP:
textSource.setText("ACTION_UP- " + x + " : " + y);
drawOnRectProjectedBitMap((ImageView)v, bitmapMaster, x, y);
finalizeDrawing();
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;
}});

}

class projectPt{
int x;
int y;

projectPt(int tx, int ty){
x = tx;
y = ty;
}
}

private projectPt projectXY(ImageView iv, Bitmap bm, int x, int y){
if(x<0 || y<0 || x > iv.getWidth() || y > iv.getHeight()){
//outside ImageView
return null;
}else{
int projectedX = (int)((double)x * ((double)bm.getWidth()/(double)iv.getWidth()));
int projectedY = (int)((double)y * ((double)bm.getHeight()/(double)iv.getHeight()));

return new projectPt(projectedX, projectedY);
}
}

private void drawOnRectProjectedBitMap(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()));

//clear canvasDrawingPane
canvasDrawingPane.drawColor(Color.TRANSPARENT, Mode.CLEAR);

Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(3);
canvasDrawingPane.drawRect(startPt.x, startPt.y, projectedX, projectedY, paint);
imageDrawingPane.invalidate();


textSource.setText(x + ":" + y + "/" + iv.getWidth() + " : " + iv.getHeight() + "\n" +
projectedX + " : " + projectedY + "/" + bm.getWidth() + " : " + bm.getHeight()
);
}
}

private void finalizeDrawing(){
canvasMaster.drawBitmap(bitmapDrawingPane, 0, 0, null);
}

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

//Create bitmap of same size for drawing
bitmapDrawingPane = Bitmap.createBitmap(
tempBitmap.getWidth(),
tempBitmap.getHeight(),
config);
canvasDrawingPane = new Canvas(bitmapDrawingPane);
imageDrawingPane.setImageBitmap(bitmapDrawingPane);


} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

break;
}
}
}

}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<Button
android:id="@+id/loadimage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Load Image 1" />

<TextView
android:id="@+id/sourceuri"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ImageView
android:id="@+id/result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:background="@android:color/background_dark"
android:scaleType="centerInside" />
<ImageView
android:id="@+id/drawingpane"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:layout_alignLeft="@id/result"
android:layout_alignTop="@id/result"
android:layout_alignRight="@id/result"
android:layout_alignBottom="@id/result"/>
</RelativeLayout>

</LinearLayout>


download filesDownload the files.

Correction: To make sure bitmapDrawingPane have alpha channel, create it with Config.ARGB_8888.

     //Create bitmap of same size for drawing
bitmapDrawingPane = Bitmap.createBitmap(
tempBitmap.getWidth(),
tempBitmap.getHeight(),
Config.ARGB_8888);



more: Something about processing images in Android

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

Get bitmap color on touched position in ImageView

Last exercise demonstrate "How to detect touch event and position on ImageView". It's modified to get bitmap color on the touched position.

Get bitmap color on touched position in ImageView


package com.example.androiddrawbitmap;

import java.io.FileNotFoundException;

import android.R.color;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
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);
textSource.setBackgroundColor(
getProjectedColor((ImageView)v, bitmapMaster, x, y));
break;
case MotionEvent.ACTION_MOVE:
textSource.setText("ACTION_MOVE- " + x + " : " + y);
textSource.setBackgroundColor(
getProjectedColor((ImageView)v, bitmapMaster, x, y));
break;
case MotionEvent.ACTION_UP:
textSource.setText("ACTION_UP- " + x + " : " + y);
textSource.setBackgroundColor(
getProjectedColor((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
* return the color on the position
*/
private int getProjectedColor(ImageView iv, Bitmap bm, int x, int y){
if(x<0 || y<0 || x > iv.getWidth() || y > iv.getHeight()){
//outside ImageView
return color.background_light;
}else{
int projectedX = (int)((double)x * ((double)bm.getWidth()/(double)iv.getWidth()));
int projectedY = (int)((double)y * ((double)bm.getHeight()/(double)iv.getHeight()));

textSource.setText(x + ":" + y + "/" + iv.getWidth() + " : " + iv.getHeight() + "\n" +
projectedX + " : " + projectedY + "/" + bm.getWidth() + " : " + bm.getHeight()
);

return bm.getPixel(projectedX, projectedY);
}
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
switch (requestCode){
case RQS_IMAGE1:
source = data.getData();
textSource.setText(source.toString());

try {
bitmapMaster = BitmapFactory.decodeStream(
getContentResolver().openInputStream(source));
imageResult.setImageBitmap(bitmapMaster);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

break;
}
}
}

}


Modify android:scaleType and android:adjustViewBounds of ImageView in activity_main.xml. You can also try different setting to see how it affect the ImageView and Bitmap reloation.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<Button
android:id="@+id/loadimage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Load Image 1" />

<TextView
android:id="@+id/sourceuri"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<ImageView
android:id="@+id/result"
android:scaleType="centerInside"
android:adjustViewBounds="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/background_dark" />

</LinearLayout>


download filesDownload the files.

Next: Detect touch and free draw on Bitmap


more: Something about processing images in Android

Detect touch on ImageView

The example demonstrate how to detect touch event and position on ImageView, by implementing OnTouchListener().

Detect touch on ImageView


MainActivity.java
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.BitmapFactory;
import android.graphics.Canvas;
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);
break;
case MotionEvent.ACTION_MOVE:
textSource.setText("ACTION_MOVE- " + x + " : " + y);
break;
case MotionEvent.ACTION_UP:
textSource.setText("ACTION_UP- " + 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;
}});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
switch (requestCode){
case RQS_IMAGE1:
source = data.getData();
textSource.setText(source.toString());

try {
bitmapMaster = BitmapFactory.decodeStream(
getContentResolver().openInputStream(source));
imageResult.setImageBitmap(bitmapMaster);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

break;
}
}
}

}


Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<Button
android:id="@+id/loadimage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Load Image 1" />

<TextView
android:id="@+id/sourceuri"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<ImageView
android:id="@+id/result"
android:scaleType="matrix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/background_dark" />

</LinearLayout>


download filesDownload the files.

Next: Get bitmap color on touched position in ImageView



more: Something about processing images in Android