dm7*_*m78 2 android touch-event android-viewpager motionevent
我将自定义子视图放入 ViewPager。我在拦截触摸事件方面没有任何重要经验,所以这对我来说都是全新的。
我在 SO 和其他博客上查看了大量与此相关的问题,但到目前为止,没有任何建议的建议对我有太大帮助。
我的自定义视图有两个相互重叠的面板。我需要允许用户在不将这些触摸事件传递给父 ViewPager 的情况下滑动最前面的面板。
我很难理解这一点,因为似乎我的观点onTouchEvent总是被调用,尽管false在我观点的onInterceptTouchEvent. 我的理解在这里可能是错误的,但我的印象是返回false应该意味着我的观点onTouchEvent不应该被调用。
这是我的自定义视图的代码:
package com.example.dm78.viewpagertouchexample;
import android.content.Context;
import android.support.v4.view.GestureDetectorCompat;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MyCustomView extends FrameLayout {
public static final String TAG = MyCustomView.class.getSimpleName();
private GestureDetectorCompat mGestureDetector;
private PanelData mPanelData;
private LinearLayout mFrontPanel;
private float xAdditive;
private float mFrontPanelInitialX = Float.NaN;
private float mDownX = Float.NaN;
private float frontPanelXSwipeThreshold = 100; // arbitrary value
public MyCustomView(Context context, PanelData holder) {
super(context);
mPanelData = holder;
init();
}
public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mGestureDetector = new GestureDetectorCompat(getContext(), new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
});
View view = View.inflate(getContext(), R.layout.my_custom_view, this);
mFrontPanel = (LinearLayout) view.findViewById(R.id.front_panel);
FrameLayout mRearPanel = (FrameLayout) findViewById(R.id.rear_panel);
TextView mFrontTextView = (TextView) findViewById(R.id.front_textView);
TextView mRearTextView = (TextView) findViewById(R.id.rear_textView);
mFrontTextView.setText(mPanelData.frontText);
mRearTextView.setText(mPanelData.rearText);
if (mPanelData.showFrontPanel) {
mFrontPanel.setVisibility(View.VISIBLE);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mFrontPanelInitialX = mFrontPanel.getX();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev) || mGestureDetector.onTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mPanelData.showFrontPanel) {
getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float dX;
long duration;
boolean result = false;
int xDir = 0;
if (mDownX != Float.NaN) {
xDir = (int) Math.abs(event.getRawX() - mDownX);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// for later ACTION_MOVE events, we'll add xAdditive to event.getRawX() to get a dX for animations
xAdditive = mFrontPanel.getX() - event.getRawX();
mDownX = event.getRawX();
result = true;
break;
case MotionEvent.ACTION_MOVE:
// left movement detected
if (xDir < 0) {
// animate panel interactively with new events coming in
// assume we haven't passed the point of no return
dX = event.getRawX() + xAdditive;
duration = 0;
if ((mFrontPanel.getX() + dX) < frontPanelXSwipeThreshold) {
// go ahead and animate the panel away
dX = -mFrontPanel.getWidth();
duration = 200;
}
mFrontPanel.animate().x(dX).setDuration(duration).start();
result = true;
}
break;
case MotionEvent.ACTION_UP:
// test value here is arbitrary
if (xDir < -10) {
int newX;
// if panel has been moved left enough, just animate it away
if (mFrontPanel.getX() < frontPanelXSwipeThreshold) {
newX = -mFrontPanel.getWidth();
}
// otherwise, animate return to initial position
else {
newX = -getContext().getResources().getDisplayMetrics().widthPixels;
}
mFrontPanel.animate().x(newX).setDuration(200).start();
result = true;
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
default:
result = super.onTouchEvent(event) || mGestureDetector.onTouchEvent(event);
}
return result;
}
public static class PanelData {
public String rearText;
public String frontText;
public boolean showFrontPanel;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,没有任何事情发生在 ViewPager 中通常不会发生。我的子视图完全忽略了触摸输入。我确定我在这里做了一些愚蠢的事情,但我对 Android SDK 的这一部分没有足够的经验来知道它是什么。
I've tossed a GestureDetector.OnGestureListener in on the suggestion of some blog, but I haven't found much use for it and don't know how it might help me to begin with.
Can you find something obviously wrong with my code? Am I even on the right track?
Update 2015-09-01:
I've discovered that ViewParent#requestDisallowInterceptTouchEvent(boolean) method should probably be called on my view's parent at a couple of points. It seems like perhaps onInterceptTouchEvent might be a good place to set the flag and in onTouchEvent in the case for ACTION_UP might be a good place to cancel it. This is obviously not correct, since when I scroll to a page that has a front panel that the user needs to swipe away, the ViewPager stops scrolling entirely and there is no user-visible response from the app. How can I make this work?
Working example app repo: https://gitlab.com/dm78/ViewPagerTouchExample/tree/master
我的代码有很多问题。
Math.abs()在确定事件的运动方向时,我忘记删除调用。requestDisallowInterceptTouchEvent(boolean)在几个地方调用我的视图的父级来启用/禁用父级吞噬我的视图上生成的所有事件。requestDisallowInterceptTouchEvent(boolean)在我看来,通过使用,这允许孩子处理事件,而不是将视图与ViewPager显示它的视图紧密耦合,我觉得这是更可取的,因为这种技术可以应用于(据我想象)大约任何 UI 上下文中任何位置的任何视图以获得所需的行为。它不依赖于视图的其他元素了解正在发生的事情的亲密细节,以便解决方案正常运行。
解决上述问题后,只需调整动画的触发方式即可实现所需的交互。
我的解决方案仍然有一个小问题,但这是另一个 SO 问题。
| 归档时间: |
|
| 查看次数: |
5915 次 |
| 最近记录: |