Android中不同方向嵌套滑動的解決方式(ListView為樣例)
前言:
就像手機QQ的聊天消息列表。一個縱向滑動的ListView列舉全部消息,但每一條消息能夠橫向滑動。
而默認情況下,僅僅能有一個地方消化處理觸摸事件,要麽ListView吃掉這個事件。要麽子Item中能滑動的部件吃掉。兩者互相沖突。
是否認為非常分裂?實現起來事實上不復雜。
理解了以後,能夠方便延伸到GridView,ViewPager,ScrollView等等滑動控件。
假設對Andoroid觸摸事件傳遞過程不熟悉,請看這裏:
為了最簡單表達實現方法,我以一個LinearLayout為ListView的Item,裏面放了消息的TextView。和一個刪除button
重寫ListView中的Item,也就是LinearLayout的onTouchEvent方法。以監聽橫向滑動和縱向滑動:
1)縱向滑動時。無論
2)橫向滑動時。請求父容器,也即是ListView不要攔截觸摸事件,自己在子View(也就是LinearLayout)裏面處理就好了,當橫向觸摸時間結束(MotionEvent.Action_UP)或者劃出邊界(MotionEvent.Action_CANCEL)時,恢復同意父容器攔截觸摸事件。
先上效果圖
重寫的LinearLayout例如以下:
package ex.oyyj.listviewfulldemo; import android.animation.Animator; import android.animation.ObjectAnimator; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; import android.view.ViewParent; import android.widget.LinearLayout; /** * Created by oyyj on 2015/8/6. */ public class HorizontalSlideLayout extends LinearLayout { private static String TAG = "VerticalSlideLayout"; private int DRAG_X_THROD = 0; private int SCROLL_X = 0; private final int ANIM_DURATION = 300; private static final int SLIDE_TO_LEFT = -1; private static final int SLIDE_TO_RIGHT = 1; private int mSlideDirection = 0; public HorizontalSlideLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //推斷橫劃的閾值,為了兼容不同尺寸的設備。以dp為單位 DRAG_X_THROD = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, context.getResources().getDisplayMetrics()); SCROLL_X = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, context.getResources().getDisplayMetrics()); } public HorizontalSlideLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public HorizontalSlideLayout(Context context) { this(context, null, 0); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); } int downX, downY; boolean isNeedToGoBack; private ObjectAnimator mAnimator; @Override public boolean onTouchEvent(MotionEvent ev) { boolean isInterceptHere = false; try { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downX = (int) ev.getX(); downY = (int) ev.getY(); isInterceptHere = true; if (mAnimator != null) { mAnimator.cancel(); } break; case MotionEvent.ACTION_MOVE: int dx = (int) Math.abs(ev.getX() - downX); int dy = (int) Math.abs(ev.getY() - downY); if (dx > dy && dx > DRAG_X_THROD) { Log.i(TAG, "橫劃!攔截它"); setParentInterceptTouchEvent(true); isInterceptHere = true; mSlideDirection = (ev.getX() - downX) > 0 ? SLIDE_TO_RIGHT : SLIDE_TO_LEFT; if (mSlideDirection == SLIDE_TO_LEFT) { isNeedToGoBack = true; playAnimation(SCROLL_X, ANIM_DURATION); } else if (mSlideDirection == SLIDE_TO_RIGHT && isNeedToGoBack) { playAnimation(0, ANIM_DURATION); } } else if (dy > dx) { Log.i(TAG, "豎劃!
不攔截"); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: setParentInterceptTouchEvent(false); isInterceptHere = false; downX = 0; downY = 0; break; } } catch (Exception e) { e.printStackTrace(); } return isInterceptHere; } private void playAnimation(int translationX, int duration) { if (mAnimator != null) { mAnimator.cancel(); } mAnimator = ObjectAnimator.ofInt(this, "scrollX", translationX); mAnimator.setDuration(duration); mAnimator.start(); mAnimator.addListener(listener); } /* 這個函數非常重要,請求禁止父容器攔截觸摸事件 */ public void setParentInterceptTouchEvent(boolean disallow) { ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(disallow); } } Animator.AnimatorListener listener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { mAnimator = null; } @Override public void onAnimationCancel(Animator animator) { mAnimator = null; } @Override public void onAnimationRepeat(Animator animator) { } }; }
貼上具體調用代碼:
activity_main.xml
<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:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity" android:background="#ffffff"> <ListView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/listView" android:divider="@drawable/divider" android:listSelector="@drawable/selector_list_background" android:layout_centerInParent="true" /> </RelativeLayout>
MainActivity.java
package ex.oyyj.listviewfulldemo; import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends Activity { private ListView mlistView; String[] films = new String[]{ "煎餅俠", "獵妖記", "大聖歸來", "道士下山", "王朝的女人·楊貴妃", "梔子花開", "太平輪(下) ",}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mlistView = (ListView) findViewById(R.id.listView); final TitleAdapter titleAdapter = new TitleAdapter(LayoutInflater.from(this), films); mlistView.setAdapter(titleAdapter); } private class TitleAdapter extends BaseAdapter { String[] itemNames; LayoutInflater inflater; public TitleAdapter(LayoutInflater _inflater, String[] names) { inflater = _inflater; itemNames = names.clone(); } @Override public Object getItem(int i) { return itemNames[i]; } @Override public long getItemId(int i) { return i; } @Override public int getCount() { return itemNames.length; } @Override public View getView(int i, View view, ViewGroup viewGroup) { try { ViewHolder holder = new ViewHolder(); if (view == null ) { view = inflater.inflate(R.layout.item_layout, viewGroup, false); holder.title = (TextView) view.findViewById(R.id.item); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } if (holder != null && holder.title != null) { TextView tv = holder.title; tv.setText(itemNames[i]); } } catch (Exception e) { e.printStackTrace(); } return view; } private class ViewHolder { TextView title; } } }
轉自:http://blog.csdn.net/oyyj42/article/details/47333443
Android中不同方向嵌套滑動的解決方式(ListView為樣例)