手勢識別兼容Android 1.x和2.x的代碼

http://www.cnblogs.com/stay/articles/1928943.html

 

摘要: 由於Android 2.x開始很多API變動比較大新增了一些比如多點觸控的支持,對於屏幕觸控手勢識別中我們需要考慮更多的實現方法,下面是一段兼容Android 1.x和2.x的代碼,可以讓我們的程序兼容幾乎99%的Android手機。 一 ...
 由於Android 2.x開始很多API變動比較大新增了一些比如多點觸控的支持,對於屏幕觸控手勢識別中我們需要考慮更多的實現方法,下面是一段兼容Android 1.x和2.x的代碼,可以讓我們的程序兼容幾乎99%的Android手機。
  一、首先新建一個抽象類判斷SDK版本問題
 public abstract class VersionedGestureDetector {
    private static final String TAG = "VersionedGestureDetector";
    OnGestureListener mListener;
    public static VersionedGestureDetector newInstance(Context context,
            OnGestureListener listener) {  //設計實例化構造方法,這裏Android123提示大家目前有3種API的實現方法,我們需要逐一考慮最優的解決方法,以滿足高平臺更多的功能實現。
        final int sdkVersion = Integer.parseInt(Build.VERSION.SDK); //使用android.os.Build判斷API Level,但需要將字符串轉換爲整形
        VersionedGestureDetector detector = null;
        if (sdkVersion < Build.VERSION_CODES.ECLAIR) { //如果版本小於2.0則使用1.5版本的API,可以兼容1.5和1.6
            detector = new CupcakeDetector();
        } else if (sdkVersion < Build.VERSION_CODES.FROYO) { //如果版本小於2.1則使用2.0版本的API,可以兼容2.0,2.0.1和2.1這三個版本
            detector = new EclairDetector();
        } else {  //否則使用2.2開始的新的觸控API
            detector = new FroyoDetector(context);
        }
        Log.d(TAG, "Created new " + detector.getClass()); //判斷最終選擇的到底是哪個版本的類
        detector.mListener = listener;
        return detector;
    }
    public abstract boolean onTouchEvent(MotionEvent ev); //我們需要根據版本決定onTouchEvent的實現
    public interface OnGestureListener { //手勢判斷接口主要是實現兩個方法
        public void onDrag(float dx, float dy);  //拖拽
        public void onScale(float scaleFactor); //縮放
    }
    private static class CupcakeDetector extends VersionedGestureDetector { //針對Android 1.5和1.6設計的兼容方式
        float mLastTouchX;
        float mLastTouchY;
        float getActiveX(MotionEvent ev) { //獲得當前X座標
            return ev.getX();
        }
        float getActiveY(MotionEvent ev) { //獲得當前Y座標
            return ev.getY();
        }
        boolean shouldDrag() { //是否是拖拽中或者說移動中
            return true;
        }
        @Override
        public boolean onTouchEvent(MotionEvent ev) { //重寫onTouchEvent方法
            switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: {  //向下
                mLastTouchX = getActiveX(ev);
                mLastTouchY = getActiveY(ev);
                break;
            }
            case MotionEvent.ACTION_MOVE: { //Android開發網提醒大家,由於1.x時代的API比較簡單,很多手勢沒有封裝,我們只能從ACTION_MOVE中根據座標變化判斷手勢樣式
                final float x = getActiveX(ev);
                final float y = getActiveY(ev);
                if (shouldDrag()) {
                    mListener.onDrag(x - mLastTouchX, y - mLastTouchY); //處理拖拽移動
                }
                mLastTouchX = x;
                mLastTouchY = y;
                break;
            }
            }
            return true;
        }
    }
    private static class EclairDetector extends CupcakeDetector { //這個是針對Android 2.0,2.0.1和2.1提供的解決方法,可以看到有很多多點觸控相關API出現
        private static final int INVALID_POINTER_ID = -1;
        private int mActivePointerId = INVALID_POINTER_ID;
        private int mActivePointerIndex = 0;
        @Override
        float getActiveX(MotionEvent ev) {
            return ev.getX(mActivePointerIndex);
        }
        @Override
        float getActiveY(MotionEvent ev) {
            return ev.getY(mActivePointerIndex);
        }
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            final int action = ev.getAction();
            switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mActivePointerId = ev.getPointerId(0);
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mActivePointerId = INVALID_POINTER_ID;
                break;
            case MotionEvent.ACTION_POINTER_UP: //有個點鬆開
                final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int pointerId = ev.getPointerId(pointerIndex); //獲取第幾個點
                if (pointerId == mActivePointerId) {
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                    mLastTouchX = ev.getX(newPointerIndex); //處理第newPointerIndex個點的x位置
                    mLastTouchY = ev.getY(newPointerIndex);
                }
                break;
            }
            mActivePointerIndex = ev.findPointerIndex(mActivePointerId);
            return super.onTouchEvent(ev);
        }
    }
    private static class FroyoDetector extends EclairDetector { //從Android 2.2開始可以很好的處理多點觸控的縮放問題
        private ScaleGestureDetector mDetector;
        public FroyoDetector(Context context) {
            mDetector = new ScaleGestureDetector(context,
                    new ScaleGestureDetector.SimpleOnScaleGestureListener() {
                @Override public boolean onScale(ScaleGestureDetector detector) {
                    mListener.onScale(detector.getScaleFactor()); //根據 ScaleGestureDetector.SimpleOnScaleGestureListener這個系統類處理縮放情況通過onScale方法
                    return true;
                }
            });
        }
        @Override
        boolean shouldDrag() {
            return !mDetector.isInProgress();
        }
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            mDetector.onTouchEvent(ev);
            return super.onTouchEvent(ev);
        }
    }
}

 有關調用方法,我們可以自定義一個View,取名爲TouchExampleView類,這裏來處理觸控相關的問題
public class TouchExampleView extends View {
    private Drawable mIcon; //我們以一個圖片爲參照物,根據手勢控制
    private float mPosX;
    private float mPosY;
    private VersionedGestureDetector mDetector; 
    private float mScaleFactor = 1.f; //原始縮放比例爲1.0
    public TouchExampleView(Context context) {
        this(context, null, 0);
    }
    public TouchExampleView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public TouchExampleView(Context context, AttributeSet attrs, int defStyle) { //實現我們自定義View的構造
        super(context, attrs, defStyle);
        mIcon = context.getResources().getDrawable(R.drawable.icon);
        mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight()); 
        mDetector = VersionedGestureDetector.newInstance(context, new GestureCallback()); //實例化剛纔的版本自適應手勢控制類
    }
    @Override
    public boolean onTouchEvent(MotionEvent ev) { //重寫onTouchEvent方法,使用VersionedGestureDetector類得出的數據。
        mDetector.onTouchEvent(ev);
        return true;
    }
    @Override
    public void onDraw(Canvas canvas) { //處理自定義View繪製方法
        super.onDraw(canvas);
        canvas.save();
        canvas.translate(mPosX, mPosY); //進行平移操作,根據mPosX和mPosY座標
        canvas.scale(mScaleFactor, mScaleFactor); //進行縮放操作,參數就是剛纔定義的float類型的縮放比例
        mIcon.draw(canvas); //直接繪製圖片變化到畫布中
        canvas.restore();
    }
    private class GestureCallback implements VersionedGestureDetector.OnGestureListener { 
        public void onDrag(float dx, float dy) { //這裏Android123提示大家在2.2中這個回調方法將可以支持拖拽的座標處理
            mPosX += dx;
            mPosY += dy;
            invalidate();
        }
        public void onScale(float scaleFactor) {
            mScaleFactor *= scaleFactor; //縮放控制
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); //限制最小縮放比例爲1.0最大爲5.0倍數
            invalidate();
        }
    }
}


 有關調用我們的自定義的TouchExampleView可以在Activity的onCreate方法中加入以下代碼,
     TouchExampleView view = new TouchExampleView(this);
     view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
     ViewGroup.LayoutParams.MATCH_PARENT));
     setContentView(view);  //替換掉原始的res.layout.main


最後Android123需要給大家說明的是使用本例子,直接使用Android 2.2的SDK創建工程,即API Level爲8,發佈時在androidmanifest.xml中加入uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8"這句可以兼容從Android 1.5到2.2的版本,有關2.3中新增的一個可以處理5個或5個以上的多點觸控增強類Android開發網將在以後的時間中介紹。

發佈了5 篇原創文章 · 獲贊 1 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章