前几天看了github上面的例子,参照它的实现,自己又稍微改了一点,往项目里面增加了一个上拉加载更多功能。具体的实现如下:

首先要重写ListView:

 import android.content.Context;
import android.util.AttributeSet;
import android.widget.AbsListView;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.ListView; import java.util.List; import njucm.edu.loadmore.activities.LoadingView;
import njucm.edu.loadmore.adapters.PagingBaseAdapter; /**
* Created by Mesogene on 10/9/15.
*/
public class LoadListView extends ListView { private OnScrollListener onScrollListener = null;
private PageEnableListener pageEnableListener = null;
LoadingView loadListView = null;
private boolean isLoading;
private boolean hasMoreItem;
private int lastVisibleItem; //最后一个可见的项
private int totalItemCount; //总的项数 public LoadListView(Context context) {
super(context);
init();
} public LoadListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public LoadListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} public void setPageEnableListener(PageEnableListener pageEnableListener) {
this.pageEnableListener = pageEnableListener;
} public boolean isLoading() {
return isLoading;
} public void setIsLoading(boolean isLoading) {
this.isLoading = isLoading;
} public boolean isHasMoreItem() {
return hasMoreItem;
} public void setHasMoreItem(boolean hasMoreItem) {
this.hasMoreItem = hasMoreItem;
if(!this.hasMoreItem){ //如果没有更多项,移除底部
removeFooterView(loadListView);
}
else if(findViewById(R.id.loading_view) == null){
addFooterView(loadListView);
ListAdapter adapter = ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter();
setAdapter(adapter);
}
} /**
* 在下载任务完成之后去调用这个方法
* @param hasMoreItem 是否还有更多项
* @param newItems 新的项
*/
public void onFinsihLoading(boolean hasMoreItem, List<? extends Object> newItems){
setHasMoreItem(hasMoreItem);
setIsLoading(false); //下载任务完成后,把loading设置成false
if(newItems != null && newItems.size() >0){
ListAdapter adapter = ((HeaderViewListAdapter)getAdapter()).getWrappedAdapter(); //获取这个listview的adapter
if(adapter instanceof PagingBaseAdapter){
((PagingBaseAdapter) adapter).addMoreItems(newItems); //添加项目,包含notify方法
}
}
}
/**
* 初始化listview的操作
*/
private void init(){
isLoading = false;
loadListView = new LoadingView(getContext());
addFooterView(loadListView);
super.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if(onScrollListener != null){
onScrollListener.onScrollStateChanged(absListView, scrollState);
}
/**
* 当你的listview移动到底部的时候,即你看到的最后一项等于总的项数,已经停止滚动 没有正在加载并且还有更多项
* 的时候会被执行
*/
if(lastVisibleItem == totalItemCount && scrollState == SCROLL_STATE_IDLE && !isLoading && hasMoreItem){
if(pageEnableListener != null){
isLoading = true; //执行之后的状态就是loading
pageEnableListener.onLoadMoreItems(); //调用回调方法
}
}
} @Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totleItem) {
//Dispatch to child OnScrollListener
if (onScrollListener != null) {
onScrollListener.onScroll(absListView, firstVisibleItem, visibleItemCount, totleItem);
}
lastVisibleItem = firstVisibleItem + visibleItemCount; //最后看到的一项
totalItemCount = totleItem; //总的项数
}
}); } @Override
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
} public interface PageEnableListener{
public void onLoadMoreItems();
} }

我们可以看到还要加一个loadingview的,就是你的正在加载界面,这个界面会被动态添加到你的footview里面的:

 package njucm.edu.loadmore.activities;

 import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout; import njucm.edu.loadmore.R; /**
* Created by Mesogene on 10/10/15.
*/
public class LoadingView extends LinearLayout { public LoadingView(Context context) {
super(context);
init();
} public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} public void init(){
inflate(getContext(), R.layout.loadinflinear, this);
} }

只是简单重写了一下LinearLayout, 下面只要写一个loading的不局文件就行了

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_margin="@dimen/dp_10"
android:id="@+id/loading_view"> <ProgressBar
android:id="@+id/video_item_image"
style="?android:progressBarStyle"
android:layout_width="@dimen/loading_view_progress_size"
android:layout_height="@dimen/loading_view_progress_size"
android:layout_marginRight="@dimen/loading_view_margin_right"
android:indeterminate="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loading"
android:layout_gravity="center"
android:textColor="@color/material_blue_grey_800"
android:textSize="@dimen/sp_18"/> </LinearLayout>

这样基本就已经好了,那么还有一个listview的父类的适配器要自己写一下:

 import android.widget.BaseAdapter;
import android.widget.ListAdapter; import java.util.ArrayList;
import java.util.List; import njucm.edu.loadmore.LoadListView; /**
* Created by Mesogene on 10/12/15.
*/
public abstract class PagingBaseAdapter<T> extends BaseAdapter { protected List<T> items = null; public PagingBaseAdapter(List<T> items) {
this.items = items;
} public PagingBaseAdapter(){
this.items = new ArrayList<>();
} public void addMoreItems(List<T> items){
this.items.addAll(items); //把新的项添加到listview里面
notifyDataSetChanged(); //更新布局
} }

这样之后,你自己的listviewAdapter就可以继承这个类,你的adapter拥有绘制每一个listitem的功能和添加下一页数据项的功能。

 package njucm.edu.loadmore.adapters;

 import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; import java.util.List; /**
* Created by Mesogene on 10/12/15.
*/
public class MyListAdapter extends PagingBaseAdapter<String> { @Override
public int getCount() {
return items.size();
} @Override
public String getItem(int position) {
return items.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View view, ViewGroup viewGroup) {
TextView textView;
String text = getItem(position);
if(view != null){
textView = (TextView) view;
}else {
textView = (TextView) LayoutInflater.from(viewGroup.getContext()).inflate(android.R.layout.simple_list_item_1, null);
}
textView.setText(text);
return textView;
}
}

最后就是如何使用了,再onCreate() 或者 onCreateView() 添加如下代码,我们发现它有一个异步加载的过程,使用到了线程

 li.setAdapter(adapter);
li.setHasMoreItem(true);
li.setPageEnableListener(new LoadListView.PageEnableListener() {
@Override
public void onLoadMoreItems() {
if(pager < 3){
new CountryAsyncTask().execute();
}else{
li.onFinsihLoading(false, null);
}
}
});
 private class CountryAsyncTask extends SafeAsyncTask<List<String>>{
@Override
public List<String> call() throws Exception { //模拟后台下载数据
List result = null;
switch (pager){
case 0:
result = firstList;
break;
case 1:
result = secondList;
break;
case 2:
result = thirdList;
break;
}
Thread.sleep(3000);
return result;
} @Override
protected void onSuccess(List<String> strings) throws Exception {
super.onSuccess(strings);
pager++;
li.onFinsihLoading(true, strings); //下载成功之后调用的方法,更新UI
}
}

这里面可能要自己添加一些数据在firstlist等里面。  还有下面是这个类似于AsyncTask但又不是的,这个类的代码如下

 package njucm.edu.loadmore.activities;

 import android.os.Handler;
import android.os.Looper;
import android.util.Log; import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; /**
* A class similar but unrelated to android's {@link android.os.AsyncTask}.
* <p/>
* Unlike AsyncTask, this class properly propagates exceptions.
* <p/>
* If you're familiar with AsyncTask and are looking for {@link android.os.AsyncTask#doInBackground(Object[])},
* we've named it {@link #call()} here to conform with java 1.5's {@link java.util.concurrent.Callable} interface.
* <p/>
* Current limitations: does not yet handle progress, although it shouldn't be
* hard to add.
* <p/>
* If using your own executor, you must call future() to get a runnable you can execute.
*
* @param <ResultT>
*/
public abstract class SafeAsyncTask<ResultT> implements Callable<ResultT> { //Callable可以返回任意类型。1.5 以后加入
public static final int DEFAULT_POOL_SIZE = 25; //默认的线程池的大小是25
protected static final Executor DEFAULT_EXECUTOR = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE); //利用线程池 protected Handler handler;
protected Executor executor;
protected StackTraceElement[] launchLocation;
protected FutureTask<Void> future; /**
* Sets executor to Executors.newFixedThreadPool(DEFAULT_POOL_SIZE) and
* Handler to new Handler()
*/
public SafeAsyncTask() {
this.executor = DEFAULT_EXECUTOR;
} /**
* Sets executor to Executors.newFixedThreadPool(DEFAULT_POOL_SIZE)
*/
public SafeAsyncTask(Handler handler) {
this.handler = handler;
this.executor = DEFAULT_EXECUTOR; //线程池用默认的设置
} /**
* Sets Handler to new Handler()
*/
public SafeAsyncTask(Executor executor) {
this.executor = executor;
} public SafeAsyncTask(Handler handler, Executor executor) {
this.handler = handler;
this.executor = executor;
} public FutureTask<Void> future() {
future = new FutureTask<Void>(newTask());
return future;
} public SafeAsyncTask<ResultT> executor(Executor executor) {
this.executor = executor;
return this;
} public Executor executor() {
return executor;
} public SafeAsyncTask<ResultT> handler(Handler handler) {
this.handler = handler;
return this;
} public Handler handler() {
return handler;
} public void execute() {
execute(Thread.currentThread().getStackTrace());
} protected void execute(StackTraceElement[] launchLocation) {
this.launchLocation = launchLocation;
executor.execute(future());
} public boolean cancel(boolean mayInterruptIfRunning) {
if (future == null) throw new UnsupportedOperationException("You cannot cancel this task before calling future()"); return future.cancel(mayInterruptIfRunning);
} /**
* @throws Exception, captured on passed to onException() if present.
*/
protected void onPreExecute() throws Exception {
} /**
* @param t the result of {@link #call()}
* @throws Exception, captured on passed to onException() if present.
*/
@SuppressWarnings({"UnusedDeclaration"})
protected void onSuccess(ResultT t) throws Exception {
} /**
* Called when the thread has been interrupted, likely because
* the task was canceled.
* <p/>
* By default, calls {@link #onException(Exception)}, but this method
* may be overridden to handle interruptions differently than other
* exceptions.
*
* @param e an InterruptedException or InterruptedIOException
*/
protected void onInterrupted(Exception e) {
onException(e);
} /**
* Logs the exception as an Error by default, but this method may
* be overridden by subclasses.
*
* @param e the exception thrown from {@link #onPreExecute()}, {@link #call()}, or {@link #onSuccess(Object)}
* @throws RuntimeException, ignored
*/
protected void onException(Exception e) throws RuntimeException {
onThrowable(e);
} protected void onThrowable(Throwable t) throws RuntimeException {
Log.e("roboguice", "Throwable caught during background processing", t);
} /**
* @throws RuntimeException, ignored
*/
protected void onFinally() throws RuntimeException {
} protected Task<ResultT> newTask() {
return new Task<ResultT>(this);
} public static class Task<ResultT> implements Callable<Void> {
protected SafeAsyncTask<ResultT> parent;
protected Handler handler; public Task(SafeAsyncTask<ResultT> parent) {
this.parent = parent;
this.handler = parent.handler != null ? parent.handler : new Handler(Looper.getMainLooper());
} public Void call() throws Exception {
try {
doPreExecute();
doSuccess(doCall()); } catch (final Exception e) {
try {
doException(e);
} catch (Exception f) {
// logged but ignored
Log.e("BACKGROUND_TASK", "Exception in", f);
} } catch (final Throwable t) {
try {
doThrowable(t);
} catch (Exception f) {
// logged but ignored
Log.e("BACKGROUND_TASK", "Exception in", f);
}
} finally {
doFinally();
} return null;
} protected void doPreExecute() throws Exception {
postToUiThreadAndWait(new Callable<Object>() {
public Object call() throws Exception {
parent.onPreExecute();
return null;
}
});
} protected ResultT doCall() throws Exception {
return parent.call();
} protected void doSuccess(final ResultT r) throws Exception {
postToUiThreadAndWait(new Callable<Object>() {
public Object call() throws Exception {
parent.onSuccess(r);
return null;
}
});
} protected void doException(final Exception e) throws Exception {
if (parent.launchLocation != null) {
final ArrayList<StackTraceElement> stack = new ArrayList<StackTraceElement>(Arrays.asList(e.getStackTrace()));
stack.addAll(Arrays.asList(parent.launchLocation));
e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()]));
}
postToUiThreadAndWait(new Callable<Object>() {
public Object call() throws Exception {
if (e instanceof InterruptedException || e instanceof InterruptedIOException) parent.onInterrupted(e);
else parent.onException(e);
return null;
}
});
} protected void doThrowable(final Throwable e) throws Exception {
if (parent.launchLocation != null) {
final ArrayList<StackTraceElement> stack = new ArrayList<StackTraceElement>(Arrays.asList(e.getStackTrace()));
stack.addAll(Arrays.asList(parent.launchLocation));
e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()]));
}
postToUiThreadAndWait(new Callable<Object>() {
public Object call() throws Exception {
parent.onThrowable(e);
return null;
}
});
} protected void doFinally() throws Exception {
postToUiThreadAndWait(new Callable<Object>() {
public Object call() throws Exception {
parent.onFinally();
return null;
}
});
} /**
* Posts the specified runnable to the UI thread using a handler,
* and waits for operation to finish. If there's an exception,
* it captures it and rethrows it.
*
* @param c the callable to post
* @throws Exception on error
*/
protected void postToUiThreadAndWait(final Callable c) throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final Exception[] exceptions = new Exception[1]; // Execute onSuccess in the UI thread, but wait
// for it to complete.
// If it throws an exception, capture that exception
// and rethrow it later.
handler.post(new Runnable() {
public void run() {
try {
c.call();
} catch (Exception e) {
exceptions[0] = e;
} finally {
latch.countDown();
}
}
}); // Wait for onSuccess to finish
latch.await(); if (exceptions[0] != null) throw exceptions[0]; } } }

好了,最简单的上拉加载就是这个样子了。我已经把它集成进了自己的项目里面。

Android 上拉加载更多功能的更多相关文章

  1. Android 开发 上拉加载更多功能实现

    实现思维 开始之前先废话几句,Android系统没有提供上拉加载的控件,只提供了下拉刷新的SwipeRefreshLayout控件.这个控件我们就不废话,无法实现上拉刷新的功能.现在我们说说上拉加载更 ...

  2. Flutter移动电商实战 --(20)首页上拉加载更多功能的制作

    这节课学习一下上拉加载效果,其实现在上拉加载的插件有很多,但是还没有一个插件可以说完全一枝独秀,我也找了一个插件,这个插件的优点就是服务比较好,作者能及时回答大家的问题.我觉的选插件也是选人,人对了, ...

  3. android ListView上拉加载更多 下拉刷新功能实现(采用pull-to-refresh)

    Android实现上拉加载更多功能以及下拉刷新功能, 采用了目前比较火的PullToRefresh,他是目前实现比较好的下拉刷新的类库. 目前他支持的控件有:ListView, ExpandableL ...

  4. Android5.0新特性:RecyclerView实现上拉加载更多

    RecyclerView是Android5.0以后推出的新控件,相比于ListView可定制性更大,大有取代ListView之势.下面这篇博客主要来实现RecyclerView的上拉加载更多功能. 基 ...

  5. SwipeRefreshLayout详解和自定义上拉加载更多

    个人主页 演示Demo下载 本文重点介绍了SwipeRefreshLayout的使用和自定View继承SwipeRefreshLayout添加上拉加载更多的功能. 介绍之前,先来看一下SwipeRef ...

  6. 实现上拉加载更多的SwipeRefreshLayout

    转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息] ...

  7. 自定义ListView下拉刷新上拉加载更多

    自定义ListView下拉刷新上拉加载更多 自定义RecyclerView下拉刷新上拉加载更多 Listview现在用的很少了,基本都是使用Recycleview,但是不得不说Listview具有划时 ...

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

    ListView是Android中一个功能强大而且很常用的控件,在很多App中都有ListView的下拉刷新数据和上拉加载更多这个功能.这里我就简单记录一下实现过程. 实现这个功能的方法不止一个,Gi ...

  9. android ListView下拉刷新 上拉加载更多

    背景 最近在公司的项目中要使用到ListView的下拉刷新和上拉加载更多(貌似现在是个项目就有这个功能!哈哈),其实这个东西GitHub上很多,但是我感觉那些框架太大,而且我这个项目只用到了ListV ...

随机推荐

  1. P127、面试题20:顺时针打印矩阵

    题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字.例如:如果输入如下矩阵:1  2  3  4 5  6  7  89  10  11  1213  14  15  16则依次打印出 ...

  2. 使用dreamever去掉文件头部BOM(bom)信息 From 百度经验

    本文来此百度经验: 地址为:http://jingyan.baidu.com/article/3f16e003c3dc172591c103e6.html OM主要处理浏览器窗口与框架,但事实上,浏览器 ...

  3. VS2005下开发PPC2003和WM50编译器一些设置

    1.vs2005开发WM5时,编译器和linker的选项配合问题 链接:http://www.mivi.name/blog/index_en.php?itemid=258 首先说ARM4 ARM4T ...

  4. 【HDOJ】4579 Random Walk

    1. 题目描述一个人沿着一条长度为n个链行走,给出了每秒钟由i到j的概率($i,j \in [1,n]$).求从1开始走到n个时间的期望. 2. 基本思路显然是个DP.公式推导也相当容易.不妨设$dp ...

  5. git clone 出错SSL certificate problem, verify that the CA cert is OK.

    先调用这个 export GIT_SSL_NO_VERIFY=true 之后再执行git clone

  6. 转载:C++ vector 类学习笔记

    声明:本文转载自http://blog.csdn.net/whz_zb/article/details/6827999 vector简介 vector是STL中最常见的容器,它是一种顺序容器,支持随机 ...

  7. 宏HASH_INSERT

    调用 方法 HASH_INSERT(lock_t, hash, lock_sys->rec_hash,lock_rec_fold(space, page_no), lock); /******* ...

  8. UVa 10048 (Floyd变形) Audiophobia

    题意: 给一个带权无向图,和一些询问,每次询问两个点之间最大权的最小路径. 分析: 紫书上的题解是错误的,应该是把原算法中的加号变成max即可.但推理过程还是类似的,如果理解了Floyd算法的话,这个 ...

  9. apache开源项目--PDFBox

    PDFBox是Java实现的PDF文档协作类库,提供PDF文档的创建.处理以及文档内容提取功能,也包含了一些命令行实用工具. 主要特性包括: 从PDF提取文本 合并PDF文档 PDF 文档加密与解密 ...

  10. Mysql加密方式

    MySQL数据库的认证密码有两种方式, MySQL 4.1版本之前是MySQL323加密,MySQL 4.1和之后的版本都是MySQLSHA1加密, MySQL数据库中自带Old_Password(s ...