zoukankan      html  css  js  c++  java
  • NestedScrollView嵌套ListView可行性总结

    由于公司项目遗留代码仍然使用PullToRefreshListView(后文简称PTRLV),且存在复用,更换RecyclerView成本太大,同时又想使用CoordinatorLayout来实现一些嵌套滑动效果,所以研究了NestedScrollView嵌套PTRLV的方案。

    对于NestedScrollView嵌套普通的ListView,常见的问题有:

    1. 嵌套后ListView只显示一行。
    2. ListView无法滑动。

    网上的解决方案主要是2种:

    1. 重写ListView的onMeasure方法。

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
         super.onMeasure(widthMeasureSpec, expandSpec);
     }

    将ListView所有子View的高度以及分隔线的高度相加,重新设置给ListView。

    private void setListViewHeightBasedOnChildren(ListView listView) {
         ListAdapter listAdapter = listView.getAdapter(); //获得Adapter
         if (listAdapter == null) { 
             return;
         }
         int totalHeight = 0;
         for (int i = 0; i < listAdapter.getCount(); i++) {
             View listItem = listAdapter.getView(i, null, listView);
             listItem.measure(0, 0);
             totalHeight += listItem.getMeasuredHeight();
         }
         ViewGroup.LayoutParams params = listView.getLayoutParams();
         params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
         listView.setLayoutParams(params);
     }
    1. 这种方法要求子View的根布局必须是LinearLayout。

    这2种方法乍看之下也都能解决NestedScrollView嵌套PTRLV的问题,但其实局限性很大,项目中实践后发现场景稍有变化就仍然有问题。

    主要有:

    1. 有些场景下展示的数据并不会在Activity的onCreate方法中就获取,例如搜索页需要用户输入关键字后才去获取数据,然后才将数据给到PTRLV展示,这时PTRLV只显示半行。
    2. 在PTRLV中改写添加的预加载功能失效。

    第1个问题出现的原因在于,上述2种解决方案都是改变PTRLV中真正的ListView的大小,让它能够将全部的子View展现出来。在这个场景中,界面初始化时,ListView中是没有数据的,所以在measure时ListView本身大小为0,只测量出了PTRLV的那个滑动指示块的高度,所以PTRLV只有那么高。

    那为什么获取到数据后再notify还是只有这么高呢?因为PTRLV在真正的ListView之外还有两层FrameLayout。虽然ListView因为重写了onMeasure方法已经展示全了,但这两层FrameLayout还是只有滑块那么高,所以PTRLV看起来还是没有改变。

    解决方案为:
    重写PTRLV内部的FrameLayout的onMeasure方法,跟ListView一样就行

    public class NoScrollFrameLayout extends FrameLayout {
    
        public NoScrollFrameLayout(Context context) {
            super(context);
        }
    
        public NoScrollFrameLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
        }
    }

    在PTRLV的父类PullToRefreshBase中改写addRefreshableView方法

    private void addRefreshableView(Context context, T refreshableView) {
        mRefreshableViewWrapper = new NoScrollFrameLayout(context);
        mRefreshableViewWrapper.addView(refreshableView, ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT);
    
        addViewInternal(mRefreshableViewWrapper, new LayoutParams(LayoutParams.MATCH_PARENT,
            LayoutParams.MATCH_PARENT));
    }

    第2个问题出现的原因是因为滑动事件的冲突。这里可能会有疑问,为什么还说滑动事件有冲突,通过上述方案不是已经可以正常滑动了吗?那是因为那只是看上去像ListView的滑动,其实是NestedSrcollView在滑动,ListView根本没有动。

    从第1个问题的原因中我们已经知道了,解决NestedScrollView中嵌套ListView显示不全问题的方法,其本质都是手动修改ListView的内容的高度,而且不是将高度match_parent,而是将ListView的子View全部加载进来,假如有50个数据,就一次性加载50个子View,所以ListView就相当于一个有50个子View的LinearLayout。(当然也就不会有复用,性能很差)

    另一方面,NestedSrcollView在onInterceptTouchEvent中拦截了所有的Touch事件,ListView没有分发到任何事件,所以虽然看上去像ListView在滑动,实际上是ScrollView包裹着相当于LinearLayout的ListView在滑动,而由于我们项目的预加载逻辑写在ListView的onScrollStateChange方法中,没有分到任何Touch事件的情况下根本不会调用onScrollStateChange,因此预加载就失效了。

    这个问题在不改动预加载逻辑实现位置的情况下是无解的。同时也可以得出:如果你在ListView的Touch事件中有自定义的逻辑,那么请不要用NestedSrcollView嵌套ListView,因为NestedSrcollView会拦截Touch事件,所有的滑动解决方案并没有真正解决滑动冲突,ListView是获取不到Touch事件的。

  • 相关阅读:
    定时器准确定时八位时钟
    数码管八位显示时钟
    串口通讯
    定时器/计数器0(定时器)
    定时器/计数器0之计数器
    定时器/计数器0(计数器)
    外部中断0
    LCD带字符液晶显示I LOVE YOU
    Altium Designer Summer 09换成中文步骤
    BAT小米奇虎美团迅雷携程等等各大企业校招,笔试面试题。
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/7371735.html
Copyright © 2011-2022 走看看