判断Listview滑到顶部的最精准方案,解决Listview设置EmptyView与SwipeRefreshLayout冲突

本文主要是介绍判断Listview滑到顶部的最精准方案,解决Listview设置EmptyView与SwipeRefreshLayout冲突,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

故事发生的背景

SwipeRefreshLayout是谷歌自家控件,提供下拉刷新的功能。然而这个控件简单易用的同时也有一个令人头疼的缺点,那就是它里面只能包含一个子View!有一天,需求来了,需要在为Listview添加EmptyView和下拉刷新,同时当显示EmptyView时也要求有下拉刷新。

尝试与探索

大家都知道,设置EmptyView需要把它放在一个容器内。这还不简单,SwipeRefreshLayout不就很好的容器吗?

    <android.support.v4.widget.SwipeRefreshLayoutandroid:id="@+id/refrashLayout"android:layout_width="match_parent"android:layout_height="match_parent"><ListViewandroid:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center_horizontal"android:clipToPadding="false"android:divider="#E1E6E9"android:dividerHeight="20dp"android:padding="20dp"android:scrollbars="none" /><ScrollViewandroid:id="@+id/emptyView"android:layout_width="match_parent"android:layout_height="match_parent"android:fillViewport="true"><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:text="Nothing here..." /></ScrollView></android.support.v4.widget.SwipeRefreshLayout>

故事就这样结束了吗?NO,这样写了后果是,当Listview没有数据显示的时候,EmptyView没有显示出来。

后来,我Google一下,在stackoverflow上看到很多解决方案,找到几种比较靠谱的方案,有一些说需要重写自定义Listview,个人觉得不够优雅就不列举出来了。

方案一:

把EmptyView放到SwipeRefreshLayout外面,这似乎是一个很完美的方案,同时也是最简单的,然而,这样的话EmptyView没有下拉刷新了。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#E1E6E9"android:orientation="vertical"><ScrollView
        android:id="@+id/emptyView"android:layout_width="match_parent"android:layout_height="match_parent"android:visibility="gone"android:fillViewport="true"><TextView
            android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:text="Nothing here..." /></ScrollView><android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refrashLayout"android:layout_width="match_parent"android:layout_height="match_parent"><ListView
            android:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center_horizontal"android:clipToPadding="false"android:divider="#E1E6E9"android:dividerHeight="20dp"android:padding="20dp"android:scrollbars="none" /></android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>

方案二

为了给EmptyView加下拉刷新,在把方案一中的EmptyView套上另一个SwipeRefreshLayout。这样的话就需要两份处理SwipeRefreshLayout的代码了,个人觉得比较麻烦,不够优雅,但也不失为一个完整的解决方案。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#E1E6E9"android:orientation="vertical"><android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/emptyView"android:layout_width=""android:layout_height=""><TextView
            android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:text="Nothing here..." /></android.support.v4.widget.SwipeRefreshLayout><android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refrashLayout"android:layout_width="match_parent"android:layout_height="match_parent"><ListView
            android:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center_horizontal"android:clipToPadding="false"android:divider="#E1E6E9"android:dividerHeight="20dp"android:padding="20dp"android:scrollbars="none" /></android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>

方案三

我一向不喜欢简单粗暴,推崇优雅的解决方法。既然你SwipeRefreshLayout只能包含一个子View,那么我用FrameLayout wrap them all together不就行了? 为什么用FrameLayout? 因为FrameLayout相比于其他几个开销更低。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#E1E6E9"android:orientation="vertical"><android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refrashLayout"android:layout_width="match_parent"android:layout_height="match_parent"><FrameLayout
            android:layout_width="match_parent"android:layout_height="match_parent"><ListView
                android:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent"android:padding="20dp"android:clipToPadding="false"android:layout_gravity="center_horizontal"android:scrollbars="none"android:divider="#E1E6E9"android:dividerHeight="20dp" /><ScrollView
                android:id="@+id/emptyView"android:layout_width="match_parent"android:layout_height="match_parent"android:fillViewport="true"><TextView
                    android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:text="Nothing here..." /></ScrollView></FrameLayout></android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>

很合理的方案,然而故事并没有这么轻易结束,运行效果如图。
这里写图片描述
LIstview没有到达顶部下拉就会刷新!显然这个bug是无法接受的。我们需要在listview没有到达顶部的时候禁用SwipeRefreshLayout,在listview到达顶部的时候恢复SwipeRefreshLayout,就可以解决这个问题了。显然我们需要判断listview是否到达顶部的方法。

判断Listview滑到顶部的方案

我们网上一搜索,方法都是前篇一律的方案一。

方案一:简洁而粗糙

listview.setEmptyView(emptyView);
//防止SwipeRefreshLayout过早触发
listview.setOnScrollListener(new AbsListView.OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {if (firstVisibleItem==0)refrashLayout.setEnabled(true);else refrashLayout.setEnabled(false);}
});

我天真地以为故事真的这样结束了,然而….
这里写图片描述
显然这是最简单的方案了,但却不能精确地判断是否真的滑到顶部!对于一些item高度比较大时,用这种方法判断似乎毫无意义。

终极方案

想到这个方法,我还是从Listview源码里面找到启发(源码真是最好的老师)。当Listview捕捉到ACTION_MOVE滑动动作时会调用trackMotionScroll方法,在里面有这样一段代码,判断子view是否移出屏幕。

if(down){  //手指向下滑动final int top = listPadding.top - incrementalDeltaY;for (int i = 0; i < childCount; i++) {final View child = getChildAt(i);if (child.getBottom() >= top) {break;} else {  //子View已经移出了屏幕count++;int position = firstPosition + i;if (position >= headerViewsCount && position < footerViewsStart) {mRecycler.addScrapView(child);}}}}else{final int bottom = getHeight() - listPadding.bottom - incrementalDeltaY;for (int i = childCount - 1; i >= 0; i--) {final View child = getChildAt(i);if (child.getTop() <= bottom) {break;} else {start = i;count++;int position = firstPosition + i;if (position >= headerViewsCount && position < footerViewsStart) {mRecycler.addScrapView(child);}}}}

那么给listview设置监听器,只要listview的第一个item的顶部y坐标值等于listview的顶部y坐标值表明listview到达了顶部
下面就show you the code!

        listview.setEmptyView(emptyView);listview.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_MOVE) {Log.d("Measure","listview.getListPaddingTop():"+listview.getListPaddingTop()+" listview.getTop():"+listview.getTop()+"listview.getChildAt(0).getTop():"+listview.getChildAt(0).getTop());if (listview.getFirstVisiblePosition() == 0 &&listview.getChildAt(0).getTop() >= listview.getListPaddingTop()) {refrashLayout.setEnabled(true);Log.d("TAG", "reach top!!!");}else refrashLayout.setEnabled(false);}return false;}});

这里写图片描述
EmptyView:
这里写图片描述

在情人节写代码,写博客,其实我是拒绝的~~不说了,完整代码已上传至Github,项目当中其他类不用管,相关代码在MainActivity中。

这篇关于判断Listview滑到顶部的最精准方案,解决Listview设置EmptyView与SwipeRefreshLayout冲突的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/838249

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

如何解决线上平台抽佣高 线下门店客流少的痛点!

目前,许多传统零售店铺正遭遇客源下降的难题。尽管广告推广能带来一定的客流,但其费用昂贵。鉴于此,众多零售商纷纷选择加入像美团、饿了么和抖音这样的大型在线平台,但这些平台的高佣金率导致了利润的大幅缩水。在这样的市场环境下,商家之间的合作网络逐渐成为一种有效的解决方案,通过资源和客户基础的共享,实现共同的利益增长。 以最近在上海兴起的一个跨行业合作平台为例,该平台融合了环保消费积分系统,在短

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

POJ1269 判断2条直线的位置关系

题目大意:给两个点能够确定一条直线,题目给出两条直线(由4个点确定),要求判断出这两条直线的关系:平行,同线,相交。如果相交还要求出交点坐标。 解题思路: 先判断两条直线p1p2, q1q2是否共线, 如果不是,再判断 直线 是否平行, 如果还不是, 则两直线相交。  判断共线:  p1p2q1 共线 且 p1p2q2 共线 ,共线用叉乘为 0  来判断,  判断 平行:  p1p

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip