web-dev-qa-db-fra.com

Chargement d'un grand nombre d'articles dans la vue Recycler

J'ai une vue recycleur dans un fragment et, en gros, j'essaie de charger une liste de chansons dans la vue recycleur. Chaque rangée de la vue recycleur contient un imageview (pour la pochette de l'album) et textview (pour le nom de la chanson) . J'ai des problèmes lorsque la taille du jeu de données est énorme, c'est-à-dire lorsqu'il y a trop de chansons, que la vue du recycleur est à la traîne et que l'application finit par donner un ANR. .Comment google music player peut-il afficher un si grand nombre de chansons sans aucun décalage?

Edit: C'est mon SongsFragment

public class SongsFragment extends Fragment {
static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
ProgressBar progressBar;    // progress bar to show after every 30 items
NestedScrollView nestedScrollView;  //for smooth scrolling of recyclerview as well as to detect the end of recyclerview
RecyclerView recyclerView;
ArrayList<Song> songMainList = new ArrayList<>();  //partial list in which items are added
ArrayList<Song> songAllList = new ArrayList<>(); //Complete List of songs
SongAdapter songsAdapter;
private LinearLayoutManager layoutManager;

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

    nestedScrollView = (NestedScrollView) rootView.findViewById(R.id.nestedScrollView);
    progressBar = (ProgressBar) rootView.findViewById(R.id.progressBar);

    String songJson = getActivity().getIntent().getStringExtra("songList");
    songAllList = new Gson().fromJson(songJson, new TypeToken<ArrayList<Song>>() {
    }.getType());
            //Getting list of all songs in songAllList

    if (songAllList.size() > 30) {  
        songMainList = new ArrayList<>(songAllList.subList(0,30));
    } else {
        songMainList = songAllList;
    }

    //if size of fetched songAllList>30 then add only 30 rows to songMainList

    recyclerView = (RecyclerView) rootView.findViewById(R.id.songs);
    int spanCount = 1; // 2 columns
    int spacing = 4; // 50px
    recyclerView.addItemDecoration(new GridItemDecoration(spanCount, spacing, true));
    recyclerView.setHasFixedSize(true);
    recyclerView.setNestedScrollingEnabled(false);
    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    songsAdapter = new SongAdapter(getActivity(), songMainList, recyclerView);

    nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            View view = (View) nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1);
            int diff = (view.getBottom() - (nestedScrollView.getHeight() + nestedScrollView
                    .getScrollY()));

            if (diff == 0) {    //NestedScrollView scrolled to bottom
                progressBar.setVisibility(View.VISIBLE);    //show progressbar
                new Handler().postDelayed(new Runnable() { 
                    @Override
                    public void run() {
                        if (songMainList.size() < songAllList.size()) {
                            int x = 0, y = 0;
                            if ((songAllList.size() - songMainList.size()) >= 30) {
                                x = songMainList.size();
                                y = x + 30;
                            } else {
                                x = songMainList.size();
                                y = x + songAllList.size() - songMainList.size();
                            }
                            for (int i = x; i < y; i++) {
                                songMainList.add(songAllList.get(i));   //Adding new items from songAllList to songMainList one by one
                                songsAdapter.notifyDataSetChanged();
                            }
                        }
                        progressBar.setVisibility(View.GONE);
                    }
                }, 1500);


            }
        }
    });
    recyclerView.setAdapter(songsAdapter);


    return rootView;
    }
}

Et voici mon RecyclerViewAdapter avec viewholder

public class SongAdapter extends RecyclerView.Adapter {

private List<Song> songsList;
private Context c;

private RecyclerView.ViewHolder holder;

public SongAdapter(Context context) {
    mainActivityContext = context;
}

public SongAdapter(Context context, List<Song> songs, RecyclerView recyclerView) {
    songsList = songs;
    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
    c = context;
}

public SongAdapter getInstance() {
    return SongAdapter.this;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.song_list_row, parent, false);
    return new SongViewHolder(view,c);
}

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {

    if (holder instanceof SongViewHolder) {
        Song song = songsList.get(position);
        this.holder = holder;

        String name = song.getName();
        String artist = song.getArtist();
        String imagepath = song.getImagepath();

        ((SongViewHolder) holder).name.setText(name);

        ((SongViewHolder) holder).artist.setText(artist);

        if (!imagepath.equalsIgnoreCase("no_image")) //if the album art has valid  imagepath for this song

            Glide.with(c).load(imagepath)
                    .centerCrop()
                    .into(((SongViewHolder) holder).iv);
        else
            ((SongViewHolder) holder).iv.setImageResource(R.drawable.empty);

        ((SongViewHolder) holder).song = song;
    }
}

@Override
public int getItemCount() {
    return songsList.size();
}

static class SongViewHolder extends RecyclerView.ViewHolder{

    ImageView iv;
    TextView name, artist;
    CardView songListCard;
    private Context ctx;

    private OnLongPressListener mListener;

    SongViewHolder(View v, Context context) {
        super(v);
        this.ctx = context;
        iv= (ImageView) v.findViewById(R.id.album_art);
        name= (TextView) v.findViewById(R.id.name);
        artist= (TextView) v.findViewById(R.id.artist_mini);
        songListCard = (CardView) v.findViewById(R.id.song_list_card);
    }
}

La liste de recyclage fonctionne bien lorsqu'il n'y a que 150 à 200 éléments, mais lorsque vous atteignez 600 à 700 éléments, l'application entière ralentit. Cela est-il dû à la façon dont j'ai utiliséglide in onBindViewHolder ?

6

Le problème a été résolu en supprimant NestedScrollView sur recyclerview . Nestedcrollview n'autorisait pas l'appel de recyclerview.addOnScrollListener (), ce qui m'avait retardé le chargement de plusieurs éléments . Voici comment j'ai implémenté le loadOnScroll for RecyclerView-

 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            if (!recyclerView.canScrollVertically(1))
                onScrolledToBottom();
        }
    });

private void onScrolledToBottom() {
    if (songMainList.size() < songAllList.size()) {
        int x, y;
        if ((songAllList.size() - songMainList.size()) >= 50) {
            x = songMainList.size();
            y = x + 50;
        } else {
            x = songMainList.size();
            y = x + songAllList.size() - songMainList.size();
        }
        for (int i = x; i < y; i++) {
            songMainList.add(songAllList.get(i));
        }
        songsAdapter.notifyDataSetChanged();
    }

}
4

Trier la réponse:

LinearLayoutManager(context).apply { isAutoMeasureEnabled = false }
// or in Java
layoutManager.setAutoMeasureEnabled(false)

Extrait de la doc de RecyclerView.LayoutManager # setAutoMeasureEnabled () nous savons:

LayoutManager appelle généralement cette méthode avec la valeur {@code true} si elle souhaite prendre en charge WRAP_CONTENT.

Cela fonctionne en appelant {@link LayoutManager # onLayoutChildren (Recycler, État)} pendant un appel {@link RecyclerView # onMeasure (int, int)}, puis en calculant les dimensions souhaitées en fonction de la position des enfants.

Si nous définissons mAutoMeasure = true, il appellera LayoutManager#onLayoutChildren(Recycler, State) lors d'un appel RecyclerView#onMeasure(int, int). La méthode onMeasure() de chaque vue enfant sera appelée, cela coûtera trop de temps.

 enter image description here


Regardons le constructeur de LinearLayoutManager

public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
    setOrientation(orientation);
    setReverseLayout(reverseLayout);
    setAutoMeasureEnabled(true);
}

Donc, après avoir défini mAutoMeasure = false, tout ira bien.

 enter image description here

9
tiiime

Chargez-vous les données en une fois? RecycleView ne devrait pas avoir de problème, je pense que si vous avez trop de données, le traitement en soi peut prendre trop de temps. Vous devez charger les données en morceaux, vérifier l'état de défilement de l'utilisateur et charger le prochain lot, etc. Comment Instagram ou Facebook le fait-il.

1
breakline

Bonsoir Naimish La seule raison pour laquelle je pense est que vous n'avez peut-être pas correctement implémenté la vue recycleur. 

Veuillez regarder ce projet comme exemple d'une bonne mise en œuvre

https://github.com/google-developer-training/Android-fundamentals/tree/master/RecyclerView

Également à des fins d’apprentissage, consultez ces 2 livres et parcourez le chapitre consacré à la vue des recycleurs.

https://google-developer-training.gitbooks.io/Android-developer-fundamentals-course-practicals/https://google-developer-training.gitbooks.io/Android-developer -fundamentals-course-concepts/

0
Roman Gherta