Firebaseui-android: No Adapter Attached, Skipping Layout

Created on 13 Dec 2016  Â·  22Comments  Â·  Source: firebase/FirebaseUI-Android

I have an activity that hosts a TabLayout with 3 tabs. I am trying to load a recyclerview based on dynamic data in Firebase and the recyclerview simply is not populating any views. I am not sure if it has to do with the FragmentSectionPager adapter or what, but I am having no luck loading the actual RecyclerView.

Activity:

public class HomeActivity extends AppCompatActivity implements TrendingFragment.FragmentListener,FollowingFragment.OnFragmentInteractionListener, LiveFragment.OnFragmentInteractionListener {


private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
private Toolbar toolbar;
private boolean isOpen;
private ProgressBar mProgressBar;
private TextView mProgressText;
private TabLayout mTabLayout;
private ViewPager mViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_home);
    toolbar = (Toolbar) findViewById(R.id.action_tool_bar);
    toolbar.setTitleTextColor(ContextCompat.getColor(getApplicationContext(), R.color.white));
    setSupportActionBar(toolbar);
    setTitle(R.string.app_name);
    Window window = getWindow();

    final android.support.v7.app.ActionBar actionBar = getSupportActionBar();
    if(actionBar != null){
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setHomeButtonEnabled(true);
    }

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    mProgressBar = (ProgressBar) findViewById(R.id.pbHeaderProgress);
    mProgressText = (TextView) findViewById(R.id.progress_text);
    mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
    mViewPager = (ViewPager) findViewById(R.id.tab_swipe_container);
    mDrawerToggle = new ActionBarDrawerToggle(
            this,                  /* host Activity */
            mDrawerLayout,         /* DrawerLayout object */
            toolbar,  /* nav drawer icon to replace 'Up' caret */
            R.string.drawer_open,  /* "open drawer" description */
            R.string.drawer_close  /* "close drawer" description */
    ) {

        /** Called when a drawer has settled in a completely closed state. */
        public void onDrawerClosed(View view) {
            super.onDrawerClosed(view);
            isOpen = false;
        }

        /** Called when a drawer has settled in a completely open state. */
        public void onDrawerOpened(View drawerView) {
            super.onDrawerOpened(drawerView);
            isOpen = true;
        }
    };
    mDrawerLayout.addDrawerListener(mDrawerToggle);

    mDrawerList = (ListView) findViewById(R.id.left_drawer);
    ArrayList<String> drawerTitleArray = new ArrayList<>();
    drawerTitleArray.add(0, "TEST");
    drawerTitleArray.add(1, "TEST 1");
    // Set the adapter for the list view
    mDrawerList.setAdapter(new ArrayAdapter<String>(this,
            R.layout.drawer_list_item, drawerTitleArray));


    mViewPager.setAdapter(new SectionPagerAdapter(getSupportFragmentManager()));
    mTabLayout.setupWithViewPager(mViewPager);
    Bundle extras = getIntent().getExtras();
    int position = 0;
    if(extras != null) {
        position = extras.getInt("viewpager_position");
    }
    mViewPager.setCurrentItem(position);

}


@Override
public boolean onOptionsItemSelected(MenuItem item){
    return mDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
}


@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    // Sync the toggle state after onRestoreInstanceState has occurred.
    mDrawerToggle.syncState();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    mDrawerToggle.onConfigurationChanged(newConfig);
}


@Override
public void onFragmentLoaded() {
    mProgressBar.setVisibility(View.INVISIBLE);
    mProgressText.setVisibility(View.INVISIBLE);
}

@Override
public void onFragmentInteraction(Uri uri) {

}

public class SectionPagerAdapter extends FragmentStatePagerAdapter {

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

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return new TrendingFragment();
            case 1:
                return new FollowingFragment();
            case 2:
                return new LiveFragment();
            default:
                return new TrendingFragment();
        }
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case 0:
                return getResources().getString(R.string.trending_text);
            case 1:
                return getResources().getString(R.string.following_text);
            case 2:
                return getResources().getString(R.string.new_text);
            default:
                return getResources().getString(R.string.trending_text);
        }
       }
    }  
 }

Fragment Containing RecyclerView:

public class LiveFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

private RecyclerView mRecyclerview;
private DatabaseReference mBaseRef;
private DatabaseReference mPollsRef;
private LinearLayoutManager mLayoutManager;

private FirebaseRecyclerAdapter <Poll, PollHolder> mFireAdapter;


// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;

private OnFragmentInteractionListener mListener;

public LiveFragment() {
    // Required empty public constructor
}

/**
 * Use this factory method to create a new instance of
 * this fragment using the provided parameters.
 *
 * @param param1 Parameter 1.
 * @param param2 Parameter 2.
 * @return A new instance of fragment LiveFragment.
 */
// TODO: Rename and change types and number of parameters
public static LiveFragment newInstance(String param1, String param2) {
    LiveFragment fragment = new LiveFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBaseRef = FirebaseDatabase.getInstance().getReference();
    mPollsRef = mBaseRef.child("Polls");

    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);

    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    final View v = inflater.inflate(R.layout.fragment_new, container, false);
    Log.v("TAG", "ON CREATE CALLED FROM NEW");

    mLayoutManager = new LinearLayoutManager(getActivity());
    mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);

    mRecyclerview = (RecyclerView)v.findViewById(R.id.new_RecyclerView);

    if (mRecyclerview != null){
        mRecyclerview.setHasFixedSize(true);
    }

    if (mRecyclerview == null){
       Log.v("TAG", "RECYCLERVIEW NULL");
    } else if (mLayoutManager == null){
        Log.v("TAG", "LAYOUTMANAGER NULL");
    } else if (mFireAdapter == null) {
        Log.v("TAG", "mFIREADAPTER NULL");
    }
    return v;
}

// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
    if (mListener != null) {
        mListener.onFragmentInteraction(uri);
    }
}

@Override
public void onStart() {
    super.onStart();


}

@Override
public void onStop() {
    super.onStop();
    if (mFireAdapter != null){
        mFireAdapter.cleanup();
    }
}

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


    mRecyclerview.setLayoutManager(mLayoutManager);
    mFireAdapter = new FirebaseRecyclerAdapter<Poll, PollHolder>(Poll.class, R.layout.latest_item, PollHolder.class, mPollsRef) {
        @Override
        protected void populateViewHolder(PollHolder viewHolder, Poll model, int position) {
            viewHolder.mPollQuestion.setText(model.getQuestion());
            Picasso.with(getActivity().getApplicationContext())
                    .load(model.getImage_URL())
                    .fit()
                    .into(viewHolder.mPollImage);
            Log.v("QUESTION", model.getQuestion());
            Log.v("IMAGE", model.getImage_URL());
        }
    };
    mRecyclerview.setAdapter(mFireAdapter);

    mPollsRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            for (DataSnapshot x : dataSnapshot.getChildren()){
                mFireAdapter.notifyItemInserted(0);
            }
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });





}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentInteractionListener) {
        mListener = (OnFragmentInteractionListener) context;
    } else {
        throw new RuntimeException(context.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

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


public static class PollHolder extends RecyclerView.ViewHolder {

    TextView mPollQuestion;
    ImageView mPollImage;


    public PollHolder(View itemView) {
        super(itemView);

        mPollQuestion = (TextView) itemView.findViewById(R.id.latest_item_question);
        mPollImage = (ImageView) itemView.findViewById(R.id.pollThumbNailImage);

    }
}

/**
 * This interface must be implemented by activities that contain this
 * fragment to allow an interaction in this fragment to be communicated
 * to the activity and potentially other fragments contained in that
 * activity.
 * <p>
 * See the Android Training lesson <a href=
 * "http://developer.android.com/training/basics/fragments/communicating.html"
 * >Communicating with Other Fragments</a> for more information.
 */
public interface OnFragmentInteractionListener {
    // TODO: Update argument type and name
    void onFragmentInteraction(Uri uri);
     }
   }

Most helpful comment

@troy21688 This warning occurs because you are setting your adapter (mRecyclerview.setAdapter(mFireAdapter);) in onCreateView, but creating the adapter in onCreate. Creating the adapter immediately adds a firebase listener which causes notifyItemInserted to be called. The error shouldn't really matter: it's just informing you that while notifyItemInserted was called, no data will be displayed because the adapter hasn't been attached yet. (Your list items should still show up as a batch update once you attach the adapter).

All 22 comments

@troy21688 This warning occurs because you are setting your adapter (mRecyclerview.setAdapter(mFireAdapter);) in onCreateView, but creating the adapter in onCreate. Creating the adapter immediately adds a firebase listener which causes notifyItemInserted to be called. The error shouldn't really matter: it's just informing you that while notifyItemInserted was called, no data will be displayed because the adapter hasn't been attached yet. (Your list items should still show up as a batch update once you attach the adapter).

I have updated the code. Something is going on with the way the Firebase Adapter handles fragments. Since fragments load views before the fragment is actually on screen, it is causing an issue with the way that the data is read and how the Firebase listener methods are triggered. I cannot figure out why, but the RecyclerView simply is not populating.

@troy21688 This shouldn't have anything to do with fragments, I've used the adapter in both fragments and activities.

For your case, I think you should start small and go through everything in steps. Initialize everything in onCreateView and sit down for a long debugging session. First, make sure you can push to the database:

pollsRef.setValue(poll).addOnCompleteListener(
        new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                task.isSuccessful() // should be true
                // if false, see what the exception is:
                task.getException()
            }
        })

If the task is successful, make sure pollsRef.addValueEventListener(...) gets called and check your polls with snapshot.getValue(). If you are getting values, than make sure you can get list items to show up without the firebase adapter. Heck, see if you can display a button! If you've done all of the above and you tried using the firebase adapter again but it still isn't working, then it would be nice for you to provide a mcve.

Here is a mcve of what I'm doing:

public class TeamsFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.recycler_view, container, false);
        mTeams = (RecyclerView) rootView.findViewById(R.id.list);
        mTeams.setHasFixedSize(true);
        mTeams.setLayoutManager(new LinearLayoutManager(getContext()));
        mTeams.setAdapter(new FirebaseIndexRecyclerAdapter<Team, TeamHolder>(
                Team.class,
                R.layout.team_list_row_layout,
                TeamHolder.class,
                Team.getIndicesRef(),
                BaseHelper.getDatabase().child(Constants.FIREBASE_TEAMS)) {
            @Override
            public void populateViewHolder(TeamHolder teamHolder, Team team, int position) {
                teamHolder.bind(getContext(), team);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                FirebaseCrash.report(databaseError.toException()); // You could also check here for errors
            }
        });
        return rootView;
    }
}

I think it may be possible that the values in Firebase are null. I read
this StackOverFlow article explaining that the values need to be written as
HashMaps:

http://stackoverflow.com/questions/37257166/android-firebase-why-does-ondatachange-returns-null-values

On Fri, Dec 16, 2016 at 9:35 PM, Alex Saveau notifications@github.com
wrote:

@troy21688 https://github.com/troy21688 This shouldn't have anything to
do with fragments, I've used the adapter in both fragments and activities.

For your case, I think you should start small and go through everything in
steps. Initialize everything in onCreateView and sit down for a long
debugging session. First, make sure you can push to the database:

pollsRef.setValue(poll).addOnCompleteListener(
new OnCompleteListener() {
@Override
public void onComplete(@NonNull Task task) {
task.isSuccessful() // should be true
// if false, see what the exception is:
task.getException()
}
})

If the task is successful, make sure pollsRef.addValueEventListener(...)
gets called and check your polls with snapshot.getValue(). If you are
getting values, than make sure you can get list items to show up without
the firebase adapter
https://developer.android.com/training/material/lists-cards.html. Heck,
see if you can display a button! If you've done all of the above and you
tried using the firebase adapter again but it still isn't working, then it
would be nice for you to provide a mcve
http://stackoverflow.com/help/mcve.

Here is a mcve of what I'm doing:

public class TeamsFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.recycler_view, container, false);
mTeams = (RecyclerView) rootView.findViewById(R.id.list);
mTeams.setHasFixedSize(true);
mTeams.setLayoutManager(new LinearLayoutManager(getContext()));
mTeams.setAdapter(new FirebaseIndexRecyclerAdapter(
Team.class,
R.layout.team_list_row_layout,
TeamHolder.class,
Team.getIndicesRef(),
BaseHelper.getDatabase().child(Constants.FIREBASE_TEAMS)) {
@Override
public void populateViewHolder(TeamHolder teamHolder, Team team, int position) {
teamHolder.bind(getContext(), team);
}

        @Override
        public void onCancelled(DatabaseError databaseError) {
            FirebaseCrash.report(databaseError.toException()); // You could also check here for errors
        }
    });
    return rootView;
}

}

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/firebase/FirebaseUI-Android/issues/457#issuecomment-267740325,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APuF-E71JVMO9kvesaGGvst9rzDjjTovks5rI1hngaJpZM4LLSeT
.

--
Troy Chuinard, CPA

@troy21688 You shouldn't have to use hashmaps, but you can try.

How would I actually debug? Can you briefly walk me through? Also, can I
share my repository with you on Bit Bucket? I would really like to knock
this out.

On Dec 16, 2016 9:41 PM, "Alex Saveau" notifications@github.com wrote:

@troy21688 https://github.com/troy21688 You shouldn't have to use
hashmaps, but you can try.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/firebase/FirebaseUI-Android/issues/457#issuecomment-267740579,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APuF-IB6DLKrix4n0yIJbVzzR1rxikhkks5rI1nhgaJpZM4LLSeT
.

You'll want to put breakpoints (by clicking a line number) pretty much everywhere I mentioned above. To start debugging, you can attach the debugger, or press the bug button on the left:
image

If I have time, I could take a look at your project. Shoot me an email at saveau.[email protected].

Thanks. It may also have to do with FragmentState PagerAdapter and its
relation to notifyDataSetChanged()......thoughts on that?

On Dec 16, 2016 9:48 PM, "Alex Saveau" notifications@github.com wrote:

You'll want to put breakpoints (by clicking a line number) pretty much
everywhere I mentioned above. To start debugging, you can attach the
debugger, or press the bug button on the left:
[image: image]
https://cloud.githubusercontent.com/assets/9490724/21284169/59f67796-c3c8-11e6-9909-f0ede87c9b8a.png

If I have time, I could take a look at your project. Shoot me an email at
saveau.[email protected].

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/firebase/FirebaseUI-Android/issues/457#issuecomment-267740912,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APuF-JNefMNqZ3_T27A2Xb0TFHAr5ooSks5rI1uVgaJpZM4LLSeT
.

If onCreate() is being called, then it shouldn't be an issue.

I actually addressed the issue with the match_parent, I will push up my
latest commit, my apologies!

On Sat, Dec 17, 2016 at 3:02 PM, Alex Saveau notifications@github.com
wrote:

See #461 (comment)
https://github.com/firebase/FirebaseUI-Android/issues/461#issuecomment-267786725

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/firebase/FirebaseUI-Android/issues/457#issuecomment-267786760,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APuF-P2-vTpEbVAKl103EH5Vq1PLBcV5ks5rJE3qgaJpZM4LLSeT
.

--
Troy Chuinard, CPA

@troy21688 So is it working?

It is not, you can check the latest commit that has incorporated your
changes.

On Sat, Dec 17, 2016 at 3:05 PM, Alex Saveau notifications@github.com
wrote:

@troy21688 https://github.com/troy21688 So is it working?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/firebase/FirebaseUI-Android/issues/457#issuecomment-267786865,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APuF-FLJfb1Iy0h8ELwwl8T5m01Odx1Tks5rJE6QgaJpZM4LLSeT
.

--
Troy Chuinard, CPA

Ok, I'll merge and push a new commit

@troy21688 Merged from master, it's working in my PR.

This is still an issue with FirebaseRecyclerAdapter. I keep getting an error:

RecyclerView: No adapter attached; skipping layout

Did you set layoutWidth and layoutHeight to match_parent?

On Tue, Dec 27, 2016 at 1:32 PM, Igor Ganapolsky notifications@github.com
wrote:

This is still an issue with FirebaseRecyclerAdapter. I keep getting an
error:

RecyclerView: No adapter attached; skipping layout

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/firebase/FirebaseUI-Android/issues/457#issuecomment-269371896,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APuF-GgcCnFDe0_OmFw_sA2UhfSgmhwDks5rMWfDgaJpZM4LLSeT
.

--
Troy Chuinard, CPA

@troy21688 Yes, I am following this codelab: https://codelabs.developers.google.com/codelabs/firebase-android/

Can you post your code? Did you create a question in StackOverflow?

On Tue, Dec 27, 2016 at 1:41 PM, Igor Ganapolsky notifications@github.com
wrote:

@troy21688 https://github.com/troy21688 Yes, I am following this
codelab: https://codelabs.developers.google.com/codelabs/firebase-android/

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/firebase/FirebaseUI-Android/issues/457#issuecomment-269373075,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APuF-AgGVShAouKzinBN13Eqs1LVzFrdks5rMWnogaJpZM4LLSeT
.

--
Troy Chuinard, CPA

I was able to fix that error by issuing the following code in onCreate():
mMessageRecyclerView.setAdapter(mFirebaseAdapter);

It is a bit silly and rudimentary, but it wasn't mentioned in the Codelab at all....

Im having this same problem 5 months later. Love their codelabs but they need to include this.

Was this page helpful?
0 / 5 - 0 ratings