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

我們先分析一下如何實(shí)現(xiàn) ListView 上拉加載。
實(shí)現(xià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 類繼承 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í)有所幫助,也希望大家多多支持武林網(wǎng)。
新聞熱點(diǎn)
疑難解答
圖片精選