问题
I have to create following layout
So far, I have successfully created the layout and populated all views. However, I am facing problem in the making ReyclerView Endless on first fragment.
Consider the RecyclerView has 10 items on first load, now on scroll I am adding another 10 items and so on. However, the RecyclerView isn't displaying those items, it's height gets fixed at the end of 10th element. I know that the elements are loaded correctly in RecyclerView and if I try to scroll with two fingers on emulator (GenyMotion), the RecyclerView scrolls just fine.
Update :-
Code for RecyclerView's Fragment -
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
}
}
}
Update :-
Adding layouts for used screens.
main_screen.xml - This is the home screen
<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's 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" />
Adding more data to the RecyclerView on scrolling -
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;
}
}
};
When data is received via API (adapter already added above)-
adapter.addNewList(homePageNew.checkin_stores.stores);
回答1:
There seems to be a similar discussion here: ViewPager in a NestedScrollView Maybe the sample of the Naruto guy (https://github.com/TheLittleNaruto/SupportDesignExample/) could solve your situation.
Edit
I'm not sure if I'm getting the point of your question. Anyway here you can a possible solution to your situation.
<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>
Then you can just listen to the RecycleView in the pager to add items as the RecycleView scrolls to bottom.
I hope it helped
回答2:
You can see my implementation of the endless RecyclerView scroll:
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 (nothing is extraordinary here):
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();
}
}
来源:https://stackoverflow.com/questions/37783337/dynamic-endless-recyclerview-scrolling-issues