当前位置: 移动技术网 > IT编程>移动开发>Android > Android ListView分页功能实现方法

Android ListView分页功能实现方法

2019年07月24日  | 移动技术网IT编程  | 我要评论

青铜巨龙圣地,花花门生下载,妙高山

通过本次小demo我学到了:

1、listview的小小的一个分页功能
2、加深了对自定义控件的理解
3、对listview的优化
4、对baseadapter的使用
5、自定义adapter
6、接口的回调

要实现下面的效果--当拖动listview到底部的时候,显示一个progressbar和一个"正在加载..."的textview。并且过两秒钟后,在下面加载出新的数据。项目的目录结构和程序要实现的效果如下:

                 

首先是布局部分:

我为了实现此效果,首先在布局文件中新建了一个footer_layout.xml的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >
 <linearlayout 
  android:id="@+id/load_layout"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="horizontal"
  android:paddingtop="10dip"
  android:paddingbottom="10dip"
  android:gravity="center"
  >
  <progressbar 
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   style="?android:attr/progressbarstylesmall"
   android:background="#ff0000"
   />
  <textview 
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="正在加载..."
   />
  
 </linearlayout>

</linearlayout>

然后新建了一个item.xml用于作为listview的子项:

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >

 <textview
  android:id="@+id/tv1"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="哈哈哈" />
 <textview 
  android:id="@+id/tv2"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="嘎嘎嘎嘎嘎"
 />
</linearlayout>

最后在主布局文件中添加了一个自定义的listview控件:

<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="match_parent"
 >

 <com.lx.loadlistview.loadlistview
  android:id="@+id/list"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_alignparenttop="true"
  android:layout_centerhorizontal="true"
  android:cachecolorhint="#00000000" >
 </com.lx.loadlistview.loadlistview>

</relativelayout>

然后为了实现listview的这种效果,我们需要一个自定义的listview,并在上面的布局文件中引用我们自定义的listview,代码如下:

package com.lx.loadlistview;

import com.example.listviewloaddemo.r;

import android.content.context;
import android.util.attributeset;
import android.view.layoutinflater;
import android.view.view;
import android.widget.abslistview;
import android.widget.listview;
import android.widget.abslistview.onscrolllistener;

public class loadlistview extends listview implements onscrolllistener {

 view footer;
 int lastvisiableitem;// 最后一个可见的item
 int totalitemcount;// item的总数量
 boolean isloading; // 正在加载
 iloadlistener iloadlistener;

 public loadlistview(context context, attributeset attrs, int defstyle) {
  super(context, attrs, defstyle);
  // todo 自动生成的构造函数存根
  initview(context);
 }

 public loadlistview(context context, attributeset attrs) {
  super(context, attrs);
  // todo 自动生成的构造函数存根
  initview(context);
 }

 public loadlistview(context context) {
  super(context);
  // todo 自动生成的构造函数存根
  initview(context);
 }

 /***
  * 添加底部提示加载布局到listview
  * 
  * @param context
  */
 public void initview(context context) {
  layoutinflater inflater = layoutinflater.from(context);
  footer = inflater.inflate(r.layout.footer_layout, null);
  // 初始时候让底部布局不可见
  footer.findviewbyid(r.id.load_layout).setvisibility(view.gone);
  this.addfooterview(footer);
  this.setonscrolllistener(this);
 }

 @override
 public void onscrollstatechanged(abslistview view, int scrollstate) {
  // todo 自动生成的方法存根
  // 当总共的item数量等于最后一个item的位置,并且滚动停止时
  if (totalitemcount == lastvisiableitem
    && scrollstate == scroll_state_idle) {
   if (!isloading) {
    isloading = true;
    footer.findviewbyid(r.id.load_layout).setvisibility(
      view.visible);
    //加载更多
    iloadlistener.onload();
   }
  }
 }
 
 /**
 *firstvisibleitem 第一个可见item的位置
 *visibleitemcount 可见的item的数量
 *totalitemcount  item的总数量
 **/
 @override
 public void onscroll(abslistview view, int firstvisibleitem,
   int visibleitemcount, int totalitemcount) {
  // todo 自动生成的方法存根
  this.lastvisiableitem = firstvisibleitem + visibleitemcount;
  this.totalitemcount = totalitemcount;
 }

 //加载完毕将footer隐藏
 public void loadcomplete(){
  isloading=false;
  footer.findviewbyid(r.id.load_layout).setvisibility(view.gone);
 }
 
 public void setinterface(iloadlistener iloadlistener) {
  this.iloadlistener = iloadlistener;
 }

 //加载更多数据回调接口
 public interface iloadlistener {
  public void onload();
 }

}

我们自定义的listview继承自listview,并实现其中父类的三个构造方法,为了将底部我们想要的布局加载到listview中来,我们自定义了一个initview方法,用于找到并实例化footer_layout.xml使其添加到listview底部。在父类的三个构造方法中添加初始化方法initview(),在initview方法的最后还要调用listview的addfooterview(view)方法,将底部布局add进来。由于在listview刚加载进来的时候我们不想显示这个footer,所以要设置它的visible为gone。想要实现listview拉到底部的时候才显示footer,要实现listview的onscrolllistener接口,并实现其父类中的两个方法。在onscrollstatechanged()方法中判断是否滚动到底部(我们定义了一个全局变量lastvisibleitem=firstvisibleitem+visibleitemcount,若此值和totalitemcount相等,则证明滚动到listview的底端了)和此时listview是否停止滚动(scrollstate=scroll_state_idle)。

为了向listview中添加数据我们定义了一个实体类apk_entity:

package com.lx.entity;

public class apkentity {

 private string name;
 private string info;
 public string getname() {
  return name;
 }
 public void setname(string name) {
  this.name = name;
 }
 public string getinfo() {
  return info;
 }
 public void setinfo(string info) {
  this.info = info;
 }
 
}

之后我们为listview定义了一个数据适配器myadapter,继承自baseadapter,并实现其中的四个方法,在其中我们主要实现数据的填充:

package com.lx.adapter;

import java.util.arraylist;

import com.example.listviewloaddemo.r;
import com.lx.entity.apkentity;


import android.content.context;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import android.widget.baseadapter;
import android.widget.textview;

public class myadapter extends baseadapter {

 arraylist<apkentity> list;
 layoutinflater inflater;
 
 
 //构造函数中传入了list列表项和初始化layoutinflater
 public myadapter(context context,arraylist<apkentity> list) {
  this.list=list;
  this.inflater=layoutinflater.from(context);
 }

 //得到list的长度(是程序在加载显示到ui上是就要先读取的,这里获得的值决定了listview显示多少行)
 @override
 public int getcount() {
  // todo 自动生成的方法存根
  return list.size();
 }

 //得到list中指定位置的data(根据listview所在的位置返回view)
 @override
 public object getitem(int position) {
  // todo 自动生成的方法存根
  return list.get(position);
 }
 
 //根据listview位置得到数据源集合中的id
 @override
 public long getitemid(int position) {
  // todo 自动生成的方法存根
  return position;
 }

 //***最主要,决定listview的界面样式
 @override
 public view getview(int position, view convertview, viewgroup parent) {
  // todo 自动生成的方法存根
  //从list中获取实体
  apkentity entity=list.get(position);
  //使用viewholder的目的是为了使每次在getview的时候不是每次都findviewbyid()来获取控件实例
  viewholder holder;
  /**
   * convertview:the old view to reuses
   * 用于将之前加载好的布局缓存,以便之后可以重用
   */
  //为了避免重复加载布局,仅仅在convertview为空的时候才使用layoutinflate加载布局
  if(convertview==null){
   holder=new viewholder();
   //找到并将layout转换为view
   convertview=inflater.inflate(r.layout.item, null);
   holder.tv_name=(textview) convertview.findviewbyid(r.id.tv1);
   holder.tv_info=(textview) convertview.findviewbyid(r.id.tv2);
   convertview.settag(holder);
  }else{
   holder=(viewholder) convertview.gettag();
  }
  holder.tv_name.settext(entity.getname());
  holder.tv_info.settext(entity.getinfo());
  return convertview;
 }
 
 class viewholder{
  textview tv_name,tv_info;
 }
 
 //布局改变时用来刷新listview
 public void ondatechanged(arraylist<apkentity> list){
  this.list=list;
  this.notifydatasetchanged();
 }

}

在这个自定义adapter中最主要的就是getview()方法,它决定了listview的每项的布局(长什么样),在getview()方法中,为了优化listview的运行效率,使得不是每次item创建的时候都要findviewbyid()来实例化控件,我们定义了一个viewholder的内部类,用来对控件的实例进行缓存,在类中声明了item布局中的布局控件。因为getview()方法每次都将布局重新加载了一遍,所以在listview快速滚动的时候就会成为性能的瓶颈。所以用到了getview()方法中的convertview参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以重新使用。通过上面的代码可以看到,如果convertview 为空的时候,我们就使用layoutinflate加载布局并实例化item中的控件,还要调用view的settag()方法,将viewholder对象存储在convertviewu 中。这样,当convertview不为空的时候,则直接调用view的gettag()方法,把viewholder直接取出,这样所有的控件的实例都缓存在了viewholder里,就没有必要每次都对控件进行findviewbyid()了。

1.使用convertview参数:避免重复加载布局,用他来对之前加载过的布局进行缓存。

2.使用viewholder:避免每次getview()的时候都对控件进行实例化,用这个类完成对控件实例化的缓存。

然后我们需要完成在mainactivity中对loadlistview的实例化,和mydapter的实例化,向实体类中添加数据并使adapter和listview适配完成填充数据:

package com.example.listviewloaddemo;

import java.util.arraylist;
import java.util.hashmap;
import java.util.list;
import java.util.map;

import com.lx.adapter.myadapter;
import com.lx.entity.apkentity;
import com.lx.loadlistview.loadlistview;
import com.lx.loadlistview.loadlistview.iloadlistener;

import android.os.bundle;
import android.os.handler;
import android.app.activity;
import android.util.log;
import android.view.menu;
import android.widget.baseadapter;
import android.widget.listview;
import android.widget.simpleadapter;

public class mainactivity extends activity implements iloadlistener {

 private loadlistview lv;
 private arraylist<apkentity> list=new arraylist<apkentity>();
 private myadapter myadapter;
 @override
 protected void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);
  getdate();
  showlistview(list);
  
 }

 private void getdate() {
  // todo 自动生成的方法存根
  for (int i = 0; i < 10; i++) {
   apkentity entity=new apkentity();
   entity.setname("大毛");
   entity.setinfo("我是一只pig");
   list.add(entity);
  } 
 }
 
 private void getonloaddate() {
  // todo 自动生成的方法存根
  for (int i = 0; i < 2; i++) {
   apkentity entity=new apkentity();
   entity.setname("小毛");
   entity.setinfo("我是一只dog");
   list.add(entity);
  } 
 }

 private void showlistview(arraylist<apkentity> list) { 
  if(myadapter==null){ 
   lv = (loadlistview) findviewbyid(r.id.list);
   lv.setinterface(this);
   log.d("setinterface--->>", this.tostring());
   myadapter=new myadapter(this, list);
   lv.setadapter(myadapter);
  }else{
   myadapter.ondatechanged(list);
  }
 }

 @override
 public void onload() {
  // todo 自动生成的方法存根
  //用现线程来控制隔多少秒之后获取数据,然后设置到listview上(正常情况下不需要加,只是为了看出来这个延时的效果)
  handler handler=new handler();
  handler.postdelayed(new runnable() { 
   @override
   public void run() {
    // todo 自动生成的方法存根
    getonloaddate();
    showlistview(list);
    //通知listview加载完毕
    lv.loadcomplete();
   }
  }, 2000); 
 }

}

mainactivity中主要需要注意的就是showlistview()方法,在该方法中我们判断了一下adapter是否为空,若adapter为空,则实例化listview,实例化adapter等等一系列操作,否则调用myadapter的ondatechanged()方法(此方法中调用了adapter的notifydatasetchanged()方法此方法用于listview发生变化时更新ui)。由于要在监听到滑动到listview底部的时候加载新的数据,所以在loadlistview类中实现一个队mainactivoity的回调,在loadlistview中写一个回调接口iloadlistener(),在其中实现一个onload()方法,在mainactivity中实现这个接口,重写onload()方法,在其中 实现想要实现的其他方法,比如新数据的加载和ui的刷新展示,最后,刷新加载完新的数据后,要将footer隐藏,所以执行loadlistview中的loadcomplete()方法。

至此,整个小demo的学习基本完成,其中还有些知识不太懂,比如说接口的回调,自定义控件部分等等,还需要加深练习。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网