Dealing With RecyclerView, NestedScrollView, and CardView - android

I'm going to achieve this UI in my App:
Well, Some ways that I tried before:
1. Using CollapsingToolbarLayout
I put my CardView insid of CollapsingToolbarLayout and put them all in AppBarLAyout.
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed" >
<CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<!-- My Views Goes there -->
</CardView
<android.support.v7.widget.Toolbar
android:id="#+id/flexible.example.toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#null"
app:layout_collapseMode="pin"
style="#style/ToolBarWithNavigationBack"
/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<RecyclerView
app:layout_behavior="#string/appbar_scrolling_view_behavior"
></RecyclerView>
</android.support.design.widget.CoordinatorLayout>
P.S: I removed unrelated codes, don't mention me
This Way works correctly but!!! When CardView height goes taller than screen height, it's content igonered by AppBarLayout and does not show to user
2. Using NestedScrollView
I put CardView and RecyclerView inside NestedScrollView. But the problem is When User Reached To end of RecyclerView and then scroll back to top, fling goes laggy and buggy and stop some where ans user have to scroll more and more to get to top!
<android.support.v4.widget.NestedScrollView
android:id="#+id/nested_scrollbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:scrollbars="none" >
<LinearLayout
android:id="#+id/nested_scrollbar_linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<android.support.v7.widget.CardView
android:id="#+id/flexible.example.cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#color/post_card_backgroind"
app:cardCornerRadius="0dp"
app:cardElevation="0dp">
</android.support.v7.widget.CardView>
<android.support.v7.widget.RecyclerView
android:id="#+id/list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/four"
android:layout_marginEnd="#dimen/four"
android:layout_marginLeft="#dimen/four"
android:layout_marginRight="#dimen/four"
android:layout_marginStart="#dimen/four"
android:layout_marginTop="#dimen/four"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
How can fix this issue?!
I don't want use adapters that make header for recyclerview, I got performance related issue from some of them.
Answer
Put RecyclerView inside NestedScrollView as you can see in 2 and apply .setNestedScrollingEnabled and set it to false

You should use getItemViewType. It is easy and won't create any performance overhead. Change the code in your Adapter like this:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
class CardViewHolder extends RecyclerView.ViewHolder {
...
}
class ItemViewHolder extends RecyclerView.ViewHolder {
...
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return 0; // Card Type
} else {
return 1; // Item Type
};
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 0:
// Card Type
return new CardViewHolder(...);
case 1:
// Item Type
return new ItemViewHolder(...);
}
}
// Optional
// If your data list does not contain CardView data
// You may need to add extra count in adapter
#Override
public final int getItemCount() {
// Add one count for CardView data
return list.size() + 1;
}
#Override
public T getItem(int position) {
// As the position is change because of CardView at position 0
// So you may have to decrement the corresponding index
return list.get(position - 1);
}
}
UPDATE 1
If you don't want to update the adapter, you can use
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CardView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Views Goes there -->
</CardView>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
// This is the key
android:nestedScrollingEnabled="false"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
android:nestedScrollingEnabled is available in API Level 21 through xml.
For lower APIs use java method i.e. recyclerView.setNestedScrollingEnabled(false); or ViewCompat.setNestedScrollingEnabled(recyclerView, false);
NOTE (12-12-2016)
When using nested scroll. Be aware of the known issue of RecyclerView not recycling views as asked here and here (apparent reason answered by Arpit Ratan and me respectively). So I'll suggest to go with the first solution i.e. using getItemViewType. For more details, see complete and better answers like this or this.
UPDATE 2
You can use setFullSpan. If the orientation of your StaggeredGridLayoutManager is vertical, you can use the following code to span it's width to full screen:
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (position == 0) {
StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) viewHolder.itemView.getLayoutParams();
layoutParams.setFullSpan(true);
}
}

the problem in 2.using
But the problem is When User Reached To end of RecyclerView and then scroll back to top
it can be deal by add android:descendantFocusability="blocksDescendants in the first layout under the NestedScrollView like this:
<android.support.v4.widget.NestedScrollView
android:id="#+id/nested_scrollbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:scrollbars="none" >
<LinearLayout
android:id="#+id/nested_scrollbar_linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:descendantFocusability="blocksDescendants" >
it work for me. NestedScrollView scrolls to top on Recyclerview resized

Related

How to get an Item of Recyclerview when scroll stopped?

I have a Recyclerview inside Scrollview, I want to store item details of Recyclerview where scroll stopped.
I have a xml like,
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/White">
<RelativeLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="#+id/tile_view_pager"
android:layout_width="match_parent"
android:layout_height="200dp"
android:visibility="gone"
android:layout_marginRight="2dp"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/news_feed_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</RelativeLayout>
I have added ScrollListener for recyclerview. But As I'm using parent Scrollview for the activity, recyclerview's ScrollListener is not getting called.
I tried changing parent scrollview to NestedScrollView then recyclerview is scrolling as a one item at once but i want scroll to be freely. Like scrolling our contacts.
Issue : When I scroll, Parent ScrollView getting scrolled and I don't have any control on my recyclerview. So It's difficult to figure out which item of recyclerview is visible once scroll stopped.
Basically I want to code without changing my XML.
I want to store item details of Recyclerview where scroll stopped.
When scroll stops, scroll state transitions into idle state. However, idle state is the state before and after scroll. Therefore you are looking for a transition into idle state after settling state.
Do it like this:
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
boolean scrolled;
int[] displayedPositions;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
scrolled = true;
} else if (newState == RecyclerView.SCROLL_STATE_IDLE && scrolled) {
scrolled = false;
int[] into = new int[//number of spans];
displayedPositions = ((StaggeredGridLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPositions(into)
}
}
}
private int overallXScrol = 0;
//...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
overallXScroll = overallXScroll + dx;
Log.i("check","overall->" + overallXScroll);
}
});
try NestedScrollView instead scrollview like below
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/White">
<RelativeLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="#+id/tile_view_pager"
android:layout_width="match_parent"
android:layout_height="200dp"
android:visibility="gone"
android:layout_marginRight="2dp"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/news_feed_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</RelativeLayout>
I found the solution. Just we need to change layout. I'm using CoordinatorLayout as a parent view. And Now I'm able to use onScrollListener of recyclerview.
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/White">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="#+id/tile_view_pager"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginEnd="2dp"
android:visibility="gone"/>
</LinearLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/news_feed_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>

NativeAdsExpress forces RecyclerView to Scroll to have the NativeAd fully visible when first loaded

I have a weird "problem" or may be it's a "feature" and I just don't know, whenever a NativeAdExpress is loaded in my RecyclerView if only part of the NativeAd is visible, it forces the RecyclerView to scroll until the Native ad becomes fully visible, this behavior causes the list to keep jumping as I scroll.
My Layout is mainly:
Activity > AppBar with Tabs and ViewPager > each Page in the Pager contains PullToRefresh and inside it there's a RecyclerView,
The RecyclerView has 2 types of items (Article and NativeAdExpress).
UPDATE: My guess on why this is happening is mainly because native ads express render in a webview, and this webview receives focus then this causes the RecyclerView to scroll to it, but that's only a Guess
UPDATE 2: Apparently this is an issue in Support Lib. 24.0.0, that's the cost of being too up2date :(
Heres's my Full XML/Layouts
<?xml version="1.0" encoding="utf-8"?>
<my.package.custom.views.CustomDrawerLayout
android:id="#+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:openDrawer="end">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
style="#style/AlertDialog.AppCompat.Light"
fontPath="fonts/fonts/DroidKufi-Regular.ttf"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
android:fitsSystemWindows="true"
app:elevation="5px"
app:headerLayout="#layout/nav_header"
app:itemIconTint="#color/colorPrimary"
app:itemTextColor="#color/contentColor"
app:actionLayout="#layout/nav_item_layout"
app:menu="#menu/drawer_menu"
app:theme="#style/NavDrawerStyle"
tools:openDrawer="end"
/>
</my.package.custom.views.CustomDrawerLayout>
where "app_bar_main.xml" is as follows:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".ui.activities.ArticlesListActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="52dp"
android:background="?attr/colorPrimary"
android:gravity="end"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingEnd="14dp"
android:paddingStart="14dp">
<android.support.v7.widget.AppCompatImageButton
android:id="#+id/ivCustomDrawable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="#color/transparent"
android:tint="#color/white"
/>
<TextView
android:id="#+id/view_title"
android:visibility="gone"
style="#style/SectionTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
/>
<android.support.v7.widget.AppCompatSpinner
android:id="#+id/sources_spinner"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:transitionName="#string/transition_splash_logo"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
tools:targetApi="lollipop"/>
</RelativeLayout>
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
style="#style/TabsStyle"
android:layout_width="match_parent"
android:layout_height="42dp"
android:layout_gravity="bottom"
android:layout_marginTop="0dp"
android:transitionGroup="true"
app:tabContentStart="0dp"
app:tabGravity="fill"
app:tabIndicatorColor="#color/white"
app:tabIndicatorHeight="3dp"
app:tabMode="scrollable"
app:tabPaddingBottom="0dp"
app:tabPaddingTop="0dp"
app:tabTextAppearance="#style/TextAppearance.RegularTextFont"
/>
</android.support.design.widget.AppBarLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".ui.activities.ArticlesListActivity"
tools:showIn="#layout/activity_newsitem_list">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
and finally I have 2 View Types Item and NativeAdExpress:
The NativeAdExpress ViewHolder is as follows:
public class NativeExpressAdViewHolder extends BaseCardAdViewHolder {
private final NativeExpressAdView view;
private boolean loaded = false;
private AdListener adListener;
private WeakReference<Context> context;
public NativeExpressAdViewHolder(View itemView, String adId, Context context) {
super(itemView);
view = new NativeExpressAdView(context);
view.setAdUnitId(adId);
((LinearLayout) itemView.findViewById(R.id.express_ad_holder)).addView(view);
this.context = new WeakReference<>(context);
}
public void loadAd(float cardWidthInDips) {
if (!loaded && null != context.get() && !view.isLoading()) {
int width = cardWidthInDips > 0 ? (int) cardWidthInDips : 330;
if (view.getAdSize() == null) {
view.setAdSize(new AdSize(width, 330));
view.setAdListener(new AdListener() {
#Override
public void onAdLoaded() {
super.onAdLoaded();
loaded = true;
if (adListener != null) {
adListener.onAdLoaded();
}
}
#Override
public void onAdFailedToLoad(int i) {
super.onAdFailedToLoad(i);
if (adListener != null) {
adListener.onAdFailedToLoad(i);
}
}
#Override
public void onAdOpened() {
super.onAdOpened();
if (adListener != null) {
adListener.onAdOpened();
}
}
});
}
new Handler(context.get().getMainLooper()).post(new Runnable() {
#Override
public void run() {
view.loadAd(new AdRequest.Builder().build());
}
});
}
}
public NativeExpressAdView getView() {
return view;
}
public void setAdListener(AdListener adListener) {
this.adListener = adListener;
}
#Override
public void destroyAd() {
if (view != null) {
view.destroy();
loaded = false;
}
}
}
and ads are create using a custom adapter as follows:
private BaseCardViewHolder createNativeExpressAdViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.ad_express, parent, false);
final NativeExpressAdViewHolder viewHolder = new NativeExpressAdViewHolder(
view,
adManager.getNativeExpressAdId(),
context.get()
);
adViewHolders.add(viewHolder);
viewHolder.setAdListener(new AdListener() {
#Override
public void onAdFailedToLoad(int errorCode) {
Log.i("ADS", "onAdFailedToLoad " + errorCode);
}
#Override
public void onAdLoaded() {
super.onAdLoaded();
// Do something
}
});
viewHolder.loadAd(cardWidthInDips);
return viewHolder;
}
I did not understand where you actually implement a RecyclerView in your code but here is anyway maybe something that could work in your case.
I had a similar problem with a GridView always getting the focus during a Fragment first load, it was also automatically scrolling down straight to the GridView.
After a lot of tests, I finally stopped this behavior with this one line on the parent layout of the GridView:
android:descendantFocusability="blocksDescendants"
This line basically means that all descendants from the parent layout will not get focus anymore. You can try it out on your RecyclerView parent layout.
I don't know if it's an issue, but the it looks like RecyclerView changed its behavior somewhere from support library 23.4.0 to 24.2.0.
Now instead of defaulting to descendantFocusability = beforeDescendants, it defaults to afterDescendants, which causes it to offer the focus to their children instead of taking it for itself.
When a child takes the focus, it ends up scrolling to make it visible.
I fixed it by adding android:descendantFocusability="beforeDescendants" to the RecyclerView, to recover the previous behavior, where it keeps the focus itself.
I had a similar issue with RecyclerView scrolling, not specifically to NativeAdExpress...but there seems to be an issue with the Android Support library 24.0.0 related to this. The android:descendantFocusability="blocksDescendants" didn't work for me. But downgrading to Android Support library 23.4.0 made it start working again.

RecyclerView does not Recycling Views when use it inside NestedScrollView

I'm using RecyclerView inside NestedScrollView. Also i set setNestedScrollingEnabled to false for recyclerview
to support lower API
ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);
Now! When user scrolled the view every thing seems okay, but!!! views in recyclerview does not recycled!!! and Heap size grows swiftly!!
Update:
RecyclerView layout manager is StaggeredLayoutManager
fragment_profile.xml:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" >
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/profileSwipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- RecyclerView and NestedScrollView -->
<include layout="#layout/fragment_profile_details" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>
fragment_profile_details.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:orientation="vertical" >
<android.support.v4.widget.NestedScrollView
android:id="#+id/nested_scrollbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:fillViewport="true"
android:scrollbars="none" >
<LinearLayout
android:id="#+id/nested_scrollbar_linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical" >
<android.support.v7.widget.CardView
android:id="#+id/profileCardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#color/card_backgroind"
app:cardCornerRadius="0dp"
app:cardElevation="0dp" >
<!-- Profile related stuff like avatar and etc. --->
</android.support.v7.widget.CardView>
<android.support.v7.widget.RecyclerView
android:id="#+id/list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/four"
android:layout_marginEnd="#dimen/four"
android:layout_marginLeft="#dimen/four"
android:layout_marginRight="#dimen/four"
android:layout_marginStart="#dimen/four"
android:layout_marginTop="#dimen/four"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:clipToPadding="false" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
ProfileFragment.java:
mAdapter = new MainAdapter(getActivity(), glide, Data);
listView = (RecyclerView) view.findViewById(R.id.list_view);
ViewCompat.setNestedScrollingEnabled(listView, false);
listView.setAdapter(mAdapter);
mStaggeredLM = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mStaggeredLM.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
listView.setLayoutManager(mStaggeredLM);
mScroll.setOnScrollChangeListener(new OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView arg0, int arg1, int arg2, int arg3, int arg4) {
View view = (View) mScroll.getChildAt(mScroll.getChildCount() - 1);
int diff = (view.getBottom() - ( mScroll.getHeight() + mScroll.getScrollY()));
if(diff == 0){
int visibleItemCount = mStaggeredLM.getChildCount();
int totalItemCount = mStaggeredLM.getItemCount();
int[] lastVisibleItemPositions = mStaggeredLM.findLastVisibleItemPositions(null);
int lastVisibleItemPos = getLastVisibleItem(lastVisibleItemPositions);
Log.e("getChildCount", String.valueOf(visibleItemCount));
Log.e("getItemCount", String.valueOf(totalItemCount));
Log.e("lastVisibleItemPos", String.valueOf(lastVisibleItemPos));
if ((visibleItemCount + 5) >= totalItemCount) {
mLoadMore.setVisibility(View.VISIBLE);
Log.e("LOG", "Last Item Reached!");
}
mMore = true;
mFresh = false;
mRefresh = false;
getPosts();
}
}
});
P.s : I've set load more to scroll view, because recyclerview do it continuously and none stoppable!
Any help is appreciated
This is because we have a recycler view which has scroll behaviour inside a scroll view. (scroll inside a scroll)
I think the best way to resolve this issue is to your profileCardview as a header in your recycler view and then remove the nested scroll view.
If it were a listview then it was as simple as listView.addHeaderView(profileCardView) but for the Recycler view there is no addheadview equivalent. Hence you could refer the below link to change your implementation.
Is there an addHeaderView equivalent for RecyclerView?
For a RecyclerView or ListView the height should be constant, because if it will not a constant size then how it will manage the maximum number of visible rows in memory. Try by changing RecyclerView attribute android:layout_height="match_parent" or a fixed height(e.g. "300dp" - as needed), instead of "wrap_content". It should improve your memory management.

Recyclerview inside scrollview- How to scroll whole content?

I am having Recyclerview inside Scrollview
<Scrollview
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:id="#+id/layoutStaticContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
//Static content.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp">
.
.
<LinearLayout>
//Dynamic content(newsfeed)
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</ScrollView>
Now while scrolling, layoutStaticContent stays fix on the top & recyclerview content scrolls independently in the bottom part.
How to scroll the whole content i.e (layoutStaticContent + recyclerview content) such that there is only 1 scrollview?
I also tried replacing scrollview with Nestedscrollview but no success.
Use the android.support.v4.widget.NestedScrollView then inside both layout
NestedScrollView
Put the LinearLayout which contains both the static and dynamic data inside of a NestedScrollView and it'll work like a charm.
Here is the code you need:
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/layoutStaticContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
//Static content.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp">
.
.
<LinearLayout>
//Dynamic content(newsfeed)
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
I hope it helps!
One possible way around this is only use RecyclerView with the static content as header to your Recyclerview.
Then the layout would simply be:
//Dynamic content(newsfeed)
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
There will be a list_item_header.xml layout for your static content:
//Static content.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp">
.
.
<LinearLayout>
And you'll have to change your recyclerview adapter to contain:
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
#Override
public int getItemCount()
{
int itemCount = super.getItemCount();
if (mIsHeaderPresent)
{
itemCount += 1;
}
return itemCount;
}
#Override
public int getItemViewType(int position)
{
if (mIsHeaderPresent && position == 0)
{
return TYPE_HEADER;
}
return TYPE_ITEM;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
if (viewType == TYPE_HEADER)
{
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_header, parent, false);
ViewHolderHeader viewHolder = new ViewHolderHeader(itemView);
return viewHolder;
} else if (viewType == TYPE_ITEM)
{
return getItemViewHolder(parent);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder passedViewHolder)
{
if (mIsHeaderPresent && passedViewHolder instanceof ViewHolderHeader)
{
onBindHeaderViewHolder((ViewHolderHeader) passedViewHolder);
} else
{
onBindItemViewHolder(passedViewHolder);
}
}
if you want to scroll all data i think you have to use CoordinatorLayout. in CoordinatorLayout you use appbar layout and CollapsingToolbarLayout where you can put your static content. because its a wrong approach in android to use a scroll able container in to another scroll able container. you can use coordinater layout like this.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
>
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentScrim="?attr/colorPrimary"
android:fitsSystemWindows="true"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
//Static content.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp">
.
.
<LinearLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
/>
</android.support.design.widget.CoordinatorLayout>
After a lot of searching and trying, I found the solution:
1. Set the height for the RecyclerView inside the ScrollView:
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
...
<android.support.v7.widget.RecyclerView
android:id="#+id/myPostsRecyclerView"
android:layout_width="match_parent"
**android:layout_height="550dp"**
android:layout_below="#+id/textView7"
android:background="#drawable/background"
android:padding="5dp"
**android:visibility="gone"**
tools:listitem="#layout/item_send" />
</RelativeLayout>
</ScrollView>
2. In the adapter , where you set the data list :
public void setData(List<Post> posts) {
this.posts.clear();
this.posts.addAll(posts);
notifyDataSetChanged();
if (posts.size() < 1)
activity.recyclerView.setVisibility(View.GONE);
else {
activity.recyclerView.setVisibility(View.VISIBLE);
ViewGroup.LayoutParams params=activity.recyclerView.getLayoutParams();
params.height=ViewGroup.LayoutParams.WRAP_CONTENT;
activity.recyclerView.setLayoutParams(params);
}
}

How can we scroll parent layout while scrolling recyclerview android

Please help me with the following situation.
I am developing an application where in one page I have a Linearlayout with a background image and RecyclerView with list of names.What I need is when i scroll up the RecyclerView I need the LinearLayout above also to move up so that the list in the recyclerView does not go under the LinearLayout above and when I scroll down the RecyclerView I need the LinearLayout above to scroll down so that we could see the image fully.
What i have done already is I used the setOnScrollListener of recyclerview and in the onScrolled() function I am getting the scrolling down and scrolling up event.But now i am stuck how to proceed further.
Below is the layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<include layout="#layout/toolbar"
android:id="#+id/toolbar" />
<android.support.v7.widget.CardView
android:id="#+id/cardview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolbar"
android:layout_margin="8dp"
card_view:cardBackgroundColor="#android:color/white"
card_view:cardCornerRadius="8dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scrollview"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="5"
android:orientation="vertical">
<LinearLayout
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:background="#drawable/hydeparkmap"
android:layout_weight="3" ></LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:scrollbars="vertical" />
</LinearLayout>
</ScrollView>
</android.support.v7.widget.CardView>
</RelativeLayout>
Here is the code, i used in corresponding java class:
#InjectView(R.id.scrollview)
ScrollView scrollview;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shoplist);
ButterKnife.inject(this);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setTitle("ShopList");
scrollview.setVerticalScrollBarEnabled(true);
lm=new LinearLayoutManager(ShopListActivity.this);
recyclerView.setLayoutManager(lm);
firstVisibleInListview = lm.findFirstVisibleItemPosition();
adapter = new RecyclerViewAdapter(ShopListActivity.this, getData());
recyclerView.setAdapter(adapter);
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(ShopListActivity.this, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
Intent intent1 = new Intent(ShopListActivity.this, ShopProductListActivity.class);
intent1.putExtra("position", String.valueOf(position));
intent1.putExtra("shopname", it.get(position).getTitle());
intent1.putExtra("shopimage", String.valueOf(it.get(position).getImgIcon()));
intent1.putExtra("subcategory", subcategory);
startActivity(intent1);
}
})
);
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int currentFirstVisible = lm.findFirstVisibleItemPosition();
if(currentFirstVisible > firstVisibleInListview){
Toast.makeText(getBaseContext(),"Scrolled up ",Toast.LENGTH_SHORT).show();
scrollview.fullScroll(ScrollView.FOCUS_UP);
}
else
Toast.makeText(getBaseContext(),"Scrolled down ",Toast.LENGTH_SHORT).show();
firstVisibleInListview = currentFirstVisible;
}
});
}
so if I understand your question completely, you some View that should scroll with
RcyclerView.
if so in this situation you should delete your ScrollView cause RecyclerView have that within itself. what I write here is step by step guide to put a header( not sticky) in top of your RecyclerView so they would scroll together:
1- first you need to create a layout containing your header in my case i had an image for header so it looked like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="#mipmap/winter"/>
</LinearLayout>
let's call this header_layout
2- you need to implement another method of RecyclerAdapter called getItemViewType it's output would be in as second parameter onCreateViewHolder(ViewGroup parent,int viewType) here you inflate the layout for each kind of view you need( for me these two look like this) :
#Override
public int getItemViewType(int position){
if(position == 0){
return 0;
} else {
return 1;
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
if(viewType==1){
itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.report_row_layout, parent, false);
} else {
itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.report_header_layout, parent, false);
}
return new MyViewHolder(itemView);
}
note that my first position (header) differs and other are the same you could have multiple views for multiple positions.
3- you should change your onBindViewHolder if needed in my case I needed to make it do nothing for my first position
4- remove your ScrollView and implement the layouts in positions and your main layout should look like this:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<include layout="#layout/toolbar"
android:id="#+id/toolbar" />
<android.support.v7.widget.CardView
android:id="#+id/cardview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolbar"
android:layout_margin="8dp"
card_view:cardBackgroundColor="#android:color/white"
card_view:cardCornerRadius="8dp">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v7.widget.CardView>
</RelativeLayout>
I don't know what your CardView is doing but delete it if you think it's not needed. if you need you can make your header a LinearLayout or anything else.
hope this helps.

Categories

Resources