java.lang.String 无法转换为 com.example.expandablelistview.NewsItem。

4
我正在尝试将可扩展列表视图(ExpandableListView)的子数据保存到SharedPreferences中。保存工作正常,但当我尝试加载它时,就会出现错误。

问题:

尝试加载设置并将其放入HashMap中时,会出现以下错误(缩短为3行):

08-24 12:40:05.138: E/AndroidRuntime(807): FATAL EXCEPTION: main
08-24 12:40:05.138: E/AndroidRuntime(807): java.lang.ClassCastException: java.lang.String cannot be cast to com.example.expandablelistview.NewsItem
08-24 12:40:05.138: E/AndroidRuntime(807):  at com.example.expandablelistview.ExpandableListAdapter.getChildView(ExpandableListAdapter.java:62)

为保存数据,我将使用以下代码:

    SharedPreferences pref = context.getSharedPreferences("myPrefs", MODE_PRIVATE);
    SharedPreferences.Editor editor= pref.edit();

    for( Entry<String, List<NewsItem>> entry : listDataChild.entrySet() )  {
          editor.putString( entry.getKey(), String.valueOf(entry.getValue())); // Save the values of listDataChild
    }

    editor.commit();

The output of the data is:

Yesterday: [[ headline=51.17346, 3.2252, Speed=2.10KM/H, Direction=158, date=18-8-2013 13:37:32]]
Older: [[ headline=51.74346, 3.23252, Speed=2.00KM/H, Direction=122, date=17-8-2013 11:40:30]]
Today: [[ headline=51.07346, 3.35252, Speed=0.00KM/H, Direction=122, date=19-8-2013 18:50:42]]

当尝试加载这些数据(错误发生在此处)并将其放入HashMap中时,我将使用以下内容(从这里):
    for( Entry<String, ?> entry : pref.getAll().entrySet() ) {
        listDataChild.put( entry.getKey(), (List<NewsItem>) Arrays.asList(entry.getValue()) ); // Put it in listDataChild
    }

数据的初始化发生在这里:


static void prepareListData(final Context context) {
    listDataHeader = new ArrayList<String>();
    listDataChild = new HashMap<String, List<NewsItem>>();

    // Adding child data
    listDataHeader.add("Today");
    listDataHeader.add("Yesterday");
    listDataHeader.add("Older");
    List<NewsItem> today = new ArrayList<NewsItem>();
    NewsItem newsData = new NewsItem();
    newsData.setHeadline("51.07346, 3.35252");
    newsData.setSpeed("0.00KM/H");
    newsData.setDirection("122");
    newsData.setDate("19-8-2013 18:50:42");
    today.add(newsData);

    List<NewsItem> yesterday = new ArrayList<NewsItem>();
    newsData = new NewsItem();
    newsData.setHeadline("51.17346, 3.2252");
    newsData.setSpeed("2.10KM/H");
    newsData.setDirection("158");
    newsData.setDate("18-8-2013 13:37:32");
    yesterday.add(newsData);

    List<NewsItem> older = new ArrayList<NewsItem>();
    newsData = new NewsItem();
    newsData.setHeadline("51.74346, 3.23252");
    newsData.setSpeed("2.00KM/H");
    newsData.setDirection("122");
    newsData.setDate("17-8-2013 11:40:30");
    older.add(newsData);

    listDataChild.put(listDataHeader.get(0), today);
    listDataChild.put(listDataHeader.get(1), yesterday);
    listDataChild.put(listDataHeader.get(2), older);
}

尝试的方法:

我曾尝试将listdataChild.put更改为加载以下数据:

listDataChild.put( entry.getKey(), new ArrayList<NewsItem>((Collection<? extends NewsItem>) Arrays.asList(entry.getValue())) );

转化为中文:

并变成这样:

listDataChild.put( entry.getKey(), (List<NewsItem>) new ArrayList(Arrays.asList(entry.getValue())) );

但是没有任何结果。(我得到了相同的错误)
问题:
是否可以将地图值转换为列表并将其放入HashMaplistDataChild中?
还是应该使用newsData.setHeadline("Value");, newsData.setSpeed("Value");等? (我不知道如何获取存储在SharedPreferences列表中的特定值)
NewsItem.java的源代码可以在这里找到,ExpandableListAdapter.java的源代码可以在这里找到,MainActivity.java的源代码可以在这里找到)

1
我非常确定你链接的 ExpandableListAdapter 版本 不是 你正在使用的版本,因为将 NewsItem 强制转换的代码在第62行而不是第66行 :( - Jon Skeet
你没有展示你在哪里初始化了 ExpandableListAdapter,这并没有帮助... - Jon Skeet
@JonSkeet 这里 是完整的 MainActivity.java。你还需要 XML 文件吗? - user1480019
@JonSkeet 感谢您的建议,我已将其更改为“普通”变量。请查看此处的更改:http://pastebin.com/tXVJkfrL。 - user1480019
1
有一个简短但完整的程序,演示问题?不应该需要链接到其他地方的代码。 - Jon Skeet
显示剩余9条评论
4个回答

2
您需要将从SharedPreferences中获取的String转换为NewsItem列表。仅仅进行类型转换是不够的。请注意保留HTML标签。

2
这个异常意味着你试图将一个String对象赋值给com.example.expandablelistview.NewsItem引用变量。我知道这并没有太大帮助,但无论如何。

1

正如其他人已经指出的那样,你简单使用Arrays.asList()并将其转换为数据列表的期望是错误的,不会起作用。当你使用SharedPreferences.Editor.putString()时,你将在首选项中插入一个String,表示由List.toString()返回的字符串(它将打印所有子项(使用toString())在方括号中)。当你从首选项中获取此字符串时,你需要将其构建成一个NewsItemsList。你可以通过以下方式之一来实现这一点:

for (Entry<String, ?> entry : pref.getAll().entrySet()) {
                    List<NewsItem> rowItems = new ArrayList<NewsItem>();
                    // the string from the preferences as it was saved
                    String prefContent = (String) entry.getValue();
                    // break against possible multiple NewsItems(I'm assuming that's 
                    // why you use a List of NewsItems) for the same
                    // key (every NewsItem starts with * so we split after
                    // that(see the NewsItems.toString method))
                    String[] newsItems = prefContent.split("\\*");
                    for (String item : newsItems) {
                        // an item which doesn't have at least 50 characters
                        // isn't a valid NewsItem string representation(based on
                        // its toString() method)
                        if (item.length() > 50) {
                            // ; is the delimiter for the NewsItem's data
                            String[] components = item.split(";");
                            NewsItem ni = new NewsItem();
                            // for each of the four data components we split
                            // after = and then use the second value obtained as
                            // that will be the value for that component
                            ni.setHeadline((components[0].split("="))[1]);
                            ni.setSpeed((components[1].split("="))[1]);
                            ni.setDirection((components[2].split("="))[1]);
                            ni.setDate((components[3].split("="))[1]);
                            rowItems.add(ni);
                        }
                    }
                    listDataChild.put(entry.getKey(), rowItems);
                }

上面的代码需要更改 NewsItem 类中的 toString() 方法:
@Override
public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("*headline=");
    sb.append(headline);
    sb.append(";");
    sb.append("speed=");
    sb.append(speed);
    sb.append(";");
    sb.append("direction=");
    sb.append(direction);
    sb.append(";");
    sb.append("date=");
    sb.append(date);
    return sb.toString();
}

1
@GerritHoekstra 如果您在toString()方法中添加了sb.append(";");这一行,会发生什么呢?就像这样:...sb.append("date="); sb.append(date);sb.append(";"); return sb.toString(); - user
1
@GerritHoekstra sb.append(";"); 应该是可以运行的。多余的 ,] 来自于 List 会在 [] 之间打印其项,并用 , 分隔这些项。因此,在 ; 后进行拆分时,最后一个组件(日期)将获得 ,] 作为最后一个元素。但是,在 toString() 方法中将一个 ; 插入日期后,将使 ; 后的拆分将额外的 ,] 移动到另一个 components 索引中。 - user
谢谢,你说得对,我看到了它的缓存版本。你知道如何在另一个private void中将数据添加到列表中而不覆盖先前的数据吗?还是我应该在这种情况下使用Guava Multimap?我尝试过实现但没有成功。 - user1480019
1
@GerritHoekstra 我不确定我理解你想做什么。如果您计划将其他NewsItems(简单对象或来自另一个项目列表)添加到属于键的列表中,则无法使用HashMap.put(key,newData),但是您可以使用List <NewsItems> list = HashMap.get(key);,然后将新数据添加到此列表中。如果您计划让一个键指向多个NewsItems列表,那么使用multimap可能是一个好主意。 - user
1
@GerritHoekstra 我假设你在将新的 NewsItem 添加到 list 时可以使用索引0,即 list.add(0, object) - user
显示剩余3条评论

0

我不确定 -

是否可以将地图值转换为列表并将其放入HashMap listDataChild中?

由于您想要将POJO数据保存在共享首选项中,因此可以使用JSON格式对NewsItemList及其包含的对象进行序列化,然后将其存储为字符串到SharedPreferences中。 Google-JSON 是一个很好的API。

当您想要获取数据时,请检索字符串并使用JSONArray检索每个对象并将其添加到新的NewsItemList中。

否则,尝试将对象序列化私有文件。这将帮助您超越SharedPreferences的putString(),putFloat(),putLong(),putInt()和putBoolean()。 谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接