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 ?
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();
}
}
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.
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.
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.
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/