1. 程式人生 > >android實現可拖動按鈕

android實現可拖動按鈕



功能:在Android中實現可拖動按鈕,同時實現按鈕的點選功能

相關問題:

  1. 按鈕拖動的界限限定。
  2. 按鈕單擊和拖動之間的衝突。
  3. 在介面未顯示之前,獲得View的高/寬。

問題描述:

  1. 如果不為按鈕的拖動範圍設定界限,按鈕將可以被拖出觸控式螢幕,影響操作。如果程式實現了位置的記錄功能(這裡暫不實現),當按鈕的顯示範圍超出顯示屏時,按鈕可能會變形。
  2. 在拖動的時間中,程式將會首先觸發事件的順序為:ACTION_DOWN -> ACTION_MOVE -> ACTION_UP,在觸發ACTION_DOWN時,系統將會在ACTION_UP結束後,觸發按鈕被點選的事件(按鈕點選事件被觸發的條件略),造成拖動事件和單機事件之間的衝突。
  3. 在Activity的onCreate()中,各個View並未被重畫,所以不過呼叫View.getHeight()還是呼叫View.getMeasuredHeight(),所得到的結果都為0。只有在onDraw中,View將會被重畫,但是此時往往對程式來說,獲取資料有顯得太晚。

解決方法:

  1. 通過對按鈕實現setOnTouchListener()監聽器,來使得按鈕可以被任意拖動,在監聽器中的ACTION_MOVE事件的處理中,對按鈕實現重畫和對拖動的界限的限定。
  2. 通過取得ACTION_UP與ACTION_DOWN之間按鈕的位移來確定按鈕所應該觸發的事件,並通過設定位移的大小來避免誤操作。
  3. 通過獲取介面中的ViewTree的監聽器,來獲得在View被重畫前,它被測量出的高/寬。

原始碼:

  1.佈局檔案(activity_main.xml)

______________________________________________________________________________________________________

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity" >

<Button
android:id="@+id/movebtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />

</RelativeLayout>

______________________________________________________________________________________________________

  2.介面程式碼(MainActivity.java)

______________________________________________________________________________________________________

package com.luxl.slideandtouch;

import android.os.Bundle;
import android.app.Activity;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.Window;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

private Button movebtn;        //可拖動按鈕
private boolean clickormove = true;  //點選或拖動,點選為true,拖動為false
private int downX, downY;      //按下時的X,Y座標
private boolean hasMeasured = false;  //ViewTree是否已被測量過,是為true,否為false
private View content;          //介面的ViewTree
private int screenWidth,screenHeight;  //ViewTree的寬和高

@Override
protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

content = getWindow().findViewById(Window.ID_ANDROID_CONTENT);//獲取介面的ViewTree根節點View

DisplayMetrics dm = getResources().getDisplayMetrics();//獲取顯示屏屬性
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;

ViewTreeObserver vto = content.getViewTreeObserver();獲取ViewTree的監聽器
vto.addOnPreDrawListener(new OnPreDrawListener() {

@Override
public boolean onPreDraw() {

// TODO Auto-generated method stub
if(!hasMeasured)
{

screenHeight = content.getMeasuredHeight();//獲取ViewTree的高度
hasMeasured = true;//設定為true,使其不再被測量。

}
return true;//如果返回false,介面將為空。

}

});
movebtn = (Button) findViewById(R.id.movebtn);
movebtn.setOnTouchListener(new OnTouchListener() {//設定按鈕被觸控的時間

int lastX, lastY; // 記錄移動的最後的位置

@Override
public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub
int ea = event.getAction();//獲取事件型別
switch (ea) {
case MotionEvent.ACTION_DOWN: // 按下事件

lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
downX = lastX;
downY = lastY;
break;

case MotionEvent.ACTION_MOVE: // 拖動事件

// 移動中動態設定位置
int dx = (int) event.getRawX() - lastX;//位移量X
int dy = (int) event.getRawY() - lastY;//位移量Y
int left = v.getLeft() + dx;
int top = v.getTop() + dy;
int right = v.getRight() + dx;
int bottom = v.getBottom() + dy;

//++限定按鈕被拖動的範圍
if (left < 0) {

left = 0;
right = left + v.getWidth();

}
if (right > screenWidth) {

right = screenWidth;
left = right - v.getWidth();

}
if (top < 0) {

top = 0;
bottom = top + v.getHeight();

}
if (bottom > screenHeight) {

bottom = screenHeight;
top = bottom - v.getHeight();

}

//--限定按鈕被拖動的範圍

v.layout(left, top, right, bottom);//按鈕重畫


// 記錄當前的位置
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
break;

case MotionEvent.ACTION_UP: // 彈起事件

  //判斷是單擊事件或是拖動事件,位移量大於5則斷定為拖動事件

if (Math.abs((int) (event.getRawX() - downX)) > 5
|| Math.abs((int) (event.getRawY() - downY)) > 5)

clickormove = false;

else

clickormove = true;

break;

}
return false;

}

});
movebtn.setOnClickListener(new OnClickListener() {//設定按鈕被點選的監聽器

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
if (clickormove)

Toast.makeText(MainActivity.this, "single click",
Toast.LENGTH_SHORT).show();

}

});

}

}