RecyclerView - внеэкранные объекты, пропускающие первый макет
У меня возникают проблемы с одним из моих адаптеров. У меня есть выбор изображений, которые я отображаю в сетке, например:
![введите описание изображения здесь]()
Это хорошо работает, когда у меня есть один размер предметов, и все они выглядят хорошо. Проблема возникает, когда есть больше элементов для прокрутки вниз. Первоначально, когда я прокручиваю вниз, элементы отображаются пустыми, контейнеры GridLayout
находятся там, и код для отображения изображений запускается, но они не отображаются на экране, как показано ниже:
![Проблема]()
Если я прокручу резервную копию, затем снова прокрутите назад, изображения будут отображаться точно там, где они должны быть:
![введите описание изображения здесь]()
Кажется, мне нужно обновить макет или сообщить визуализатору, что что-то изменилось, но ничего, что я пытаюсь, кажется, ничего не делает. Я пробовал звонить requestLayout
и invalidate
, но, похоже, это не решает проблему. Я также зарегистрировал GlobalOnLayoutListener, но это не улучшилось.
Вот соответствующий код:
Это из моего метода onBindViewHolder
final GridLayout grid = ((KKAlbumViewHolder) holder).mGridLayout;
grid.removeAllViews();
int width = grid.getWidth();
if(width > 0) {
albumHolder.setPreviewImages(posts);
}else {
albumHolder.itemView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
albumHolder.itemView.removeOnLayoutChangeListener(this);
albumHolder.setPreviewImages(posts);
albumHolder.mGridLayout.invalidate();
}
});
}
Это метод, который я использую для компоновки изображений. Это в основном выводит изображения в нерегулярную сетку, поэтому, если есть длинное или высокое изображение, это изображение будет пытаться заняться полной строкой или полным столбцом в зависимости от свободного места. Причина, по которой я устанавливаю ширину и высоту параметра, поэтому пикассо не жалуется на метод .fit()
:
public void setPreviewImages(ArrayList<KKPost> posts) {
Picasso picasso = Picasso.with(itemView.getContext());
int space = 0;
int tl = 1 << 0;
int tr = 1 << 1;
int bl = 1 << 2;
int br = 1 << 3;
int lCol = tl | bl;
int rCol = tr | br;
int tRow = tl | tr;
int bRow = bl | br;
int full = lCol | rCol;
boolean hasLongImage = false;
for(KKPost post : posts) {
if(space == full){
break;
}
int margin = 2;
ImageView view = new ImageView(itemView.getContext());
GridLayout.LayoutParams param = new GridLayout.LayoutParams();
view.setBackgroundColor(Color.LTGRAY);
if(posts.size() < 4){
param.columnSpec = GridLayout.spec(0, 2);
param.rowSpec = GridLayout.spec(0, 2);
param.width = mGridLayout.getWidth();
param.height = mGridLayout.getHeight();
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
break;
}
if (post.aspectRatio >= 1.2 && !hasLongImage) {
//landscape
if((space & tRow) == 0){
param.rowSpec = GridLayout.spec(0, 1);
param.columnSpec = GridLayout.spec(0, 2);
param.height = mGridLayout.getHeight()/2;
param.width = mGridLayout.getWidth();
param.bottomMargin = margin;
space |= tRow;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
hasLongImage = true;
continue;
}
if((space & bRow) == 0){
param.rowSpec = GridLayout.spec(1, 1);
param.columnSpec = GridLayout.spec(0, 2);
param.height = mGridLayout.getHeight()/2;
param.width = mGridLayout.getWidth();
param.topMargin = margin;
space |= bRow;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
hasLongImage = true;
continue;
}
} else if (post.aspectRatio <= 0.8 && post.aspectRatio > 0 && !hasLongImage) {
//portrait
if((space & lCol) == 0){
param.columnSpec = GridLayout.spec(0, 1);
param.rowSpec = GridLayout.spec(0, 2);
param.height = mGridLayout.getHeight();
param.width = mGridLayout.getWidth()/2;
param.rightMargin = margin;
space |= lCol;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
hasLongImage = true;
continue;
}
if((space & rCol) == 0){
param.columnSpec = GridLayout.spec(1, 1);
param.rowSpec = GridLayout.spec(0, 2);
param.height = mGridLayout.getHeight();
param.width = mGridLayout.getWidth()/2;
param.leftMargin = margin;
space |= rCol;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
hasLongImage = true;
continue;
}
}
//square or no space or unknown
if((space & tl) == 0){
param.columnSpec = GridLayout.spec(0,1);
param.rowSpec = GridLayout.spec(0,1);
space |= tl;
param.bottomMargin = margin;
param.rightMargin = margin;
}else
if((space & tr) == 0) {
param.columnSpec = GridLayout.spec(1,1);
param.rowSpec = GridLayout.spec(0,1);
space |= tr;
param.bottomMargin = margin;
param.leftMargin = margin;
}else
if((space & bl) == 0){
param.columnSpec = GridLayout.spec(0,1);
param.rowSpec = GridLayout.spec(1,1);
space |= bl;
param.rightMargin = margin;
param.topMargin = margin;
}else
if((space & br) == 0){
param.columnSpec = GridLayout.spec(1,1);
param.rowSpec = GridLayout.spec(1,1);
space |= br;
param.leftMargin = margin;
param.topMargin = margin;
}
param.height = mGridLayout.getHeight()/2;
param.width = mGridLayout.getWidth()/2;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
}
}
}
Почему это, кажется, пропускает первый проход рендеринга для элементов, которые не видны изначально?
EDIT: я немного переработал рефакторинг, чтобы повторно использовать образы изображений вместо удаления/создания новых и обнаружил, что представления, добавляемые для этих элементов, не получают обратного вызова onLayoutChange. Все остальные элементы получают обратный вызов onLayoutChange и, как и прежде, элементы, которые не получают вызов изначально, получают его после прокрутки вверх и прокрутки вниз. См:
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
view.removeOnLayoutChangeListener(this);
picasso.load(post.url).into(view);
}
});
Редактировать: 15 ноября
Вот xml для макета зрителя
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
android:id="@+id/album_card_view"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
card_view:cardCornerRadius="@dimen/feed_card_corner_radius"
card_view:cardElevation="@dimen/cardview_default_elevation"
card_view:cardMaxElevation="@dimen/cardview_default_elevation"
card_view:cardUseCompatPadding="true"
xmlns:autofit="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.percent.PercentRelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/percent_layout">
<GridLayout
android:background="@color/white"
android:id="@+id/preview_grid_layout"
android:columnCount="2"
android:rowCount="2"
android:alignmentMode="alignBounds"
android:layout_height="0dp"
android:layout_width="0dp"
app:layout_widthPercent="100%"
app:layout_aspectRatio="100%">
</GridLayout>
</android.support.percent.PercentRelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/card_padding_4dp"
android:orientation="vertical">
<TextView
android:id="@+id/album_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Family Photos"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
Ответы
Ответ 1
Как вы пытаетесь сделать список, возможно, вам стоит рассмотреть класс RecyclerView. Таким образом, вы сможете использовать LayoutManager для более легкого компоновки ваших изображений. Это может быть лучше, поскольку ваш макет не является стандартной сеткой (которая имеет постоянные ширины столбцов и высоты). Интересный пример для пользовательского LayoutManager можно найти здесь.
Ответ 2
Поскольку полного кода нет, я могу сделать предположение. Кажется, что когда вы прокручиваете первый раз, адаптер recycler создает представления, прежде чем сетка получит возможность создавать и добавлять представления. Я предполагаю, что если вы используете другой адаптер, то есть адаптер сетки, чтобы заполнить ваши виды сетки, он должен работать нормально.
Я уверен, что вы используете onScrollListener для просмотра recycler, чтобы обнаруживать прокрутку, а затем загружать больше сеток, которые, в свою очередь, загружают больше элементов сетки через сетевой адаптер для каждой сетки.
Ответ 3
Проверьте, есть ли у вас setHasStableIds(true)
, если вы это делаете, вам нужно переопределить getItemId(int position)
, чтобы указать идентификаторы для каждого вида. Если вы этого не сделаете, последующие вызовы onBindViewholders()
не будут запускаться, и ваше представление не будет обновляться