Подтвердить что ты не робот

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>
4b9b3361

Ответ 1

Как вы пытаетесь сделать список, возможно, вам стоит рассмотреть класс RecyclerView. Таким образом, вы сможете использовать LayoutManager для более легкого компоновки ваших изображений. Это может быть лучше, поскольку ваш макет не является стандартной сеткой (которая имеет постоянные ширины столбцов и высоты). Интересный пример для пользовательского LayoutManager можно найти здесь.

Ответ 2

Поскольку полного кода нет, я могу сделать предположение. Кажется, что когда вы прокручиваете первый раз, адаптер recycler создает представления, прежде чем сетка получит возможность создавать и добавлять представления. Я предполагаю, что если вы используете другой адаптер, то есть адаптер сетки, чтобы заполнить ваши виды сетки, он должен работать нормально.

Я уверен, что вы используете onScrollListener для просмотра recycler, чтобы обнаруживать прокрутку, а затем загружать больше сеток, которые, в свою очередь, загружают больше элементов сетки через сетевой адаптер для каждой сетки.

Ответ 3

Проверьте, есть ли у вас setHasStableIds(true), если вы это делаете, вам нужно переопределить getItemId(int position), чтобы указать идентификаторы для каждого вида. Если вы этого не сделаете, последующие вызовы onBindViewholders() не будут запускаться, и ваше представление не будет обновляться