zoukankan      html  css  js  c++  java
  • Android学习随笔ListView的分页功能

    第一次写博客,可能格式,排版什么的会非常不美观,不过我主要是为了记录自己的Android学习之路,为了以后能有些东西回顾。既然是为了学习,那我肯定会吸收各位大大们的知道经验,有不足的地方请指出。

    通过本次小Demo我学到了:

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

    本次我是通过慕课网(视频链接:http://www.imooc.com/learn/136)学习,要实现下面的效果--当拖动ListView到底部的时候,显示一个ProgressBar和一个"正在加载..."的TextView。并且过两秒钟后,在下面加载出新的数据。项目的目录结构和程序要实现的效果如下:

                     

    首先是布局部分:

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

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" >
     6     <LinearLayout 
     7         android:id="@+id/load_layout"
     8         android:layout_width="match_parent"
     9         android:layout_height="wrap_content"
    10         android:orientation="horizontal"
    11         android:paddingTop="10dip"
    12         android:paddingBottom="10dip"
    13         android:gravity="center"
    14         >
    15         <ProgressBar 
    16             android:layout_width="wrap_content"
    17             android:layout_height="wrap_content"
    18             style="?android:attr/progressBarStyleSmall"
    19             android:background="#ff0000"
    20             />
    21         <TextView 
    22             android:layout_width="wrap_content"
    23             android:layout_height="wrap_content"
    24             android:text="正在加载..."
    25             />
    26         
    27     </LinearLayout>
    28 
    29 </LinearLayout>

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

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" >
     6 
     7     <TextView
     8         android:id="@+id/tv1"
     9         android:layout_width="wrap_content"
    10         android:layout_height="wrap_content"
    11         android:text="哈哈哈" />
    12     <TextView 
    13         android:id="@+id/tv2"
    14         android:layout_width="wrap_content"
    15         android:layout_height="wrap_content"
    16         android:text="嘎嘎嘎嘎嘎"
    17     />
    18 </LinearLayout>

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

     1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     >
     6 
     7     <com.lx.loadListView.LoadListView
     8         android:id="@+id/list"
     9         android:layout_width="match_parent"
    10         android:layout_height="wrap_content"
    11         android:layout_alignParentTop="true"
    12         android:layout_centerHorizontal="true"
    13         android:cacheColorHint="#00000000" >
    14     </com.lx.loadListView.LoadListView>
    15 
    16 </RelativeLayout>

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

     1 package com.lx.loadListView;
     2 
     3 import com.example.listviewloaddemo.R;
     4 
     5 import android.content.Context;
     6 import android.util.AttributeSet;
     7 import android.view.LayoutInflater;
     8 import android.view.View;
     9 import android.widget.AbsListView;
    10 import android.widget.ListView;
    11 import android.widget.AbsListView.OnScrollListener;
    12 
    13 public class LoadListView extends ListView implements OnScrollListener {
    14 
    15     View footer;
    16     int lastVisiableItem;// 最后一个可见的Item
    17     int totalItemCount;// Item的总数量
    18     boolean isLoading; // 正在加载
    19     ILoadListener iLoadListener;
    20 
    21     public LoadListView(Context context, AttributeSet attrs, int defStyle) {
    22         super(context, attrs, defStyle);
    23         // TODO 自动生成的构造函数存根
    24         initView(context);
    25     }
    26 
    27     public LoadListView(Context context, AttributeSet attrs) {
    28         super(context, attrs);
    29         // TODO 自动生成的构造函数存根
    30         initView(context);
    31     }
    32 
    33     public LoadListView(Context context) {
    34         super(context);
    35         // TODO 自动生成的构造函数存根
    36         initView(context);
    37     }
    38 
    39     /***
    40      * 添加底部提示加载布局到listView
    41      * 
    42      * @param context
    43      */
    44     public void initView(Context context) {
    45         LayoutInflater inflater = LayoutInflater.from(context);
    46         footer = inflater.inflate(R.layout.footer_layout, null);
    47         // 初始时候让底部布局不可见
    48         footer.findViewById(R.id.load_layout).setVisibility(View.GONE);
    49         this.addFooterView(footer);
    50         this.setOnScrollListener(this);
    51     }
    52 
    53     @Override
    54     public void onScrollStateChanged(AbsListView view, int scrollState) {
    55         // TODO 自动生成的方法存根
    56         // 当总共的Item数量等于最后一个Item的位置,并且滚动停止时
    57         if (totalItemCount == lastVisiableItem
    58                 && scrollState == SCROLL_STATE_IDLE) {
    59             if (!isLoading) {
    60                 isLoading = true;
    61                 footer.findViewById(R.id.load_layout).setVisibility(
    62                         View.VISIBLE);
    63                 //加载更多
    64                 iLoadListener.onLoad();
    65             }
    66         }
    67     }
    68     
    69     /**
    70     *firstVisibleItem  第一个可见Item的位置
    71     *visibleItemCount  可见的Item的数量
    72     *totalItemCount       Item的总数量
    73     **/
    74     @Override
    75     public void onScroll(AbsListView view, int firstVisibleItem,
    76             int visibleItemCount, int totalItemCount) {
    77         // TODO 自动生成的方法存根
    78         this.lastVisiableItem = firstVisibleItem + visibleItemCount;
    79         this.totalItemCount = totalItemCount;
    80     }
    81 
    82     //加载完毕将footer隐藏
    83     public void loadComplete(){
    84         isLoading=false;
    85         footer.findViewById(R.id.load_layout).setVisibility(View.GONE);
    86     }
    87     
    88     public void setInterface(ILoadListener iLoadListener) {
    89         this.iLoadListener = iLoadListener;
    90     }
    91 
    92     //加载更多数据回调接口
    93     public interface ILoadListener {
    94         public void onLoad();
    95     }
    96 
    97 }

    我们自定义的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:

     1 package com.lx.entity;
     2 
     3 public class ApkEntity {
     4 
     5     private String name;
     6     private String info;
     7     public String getName() {
     8         return name;
     9     }
    10     public void setName(String name) {
    11         this.name = name;
    12     }
    13     public String getInfo() {
    14         return info;
    15     }
    16     public void setInfo(String info) {
    17         this.info = info;
    18     }
    19     
    20 }
    Apk_entity

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

     1 package com.lx.adapter;
     2 
     3 import java.util.ArrayList;
     4 
     5 import com.example.listviewloaddemo.R;
     6 import com.lx.entity.ApkEntity;
     7 
     8 
     9 import android.content.Context;
    10 import android.view.LayoutInflater;
    11 import android.view.View;
    12 import android.view.ViewGroup;
    13 import android.widget.BaseAdapter;
    14 import android.widget.TextView;
    15 
    16 public class MyAdapter extends BaseAdapter {
    17 
    18     ArrayList<ApkEntity> list;
    19     LayoutInflater inflater;
    20     
    21     
    22     //构造函数中传入了list列表项和初始化LayoutInflater
    23     public MyAdapter(Context context,ArrayList<ApkEntity> list) {
    24         this.list=list;
    25         this.inflater=LayoutInflater.from(context);
    26     }
    27 
    28     //得到list的长度(是程序在加载显示到UI上是就要先读取的,这里获得的值决定了ListView显示多少行)
    29     @Override
    30     public int getCount() {
    31         // TODO 自动生成的方法存根
    32         return list.size();
    33     }
    34 
    35     //得到list中指定位置的data(根据ListView所在的位置返回View)
    36     @Override
    37     public Object getItem(int position) {
    38         // TODO 自动生成的方法存根
    39         return list.get(position);
    40     }
    41     
    42     //根据ListView位置得到数据源集合中的ID
    43     @Override
    44     public long getItemId(int position) {
    45         // TODO 自动生成的方法存根
    46         return position;
    47     }
    48 
    49     //***最主要,决定ListView的界面样式
    50     @Override
    51     public View getView(int position, View convertView, ViewGroup parent) {
    52         // TODO 自动生成的方法存根
    53         //从list中获取实体
    54         ApkEntity entity=list.get(position);
    55         //使用ViewHolder的目的是为了使每次在getView的时候不是每次都findViewById()来获取控件实例
    56         ViewHolder holder;
    57         /**
    58          * convertView:The old View to reuses
    59          * 用于将之前加载好的布局缓存,以便之后可以重用
    60          */
    61         //为了避免重复加载布局,仅仅在convertView为空的时候才使用LayoutInflate加载布局
    62         if(convertView==null){
    63             holder=new ViewHolder();
    64             //找到并将layout转换为View
    65             convertView=inflater.inflate(R.layout.item, null);
    66             holder.tv_name=(TextView) convertView.findViewById(R.id.tv1);
    67             holder.tv_info=(TextView) convertView.findViewById(R.id.tv2);
    68             convertView.setTag(holder);
    69         }else{
    70             holder=(ViewHolder) convertView.getTag();
    71         }
    72         holder.tv_name.setText(entity.getName());
    73         holder.tv_info.setText(entity.getInfo());
    74         return convertView;
    75     }
    76     
    77     class ViewHolder{
    78         TextView tv_name,tv_info;
    79     }
    80     
    81     //布局改变时用来刷新ListView
    82     public void onDateChanged(ArrayList<ApkEntity> list){
    83         this.list=list;
    84         this.notifyDataSetChanged();
    85     }
    86 
    87 }

    在这个自定义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适配完成填充数据:

     1 package com.example.listviewloaddemo;
     2 
     3 import java.util.ArrayList;
     4 import java.util.HashMap;
     5 import java.util.List;
     6 import java.util.Map;
     7 
     8 import com.lx.adapter.MyAdapter;
     9 import com.lx.entity.ApkEntity;
    10 import com.lx.loadListView.LoadListView;
    11 import com.lx.loadListView.LoadListView.ILoadListener;
    12 
    13 import android.os.Bundle;
    14 import android.os.Handler;
    15 import android.app.Activity;
    16 import android.util.Log;
    17 import android.view.Menu;
    18 import android.widget.BaseAdapter;
    19 import android.widget.ListView;
    20 import android.widget.SimpleAdapter;
    21 
    22 public class MainActivity extends Activity implements ILoadListener {
    23 
    24     private LoadListView lv;
    25     private ArrayList<ApkEntity> list=new ArrayList<ApkEntity>();
    26     private MyAdapter myAdapter;
    27     @Override
    28     protected void onCreate(Bundle savedInstanceState) {
    29         super.onCreate(savedInstanceState);
    30         setContentView(R.layout.activity_main);
    31         getDate();
    32         showListView(list);
    33         
    34     }
    35 
    36     private void getDate() {
    37         // TODO 自动生成的方法存根
    38         for (int i = 0; i < 10; i++) {
    39             ApkEntity entity=new ApkEntity();
    40             entity.setName("大毛");
    41             entity.setInfo("我是一只pig");
    42             list.add(entity);
    43         }    
    44     }
    45     
    46     private void getOnLoadDate() {
    47         // TODO 自动生成的方法存根
    48         for (int i = 0; i < 2; i++) {
    49             ApkEntity entity=new ApkEntity();
    50             entity.setName("小毛");
    51             entity.setInfo("我是一只dog");
    52             list.add(entity);
    53         }    
    54     }
    55 
    56     private void showListView(ArrayList<ApkEntity> list) {    
    57         if(myAdapter==null){    
    58             lv = (LoadListView) findViewById(R.id.list);
    59             lv.setInterface(this);
    60             Log.d("SetInterface--->>", this.toString());
    61             myAdapter=new MyAdapter(this, list);
    62             lv.setAdapter(myAdapter);
    63         }else{
    64             myAdapter.onDateChanged(list);
    65         }
    66     }
    67 
    68     @Override
    69     public void onLoad() {
    70         // TODO 自动生成的方法存根
    71         //用现线程来控制隔多少秒之后获取数据,然后设置到ListView上(正常情况下不需要加,只是为了看出来这个延时的效果)
    72         Handler handler=new Handler();
    73         handler.postDelayed(new Runnable() {    
    74             @Override
    75             public void run() {
    76                 // TODO 自动生成的方法存根
    77                 getOnLoadDate();
    78                 showListView(list);
    79                 //通知ListView加载完毕
    80                 lv.loadComplete();
    81             }
    82         }, 2000);    
    83     }
    84 
    85 }

    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的学习基本完成,其中还有些知识不太懂,比如说接口的回调,自定义控件部分等等,还需要加深练习。

  • 相关阅读:
    关于Maya Viewport 2.0 API 开发的介绍视频
    春节大假
    Some tips about the life cycle of Maya thread pool
    Can I compile and run Dx11Shader for Maya 2015 on my side?
    How to get current deformed vertex positions in MoBu?
    想加入全球首届的 欧特克云加速计划吗?
    三本毕业(非科班),四次阿里巴巴面试,终拿 offer(大厂面经)
    mac、window版编辑器 webstorm 2016... 永久破解方法。
    node 搭载本地代理,处理web本地开发跨域问题
    js 一维数组,转成嵌套数组
  • 原文地址:https://www.cnblogs.com/RabbitLx/p/5424226.html
Copyright © 2011-2022 走看看