之前寫過關於HorizontalScrollView滑動和按鈕事件觸發問題,但是不能所有的情況,最近幾天一直在想這個問題,今天有一個比較好的解決思路,最終應用在項目里面效果也很好,首先說明一下功能:
(1)、按下按鈕,不滑動,觸發按鈕功能
(2)、按下按鈕,滑動觸發滑動事件
這里的按下包含長按和短按情況
首先要解決這個問題需要明白Android中的Touch事件是如何進行處理的,這里有一篇文章:http://blog.csdn.net/jwzhangjie/article/details/9718693 里面詳細介紹了Touch事件處理方法,總結性語句:
當TouchEvent發生時,首先Activity將TouchEvent傳遞給最頂層的View, TouchEvent最先到達最頂層 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法進行分發,如果dispatchTouchEvent返回true ,則交給這個view的onTouchEvent處理,如果dispatchTouchEvent返回 false ,則交給這個 view 的 interceptTouchEvent 方法來決定是否要攔截這個事件,如果 interceptTouchEvent 返回 true ,也就是攔截掉了,則交給它的 onTouchEvent 來處理,如果 interceptTouchEvent 返回 false ,那么就傳遞給子 view ,由子 view 的 dispatchTouchEvent 再來開始這個事件的分發。如果事件傳遞到某一層的子 view 的 onTouchEvent 上了,這個方法返回了 false ,那么這個事件會從這個 view 往上傳遞,都是 onTouchEvent 來接收。而如果傳遞到最上面的 onTouchEvent 也返回 false 的話,這個事件就會“消失”,而且接收不到下一次事件。
注意:
(1)、當滑動的時候,你不一定能正好移動合適的位置,這個時候就需要使用smoothScrollTo一類的功能函數來調整位置,而smoothScrollTo調用的時候,最好放在Handler里面實現--問題:當手慢慢移動按鈕然后松開會出現異常,不過讓你快速移動按鈕,然后松開,按鈕自己滑動停止不會有問題。
(2)、一般來說我們把一個按鈕加上它前面的空白作為一組,滑動停止的時候,通過 getScrollX()來獲取你當前滑動的位置,這樣就可以計算你滑動停止的位置是否適合,如果不適合通過smoothScrollTo來調整,這里我用AppInforToSystem.bottom_btn_scroll_range作為一組的長度,方法如下:
更需要注意的是里面this.computeScroll();一定要加上,不然會出現按鈕和上面的文字分離的現象,
public void checkRange(int value){
try {
if (value <= AppInforToSystem.bottom_btn_scroll_range / 2) {
value = 0;
smoothScrollTo(0, 0);
AppInforToSystem.bottom_btn_scroll_flag = 2;
}else if (value >= (diff_scroll - AppInforToSystem.bottom_btn_scroll_range / 2)) {
smoothScrollTo(diff_scroll, 0);
AppInforToSystem.bottom_btn_scroll_flag = 1;
}else {
int val = value / AppInforToSystem.bottom_btn_scroll_range;
int diff = value % AppInforToSystem.bottom_btn_scroll_range;
if (diff < AppInforToSystem.bottom_btn_scroll_range / 2) {
smoothScrollTo(AppInforToSystem.bottom_btn_scroll_range * val, 0);
}else {
smoothScrollTo(AppInforToSystem.bottom_btn_scroll_range * (val+1), 0);
}
AppInforToSystem.bottom_btn_scroll_flag = 0;
}
this.computeScroll();
} catch (Exception e) {
AppInforToSystem.bottom_btn_scroll_flag = 0;
}
}
更需要注意的是里面this.computeScroll();一定要加上,不然會出現按鈕和上面的文字分離的現象,
(3)、
HorizontalScrollView只能有一個子孩子,所以要實現滑動多個組件的時候,需要使用LinearLayout或者RelativeLayout.
View view = (View) this.getChildAt(this.getChildCount() - 1);
diff_scroll = view.getRight() - getWidth();//初始狀態為0,沒有滾動,如果滾動到右邊,則等於subViewWidth - width
HorizontalScrollView只能有一個子孩子,所以要實現滑動多個組件的時候,需要使用LinearLayout或者RelativeLayout.
上面的diff_scroll就是你能夠滑動的最大距離
(4)、如何檢測滑動事件呢
我們需要實現一個類來繼承HorizontalScrollView,並且重寫onTouchEvent,當在ACTION_DOWN和ACTION_MOVE檢測滑動事件並作相應處理
case MotionEvent.ACTION_DOWN:
if(AppInforToSystem.bottom_btn_scroll_flag != 3){
AppInforToSystem.bottom_btn_scroll_flag = 3;
AppConnect.getInstance().callBack(CustomerInterface.MESSAGE_SCROLL_LR_FLAG);
}
case MotionEvent.ACTION_MOVE:
if(AppInforToSystem.bottom_btn_scroll_flag != 3){
AppInforToSystem.bottom_btn_scroll_flag = 3;
AppConnect.getInstance().callBack(CustomerInterface.MESSAGE_SCROLL_LR_FLAG);
}
break;
有的人會問為什么在ACTION_DOWN檢測了還要在ACTION_MOVE進行檢測,這個你就需要之前那篇文章里面的內容,Touch事件會一直傳遞到子view上面,所以不會觸發HorizontalScrollView的ACTION_DOWN事件,如果你移動按鈕,首先出發的是按鈕的MOVE事件,如果你移動幅度大的話就會觸發HorizontalScrollView的ACTION_MOVE事件
所以我們需要在這兩個事件都要檢測
(5)、有的按鈕功能是按下實現,松開關閉,這種情況就要用到onInterceptTouchEvent,讓它返回false,不攔截這個事件,你可以設置一個變量,當要實現這種功能的按你按下去的時候,設置變量AppInforToSystem.main_touch_flag為0,而在onInterceptTouchEvent檢測到AppInforToSystem.main_touch_flag == 0的時候返回false,這樣就不會觸發HorizontalScrollView的MOVE事件
(6)、如果你按下一個按鈕的時候,再移動按鈕觸發了HorizontalScrollView的ACTION_MOVE,則這個按鈕雖然不能觸發ACTION_UP事件,但是可以觸發ACTION_CANCEL事件,這樣我們就可以恢復你在DOWN的時候設置。