最近準(zhǔn)備材料统阿,發(fā)現(xiàn)了學(xué)習(xí)總結(jié)寫筆記的更多好處炊林,這兩天解bug姥卢,涉及到這個TouchDelegate,玩了一下渣聚,決定督促自己文檔独榴,demo都要寫好。
適用范圍
感覺一般的控件要擴(kuò)大點(diǎn)擊區(qū)域奕枝,用padding實(shí)現(xiàn)即可棺榔,這次碰到一個特殊的情況,如圖隘道,
因?yàn)椴季謱R的關(guān)系症歇,這個SeekBar不能有paddingTop,而這時又需要在上方增加可響應(yīng)區(qū)域谭梗,就用TouchDelegate了忘晤。
參考
Managing Touch Events in a ViewGroup這篇是Android Developer介紹TouchDelegate的文檔,含demo snippet激捏。下文代碼都截自這篇文檔设塔。
一句話概括
誰有足夠的地,就去跟誰要地種远舅;要地的時候得說清楚要哪一塊闰蛔,跟誰要就用誰地盤上的相對坐標(biāo)。
跟誰要地
控件自己的區(qū)域有限图柏,想要響應(yīng)它區(qū)域外的事件序六,就得要拜托那塊區(qū)域的地主了,拜托他把他的事件通知給自己蚤吹。因此例诀,
if (View.class.isInstance(myButton.getParent())) {
((View) myButton.getParent()).setTouchDelegate(touchDelegate);
}
只有它的parent的區(qū)域包含你需要擴(kuò)大到的那片區(qū)域,這個才會有效距辆∮嗟瑁看View的源碼:
/**
* Sets the TouchDelegate for this View.
*/
public void setTouchDelegate(TouchDelegate delegate) {
mTouchDelegate = delegate;
}
public boolean onTouchEvent(MotionEvent event) {
......
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
......
}
發(fā)現(xiàn)的確是地主有那塊地,才能分給別人去種跨算。
要哪塊地
TouchDelegate的構(gòu)造器
public TouchDelegate(Rect bounds, View delegateView)
第二個參數(shù)好理解,就是要被擴(kuò)大響應(yīng)范圍的那個View椭懊,那么第一個bounds區(qū)域到底是什么呢诸蚕?demo里是這樣寫的
myButton.getHitRect(delegateArea);
delegateArea.right += 100;
delegateArea.bottom += 100;
TouchDelegate touchDelegate = new TouchDelegate(delegateArea, myButton);
if (View.class.isInstance(myButton.getParent())) {
((View) myButton.getParent()).setTouchDelegate(touchDelegate);
}
看看這個區(qū)域被拿去做什么步势,上面的代碼看到一個View如果有被setTouchDelegete,它會先把touch事件給TouchDelegete處理背犯。在這里會用bounds判斷當(dāng)前的事件是否屬于我們想要擴(kuò)充到的范圍坏瘩,而這一切的位置,無論是MotionEvent的getX(),getY(),還有這個bounds漠魏,都是相對這個View的內(nèi)的坐標(biāo)倔矾,而非相對整個屏幕的坐標(biāo)。由此可見柱锹,這個bounds參數(shù)就是地主要分給別人種的地了哪自。
public boolean onTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
boolean sendToDelegate = false;
boolean hit = true;
boolean handled = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Rect bounds = mBounds;
if (bounds.contains(x, y)) {
mDelegateTargeted = true;
sendToDelegate = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
......
}
if (sendToDelegate) {
final View delegateView = mDelegateView;
if (hit) {
// Offset event coordinates to be inside the target view
event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2);
} else {
......
}
handled = delegateView.dispatchTouchEvent(event);
}
return handled;
}
要怎么知道要地主的哪塊地呢?demo中直接調(diào)用了View的getHitRect()方法禁熏,這個方法得到的是Hit rectangle in parent's coordinates壤巷,也就是說,如果地主是parent瞧毙,getHitRect()得到的正式那快地的位置胧华,但是parent沒有足夠的地,得要讓parent的parent去做地主呢宙彪?
下面這段代碼中矩动,mMiddle是mTarget的parent。通過view和parent的關(guān)系释漆,parent和grandparent的關(guān)系铅忿,推算出view和grandparent的關(guān)系,從而獲得當(dāng)parent的parent做地主時灵汪,那塊地的坐標(biāo)檀训。
Rect middleDelegateArea = new Rect();
mMiddle.getHitRect(middleDelegateArea);
Rect littleDelegateArea = new Rect();
mTarget.getHitRect(littleDelegateArea);
Rect delegateArea = new Rect();
delegateArea.left = middleDelegateArea.left + littleDelegateArea.left;
delegateArea.top = middleDelegateArea.top + littleDelegateArea.top;
delegateArea.right = delegateArea.left + littleDelegateArea.width();
delegateArea.bottom = delegateArea.top + littleDelegateArea.height();
delegateArea.left -= 150;
delegateArea.top -= 150;
delegateArea.right += 150;
delegateArea.bottom += 150;
if (View.class.isInstance(mTarget.getParent().getParent())) {
((View) mTarget.getParent().getParent()).setTouchDelegate(new TouchDelegate(delegateArea, mTarget));
}
代碼
寫了demo在Github,含apk享言,分別通過向parent和parent的parent 設(shè)置TouchDelegate來實(shí)現(xiàn)擴(kuò)大點(diǎn)擊范圍峻凫,通過這兩種方式的實(shí)現(xiàn)來加深對delegateArea的理解。