Search code examples
androidandroid-fragmentsandroid-dialogfragment

How can I populate an adapter in a fragment which is within a viewPager within a dialogFragment?


I'm trying to create the following scenario:

User clicks a Button > this shows a DialogFragment The DialogFragment contains a ViewPager, which has 2 tabs - one shows a colour picker fragment and the other a symbol picker fragment Each picker fragment contains an adapter which transforms an array of colours/symbols into a nice grid.

However, it's currently showing up blank - the DialogFragment appears, with the other parts of the fragment, but no adapter view.

Here's what I have:

The initial activity is called EditActivity. It has a Button, which, when clicked, calls this function to reveal the DialogFragmentPickers:

public void showPickers() {
        if(mDialogFragmentPickers == null) return;
        mDialogFragmentPickers.setColourSet(mColourSet);
        mDialogFragmentPickers.show(mFragmentManager, "Pickers");
    }

DialogFragmentPickers is initialised in the onCreate method of EditActivity like this:

mDialogFragmentPickers = new DialogFragmentPickers();

The ColourSet being passed in is basically just an array of the colours that are to be shown in the picker. DialogFragmentPickers should then pass it on through to the FragmentColourPicker when it's initialised.

The DialogFragmentPickers class looks like this (I've only shown the parts pertaining to the ColourPicker, but the SymbolPicker works in the same way):

public class DialogFragmentPickers extends DialogFragment {

    private PickersPagerAdapter mPagerAdapter;
    private ViewPager           mViewPager;
    private ColourSet           mColourSet;
    private FragmentColourPicker    mColourPicker;

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

        // Inflate layout
        View view = inflater.inflate(R.layout.dialog_fragment_pickers, container, false);

        mPagerAdapter = new PickersPagerAdapter(getChildFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = view.findViewById(R.id.pager);
        mViewPager.setAdapter(mPagerAdapter);

        return view;

    }

    public void setColourSet(ColourSet colourSet) {
        mColourSet = colourSet;
        if(mColourPicker == null) getColourPickerFragment();
        else mColourPicker.setColourSet(mColourSet);
    }

    private void getColourPickerFragment() {
        if (mPagerAdapter == null) return;
        mColourPicker = (FragmentColourPicker) mPagerAdapter.getItem(0);
        if(mColourSet != null) mColourPicker.setColourSet(mColourSet);
    }

    public class PickersPagerAdapter extends FragmentPagerAdapter
    {

        PickersPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            switch(position) {
                case 0:
                    return new FragmentColourPicker();
                case 1:
                    return new FragmentSymbolPicker();
                default:
                    return null;
            }
        }

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

        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return getResources().getString(R.string.colours);
                case 1:
                    return getResources().getString(R.string.symbols);
            }
            return null;
        }
    }

The layout file for this contains:

<android.support.v4.view.ViewPager
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- Current + adjacent page titles...-->
    <android.support.v4.view.PagerTitleStrip
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:background="#33b5e5"
        android:paddingBottom="4dp"
        android:paddingTop="4dp"
        android:textColor="#fff" />
</android.support.v4.view.ViewPager>

The first Fragment used in this pager is FragmentColourPicker. It is defined like this:

public class FragmentColourPicker extends Fragment {

    GridView mColourGrid;
    Context mContext;
    ColourSet mColourSet;
    ChartColourAdapter mColourAdapter;


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

        View view = inflater.inflate(R.layout.fragment_colour_picker, container, false);

        mColourGrid = view.findViewById(R.id.gridview_colourPicker);

        initialiseWithColourSet(); // Returns without doing anything if colourSet is not set up yet

        return view;
    }

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

        mContext = getActivity();

    }

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

    public void setColourSet(ColourSet thisColourSet) {
        mColourSet = thisColourSet;
        initialiseWithColourSet(); // Returns without doing anything if the colourGrid is not set up yet
    }

    public void initialiseWithColourSet() {

// Make sure that the colourSet and the colourGrid have both been initialised
        if(mColourSet == null) return;
        if(mColourGrid == null) return;

        // Now set up ChartColourAdapter to display colours in grid
        // Initialise currently highlighted colour to default
        // Set on click listener for each colour
        mColourAdapter = new ChartColourAdapter(mContext,
                R.layout.adapter_pattern_colour_cell_layout,
                mColourSet.getColours());
        mColourGrid.setAdapter(mColourAdapter);

    }

}

and the layout file for FragmentColourPicker (fragment_colour_picker.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/colour_picker_fragment"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/fui_transparent"
    android:orientation="vertical">

    <GridView 
        android:id="@+id/gridview_colourPicker"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorBg"
        android:paddingBottom="@dimen/activity_standard_margin"
        android:columnWidth="@dimen/grid_cell_column_width"
        android:gravity="center"
        android:horizontalSpacing="10dp"
        android:numColumns="auto_fit"
        android:padding="@dimen/activity_small_margin"
        android:stretchMode="columnWidth"
        android:verticalSpacing="10dp" />

    <TextView
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:text="@string/prompt_swipe_for_symbols"
        android:padding="@dimen/activity_small_margin"
        android:background="@color/colorBg"
        />

</LinearLayout>

When I run all of this, the DialogFragment appears when the button is clicked, and shows the FragmentColourPicker. I can swipe to the FragmentSymbolPicker and back. However, the only thing shown in the ColourPicker is the TextView which prompts the user to swipe across to see the symbols. The GridView with the actual colours in it doesn't show at all.

From the debugger, I think the issue is that initialiseWithColourSet only ever seems to have either colourSet OR colourGrid set - never both. This makes it return without attempting to set up the colour adapter.

If initialiseWithColourSet is called from onCreateView, then colourGrid is set, but colourSet is not (since it hasn't been passed in yet). That's reasonable enough. However, when colourSet is passed in later (from the DialogFragment), colourGrid has gone back to being null, so again the function returns without doing anything.

I clearly have something in the wrong order, or am re-initialising the colourGrid / ColourPicker without realising it.


Solution

  • It looks like an order issue to me. To ensure you're getting your data to the Fragment at the right time, I would pass the data that the fragments need into the Adapter via the constructor. I would keep a reference of them there and then instantiate the fragments using a static newInstance(... requiredData) method. Then, you can be sure that when they are "created" by the adapter, they'll have the required information.

    Something like this:

    Your fragment class:

    ...
    
    Object myRequiredData;
    
    static FragmentColourPicker newInstance(... requiredData) {
       FragmentColourPicker f = new FragmentColourPicker();
        f.setRequiredData(requiredData);        
    
        return f;
    }
    
    public void setRequiredData(Object reqData){
        this.myRequiredData = reqData;
    }
    
    ...
    

    Your adapter class:

    public class PickersPagerAdapter extends FragmentPagerAdapter {
    
            Object reqData0;
            Object reqData1;
    
            PickersPagerAdapter(FragmentManager fm, Object reqData0, Object reqData1) {
                super(fm);
                this.reqData0 = reqData0;
                this.reqData1 = reqData1;
            }
    
            @Override
            public Fragment getItem(int position) {
                switch(position) {
                    case 0:
                        return FragmentColourPicker.newInstance(reqData0);
                    case 1:
                        return FragmentSymbolPicker.newInstance(reqData1);
                    default:
                        return null;
                }
            }
    
        ...
    
    }