Проблемы с прокруткой Dynamic Endless RecyclerView

Мне нужно создать следующий макет введите описание изображения здесь

До сих пор я успешно создал макет и заполнил все виды. Тем не менее, я столкнулся с проблемой при создании ReyclerView Endless на первом фрагменте.

Считайте, что RecyclerView имеет 10 элементов при первой загрузке, теперь на свитке я добавляю еще 10 элементов и так далее. Однако RecyclerView не отображает эти элементы, высота которых фиксируется в конце 10-го элемента. Я знаю, что элементы загружены правильно в RecyclerView, и если я попытаюсь прокрутить два пальца на эмуляторе (GenyMotion), RecyclerView прокручивается просто отлично.

Обновление: -

Код фрагмента RecyclerView -

public class CheckInFragmentRecyclerAdapter extends RecyclerView.Adapter<CheckInFragmentRecyclerAdapter.ViewHolder> {
    final List<StoreNew> stores;

    public CheckInFragmentRecyclerAdapter(final List<StoreNew> stores) {
        this.stores = stores;
    }

    @Override
    public CheckInFragmentRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.child_check_in_fragment, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(CheckInFragmentRecyclerAdapter.ViewHolder holder, int position) {
        // Setting data
    }


    @Override
    public int getItemCount() {
        return stores.size();
    }

    /**
     * Function to clear existing data from list
     * @param stores StoreNew instance containing store information
     */
    public void update(final List<StoreNew> stores) {
        this.stores.clear();
        this.stores.addAll(stores);
        notifyDataSetChanged();
    }

    /**
     * Function to add more data to list
     * @param stores StoreNew instance containing store information
     */
    public void addNewList(final List<StoreNew> stores) {
        this.stores.addAll(stores);
        notifyDataSetChanged();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public ViewHolder(View itemView) {
            super(itemView);

            // Initializing component
        }
    }
}

Обновление: -

Добавление макетов для используемых экранов.

main_screen.xml. Это главный экран

<android.support.v4.widget.NestedScrollView
        android:id="@+id/scrollView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_above="@+id/home_footer"
        android:layout_below="@+id/toolbar"
        android:fillViewport="true">

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <!-- Sliding Tab for showing images -->
            <com.example.slidingtab.SlidingTabLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/white" />
            <!-- ViewPager for Images -->
            <android.support.v4.view.ViewPager
                android:id="@+id/vpOffers"
                android:layout_width="match_parent"
                android:layout_height="150dp"
                android:layout_marginTop="8dp" />
            <!-- Segmented Control for fragments -->
            <info.hoang8f.android.segmented.SegmentedGroup xmlns:segmentedgroup="http://schemas.android.com/apk/res-auto"
                android:id="@+id/segmented2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="15dp"
                android:layout_marginEnd="10dp"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginStart="10dp"
                android:layout_marginTop="10dp"
                android:orientation="horizontal"
                segmentedgroup:sc_border_width="1dp"
                segmentedgroup:sc_corner_radius="4dp"
                segmentedgroup:sc_tint_color="@color/black">

                <RadioButton
                    android:id="@+id/rbTab1"
                    style="@style/segmented_radio_button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:checked="true"
                    android:textSize="@dimen/normal_text"
                    android:text="@string/check_in" />

                <RadioButton
                    android:id="@+id/rbTab2"
                    style="@style/segmented_radio_button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:textSize="@dimen/normal_text"
                    android:text="@string/upload_bill" />

                <RadioButton
                    android:id="@+id/rbTab3"
                    style="@style/segmented_radio_button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:textSize="@dimen/normal_text"
                    android:text="@string/redeem" />

            </info.hoang8f.android.segmented.SegmentedGroup>

            <!-- Custom wrap content ViewPager containing fragments -->
            <!-- This will make sure that the height of ViewPager is equal to height of Fragment -->
            <com.example.ui.custom.WrapContentHeightViewPager
                android:id="@+id/vpFragments"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="-7dp" />
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

WrapContentHeightViewPager.java

public class WrapContentHeightViewPager extends ViewPager {

    private static final String TAG = WrapContentHeightViewPager.class.getSimpleName();
    private int height = 0;
    private int decorHeight = 0;
    private int widthMeasuredSpec;

    private boolean animateHeight;
    private int rightHeight;
    private int leftHeight;
    private int scrollingPosition = -1;
    private boolean enabled;

    public WrapContentHeightViewPager(Context context) {
        super(context);
        init();
    }

    public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.enabled = true;
        init();
    }

    private void init() {
        addOnPageChangeListener(new OnPageChangeListener() {


            public int state;

            @Override
            public void onPageScrolled(int position, float offset, int positionOffsetPixels) {}

            @Override
            public void onPageSelected(int position) {
                if (state == SCROLL_STATE_IDLE) {
                    height = 0; // measure the selected page in-case it a change without scrolling
                    Log.d(TAG, "onPageSelected:" + position);
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                this.state = state;
            }
        });
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return this.enabled && super.onTouchEvent(event);

    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return this.enabled && super.onInterceptTouchEvent(event);

    }

    public void setPagingEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public void setAdapter(PagerAdapter adapter) {
        height = 0; // so we measure the new content in onMeasure
        super.setAdapter(new PagerAdapterWrapper(adapter));
    }

    /**
     * Allows to redraw the view size to wrap the content of the bigger child.
     *
     * @param widthMeasureSpec  with measured
     * @param heightMeasureSpec height measured
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        widthMeasuredSpec = widthMeasureSpec;
        int mode = MeasureSpec.getMode(heightMeasureSpec);

        if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
            if(height == 0) {
                // measure vertical decor (i.e. PagerTitleStrip) based on ViewPager implementation
                decorHeight = 0;
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    if(lp != null && lp.isDecor) {
                        int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
                        boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
                        if(consumeVertical) {
                            decorHeight += child.getMeasuredHeight() ;
                        }
                    }
                }

                // make sure that we have an height (not sure if this is necessary because it seems that onPageScrolled is called right after
                int position = getCurrentItem();
                View child = getViewAtPosition(position);
                if (child != null) {
                    height = measureViewHeight(child);
                }
                //Log.d(TAG, "onMeasure height:" + height + " decor:" + decorHeight);

            }
            int totalHeight = height + decorHeight + getPaddingBottom() + getPaddingTop();
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY);
            //Log.d(TAG, "onMeasure total height:" + totalHeight);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public void onPageScrolled(int position, float offset, int positionOffsetPixels) {
        super.onPageScrolled(position, offset, positionOffsetPixels);
        // cache scrolled view heights
        if (scrollingPosition != position) {
            scrollingPosition = position;
            // scrolled position is always the left scrolled page
            View leftView = getViewAtPosition(position);
            View rightView = getViewAtPosition(position + 1);
            if (leftView != null && rightView != null) {
                leftHeight = measureViewHeight(leftView);
                rightHeight = measureViewHeight(rightView);
                animateHeight = true;
                //Log.d(TAG, "onPageScrolled heights left:" + leftHeight + " right:" + rightHeight);
            } else {
                animateHeight = false;
            }
        }
        if (animateHeight) {
            int newHeight = (int) (leftHeight * (1 - offset) + rightHeight * (offset));
            if (height != newHeight) {
                //Log.d(TAG, "onPageScrolled height change:" + newHeight);
                height = newHeight;
                requestLayout();
                invalidate();
            }
        }
    }

    private int measureViewHeight(View view) {
        view.measure(getChildMeasureSpec(widthMeasuredSpec, getPaddingLeft() + getPaddingRight(), view.getLayoutParams().width), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        return view.getMeasuredHeight();
    }

    protected View getViewAtPosition(int position) {
        if(getAdapter() != null) {
            Object objectAtPosition = ((PagerAdapterWrapper) getAdapter()).getObjectAtPosition(position);
            if (objectAtPosition != null) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    if (child != null && getAdapter().isViewFromObject(child, objectAtPosition)) {
                        return child;
                    }
                }
            }
        }
        return null;
    }


    /**
     * Wrapper for PagerAdapter so we can ask for Object at index
     */
    private class PagerAdapterWrapper extends PagerAdapter {
        private final PagerAdapter innerAdapter;
        private SparseArray<Object> objects;

        public PagerAdapterWrapper(PagerAdapter adapter) {
            this.innerAdapter = adapter;
            this.objects = new SparseArray<>(adapter.getCount());
        }


        @Override
        public void startUpdate(ViewGroup container) {
            innerAdapter.startUpdate(container);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Object object = innerAdapter.instantiateItem(container, position);
            objects.put(position, object);
            return object;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            innerAdapter.destroyItem(container, position, object);
            objects.remove(position);
        }

        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            innerAdapter.setPrimaryItem(container, position, object);
        }

        @Override
        public void finishUpdate(ViewGroup container) {
            innerAdapter.finishUpdate(container);
        }

        @Override
        public Parcelable saveState() {
            return innerAdapter.saveState();
        }

        @Override
        public void restoreState(Parcelable state, ClassLoader loader) {
            innerAdapter.restoreState(state, loader);
        }

        @Override
        public int getItemPosition(Object object) {
            return innerAdapter.getItemPosition(object);
        }

        @Override
        public void notifyDataSetChanged() {
            innerAdapter.notifyDataSetChanged();
        }

        @Override
        public void registerDataSetObserver(DataSetObserver observer) {
            innerAdapter.registerDataSetObserver(observer);
        }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {
            innerAdapter.unregisterDataSetObserver(observer);
        }

        @Override
        public float getPageWidth(int position) {
            return innerAdapter.getPageWidth(position);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return innerAdapter.getPageTitle(position);
        }

        @Override
        public int getCount() {
            return innerAdapter.getCount();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return innerAdapter.isViewFromObject(view, object);
        }

        public Object getObjectAtPosition(int position) {
            return objects.get(position);
        }
    }
}

first_fragment.xml

<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lvCheckIn"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:requiresFadingEdge="none"
    android:fadingEdgeLength="0dp"
    android:orientation="vertical" />

Добавление дополнительных данных в RecyclerView при прокрутке -

private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);

        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            visibleItemCount = recyclerView.getChildCount();
            totalItemCount = adapter.getItemCount();
            firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
            if (loading) {
                if (totalItemCount > previousTotal) {
                    loading = false;
                    previousTotal = totalItemCount;
                }
            }
            if (!loading && (totalItemCount - visibleItemCount)
                    <= (firstVisibleItem + visibleThreshold) && current_page < totalPages) {
                // End has been reached

                // Do something
                current_page++;
                // Sending request to server

                loading = true;
            }
        }
    };

Когда данные получены через API (уже добавлен адаптер) -

adapter.addNewList(homePageNew.checkin_stores.stores);

Ответ 1

Здесь, похоже, есть аналогичная дискуссия: ViewPager в NestedScrollView Возможно, образец парня Наруто (https://github.com/TheLittleNaruto/SupportDesignExample/) может решить вашу ситуацию.

Edit

Я не уверен, получу ли я ваш вопрос. В любом случае, здесь вы можете найти возможное решение вашей ситуации.

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/app_bar_layout"
>
<android.support.design.widget.CollapsingToolbarLayout
    android:id="@+id/collapsing_toolbar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_scrollFlags="scroll|exitUntilCollapsed"
>

---- include here everything before the pager ----
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
---- this is your pager
<include layout="@layout/fragment_pager"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"

android:layout_below="@+id/app_bar_layout"

/>

</android.support.design.widget.CoordinatorLayout>

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

Я надеюсь, что это помогло

Ответ 2

Вы можете увидеть мою реализацию бесконечного прокрутки RecyclerView:

GalleryActivity.java

public class GalleryActivity extends AppCompatActivity {

private StaggeredGridLayoutManager layoutManager;
private RecyclerView recyclerView;
private ImageRecyclerViewAdapter imageAdapter;

private List<ImageItem> images;
private ImageSearchClient imageSearchClient;

private String query;
private int currentStartPosition = 1;
private boolean loading = true;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_gallery);
    getSupportActionBar().setHomeButtonEnabled(true);

    query = getIntent().getStringExtra(Utils.QUERY_TAG);
    if (query == null)
        return;
    imageSearchClient = new ImageSearchClient(query);

    recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    recyclerView.setHasFixedSize(true);

    layoutManager = new StaggeredGridLayoutManager(2, 1);
    recyclerView.setLayoutManager(layoutManager);

    images = new ArrayList<>();

    imageAdapter = new ImageRecyclerViewAdapter(this, images);
    recyclerView.setAdapter(imageAdapter);
    recyclerView.addOnScrollListener(new EndlessRecyclerScrollListener());

    loadMoreData();
}

private void loadMoreData() {
    loading = true;
    imageSearchClient.getService().customSearch(Utils.API_KEY, Utils.CX_KEY, query,
            Utils.IMAGE_SEARCH_TYPE,
            currentStartPosition,
            Utils.ITEMS_COUNT,
            new Callback<ImageResponse>() {
                @Override
                public void success(ImageResponse imageResponse, Response response) {
                    List<ImageItem> items = imageResponse.getItems();
                    for (ImageItem item : items) {
                        images.add(item);
                    }
                    imageAdapter.notifyDataSetChanged();
                    currentStartPosition += items.size();
                    loading = false;
                }

                @Override
                public void failure(RetrofitError error) {
                    Log.e(GalleryActivity.class.getSimpleName(),
                            error.getResponse().getReason());
                }
            });
}

private class EndlessRecyclerScrollListener extends RecyclerView.OnScrollListener {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        int[] visibleItems = layoutManager.findLastVisibleItemPositions(null);
        int lastItem = 0;
        for (int i : visibleItems) {
            lastItem = Math.max(lastItem, i);
        }
        if (lastItem > 0 && lastItem > images.size() - Utils.ITEMS_COUNT && !loading) {
            if (NetworkUtils.hasConnection(GalleryActivity.this)) {
                loadMoreData();
            } else {
                Toast.makeText(GalleryActivity.this, R.string.network_error,
                        Toast.LENGTH_SHORT).show();
            }
        }
    }
}

}

ImageRecyclerViewAdapter.java(здесь нет ничего необычного):

public class ImageRecyclerViewAdapter extends RecyclerView.Adapter<ImageViewHolder> {

private List<ImageItem> itemList;
private Context context;
private int parentWidth = 0;

public ImageRecyclerViewAdapter(Context context, List<ImageItem> itemList) {
    this.context = context;
    this.itemList = itemList;
}

@Override
public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View layoutView =
            LayoutInflater.from(parent.getContext()).inflate(R.layout.gallery_card_item, null);
    ImageViewHolder imageViewHolder = new ImageViewHolder(layoutView);
    parentWidth = parent.getWidth();
    return imageViewHolder;
}

@Override
public void onBindViewHolder(final ImageViewHolder holder, int position) {
    Picasso.with(context)
            .load(itemList.get(position).getLink())
            .error(R.drawable.error_image)
            .placeholder(R.drawable.progress_animation)
            .into(new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    int targetWidth = parentWidth / 2;
                    float ratio = (float) bitmap.getHeight() / (float) bitmap.getWidth();
                    float heightFloat = ((float) targetWidth) * ratio;

                    final android.view.ViewGroup.MarginLayoutParams layoutParams
                            = (ViewGroup.MarginLayoutParams) holder.image.getLayoutParams();

                    layoutParams.height = (int) heightFloat;
                    layoutParams.width = (int) targetWidth;
                    holder.image.setLayoutParams(layoutParams);
                    holder.image.setImageBitmap(bitmap);
                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                }
            });
}

@Override
public int getItemCount() {
    return this.itemList.size();
}

}