2

Friends, I've spent enough time to find an answer to my question(here and on android's site) But no luck. Here is the logic: From DialogFragment upon finishing putting user's data he clicks OK button:

import de.greenrobot.event.EventBus;
...
public class AddDialogFragment extends DialogFragment implements
        DialogInterface.OnClickListener {
...
 public void onClick(DialogInterface dialog, int which) {

        StringBuilder date = new StringBuilder(today.getText().subSequence(0,
                today.getText().length()));
        date = (date.toString().equals(getString(R.string.today))) ? new StringBuilder("20150104") : date;

        int weight = 89;
        String[] bsi = rb.getSelectedItems();
        String[] gsi = rg.getSelectedItems();

        DatabaseManipulation dm = new DatabaseManipulation(getActivity());
        dm.run(date, weight, bsi, gsi);
        dm.destroy();
        DialogDataModel toSend = new DialogDataModel(weight, date.toString(), bsi, gsi);
/*
        Context activity = getActivity();
        if (activity instanceof WDActivity)
            ((WDActivity) activity).updateListItemFragment();
*/
        EventBus.getDefault().post(new UpdateItemEvent(toSend));
        Log.d(LOG_TAG, "send event");

        Toast.makeText(getActivity(), R.string.saved_data, Toast.LENGTH_LONG).show();

    }
...

Event is successfully sent to Adapter class which is registered for receiving this event:

...
import de.greenrobot.event.EventBus;    

public class ItemsAdapter extends BaseAdapter implements ListAdapter {
    private static final  String LOG_TAG = "Logs";

    private ArrayList<DialogDataModel> list = new ArrayList<DialogDataModel>();
    private Context activity;

    public ItemsAdapter (ArrayList<DialogDataModel> list, Context context) {
        this.list = list;
        this.activity = context;
        registerEventBus();
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int pos) {
        return list.get(pos);
    }

    @Override
    public long getItemId(int pos) {
        return pos;
        //just return 0 if your list items do not have an Id variable.
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View view = convertView;
        if (view == null) {
            LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.row_item, null);
        }

        DialogDataModel curData = list.get(position);
        TextView itemDate = (TextView)view.findViewById(R.id.item_date);
        itemDate.setText(curData.getDate());

        TextView itemWeight = (TextView)view.findViewById(R.id.item_weight);
        itemWeight.setText( Integer.toString(curData.getWeight()) );

        TextView itemEvents = (TextView)view.findViewById(R.id.item_events);
        itemEvents.setText(curData.getEventsShort());

        //Handle buttons and add onClickListeners
        ImageButton editBtn = (ImageButton)view.findViewById(R.id.item_edit_btn);

        editBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (activity instanceof WDActivity)
                    ((WDActivity) activity).AddNewData(v, list.get(position));

                notifyDataSetChanged();
            }
        });

        ImageButton deleteBtn = (ImageButton) view.findViewById(R.id.item_delete_btn);

        deleteBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String date = list.get(position).getDate();

                DatabaseManipulation dm = new DatabaseManipulation(activity);
                dm.deleteItem(date);
                dm.destroy();

                list.remove(position);
                notifyDataSetChanged();
            }
        });

        return view;
    }

    public void registerEventBus(){
        if (!EventBus.getDefault().isRegistered(this)) EventBus.getDefault().register(this);
        Log.d(LOG_TAG, "Registred from adapter");
    }

    public void unregisterEventBus(){
        EventBus.getDefault().unregister(this);
    }

    public void onEventMainThread(UpdateItemEvent event) {
        Log.d(LOG_TAG, "Event fired!");
        list = DialogDataModel.replaceFieldsInArray(list,event.getModel());
        notifyDataSetChanged();
    }
}

After clicking OK in dialog window the user sees the list of items and the item he's updated is up to date. Without this logic he sees the old version of this item item in the list and to refresh the list he needs to relaunch the app.

Yes, it's working with EventBus. But as you can see here in the class there is no place for unregistering it from listening for UpdateItemEvent. Since there are no destructors in java(I don't believe that garbage collector should do this for me) I should handle this. Moreover the class registeres as listener in constructor (it happens several times, that's why you see this ugly if statement(if (!EventBus.getDefault().isRegistered(this))) You may ask me why not getting this adapter through Activity (there is the only one in the app) I tried building a chain AddDialogFragment -> WDActivity -> PlaceholderFragment -> ItemsAdapter

//AddDialogFragment 
   Context activity = getActivity();
   if (activity instanceof WDActivity)
      ((WDActivity) activity).updateListItemFragment();

//WDActivity --beginTransaction().replace does not work here
  ...
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wd);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);

    }
    ...
    public void updateListItemFragment() {
        android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
        android.support.v4.app.Fragment currentFragment = fragmentManager.findFragmentById(R.id.container);
        if (currentFragment instanceof PlaceholderFragment && currentFragment != null) {
            fragmentManager.beginTransaction().remove(currentFragment).commit();
            fragmentManager.beginTransaction().add(R.id.container, currentFragment).commit();
            ((PlaceholderFragment)currentFragment).fillList();

        }

//PlaceholderFragment
  public class PlaceholderFragment extends Fragment {
    private static final  String LOG_TAG = "Logs";

    private static final String ARG_SECTION_NUMBER = "section_number";

    private ListView listViewItem;

    public static PlaceholderFragment newInstance(int sectionNumber) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }
    public PlaceholderFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        if(getArguments().getInt(ARG_SECTION_NUMBER)==1)
            return onCreateViewInputChart(inflater, container, savedInstanceState);
        if(getArguments().getInt(ARG_SECTION_NUMBER)==2)
            return onCreateViewManual(inflater, container, savedInstanceState);
        return onCreateViewInputData(inflater, container, savedInstanceState);    

    }

    private View onCreateViewManual(LayoutInflater inflater, ViewGroup container,
                                    Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_howto, container, false);
        return rootView;
    }

    private View onCreateViewInputData(LayoutInflater inflater, ViewGroup container,
                                       Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_list_data, container, false);

        findViewsByIdListItem(rootView);
        fillList();

        return rootView;
    }

    private View onCreateViewInputChart(LayoutInflater inflater, ViewGroup container,
                                        Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_chart, container, false);
        return rootView;
    }
    //listViewItems
    private void findViewsByIdListItem(View v) {
        listViewItem = (ListView) v.findViewById(R.id.listViewItems);
    }

    public void fillList(){
        Context activity = getActivity();
        DatabaseManipulation dm = new DatabaseManipulation(activity);
        ArrayList<DialogDataModel> aldm = dm.getItemsList();
        dm.destroy();

        ItemsAdapter innerAdapter = new ItemsAdapter(aldm, activity);

        listViewItem.setAdapter(innerAdapter);
        ( (BaseAdapter) listViewItem.getAdapter() ).notifyDataSetChanged();
    }

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

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

//SectionsPagerAdapter - just for your information
  public class SectionsPagerAdapter extends FragmentPagerAdapter {

    public SectionsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page.
        return PlaceholderFragment.newInstance(position);
    }

    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case 0:
                return "Enter your data";
            case 1:
                return "Chart";
            case 2:
                return "How to use it";
        }
        return null;
    }
  }

//ItemsAdapter is placed higher

This did not work. Finally I found out that the bottleneck is in linking PlaceholderFragment and ListAdapter: listViewItem is null everywhere except onCreateViewInputData. I could not figure out why. Google didn't give me the answer and I took it as a magic. Btw due to this feature I cannot use PlaceholderFragment's onAttach and onDetach for saying ItemsAdapter to register/unregister for receiving the event. This:

    @Override
    public void onAttach(Context activity) {
        super.onAttach(activity);
        ( (ItemsAdapter) listViewItem.getAdapter() ).registerEventBus();
    }

doesn't work for the same reason listViewItem is null. Maybe there is some way to manage with this more accurately?

rituzy
  • 53
  • 4
  • 1
    IMHO, events should only go to things with clear lifecycles (e.g., activities, fragments, services) or everlasting singletons (e.g., caches). I would never have an `Adapter` be a registrant on an event bus. Beyond that, to be honest, I cannot make sense of your code. My best recommendation, short of a rewrite, is that whatever owns the `AdapterView` that has the `BaseAdapter` that is registered on the event bus needs to tell the adapter to register and unregister from appropriate lifecycle methods. – CommonsWare Jan 10 '16 at 21:10

1 Answers1

0

Finally I cope with this: the bottleneck is in linking PlaceholderFragment and ListAdapter: listViewItem is null everywhere except onCreateViewInputData . Below is rewised SectionsPagerAdapter:

public class SectionsPagerAdapter extends FragmentPagerAdapter {
  Context activity;
  SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

  public SectionsPagerAdapter(FragmentManager fm, Context activity) {
    super(fm);
    this.activity = activity;
  }

  @Override
  public Fragment getItem(int position) {
    // getItem is called to instantiate the fragment for the given page.
    return PlaceholderFragment.newInstance(position);
  }

  @Override
  public int getCount() {
    return 3;
  }

  @Override
  public CharSequence getPageTitle(int position) {
    switch (position) {
        case 0:
            return activity.getString(R.string.chart);
        case 1:
            return activity.getString(R.string.weight_diary);
        case 2:
            return activity.getString(R.string.how_to);
      }
      return null;
  }

  @Override
  public Object instantiateItem(ViewGroup container, int position) {
    Fragment fragment = (Fragment) super.instantiateItem(container, position);
    registeredFragments.put(position, fragment);
    return fragment;
  }

  @Override
  public void destroyItem(ViewGroup container, int position, Object object) {
    registeredFragments.remove(position);
    super.destroyItem(container, position, object);
  }

  public Fragment getRegisteredFragment(int position) {
    return registeredFragments.get(position);
    //maybe use adapater.getRegisteredFragment(viewPager.getCurrentItem());
  }
}

and here is my call to the widget in the one of 3 fragments:

   public void updateListItemFragment() {
      android.support.v4.app.Fragment inputData = mSectionsPagerAdapter.getRegisteredFragment(1);
      ListView lv = ((PlaceholderFragment)inputData).listViewItem;
      //for test purposes I refill listview with no items - here you need to hand filled BaseAdapter instance: CustomAdapter innerAdapter = new CustomAdapter(ArrayList<yourDataModel>, getContext());
      lv.setAdapter(null);
  }

And as you've may already wondered there is no need for event bus anymore. Btw if the fragment is not crated yet you need to check it: details are here. Thanks to CommonsWare for quik reply and advice.

Community
  • 1
  • 1
rituzy
  • 53
  • 4