5

I am using a ListFragment with a custom list adapter and I want to refresh the list with a click on a icon in the ActionBar. Unfortunately it does not work and I have no idea why.

My ItemListFragment:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Log.d("debug","Hallo in ItemListFragment");

    //show ActionBar
    setHasOptionsMenu(true);

    //get reference to activity
    myApp = getActivity().getApplication();

    //check if intent from ItemListActivity is null
    Bundle be = getActivity().getIntent().getExtras();
    if (be == null){
        //if null read local feed
        feed = ReadFeed(fileName);
        Log.d("debug", "Lese Feed lokal :"+feed);
    }else{
        //else get extras from the intent
        feed = (RSSFeed) getActivity().getIntent().getExtras().get("feed");
        Log.d("debug", "Intent von ItemListActivity an ItemListFragment vorhanden");
    }

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
        Bundle savedInstanceState) {
    //inflate the fragment with the custom detail fragment
    View view = inflater.inflate(R.layout.feed_list, null);
    return view;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    //get listview from layout
    lv = getListView();
    lv.setVerticalFadingEdgeEnabled(true);

    // Set custom list adapter to the ListView        
    adapter = new CustomListAdapter(getActivity(), feed);
    lv.setAdapter(adapter);

}

//Inflate ActionBar
@Override
public void onCreateOptionsMenu(Menu optionsMenu, MenuInflater inflater) {
    inflater.inflate(R.menu.main, optionsMenu);
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    // Restore the previously serialized activated item position.
    if (savedInstanceState != null
            && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
        setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
    }
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    // Activities containing this fragment must implement its callbacks.
    if (!(activity instanceof Callbacks)) {
        throw new IllegalStateException("Activity must implement fragment's callbacks.");
    }

    mCallbacks = (Callbacks) activity;
}

@Override
public void onDetach() {
    super.onDetach();

    // Reset the active callbacks interface to the dummy implementation.
    mCallbacks = sCallbacks;
}

@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
    super.onListItemClick(listView, view, position, id);

    // Notify the active callbacks interface (the activity, if the
    // fragment is attached to one) that an item has been selected.
    if (mCallbacks != null) {
        Log.d("debug","Callback in ItemListFragment mit Position: "+position+"und Feed: "+feed);
        mCallbacks.onItemSelected(position, feed);
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    if (mActivatedPosition != ListView.INVALID_POSITION) {
        // Serialize and persist the activated item position.
        outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
    }
}

/**
 * Turns on activate-on-click mode. When this mode is on, list items will be
 * given the 'activated' state when touched.
 */
public void setActivateOnItemClick(boolean activateOnItemClick) {
    // When setting CHOICE_MODE_SINGLE, ListView will automatically
    // give items the 'activated' state when touched.
    getListView().setChoiceMode(activateOnItemClick
            ? ListView.CHOICE_MODE_SINGLE
                    : ListView.CHOICE_MODE_NONE);
}

private void setActivatedPosition(int position) {
    if (position == ListView.INVALID_POSITION) {
        getListView().setItemChecked(mActivatedPosition, false);
    } else {
        getListView().setItemChecked(position, true);
    }

    mActivatedPosition = position;
}

//OnClick auf ActionBar
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case android.R.id.home:
        return true;
    case R.id.refresh_option:
        refreshList(item);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

//Click on refresh in ActionBar -> Refresh the List
public void refreshList(final MenuItem item) {
    /* Attach a rotating ImageView to the refresh item as an ActionView */
    LayoutInflater inflater = (LayoutInflater) getActivity().getApplication()
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    ImageView iv = (ImageView) inflater.inflate(R.layout.action_refresh,
            null);

    Animation rotation = AnimationUtils.loadAnimation(getActivity(),
            R.anim.refresh_rotate);
    rotation.setRepeatCount(Animation.INFINITE);
    iv.startAnimation(rotation);

    item.setActionView(iv);

    // trigger feed refresh:
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            DOMParser tmpDOMParser = new DOMParser();
            feed = tmpDOMParser.parseXml("http://www.example.de/feed");

            Log.d("debug", "Refresh Liste mit Feed: "+feed);

            ItemListFragment.this.getActivity().runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    if (feed != null && feed.getItemCount() > 0) {
                        Log.d("debug", "Aktualisiere Liste");
                        adapter.notifyDataSetChanged();
                        item.getActionView().clearAnimation();
                        item.setActionView(null);
                    }
                }
            });
        }
    });
    thread.start();
}

@Override
public void onDestroy() {
    super.onDestroy();
    //TODO Datenverbrauch dadurch geringer?
    //adapter.imageLoader.clearCache();
    adapter.notifyDataSetChanged();
}   

My CustomListAdapter.java

public class CustomListAdapter extends BaseAdapter  {

    private LayoutInflater layoutInflater;
    public ImageLoader imageLoader;
    public RSSFeed _feed;

    public CustomListAdapter(Activity activity, RSSFeed feed) {

        _feed = feed;

        layoutInflater = (LayoutInflater) activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        imageLoader = new ImageLoader(activity.getApplicationContext());
    }

    @Override
    public int getCount() {
        // Set the total list item count
        return _feed.getItemCount();
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        // Inflate the item layout and set the views
        View listItem = convertView;
        int pos = position;
        if (listItem == null) {
            listItem = layoutInflater.inflate(R.layout.list_item, null);
        }

        // Initialize the views in the layout
        ImageView iv = (ImageView) listItem.findViewById(R.id.thumb);
        TextView tvTitle = (TextView) listItem.findViewById(R.id.title);
        TextView tvDate = (TextView) listItem.findViewById(R.id.date);
        TextView tvDesc = (TextView) listItem.findViewById(R.id.description);

        // Set the views in the layout
        imageLoader.DisplayImage(_feed.getItem(pos).getImage(), iv);
        tvTitle.setText(_feed.getItem(pos).getTitle());
        tvDate.setText(_feed.getItem(pos).getDate());
        tvDesc.setText(_feed.getItem(pos).getShortDescription());

        return listItem;
    }
}

This is the part where I try to refresh the list:

        @Override
        public void run() {
            if (feed != null && feed.getItemCount() > 0) {
                Log.d("debug", "Aktualisiere Liste");
                adapter.notifyDataSetChanged();
                item.getActionView().clearAnimation();
                item.setActionView(null);
            }
        }

Where is my mistake?

Liam
  • 27,717
  • 28
  • 128
  • 190
Mokkapps
  • 2,028
  • 9
  • 42
  • 67
  • Don't think this is related but you don't need to do `lv.setAdapter(adapter);` on feed refresh. Also, have you checked to see if this block of code does run to its completion? – Kai Nov 16 '13 at 04:17
  • My animation in the ActionBar stops, so I think the code is running completely – Mokkapps Nov 16 '13 at 08:32
  • 1
    In that thread you're parsing the xml again and assign it to the `feed` variable and then call `notifyDataSetChanged()` but this will not update the adapter because you don't update the actual data reference held by the adapter(so it will still see the old list of data). So when you need to update the adapter, before calling `notifyDataSetChanged()` first update the adapter's `_feed` variable to point to the new parsed `feed`(you could add a setter method in the adapter to assign `feed` to `_feed`(and call it in the update thread). – user Nov 18 '13 at 19:33
  • You saved my day that works! Could you please write a new comment so that I can mark it as solution! – Mokkapps Nov 18 '13 at 20:33

2 Answers2

10

Your current update code of the adapter will not work because you're not updating the proper reference to the data, the one on which the adapter actually is based. When you call refreshList, you create that thread to parse the xml and assign the results to the feed variable, however your adapter has its own reference to the initial data(_feed) which is not affected by the previous assignment so at the moment of the notifyDataSetChanged() call it will still see the old data and will do nothing.

The solution is to update the _feed reference of the adapter to point to the new set of parsed results and then call notifyDataSetChanged() on the adapter.

user
  • 86,916
  • 18
  • 197
  • 190
0
        Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        DOMParser tmpDOMParser = new DOMParser();
        feed = tmpDOMParser.parseXml("http://www.example.de/feed");

        Log.d("debug", "Refresh Liste mit Feed: "+feed);

        ItemListFragment.this.getActivity().runOnUiThread(new Runnable() {

            @Override
            public void run() {
                if (feed != null && feed.getItemCount() > 0) {
                    Log.d("debug", "Aktualisiere Liste");
    // Set custom list adapter to the ListView        
  adapter = new CustomListAdapter(getActivity(), feed);
      lv.setAdapter(adapter);
                     adapter.notifyDataSetChanged();
                    item.getActionView().clearAnimation();
                    item.setActionView(null);
                }
            }
        });
    }
});
thread.start();

and also instead of refresh button you can use this.this helps you. pulltorefresh

Invader
  • 679
  • 3
  • 10
  • This would work..No doubt, but it will take you to the top of the listview every time you get to the bottom..It will result in a very poor user experience...Hence this is not the solution – Bala Vishnu May 17 '14 at 08:48
  • What is the use of notifyDataSetChanged If you are doing setAdapter again and again. – Ajay Shrestha Jan 24 '16 at 00:41