4

I'm trying to save the expandablelistview child data to SharedPreferences. The saving is working but when I try to load it then I get errors.

Problem:

When trying to load the settings and put it in a HashMap I get these errors (shorted to 3 lines):

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)

For saving the data I'll use this code:

    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]]

When trying to load these data (the errors happened here) and put it in a HashMap I'll use this (from here):

    for( Entry<String, ?> entry : pref.getAll().entrySet() ) {
        listDataChild.put( entry.getKey(), (List<NewsItem>) Arrays.asList(entry.getValue()) ); // Put it in listDataChild
    }

The initialization of the data is happend here:

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);
}

Tried attempts:

I've tried to change the listdataChild.put to load the data to this:

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

And to this:

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

But without any results. (I've got the same error)

Question:

Is it possible to convert the map values to list and put it in the HashMap listDataChild?

Or should I use newsData.setHeadline("Value");, newsData.setSpeed("Value"); etc.? (I don't know how to obtain specific values that's in SharedPreferences list)

(The source of NewsItem.java can be found here, the source of ExpandableListAdapter.java can be found here, and the source of MainActivity.java can be found here)

Community
  • 1
  • 1
  • 1
    I'm pretty sure the version of `ExpandableListAdapter` you've linked to *isn't* the one you're using, as the cast to `NewsItem` occurs on line 62, not 66 :( – Jon Skeet Aug 24 '13 at 11:15
  • @JonSkeet Woops, I have deleted a few lines before upload. It's now indeed on line 62. Thanks for you notice. :) –  Aug 24 '13 at 11:21
  • It doesn't help that you haven't shown where you're initializing the `ExpandableListAdapter`, either... – Jon Skeet Aug 24 '13 at 11:21
  • @JonSkeet [Here](http://pastebin.com/FLtxm03Y) is the full `MainActivity.java`. Do you also want the XML files? –  Aug 24 '13 at 11:26
  • No, although if you could put some work into reducing all the code to a *short* but complete example demonstrating the problem (and nothing else) it would make everyone's lives easier. – Jon Skeet Aug 24 '13 at 11:27
  • Why are your variables in MainActivity static? That sounds like a really bad idea to start with... – Jon Skeet Aug 24 '13 at 11:28
  • @JonSkeet I'm sorry for missing the short demonstration for the problem. The variables are static for later usage, it's possible to change `preparelistData` to a `private void`. –  Aug 24 '13 at 11:48
  • The variables being static really *really* feels like a mistake to me. It doesn't make sense for two instances of the class to share data, IMO. – Jon Skeet Aug 24 '13 at 11:54
  • @JonSkeet Thanks for the advice, I've changed it to 'normal' variables. See [here](http://pastebin.com/tXVJkfrL) for the change. –  Aug 24 '13 at 12:05
  • 1
    And a short but complete program which *only* demonstrates the problem? There should be no need to link to code elsewhere. – Jon Skeet Aug 24 '13 at 13:10
  • @JonSkeet I have slightly modified the post. I hope it's good. –  Aug 24 '13 at 14:18
  • Well it *still* isn't a short but complete program demonstrating the problem, is it? I'm still waiting for *minimal* code showing the problem - it shouldn't need to load the data from anywhere, or do anything complicated... just the bare minimum to demonstrate what's going wrong. – Jon Skeet Aug 24 '13 at 15:33
  • the problem seems to be the way you are saving data at 'String.valueOf(entry.getValue())' in 'editor.putString(...' – Its not blank Aug 26 '13 at 11:46
  • so the issue happened here ' (List) Arrays.asList(entry.getValue()) );' – Its not blank Aug 26 '13 at 11:57

4 Answers4

2

You need to convert the String you get back from SharedPreferences somehow to a list of NewsItem. A cast alone will not do that.

Henry
  • 42,982
  • 7
  • 68
  • 84
2

this exceptions means that you are trying to assign a String object to a com.example.expandablelistview.NewsItem reference variable

i know this doesnt help much, but any way

Ahmed Adel Ismail
  • 2,168
  • 16
  • 23
1

As others have already pointed out your, expectations of simply using Arrays.asList() and casting to your data list is incorrect and will not work. As you use SharedPreferences.Editor.putString() you'll insert a String in the preferences representing the string returned by List.toString()(which will print all of its children(using toString()) in square brackets). When you get this string from the preferences back, you need to build it up into a List of NewsItems. One way of how you can do this would be like below:

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);
                }

The code above would require a change in the toString() method of the NewsItem class:

@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();
}
user
  • 86,916
  • 18
  • 197
  • 190
  • Thanks! Only one thing when I see the list after the `Date` string there is a `,` displayed and if it's the last item on the list its a `]`. How can I solve this issue? –  Sep 02 '13 at 10:54
  • 1
    @GerritHoekstra What happens if you add the line `sb.append(";");` in the `toString()` method like this: `...sb.append("date="); sb.append(date);sb.append(";"); return sb.toString();`? – user Sep 02 '13 at 13:36
  • 1
    @GerritHoekstra The `sb.append(";");` should work. The extra `,` and `]` comes from the fact that a `List` will print its items between `[` and `]` separating the items with `,`. Because of this when doing the split after `;` the last component(the date) will get either a `,` or a `]` for the last element. But inserting a `;` after the date in the `toString()` method would make the split after `;` to move the extra `,` or `]` in another `components` index. – user Sep 03 '13 at 09:12
  • Thanks you're right, I saw the cached version of it. Do you also know how to add data to the list in another `private void` without overriding the previous data? Or should I use [Guava Multimap](http://guava-libraries.googlecode.com/svn/tags/release03/javadoc/com/google/common/collect/Multimap.html) for this case? I've tried to implement that with no luck. –  Sep 03 '13 at 13:04
  • 1
    @GerritHoekstra I'm not sure I understand what you're trying to do. If you plan to add other NewsItems(simple object or from another list of items) to a list belonging to a key then you can't use `HashMap.put(key, newData)` but you could use `List list = HashMap.get(key);' and then add the new data to this list. If you plan to have a key pointing to more than one lists of NewsItems then it would probably be a good idea to use that multimap. – user Sep 03 '13 at 13:12
  • Thanks it's working! Only the item is now added to the bottom of the child `Today` is it possible to put the item to the top? –  Sep 03 '13 at 16:30
  • 1
    @GerritHoekstra I assume that you can use the index 0 when adding the new `NewsItem` to the `list list.add(0, object)`. – user Sep 03 '13 at 16:48
  • Do you also know how to add a item to `ExpandableListView` from another activity? I've asked the question [here](http://stackoverflow.com/questions/18622417/adding-a-item-from-another-activity-to-expandablelistview). –  Sep 05 '13 at 14:22
0

I'm not sure about -

Is it possible to convert the map values to list and put it in the HashMap listDataChild?

As you are looking for to save the POJO data in shared pref. You can use the JSON format to serialize your NewsItemList and the objects it contains, and then store is as a String into the SharedPreferences. Google-JSON is a good api for this.

When you want to get the data back, retrieve the String and use a JSONArray to retrieve each object and add it to a new NewsItemList.

Otherwise, try serializing your object to a private file. This will help you to go beyond SharedPreferences's putString(), putFloat(), putLong(), putInt() and putBoolean(). Thank you.

Pradip
  • 3,189
  • 3
  • 22
  • 27