博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AndroidUI系列--在DecorView层解决RecyclerView和ScrollView的滑动冲突
阅读量:4166 次
发布时间:2019-05-26

本文共 8934 字,大约阅读时间需要 29 分钟。

滑动冲突,这个是作安卓的必经之坑。最开始的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
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。

这里就是弹窗了,recyclerview和scrollview同级,不会产生滑动冲突。

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

git地址:

效果图:

你可能感兴趣的文章
flash as3输出并执行javascript
查看>>
flash 控制声音的播放和停止
查看>>
flash在android上的延迟可能的解决方案
查看>>
flash as3设置全屏
查看>>
八款开源Android游戏引擎
查看>>
Android MediaPlayer基本使用方式
查看>>
android 获取坐标
查看>>
彻底解决 Eclipse + Android 自动补全卡死的问题
查看>>
请记住: i AM SoLiD. (关于View的事件触发顺序)
查看>>
As3.0 删除容器所有子对象
查看>>
TweenLite参数说明
查看>>
flash TweenLite onComplete 提前执行的问题
查看>>
mysqldump
查看>>
字符串 与 java.sql.Timestamp转换博客分类: javaJavaSQL
查看>>
奇怪的ubuntu不能解析域名的问题
查看>>
cat | wc -l 少一行的问题
查看>>
socket 科普文章
查看>>
Mutex, semaphore, spinlock的深度解析
查看>>
pthread线程使用小结
查看>>
线程池 范例
查看>>