Child fragment from onListItemClick within viewPager behaves unexpectedly

670 Views Asked by At

I have 3 ListFragments being handled by a viewPager (managed by a FragmentAdapter) - they work perfectly. Now when the user clicks an item in ListFragment #1, a new Fragment should open with the details. It's behaving strangely in the following manner:

  1. Only clicking a list item twice opens the DetailFragment, yet debugging shows the first click indeed goes into the DetailFragment, but doesn't show the view (the view still shows the current ListFragment).

  2. After clicking the 2nd time, the DetailFragment does show it's layout, but not the elements within it (like TextView, etc).

  3. If the user 'accidently' swipes the screen when DetailFragment is showing, the viewPager sets it in place of the 2nd ListFragment! Only when pressing back on the DetailFragment view will 'reset' the viewPager to it's correct ListFragment. Of course if the user swipes when in a DetailFragment, the next ListFragment of the viewPager should appear, and the DetailFragment should be removed.

Thanks for any tips muddling through Android's odd world of fragments and views :)

public class PlanetFragment extends ListFragment{

    LayoutInflater inflater;
    ListView list;
    ArrayList<HashMap<String, String>> planetListArray;

    HashMap<String, String> planetMap;
    Activity activity;
    Context context;

       @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.planets_tab_layout, container, false);
            inflater=(LayoutInflater)getLayoutInflater(savedInstanceState);
            activity = getActivity();
            context = PlanetFragment.this.getActivity();
            String dbTableName = "Table_Planets";
            SQLiteHelper info = new SQLiteHelper(getActivity().getBaseContext());       
            info.open();

            ArrayList<HashMap<String, String>> datafromSQL = info.getData(dbTableName);

            if(!datafromSQL.isEmpty()){ 
                planetListArray = new  ArrayList<HashMap<String, String>>();
                for (int i = 0; i<datafromSQL.size(); i++){

                    planetMap = new HashMap<String, String>();
                    planetMap.put(PLANET_ID, datafromSQL.get(i).get(KEY_PLANET_ID));
                    planetMap.put(ZODIAC_ID, datafromSQL.get(i).get(KEY_ZODIAC_ID));
                    planetMap.put(DEGREES, datafromSQL.get(i).get(KEY_DEGREES));
                    planetMap.put(CONTENT, datafromSQL.get(i).get(KEY_CONTENT));

                    planetListArray.add(planetMap);              
                }   
                info.close();
            }

          list = (ListView) v.findViewById(android.R.id.list); 
          PlanetAdapter adapter=new PlanetAdapter(getActivity(), R.layout.planets_row, planetListArray); 
          list.setAdapter(adapter);

          return v;
       }

       @Override
        public void onViewCreated(View view, Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            //the dividers 
            getListView().setDivider(getResources().getDrawable(R.drawable.purplebartop));
        }

    @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
            super.onListItemClick(l, v, position, id);
            HashMap<String, String> item = planetListArray.get(position);

            Bundle bundle = new Bundle();
            bundle.putSerializable("itemMap", item);
            bundle.putInt("position", position);
            Fragment frag = DetailFragment.newInstance();
            frag.setArguments(bundle);
            if (frag != null) {
                getActivity().getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.pager, frag, "frag")
                    .addToBackStack("frag")
                    .commit();
            }
        }
}



public class DetailFragment extends Fragment{

    Context context;
    Activity activity;
    TextView planetName;

    public static android.support.v4.app.Fragment newInstance() {
        DetailFragment f = new DetailFragment();
        return f;
    }

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

        inflater=(LayoutInflater)getLayoutInflater(savedInstanceState);
        View v = inflater.inflate(R.layout.dialog_details, container, false);
        activity = getActivity();
        context = DetailFragment.this.getActivity();
        planetName = (TextView)v.findViewById(R.id.planetNameExpanded);
        planetName.setText("planetX");

        return v;
    }
}

EDIT: Instead of getActivity().getSupportFragmentManager() I have also tried getChildFragmentManager() but it always gives the error: The method getChildFragmentManager() is undefined for the type PlanetFragment.

1

There are 1 best solutions below

1
On BEST ANSWER

When you click on a list item, you are indeed constructing a new details fragment and telling the fragment manager to replace the tag "frag" with that fragment. However, you are not telling the view pager to switch over to that fragment.

Since you already have a back-pointer to your activity, you could use findViewById to find your view pager, and then call viewPager.setCurrentItem.

I think you might be asking for trouble by constructing a new details fragment inside of the list fragment. When you use a FragmentPagerAdapter, the adapter usually constructs the fragments. I would have implemented this by letting the adapter make the fragments, and then in your onListItemClick find the existing details fragment and call a method on it to configure it with the new data. But maybe just the setCurrentItem will fix your problem.

EDIT

First, I would write your FragmentPagerAdapter so you can use getItem to fetch the existing fragment, without creating a new one each time.

public class PlanetFragmentAdapter extends FragmentPagerAdapter {
    private Fragment [] fragments = new Fragments[3];

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

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

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = fragments[position];

        if (fragment == null) {
            switch (position) {
                case 0:
                    fragment = new PlanetFragment();
                    break;
                case 1:
                    fragment = new DetailFragment();
                    break;
                case 2:
                    fragment = new MysteryFragment();
                    break;
            }
            fragments[position] = fragment;
        }
        return fragment;
    }
}

Also add functions in your activity to work with your fragments:

public void setPage(int position) {
    viewPager.setCurrentItem(position);
}

public DetailFragment getDetailFragment() {
    return (DetailFragment) viewPager.getItem(1); // now it doesn't create a new instance
    // you could also use getSupportFragmentManager().findFragmentById() here
}

Now when you click on an item in your list fragment, you can get the existing detail fragment, configure it, and set the ViewPager to show the detail fragment.

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    HashMap<String, String> item = planetListArray.get(position);

    Bundle bundle = new Bundle();
    bundle.putSerializable("itemMap", item);
    bundle.putInt("position", position);
    PlanetActivity pa = (PlanetActivity) activity;
    DetailFragment frag = pa.getDetailFragment();
    frag.setArguments(bundle);
    pa.setCurrentItem(1);
}