第一步: 老生常谈, 首先定义相关的布局文件, 和普通的ListView一样, 不再赘述;
第二步: 定义一个实体类, 用该实体类来存储一个标题以及该标题下的所有Item的数据,具体示例代码如下;
public class MealCategoryEntity {
private String mCategoryName;
// Item列表, 不含标题
private List<ShakedListItemEntity> mCategoryItem = new ArrayList<ShakedListItemEntity>();
public MealCategoryEntity(String mCategroyName) {
// 设置标题
mCategoryName = mCategroyName;
}
public String getmCategoryName() {
return mCategoryName;
}
public void addItem(ShakedListItemEntity pItemName) {
mCategoryItem.add(pItemName);
}
/**
* 获取Item内容
* @param pPosition
* @return
*/
public Object getItem(int pPosition) {
// 标题排在第一位
if (pPosition == 0) {
return mCategoryName;
} else {
return mCategoryItem.get(pPosition - 1);
}
}
/**
* 当前类别Item总数。标题也需要占用一个Item
* @return
*/
public int getItemCount() {
return mCategoryItem.size() + 1;
}
}
/** 这是ShakedListItemEntity类的定义 **/
public class ShakedListItemEntity {
private String vegetable_name;
private int vegetable_num;
private double vegetable_cost;
public String getVegetable_name() {
return vegetable_name;
}
public void setVegetable_name(String vegetable_name) {
this.vegetable_name = vegetable_name;
}
public int getVegetable_num() {
return vegetable_num;
}
public void setVegetable_num(int vegetable_num) {
this.vegetable_num = vegetable_num;
}
public double getVegetable_cost() {
return vegetable_cost;
}
public void setVegetable_cost(float vegetable_cost) {
this.vegetable_cost = vegetable_cost;
}
}
第三步: 实例化上述定义的实体类,根据自己的需要可以实例化一个或多个,并将它们添加到List集合当中;
第四步: 定义自己Adapter并集成BaseAdapter, 重写其中的方法。这是最关键的一步,也是难度最大的一步, 具体示例代码及其注释(不要着急,请细细看其中的注释)如下:
public class MealCategoryAdapter extends BaseAdapter {
// 标题类型
private static final int TYPE_CATEGORY_ITEM = 0;
// 子项类型
private static final int TYPE_ITEM = 1;
// 类别集合
private List<MealCategoryEntity> mListData;
// 布局填充器
private LayoutInflater mInflater;
public MealCategoryAdapter(Context context, List<MealCategoryEntity> pData) {
mListData = pData;
mInflater = LayoutInflater.from(context);
}
/**
* 获取所有分类的Item的总数
*/
@Override
public int getCount() {
int count = 0;
if (null != mListData) {
// 所有分类中item的总和是ListVIew Item的总个数
for (MealCategoryEntity category : mListData) {
count += category.getItemCount();
}
}
return count;
}
/**
* 获取Item
*/
@Override
public Object getItem(int position) {
// 异常情况处理
if (null == mListData || position < 0 || position > getCount()) {
return null;
}
// 同一分类内,第一个元素的索引值
int categroyFirstIndex = 0;
for (MealCategoryEntity category : mListData) {
int size = category.getItemCount();
// 在当前分类中的索引值
int categoryIndex = position - categroyFirstIndex;
// item在当前分类内
if (categoryIndex < size) {
return category.getItem(categoryIndex);
}
// 索引移动到当前分类结尾,即下一个分类第一个元素索引
categroyFirstIndex += size;
}
return null;
}
/**
* 获取Item的类别,也即确定是子项还是标题项
*/
@Override
public int getItemViewType(int position) {
// 异常情况处理
if (null == mListData || position < 0 || position > getCount()) {
return TYPE_ITEM;
}
// 初始遍历位置
int categroyFirstIndex = 0;
// 遍历实体类集合
for (MealCategoryEntity category : mListData) {
int size = category.getItemCount();
// 在当前分类中的索引值
int categoryIndex = position - categroyFirstIndex;
// 如果是首项返回标题类型
if (categoryIndex == 0) {
return TYPE_CATEGORY_ITEM;
}
//如果是子项返回子项类型
if (categoryIndex > 0
&& categoryIndex < category.getItemCount() - 1) {
return TYPE_ITEM;
}
categroyFirstIndex += size;
}
return TYPE_ITEM;
}
//返回标题的数目, 该方法不能删除,标题数目根据自己程序的需要返回不同的值,这里我的例子中只有两个标题
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//获取指定位置的Item的类型
int itemViewType = getItemViewType(position);
switch (itemViewType) {
//如果是标题类型, 则填充标题布局
case TYPE_CATEGORY_ITEM:
if (null == convertView) {
convertView = mInflater.inflate(
R.layout.category_listview_item_header, null);
}
TextView textView = (TextView) convertView
.findViewById(R.id.header);
textView.setTypeface(MyApplication.getInstance().getTypeface());
String itemValue = (String) getItem(position);
textView.setText(itemValue);
break;
//如果是子项类型则填充子项的布局
case TYPE_ITEM:
ViewHolder viewHolder = null;
if (null == convertView) {
convertView = mInflater.inflate(
R.layout.category_listview_item, null);
viewHolder = new ViewHolder();
viewHolder.vegeName = (TextView) convertView
.findViewById(R.id.vegeName_id);
viewHolder.vegeName.setTypeface(MyApplication.getInstance()
.getTypeface());
viewHolder.orderNum = (TextView) convertView
.findViewById(R.id.orderNum_id);
viewHolder.vegeCost = (TextView) convertView
.findViewById(R.id.mealCost_id);
viewHolder.arrow_right = (ImageView) convertView
.findViewById(R.id.arrow_id);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
// 绑定数据
viewHolder.vegeName
.setText(((ShakedListItemEntity) getItem(position))
.getVegetable_name());
viewHolder.vegeName.setTypeface(MyApplication.getInstance()
.getTypeface());
viewHolder.orderNum.setText("x "
+ String.valueOf(((ShakedListItemEntity) getItem(position))
.getVegetable_num()));
viewHolder.vegeCost.setText("¥"
+ String.valueOf(((ShakedListItemEntity) getItem(position))
.getVegetable_cost()));
break;
}
return convertView;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
return getItemViewType(position) != TYPE_CATEGORY_ITEM;
}
private class ViewHolder {
TextView vegeName;
TextView orderNum;
TextView vegeCost;
ImageView arrow_right;
}
}
第五步: 根据具体的业务数据遍历第三步中定义的List集合并为其存储的实体类实例填充相应的数据;
第六步: 在程序当中根剧当前上下文及List集合实例化我们自己写的Adapter并将Adapter绑定到指定的ListView控件上;
第七步: 到此开发带标题的ListView算是告一段落, 接下来大家可以试着按以上流程走一遍了,若有任何不明白的地方可以给我留言,我会尽力帮助大家。
最后附几张效果图供大家参考: