2012年11月13日 星期二

Android:OpenGL 建立2D矩形


Square:


package com.example.opengltest2;
import ....

public class Square {
// 點的陣列
private float vertices[] = { -1.0f, 1.0f, 0.0f, // 0, 左上角
  -1.0f, -1.0f, 0.0f, // 1, 左下角
  1.0f, -1.0f, 0.0f, // 2, 右下角
  1.0f, 1.0f, 0.0f, // 3, 右上角
};

// 連接點的次序
private short[] indices = { 0, 1, 2, 0, 2, 3 };

// 點的緩衝區
private FloatBuffer vertexBuffer;

// 索引值緩衝區
private ShortBuffer indexBuffer;

public Square() {
//初始化頂點座標暫存
// 浮點數是4位元組因此需要把點陣列長度乘以4
 ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
 vbb.order(ByteOrder.nativeOrder());//設定為本機平台的位元組順序
 vertexBuffer = vbb.asFloatBuffer();//轉換為float形式的緩衝,並建立實例
 vertexBuffer.put(vertices);//將頂點座標陣列放進緩衝
 vertexBuffer.position(0);////設定緩衝區的起始位置

 ////初始化構造索引資料緩衝
 // 短整數是2位元組因此需要把點陣列長度乘以2
 ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
 ibb.order(ByteOrder.nativeOrder());
 indexBuffer = ibb.asShortBuffer();
 indexBuffer.put(indices);
 indexBuffer.position(0);
}

/**
 * 畫圖函式
 *
 * @param gl
 */
public void draw(GL10 gl) {
 // 逆時鐘
 gl.glFrontFace(GL10.GL_CCW);
 // 啟動CULL_FACE
 gl.glEnable(GL10.GL_CULL_FACE);
 // 刪除多邀形的背景
 gl.glCullFace(GL10.GL_BACK);

 // 啟動點的緩衝區
 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
 // 指定位置和資料格式
 gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

 // 以三點劃出三角形
 gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
   GL10.GL_UNSIGNED_SHORT, indexBuffer);

 // 除能點的緩衝區
 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
 // 除能CULL_FACE
 gl.glDisable(GL10.GL_CULL_FACE);
}

}

OpenGLRenderer

package com.example.opengltest2;
import ...

public class OpenGLRenderer implements Renderer {
private Square square;

public OpenGLRenderer() {
 // 初始化
 square = new Square();
}
@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
// 清除螢幕和深度緩衝區
 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 
 // 以單位矩陣取代目前的矩陣
 gl.glLoadIdentity();
 // Z軸轉置 4 單位
 gl.glTranslatef(0, 0, -4);
 // 畫出方形
 square.draw(gl);

}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
// 設定新視域視窗的大小
 gl.glViewport(0, 0, width, height);
 // 選擇投射的陣列模式
 gl.glMatrixMode(GL10.GL_PROJECTION);
 // 重設投射陣
 gl.glLoadIdentity();
 // 也可以使用GLU內建的投影方法設置
 //GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f, 100.0f);
 float ratio=(float)width/height;//計算寬高比例大小
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);//設定投影模式
 // 選擇MODELVIEW陣列
 gl.glMatrixMode(GL10.GL_MODELVIEW);
 // 重設MODELVIEW陣列
 gl.glLoadIdentity();
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
// 設定背景顏色為黑色, 格式是RGBA
 gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
 // 設定流暢的陰影模式
 gl.glShadeModel(GL10.GL_SMOOTH);
 // 深度緩區的設定
 gl.glClearDepthf(1.0f);
 // 啟動深度的測試
 gl.glEnable(GL10.GL_DEPTH_TEST);
 // GL_LEQUAL深度函式測試
 gl.glDepthFunc(GL10.GL_LEQUAL);
 // 設定很好的角度計算模式
 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);

}

}

2012年11月12日 星期一

Android:OpenGL視界設定

1.MainActiviry


package com.example.opengltest2;

import ....

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //GLSurfaceView也可以使用圖形化工具建立,再使用findViewById叫進來
       GLSurfaceView view=new GLSurfaceView(this);
       view.setRenderer(new OpenGLRenderer());
       this.setContentView(view);
    } 
}


2.OpenGLRenderer


package com.example.opengltest2;

import ...

public class OpenGLRenderer implements Renderer {

@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
// 清除螢幕和深度緩衝區
 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 
 // 以單位矩陣取代目前的矩陣
 gl.glLoadIdentity();
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
// 設定新視域視窗的大小
 gl.glViewport(0, 0, width, height);
 // 選擇投射的陣列模式
 gl.glMatrixMode(GL10.GL_PROJECTION);
 // 重設投射陣
 gl.glLoadIdentity();
 // 這邊也可使用GLU內建的投影方法
 //GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f,100.0f);
 float ratio=(float)width/height;//計算視窗的寬高比例大小
  gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);//設定投影模式
 // 選擇MODELVIEW陣列
 gl.glMatrixMode(GL10.GL_MODELVIEW);
 // 重設MODELVIEW陣列
 gl.glLoadIdentity();
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
// 設定背景顏色為黑色, 格式是RGBA (這邊基本上是固定組態設定)
 gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
 // 設定流暢的陰影模式
 gl.glShadeModel(GL10.GL_SMOOTH);
 // 深度緩區的設定
 gl.glClearDepthf(1.0f);
 // 啟動深度的測試
 gl.glEnable(GL10.GL_DEPTH_TEST);
 // GL_LEQUAL深度函式測試
 gl.glDepthFunc(GL10.GL_LEQUAL);
 // 設定很好的角度計算模式
 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);

}

}

2012年10月30日 星期二

Android:OpenGL 建立


專案連結
package com.example.opengltest1;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;

import javax.microedition.khronos.opengles.GL10;

public class Triangle {

 private IntBuffer myVertexBuffer;// 頂點座標資料緩衝
 //private FloatBuffer myVertexBuffer;// 頂點座標資料緩衝
 //private FloatBuffer myColorBuffer;// 頂點著色資料緩衝
 private IntBuffer myColorBuffer;// 頂點著色資料緩衝
 private ByteBuffer myIndexBuffer;// 頂點建構的索引資料緩衝
 int vCount = 0;// 頂點數量
 int iCount = 0;// 索引數量
 float yAngle = 0.0f;// 繞Y旋轉的角度
 float zAngle = 0.0f;// 繞Z旋轉的角度

 public Triangle() {
  //初始化三角形的頂點座標暫存
  vCount = 3;// 設定頂點數量
  int[] vertices = new int[] {// 建立頂點資料陣列
  -80000, 60000, 0,
  -80000, -60000, 0,
  80000, -60000, 0, };
     //float[] vertices =new float[] { -80000.0f, 60000.0f, 0.0f, // 0, 左上角
       //-80000.0f, -60000.0f, 0.0f, // 1, 左下角
       //80000.0f, -60000.0f, 0.0f, // 2, 右下角  
     //};
  /*建立頂點座標資料暫存,由於不同平台位元組順序不同,
   * 資料單位不是位元組的要經由ByteBuffer轉換
   */
  ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4);
  //配置新的區塊
  vbb.order(ByteOrder.nativeOrder());//設定為本機平台的位元組順序
  //myVertexBuffer=vbb.asFloatBuffer();
  myVertexBuffer=vbb.asIntBuffer();//轉換為Int形式的緩衝,並將頂點作標資料緩衝建立實例
  myVertexBuffer.put(vertices);//將頂點座標資料放進緩衝
  myVertexBuffer.position(0);//設定緩衝區的起始位置

  //初始化三角形的顏色資料暫存
  int[] color=new int[]{// 建立頂點顏色陣列,有三個頂點,四個色彩值為RGBA
   0,65535,65535,0,
   65535,65535,65535,0,
   65535,65535,65535,0,
  };
  //float[] color=new float[]{
    //1.0f,1.0f,1.0f,0.0f,
    //1.0f,1.0f,1.0f,0.0f,
    //1.0f,1.0f,1.0f,0.0f,
  //};

  /*建立顏色資料暫存,由於不同平台位元組順序不同,
   * 資料單位不是位元組的要經由ByteBuffer轉換
   */
  ByteBuffer cbb=ByteBuffer.allocateDirect(color.length*4);
  //配置新的區塊
  cbb.order(ByteOrder.nativeOrder());//設定本機平台的位元組順序
  myColorBuffer=cbb.asIntBuffer();//轉換為Int形式的Buffer,並建立顏色資料緩衝的實例
  //myColorBuffer=cbb.asFloatBuffer();
  myColorBuffer.put(color);//將顏色資料陣列放入緩衝
  myColorBuffer.position(0);

  //初始化三角型構造索引資料緩衝
  iCount=3;
  byte[]index=new byte[]{
   0,1,2
  };
  //建立三角形構造資料緩衝,因為此資料陣列本身就是Byte形式,因此不需要作轉換
  myIndexBuffer=ByteBuffer.allocateDirect(index.length);
  //配置新的區塊
  myIndexBuffer.put(index);
  myIndexBuffer.position(0);

 }

 public void drawself(GL10 gl) {
  // TODO Auto-generated method stub
  gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//啟動頂點座標陣列
  gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//啟動頂點顏色陣列
  gl.glRotatef(yAngle, 0, 1, 0);//根據yAngle的角度值,繞Y旋轉
  gl.glRotatef(zAngle, 0, 0, 1);//根據zAngle的角度值,繞z旋轉
  gl.glVertexPointer(3, GL10.GL_FIXED, 0, myVertexBuffer);
  gl.glColorPointer(4, GL10.GL_FIXED, 0, myColorBuffer);
  gl.glDrawElements(GL10.GL_TRIANGLES, iCount, GL10.GL_UNSIGNED_BYTE, myIndexBuffer);
 }
}

MainActivity.java
package com.example.opengltest1;

import android.os.Bundle;
import android.app.Activity;
import android.widget.LinearLayout;

public class MainActivity extends Activity
{
    private MySurfaceView mysurfaceview;
    private LinearLayout lin;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lin=(LinearLayout)findViewById(R.id.LinearLayout1);
        mysurfaceview=new MySurfaceView(this);
        mysurfaceview.requestFocus();
        mysurfaceview.setFocusableInTouchMode(true);
        lin.addView(mysurfaceview);
    }
    @Override
    protected void onPause()
    {
        // TODO Auto-generated method stub
        super.onPause();
        mysurfaceview.onPause();
    }
    @Override
    protected void onResume()
    {
        // TODO Auto-generated method stub
        super.onResume();
        mysurfaceview.onResume();
    }


}

MyRenderer.java
package com.example.opengltest1;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;

public class MyRenderer implements Renderer
{
    Triangle tr = new Triangle();

    public void onDrawFrame(GL10 gl)
    {
        // TODO Auto-generated method stub
        gl.glEnable(GL10.GL_CULL_FACE);// 打開背面剪裁
        gl.glShadeModel(GL10.GL_SMOOTH);//設定著色模型為平滑著色
        gl.glFrontFace(GL10.GL_CCW);//設定自訂捲繞順序:逆時針為正面
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);//清除暫存
        gl.glMatrixMode(GL10.GL_MODELVIEW);//設定當前矩陣為模式矩陣
        gl.glLoadIdentity();//設定當前矩陣為單位矩陣,此方法相当于我们手机的重置功能,它将所选择的矩阵状态恢复成原始状态
        gl.glTranslatef(0, 0, -4.0f);//把座標系往Z軸負方向平移兩個單位
        tr.drawself(gl);
    }

    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        // TODO Auto-generated method stub
        gl.glViewport(0, 0, width, height);//設定視窗大小和位置
        gl.glMatrixMode(GL10.GL_PROJECTION);//設定矩陣為投影矩陣
        gl.glLoadIdentity();
        float ratio=(float)width/height;//比例大小
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);//設定投影模式
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        // TODO Auto-generated method stub
        gl.glDisable(GL10.GL_DITHER);//關閉抗抖動
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_FASTEST);//設置模式
        gl.glClearColorx(0, 0, 0, 0);//螢幕背景為黑色(RGBA)
        gl.glEnable(GL10.GL_DEPTH_TEST);//啟動深度檢測
    }

}

MySurfaceView.java
package com.example.opengltest1;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;

public class MySurfaceView extends GLSurfaceView
{

    private final float TOUCH_SCALE_FATOR=180.0f/320;
    private MyRenderer myren;
    private float previousX;
    private float previousY;

    public MySurfaceView(Context context)
    {
        super(context);
        // TODO Auto-generated constructor stub
        myren=new MyRenderer();
        this.setRenderer(myren);
        this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        // TODO Auto-generated method stub
        float x=event.getX();
        float y=event.getY();
        switch (event.getAction())
        {
        case MotionEvent.ACTION_MOVE:
            float dy=y-previousY;
            float dx=x-previousX;
            myren.tr.yAngle=myren.tr.yAngle+dx*TOUCH_SCALE_FATOR;
            myren.tr.zAngle=myren.tr.zAngle+dy*TOUCH_SCALE_FATOR;
            MySurfaceView.this.requestRender();
            //break;
        }
        previousY=y;
        previousX=x;
        return true;
    }



}

2012年9月6日 星期四

Android:QR-Code掃描以及SimpleAdapter應用

1.新建立一個XML layout,用來顯示ListView呈現的介面,這邊就簡單3個TextView


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView" />

</LinearLayout>

2. Java Code


package com.example.qrcodetest;

import....
public class MainActivity extends Activity {
ListView listview;
//建構SimpleAdapter所需之參數,用來放置要顯示在ListView的資訊
ArrayList<HashMap<String,String>> list=new ArrayList<HashMap<String,String>>();
private int requestCode = 1;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listview = (ListView) findViewById(R.id.listView1);

}

public void but_onclic(View v) {
//啟動QR-Code Scanner,此為固定步驟
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
startActivityForResult(intent, requestCode);

}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
//取回掃描到的資料
String content = data.getStringExtra("SCAN_RESULT");
//切割字串資料
String[] result = content.split("\n");
//宣告HashMap<String,String>,並將切割好的資料一一放入
HashMap<String,String>latte=new HashMap<String,String>();
if(result.length>0){
latte.put("name", result[0]);
latte.put("tel", result[1]);
latte.put("addr", result[2]);
}
//再將HashMap放回ArrayList,準備建構SimpleAdapter
list.add(latte);
importData2List();
}
}else{
Toast.makeText(this, "scan error", Toast.LENGTH_SHORT).show();
}

}

private void importData2List() {
// TODO Auto-generated method stub
SimpleAdapter simpleadapter = new SimpleAdapter(
this,        //設定接口環境
list,   //設定接口集合容器(ArrayList)
R.layout.raw,//資料顯示用的UI XML檔 
new String[] { "name", "tel", "addr" },//資料欄位相對於Key
new int[] { R.id.textView1, R.id.textView2, R.id.textView3 }//資料顯示UI元件的id
);
listview.setAdapter(simpleadapter);
}

}




2012年8月30日 星期四

Android:藉由VelocityTracker偵測觸控滑動方向


package com.example.mytouch2;

import...

public class MainActivity extends Activity {
private TextView msg;
private StringBuilder data;
//宣告觸控速率追蹤物件之變數
private VelocityTracker tracker;
private LinearLayout rootlayout;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
data = new StringBuilder();
msg = (TextView) findViewById(R.id.msg);
msg.setTextSize(26);
rootlayout = (LinearLayout) findViewById(R.id.rootlayout);
//註冊觸控監聽物件以及處理
rootlayout.setOnTouchListener(new OnTouchListener() {

public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:
data.delete(0, data.length());
msg.setText("");
//取得觸控速率追蹤物件
tracker = VelocityTracker.obtain();
break;

case MotionEvent.ACTION_MOVE:
// 將觸控相關情境資料加入tracker中
tracker.addMovement(event);
break;

case MotionEvent.ACTION_UP:
// 計算目前速率,以每微秒1個像素為單位
tracker.computeCurrentVelocity(1);
/*
* 判斷X分量與Y分量何者較大(必須取絕對值),
* |x|>|Y|代表為接近水平方向, 其中,X>0為向右;X<0為向左
* |x|>|Y|代表為接近垂直方向, 其中,Y>0為向下;Y<0為向上                                
*/
if (Math.abs(tracker.getXVelocity()) > Math.abs(tracker
.getYVelocity())) {
if (tracker.getXVelocity() > 0) {
// 水平,向右
msg.setText(String.format("水平(左->右)X:%.4f%nY:%.4f",
tracker.getXVelocity(),
tracker.getYVelocity()));
rootlayout.setBackgroundColor(Color.BLUE);
} else {
// 水平向左
msg.setText(String.format("水平(右->左)X:%.4f%nY:%.4f",
tracker.getXVelocity(),
tracker.getYVelocity()));
rootlayout.setBackgroundColor(Color.YELLOW);
}

} else {
if (tracker.getYVelocity() > 0) {
// 垂直向下
msg.setText(String.format("垂直(上->下)X:%.4f%nY:%.4f",
tracker.getXVelocity(),
tracker.getYVelocity()));
rootlayout.setBackgroundColor(Color.RED);
} else {
// 垂直向上
msg.setText(String.format("垂直(上->下)X:%.4f%nY:%.4f",
tracker.getXVelocity(),
tracker.getYVelocity()));
rootlayout.setBackgroundColor(Color.GREEN);
}

}
break;

}

return true;
}

});

}

}

Android:多點觸控之應用以及取得螢幕解析度


package com.example.my_touch3;

import ...

public class MainActivity extends Activity {
private int x,y;//圓心
private int gRadius=100;//預設圓半徑
private int touchx1,touchy1,touchx2,touchy2;//用來判斷當兩點觸控圓心所用
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new TouchView(this));
       
        // 取得螢幕解析度,dm.widthPixels為寬,dm.heightPixels為高
        DisplayMetrics dm = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(dm);
        x = (dm.widthPixels)/2;
        y = (dm.heightPixels)/2;
    }
//自建的類別,繼承View,當成活動之畫面呈現
private class TouchView extends View{
private Paint paint;
    //類別建構式
public TouchView(Context context) {
super(context);
paint=new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//設定畫筆顏色,反鋸齒,挖空心
paint.setColor(0xffff0000);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
//畫圓 (圓心X,圓心Y,半徑,畫筆)
canvas.drawCircle(x, y, gRadius, paint);
}
//螢幕觸控事件處理,這邊是簡易方法只能偵測layout,無法針對特定元件進行監聽
@Override
public boolean onTouchEvent(MotionEvent event) {
int pointerNum=event.getPointerCount();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(pointerNum==1){
//單點觸控時
x=(int) event.getX();
y=(int) event.getY();

}else if(pointerNum==2){
//兩點觸控時
touchx1=(int)event.getX(0);
touchy1=(int)event.getY(0);
touchx2=(int)event.getX(1);
touchy2=(int)event.getY(1);

x=(int)(touchx1+touchx2)/2;
y=(int)(touchy1+touchy2)/2;
//取得圓半徑的公式
gRadius=(int)(Math.sqrt( (Math.pow(touchx1-touchx2,2))+ (Math.pow(touchy1-
                                                      touchy2,2))));


}
invalidate();//自動呼叫onDraw方法
break;
case MotionEvent.ACTION_UP:
break;
}
return true;

}


}
}

Android:多點觸控偵測以及螢幕觸控事件監聽




package com.example.my_touch1;

import ....
public class MainActivity extends Activity {
LinearLayout layout;
TextView msg;
private StringBuilder data;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        layout=(LinearLayout)findViewById(R.id.rootlayout);
        msg=(TextView)findViewById(R.id.textView1);
        data=new StringBuilder();
        //註冊觸控監聽物件
        layout.setOnTouchListener(new MytouchListener());
    }
   //觸控監聽物件之類別,需實作OnTouchListener
   private class MytouchListener implements OnTouchListener{
   //實作OnTouchListener所需實作之方法
public boolean onTouch(View view, MotionEvent event) {
// 判斷觸控事件
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
data.delete(0, data.length());
msg.setText(data.append("Action down"+"("+event.getX()+","+event.getY()+")"+"\n"));
break;
case MotionEvent.ACTION_UP:
msg.setText(data.append("Action up"+"("+event.getX()+","+event.getY()+")"+"\n"));
break;
case MotionEvent.ACTION_MOVE:
msg.setText(data.append("Action move"+"("+event.getX()+","+event.getY()+")"+"\n"));
break;
}
//取得多點觸控的點數
int pCount=event.getPointerCount();
data.append("多點觸控點數為:"+pCount+"\n");
for(int i=0;i<pCount;i++){
//(event.getX(0),event.getY(0))代表第一點觸控點之座標
data.append(String.format("觸控點%d:(%.2f,%.2f)%n",
                                                                   event.getPointerId(i),event.getX(i),event.getY(i)));

}
msg.setText(data);
return true;
}

   }
}