AndroidUI系列--在DecorView层解决RecyclerView和ScrollView的滑动冲突
2017-05-23 11:06
656 查看
滑动冲突,这个是作安卓的必经之坑。最开始的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,用来弹窗。
上升过程中,绘制动画,使用ValueAnimator在回调里进行更新界面,调用invalidate()。中间使用到了二阶贝塞尔曲线,关于贝塞尔其实很简单的,在网上一搜,当然就有了。
那么在创建一个类,用来加载BounceVeiw。
在这里面,传入需要添加recylerview的跟布局,通过这个根布局获得decorview的contentviews这个布局,然后在这个布局上添加recyclerview。这样就是与activity_main同级,不会有滑动冲突。在bounceview的监听里,添加recyclerview的动画。
那么再来看看MianAcicity的代码:
调用就是相当的简单了。同时还有炫酷的动画,何乐而不为呢。接下来所有代码都贴出来。
activity_main的xml布局。使用ScrollView,展示主要数据。在每一项的点击事件,触发弹窗,recyclerview。
那么再看看弹窗的xml。
这里就是弹窗了,recyclerview和scrollview同级,不会产生滑动冲突。
这个是解决滑动冲突的一个可行方案,相当不错。如果觉得动画不必要,直接去掉动画,只需要BounceMenu中的一些逻辑就ok了。我会把代码放在git上,有兴趣的朋友可以自己研究研究。
git地址:https://github.com/SingleShu/BounceView
效果图:
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
效果图:
相关文章推荐
- android ScrollView 嵌套RecyclerView 解决滑动冲突
- 【Android 手势冲突】彻底解决RecyclerView与ScrollView滑动冲突问题,并实现RecyclerView悬停导航栏(附demo)
- Android在RecyclerView中嵌套ScrollView,解决两者间的滑动冲突
- Android控件-ScrollView 和WebView之见滑动冲突解决
- ScrollView、SwipeRefreshLayout、ListView、RecyclerView等控件解决滑动冲突
- Android 解决WebView和ScrollView滚轮滑动冲突
- 解决ScrollView+RecyclerView的滑动冲突问题
- Android滑动冲突解决方式(下拉刷新上拉加载更多,适配RecyclerView/ListView/ScrollView)
- Android中RecyclerView嵌套滑动冲突解决的代码片段
- Android 解决ScrollView嵌套RecyclerView导致滑动不流畅的问题
- NestedScrollview 嵌套 RecyclerView出现滑动冲突解决方法
- Android中Recyclerview使用10----Recyclerview外面嵌套ScrollView滑动事件冲突
- RecyclerView添加下拉加载以及和ScrollView的滑动冲突的解决
- Android ScrollView里嵌套RecyclerView时,在RecyclerView上滑动时出现卡顿(冲突)的现象
- android控件-ScrollView 和WebView之见滑动冲突解决
- 条目有限的RecyclerView 与ScrollView滑动冲突解决(仅限条目有限)
- Android Scrollview嵌套RecyclerView导致滑动卡顿问题解决
- ScrollView、SwipeRefreshLayout、ListView、RecyclerView等控件解决滑动冲突
- Android之NestedScrollView 嵌套 RecyclerView 滑动冲突的问题
- ViewPager 与SwipeRefreshLayout,RecyclerView,ScrollView滑动冲突解决方法