Thursday, May 8, 2014

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://android-er.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://android-er.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

No comments: