可扩展的 ListView 按年/月对项目进行分组
Expandable ListView Grouping Items by Year / Month
我有一长串要从 SQLite 数据库中检索的对象。为简化起见,假设 Event
对象具有属性 Date
、Name
和 Location
.
我可以使用以下列表适配器并从按 Date
.[=24= 排序的数据库中获取 Event
s Event
s 的时间顺序 ListView
]
public class ImageListButtonListAdapter extends ArrayAdapter<ImageListButtonListItem> {
public ImageListButtonListAdapter(Context c, List<ImageListButtonListItem> items) {
super(c, 0, items);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageListButtonView itemView = (ImageListButtonView)convertView;
if (null == itemView)
itemView = ImageListButtonView.inflate(parent);
itemView.setItem(getItem(position));
return itemView;
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
}
到目前为止,还不错。现在这个列表很长,我想使用 ExpandableListView
进一步构建列表,并按月对 Event
项目进行分组。我发现创建从 BaseExpandableListAdapter
派生的列表适配器的示例通常包括预定义的组和组名。我只有一大组项目,每个项目都包含一个 (Joda Time) LocalDate
,基于它我可以添加一个可用于排序的计算 属性 YearMonth
。
我的第一个想法是将整个列表传递给适配器并让适配器确定组,并在创建适配器时将项目分配给组。但我担心以这种方式处理整个列表,在列表显示之前创建组并将项目分配给这些组在已经对性能敏感的视图中将是一个代价高昂的过程。
我可以想象这是一个相当普遍的场景,并且有一种更优雅的方法来解决它。什么是最好的方法?
只是为了添加一个答案。
我建议不要在从数据库中获取数据后对其进行排序,而是建议通过更改查询数据库的方式对数据进行正确分组。
希望这对您有所帮助:-)
Darwind的评论和回答原则上回答了问题。对于任何对代码感兴趣的人,我添加这个答案。
在创建 ListView
之前准备组和 children 是正确的方法。正如达尔文的回答中所指出的那样,通过从数据库中检索数据的方式(此处未包括在内)可能会获得额外的性能提升。现在代码如下:
public class EventsListFragment extends ExpandableListFragment{
(...)
public void onResume() {
super.onResume();
dbManager = new DatabaseManager(getActivity());
mEvents = dbManager.GetEvents();
mItems.clear();
mGroups.clear();
mChildGroups.clear();
ArrayList<ImageListButtonListItem> childGroup = new ArrayList<>();
for (int i = mEvents.size()-1; i >= 0; i--) {
LocalDate date = mEvents.get(i).getDate();
DateTimeFormatter dtf = DateTimeFormat.longDate();
String date = dtf.print(date);
String name = mEvents.get(i).getEventName();
String location = mEvents.get(i).getLocation();
String imagePath = (...)
ImageListButtonListItem item = new ImageListButtonListItem(date, name, location, imagePath);
// List category (sorted by months).
YearMonth yearMonth = new YearMonth(date.getYear(), date.getMonthOfYear());
if(mGroups.isEmpty()) {
mGroups.add(yearMonth);
} else if(!mGroups.contains(yearMonth)) {
mChildGroups.add(childGroup);
mGroups.add(yearMonth);
childGroup = new ArrayList<>();
}
childGroup.add(item);
}
if (!childGroup.isEmpty()) {
mChildGroups.add(childGroup);
}
mAdapter.notifyDataSetChanged();
}
(...)
}
-
public class MonthExpandableImageListButtonListAdapter extends BaseExpandableListAdapter {
private Context mContext;
private List<Object> mChildGroups;
private List<YearMonth> mGroups;
public LayoutInflater mInflater;
public Activity mActivity;
public MonthExpandableImageListButtonListAdapter(Context context, List<Object> childGroups, List<YearMonth> groups) {
mContext = context;
mChildGroups = childGroups;
mGroups = groups;
}
public void setInflater(LayoutInflater inflater, Activity act) {
mInflater = inflater;
mActivity = act;
}
@Override
public int getGroupCount() { return mGroups.size(); }
@Override
public int getChildrenCount(int groupPosition) { return ((ArrayList<Object>)mChildGroups.get(groupPosition)).size(); }
@Override
public Object getGroup(int groupPosition) { return null; }
@Override
public Object getChild(int groupPosition, int childPosition) { return null; }
@Override
public long getGroupId(int groupPosition) { return 0; }
@Override
public long getChildId(int groupPosition, int childPosition) { return 0; }
@Override
public boolean hasStableIds() { return false; }
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.view_month_expandable_list_header, null);
}
((CheckedTextView) convertView).setText(mGroups.get(groupPosition).toString());
((CheckedTextView) convertView).setChecked(isExpanded);
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
ImageListButtonView itemView = (ImageListButtonView)convertView;
if (null == itemView)
itemView = ImageListButtonView.inflate(parent);
itemView.setItem(((ArrayList<ImageListButtonListItem>) mChildGroups.get(groupPosition)).get(childPosition));
return itemView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) { return false; }
}
我有一长串要从 SQLite 数据库中检索的对象。为简化起见,假设 Event
对象具有属性 Date
、Name
和 Location
.
我可以使用以下列表适配器并从按 Date
.[=24= 排序的数据库中获取 Event
s Event
s 的时间顺序 ListView
]
public class ImageListButtonListAdapter extends ArrayAdapter<ImageListButtonListItem> {
public ImageListButtonListAdapter(Context c, List<ImageListButtonListItem> items) {
super(c, 0, items);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageListButtonView itemView = (ImageListButtonView)convertView;
if (null == itemView)
itemView = ImageListButtonView.inflate(parent);
itemView.setItem(getItem(position));
return itemView;
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
}
到目前为止,还不错。现在这个列表很长,我想使用 ExpandableListView
进一步构建列表,并按月对 Event
项目进行分组。我发现创建从 BaseExpandableListAdapter
派生的列表适配器的示例通常包括预定义的组和组名。我只有一大组项目,每个项目都包含一个 (Joda Time) LocalDate
,基于它我可以添加一个可用于排序的计算 属性 YearMonth
。
我的第一个想法是将整个列表传递给适配器并让适配器确定组,并在创建适配器时将项目分配给组。但我担心以这种方式处理整个列表,在列表显示之前创建组并将项目分配给这些组在已经对性能敏感的视图中将是一个代价高昂的过程。
我可以想象这是一个相当普遍的场景,并且有一种更优雅的方法来解决它。什么是最好的方法?
只是为了添加一个答案。
我建议不要在从数据库中获取数据后对其进行排序,而是建议通过更改查询数据库的方式对数据进行正确分组。
希望这对您有所帮助:-)
Darwind的评论和回答原则上回答了问题。对于任何对代码感兴趣的人,我添加这个答案。
在创建 ListView
之前准备组和 children 是正确的方法。正如达尔文的回答中所指出的那样,通过从数据库中检索数据的方式(此处未包括在内)可能会获得额外的性能提升。现在代码如下:
public class EventsListFragment extends ExpandableListFragment{
(...)
public void onResume() {
super.onResume();
dbManager = new DatabaseManager(getActivity());
mEvents = dbManager.GetEvents();
mItems.clear();
mGroups.clear();
mChildGroups.clear();
ArrayList<ImageListButtonListItem> childGroup = new ArrayList<>();
for (int i = mEvents.size()-1; i >= 0; i--) {
LocalDate date = mEvents.get(i).getDate();
DateTimeFormatter dtf = DateTimeFormat.longDate();
String date = dtf.print(date);
String name = mEvents.get(i).getEventName();
String location = mEvents.get(i).getLocation();
String imagePath = (...)
ImageListButtonListItem item = new ImageListButtonListItem(date, name, location, imagePath);
// List category (sorted by months).
YearMonth yearMonth = new YearMonth(date.getYear(), date.getMonthOfYear());
if(mGroups.isEmpty()) {
mGroups.add(yearMonth);
} else if(!mGroups.contains(yearMonth)) {
mChildGroups.add(childGroup);
mGroups.add(yearMonth);
childGroup = new ArrayList<>();
}
childGroup.add(item);
}
if (!childGroup.isEmpty()) {
mChildGroups.add(childGroup);
}
mAdapter.notifyDataSetChanged();
}
(...)
}
-
public class MonthExpandableImageListButtonListAdapter extends BaseExpandableListAdapter {
private Context mContext;
private List<Object> mChildGroups;
private List<YearMonth> mGroups;
public LayoutInflater mInflater;
public Activity mActivity;
public MonthExpandableImageListButtonListAdapter(Context context, List<Object> childGroups, List<YearMonth> groups) {
mContext = context;
mChildGroups = childGroups;
mGroups = groups;
}
public void setInflater(LayoutInflater inflater, Activity act) {
mInflater = inflater;
mActivity = act;
}
@Override
public int getGroupCount() { return mGroups.size(); }
@Override
public int getChildrenCount(int groupPosition) { return ((ArrayList<Object>)mChildGroups.get(groupPosition)).size(); }
@Override
public Object getGroup(int groupPosition) { return null; }
@Override
public Object getChild(int groupPosition, int childPosition) { return null; }
@Override
public long getGroupId(int groupPosition) { return 0; }
@Override
public long getChildId(int groupPosition, int childPosition) { return 0; }
@Override
public boolean hasStableIds() { return false; }
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.view_month_expandable_list_header, null);
}
((CheckedTextView) convertView).setText(mGroups.get(groupPosition).toString());
((CheckedTextView) convertView).setChecked(isExpanded);
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
ImageListButtonView itemView = (ImageListButtonView)convertView;
if (null == itemView)
itemView = ImageListButtonView.inflate(parent);
itemView.setItem(((ArrayList<ImageListButtonListItem>) mChildGroups.get(groupPosition)).get(childPosition));
return itemView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) { return false; }
}