Android学习(五)分组ListView(Sectioned Headers)
问题
当你想展示一个分类的数据列表比如依据时间、日期、产品累不或者销售价格序等
解决方案
我们可以使用“Sectioned headers“(这里不知道应该具体叫啥,故未翻译,可以理解为分割头,或者分组) ListView.这里我们自定义一个Adapter,使用不同类型的视图和其对应的Adapter来实现,这里我们必须关注2个方法:
为特定的视图返回getView(int, View, ViewGroup)即将构建的视图类型
int getViewTypeCount()
返回getView(int, View, ViewGroup)即将构建的视图的视图类型个数.
下面是我们的实现代码:
我们这里构建我们的Android工程,并添加一个GroupListVeiwActivity(Project的包名:com.jeriffe.app)
首先我们需要定义我们的Layout文件:
注:这里我们有3个Layout文件,分别是activity的Layout,sectionsHeader的Layout,及listView Item的Layout,以下只是演示,故Layout代码文件格式并未应用Theme及Style
activity_group_list_veiw.xml(activity的Layout)
<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" > <ListView android:id="@+id/add_journalentry_menuitem" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <ListView android:id="@+id/list_journal" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </RelativeLayout>
group_list_header.xml(sectionsHeader的Layout)
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list_header_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@color/gray" android:paddingBottom="2dip" android:paddingLeft="5dip" android:paddingTop="2dip" android:textColor="@color/black" />
(listView Item的Layout)
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list_item_title" android:layout_width="fill_parent" android:layout_height="fill_parent" android:paddingBottom="10dip" android:paddingLeft="15dip" android:paddingTop="10dip" />
接下来便是我们需要自定义的一个Adapter
我们添加一个新的类:SeparatedListAdapter 继承自BaseAdapter适配器类,以下代码已经很直白,关键代码已经添加了注释,最重要的4个方法如下粗体所示
package com.jeriffe.app; import java.util.LinkedHashMap; import java.util.Map; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.widget.Adapter; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; public class SeparatedListAdapter extends BaseAdapter { public final Map<String, Adapter> sections = new LinkedHashMap<String, Adapter>(); public final ArrayAdapter<String> headers; public final static int TYPE_SECTION_HEADER = 0; public SeparatedListAdapter(Context context) { headers = new ArrayAdapter<String>(context, R.layout.group_list_header); } public void addSection(String section, Adapter adapter) { this.headers.add(section); this.sections.put(section, adapter); } public Object getItem(int position) { for (Object section : this.sections.keySet()) { Adapter adapter = sections.get(section); int size = adapter.getCount() + 1; // check if position inside this section if (position == 0) return section; if (position < size) return adapter.getItem(position - 1); // otherwise jump into next section position -= size; } return null; } public int getCount() { // total together all sections, plus one for each section header int total = 0; for (Adapter adapter : this.sections.values()) total += adapter.getCount() + 1; return total; } public boolean areAllItemsSelectable() { return false; } @Override public int getViewTypeCount() { // assume that headers count as one, then total all sections int total = 1; for (Adapter adapter : this.sections.values()) total += adapter.getViewTypeCount(); return total; } @Override public int getItemViewType(int position) { int type = 1; for (Object section : this.sections.keySet()) { Adapter adapter = sections.get(section); int size = adapter.getCount() + 1; // check if position inside this section if (position == 0) return TYPE_SECTION_HEADER; if (position < size) return type + adapter.getItemViewType(position - 1); // otherwise jump into next section position -= size; type += adapter.getViewTypeCount(); } return -1; } @Override public boolean isEnabled(int position) { // 设置header enabled is false,通俗点就是点击header无效 return (getItemViewType(position) != TYPE_SECTION_HEADER); } public View getView(int position, View convertView, ViewGroup parent) { int sectionnum = 0; for (Object section : this.sections.keySet()) { Adapter adapter = sections.get(section); int size = adapter.getCount() + 1; // check if position inside this section if (position == 0) return headers.getView(sectionnum, convertView, parent); if (position < size) return adapter.getView(position - 1, convertView, parent); // otherwise jump into next section position -= size; sectionnum++; } return null; } public long getItemId(int position) { return position; } }
接下来便是我们的Activity类
GroupListVeiwActivity类比较简单就是构建UI,并初始化Adapter来展示数据:
package com.jeriffe.app; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; public class GroupListVeiwActivity extends Activity { // SectionHeaders private final static String[] days = new String[]{"2012-04-06", "2012-05-08", "2012-09-28", "2012-10-16", "2012-12-03"}; // Section Contents private final static String[][] notes = new String[][]{ {"阴转多云", "12℃/25℃", "穿衣指数:天气较热,建议着短裙、短裤、短套装。"}, {"晴天", "22℃/34℃", "穿衣指数:天气大热,建议着短裙、短裤、注意预防中暑。"}, {"小雨", "8℃/16℃", "穿衣指数:天气微冷,建议着秋季服装。"}, {"中雨", "5℃/12℃", "穿衣指数:天气较冷,建议着秋季服装。"}, {"小雪", "-1℃/6℃", "穿衣指数:天气寒冷,建议冬季服装。"}}; // ListView Contents private ListView journalListView; // Adapter for ListView Contents private SeparatedListAdapter adapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_group_list_veiw); setupViews(); } private void setupViews() { // Create and Initialize the ListView Adapter initializeAdapter(); // Get a reference to the ListView holder journalListView = (ListView) this.findViewById(R.id.list_journal); journalListView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long duration) { String item = (String) adapter.getItem(position); Toast.makeText(getApplicationContext(), item, Toast.LENGTH_SHORT).show(); } }); // Set the adapter on the ListView holder journalListView.setAdapter(adapter); } private void initializeAdapter() { adapter = new SeparatedListAdapter(this); for (int index = 0; index < days.length; index++) { ArrayAdapter<String> listadapter = new ArrayAdapter<String>(this, R.layout.group_list_item, notes[index]); adapter.addSection(days[index], listadapter); } } }
好了到此已经相关代码已经Over,粘贴代码到自己构建Project,编译即可运行(当然String.xml及Color.xml还是要自己来添加的),下面来一张运行效果图:
注:这里,本人运行环境是Android4.1.2
以下文章探讨了其他的实现方式:
1.用2个Adapter数据源:一个是Header数据源,一个是包含Item的数据源,界面数据全部来自Item数据源
,自定义Adapter继承自ArrayAdapter<String>,其主要核心代码是getView方法,详细可见博客原文。
原文地址:http://www.cnblogs.com/qianxudetianxia/archive/2011/06/07/2074326.html
注:下面的代码,group_list_item_text再给TextVeiw设置文本的时候要留意:Header及Item的ID是一致的,详细可以见原文,
@Override public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; //根据标签类型加载不通的布局模板 if(listTag.contains(getItem(position))){ //如果是标签项 view = LayoutInflater.from(getContext()).inflate(R.layout.group_list_item_tag, null); }else{ //否则就是数据项了 view = LayoutInflater.from(getContext()).inflate(R.layout.group_list_item, null); } //显示名称 TextView textView = (TextView) view.findViewById(R.id.group_list_item_text); textView.setText(getItem(position)); //返回重写的view return view; }
2.使用第三方插件,详细我也为研究,感兴趣的同志见:http://code.google.com/p/android-section-list/
3.利用GONE Visibility,具体的实现可见这里:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=157934&extra=page%3D1&page=1