Android ListView 實(shí)現(xiàn)上拉加載的示例代碼
本文介紹了Android ListView 實(shí)現(xiàn)上拉加載的示例代碼,分享給大家,具體如下:

我們先分析一下如何實(shí)現(xiàn) ListView 上拉加載。
- 當(dāng)我們上拉的時(shí)候,會(huì)出現(xiàn)一個(gè)提示界面,即 ListView 的 Footer 布局。
- ListView 要實(shí)現(xiàn)滾動(dòng),所以要監(jiān)聽(tīng) ListView 滾動(dòng)事件,即 OnScrollListener() 事件。
- 當(dāng)我們開(kāi)始滾動(dòng)時(shí),F(xiàn)ooter 布局才慢慢顯示出來(lái),所以需要監(jiān)聽(tīng) ListView 的 onTouch() 事件。
實(shí)現(xiàn)思路
- 首先判斷 ListView 加載時(shí)機(jī),當(dāng) ListView 的 lastVisibleItem == totalItemCount 時(shí)表示當(dāng)前處于 ListView 最底端,此時(shí)允許下拉。
- 自定義一個(gè) FooterView,將 FooterView 添加到 ListView 底部,在上拉時(shí)候的顯示和完成時(shí)候的隱藏。
- 定義一個(gè)加載接口,當(dāng)上拉動(dòng)作完成時(shí)候回調(diào),用于標(biāo)記狀態(tài)并加載最新數(shù)據(jù)進(jìn)行展示。
1、定義 Footer
Footer 要實(shí)現(xiàn)的效果:
第一次上拉時(shí),F(xiàn)ooter 逐漸顯示,文字顯示為下拉可以加載,箭頭向上,進(jìn)度條隱藏。
當(dāng)松開(kāi)加載的時(shí)候,箭頭隱藏,進(jìn)度條展示,文字改為正在加載。

1、Footer 加載時(shí)狀態(tài)變化
定義一個(gè)如上圖所示的 Footer 的 XML 文件 footer_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="10dp"
android:paddingTop="10dp">
<LinearLayout
android:id="@+id/layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉可以刷新"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:id="@+id/img_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@+id/layout"
android:src="@drawable/pull_to_refresh_arrow" />
<ProgressBar
android:id="@+id/progress"
style="@style/progressBar_custom_drawable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@+id/img_arrow"
android:visibility="gone"
tools:visibility="visible" />
</RelativeLayout>
2、初始化布局
定義一個(gè) RefreshListView 類(lèi)繼承 ListView,重寫(xiě)構(gòu)造函數(shù),并將 Footer 添加到 ListView 中。
public class RefreshListView extends ListView {
private View header;
private int headerHeight;//頂部布局高度
private int firstVisibleItem;//當(dāng)前第一個(gè) Item 可見(jiàn)位置
private float startY;//按下時(shí)開(kāi)始的Y值
private int scrollState;//當(dāng)前滾動(dòng)狀態(tài)
private View footer;
private int footerHeight;//底部布局高度
private float lastY;
private boolean canLoadMoreEnabled;//是否允許加載更多
public RefreshListView(Context context) {
super(context);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
header = LayoutInflater.from(context).inflate(R.layout.header_layout, null);
footer = LayoutInflater.from(context).inflate(R.layout.footer_layout, null);
measureView(header);
measureView(footer);
//這里獲取高度的時(shí)候需要先通知父布局header占用的空間
headerHeight = header.getMeasuredHeight();
footerHeight = footer.getMeasuredHeight();
topPadding(-headerHeight);
bottomPadding(-footerHeight);//用于隱藏 Footer
this.addHeaderView(header);
this.addFooterView(footer);
this.setOnScrollListener(this);
}
/**
* 設(shè)置 Footer 布局的下邊距
* 以隱藏 Footer
* @param topPadding
*/
private void bottomPadding(int bottomPadding) {
footer.setPadding(footer.getPaddingLeft(), footer.getPaddingTop(),
footer.getPaddingRight(),
bottomPadding);
footer.invalidate();
}
}
3、實(shí)現(xiàn)上拉加載
給 ListView 設(shè)置監(jiān)聽(tīng)
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
private int firstVisibleItem;//當(dāng)前第一個(gè) Item 可見(jiàn)位置
private int scrollState;//當(dāng)前滾動(dòng)狀態(tài)
private void initView(Context context) {
header = LayoutInflater.from(context).inflate(R.layout.header_layout, null);
footer = LayoutInflater.from(context).inflate(R.layout.footer_layout, null);
measureView(header);
measureView(footer);
//這里獲取高度的時(shí)候需要先通知父布局header占用的空間
headerHeight = header.getMeasuredHeight();
footerHeight = footer.getMeasuredHeight();
topPadding(-headerHeight);
bottomPadding(-footerHeight);
this.addHeaderView(header);
this.addFooterView(footer);
this.setOnScrollListener(this);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState = scrollState;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.firstVisibleItem = firstVisibleItem;
this.lastVisibleItem = firstVisibleItem + visibleItemCount;
this.totalItemCount = totalItemCount;
}
}
加載的時(shí)機(jī)是判斷l(xiāng)astVisibleItem == totalItemCount,而上拉事件我們需要重寫(xiě) onTouchEvent() 事件,首先定義幾個(gè)狀態(tài)。
private float lastY; private static int state;//當(dāng)前狀態(tài) private final static int NONE = 0;//正常狀態(tài) private final static int PULL = 1;//下拉狀態(tài) private final static int RELEASE = 2;//釋放狀態(tài) private final static int REFRESHING = 3;//正在刷新?tīng)顟B(tài)
在 onTouchEvent 中,在 ACTION_DOWN 時(shí),記錄最開(kāi)始的 Y 值,然后在 ACTION_MOVE 事件中實(shí)時(shí)記錄移動(dòng)距離 space,不斷刷新 FooterView 的 bootomPadding,讓它跟隨滑動(dòng)距離進(jìn)行顯示,繼續(xù)滑動(dòng),當(dāng) space 大于了 FooterHeight 時(shí),狀態(tài)給為 RELEASE,表示可以釋放進(jìn)行刷新操作。
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//最頂部
if (firstVisibleItem == 0) {//刷新
canRefreshEnabled = true;
startY = ev.getY();
} else if (lastVisibleItem == totalItemCount) {//加載更多
canLoadMoreEnabled = true;
lastY = ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev);
break;
case MotionEvent.ACTION_UP:
if (state == RELEASE) {//如果已經(jīng)釋放,則可以提示刷新數(shù)據(jù)
state = REFRESHING;
if (iRefreshListener != null) {
iRefreshListener.onRefresh();
}
if (iLoadMoreListener != null) {
iLoadMoreListener.onLoadMore();
}
} else if (state == PULL) {//如果是在下拉狀態(tài),不刷新數(shù)據(jù)
state = NONE;
}
if (canRefreshEnabled) {
refreshViewByState();
}
if (canLoadMoreEnabled) {
loadViewByState();
}
canLoadMoreEnabled = false;
canRefreshEnabled = false;
break;
}
return super.onTouchEvent(ev);
}
/**
* 判斷移動(dòng)過(guò)程中的操作
*
* @param ev
*/
private void onMove(MotionEvent ev) {
int tempY = (int) ev.getY();
int refreshSpace = (int) (tempY - startY);//向下移動(dòng)的距離
int topPadding = refreshSpace - headerHeight;//在移動(dòng)過(guò)程中不斷設(shè)置 topPadding
int loadSpace = (int) (lastY - tempY);//向上移動(dòng)的距離
int bottomPadding = loadSpace - footerHeight;//在移動(dòng)過(guò)程中不斷設(shè)置 bottomPadding
switch (state) {
case NONE:
//下拉移動(dòng)距離大于0
if (refreshSpace > 0) {
state = PULL; //狀態(tài)變成下拉狀態(tài)
refreshViewByState();
}
//上拉移動(dòng)距離大于0
if (loadSpace > 0) {
state = PULL;//狀態(tài)變成下拉狀態(tài)
loadViewByState();
}
break;
case PULL:
if (canRefreshEnabled) {
topPadding(topPadding);//在移動(dòng)過(guò)程中不斷設(shè)置 topPadding,讓 Header 隨著下拉動(dòng)作慢慢顯示
}
if (canLoadMoreEnabled) {
bottomPadding(bottomPadding);//在移動(dòng)過(guò)程中不斷設(shè)置 bottomPadding,讓 Footer 隨著上拉動(dòng)作慢慢顯示
}
//移動(dòng)距離大于headerHeight并且正在滾動(dòng)
if (canRefreshEnabled && refreshSpace > (headerHeight + 30) && scrollState == SCROLL_STATE_TOUCH_SCROLL) {
state = RELEASE;//提示釋放
refreshViewByState();
}
//移動(dòng)距離大于footerHeight并且正在滾動(dòng)
if (canLoadMoreEnabled && loadSpace > footerHeight + 30 && scrollState == SCROLL_STATE_TOUCH_SCROLL) {
state = RELEASE;//提示釋放
loadViewByState();//刷新footer布局
}
break;
case RELEASE:
if (canRefreshEnabled) {
topPadding(topPadding);
//移動(dòng)距離小于headerHeight
if (refreshSpace < headerHeight + 30) {
state = PULL;//提示下拉
} else if (refreshSpace <= 0) {
state = NONE;
}
refreshViewByState();//更新header
}
if (canLoadMoreEnabled) {
bottomPadding(bottomPadding);
//移動(dòng)距離小于footerHeight
if (loadSpace < footerHeight + 30) {
state = PULL;//提示下拉
} else if (loadSpace <= 0) {
state = NONE;
}
loadViewByState();//更新footer
}
break;
}
}
加載數(shù)據(jù)的時(shí)候,要根據(jù)狀態(tài)不斷改變 FooterView 的顯示,箭頭定義一個(gè)旋轉(zhuǎn)動(dòng)畫(huà)讓其跟隨滑動(dòng)距離實(shí)現(xiàn)旋轉(zhuǎn),進(jìn)度條也設(shè)置了逐幀動(dòng)畫(huà)實(shí)現(xiàn)自定義進(jìn)度條。
private void loadViewByState() {
TextView tip = footer.findViewById(R.id.tv_tip);
ImageView arrow = footer.findViewById(R.id.img_arrow);
ProgressBar progressBar = footer.findViewById(R.id.progress);
progressBar.setBackgroundResource(R.drawable.custom_progress_bar);
AnimationDrawable animationDrawable = (AnimationDrawable) progressBar.getBackground();
//給箭頭設(shè)置動(dòng)畫(huà)
RotateAnimation anim = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
RotateAnimation anim1 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(200);
anim.setFillAfter(true);
anim1.setDuration(200);
anim1.setFillAfter(true);
switch (state) {
case NONE://正常,footer不顯示
bottomPadding(-footerHeight);
arrow.clearAnimation();
break;
case PULL://下拉狀態(tài)
arrow.setVisibility(VISIBLE);//箭頭顯示,進(jìn)度條隱藏
progressBar.setVisibility(GONE);
if (animationDrawable.isRunning()) {
animationDrawable.stop();
}
tip.setText("上拉可以加載");
arrow.clearAnimation();
arrow.setAnimation(anim);//箭頭向下
break;
case RELEASE://釋放狀態(tài)
arrow.setVisibility(VISIBLE);//箭頭顯示,進(jìn)度條隱藏
progressBar.setVisibility(GONE);
if (animationDrawable.isRunning()) {
//停止動(dòng)畫(huà)播放
animationDrawable.stop();
}
tip.setText("松開(kāi)開(kāi)始加載");
arrow.clearAnimation();
arrow.setAnimation(anim);//箭頭向上
break;
case REFRESHING://刷新?tīng)顟B(tài)
bottomPadding(50);
arrow.setVisibility(GONE);//箭頭顯示,進(jìn)度條隱藏
progressBar.setVisibility(VISIBLE);
animationDrawable.start();
tip.setText("正在加載...");
arrow.clearAnimation();
break;
}
}
4、下拉刷新完成回調(diào)
當(dāng)上拉加載完成時(shí),我們需要實(shí)現(xiàn)數(shù)據(jù)的刷新,并且要通知 Adapter 刷新數(shù)據(jù),這里我們定義一個(gè)監(jiān)聽(tīng)接口實(shí)現(xiàn)回調(diào)即可?;卣{(diào)在 ACTION_UP 的 RELEASE 狀態(tài)下進(jìn)行注冊(cè)。
private ILoadMoreListener iLoadMoreListener;
public void setILoadMoreListener(ILoadMoreListener iLoadMoreListener) {
this.iLoadMoreListener = iLoadMoreListener;
}
public interface ILoadMoreListener {
void onLoadMore();
}
5、測(cè)試
package com.dali.refreshandloadmorelistview;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import java.util.ArrayList;
public class MainActivity extends Activity implements RefreshListView.IRefreshListener, RefreshListView.ILoadMoreListener {
private ArrayList<ApkEntity> apk_list;
private ListAdapter adapter;
private RefreshListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setData();
showList(apk_list);
}
private void showList(ArrayList<ApkEntity> apk_list) {
if (adapter == null) {
listView = (RefreshListView) findViewById(R.id.listview);
listView.setIRefreshListener(this);
listView.setILoadMoreListener(this);
adapter = new ListAdapter(this, apk_list);
listView.setAdapter(adapter);
} else {
adapter.onDateChange(apk_list);
}
}
private void setData() {
apk_list = new ArrayList<ApkEntity>();
for (int i = 0; i < 10; i++) {
ApkEntity entity = new ApkEntity();
entity.setName("默認(rèn)數(shù)據(jù)" + i);
entity.setDes("這是一個(gè)神奇的應(yīng)用");
entity.setInfo("50w用戶");
apk_list.add(entity);
}
}
private void setRefreshData() {
for (int i = 0; i < 2; i++) {
ApkEntity entity = new ApkEntity();
entity.setName("默認(rèn)數(shù)據(jù) + 刷新" + i);
entity.setDes("這是一個(gè)神奇的應(yīng)用");
entity.setInfo("50w用戶");
apk_list.add(0, entity);
}
}
private void setLoadData() {
for (int i = 0; i < 2; i++) {
ApkEntity entity = new ApkEntity();
entity.setName("默認(rèn)數(shù)據(jù) + 加載" + (adapter.getCount() + i));
entity.setDes("這是一個(gè)神奇的應(yīng)用");
entity.setInfo("50w用戶");
apk_list.add(entity);
}
}
@Override
public void onRefresh() {
//添加刷新動(dòng)畫(huà)效果
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
//獲取最新數(shù)據(jù)
setRefreshData();
//通知界面顯示數(shù)據(jù)
showList(apk_list);
//通知 ListView 刷新完成
listView.refreshComplete();
}
}, 2000);
}
@Override
public void onLoadMore() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
//獲取最新數(shù)據(jù)
setLoadData();
//通知界面顯示數(shù)據(jù)
showList(apk_list);
//通知 ListView 刷新完成
listView.loadMoreComplete();
}
}, 2000);
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- android使用SwipeRefreshLayout實(shí)現(xiàn)ListView下拉刷新上拉加載
- android使用PullToRefresh框架實(shí)現(xiàn)ListView下拉刷新上拉加載更多
- Android通過(guò)XListView實(shí)現(xiàn)上拉加載下拉刷新功能
- XListView實(shí)現(xiàn)下拉刷新和上拉加載原理解析
- react-native ListView下拉刷新上拉加載實(shí)現(xiàn)代碼
- Android中Listview下拉刷新和上拉加載更多的多種實(shí)現(xiàn)方案
- Android ListView實(shí)現(xiàn)上拉加載下拉刷新和滑動(dòng)刪除功能
- Android自定義listview布局實(shí)現(xiàn)上拉加載下拉刷新功能
- Android XListView下拉刷新和上拉加載更多
- Android實(shí)現(xiàn)上拉加載更多ListView(PulmListView)
相關(guān)文章
ScrollView嵌套ListView及ListView嵌套的高度計(jì)算方法
下面小編就為大家分享一篇ScrollView嵌套ListView及ListView嵌套的高度計(jì)算方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
android項(xiàng)目手機(jī)衛(wèi)士來(lái)電顯示號(hào)碼歸屬地
由于詐騙電話越來(lái)越猖狂,號(hào)碼歸屬地顯示越來(lái)越重要,本篇文章主要介紹了android手機(jī)衛(wèi)士來(lái)電顯示號(hào)碼歸屬地,有要的朋友可以了解一下。2016-10-10
Android使用RotateImageView 旋轉(zhuǎn)ImageView
這篇文章主要介紹了Android使用RotateImageView 旋轉(zhuǎn)ImageView 的相關(guān)資料,需要的朋友可以參考下2016-01-01
Android使用ViewPager實(shí)現(xiàn)類(lèi)似laucher左右拖動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android使用ViewPager實(shí)現(xiàn)類(lèi)似laucher左右拖動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
Android調(diào)用系統(tǒng)圖庫(kù)獲取圖片的方法
這篇文章主要為大家詳細(xì)介紹了Android調(diào)用系統(tǒng)圖庫(kù)獲取圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
Android手勢(shì)密碼的實(shí)現(xiàn)
這篇文章主要介紹了Android手勢(shì)密碼的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2016-04-04
Android 物理游戲之重力系統(tǒng)開(kāi)發(fā)示例代碼
介紹Android 物理游戲之重力系統(tǒng),這里提供了詳細(xì)的資料整理,并附示例代碼和實(shí)現(xiàn)效果圖,有興趣的小伙伴可以參考下2016-08-08
Android開(kāi)發(fā)之判斷有無(wú)虛擬按鍵(導(dǎo)航欄)的實(shí)例
下面小編就為大家分享一篇Android開(kāi)發(fā)之判斷有無(wú)虛擬按鍵(導(dǎo)航欄)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01

