我在SQLite表中有以下格式的数据:
id|datetime|col1|col2
1|2013-10-30 23:59:59|aaa|aab
2|2013-10-30 23:59:59|abb|aba
3|2013-10-30 23:59:59|abb|aba
4|2013-10-31 23:59:59|abb|aba
5|2013-10-31 23:59:59|abb|aba
我想实现一个ExpandableListView
,以便数据按datetime
分组并显示如下:
> 2013-10-30 23:59:59 // Group 1
1|aaa|aab
2|abb|aba
3|abb|aba
> 2013-10-31 23:59:59 // Group 2
4|abb|aba
5|abb|aba
我有一个自定义CursorAdapter
我可以轻松填充ListView
,显示每个项目的日期,但我不知道如何“分组”数据并填充它在ExpandableListView
- 你可以给我任何提示吗?
答案 0 :(得分:5)
我有一个可以轻松用来填充的自定义CursorAdapter ListView,显示每个项目的日期,但我不知道如何 “分组”数据并将其填充到ExpandableListView上 - 你能吗? 请给我任何提示?
以下是目前我在我的项目中使用的解决方案:
您不能(不应该)使用CursorAdapter 因为它不适合您的解决方案。您需要通过从BaseExpandableListAdapter
然后,由于您要创建“您自己的分组”,您需要更改当前的应用程序逻辑:
所以你的 Foo 对象应该是这样的(由于你的要求,只创建变量名来解释解决方案的想法):
public class Foo {
private String title;
private List<Data> children;
public void setChildren(List<Data> children) {
this.children = children;
}
}
标题将是数据库中的日期列,子项将是特定(唯一)日期的列。
由于你的例子:
id|datetime|col1|col2
1|2013-10-30 23:59:59|aaa|aab
2|2013-10-30 23:59:59|abb|aba
3|2013-10-30 23:59:59|abb|aba
4|2013-10-31 23:59:59|abb|aba
5|2013-10-31 23:59:59|abb|aba
一个特定日期(Object Foo的title属性)具有更多关联行,因此将使用Foo对象中已定义的子集合来模拟这些行。
所以现在你需要你的getAll()方法中的 <(从数据库中返回通常被称为类似的数据的方法)你的DAO对象(与数据库通信的对象,这是唯一的术语)创建Foo这个逻辑中的对象。
由于您需要为每个唯一日期正确初始化子集合,因此您需要使用两个选择查询。您的第一个选择将返回不同的日期 - 因此,如果您在数据库中有40个行,其中包含10个不同(唯一)日期,那么您的选择将包含10行具有这些唯一日期。
确定即可。 现在,您有ListView 的“群组”。
现在你需要为每个创建的“组”创建它的子项。所以这里是第二个选择,它将选择所有行并且条件正确,你将为每个“group”Foo对象分配自己的子集合。
这是伪代码#1:
String query = "select * from YourTable";
Cursor c = db.rawQuery(query, null);
List<Data> childen = new ArrayList<Data>();
if (c != null && c.moveToFirst()) {
for (Foo item: collection) {
// search proper child for current item
do {
// if current row date value equals with current item datetime
if (item.getTitle().equals(c.getString(2))) {
children.add(new Data(column3, column4)); // fetch data from columns
}
} while (c.moveToNext());
// assign created children into current item
item.setChildren(children);
// reset List that will be used for next item
children = null;
children = new ArrayList<Data>();
// reset Cursor and move it to first row again
c.moveToFirst();
}
}
// finally close Cursor and database
所以现在你的收藏是“分组”,现在剩下的工作就在你实施的ListAdapter上了 - 这并不棘手。
您需要的只是正确实施 getGroupView()和 getChildView()方法。
在“group method”中,您将使用Foo对象集合中的标题对行进行充气和初始化。这些行将成为ListView中的组。
在“子方法”中你会做同样的事情,但你不会从集合中扩充标题,而是从集合中扩展当前Foo对象的子项。这些行将成为一个特定组的子项。
由于#1。我简化了用于演示目的的源代码。但是“在行动中”你可以改变一些事情:
一般而言,取代c.getString(2)
获取第二列
建议使用列名,以便使用
而是c.getColumnIndex("columnName")
。
将源代码包装到try-finally块和in中是一种很好的做法 finnaly阻止关闭并释放使用的游标,如游标和 数据库。
而不是“重用”同一个孩子的集合,例如, 你可以在Foo类中创建将要添加项的公共方法 直接收集(代码片段#2 )。
代码片段#2:
public class Foo {
private String title;
private List<Data> children = new ArrayList<Data>();
public void addChild(Data child) {
this.children.add(child);
}
...
}
希望你理解我(我试图以尽可能简单的方式解释事情,不幸的是,面对面的个人沟通是最好的,但这个解决方案对我们来说不可用)而且我希望我帮助你解决你的问题问题
答案 1 :(得分:3)
您可以使用LinkedHashMap<String, List<Data>>
将您的数据建模和表示形式建模为Data
,其中public class Data {
private int id;
private String col1;
private String col2;
public Data(int id, String col1, String col2) {
this.id = id;
this.col1 = col1;
this.col2 = col2;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCol1() {
return col1;
}
public void setCol1(String col1) {
this.col1 = col1;
}
public String getCol2() {
return col2;
}
public void setCol2(String col2) {
this.col2 = col2;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(id).append(", ").append(col1).append(", ").append(col2);
return sb.toString();
}
}
可能如下所示:
LinkedHasMap
为什么public LinkedHashMap<String, List<Data>> readData(SQLiteDatabase db) {
LinkedHashMap<String, List<Data>> result = new LinkedHashMap<String, List<Data>>();
Cursor cursor = null;
try {
cursor = db.query("MY_TABLE", new String[] {
"datetime", "id", "col1", "col2"
}, null, null, null, null, "datetime, id ASC");
while (cursor.moveToNext()) {
String dateTime = cursor.getString(cursor.getColumnIndex("datetime"));
int id = cursor.getInt(cursor.getColumnIndex("id"));
String col1 = cursor.getString(cursor.getColumnIndex("col1"));
String col2 = cursor.getString(cursor.getColumnIndex("col2"));
List<Data> list = null;
if (result.containsKey(dateTime)) {
list = result.get(dateTime);
} else {
list = new ArrayList<Data>();
result.put(dateTime, list);
}
list.add(new Data(id, col1, col2));
}
} catch (Exception ex) {
Log.e("TAG", null, ex);
} finally {
if (cursor != null) {
cursor.close();
}
}
return result;
}
?因为您需要保留插入数据的顺序。所以你的SQLite阅读方法可能如下所示:
public class ExpAdapter extends BaseExpandableListAdapter {
private LinkedHashMap<String, List<Data>> input;
private LayoutInflater inflater;
public ExpAdapter(LayoutInflater inflater, LinkedHashMap<String, List<Data>> input) {
super();
this.input = input;
this.inflater = inflater;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return getChildData(groupPosition, childPosition);
}
private Data getChildData(int groupPosition, int childPosition) {
String key = getKey(groupPosition);
List<Data> list = input.get(key);
return list.get(childPosition);
}
private String getKey(int keyPosition) {
int counter = 0;
Iterator<String> keyIterator = input.keySet().iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
if (counter++ == keyPosition) {
return key;
}
}
// will not be the case ...
return null;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return getChildData(groupPosition, childPosition).getId();
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
TextView simpleTextView = null;
if (convertView == null) {
// inflate what you need, for testing purposes I am using android
// built-in layout
simpleTextView = (TextView) inflater.inflate(android.R.layout.simple_list_item_1,
parent, false);
} else {
simpleTextView = (TextView) convertView;
}
Data data = getChildData(groupPosition, childPosition);
simpleTextView.setText(data.toString());
return simpleTextView;
}
@Override
public int getChildrenCount(int groupPosition) {
String key = getKey(groupPosition);
return input.get(key).size();
}
@Override
public Object getGroup(int groupPosition) {
return getKey(groupPosition);
}
@Override
public int getGroupCount() {
return input.size();
}
@Override
public long getGroupId(int groupPosition) {
return 0;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
TextView simpleTextView = null;
if (convertView == null) {
// inflate what you need, for testing purposes I am using android
// built-in layout
simpleTextView = (TextView) inflater.inflate(android.R.layout.simple_list_item_1,
parent, false);
} else {
simpleTextView = (TextView) convertView;
}
simpleTextView.setText(getKey(groupPosition));
return simpleTextView;
}
@Override
public boolean hasStableIds() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
基本适配器如下所示:
public class MyExpandableActivity extends FragmentActivity {
private ExpandableListView expListView;
@Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.expandable_layout);
expListView = (ExpandableListView) findViewById(R.id.exp_listview);
fillList();
}
private void fillList() {
LinkedHashMap<String, List<Data>> input = getMockList(); // get the collection here
ExpAdapter adapter = new ExpAdapter(LayoutInflater.from(this), input);
expListView.setAdapter(adapter);
}
}
虽然它在基本活动中的简单用法是这样的:
<ExpandableListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/exp_listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
活动的简单布局:
{{1}}
有道理吗?