Android 截取包含可滚动小部件(scrollview、listview等)的片段上的手势
我遇到了一个关于手势识别的应用程序的一部分问题,这个应用程序已经让我发疯好几个小时了 我有一个包含两个框架布局的活动,它们占据了整个屏幕,一个包含应用程序的主要内容,另一个包含聊天日志。一次只有一个设置为可见。我的想法是,我可以在主内容片段上向右滑动,聊天片段将在facebook风格的界面中可见。同样,在聊天片段上向左滑动将使主要内容片段回到视图中 滑动侦听器的代码:Android 截取包含可滚动小部件(scrollview、listview等)的片段上的手势,android,android-fragments,android-listview,fragment,gesture,Android,Android Fragments,Android Listview,Fragment,Gesture,我遇到了一个关于手势识别的应用程序的一部分问题,这个应用程序已经让我发疯好几个小时了 我有一个包含两个框架布局的活动,它们占据了整个屏幕,一个包含应用程序的主要内容,另一个包含聊天日志。一次只有一个设置为可见。我的想法是,我可以在主内容片段上向右滑动,聊天片段将在facebook风格的界面中可见。同样,在聊天片段上向左滑动将使主要内容片段回到视图中 滑动侦听器的代码: private void attachFragmentSwipeListeners() { int scree
private void attachFragmentSwipeListeners() {
int screenOrientation = getResources().getConfiguration().orientation;
if (screenOrientation == Configuration.ORIENTATION_PORTRAIT) {
FrameLayout mainContent = (FrameLayout) findViewById(R.id.flMainContent);
mainContent.setOnTouchListener(new OnSwipeTouchListener(ExICS_Main.this) {
@Override
public void onSwipeLeft() {
showChatLog();
super.onSwipeLeft();
}
});
FrameLayout chatWindow = (FrameLayout) findViewById(R.id.flChatWindow);
chatWindow.setOnTouchListener(new OnSwipeTouchListener(ExICS_Main.this) {
@Override
public void onSwipeRight() {
hideChatLog();
super.onSwipeRight();
}
});
}
}
听众本身:
public class OnSwipeTouchListener implements View.OnTouchListener {
private static final String TAG = OnSwipeTouchListener.class.getName();
private final GestureDetector gestureDetector;
public OnSwipeTouchListener(Context context) {
gestureDetector = new GestureDetector(context, new GestureListener());
}
public void onSwipeLeft() {
}
public void onSwipeRight() {
}
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_DISTANCE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.i(TAG, "OnSwipeTouchListener Fling");
float distanceX = e2.getX() - e1.getX();
float distanceY = e2.getY() - e1.getY();
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (distanceX > 0) onSwipeRight();
else onSwipeLeft();
return true;
}
return false;
}
}
}
这已经过测试,可以正常工作,这在使用两个只包含文本视图等的占位符片段时非常好。我遇到的问题是当片段包含交互式小部件时,例如滚动视图或列表视图
在这些情况下,小部件的使用应确保它们只能以垂直方式滚动,然而,它们正在使用fling交互,甚至是水平交互,因此在应用程序中侦听片段级别的手势检测器永远不会被触发
我想做的是,当在片段上进行滑动时,让片段上的gesturedetector决定水平滑动是否足以触发主要内容和聊天之间的切换,反之亦然,如果是,则在活动级别调用show/hideChatLog()。如果没有,则将TouchEvent传播回片段的子视图,不管它是什么,以便它可以按照自己的意愿执行
或者,覆盖小部件本身上的手势检测,如果片段手势侦听器正在侦听的水平滑动,则将其推回到视图层次结构上,而不是使用它,以便片段手势检测器可以看到并对其进行操作
我尝试过创建一个新的手势监听器,它实现了onFling方法,如下所示,并将其附加到ListView或ScrollView等
public class IgnoreHorizontalSwipeInterceptor implements View.OnTouchListener {
private static final String TAG = IgnoreHorizontalSwipeInterceptor.class.getName();
private final GestureDetector gestureDetector;
public IgnoreHorizontalSwipeInterceptor(Context context) {
gestureDetector = new GestureDetector(context, new GestureListener());
}
public void onSwipeLeft() {
}
public void onSwipeRight() {
}
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_DISTANCE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.i(TAG, "OnSwipeTouchListener Fling");
float distanceX = e2.getX() - e1.getX();
float distanceY = e2.getY() - e1.getY();
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
return false;
}
return false;
}
}
}
然后像这样附加它:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.fragment_room_list, container, false);
ListView roomListView = (ListView) fragmentView.findViewById(R.id.lvRoomList);
...
RoomListFragmentListAdapter adapter = new RoomListFragmentListAdapter(getActivity(), R.layout.room_list_item_layout, rooms);
roomListView.setAdapter(adapter);
roomListView.setOnTouchListener(new IgnoreHorizontalSwipeInterceptor(getActivity().getBaseContext()));
return fragmentView;
}
我曾希望,通过在连接到listView的IgnoreHorizontalSwipeInterceptor中始终在onFling()中返回false,它将导致事件带回父级,即片段本身,但这不会发生
当我在listview上滑动时,我可以在日志中看到来自IgnoreHorizontalSwipeInterceptor onFling()的OnSwipeTouchListener Fling。如果我对一个不包含可滚动小部件的片段执行相同的操作,我可以像我希望的那样看到OnSwipeTouchListener Fling
我已经找到了答案,但这对我来说真的没有多大意义。。。它提到了重写InterceptTouchEvent,但我不知道在何处这样做或如何做才有意义。如果这确实是正确的方法,我们将非常感谢在何处以及如何做到这一点
另一种(可能更容易)的方法是实现一个接口,这样当片段小部件检测到自身发生水平滑动时,它会使用对父活动的回调来触发我期望的操作show/hideChatLog()。然而,这似乎是一种相当肮脏的做法,如果可能的话,我们宁愿避免
展示我对布局的理解
使用内容视图中的此片段,向右滑动以显示聊天日志,效果与预期一致:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_bright"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="XXXX.Activities.ExICS_Main$PlaceholderFragment">
<TextView
android:id="@+id/section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is a fragment" />
</RelativeLayout>
但这一个没有,因为listview正在使用触摸事件,并且它没有被传递给片段onTouchEvent GestureListener:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="XXX.Fragments.Room_List_Fragment">
<ListView
android:id="@+id/lvRoomList"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>