ListView分类显示

第一个完整示例:

1. 引言

 

    在Android开发过程中往往有这样的需求,将ListView中的内容按年,月,日进行分类显示,要实现这样的效果我们可能有很多种方法,

 

    如:多ListView拼合,自定义ListView组件等,下面介绍一种比较简单,而且实现结构清晰的实现方式,效果图及实现如下。

 

2. 效果图

    ListView分类

3. 功能实现

 

    (1) 主布局(main.xml)实现:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"  
  3.     android:orientation = "vertical"  
  4.     android:layout_width = "fill_parent"  
  5.     android:layout_height = "fill_parent"  
  6.     >  
  7.     <ListView  
  8.         android:id = "@+id/categoryList"  
  9.         android:layout_width = "fill_parent"   
  10.         android:layout_height = "fill_parent"  
  11.         />  
  12. </LinearLayout>   

 

    (2) 主Activity实现:

  1. package com.flora;  
  2. import android.app.Activity;  
  3. import android.os.Bundle;  
  4. import android.view.View;  
  5. import android.view.ViewGroup;  
  6. import android.widget.ArrayAdapter;  
  7. import android.widget.ListView;  
  8. import android.widget.TextView;  
  9. public class ListViewCategoryActivity extends Activity {  
  10.       
  11.     private String [] mContacts = {"马英才""张三""李四"};  
  12.     private String [] mMusic = {"素顔""庐州月""半城烟沙"};  
  13.     private String [] mEBook = {"拆掉思维里的墙""淡定力""人脉决定命脉"};  
  14.       
  15.     @Override  
  16.     public void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(R.layout.main);  
  19.           
  20.         mCategoryAdapter.addCategory("人名"new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mContacts));  
  21.           
  22.         mCategoryAdapter.addCategory("音乐",new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mMusic));  
  23.           
  24.         mCategoryAdapter.addCategory("书籍",new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mEBook));  
  25.           
  26.         ListView categoryList = (ListView) findViewById(R.id.categoryList);  
  27.           
  28.         categoryList.setAdapter(mCategoryAdapter);  
  29.     }  
  30.       
  31.     private CategoryAdapter mCategoryAdapter = new CategoryAdapter() {  
  32.         @Override  
  33.         protected View getTitleView(String title, int index, View convertView, ViewGroup parent) {  
  34.             TextView titleView;  
  35.               
  36.             if (convertView == null) {  
  37.                 titleView = (TextView)getLayoutInflater().inflate(R.layout.title, null);  
  38.             } else {  
  39.                 titleView = (TextView)convertView;  
  40.             }  
  41.               
  42.             titleView.setText(title);  
  43.               
  44.             return titleView;  
  45.         }  
  46.     };  
  47.       
  48. }  

 

    (3) Adapter实现:

  1. package com.flora;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. import android.view.View;  
  5. import android.view.ViewGroup;  
  6. import android.widget.Adapter;  
  7. import android.widget.BaseAdapter;  
  8. public abstract class CategoryAdapter extends BaseAdapter {  
  9.     private List<Category> categories = new ArrayList<Category>();  
  10.       
  11.     public void addCategory(String title, Adapter adapter) {  
  12.         categories.add(new Category(title, adapter));  
  13.     }  
  14.       
  15.     @Override  
  16.     public int getCount() {  
  17.         int total = 0;  
  18.           
  19.         for (Category category : categories) {  
  20.             total += category.getAdapter().getCount() + 1;  
  21.         }  
  22.           
  23.         return total;  
  24.     }  
  25.     @Override  
  26.     public Object getItem(int position) {  
  27.         for (Category category : categories) {  
  28.             if (position == 0) {  
  29.                 return category;  
  30.             }  
  31.               
  32.             int size = category.getAdapter().getCount() + 1;  
  33.             if (position < size) {  
  34.                 return category.getAdapter().getItem(position-1);  
  35.             }  
  36.             position -= size;  
  37.         }  
  38.           
  39.         return null;  
  40.     }  
  41.     @Override  
  42.     public long getItemId(int position) {  
  43.         return position;  
  44.     }  
  45.       
  46.     public int getViewTypeCount() {  
  47.         int total = 1;  
  48.           
  49.         for (Category category : categories) {  
  50.             total += category.getAdapter().getViewTypeCount();  
  51.         }  
  52.           
  53.         return total;  
  54.     }  
  55.     public int getItemViewType(int position) {  
  56.         int typeOffset = 1;  
  57.           
  58.         for (Category category : categories) {  
  59.             if (position == 0) {  
  60.                 return 0;  
  61.             }  
  62.               
  63.             int size = category.getAdapter().getCount() + 1;  
  64.             if (position < size) {  
  65.                 return typeOffset + category.getAdapter().getItemViewType(position - 1);  
  66.             }  
  67.             position -= size;  
  68.               
  69.             typeOffset += category.getAdapter().getViewTypeCount();  
  70.         }  
  71.           
  72.         return -1;  
  73.     }  
  74.     @Override  
  75.     public View getView(int position, View convertView, ViewGroup parent) {  
  76.         int categoryIndex = 0;  
  77.           
  78.         for (Category category : categories) {  
  79.             if (position == 0) {  
  80.                 return getTitleView(category.getTitle(), categoryIndex,convertView, parent);  
  81.             }  
  82.             int size = category.getAdapter().getCount()+1;  
  83.             if (position < size) {  
  84.                 return category.getAdapter().getView(position - 1, convertView, parent);  
  85.             }  
  86.             position -= size;  
  87.               
  88.             categoryIndex++;  
  89.         }  
  90.           
  91.         return null;  
  92.     }  
  93.       
  94.     protected abstract View getTitleView(String caption,int index,View convertView,ViewGroup parent);  
  95.       
  96. }   

 

    (4) 分类ValueBean实现:

  1. package com.flora;  
  2. import android.widget.Adapter;  
  3. public class Category {  
  4.     private String mTitle;  
  5.       
  6.     private Adapter mAdapter;  
  7.     public Category(String title, Adapter adapter) {  
  8.         mTitle = title;  
  9.         mAdapter = adapter;  
  10.     }  
  11.       
  12.     public void setTile(String title) {  
  13.         mTitle = title;  
  14.     }  
  15.       
  16.     public String getTitle() {  
  17.         return mTitle;  
  18.     }  
  19.       
  20.     public void setAdapter(Adapter adapter) {  
  21.         mAdapter = adapter;  
  22.     }  
  23.       
  24.     public Adapter getAdapter() {  
  25.         return mAdapter;  
  26.     }  
  27.       
  28. }   

 

    (5) 分类Title实现:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <TextView  
  3.     xmlns:android = "http://schemas.android.com/apk/res/android"  
  4.     android:layout_width = "match_parent"  
  5.     android:layout_height = "match_parent"  
  6.     android:minHeight = "30dip"  
  7.     android:gravity = "center_vertical"  
  8.     android:paddingLeft = "10dip"  
  9.     android:background = "@color/title_background_color"  
  10.     />   


第二个完整示例

今天和大家分享关于“listview的分类显示”。现在有比较多的应用都有这个效果,比如在androidICS风格的“设置”选项里面就有这个效果,先看看效果:

ListView分类显示

实现这个效果比较简单,在填充listviewadapter的时候,我们都会通过继承BaseAdapter来写我们自己的adapterlistview里面的item是通过getView(int position, View convertView, ViewGroup parent) 实现。其实这边有实现预加载,你只要在getview方法里面打印出log信息就会发现,listview刚开始显示的时候getview不会返回所有的item,只是返回了前面几个,当你往下拖拽的时候getview方法会加载剩下的item。这样做的好处大家都知道,如果不这样做估计早就出现了内存泄漏了。

好吧,我们回到主题,实现分类显示只需要你把你显示的数据打包好。Listview里面的item都是通过getView来生成,所以可以这样,如果在getview里面生成item的时候,你返回两次convertView不就可以了吗?也就是说平时我们都是通过convertView来返回item,但是现在多了一个操作就是你根据自身打包的数据,如果当前返回的item是和之前显示的item不属于同一类就返回两次convertView。这样理解这个就好实现多了吧。注意的是像上面图上“Label”、“类别1”、“类别2”是不可点击的,只要实现BaseAdapter里面的isEnabled(int position)的方法就可以。

下面介绍的实现方式是运用了工厂模式实现,下面是草图

ListView分类显示

新建了一个ListItems接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/***
  * <span class="referer">@author</span>  huangsm
  * @date 2012-8-29
  * <span class="referer">@email</span>  huangsanm@gmail.com<script cf-hash="f9e31" type="text/javascript">
/* <![CDATA[ */ !function(){ try {var t= "currentScript" in document?document.currentScript:function(){ for (var t=document.getElementsByTagName( "script" ),e=t.length;e--;) if (t[e].getAttribute( "cf-hash" )) return t[e]}(); if (t&&t.previousSibling){var e,r,n,i,c=t.previousSibling,a=c.getAttribute( "data-cfemail" ); if (a){ for (e= "" ,r=parseInt(a.substr( 0 , 2 ), 16 ),n= 2 ;a.length-n;n+= 2 )i=parseInt(a.substr(n, 2 ), 16 )^r,e+=String.fromCharCode(i);e=document.createTextNode(e),c.parentNode.replaceChild(e,c)}}} catch (u){}}();/* ]]> */</script>
  @desc 接口
  */
public interface ListItems {
 
     public int getLayout();
     
     public boolean isClickable();
     
     public View getView(Context context, View convertView, LayoutInflater inflater);
     
}

其中LabelItem和ContentItem分别是显示的“类别”和“内容”,他们分别实现ListItems接口。LabelItem实现: 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/***
  * <span class="referer">@author</span>  huangsm
  * @date 2012-8-29
  * <span class="referer">@email</span>  huangsanm@gmail.com<script cf-hash="f9e31" type="text/javascript">
/* <![CDATA[ */ !function(){ try {var t= "currentScript" in document?document.currentScript:function(){ for (var t=document.getElementsByTagName( "script" ),e=t.length;e--;) if (t[e].getAttribute( "cf-hash" )) return t[e]}(); if (t&&t.previousSibling){var e,r,n,i,c=t.previousSibling,a=c.getAttribute( "data-cfemail" ); if (a){ for (e= "" ,r=parseInt(a.substr( 0 , 2 ), 16 ),n= 2 ;a.length-n;n+= 2 )i=parseInt(a.substr(n, 2 ), 16 )^r,e+=String.fromCharCode(i);e=document.createTextNode(e),c.parentNode.replaceChild(e,c)}}} catch (u){}}();/* ]]> */</script>
  @desc 标签
  */
public class LabelItem  implements ListItems {
 
     private String mLabel;
     public LabelItem(String label){
         mLabel = label;
     }
     
     @Override
     public int getLayout() {
         return R.layout.label_layout;
     }
 
     @Override
     public boolean isClickable() {
         return false ;
     }
 
     @Override
     public View getView(Context context, View convertView, LayoutInflater inflater) {
         convertView = inflater.inflate(getLayout(),  null );
         TextView title = (TextView) convertView;
         title.setText(mLabel);
         return convertView;
     }
 
}

ContentItem的实现: 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/***
  * <span class="referer">@author</span>  huangsm
  * @date 2012-8-29
  * <span class="referer">@email</span>  huangsanm@gmail.com<script cf-hash="f9e31" type="text/javascript">
/* <![CDATA[ */ !function(){ try {var t= "currentScript" in document?document.currentScript:function(){ for (var t=document.getElementsByTagName( "script" ),e=t.length;e--;) if (t[e].getAttribute( "cf-hash" )) return t[e]}(); if (t&&t.previousSibling){var e,r,n,i,c=t.previousSibling,a=c.getAttribute( "data-cfemail" ); if (a){ for (e= "" ,r=parseInt(a.substr( 0 , 2 ), 16 ),n= 2 ;a.length-n;n+= 2 )i=parseInt(a.substr(n, 2 ), 16 )^r,e+=String.fromCharCode(i);e=document.createTextNode(e),c.parentNode.replaceChild(e,c)}}} catch (u){}}();/* ]]> */</script>
  @desc 内容
  */
public class ContentItem  implements ListItems {
 
     private Item mItem;
     public ContentItem(Item item){
         mItem = item;
     }
     
     @Override
     public int getLayout() {
         return R.layout.content_layout;
     }
 
     @Override
     public boolean isClickable() {
         return true ;
     }
 
     @Override
     public View getView(Context context, View convertView, LayoutInflater inflater) {
         convertView = inflater.inflate(getLayout(),  null );
         ImageView iv = (ImageView) convertView.findViewById(R.id.content_image);
         iv.setImageResource(mItem.getResid());
         TextView tv = (TextView) convertView.findViewById(R.id.content_text);
         tv.setText(mItem.getTitle());
         return convertView;
     }
}

在activity中实现就相对来说比较麻烦一些。定义一个以ListItems为泛型的list集合mListItems,作为填充adapter的数据源,然后在adapter里面处理就很简单:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class PartAdapter  extends BaseAdapter {
 
         @Override
         public int getCount() {
             return mListItems.size();
         }
 
         @Override
         public Object getItem( int position) {
             return mListItems.get(position);
         }
 
         @Override
         public long getItemId( int position) {
             return position;
         }
         
         @Override
         public boolean isEnabled( int position) {
             return mListItems.get(position).isClickable();
         }
 
         @Override
         public View getView( int position, View convertView, ViewGroup parent) {
             return mListItems.get(position).getView(mContext, convertView, mInflater);
         }
     }

接下来是初始化数据,需要注意的是LabelItem的初始化,不过这个动作可以在你打包数据的时候处理好,这样在activity里面就不会那么麻烦了 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.part_listview);
         mListView = (ListView) findViewById(R.id.part_list);
         mContext =  this ;
         mInflater = LayoutInflater.from(mContext);
         mListItems =   new ArrayList
    <listitems>     ();
         
         //初始化数据
         LabelItem label1 =  new LabelItem( "Label" );
         mListItems.add(label1);
         
         Item item1 =  new Item();
         item1.setResid(R.drawable.ic_launcher);
         item1.setTitle(getString(R.string.app_name));
         ContentItem content1 =  new ContentItem(item1);
         mListItems.add(content1);
         
         for ( int i =  0 ; i <  3 ; i++) {
             LabelItem label =  new LabelItem( "类别" + (i +  1 ));
             mListItems.add(label);
             
             for ( int j =  0 ; j <  3 ; j++) {
                 Item item =  new Item();
                 item.setResid(R.drawable.ic_launcher_biz);
                 item.setTitle( "Content" + (i +  1 ));
                 ContentItem content =  new ContentItem(item);
                 mListItems.add(content);
             }
         }
         
         //设置adapter
         PartAdapter adapter =  new PartAdapter();
         mListView.setAdapter(adapter);
     }
    </listitems>

第三个完整示例:

我们有时候会遇到这么一个情况。就是我在一个ListView里面需要显示的东西其实是有种类之分的。比如我要分冬天,夏天,秋天,春天,然后在这每个季节下面再去加载各自的条目数据。还有,比如我们的通讯录,我们需要按A,B,C这样的字母顺序分类然后显示。这个怎么实现呢?

下面我们不用ExpandableListView,而是只用ListView来实现这一显示效果。

MainActivity.java

package com.xzq.listviewadapter;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.widget.ListView;

public class MainActivity extends Activity {

private String[] string = { "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z" };

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);

ListView listview = (ListView) findViewById(R.id.listview);
MyCustomAdapter adapter = new MyCustomAdapter(this);

int size = string.length;
for (int i = 0; i < size; i++) {
adapter.addSeparatorItem(string[i]);
for (int k = 0; k < 5; k++) {
adapter.addItem("item " + k);
}
}
listview.setAdapter(adapter);
}
}

MyCustomAdapter.java

package com.xzq.listviewadapter;

import java.util.ArrayList;
import java.util.TreeSet;

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 MyCustomAdapter extends BaseAdapter {

private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;
private ArrayList<String> data = new ArrayList<String>();
private LayoutInflater inflater;
private TreeSet<Integer> set = new TreeSet<Integer>();

public MyCustomAdapter(Context context) {
inflater = LayoutInflater.from(context);
}

public void addItem(String item) {
data.add(item);
}

public void addSeparatorItem(String item) {
data.add(item);
set.add(data.size() - 1);
}

public int getItemViewType(int position) {
return set.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}

@Override
public int getViewTypeCount() {
return TYPE_MAX_COUNT;
}

@Override
public int getCount() {
return data.size();
}

@Override
public Object getItem(int position) {
return data.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int type = getItemViewType(position);
if (convertView == null) {
holder = new ViewHolder();
switch (type) {
case TYPE_ITEM:
convertView = inflater.inflate(R.layout.item1, null);
holder.textView = (TextView) convertView
.findViewById(R.id.item1);
break;
case TYPE_SEPARATOR:
convertView = inflater.inflate(R.layout.item2, null);
holder.textView = (TextView) convertView
.findViewById(R.id.item2);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(data.get(position));
return convertView;
}

public static class ViewHolder {
public TextView textView;
}
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff"
android:orientation="vertical" >

<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />

</LinearLayout>

item1.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff"
android:orientation="vertical" >

<TextView
android:id="@+id/item1"
android:layout_width="fill_parent"
android:layout_height="30dip"
android:gravity="center_vertical" />

</LinearLayout>

item2.xml

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

<TextView
android:id="@+id/item2"
android:layout_width="fill_parent"
android:layout_height="30dip"
android:background="#3c4857"
android:gravity="center_vertical" />

</LinearLayout>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值