webentwicklung-frage-antwort-db.com.de

Laden einer großen Anzahl von Artikeln in der Recycler-Ansicht

Ich habe eine Recycler-Ansicht in einem Fragment und versuche im Grunde, die Songliste in der Recycler-Ansicht zu laden. Jede Zeile der Recycler-Ansicht enthält eine Bildansicht (für Albumcover) und Textansicht (für Songennamen). . Ich habe Probleme, wenn der Datensatz sehr groß ist, das heißt, wenn zu viele Titel vorhanden sind, die Ansicht des Recyclers verzögert und die App eine ANR ausgibt. Ich verwende Glide, um Albumcover in die Bildansicht jeder Zeile zu laden. Wie kann Google Music Player so viele Titel ohne Verzögerung anzeigen?

Edit: Das ist mein 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;
    }
}

Und das ist mein RecyclerViewAdapter zusammen mit 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);
    }
}

Die Recyclingübersicht funktioniert gut, wenn es nur 150-200 Artikel gibt, aber wenn 600-700 Artikel erreicht werden, verlangsamt sich die gesamte App. Könnte dies an der Art liegen, wie ich glide in onBindViewHolder verwendet habe?

6

Das Problem wurde gelöst, indem die NestedScrollView über dem Recycler entfernt wurde. Der nestedscrollview erlaubte nicht, dass der Recyclerview.addOnScrollListener () aufgerufen wurde, weshalb ich beim Laden weiterer Elemente eine Verzögerung erhielt loadOnScroll für 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

Antwort sortieren:

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

Aus dem Dokument von RecyclerView.LayoutManager # setAutoMeasureEnabled () wissen wir:

Diese Methode wird normalerweise vom LayoutManager mit dem Wert {@code true} aufgerufen, wenn WRAP_CONTENT unterstützt werden soll

Es funktioniert durch Aufrufen von {@link LayoutManager # onLayoutChildren (Recycler, State)} während eines Aufrufs von {@link RecyclerView # onMeasure (int, int)}. Anschließend werden die gewünschten Abmessungen basierend auf den Positionen von Kindern berechnet.

Wenn wir mAutoMeasure = true einstellen, wird LayoutManager#onLayoutChildren(Recycler, State) während eines RecyclerView#onMeasure(int, int)-Aufrufs aufgerufen. Die onMeasure()-Methode jeder untergeordneten Ansicht wird aufgerufen, dies kostet zu viel Zeit.

 enter image description here


Schauen wir uns den Konstruktor von LinearLayoutManager an

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

Nachdem wir mAutoMeasure = false eingestellt haben, wird alles in Ordnung sein.

 enter image description here

9
tiiime

Laden Sie die Daten auf einmal? RecycleView sollte kein Problem haben. Wenn Sie zu viele Daten haben, kann die Verarbeitung selbst zu lange dauern. Sie sollten die Daten in Blöcken laden und den Bildlaufstatus des Benutzers überprüfen und den nächsten Stapel laden usw. So wie Instagram oder Facebook dies tut.

1
breakline

Guten Abend, Naimish. Die einzigen Gründe, an die ich denke, sind, dass Sie die Recycler-Ansicht möglicherweise nicht ordnungsgemäß implementiert haben. 

Bitte betrachten Sie dieses Projekt als Beispiel für eine gute Implementierung

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

Schauen Sie sich diese beiden Bücher auch zu Lernzwecken an und durchsuchen Sie das Kapitel zur Recycling-Ansicht.

https://google-developer-training.gitbooks.io/Android-developer-fundamentalscourse-practicals/https://google-developer-training.gitbooks.io/Android-developer -Fundamente-Kurs-Konzepte/

0
Roman Gherta