Listview现在用的很少了,基本都是使用Recycleview,但是不得不说Listview具有划时代的意义,拓展性很强,我们可以自己添加下拉刷新,上拉加载更多功能。他和recycleview不同,他生来具有addHeaderView和addFooterView的功能,这也导致同样都是列表控件,实现上拉下拉的方式缺截然不同。

效果图

1、创建刷新控件

public class MyListview extends ListView {

    public MyListview(Context context) {
this(context,null);
} public MyListview(Context context, AttributeSet attrs) {
this(context,attrs,0);
} public MyListview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public void init(Context context) { }

1.1、创建头部View

头部样式,我写的是最简单的,根据业务需求来定,下拉的时候无非就是几种

  • 产品logo作为箭头转动的icon
  • 添加刷新时间
  • 加入其他具有特色的动效

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
tools:ignore="MissingDefaultResource">
<ProgressBar
android:layout_width="20dp"
android:layout_height="match_parent"
style="?android:attr/progressBarStyle"
android:layout_marginEnd="10dp"
android:indeterminateTint="#E8AD56"
/>
<TextView
android:id="@+id/header_text"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="我是头部"
android:gravity="center"
android:textColor="#E8AD56"/>
</LinearLayout>

样式写好后,我们需要添加到我们控件中,addHeaderView就起到作用了

public void init(Context context) {
//添加头部
viewHeader = View.inflate(context, R.layout.view_header, null);
viewHeader.measure(0, 0);
//让系统自动检测头部高度
heightHeader = viewHeader.getMeasuredHeight();
header_text = viewHeader.findViewById(R.id.header_text);
viewHeader.setPadding(0, -heightHeader, 0, 0);
this.addHeaderView(viewHeader);
}

一些控件我定义成全局的是因为下面会用到。setPadding可以设置显示位置,左上右下,上为负数就是隐藏在顶部。我们需要手指下拉去控制他缓慢显示,就用到了OnTouchListener,我们实现OnTouch方法做一些事件的分发处理。

1.2、下拉事件

public class MyListview extends ListView implements View.OnTouchListener{
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
public void init(Context context){
setOnTouchListener(this);
}
}

注意:别忘记了setOnTouchListener在init中添加

这时候我们需要对event的down,move,up事件进行逻辑处理,当手指按在屏幕时会触发事件,一个down事件,0~无数次move事件,一个up事件,这里面着重对move事件做处理,我们记录一下down事件的Y,因为是上下拉动,没必要计算X。然后diffY就是手指滑动的距离,我们需要处理一下这个值,因为值太大,而且值是整数,会让我们下拉的时候产生错乱,我们本意是让其从-100到0缓慢滑出(比如头部高度是100,从隐藏到显示就是-100到0),小伙伴都可以试试viewHeader.setPadding(0, diffY, 0, 0);和viewHeader.setPadding(0, paddY, 0, 0);效果是不一样的,diff/3是让其有种阻尼的感觉,不然的话会很块就被拉出来了。还有事件消费的话一定要return true。

@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
moveY = (int) event.getY();
diffY = moveY - downY;
//滑动的距离
paddY = - heightHeader + diffY / 3;
viewHeader.setPadding(0, paddY, 0, 0);
return true;
//break;
case MotionEvent.ACTION_UP: break;
}
return false;
}

我们下面要做下拉时给人的反馈,我们下拉时有3种状态分别是

  • 开始刷新  (下拉距离不超过100)
  • 释放刷新   (下拉距离超过100)
  • 刷新中    (手指释放,up事件处理)

//属性-开始刷新状态
private final int PULL_REFRESH_STATE = 0;
private final int PULL_REFRESH_RELEASE = 1;//释放刷新
private final int PULL_REFRESHING = 2;//正在刷新
private int pull_current_state = PULL_REFRESH_STATE;//当前状态
public void updateHeaderState() {
switch (pull_current_state) {
//开始
case PULL_REFRESH_STATE:
header_text.setText("开始刷新");
viewHeader.setPadding(0, -heightHeader, 0, 0);
break;
//释放
case PULL_REFRESH_RELEASE:
header_text.setText("释放刷新");
break;
//正在
case PULL_REFRESHING:
header_text.setText("刷新中.......");
viewHeader.setPadding(0, 0, 0, 0);
break; }
}

我们只需要判断是否处于刷新中,如果不是,则计算是不是第一个item可见并且滑动距离大于0,证明手指滑动了。然后不同滑出高度显示不同的文字即可。

@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
moveY = (int) event.getY();
diffY = moveY - downY;
//滑动的距离
paddY = - heightHeader + diffY / 3;
//如果是正在刷新中不做任何操作
if (pull_current_state == PULL_REFRESHING) {
return false;
}
// 下拉//第一条,并且滑动距离大于0
if (getFirstVisiblePosition() == 0 && diffY > 0) {
if (paddY > 0 && pull_current_state != PULL_REFRESH_RELEASE) {
//释放刷新
pull_current_state = PULL_REFRESH_RELEASE;
updateHeaderState();
} else if (paddY < 0 && pull_current_state != PULL_REFRESH_STATE) {
pull_current_state = PULL_REFRESH_STATE;
header_text.setText("开始刷新");
updateHeaderState();
}
viewHeader.setPadding(0, paddY, 0, 0);
return true;
} break;
case MotionEvent.ACTION_UP:
if (pull_current_state == PULL_REFRESH_STATE) {
updateHeaderState();
} else if (pull_current_state == PULL_REFRESH_RELEASE) {
pull_current_state = PULL_REFRESHING;
updateHeaderState();
}
break;
}
return false;
}

1.3、接口回调

我们需要状态根据业务来动态调整,在可以刷新的时候做一些逻辑处理,同时处理完了,调整状态。

public void setPullDownFinish() {
pull_current_state = PULL_REFRESH_STATE;
viewHeader.setPadding(0, -heightHeader, 0, 0);
}
public IPullDownRefreshService iPullDownRefreshService;
public interface IPullDownRefreshService {
void onPullDownRefresh();//下拉刷新 void onLoadMore();//上拉加载更多刷新
} public void setOnRefreshListener(IPullDownRefreshService iPullDownRefreshService) {
this.iPullDownRefreshService = iPullDownRefreshService;
}

准备工作做好后,我们在更新状态的地方调用

public void updateHeaderState() {
switch (pull_current_state) {
//开始
case PULL_REFRESH_STATE:
break;
//释放
case PULL_REFRESH_RELEASE:
break;
//正在
case PULL_REFRESHING:
header_text.setText("刷新中.......");
viewHeader.setPadding(0, 0, 0, 0);
if (iPullDownRefreshService != null) {
iPullDownRefreshService.onPullDownRefresh();
}
break; }
}

Activity中使用,来一个3秒刷新完成

mBinding.listview.setOnRefreshListener(new MyListview.IPullDownRefreshService() {
@Override
public void onPullDownRefresh() {
refreshSuccess();
Toast.makeText(ActivityRefresh.this, "下拉-加载中.....", Toast.LENGTH_SHORT).show();
} @Override
public void onLoadMore() {
Toast.makeText(ActivityRefresh.this, "more-加载中.....", Toast.LENGTH_SHORT).show();
}
}); CountDownTimer countDownTimer;
public void refreshSuccess() {
if (countDownTimer == null) {
countDownTimer = new CountDownTimer(3000, 1000) { @Override
public void onTick(long millisUntilFinished) { } @Override
public void onFinish() {
mBinding.listview.setPullDownFinish();
countDownTimer.cancel();
countDownTimer = null;
}
}.start();
}
}

到这下拉刷新就结束了。

上拉加载更多也是如此

2、上拉加载更多

2.1、底部样式

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
tools:ignore="MissingDefaultResource">
<ProgressBar
android:id="@+id/footer_prggress"
android:layout_width="20dp"
android:layout_height="match_parent"
style="?android:attr/progressBarStyle"
android:layout_marginEnd="10dp"
android:indeterminateTint="#E8AD56"
/>
<TextView
android:id="@+id/footer_text"
android:layout_width="wrap_content"
android:layout_height="70dp"
android:text="正在加载更多"
android:gravity="center"
android:textColor="#E8AD56"/> </LinearLayout>

2.2、布局添加

public void init(Context context) {
viewFooter = View.inflate(context, R.layout.view_footer, null);
viewFooter.measure(0, 0);
footer_text = viewFooter.findViewById(R.id.footer_text);
footer_prggress = viewFooter.findViewById(R.id.footer_prggress);
heightFooter = viewFooter.getMeasuredHeight();
viewFooter.setPadding(0, -heightFooter, 0, 0);
this.addFooterView(viewFooter);
}

到这里也布局算是添加完毕了,加载更多实现方式不同,我罗列两种

  1. 结合OnTouchListener
  2. 结合OnScrollListener

方式一

这里我先说方式一,因为我们下拉也是用的OnTouchListener,上拉加载也有几种状态,有加载中,还有暂无数据,普遍大家会写, 已经到底了~,一直显示在最底部,提示用户没数据了,从而我们控件的上拉事件不可触发状态。

private final int MORE_LOAD_STATE = 10;
private final int MORE_LOADING = 11;
private final int MORE_NO = 12;//已加载全部数据
private int more_current_state = MORE_LOAD_STATE;//当前状态

只有一点需要注意paddFooterY = paddFooterY > heightFooter ? 0 : paddFooterY;来判断不能滑出底部的高度。

@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
moveY = (int) event.getY();
diffY = moveY - downY;
//滑动的距离
paddFooterY = heightFooter - diffY/3 ;
//上拉加载更多
if (getLastVisiblePosition() == getCount() - 1 && more_current_state != MORE_NO) {
if( more_current_state!=MORE_LOADING){
more_current_state = MORE_LOADING;
if (iPullDownRefreshService != null) {
iPullDownRefreshService.onLoadMore();
}
}
paddFooterY = paddFooterY > heightFooter ? 0 : paddFooterY;
viewFooter.setPadding(0,paddFooterY , 0, 0);
return true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}

方式二

public class MyListview extends ListView implements View.OnTouchListener, AbsListView.OnScrollListener{
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//没有滚动,或者已经用户触摸滚动动画结束
if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING){
//最后一个可见
if(getLastVisiblePosition() == getCount()-1){
viewFooter.setPadding(0,0,0,0);
if(iPullDownRefreshService!=null){
iPullDownRefreshService.onLoadMore();
}
}
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }

注意:在init方法中添加

public void init(Context context) {
setOnTouchListener(this);
......
this.setOnScrollListener(this);
}

完工

自定义ListView下拉刷新上拉加载更多的更多相关文章

  1. Android 自定义 ListView 上下拉动“刷新最新”和“加载更多”歌曲列表

    本文内容 环境 测试数据 项目结构 演示 参考资料 本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表.所谓"刷新最新"和"加载更多"是指日期.演示代码 ...

  2. SwipeRefreshLayout实现下拉刷新上滑加载

    1. 效果图 2.RefreshLayout.java package myapplication.com.myapplication; import android.content.Context; ...

  3. Android 下拉刷新上啦加载SmartRefreshLayout + RecyclerView

    在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/de ...

  4. juery下拉刷新,div加载更多元素并添加点击事件(二)

    buffer.append("<div class='col-xs-3 "+companyId+"' style='padding-left: 10px; padd ...

  5. 移动端下拉刷新上拉加载-mescroll.js插件

    最近无意间看到有这么一个上拉刷新下拉加载的插件 -- mescroll.js,个人感觉挺好用的,官网地址是:http://www.mescroll.com 然后我就看了一下文档,简单的写了一个小dem ...

  6. Android如何定制一个下拉刷新,上滑加载更多的容器

    前言 下拉刷新和上滑加载更多,是一种比较常用的列表数据交互方式. android提供了原生的下拉刷新容器 SwipeRefreshLayout,可惜样式不能定制. 于是打算自己实现一个专用的.但是下拉 ...

  7. react-native-page-listview使用方法(自定义FlatList/ListView下拉刷新,上拉加载更多,方便的实现分页)

    react-native-page-listview 对ListView/FlatList的封装,可以很方便的分页加载网络数据,还支持自定义下拉刷新View和上拉加载更多的View.兼容高版本Flat ...

  8. ListView下拉刷新上拉加载更多实现

    这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...

  9. listview下拉刷新上拉加载扩展(二)-仿美团外卖

    经过前几篇的listview下拉刷新上拉加载讲解,相信你对其实现机制有了一个深刻的认识了吧,那么这篇文章我们来实现一个高级的listview下拉刷新上拉加载-仿新版美团外卖的袋鼠动画: 项目结构: 是 ...

随机推荐

  1. 定时脚本删除docker容器中内容

    今天在我同步mongo数据库的时候,服务器的磁盘突然就被占满了导致同步中断,mongo容器也停止工作了.然后就想要弄一个能够定时清理同步过程中留存在docker容器中的mongo数据的脚本.话不多说, ...

  2. Win10系统下搭建Go lang开发环境更换国内源并且体验宇宙最快框架Iris

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_156 最近有同学开始尝试接触Go lang,拥抱新技术永远都会是一个好习惯,之前的一篇文章介绍了如何在Mac os系统下配置Go ...

  3. MAUI Blazor 权限经验分享 (定位,使用相机)

    入门文章 Blazor Hybrid / MAUI 简介和实战 https://www.cnblogs.com/densen2014/p/16240966.html 在 Mac 上开发 .NET MA ...

  4. 如何用WebGPU流畅渲染千万级2D物体:基于光追管线

    大家好~我们已经实现了百万级2D物体的流畅渲染,不过是基于计算管线实现的.本文在它的基础上,改为基于光追管线实现,主要进行了CPU和GPU端内存的优化,成功地将渲染的2D物体数量由4百万提高到了2千万 ...

  5. 为什么企业不愿意升级ERP系统

    前段时间看到一篇文章讲企业为何不愿意升级内部系统的文章,觉得有意思,也想聊聊为何大部分企业不愿意升级ERP的事情. 老东家用的ERP是QAD系统,92年版本,没有图形界面,用户都是通过NetTerm等 ...

  6. 我与Apache DolphinScheduler的成长之路

    关于 Apache DolphinScheduler社区 Apache DolphinScheduler(incubator) 于17年在易观数科立项,19年3月开源, 19 年8月进入Apache ...

  7. RS485自动收发切换电路 [原创www.cnblogs.com/helesheng]

    RS485是最常见的一种远距离可靠传输和组网的UART串口信号接口协议.与同样传输UART串口信号的RS422协议相比,RS485使用半双工通信,即只有一个信道,在同一时刻要么从A到B,要么从B到A传 ...

  8. js运算符和逻辑分支

    运算符 1.拼接运算符:+,加号两边只要有一边出现字符串就是拼接 2.算术运算符  如:2+3: 3.赋值运算符+=,-=,/=,*= 4.关系运算符>,<,==,=== != !== ! ...

  9. C++ UAC 提权 以一个管理员身份运行程序

    这里是我编译的和一个测试Demo:http://pan.baidu.com/s/1qWNgC6C 大家如果看我下边的不是很清楚,可以下载这个具体工程: 群:103197177 C++进阶讨论:欢迎喜欢 ...

  10. 我用Axure制作了一款火影小游戏 | PM老猫

    Axure不仅仅是一个原型工具,除了原型之外还可以用来制作一些静态网页,这点对于不懂代码或前端的同学来说挺实用.之前整理了一版<Axure函数自查表>,因为感觉内容太多又对前端样式及脚本感 ...