我在处理有关手势识别的应用程序的一部分时出现问题,这种应用程序已经让我疯了几个小时了...
我有一个活动,其中包含两个填充整个屏幕的FrameLayouts,一个包含应用程序的主要内容,另一个包含聊天记录。一次只能设置一个。我的想法是,我可以直接在主要内容片段上滑动,聊天片段将在Facebook样式界面中显示。再次,在聊天片段上向左滑动将使主要内容片段重新进入视图。
滑动侦听器的代码:
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;
}
}
}
这已经过测试,并且在使用两个仅包含textviews等的占位符片段时可以正常工作。我遇到的问题是片段包含交互式小部件,例如scrollview或listview。
在这些情况下,使用小部件使得它们只能以垂直方式滚动,但是,它们正在消耗拖曳交互,甚至是水平交互,这样在应用程序中的片段级别上监听的GestureDetectors永远不会被触发
我想要做的是在片段上进行滑动时,让片段上的gesturedetector决定它的水平滑动是否足以触发主内容和聊天之间的切换,反之亦然,如果是,则调用show / hideChatLog()在活动级别。如果没有,将TouchEvent传播回片段的子视图,无论它是什么,所以它可以按照自己的意愿进行。
或者,覆盖窗口小部件本身的gesturedetection,如果片段手势监听器正在侦听的水平滑动,则将其推回到视图层次结构而不消耗它,以便片段手势检测器将看到并因此对其进行操作
我尝试创建一个新的手势监听器,它按如下方式实现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,它会导致事件返回到父级,片段本身,但这不会发生。
当我在列表视图上滑动时,我可以在IgnoreHorizontalSwipeInterceptor onFling()的日志OnSwipeTouchListener Fling中看到。如果我对不包含可滚动小部件的片段执行相同操作,我可以同样看到OnSwipeTouchListener Fling,我希望如此。
我找到了Android documentation for gestures on viewgroups,但这对我来说真的没有多大意义......它提到了onInterceptTouchEvent,但我无法弄清楚这样做或者如何才有意义。如果这确实是正确的方法,那么在哪里以及如何做到这一点的某些方向将是值得赞赏的。
另一种(可能更容易)方法是实现一个接口,这样当片段小部件检测到自身的水平滑动时,它会使用对父活动的回调来触发我期望的动作,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>