3

This problem has been beaten a million times, but unfortunately, no answer on SO has helped solve my issue.

Fundamentally, my question is that, in a RecyclerView with a CardView in a Fragment, when and where should the following tasks be performed:

  • Fetching data from a server to the local database on the user's device
  • Processing the so-fetched local data
  • Instantiating and setting the RecyclerView's adapter
  • Calling the RecyclerView's notifyfDataSetChanged()

The RecyclerView in my implementation presently calls back only getItemCount(). onCreateViewHolder() and onBindViewHolder() are never getting called back. (I have seen this answer and a gazillion more.)

Here is the sequence of how its being done:

  1. An AppCompatActivity instantiates a Fragment
  2. The Fragment's layout contains a RelativeLayout with a toolbar and an include tag for a ViewPager. The ViewPager layout contains another include tag that sets a layout with a RecyclerView in a LinearLayout. The layout XML with the CardView is set in the RecyclerView's adapter:

        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        Log.wtf(LOG_TAG, "Inside onCreateViewHolder()");
        View myView;
        LayoutInflater inflater;
        inflater = LayoutInflater.from(parent.getContext());
        myItemView = inflater.inflate(R.layout.my_row_layout, parent, false);
        MyViewHolder myViewHolderItem = new MyViewHolder(myItemView);
        return myViewHolderItem;
    }
    
  3. There's no ScrollView in any layout

  4. The Fragment's onCreate() calls a method to fetch data from a server

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    // Some cache processing
    args = getArguments();

    myDBHelper = MyDBHelper.getInstance(getActivity());
    Intent intent = getActivity().getIntent();
    // Search comes from search intent
    if (intent.getBooleanExtra(MyContract.MY_SEARCH_REQUEST, false))
    {
        ...            
        getDataFromCloud("", true);
        getDataFromLocalDB(false, item);
    } // of if search intent 
    // Plain intent
    else 
    {
        getDataFromCloud("item", false);
        getDataFromLocalDB(true, item);
    }
} // of onCreate()
  1. This data gets populated into a local database on the user's device
  2. Another method in the Fragment's onCreate()filters the local data into an ArrayList
  3. This ArrayList is presented in the RecyclerView - CardView combo finally to the user.
  4. notifyDataSetChanged() is called in the Fragment's onActivityCreated(). But the fact is, its not making a difference anywhere it is called from. Also, the adapter is instantiated in the onPostExecute() of the AsyncTask that creates the ArrayList for the CardView from the local database.

Here's the log that results on running the app:

01-03 08:47:34.546 13489-14033/com.my A/MyDBHelper class: Create View-1 query: CREATE VIEW 
01-03 08:47:34.562 13489-13489/com.my A/MyFragment: getItemCount: 0 
01-03 08:47:34.584 13489-14033/com.my A/MyDBHelper class: Create View-2 query: CREATE TABLE 
01-03 08:47:34.638 13489-13489/com.my A/MyFragment: getItemCount: 0
01-03 08:47:34.722 13489-13489/com.my A/MyFragment: getItemCount: 2
01-03 08:47:34.743 13489-13489/com.my A/MyFragment: getItemCount: 2

In the above log, the View-1 query fetches server data and the View-2 query processes the so-fetched local data.

I have log statements in the onCreateViewHolder() and onBindViewHolder(). As evident above, these methods are just not being invoked, despite a positive return from getItemCount(), which is the size of the ArrayList. The app just shows the toolbar, and an empty white card, despite there being two records or items to be shown.

Been staring at these logs for the past couple of days with no progress or clues. Many thanks in advance for your expert advice or pointers (and for saving what's left of my sanity)!

Narayana J
  • 307
  • 5
  • 17
  • So where do you call `notifyDataSetChanged()` on your adapter or one of the other `notify` methods? – ianhanniballake Jan 03 '16 at 05:02
  • Thanks for looking at the question, @ianhanniballake! Forgot to mention - `notifyDataSetChanged()` is called in the `Fragment`'s `onActivityCreated()`. But the fact is, its not making a difference anywhere it is called from. Also, the adapter is instantiated in the `onPostExecute()` of the `AsyncTask` that populates the `CardView` from the local database. – Narayana J Jan 03 '16 at 05:27
  • 1
    review all notifies http://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html understand OBSERVER pattern in your particular instance ( your implementations COLLECTION & RECYCLER.ADAPTER ) . Async Fetch mean that the provider is on its own thread and timeline. Just create your adapter and LISTEN. whenever the provider has something to insert to the collection provided to the Adapters instance, use the correct array.inserts and the correct event signallers to notify the observer. you may need to default some things in viewHldr while wait for the net work. – Robert Rowntree Jan 03 '16 at 15:01
  • http://stackoverflow.com/questions/27845069/add-a-new-item-to-recyclerview-programatically – Robert Rowntree Jan 03 '16 at 15:05
  • @RobertRowntree, thank you. I just simplified the problem. Instead of depending on server calls to finish, I pushed all the data loading to a separate `AsyncTask` and ready the `ArrayList` before instantiating the `RecyclerView` Adapter. So the data for the `RecyclerView` is absolutely ready for the asking. In spite of this, only the `getItemCount()` is called back, returning positive. **NO OTHER method** is being called back. Some silly thing I am badly missing, just head over heels on how to fix it. _Pray, please help._ – Narayana J Jan 05 '16 at 04:48
  • This is my Gradle fragment: `dependencies { ... compile 'com.android.support:recyclerview-v7:23.1.1' compile 'com.android.support:cardview-v7:23.1.1' }` – Narayana J Jan 05 '16 at 04:54
  • Add or remove your views? http://antonioleiva.com/recyclerview/ – Robert Rowntree Jan 05 '16 at 08:47
  • @RobertRowntree, thank you, I had seen this earlier. Still, I re-read it and inserted the `add` method in the `RecyclerView` adapter. Am sure you noticed that he is not calling `add`anywhere in his example and neither am I. Obviously, the same result ensued, futile again. **WHY IN HEAVEN'S NAME ARE _onCreateViewHolder()_ OR _onBindViewHolder()_ NOT BEING CALLED BACK?** I am crawling out of my skin... Thanks again! Do you need to inspect the layout or other code? Kindly let me know. – Narayana J Jan 05 '16 at 10:06
  • 1
    ok. set yours aside for bit and go to what already works . Say android SDK samples / recyclerview sample or on git where there is alot of good recycler stuff ... https://github.com/radzio/android-data-binding-recyclerview and watch whats happening , inspect build / gradle packages and default toolsets. Inspect working sample observe the lifecycle and the exact context of the constructor. – Robert Rowntree Jan 05 '16 at 14:04
  • @RobertRowntree, thanks again for the pointers. I was able to fix it and get it going. Please comment on my self-answer if necessary. Cheers! – Narayana J Jan 06 '16 at 10:06

1 Answers1

1

Finally, it was Jared Burrows's advice that drove the last nail in the coffin. BoyOboy, the only way to get it right is fight! (...almost always!)

Though I cannot pin-point what was going wrong, here's what I re-did:

  1. Created a fresh new project with only the RecyclerView - CardView combo in a Fragment. Well worth the time and effort, in terms of the clarity it brought in.
  2. Read-up this very good resource.
  3. Switched back to the ListView implementation of my app, first got the CardView right, into the ListView
  4. With a fresh new understanding of RecyclerView, systematically replaced all the ListView code with the RecyclerView counterpart
  5. In the app, the custom RecyclerAdapter and ViewHolder live in their own, independent classes now
  6. Was stuck for another couple of hours with the "RecyclerView: No Adapter attached; skipping layout” error; nothing was showing up in the Fragment. That's when Jared Burrows's advice pitched in.

The RecyclerView is now looking all hunky dory, waiting to throw up the next challenge: animation!

Community
  • 1
  • 1
Narayana J
  • 307
  • 5
  • 17