RecyclerView - 跳过第一个布局的屏幕外项目通过

时间:2016-11-07 03:11:29

标签: android android-recyclerview android-gridlayout gridlayoutmanager

我的一个适配器出现问题。我有一系列图像,我在网格中显示如下:

enter image description here

当我有一个单一屏幕的项目并且它们都显示正常时,这很有效。当有更多项目向下滚动时,会出现问题。最初当我向下滚动时,项目显示为空白,GridLayout容器在那里,显示图像的代码会运行,但它们不会出现在屏幕上,如下所示:

The problem

如果我向上滚动,然后再向下滚动,图像就会显示在它们应该的位置:

enter image description here

似乎我需要刷新布局或告诉渲染器某些内容已发生变化,但我尝试的任何东西似乎都没有做任何事情。我尝试过调用requestLayoutinvalidate但似乎没有解决问题。我还注册了一个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);
        }
    }
}

为什么这似乎会跳过最初不可见的项目的第一个渲染过程?

编辑:我做了一些重构,所以我可以重新使用imageview而不是删除/创建新的,并且发现为这些项添加的视图没有得到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);
                }
            });

编辑:11月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>

3 个答案:

答案 0 :(得分:0)

当您尝试列出一个列表时,您应该考虑使用RecyclerView类。这样您就可以使用LayoutManager更轻松地布局图片。这可能会更好,因为您的布局不是标准网格(具有恒定的列宽和高度)。可以找到一个有趣的示例来查看自定义LayoutManager {。{3}}。

答案 1 :(得分:0)

由于没有完整的代码,所以我可以猜测。当您第一次滚动回收器适配器时,似乎在网格有机会创建和添加视图之前创建视图。我想如果你使用另一个适配器,即网格适配器来填充网格视图,它应该可以正常工作。

我正在使用onScrollListener为您的回收站视图检测滚动,然后加载更多网格,然后通过网格适配器为每个网格加载更多网格项。

答案 2 :(得分:-1)

检查您是否有setHasStableIds(true),如果您这样做,则必须覆盖getItemId(int position)以指定每个视图的ID。如果您不这样做,则不会触发后续onBindViewholders()次呼叫,您的观点不会更新