Android中不同方向嵌套滑动的解决方式(ListView为样例)

Posted llguanli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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为样例)的主要内容,如果未能解决你的问题,请参考以下文章

滑动冲突常见情形及解决方案

优雅的嵌套滑动解决方式-NestedScroll

关于Android滑动冲突的解决方法

Android 解决viewPager中嵌套webView的滑动冲突

Android滑动事件冲突(上)

Android嵌套滑动控件的冲突解决和ViewPager适配当前子控件高度不留空白的办法