Android自定义ListView实现下拉刷新上拉加载更多

作者:TMusketeer 时间:2021-08-20 21:17:04 

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

效果图

Android自定义ListView实现下拉刷新上拉加载更多

Android自定义ListView实现下拉刷新上拉加载更多

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

  • 添加刷新时间

  • 加入其他具有特色的动效

Android自定义ListView实现下拉刷新上拉加载更多

<?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事件处理)

Android自定义ListView实现下拉刷新上拉加载更多

//属性-开始刷新状态
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>

Android自定义ListView实现下拉刷新上拉加载更多

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);
}

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

  • 结合OnTouchListener

  • 结合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);
}

来源:https://www.cnblogs.com/cmusketeer/p/16769560.html

标签:Android,ListView,下拉刷新,上拉加载
0
投稿

猜你喜欢

  • 完美解决java读取大文件内存溢出的问题

    2023-07-31 17:53:17
  • android 仿微信聊天气泡效果实现思路

    2022-11-12 21:11:35
  • 带你了解Java数据结构和算法之队列

    2022-07-03 12:45:34
  • 详解java8在Collection中新增加的方法removeIf

    2022-06-04 20:51:45
  • springboot配置项目启动后自动打开浏览器访问项目方式

    2023-10-05 07:33:09
  • Android中获取IMEI码的方法

    2023-04-08 09:10:58
  • Android开发笔记之如何正确获取WebView的网页Title

    2022-06-04 07:02:41
  • Spring Boot2深入分析解决java.lang.ArrayStoreException异常

    2021-10-20 12:10:24
  • C#/VB.NET 在PDF中添加文件包(Portfolio)的方法

    2023-08-28 03:01:18
  • Android studio 快捷键大全

    2022-03-27 14:58:56
  • SSH框架网上商城项目第8战之查询和删除商品类别功能实现

    2023-02-12 05:54:39
  • SpringBoot整合freemarker实现代码生成器

    2023-07-17 20:31:08
  • mybatis-plus多表联查join的实现

    2023-11-24 06:49:56
  • Android Studio做超好玩的拼图游戏 附送详细注释源码

    2023-08-05 12:19:16
  • Java Base64解码错误及解决方法

    2023-02-09 03:36:35
  • C语言函数声明以及函数原型超详细讲解示例

    2023-03-31 03:12:02
  • SpringBoot2.3新特性优雅停机详解

    2023-11-28 07:59:43
  • JavaWeb实现学生信息管理系统(2)

    2022-03-04 02:31:12
  • C#编程实现动态改变配置文件信息的方法

    2022-05-01 15:09:25
  • Android 动态菜单实现实例代码

    2023-05-19 16:24:58
  • asp之家 软件编程 m.aspxhome.com