12

У меня возникают проблемы с одним из моих адаптеров. У меня есть выбор изображений, которые я показываю в сетке, как следующее:RecyclerView - внеэкранные объекты, пропускающие первый макет пропуска

enter image description here

Это хорошо работает, когда у меня есть один экран стоит пунктов, и все они показывают вверх штраф. Проблема возникает, когда есть больше элементов для прокрутки вниз. Первоначально, когда я прокручиваю вниз дальше элементы оказываются пустыми, то GridLayout контейнеры там и код для отображения изображений получает бежать, но они не отображаются на экране, как показано здесь:

The problem

Если я прокручиваю обратно вверх, а затем прокрутите вниз снова, изображения появляются именно там, где они должны быть:

enter image description here

Похоже, мне нужно обновить макет или сказать рендерер что-то изменилось, но ничего я стараюсь не кажется делать что-либо. Я попытался позвонить 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(); 
       } 
    }); 
} 

Это метод я использую макет изображения. Это в основном выводит изображения в нерегулярную сетку, поэтому, если есть длинное или высокое изображение, это изображение будет пытаться заняться полной строкой или полным столбцом в зависимости от свободного места. Причина я установить ширину и высоту парам так Picasso не жалуется метода .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); 
       } 
      }); 

Edit: Nov. 15

Вот XML для viewholder макете

<?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> 
+0

Если кто-нибудь может подумать над лучшим заголовком для этого вопроса, не стесняйтесь его редактировать –

+0

Каждый раз, когда вызывается 'onBindViewHolder', вы заменяете все изображения из' GridView'? Зачем? – rubenlop

+0

Ну, может быть где-то от 1-4 изображений в макете для любого элемента, поэтому, когда предметы будут переработаны, это приведет к некоторому выступу. Я думаю, что я мог бы сохранить список ImageViews, а затем просто установить их на видимость, когда они будут переработаны, но производительность прокрутки была достаточно приличной, поэтому мне было меньше беспокоиться об этом, чем проблема рендеринга. –

ответ

-1

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

+0

'setStableIds()' не установлено в true. Метод 'onBindViewHolder' вызывается правильно, как в моем вопросе, но представления изображений не получают вызовы onLayout. Другая информация на зрителе верна. –

0

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

+0

Это использование RecyclerView. У каждого из ViewHolders есть GridLayout внутри него –

+0

GridLayout внутри RecyclerView кажется немного похож на излишний. Вы можете иметь несколько элементов разных размеров в одной строке при использовании RecyclerView с настраиваемым менеджером макетов. Если бы я был вами, я бы начал, пытаясь сделать вещи проще (либо избавиться от GridView, либо заменить его чем-то более простым, либо использовать только GridView без сложного макета вокруг него) только для целей тестирования. Другой вариант - заменить Picasso на Glide (снова для устранения). –

0

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

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

Смежные вопросы