android一個下拉放大庫bug的解決過程及思考
起因
項目中要做一個下拉縮放圖片的效果,搜索了下github上面,找到了兩個方案。
-
https://github.com/Frank-Zhu/PullZoomView
這個庫本來做的還可以,不過有個缺陷就是,當scroolview滑動到底部,再向上拉動,會導致放大效果不連續,需要重新釋放,再次下拉,這對於追求細節的我來說,不可忍受。看了半天他的代碼,感覺他的實現方式很難修改為我想要的效果,后來就放棄了。
-
https://github.com/Gnod/ParallaxListView
這個自定義view寫的筆記簡單,也很容易看懂,同時避免了上面那個庫的問題,所以通過簡單的改造,我修改成為ScrollView的方式。但是同時也碰到一個問題,那就是當ScrollView中包含的view設置了OnClickListner事件的時候,觸摸事件的傳遞會出現問題。導致滑動出現異常。
下面就是我修改后的項目地址
下面主要說明下,我在修復bug的時候的思路。
首先看下具體問題:
當scrollview 中的元素未占滿scrollview的時候,在scrollivew的onInterceptTouchEvent方法中
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//中間代碼省略
/*
* Don't try to intercept touch if we can't scroll anyway.
*/
if (getScrollY() == 0 && !canScrollVertically(1)) {
return false;
}
//中間代碼省略
}
有一個canScrollVertically的判斷,用來返回scrollview的內容是否撐滿,所以當scrollview中的元素未占滿的時候,這里直接返回false,scrollview的觸摸事件沒有截獲,直接交給了子view處理,而這個時候,子view又設置了click事件,對觸摸進行了消費,所以scrollivew無法響應下拉的手勢操作了。
解決思路
對於這種觸摸沖突的問題,之前看《android藝術開發探索》時候,有過了解,無非就是兩個,一個外部攔截,一個內部攔截。想到scrollview中的元素可能會嵌套很多類型的。所以內部攔截的方式,可能工作量很大,需要每個View做處理。所以初步鎖定了外部攔截方式。
剛開始的思路也很清晰,就是當向下滑動的距離大於了TouchSlop時候,就截獲事件,不向下傳遞,如果是點擊的時候,則直接傳遞到下面的view進行處理。
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
xFirst = event.getX();
yFirst = event.getY();
mIsIntercept = false;
case MotionEvent.ACTION_MOVE:
xDistance = event.getX()-xFirst;
yDistance = event.getY()-yFirst;
if(Math.abs(yDistance)>mTouchSlop){
mIsIntercept = true;
}else {
mIsIntercept = false;
}
break;
case MotionEvent.ACTION_UP:
mIsIntercept = false;
break;
default:
break;
}
return mIsIntercept;
}
@Override
public boolean canScrollVertically(int direction) {
return true;
}
可是當這樣寫之后,總是會拋一個錯誤,Invalid pointerId=-1 in onTouchEvent,到源碼里面看,這個錯是
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("scrollview",event.getAction()+"");
touchListener.onTouchEvent(event);
return super.onTouchEvent(event);
}
是調用super.onTouchEvent(event)的時候拋出來的,再次查看源碼,原來是scrollview里面的一個變量沒有賦值,原本的賦值操作是在onInterceptTouchEvent中,但是這個函數已經被我們重寫,那怎么辦呢。后來想到,假如我在ACTION_DWON里面,先調用下surper.onInterceptTouchEvent(event),將變量賦值,那不就ok了,所以最后的代碼結果是
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
xFirst = event.getX();
yFirst = event.getY();
mIsIntercept = false;
//這句話是關鍵
super.onInterceptTouchEvent(event);
case MotionEvent.ACTION_MOVE:
xDistance = event.getX()-xFirst;
yDistance = event.getY()-yFirst;
if(Math.abs(yDistance)>mTouchSlop){
mIsIntercept = true;
}else {
mIsIntercept = false;
}
break;
case MotionEvent.ACTION_UP:
mIsIntercept = false;
break;
default:
break;
}
return mIsIntercept;
}
@Override
public boolean canScrollVertically(int direction) {
return true;
}
同時我們也將canScrollVertically這個函數直接返回true,即不讓super.onInterceptTouchEvent(event);調用的時候直接返回false
這樣我就完美的解決了事件的分發處理與Invalid pointerId=-1 in onTouchEvent這個問題。
總結
上述的問題,大概經過了兩天的各種嘗試,才得到結果。而且靈感是在回家之后,不經意的一個瞬間,想到的,所以有時候,遇到問題,自己可以暫時先放一放,換個時間思維角度也許就大不一樣,問題也就迎刃而解。
