Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> 結合支付寶和微信首頁鞏固Android事件分發機制 (附項目源碼)

結合支付寶和微信首頁鞏固Android事件分發機制 (附項目源碼)

編輯:Android開發實例

  Android的事件分發和處理方式

  對android開發有一定了解的同學一定或多或少知道android的觸摸事件分發,整個事件的分發消耗流程都可以通過看源碼理解,下面通過講解demo幫助加深事件分發的理解和在實戰中的應用。首先直接上demo截圖:

結合支付寶和微信首頁鞏固Android事件分發機制

  demo布局

  整個首頁布局是這樣的,最外層是ViewPager,裡面包含四個子功能,每個子功能的視圖都是一個Fragment。“功能1”裡的列表項是一個GridView,此gridview外層是帶有LinearLayout的ScrollView。

  布局如下

結合支付寶和微信首頁鞏固Android事件分發機制

  難點分析和講解

  一、GridView高度問題

  二、長按GridView某一項可以替換位置,最後一項“更多”不參與滑動與替換位置

  三、GridView長按並滑動某一項時,滑動過程中與ScrollView和ViewPager沖突問題

  四、滑動GridView中某一項時,當滑動到頂部時ScrollView要能向下滾動;手指滑動到底部時要能項上滾動

  解決問題

  解決問題一

  為了解決這一系列問題,我需要重寫DragGridView繼承GridView,第一步需要讓DragGridView的每一項item占滿整個視圖,而gridview默認是限定高度的,解決辦法是重寫onMeasure,修改測量高度方法,如下

Java代碼
  1. @Override  
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.     int expandSpec = View.MeasureSpec.makeMeasureSpec(  
  4.             Integer.MAX_VALUE >> 2, View.MeasureSpec.AT_MOST);//1.精確模式(MeasureSpec.EXACTLY) 2.最大模式(MeasureSpec.AT_MOST) 3.未指定模式(MeasureSpec.UNSPECIFIED)  
  5.     super.onMeasure(widthMeasureSpec, expandSpec);  
  6. }  

  解決問題二

  現在我們需要長按GridView某一項可以替換位置,思路是首先需要捕捉到長按事件,那麼怎麼算長按事件呢?手指快速的從item上劃是不算的,手指按下然後很快離開這是點擊事件也不能算長按。只有手指在item上按下並且停留在item上一定時間才能算長按。有了思路,下面看關鍵的幾處代碼:

Java代碼
  1. private Handler mHandler = new Handler();  
  2. //用來處理是否為長按的Runnable  
  3. private Runnable mLongClickRunnable = new Runnable() {  
  4.   
  5.     @Override  
  6.     public void run() {  
  7.     //todo  
  8.     }  
  9. };  
  10.   
  11. @Override  
  12. public boolean dispatchTouchEvent(MotionEvent ev) {  
  13.     switch(ev.getAction()){  
  14.         case MotionEvent.ACTION_DOWN:  
  15.             //使用Handler延遲dragResponseMS執行mLongClickRunnable  
  16.             mHandler.postDelayed(mLongClickRunnable, dragResponseMS);  
  17.             break;  
  18.         case MotionEvent.ACTION_MOVE:  
  19.             //如果我們在按下的item上面移動,只要不超過item的邊界我們就不移除mRunnable  
  20.             if(!isTouchInItem(mStartDragItemView, moveX, moveY)){  
  21.                 mHandler.removeCallbacks(mLongClickRunnable);  
  22.                 mHandler.removeCallbacks(mScrollRunnable);  
  23.             }  
  24.   
  25.             break;  
  26.         case MotionEvent.ACTION_UP:  
  27.         case MotionEvent.ACTION_CANCEL:  
  28.             mHandler.removeCallbacks(mLongClickRunnable);  
  29.             break;  
  30.     }  
  31.     return super.dispatchTouchEvent(ev);  
  32. }  

  解析: 定義一個Handler,重寫ViewGroup(這裡即DragGridView)分發事件方法dispatchTouchEvent(),在接受到ACTION DOWN事件時handler向消息隊列會延遲發送一條消息,延遲時間就是長按時間的閥值,當接受到消息時說明長按事件已成立,如果手指在消息延遲期間滑走或移出則取消消息。

  有了長按事件如何滑動某一條item呢?如何替換兩個item位置呢?

  這裡的解決思路是,當其中的某項假設是item1接收到長按事件後,先隱藏item1,在item1的位置上新建一個和item1長相位置都一樣view假設叫它litem,手指滑動時對litem跟隨滑動,接著重點來了,當手指滑動到item2區域後發出一個替換事件。這樣我們就成功將兩個item替換了位置並且用戶體驗比較好,代碼片段如下:

Java代碼
  1.  //用來處理是否為長按的Runnable  
  2. private Runnable mLongClickRunnable = new Runnable() {  
  3.   
  4.     @Override  
  5.     public void run() {  
  6.         if (onDragStartListener != null) {  
  7.             onDragStartListener.onDragStart();  
  8.         }  
  9.         isDrag = true; //設置可以拖拽  
  10.         mVibrator.vibrate(50); //震動一下  
  11.         mStartDragItemView.setVisibility(View.INVISIBLE);//隱藏該item  
  12.         //根據我們按下的點顯示item鏡像  
  13.         createDragImage(mDragBitmap, mDownX, mDownY);  
  14.   
  15.   
  16.     }  
  17. };  
  18. /** 
  19.  * 設置替換回調接口 
  20.  * @param onChangeListener 
  21.  */  
  22. public void setOnChangeListener(OnChangeListener onChangeListener){  
  23.     this.onChangeListener = onChangeListener;  
  24. }  
  25. /** 
  26.  * 創建拖動的鏡像 
  27.  * @param bitmap 
  28.  * @param downX 
  29.  *          按下的點相對父控件的X坐標 
  30.  * @param downY 
  31.  *          按下的點相對父控件的X坐標 
  32.  */  
  33. private void createDragImage(Bitmap bitmap, int downX , int downY){  
  34.     mWindowLayoutParams = new WindowManager.LayoutParams();  
  35.     mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; //圖片之外的其他地方透明  
  36.     mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;  
  37.     mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left;  
  38.     mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top - mStatusHeight;  
  39.     mWindowLayoutParams.alpha = 0.55f; //透明度  
  40.     mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
  41.     mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
  42.     mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
  43.             | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE ;  
  44.   
  45.     mDragImageView = new ImageView(getContext());  
  46.     mDragImageView.setImageBitmap(bitmap);  
  47.     mWindowManager.addView(mDragImageView, mWindowLayoutParams);  
  48. }  
  49. @Override  
  50. public boolean onTouchEvent(MotionEvent ev) {  
  51.     if(isDrag && mDragImageView != null){  
  52.         switch(ev.getAction()){  
  53.             case MotionEvent.ACTION_MOVE:  
  54.                 moveX = (int) ev.getX();  
  55.                 moveY = (int) ev.getY();  
  56.                 moveRawY = (int) ev.getRawY();  
  57.                 //拖動item  
  58.                 onDragItem(moveX, moveY);  
  59.                 break;  
  60.             case MotionEvent.ACTION_UP:  
  61.                 onStopDrag();  
  62.                 isDrag = false;  
  63.                 break;  
  64.         }  
  65.         return true;  
  66.     }  
  67.     return super.onTouchEvent(ev);  
  68. }  

  解析:在接收到長按事件後會調用 createDragImage(mDragBitmap, mDownX, mDownY)方法,這是為了在item的位置上創建一個長相一樣的view,當手指滑動時在onTouch()方法中監聽move事件處理view的位置

  解決問題三

  為題三的解決比較簡單,當gridview接收到長按事件後,處理ViewPager和ScrollView的方法一樣requestDisallowInterceptTouchEvent(false); 這個方法是讓ViewGroup本身不攔截觸摸事件。當gridView滑動結束在requestDisallowInterceptTouchEvent(true);

  解決問題四

Java代碼
  1. public class ControlScrollView extends ScrollView {  
  2.   
  3. private boolean isInControl = true;  
  4. private int moveSpeed = 5;  
  5. private final int msgWhat = 1;  
  6. private final int time = 20;  
  7. private ScrollState scrollState;  
  8.   
  9. public ControlScrollView(Context context) {  
  10.     super(context);  
  11.     init();  
  12. }  
  13.   
  14. public ControlScrollView(Context context, AttributeSet attrs) {  
  15.     super(context, attrs);  
  16.     init();  
  17. }  
  18.   
  19. @Override  
  20. public boolean dispatchTouchEvent(MotionEvent ev) {  
  21.     switch (ev.getAction()) {  
  22.         case MotionEvent.ACTION_MOVE:/**/  
  23.             if (!isInControl) {  
  24.                 if (ev.getY() < 0) {  
  25.                     if (!myHandler.hasMessages(msgWhat)) {  
  26.                         Message msg = new Message();  
  27.                         msg.arg1 = -1;  
  28.                         msg.what = msgWhat;  
  29.                         myHandler.sendMessageDelayed(msg, time);  
  30.                     }  
  31.                     return super.dispatchTouchEvent(ev);  
  32.                 } else if (ev.getY() > getHeight()) {  
  33.                     if (!myHandler.hasMessages(msgWhat)) {  
  34.                         Message msg = new Message();  
  35.                         msg.arg1 = 1;  
  36.                         msg.what = msgWhat;  
  37.                         myHandler.sendMessageDelayed(msg, time);  
  38.                     }  
  39.                     return super.dispatchTouchEvent(ev);  
  40.                 } else {  
  41.                     myHandler.removeMessages(msgWhat);  
  42.                 }  
  43.             } else {  
  44.                 myHandler.removeMessages(msgWhat);  
  45.             }  
  46.             break;  
  47.         case MotionEvent.ACTION_UP:  
  48.         case MotionEvent.ACTION_CANCEL:  
  49.             if (scrollState != null) {  
  50.                 scrollState.stopTouch();  
  51.             }  
  52.             myHandler.removeMessages(msgWhat);  
  53.             requestDisallowInterceptTouchEvent(false);  
  54.             break;  
  55.     }  
  56.     return super.dispatchTouchEvent(ev);  
  57. }  
  58.   
  59.   
  60. @Override  
  61. protected void onDetachedFromWindow() {  
  62.     super.onDetachedFromWindow();  
  63.     myHandler.removeMessages(msgWhat);  
  64. }  
  65.   
  66.   
  67. private void init() {  
  68.     moveSpeed = ScreenUtils.dip2px(getContext(), moveSpeed);  
  69. }  
  70.   
  71. public boolean isInControl() {  
  72.     return isInControl;  
  73. }  
  74.   
  75. public ScrollState getScrollState() {  
  76.     return scrollState;  
  77. }  
  78.   
  79. public void setScrollState(ScrollState scrollState) {  
  80.     this.scrollState = scrollState;  
  81. }  
  82.   
  83. public void setInControl(boolean inControl) {  
  84.     isInControl = inControl;  
  85. }  
  86.   
  87. private Handler myHandler = new Handler() {  
  88.     @Override  
  89.     public void dispatchMessage(Message msg) {  
  90.         super.dispatchMessage(msg);  
  91.         smoothScrollBy(0, moveSpeed * (msg.arg1 > 0 ? 1 : -1));  
  92.         Message msg1 = new Message();  
  93.         msg1.what = msg.what;  
  94.         msg1.arg1 = msg.arg1;  
  95.         myHandler.sendMessageDelayed(msg1, time);  
  96.     }  
  97. };  
  98.   
  99. public interface ScrollState {  
  100.     void stopTouch();  
  101. }  

  解析:大致邏輯就是判斷手指位置,超出邊界發送消息對scrollview進行滾動

  結束

  源碼地址 https://github.com/halibobo/TouchListenerConflict

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved