RecyclerView No adapter attached; skipping layout, Data not showing
Asked Answered
Y

2

1

I'm making a music player application where I'm using a loader to load song data to the adapter which is to be shown using a RecyclerView. However, I'm getting this weird error of my adapter methods not working. Only the constructor method of adapter is getting called. I'm also getting "No adapter attached; skipping layout" despite going through all the available solutions here in stack overflow.

Few points to be noted:

  • I've tried all the solutions for "No adapter attached; skipping layout" in recyclerview No adapter attached; skipping layout thread and all associated duplicate threads.
  • The RecyclerView I'm using is not the regular one but FastScrollRecyclerView, but since it extends from the regular RecyclerView and there are no relatable issues mentioned on github so I'm convinced that using this library is not an issue here
  • I've also tried all solutions for the adapter methods not being called from this thread but no luck.

Here's the code:

SongsFragment.java

public class SongsFragment extends Fragment
    implements LoaderManager.LoaderCallbacks<List<Song>>{

public static final String LOG_TAG = SongsFragment.class.getSimpleName();
private static final int LOADER_ID = 1;
private ContentResolver mContentResolver;
private SongListAdapter mSongListAdapter;
private List<Song> mSongs;
@BindView(R.id.rvSongs) FastScrollRecyclerView mRecyclerView;

public SongsFragment() {}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ButterKnife.bind(getActivity());
    mSongs = new ArrayList<>();
    mRecyclerView = new FastScrollRecyclerView(getContext());
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    mRecyclerView.setLayoutManager(layoutManager);
    mRecyclerView.setHasFixedSize(true);
    mSongListAdapter = new SongListAdapter(getContext(), mSongs);
    mRecyclerView.setAdapter(mSongListAdapter);

}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    getLoaderManager().initLoader(LOADER_ID, null, this).forceLoad();
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_songs, container, false);
}

@Override
public Loader<List<Song>> onCreateLoader(int id, Bundle args) {
    mContentResolver = getActivity().getContentResolver();
    return new SongsLoader(getContext(), mContentResolver);
}

@Override
public void onLoadFinished(Loader<List<Song>> loader, List<Song> data) {
    mSongs = data;
    mSongListAdapter.setData(mSongs);
}

@Override
public void onLoaderReset(Loader<List<Song>> loader) {
    mSongListAdapter.setData(new ArrayList<Song>());
}

}

SongsListAdapter.java

public class SongListAdapter
  extends FastScrollRecyclerView.Adapter<SongListAdapter.SongItemViewHolder>
  implements FastScrollRecyclerView.SectionedAdapter{

public static final String LOG_TAG = SongListAdapter.class.getSimpleName();
private Context mContext;
private List<Song> mSongList = new ArrayList<>();

public SongListAdapter(Context context, List<Song> songList) {
    Log.d(LOG_TAG, "Constructor called");
    mContext = context;
    mSongList = songList;
}

@NonNull
@Override
public String getSectionName(int position) {
    return String.valueOf(mSongList.get(position).getTitle().charAt(0)).toUpperCase();
}

@Override
public SongItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    Log.d(LOG_TAG, "onCreateViewHolder called");
    View view = LayoutInflater.from(mContext).inflate(R.layout.list_item_song, null);
    return new SongItemViewHolder(view);
}

@Override
public void onBindViewHolder(SongItemViewHolder holder, int position) {
    Log.d(LOG_TAG, "onBindViewHolder called");
    Uri albumArtUri = mSongList.get(position).getAlbumArtUri();
    Glide.with(mContext)
            .load(albumArtUri)
            .into(holder.albumArt);
    holder.titleText.setText(mSongList.get(position).getTitle());
    holder.artistText.setText(mSongList.get(position).getArtistName());
    Log.d("Data", albumArtUri.toString() + "\n" + mSongList.get(position).getTitle() + "\n" + mSongList.get(position).getArtistName());
}

@Override
public int getItemCount() {
    Log.d(LOG_TAG, "getItemCount called");
    return (mSongList != null ? mSongList.size() : 0);
}

public void setData(List<Song> songs){
    mSongList = songs;
    notifyDataSetChanged();
}

public class SongItemViewHolder extends FastScrollRecyclerView.ViewHolder {
    ImageView albumArt;
    TextView titleText;
    TextView artistText;

    SongItemViewHolder(View view) {
        super(view);
        Log.d(LOG_TAG, "SongItemViewHolder called");
        albumArt = (ImageView) view.findViewById(R.id.item_song_image);
        titleText = (TextView) view.findViewById(R.id.item_song_title);
        artistText = (TextView) view.findViewById(R.id.item_song_artist_name);
    }
}

}

fragment_songs.xml (SongsFragment is inflating this layout)

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="match_parent">


    <com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
        android:id="@+id/rvSongs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:fastScrollPopupBgColor="@color/colorAccent"
        app:fastScrollPopupTextColor="@android:color/primary_text_dark"
        app:fastScrollThumbColor="@color/colorAccent"/>

</ScrollView>

list_item_song.xml (Individual item in recycler view)

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

<View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:layout_gravity="center_horizontal"
    android:layout_marginStart="12dp"
    android:layout_marginLeft="12dp"
    android:layout_marginRight="12dp"
    android:background="#FFFFFF"/>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:paddingBottom="10dp"
    android:paddingLeft="8dp"
    android:paddingRight="8dp"
    android:paddingStart="8dp"
    android:paddingTop="10dp">

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/item_song_image"
            android:layout_width="64dp"
            android:src="@drawable/music_placeholder"
            android:layout_height="64dp"/>

    </FrameLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/item_song_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:text="Song_Title"/>

        <TextView
            android:id="@+id/item_song_artist_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="3dp"
            android:textSize="12sp"
            android:text="Song_Artist"/>

    </LinearLayout>

</LinearLayout>

</LinearLayout>

This has been really frustrating. Please review the code and help me with this. I think I've done everything correctly but I may be wrong. I know scrollview and recyclerview don't go that well but I've seen the preview and the recycler view shows. Any help will be appreciated. Thanks!

Yiddish answered 28/5, 2017 at 8:23 Comment(6)
Log the size of the list being supplied to your SongListAdapter.setData().Gavotte
Used a log statement to know the size of the list passing on to SongListAdapter.setData() . The size is 609. 609 songs are passed to that song for my device.Yiddish
Do you get Log.d(LOG_TAG, "getItemCount called");?Gavotte
No, only the log in SongsListAdapter constructor appearsYiddish
Try wrapping your notifyDataSetChanged() in runOnUIThread() juat for the sake of it.Gavotte
Just tried it but not working. It's taking forever to debug thisYiddish
Y
1

Ugh! I wasted a lot of time in this but finally came out with the solution. I removed Butterknife binding and used conventional findViewById inside onCreateView() of SongsFragment after capturing the view instance by changing the onCreateView() to this:

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_songs, container, false);
mRecyclerView = (FastScrollRecyclerView) rootView.findViewById(R.id.rvSongs);
//Rest of the things
}

Turns out I was using ButterKnife the wrong way so the instance mRecyclerView was null but later with line mRecyclerView = new FastScrollRecyclerView(getContext()); it wasn't null anymore but it still wasn't connected to the view so I didn't get NullPointerException and the code didn't work.

I know it was a noob mistake :D

Correct way to use ButterKnife with fragments as picked up from official website is:

public class FancyFragment extends Fragment {
  @BindView(R.id.button1) Button button1;
  @BindView(R.id.button2) Button button2;

  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fancy_fragment, container, false);
    ButterKnife.bind(this, view);
    // TODO Use fields...
    return view;
  }
}
Yiddish answered 15/6, 2017 at 13:27 Comment(0)
D
1

Try setting adapter in onLoadFinished() and also use getActivity() for context in adapter object

@Override
public void onLoadFinished(Loader<List<Song>> loader, List<Song> data) {
    mSongs = data;
     mSongListAdapter = new SongListAdapter(getActivity(), mSongs);
    mRecyclerView.setAdapter(mSongListAdapter);
}

also in this mRecyclerView = new FastScrollRecyclerView(getContext()); to

mRecyclerView = new FastScrollRecyclerView(getActivity());

Basically use getActivity() for context in fragment class

Democracy answered 28/5, 2017 at 8:27 Comment(2)
Have already tried to set adapter in onLoadFinished() but that didn't work out. Tried using getActivity() but didn't work out :(Yiddish
Thanks @quicklearner for having a look! I'm stuck on this for a long time now. I hope I get the solution soon.Yiddish
Y
1

Ugh! I wasted a lot of time in this but finally came out with the solution. I removed Butterknife binding and used conventional findViewById inside onCreateView() of SongsFragment after capturing the view instance by changing the onCreateView() to this:

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_songs, container, false);
mRecyclerView = (FastScrollRecyclerView) rootView.findViewById(R.id.rvSongs);
//Rest of the things
}

Turns out I was using ButterKnife the wrong way so the instance mRecyclerView was null but later with line mRecyclerView = new FastScrollRecyclerView(getContext()); it wasn't null anymore but it still wasn't connected to the view so I didn't get NullPointerException and the code didn't work.

I know it was a noob mistake :D

Correct way to use ButterKnife with fragments as picked up from official website is:

public class FancyFragment extends Fragment {
  @BindView(R.id.button1) Button button1;
  @BindView(R.id.button2) Button button2;

  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fancy_fragment, container, false);
    ButterKnife.bind(this, view);
    // TODO Use fields...
    return view;
  }
}
Yiddish answered 15/6, 2017 at 13:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.