我有一个由许多ScrollView组成的图库,每个ScrollView都占据整个屏幕。问题是ScrollViews的onTouchEvent返回true,因此阻止DOM中的任何其他视图处理相同的事件(在ScrollView级别处理后被吞下)。因此,Gallery不再滚动。另一方面,如果我像这样重写onTouchEvent:
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
return false; // <<<<<<<<<<<<<<<<<
}
然后Gallery接收其on事件进行处理,但SrollView不再滚动。无论哪种方式,你输了!或者你呢?
问题听起来很令人费解,但我相信如果你在过去偶然发现它,你会立即认出它,因为它真是一个该死的!
感谢
答案 0 :(得分:6)
以下是我在使用纵向ScrollViews
的图库中的尝试。
它使用自己的GestureDetector
实例,并使用MotionEvents
中的onInterceptTouchEvent
提供。
当手势检测器识别出滚动时,我们确定它是水平还是垂直并锁定方向,直到手势结束。这避免了对角线滚动。
如果是水平滚动,onInterceptTouchEvent
将返回true,以便将来的动作事件转到继承Gallery.onTouchEvent
进行实际滚动。
Gallery
自己的手势检测器(mGestureDetector
中的Gallery.java
)未获得所有动作事件,因此有时会报告导致画廊跳转的巨大突然滚动。我投入了一个令人讨厌的黑客,丢弃了那些。
代码:
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.Gallery;
public class BetterGallery extends Gallery {
/* This gets set when we detect horizontal scrolling */
private boolean scrollingHorizontally = false;
/* This gets set during vertical scrolling. We use this to avoid detecting
* horizontal scrolling when vertical scrolling is already in progress
* and vice versa. */
private boolean scrollingVertically = false;
/* Our own gesture detector, Gallery's mGestureDetector is private.
* We'll feed it with motion events from `onInterceptTouchEvent` method. */
private GestureDetector mBetterGestureDetector;
public BetterGallery(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mBetterGestureDetector = new GestureDetector(new BetterGestureListener());
}
public BetterGallery(Context context, AttributeSet attrs) {
super(context, attrs);
mBetterGestureDetector = new GestureDetector(new BetterGestureListener());
}
public BetterGallery(Context context) {
super(context);
mBetterGestureDetector = new GestureDetector(new BetterGestureListener());
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// Limit velocity so we don't fly over views
return super.onFling(e1, e2, 0, velocityY);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Documentation on this method's contract:
// http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)
// Reset our scrolling flags if ACTION_UP or ACTION_CANCEL
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
scrollingHorizontally = false;
scrollingVertically = false;
}
// Feed our gesture detector
mBetterGestureDetector.onTouchEvent(ev);
// Intercept motion events if horizontal scrolling is detected
return scrollingHorizontally;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// Hack: eat jerky scrolls caused by stale state in mGestureDetector
// which we cannot directly access
if (Math.abs(distanceX) > 100) return false;
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Reset our scrolling flags if ACTION_UP or ACTION_CANCEL
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
scrollingHorizontally = false;
scrollingVertically = false;
}
super.onTouchEvent(event);
return scrollingHorizontally;
}
private class BetterGestureListener implements GestureDetector.OnGestureListener {
@Override
public boolean onDown(MotionEvent arg0) {
return false;
}
@Override
public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
return false;
}
@Override
public void onLongPress(MotionEvent arg0) {
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (scrollingHorizontally || scrollingVertically) {
// We already know we're scrolling, ignore this callback.
// This avoids changing scrollingHorizontally / scrollingVertically
// flags mid-scroll.
return false;
}
scrollingHorizontally |= Math.abs(distanceX) > Math.abs(distanceY);
// It's a scroll, and if it's not horizontal, then it has to be vertical
scrollingVertically = !scrollingHorizontally;
return false;
}
@Override
public void onShowPress(MotionEvent arg0) {
}
@Override
public boolean onSingleTapUp(MotionEvent arg0) {
return false;
}
}
}
警告:类名中的“更好”一词可能会产生误导!
<强>更新强>
忘记提及,我还设置了将onTouchEvent
转发到相册的活动:
@Override
public boolean onTouchEvent(MotionEvent event) {
return mGallery.onTouchEvent(event);
}
更新2:
我对此代码进行了一些改进,并将其放在bitbucket上。 还有一个示例应用程序。它演示了此窗口小部件与ListView一起出现问题: - /
更新3:
从Gallery
切换到HorizontalScrollView
作为我的自定义小部件的基类。 More on this here。随着孩子的工作,Flings工作,ListViews和ExpandableListViews,在Android 1.6,2.2,2.3.4上测试。它的行为现在非常接近谷歌应用程序,IMO。
更新4:
Google发布了ViewPager!
答案 1 :(得分:4)
这让我很头疼,我想我会根据chrisschell的回答发布一个解决方案,因为它对我有很多帮助。
这是新的和改进的画廊。
public class FriendlyGallery extends Gallery {
FriendlyScrollView currScrollView;
public FriendlyGallery(Context context) {
super(context);
}
public FriendlyGallery(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
currScrollView = getCurrScrollView();
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if(currScrollView != null)
currScrollView.scrollBy(0, (int) distanceY);
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if(currScrollView != null)
currScrollView.fling(-(int) distanceY);
return super.onFling(e1, e2, distanceX, distanceY);
}
private FriendlyScrollView getCurrScrollView() {
//I have a load more button that shouldn't be cast to a scrollview
int pos = getFirstVisiblePosition();
if(pos != getAdapter().getCount()-1)
return (FriendlyScrollView)this.getSelectedView();
else
return null;
}
}
滚动视图。
public class FriendlyScrollView extends ScrollView {
public FriendlyScrollView(Context context) {
super(context);
}
public FriendlyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
希望这有助于并再次感谢chrisschell指出我正确的方向。
答案 2 :(得分:1)
我写了这个自定义类来处理这个问题,到目前为止它似乎是一个不错的解决方案。它并不完美,我没有用叉子来调用它,但希望这对某人有用: - )
import android.content.Context;
import android.view.MotionEvent;
import android.widget.Gallery;
import android.widget.ScrollView;
public class GalleryFriendlyScrollView extends ScrollView{
private static int NONE = -1;
private static int DOSCROLLVIEW = 0;
private static int DOGALLERY = 1;
private float lastx = 0;
private float lasty = 0;
private float firstx = 0;
private float firsty = 0;
private float lastRawx = 0;
private float lastRawy = 0;
private int gestureRecipient = NONE;
private boolean donewithclick = true;
private Gallery parent = null;
private boolean firstclick = true;
public GalleryFriendlyScrollView(Context context) {
super(context);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean retthis = true;
//Indicating a fresh click
if(donewithclick){
firstx = ev.getX();
firsty = ev.getY();
lastx = firstx;
lasty = firsty;
lastRawx = ev.getRawX();
lastRawy = ev.getRawY();
donewithclick = false;
firstclick = true;
}
//We don't know where these gesture events are supposed to go to.
//We have movement on the x and/or why axes, so we can determine where they should go now.
if((gestureRecipient == NONE) && (lastx != ev.getX() || lasty != ev.getY())){
//Determine whether there's more movement vertically or horizontally
float xdiff = ev.getX() - lastx;
float ydiff = ev.getY() - lasty;
if(xdiff < 0)xdiff = xdiff * -1;
if(ydiff < 0)ydiff = ydiff * -1;
if((xdiff) > (ydiff)){
gestureRecipient = DOGALLERY;
} else {
gestureRecipient = DOSCROLLVIEW;
}
}
if(gestureRecipient == DOGALLERY){
if(!firstclick){
//After you drag the screen left or right a bit, the baseline for where it calculates
//x and y from changes, so we need to adjust the offset to our original baseline
float offsetx = (((ev.getX() - lastx) - (ev.getRawX() - lastRawx)) * -1);
float offsety = (((ev.getY() - lasty) - (ev.getRawY() - lastRawy)) * -1);
ev.offsetLocation(offsetx, offsety);
}
retthis = getGallery().onTouchEvent(ev);
firstclick = false;
} else if(gestureRecipient == DOSCROLLVIEW){
retthis = super.onTouchEvent(ev);
}
if(ev.getAction() == MotionEvent.ACTION_UP){
//User's finger has been lifted
if(((firstx == ev.getX()) && (firsty == ev.getY()))){
//Since there isn't any movement in either direction, it's a click
getGallery().onSingleTapUp(ev);
super.onTouchEvent(ev);
}
donewithclick = true;
gestureRecipient = NONE;
}
//And record our coordinate data
lastx = ev.getX();
lasty = ev.getY();
lastRawx = ev.getRawX();
lastRawy = ev.getRawY();
return retthis;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
getGallery().onTouchEvent(ev);
return super.onInterceptTouchEvent(ev);
}
private Gallery getGallery(){
//Gets the gallery, in this case assuming the ScrollView is the direct child in the gallery.
//Adjust as needed
if(parent == null){
parent = (Gallery) this.getParent();
}
return parent;
}
}
我很想听听人们对此的体验,以及您的任何建议。
答案 3 :(得分:1)
我发现画廊滚动 - atraudes解决方案的行为不是最优,所以我寻找其他解决方案。最后我找到了一个适合我的方法:
答案 4 :(得分:0)
此解决方案如何:只需处理ScrollView
和Gallery
中的事件:
public class GalleryFriendlyScrollView extends ScrollView {
private Gallery fParent;
public GalleryFriendlyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean superDone = super.onTouchEvent(ev);
// correct the location of the event because the gallery
// might have moved the scroll pane horizontally
ev.setLocation(ev.getX() + getLeft(), ev.getY());
// dispatch the event also to the gallery
boolean galleryDone = getGallery().onTouchEvent(ev);
return superDone || galleryDone;
}
private Gallery getGallery() {
if (fParent == null) {
fParent = (Gallery)this.getParent();
}
return fParent;
}
}