AndroidUI系列--在DecorView层解决RecyclerView和ScrollView的滑动冲突

来源:互联网 发布:淘宝技术这十年 编辑:程序博客网 时间:2024/06/13 21:30

滑动冲突,这个是作安卓的必经之坑。最开始的ListView和ScollView冲突,或者ListView嵌套ListView滑动冲突,再或者ListView和ViewPager的滑动冲突,再或者是GraidView等可滑动控件互相嵌套的冲突。解决方案呢,有很多。比如在onTouchEvent中拦截事件。又或者自定义ListView,修改onMesure测量,使它在测量时获得最大的宽高,这样可以让它不滑动。全部展示,当然作为在android摸爬滚打了这么久的程序猿,这些坑都应该踩过了,而且网上一大堆解决方案,不得不说,这就是开源的好处啊,想着谷歌巴巴把kotlin扶上位了,我们这些苦逼的程序猿,那就只有跟着大部队走了。没办法呀~夹缝里生存。

这里写图片描述

View的绘制流程,Activity–phonewindow–decorview–contentview,如下图

这里写图片描述

我们平时在Activity的setContentView就是在ContentViews作文章。那么我们的冲突就是在这里,在ContentView里设置了一个activity_main.xml,为什么会有滑动冲突呢,那是因为recyclerview和scollview都设置在了activity_main.xml。那么换个角度,如果把recyclerview加在contentviews和activity_main.xml布局平级。那么是不是就不存在滑动冲突了呢,想到就来试试。
首先自定义一个view,用来弹窗。

package com.example.administrator.bounceview;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.View;/** * Created by ShuWen on 2017/5/23. */public class BounceView extends View {    private int mArcMaxHeight;//弹窗最高距离    private int mArcHeight;//记录变换过程的距离    private Paint mPaint;//画笔    private Path mPath = new Path();//绘制动画弧度    private BounceAnimatorListener animatorListener;//动画开始的监听回调    private Status status = Status.NONE;//记录动画的状态    public enum Status{        //没动,上升,下降        NONE,STATUS_UP,STATUS_DOWN    }    public BounceView(Context context) {        super(context);        init();    }    public BounceView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public BounceView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    //初始化    private void init() {        mPaint = new Paint();        mPaint.setColor(Color.WHITE);        mPaint.setAntiAlias(true);        mPaint.setStyle(Paint.Style.FILL);        mArcMaxHeight = getResources().getDimensionPixelOffset(R.dimen.m_maxarcheight);    }    //上升的动画    public void show(){        status = Status.STATUS_UP;        if (animatorListener != null){            this.postDelayed(new Runnable() {                @Override                public void run() {                   animatorListener.showContent();                }            },600);        }        ValueAnimator animator = ValueAnimator.ofInt(0,mArcMaxHeight);        animator.setDuration(700);        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mArcHeight = (int) valueAnimator.getAnimatedValue();                if (mArcHeight == mArcMaxHeight){                    bounce();                }                invalidate();            }        });        animator.start();    }    //下降的动画    private void bounce() {        status = Status.STATUS_DOWN;        ValueAnimator valueAnimator = ValueAnimator.ofInt(mArcMaxHeight,0);        valueAnimator.setDuration(600);        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mArcHeight = (int) valueAnimator.getAnimatedValue();                invalidate();            }        });        valueAnimator.start();    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        int currentY = 0;        switch (status){            case NONE:                currentY = 0;                break;            case STATUS_UP:                currentY = (int) (getHeight()*(1 - (float)(mArcHeight/mArcMaxHeight))+mArcMaxHeight);                break;            case STATUS_DOWN:                currentY = mArcMaxHeight;                break;        }        mPath.reset();        mPath.moveTo(0,currentY);        mPath.quadTo(getWidth()/2,currentY - mArcHeight,getWidth(),currentY);        mPath.lineTo(getWidth(),getHeight());        mPath.lineTo(0,getHeight());        mPath.close();        canvas.drawPath(mPath,mPaint);    }    public void setAnimatorListener(BounceAnimatorListener animatorListener){        this.animatorListener = animatorListener;    }    public interface BounceAnimatorListener{        void showContent();    }}

上升过程中,绘制动画,使用ValueAnimator在回调里进行更新界面,调用invalidate()。中间使用到了二阶贝塞尔曲线,关于贝塞尔其实很简单的,在网上一搜,当然就有了。

那么在创建一个类,用来加载BounceVeiw。

package com.example.administrator.bounceview;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.view.ViewParent;import android.widget.FrameLayout;/** * Created by ShuWen on 2017/5/23. */public class BounceMenu {    private RecyclerView recyclerView;    private BounceView bounceView;    private ViewGroup parentVG;    private View rootView;    private BounceMenu(View view, int resId, final MyAdapter myAdapter) {        parentVG = findParentVG(view);        rootView = LayoutInflater.from(view.getContext()).inflate(resId,null,false);        recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);        bounceView = (BounceView) rootView.findViewById(R.id.bounceview);        recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));        bounceView.setAnimatorListener(new BounceView.BounceAnimatorListener() {            @Override            public void showContent() {                recyclerView.setVisibility(View.VISIBLE);                recyclerView.setAdapter(myAdapter);                recyclerView.scheduleLayoutAnimation();            }        });    }    public static BounceMenu makeBounce(View view, int resId, final MyAdapter myAdapter){        return new BounceMenu(view, resId, myAdapter);    }    public void show(){        if (rootView != null){            parentVG.removeView(rootView);        }        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);        parentVG.addView(rootView,layoutParams);        bounceView.show();    }    private ViewGroup findParentVG(View view) {        do {            if (view instanceof FrameLayout){                //找到decorview的根布局                if (view.getId() == android.R.id.content){                    return (ViewGroup) view;                }            }            if (view != null){                ViewParent viewParent = view.getParent();                view = viewParent instanceof View? (View) viewParent :null;            }        }while (view!= null);        return null;    }}

在这里面,传入需要添加recylerview的跟布局,通过这个根布局获得decorview的contentviews这个布局,然后在这个布局上添加recyclerview。这样就是与activity_main同级,不会有滑动冲突。在bounceview的监听里,添加recyclerview的动画。
那么再来看看MianAcicity的代码:

package com.example.administrator.bounceview;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.RecyclerView;import android.view.View;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private MyAdapter myAdapter;    private List<String> stringList;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        stringList = new ArrayList<>();        for (int i = 0; i < 20; i++) {            stringList.add("阿西吧"+i);        }        myAdapter = new MyAdapter(this,stringList) {            @Override            protected int ItemLayoutId() {                return R.layout.item;            }            @Override            protected void onBindHolder(MyViewHolder myViewHolder, int position) {                TextView textView = myViewHolder.getTextView(R.id.text);                textView.setText(stringList.get(position));            }        };    }    public void click(View view){        BounceMenu bounceMenu = BounceMenu.makeBounce(findViewById(R.id.activity_main),R.layout.bounce_view_layout,myAdapter);        bounceMenu.show();    }}

调用就是相当的简单了。同时还有炫酷的动画,何乐而不为呢。接下来所有代码都贴出来。
activity_main的xml布局。使用ScrollView,展示主要数据。在每一项的点击事件,触发弹窗,recyclerview。

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.administrator.bounceview.MainActivity">    <ScrollView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:background="#465">        <LinearLayout            android:orientation="vertical"            android:layout_width="match_parent"            android:layout_height="match_parent">            <TextView                android:layout_width="match_parent"                android:layout_height="100dp"                android:gravity="center"                android:textSize="20sp"                android:onClick="click"                android:text="你滑动啊"                />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View>            <TextView                android:layout_width="match_parent"                android:layout_height="100dp"                android:gravity="center"                android:textSize="20sp"                android:onClick="click"                android:text="你滑动啊"                />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View>            <TextView                android:layout_width="match_parent"                android:layout_height="100dp"                android:gravity="center"                android:onClick="click"                android:textSize="20sp"                android:text="你滑动啊"                />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View>            <TextView                android:layout_width="match_parent"                android:layout_height="100dp"                android:gravity="center"                android:textSize="20sp"                android:onClick="click"                android:text="你滑动啊"                />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View>            <TextView                android:layout_width="match_parent"                android:layout_height="100dp"                android:gravity="center"                android:textSize="20sp"                android:onClick="click"                android:text="你滑动啊"                />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View>            <TextView                android:layout_width="match_parent"                android:layout_height="100dp"                android:gravity="center"                android:textSize="20sp"                android:onClick="click"                android:text="你滑动啊"                />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View>            <TextView                android:layout_width="match_parent"                android:layout_height="100dp"                android:gravity="center"                android:textSize="20sp"                android:onClick="click"                android:text="你滑动啊"                />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View> <TextView            android:layout_width="match_parent"            android:layout_height="100dp"            android:gravity="center"            android:textSize="20sp"            android:onClick="click"            android:text="你滑动啊"            />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View>            <TextView                android:layout_width="match_parent"                android:layout_height="100dp"                android:gravity="center"                android:textSize="20sp"                android:onClick="click"                android:text="你滑动啊"                />            <View                android:layout_width="match_parent"                android:layout_height="1dp"                android:background="#fff">            </View>        </LinearLayout>    </ScrollView></RelativeLayout>

那么再看看弹窗的xml。

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="290dp"        android:layout_gravity="bottom">        <com.example.administrator.bounceview.BounceView            android:id="@+id/bounceview"            android:layout_width="match_parent"            android:layout_height="wrap_content"            />        <android.support.v7.widget.RecyclerView            android:id="@+id/recyclerview"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:clipChildren="false"            android:overScrollMode="never"            android:layout_alignParentBottom="true"            android:layoutAnimation="@anim/bounce_layout"            android:layout_marginTop="70dp">        </android.support.v7.widget.RecyclerView>    </RelativeLayout></FrameLayout>

这里就是弹窗了,recyclerview和scrollview同级,不会产生滑动冲突。
这个是解决滑动冲突的一个可行方案,相当不错。如果觉得动画不必要,直接去掉动画,只需要BounceMenu中的一些逻辑就ok了。我会把代码放在git上,有兴趣的朋友可以自己研究研究。

git地址:https://github.com/SingleShu/BounceView

效果图:

阅读全文
1 0
原创粉丝点击