-2

I'm trying to implement a FragmentPagerAdapter with dynamically created Tabs based on a Cursor that retrieves distinct values from an SQLite DB column. Then the ListFragment loads data based on a 2nd Cursor that selects data with a filter based on the Tab name. Finally I add a Tab to include "All" data, rather than a subset.

All is working well, except for getting the right data into the first "All" sheet. The ListFragment onCreateLoader never gets to select "All" and somehow pulls data in both the first and second Tab, based on the second Tab's name.

My code (filtered for relevance; hopefully not too much)

MainActivity

public class MainActivity extends AppCompatActivity {

    // arraylist of tabs in which tab details are stored
    private ArrayList<String> tabNames;
    // to hold tabs data when first fetch from sqlite db
    private Cursor tabCursor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(com.simsolec.flavorsaver.R.layout.activity_main);

        getTabs();

        // Create an adapter that knows which fragment should be shown on each page
        FlavorFragmentPagerAdapter adapter = new FlavorFragmentPagerAdapter(
                getSupportFragmentManager(), MainActivity.this, tabNames);

        // Find the view pager that will allow the user to swipe between fragments
        ViewPager mViewPager = findViewById(com.simsolec.flavorsaver.R.id.viewpager);
        // Set the adapter onto the view pager
        mViewPager.setAdapter(adapter);

        // Give the TabLayout the ViewPager
        TabLayout tabLayout = findViewById(com.simsolec.flavorsaver.R.id.sliding_tabs);
        tabLayout.setupWithViewPager(mViewPager);
    }
    
    private void getTabs( ){
        try{
            String[] Projection = { FlavorEntry.COLUMN_FLAVOR_RANGE };
            tabCursor = getContentResolver().query(
                    FlavorEntry.CONTENT_URI_DISTINCT,
                    Projection,
                    null,
                    null,
                    FlavorEntry.COLUMN_FLAVOR_RANGE );

            tabNames = new ArrayList();
            tabNames.add("All");

            if (tabCursor != null && tabCursor.getCount() > 0) {
                if(tabCursor.moveToFirst()) {
                    do{
                        String tabName;
                        tabName = tabCursor.getString(0);
                        tabNames.add(tabName);

                    }while (tabCursor.moveToNext());
                }
                tabCursor.close();
            }
        }catch (NullPointerException  | SQLException e) {

        }
        finally {
            tabCursor.close();
        }
    }
}

FragmentPagerAdapter

public class FlavorFragmentPagerAdapter extends FragmentPagerAdapter {
    private Context context;
    private ArrayList<String> mTabNames;

    public FlavorFragmentPagerAdapter(FragmentManager fm, Context context, ArrayList<String> tabNames) {
        super(fm);
        this.context = context;
        mTabNames = tabNames;
    }

    @Override
    public int getCount() {
        //return PAGE_COUNT;
        return mTabNames.size();
    }

    @Override
    public Fragment getItem(int position) {
        // return FlavorListFragment.newInstance(position + 1);
        // getItem is called to instantiate the fragment for the given page.
        // Return a PlaceholderFragment (defined as a static inner class below).
        String tabName = mTabNames.get(position);
        return FlavorListFragment.newInstance(tabName);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        //Generate title based on item position
        //return tabTitles[position];
        String tabName = mTabNames.get(position);
        return  tabName;
    }
}

ListFragment

public class FlavorListFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{

    // The fragment argument representing the tab name & number for this fragment.
    private static final String ARG_TAB_NAME ="tabName";

    // Store the tabName for later use
    private static String mTabName;

    public static FlavorListFragment newInstance(String tabName) {
        FlavorListFragment fragment = new FlavorListFragment();
        mTabName = tabName;
        Bundle args = new Bundle();
        args.putString(ARG_TAB_NAME, tabName);
        fragment.setArguments(args);
        return fragment;
    }

    /** Identifier for the flavor data loader */
    private static final int FLAVOR_LOADER = 0;

    /** Adapter for the ListView */
    FlavorCursorAdapter mCursorAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(com.simsolec.flavorsaver.R.layout.fragment_flavor_list,
                container, false);

        // Find the ListView which will be populated with the flavor data
        ListView flavorListView = rootView.findViewById(com.simsolec.flavorsaver.R.id.flavor_list);

        // Setup an Adapter to create a list item for each row of flavor data in the Cursor.
        // There is no flavor data yet (until the loader finishes) so pass in null for the Cursor.
        mCursorAdapter = new FlavorCursorAdapter(getActivity(), null);
        flavorListView.setAdapter(mCursorAdapter);

        // Kick off the loader
        getLoaderManager().initLoader(FLAVOR_LOADER, null, this);

        // Setup the item click listener
        flavorListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                // Create new intent to go to {@link FlavorActivity}
                Intent intent = new Intent(getContext(), FlavorActivity.class);

                // Form the content URI that represents the specific flavor that was clicked on,
                // by appending the "id" (passed as input to this method) onto the
                // {@link FlavorEntry#CONTENT_URI}.
                // For example, the URI would be "content://com.simsolec.flavorsaver/flavors/2"
                // if the flavor with ID 2 was clicked on.
                Uri currentFlavorUri = ContentUris.withAppendedId(FlavorEntry.CONTENT_URI, id);

                // Set the URI on the data field of the intent
                intent.setData(currentFlavorUri);

                // Launch the {@link FlavorActivity} to display the data for the current flavor.
                startActivity(intent);
            }
        });

        return rootView;
    }

    @Override
    public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
        // Define a projection that specifies the columns from the table we care about.
        String[] projection = {
                FlavorEntry._ID,
                FlavorEntry.COLUMN_FLAVOR_NAME,
                FlavorEntry.COLUMN_DESCRIPTION_SHORT,
                FlavorEntry.COLUMN_CUP_IMAGE,
                FlavorEntry.COLUMN_RATING };

        // This loader will execute the ContentProvider's query method on a background thread
        String[] selectionArgs;
        if (mTabName=="All") {
            selectionArgs = new String[] {"*"};
        } else {
            selectionArgs = new String[] {mTabName};
        }
        String selection = FlavorEntry.COLUMN_FLAVOR_RANGE + " in (";
        for (int s = 0; s < selectionArgs.length; s++) {
            selection += "?, ";
        }
        selection = selection.substring(0, selection.length() - 2) + ")";
        return new CursorLoader(getActivity(),   // Parent activity context
                FlavorEntry.CONTENT_URI,   // Provider content URI to query
                projection,             // Columns to include in the resulting Cursor
                selection,                   // No selection clause
                selectionArgs,                   // No selection arguments
                FlavorEntry.COLUMN_FLAVOR_NAME + " ASC");                  // Default sort order
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // Update {@link FlavorCursorAdapter} with this new cursor containing updated flavor data
        mCursorAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // Callback called when the data needs to be deleted
        mCursorAdapter.swapCursor(null);
    }

}

Can someone point me in the right direction?

Thank you, -Joost

Joost
  • 82
  • 1
  • 9
  • are you sure that sqlite has `*` wildcard for `LIKE operator` ? I don't think so ... most SQLs are using different wildcard for this ... **but more important is can we use wildcards with `IN operator` ? I don't think so** ... `WHERE ColumnX IN ('*')` will obviously will return only rows where value in columnX is equal '*' – Selvin Jan 10 '18 at 10:08
  • ... so obvious solution is to pass `null` for both `selection` and `selectionArgs` when `tabName == 'All'` (<=well, hehe, this is not how we compare strings in java) ... **which also means that there are multiple errors here (at least 3 - cos jignesh has right also)** - which means that your code is FUBAR – Selvin Jan 10 '18 at 10:12
  • Thank you @Selvin, for pointing out the null suggestion. Also thank you for reminding me how not to compare strings... I should mention this is my first attempt at coding anything beyond an Excel macro, so providing the proper way of comparing strings would've been even more helpful. – Joost Jan 10 '18 at 11:29

1 Answers1

1

Set the value of mTabName from Bundle not from newInstance() method because mTabName is define as static so remove static keyword from mTabName

 public static FlavorListFragment newInstance(String tabName) {
        FlavorListFragment fragment = new FlavorListFragment();
       // mTabName = tabName;
        Bundle args = new Bundle();
        args.putString(ARG_TAB_NAME, tabName);
        fragment.setArguments(args);
        return fragment;
    }

OnCreateView of FlavorListFragment

 @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(com.simsolec.flavorsaver.R.layout.fragment_flavor_list,
                    container, false);


        Bundle b= getArguments();
        //mTabName=b.getString(mTabName);
mTabName=b.getString(ARG_TAB_NAME);
}
MJM
  • 5,119
  • 5
  • 27
  • 53
  • Thanks @jignesh. I've removed static and applied the suggested changes. Now mTabName however remains `null`, hence none of the tabs work. – Joost Jan 10 '18 at 11:34
  • just use it locally(inside onCreateLoader) not as field in the way he provide – Selvin Jan 10 '18 at 11:44
  • Apologies for my ignorance here, but then mTabName is no longer available in onCreateLoader. @Selvin any further suggestions? Should I update for changes above to clarify? – Joost Jan 10 '18 at 11:49
  • use local variable inside onCreateLoader – Selvin Jan 10 '18 at 11:50
  • Thank you both @Selvin. With your help I was able to figure it out. It also took a little fixing on the b.getString(mTabName) statement, which needed to be "tabName". Not sure why this got downvoted twice though... was it that bad of a question, even for an absolute newbie? – Joost Jan 10 '18 at 12:53
  • @Joost i have updated the answer,for get the arg from the bundle – MJM Jan 10 '18 at 13:04