ViewPager与CoordinatorLayout一起使用的一个Bug
来源:互联网 发布:域名未授权是什么意思 编辑:程序博客网 时间:2024/06/07 06:32
本文记录一个关于ViewPager
与CoordinatorLayout
一起使用的Bug,目前虽然有解决问题的方法,但是引起这个bug的原因依然没有找到。
最初的布局是正常的
项目最初的布局树是这样的:
CoordinatorLayout--RelativeLayout(height:match_parent)----各种View----FrameLayout(height:wrap_content)------layout------WrapHeightViewPager----ImageView
这里的布局要求是这样的: layout
与WrapHeightViewPager
同一时间最多只有其中一个会显示,也可能两个都不显示。当它们之间有任何一个显示的时候,ImageView
要在它们上面,当它们两个都不显示的时候,ImageView
要在底部。
所以我把这两个View放在了一个FrameLayout
中,并且让ImageView
在其之上来实现这个需求。
WrapHeightViewPager
继承自ViewPager
,以实现按内容自适应高度。它重写了以下方法:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); int h = child.getMeasuredHeight(); if (h > height) height = h; } heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
也就是在onMeasure
的时候去测量子View,以子View的最大高度作为ViewPager
的高度。
因交互需求而做的改动
由于接下来在本次版本迭代中,交互会做比较大的改动,layout
这一部分可能需要使用CoordinatorLayout
中的Behavior
来实现一些效果,ImageView
也会影响到。所以我将涉及到的这两块View都抽出到CoordinatorLayout
下,为后续的改动先做一个铺垫。改动后的布局树如下:
CoordinatorLayout--RelativeLayout(height:match_parent)--FrameLayout(heiht:wrap_content)----layout----WrapHeightViewPager--ImageView
也就是把FrameLayout
和ImageView
从RelativeLayout
中抽到外面来了。
然后为ImageView
写了一个Behavior
,使用Behavior
来控制ImageView
的UI逻辑。
然后发现,把布局抽出来后,一个Bug就来了。
Bug出现及原因追查
我发现WrapHeightViewPager
在第一次数据更新,有了数据之后,并没有被显示出来,但是第二次更新数据就出来了。
打印了FrameLayout
高度,为0。
我以为是调用setVisibility(int)
没起作用,打印了WrapHeightViewPager
的getVisibility()
,值为VISIBLE
。也就是生效了。
打印了WrapHeightViewPager
的高度,为0。
换成ViewPager
,能显示出来,但是高度已经是占满父布局了,看来应该是高度测量的问题。
继续换回来找原因。
打印里面的addView(View)
和onMeasure(int, int)
方法。发现前者有被调用,但后者只在界面初始化的时候被调用一次,这时没有数据所以计算出来的高度为0,在更新数据之后并没有跟着被调用,所以导致WrapHeightViewPager
的高度仍然为0。
尝试解决
然后尝试。
在adapter更新数据之后,调用viewpager的requestLayout()
,无效。
在addView(View)
之后调用requestLayout()
,无效。
改成forceLayout()
,无效。
在addView(View)
之后,调用requestApplyInsets()
,居然起作用了!!
但是—— requestApplyInsets()
需要在API 20之后才可以使用,我们应用的minSdkVersion
为15。
继续寻找其他方法。
google,没找到一样的问题。
stackoverflow,同样没找到一样的问题。但其中有一个问题的答案给了我灵感:https://stackoverflow.com/a/33253976/2673757
mHandler.postDelayed(new Runnable() { @Override public void run() { mViewPager.requestLayout(); } }, 100);
由于在第二次的时候,WrapHeightViewPager
会显示出来,也就是它的高度最终还是会测量的。但是在第一次一更新就去调用requestLayout()
并没有效,所以我尝试也加了个延迟:
mBillPager.postDelayed(new Runnable() { @Override public void run() { mBillPager.requestLayout(); }}, 100);
结果果然显示出来了。但是这样却还有两个问题:
1. 这里是写死的固定100毫秒的延迟,但是实际上我并不需要多少才够,100这个魔数让我看着并不舒服。
2. 有多个地方会更新adapter里的数据,WrapContentViewPager
本身的业务无关的界面逻辑却要在业务逻辑里处理,代码耦合度大。
于是今天早上继续探寻此问题。
继续求解
我打印了WrapHeightViewPager
的onLayout(boolean, int, int, int, int)
方法,发现它是有被调用的。然后我试着把requestLayout()
的调用放到它这里。直接调用还是不行,改为如下:
@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { post(new Runnable() { @Override public void run() { requestLayout(); } ); }}
同样问题解决。现在业务逻辑的代码与改动布局之前基本一样,也就是布局上的重构没有影响到业务代码,两者分离。Bug暂先这样处理,不给业务逻辑带来其他代码。
2017-10-26补充:
使用Behavior来解决
今天又发现可以使用CoordinatorLayout.Behavior
来解决这一问题。由于ViewPager更新时会回调到Behavior
的onLayoutChild(CoordinatorLayout parent, ViewPager child, int layoutDirection)
方法,所以可以在该方法重新对ViewPager
进行layout操作。
代码如下:
/* * Copyright (c) 2017. Xi'an iRain IOT Technology service CO., Ltd (ShenZhen). All Rights Reserved. */import android.content.Context;import android.support.design.widget.CoordinatorLayout;import android.support.v4.view.ViewPager;import android.util.AttributeSet;import android.view.View;import com.githang.android.snippet.view.WrapHeightViewPager;/** * @since 2017-10-25 4.2.3 */public class ViewPagerBehavior extends CoordinatorLayout.Behavior<ViewPager> { public ViewPagerBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onLayoutChild(CoordinatorLayout parent, ViewPager child, int layoutDirection) { parent.onLayoutChild(child, layoutDirection); if (child instanceof WrapHeightViewPager) { int height = 0; int measureHeight = parent.getHeight(); int measureWidth = parent.getWidth(); int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(measureWidth, View.MeasureSpec.AT_MOST); for (int i = 0; i < child.getChildCount(); i++) { View item = child.getChildAt(i); item.measure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(measureHeight, View.MeasureSpec.UNSPECIFIED)); int h = item.getMeasuredHeight(); if (h > height) { height = h; } } height += child.getPaddingTop() + child.getPaddingBottom(); child.layout(parent.getLeft(), child.getBottom() - height, child.getRight(), child.getBottom()); } return true; }}
- ViewPager与CoordinatorLayout一起使用的一个Bug
- CoordinatorLayout的一个简单使用
- ViewPager与support包里的Fragment家族一起使用
- FragmentTabhost和viewPager一起使用会出现的小bug及解决方案
- mx:TabNavigator 与s:TextInput一起使用的Bug
- ViewPager(2):ViewPager与Fragment一起使用
- viewpager和fragment的一起使用
- 关于ListView和ViewPager的一个Bug
- CoordinatorLayout与Behavior 的使用总结
- CoordinatorLayout与AppBarLayout嵌套使用的注意事项
- CoordinatorLayout与SnackBar的简单使用
- Frament+ViewPager一起使用
- CoordinatorLayout的一个例子
- ViewPager与Item滑动冲突的bug
- Flex 4.5下 mx:TabNavigator 与s:TextInput一起使用的Bug
- RHEL6.5上Oracle ACFS与Linux samba一起使用时遇到的bug
- CoordinatorLayout+ViewPager不能自动折叠的问题
- CoordinatorLayout、SwipeRefreshLayout的使用
- LoadRunner参数化
- 解决Linux安装kettle问题
- jenkins -- shell 部署脚本
- 第四周项目二 单链表算法库的建立
- CSS_绝对布局
- ViewPager与CoordinatorLayout一起使用的一个Bug
- VS2008报错Msbuildtoolspath is not specified for the ToolsVersion“14.0”
- 自动轮播(不用导入依赖)
- 页面样式,隐藏竖滚动条,修改下拉框placeholder样式,以及父元素清除浮动
- ormLite抛Can't find a no-arg constructor for class异常
- CentOS7下,在安装过mysql5.7后,安装cloudera-scm-server报错:Require:libmysqlclient.so.18(libmysqlclient_18)(64bit
- 每天学些redis命令---1
- Layer弹窗
- RMAN全库备份