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