Android中自定义ListView实现上拉加载更多和下拉刷新
ListView是Android中一个功能强大而且很常用的控件,在很多App中都有ListView的下拉刷新数据和上拉加载更多这个功能。这里我就简单记录一下实现过程。
实现这个功能的方法不止一个,GitHub上有一些开源库可以使用,但是本着学习的精神,我做的是使用自定义ListView实现这个功能。
思路:谷歌提供的ListView是不能提供下拉刷新和下拉加载的,所以我们就需要重写ListView。在ListView的头部和尾部加上我们的布局文件(progressbar)。
先说上拉加载更多实现原理:
首先先写一个底部布局的xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/foot_load"
android:padding="5dp"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyle"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载中。。。。"
/>
</LinearLayout> </LinearLayout>
写一个自定义ListView继承自ListView实现它的三个构造方法,这里必须实现三个构造方法,不实现的话就无法显示ListView.并且在每个构造方法中都写一个方法用于初始化相关控件。
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
initView(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
} public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
}
在initView(context)方法中通过LayoutInfalter.from(context).from(你的xml,null);拿到xml。返回一个View对象,ListView中有一个setfootView()方法,使用他可以设置View到ListView的底部,当然做了这两步是完全不够的,我们还需要对这个View做一个交互的操作,当用户滑到底部时这个View显示,数据加载完毕之后这个View消失。
第一步:需要对这个底部footView隐藏:
第一种方式是通过setVisibility(View.GONE)这个方法让这个View不显示。
第二种方式:
//测量footview的高度,通过measure()传入两个0参数,系统会不认这个0,自动帮我们测量出加在底部View的长度信息
footview.measure(0,0);
//拿到高度
footViewHeight=footview.getMeasuredHeight();
//隐藏view,让头部显示高度为实际测量高度的负数,实现布局的隐藏
footview.setPadding(0,-footViewHeight,0,0);
第二部:当滑到底部的时候显示这个footView。这里就要实现下滑监听接口implements AbsListView.OnScrollListener,实现接口的两个方法。
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (totaItemCounts==lasstVisible&&scrollState==SCROLL_STATE_IDLE) {
if (!isLoading) {
isLoading=true;
// footview.findViewById(R.id.foot_load).setVisibility(View.VISIBLE);
footview.setPadding(0,0,0,0);
//加载数据
loadListener.onLoad();}
} }
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//最后一个可见item是第一个可见item加上所有可见item(第一个可见item为0)
this.lasstVisible=firstVisibleItem+visibleItemCount;
//所有item为listview的所有item
this.totaItemCounts=totalItemCount; }
那么如何在刷新的时候通知ListView加载数据呢?这里使用一个接口回调的方法在自定义ListView写一个接口,里面写一个更新ListView的方法,让主布局调用这个接口实现这个接口的方法。
public void setInterface(LoadListener loadListener){
this.loadListener=loadListener; }
//接口回调
public interface LoadListener{ void onLoad(); }
@Override
public void onLoad() {
//设置三秒延迟模仿延时获取数据
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//加载数据
for (int j=1;j<11;j++){ list.add(j);
}
//更新 数据
adapter.notifyDataSetChanged();
//加载完毕,就是让View隐藏
listview.loadComplete(); }
},3000);
}
好了,这就实现了一个简单的上拉加载功能。
下拉刷新和上拉加载过程差不多,但是有时候,下拉刷新有提示,下拉刷新,松开刷新,正在刷新提示,这又该如何实现呐,这里就需要实现onTouchEvent方法,设置下拉长度匹配刷新数据。同时,设置progrebar的可见状态。
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
yload=(int)ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY=(int)ev.getY();
int paddingY=-headViewHeight+(moveY-yload)/2; if (paddingY<0){
updateInfo.setText("下拉刷新。。。");
progressBar.setVisibility(View.GONE);
}
if (paddingY>0){
updateInfo.setText("松开刷新。。。");
progressBar.setVisibility(View.GONE);
} headview.setPadding(0,paddingY,0,0); break;
// case MotionEvent.ACTION_UP:
// headview.setPadding(0,0,0,0);
// updateInfo.setText("正在刷新。。。");
// progressBar.setVisibility(View.VISIBLE);
// loadListener.pullLoad();
//
// break;
}
return super.onTouchEvent(ev);
}
附上完整源码:
main.xml布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.yakir.view.MyListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_view"></com.yakir.view.MyListView> </RelativeLayout>
main activity:
package com.yakir.demo; import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter; import com.yakir.view.MyListView; import java.util.ArrayList;
import java.util.List; public class MainActivity extends AppCompatActivity implements MyListView.LoadListener{
private MyListView listview;
private List<Integer>list=new ArrayList<>();
private ArrayAdapter adapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i=1;i<20;i++){
list.add(i);
}
initView();
adapter=new ArrayAdapter<>(this,android.R.layout.simple_expandable_list_item_1,list);
listview.setAdapter(adapter); } private void initView() {
listview=(MyListView) findViewById(R.id.list_view);
listview.setInterface(this);
} @Override
public void onLoad() {
//设置三秒延迟模仿延时获取数据
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//加载数据
for (int j=1;j<11;j++){ list.add(j);
}
//更新 数据
adapter.notifyDataSetChanged();
//加载完毕
listview.loadComplete(); }
},3000); } @Override
public void pullLoad() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
list.clear();
for (int i=1;i<20;i++){
list.add(i+1);
}
adapter.notifyDataSetChanged();
listview.loadComplete(); }
},2000); }
}
头布局headview:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/head"
android:orientation="horizontal"
android:gravity="center"
android:padding="5dp">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleInverse"
android:id="@+id/progressbar"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在刷新。。。。"
android:id="@+id/update_info"/> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/update_time"
/> </LinearLayout> </LinearLayout>
尾布局footview:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/foot_load"
android:padding="5dp"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyle"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载中。。。。"
/>
</LinearLayout> </LinearLayout>
自定义ListView:
package com.yakir.view; import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView; import com.yakir.demo.R; import java.text.SimpleDateFormat; /**
* 自定义listview实现上拉刷新下拉加载
* Created by qbqw7 on 2016/7/12.
*/
public class MyListView extends ListView implements AbsListView.OnScrollListener{
private View footview;
private View headview;
private int totaItemCounts;
private int lasstVisible;
private int fistVisiable;
private LoadListener loadListener;
private int footViewHeight;
private int headViewHeight;
private int yload;
boolean isLoading;
private TextView updateInfo;
private TextView updateTime;
private ProgressBar progressBar;
public MyListView(Context context) {
super(context);
initView(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
} public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
} private void initView(Context context) {
//拿到头布局xml
headview=LayoutInflater.from(context).inflate(R.layout.head_layouyt,null);
updateInfo=(TextView)headview.findViewById(R.id.update_info);
updateTime=(TextView)headview.findViewById(R.id.update_time);
progressBar=(ProgressBar)headview.findViewById(R.id.progressbar);
updateTime.setText("更新于:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis()));
//拿到尾布局xml
footview=LayoutInflater.from(context).inflate(R.layout.proces_layout,null);
//测量footview的高度
footview.measure(0,0);
//拿到高度
footViewHeight=footview.getMeasuredHeight();
//隐藏view
footview.setPadding(0,-footViewHeight,0,0);
headview.measure(0,0);
headViewHeight=headview.getMeasuredHeight();
headview.setPadding(0,-headViewHeight,0,0);
//设置不可见
// footview.findViewById(R.id.foot_load).setVisibility(View.GONE);
// headview.findViewById(R.id.head).setVisibility(View.GONE);
//添加到listview底部
this.addFooterView(footview);
//添加到listview头部
this.addHeaderView(headview);
//设置拉动监听
this.setOnScrollListener(this); } @Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
yload=(int)ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY=(int)ev.getY();
int paddingY=-headViewHeight+(moveY-yload)/2; if (paddingY<0){
updateInfo.setText("下拉刷新。。。");
progressBar.setVisibility(View.GONE);
}
if (paddingY>0){
updateInfo.setText("松开刷新。。。");
progressBar.setVisibility(View.GONE);
} headview.setPadding(0,paddingY,0,0); break;
// case MotionEvent.ACTION_UP:
// headview.setPadding(0,0,0,0);
// updateInfo.setText("正在刷新。。。");
// progressBar.setVisibility(View.VISIBLE);
// loadListener.pullLoad();
//
// break;
}
return super.onTouchEvent(ev);
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (totaItemCounts==lasstVisible&&scrollState==SCROLL_STATE_IDLE) {
if (!isLoading) {
isLoading=true;
// footview.findViewById(R.id.foot_load).setVisibility(View.VISIBLE);
footview.setPadding(0,0,0,0);
//加载数据
loadListener.onLoad(); }
}
if (fistVisiable==0){
headview.setPadding(0,0,0,0);
updateInfo.setText("正在刷新。。。");
progressBar.setVisibility(View.VISIBLE);
loadListener.pullLoad();
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.fistVisiable=firstVisibleItem;
this.lasstVisible=firstVisibleItem+visibleItemCount;
this.totaItemCounts=totalItemCount; }
//加载完成
public void loadComplete(){
isLoading=false;
//footview.findViewById(R.id.foot_load).setVisibility(View.GONE);
footview.setPadding(0,-footViewHeight,0,0);
headview.setPadding(0,-headViewHeight,0,0); }
public void setInterface(LoadListener loadListener){
this.loadListener=loadListener; }
//接口回调
public interface LoadListener{ void onLoad();
void pullLoad(); }
}
Android中自定义ListView实现上拉加载更多和下拉刷新的更多相关文章
- YCRefreshView-自定义支持上拉加载更多,下拉刷新。。。
自定义支持上拉加载更多,下拉刷新,支持自由切换状态[加载中,加载成功,加载失败,没网络等状态]的控件,拓展功能[支持长按拖拽,侧滑删除]可以选择性添加 .具体使用方法,可以直接参考demo. 轻量级侧 ...
- 滚动到底部加载更多及下拉刷新listview的使用
最新内容建议直接访问原文:滚动到底部加载更多及下拉刷新listview的使用 本文主要介绍可同时实现下拉刷新及滑动到底部加载更多的ListView的使用. 该ListView优点包括:a. 可自定义下 ...
- 微信小程序采坑之上拉触底加载更多和下拉刷新
小程序中加载更多数据一般都是触底刷新 有自带的函数: onReachBottom: function (){} 但是在使用时触发完全没有反应,后来尝试给外层加了一个高度,解决问题 仔细想想也是,没有设 ...
- RecyclerView下拉刷新上拉加载更多
现在Android里都建议用RecyclerView代替ListView和GridView,所以下拉刷新和上拉加载更多也需要实现.下拉刷新可以用SwipeRefreshLayout 包裹Recycle ...
- 微信小程序实现上拉和下拉加载更多
在上一篇文章中,我们知道了使用 scroll-view 可以实现上拉加载更多,但是由于 scroll-view 的限制,它无法实现下拉加载更多,这篇文章我们使用 view 组件来实现 上拉和下拉加载更 ...
- 移动端touch事件 || 上拉加载更多
前言: 说多了都是泪,在进行项目开发时,在上拉加载更多实现分页效果的问题上,由于当时开发任务紧急,所以就百度找了各种移动端的上拉下拉 实现加载更多的插件.然后就留下了个坑:上拉加载的时候会由于用户错误 ...
- mui的上拉加载更多 下拉刷新 自己封装的demo
----------------------------------------------- 这是一个非常呆萌的程序妹子,深夜码的丑代码------------------------------- ...
- mui实现分页上拉加载更多 下拉刷新数据的简单实现 移动端下拉上拉
空下来把mui上拉加载更多,下拉刷新数据做了一个简单的实现,希望可以帮助到需要的朋友 demo项目的结构 <!DOCTYPE html> <html> <head> ...
- Taro下拉刷新,上拉加载更多
1.引入插件 import Taro, { Component } from '@tarojs/taro' import { View, Text, ScrollView } from '@taroj ...
随机推荐
- 移动端base.css
html { color: #333; /*规定主色调,依据业务场景(非必须)*/ background: #F6F6F6; /*规定主背景,依据业务场景(非必须)*/ overflow-y: aut ...
- AppScan9.0.3.5漏洞扫描记录
1.跨站点脚本编制 这个安全漏洞拿cookie做文章,而且是将前端的一些弹窗方法,要么通过脚本注入,要么通过url.encode之后注入,看几个变异的版本: 版本一: cookie 从以下位置进行控 ...
- Python 函数 memoryview()
memoryview() 函数返回给定参数的内存查看对象(Momory view). 所谓内存查看对象,是指对支持缓冲区协议的数据进行包装,在不需要复制对象基础上允许Python代码访问.返回元组列表 ...
- Request.UrlReferrer详解
使用前需要进行判断: if (Request != null && Request.UrlReferrer != null && Request.UrlReferrer ...
- 打包python文件,让文件程序化
通过对源文件打包,Python程序可以在没有安装 Python的环境中运行,也可以作为一个独立文件方便传递和管理. 现在网上主流的打包方式有两种py2exe或者pyinstaller两款多平台的Pyt ...
- FPGA的CNN加速,你怎么看?
网上对于FPGACNN加速的研究已经很多了,神经网络的硬件加速似乎已经满大街都是了,这里我们暂且不讨论谁做的好谁做的不好,我们只是根据许许多多的经验来总结一下实现硬件加速,需要哪些知识,考虑哪些因素. ...
- Hibernate学习7—Hibernate 映射继承
需求:学生有很多照片,分为生活照和工作照: 第一节:每个具体类对应一个表 Student.java: package com.cy.model; import java.util.Set; publi ...
- HDU 1166 敌兵布阵 (线段树模版题)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...
- CentOS7 tar打包工具 打包,解包,打包压缩,打包解压缩
tar命令 選項與參數: -c :建立打包檔案,可搭配 -v 來察看過程中被打包的檔名(filename) -t :察看打包檔案的內容含有哪些檔名,重點在察看『檔名』就是了: -x :解打包或解壓縮的 ...
- 关于SQLSERVER的全文目录跟全文索引的区别
很久没有写随笔了,本来之前想写一篇关于SQLSERVER全文索引的随笔,可惜没有时间,一直拖到现在才有时间写,不好意思让各位久等了~ 先介绍一下SQLSERVER中的存储类对象,哈哈,先介绍一下概念嘛 ...