In some cases, we need to make the RecyclerView cannot scroll, so play to your strengths for this questions.
I have some solutions for this problem, hope it can help you.See below for details:
void setLayoutFrozen (boolean frozen);
I strongly recommend if we only want to inhabit scroll for this scheme for its simplicity and convenience; if you want to know deeply
When we use LayoutManager(LinearManager or GridLayoutManager), we can use this.
`
LinearLayoutManager linearLayoutManager = new
LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false){
#Override
public boolean canScrollVertically() {
return false;
}
#Override
public boolean canScrollHorizontally() {
return super.canScrollHorizontally();
}
};`
GridLayoutManager is alike.
From above second item, we can extend corresponding LayoutManager.
`
public class MyGridLayoutManager extends GridLayoutManager {
private boolean isScrollEnabled = true;
public MyGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
public MyGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public MyGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
public void setScrollEnabled(boolean flag) {
this.isScrollEnabled = flag;
}
#Override
public boolean canScrollVertically() {
return isScrollEnabled && super.canScrollVertically();
}
}
`
4. We can handle the RecyclerView TouchEvent to achieve the effect.
From above, I believe you can make yourself. Good Luck...
Related
RecyclerView inside NestedScrollView freezes the Activity when loading large amount of data. It loads much faster with ScrollView, but scrolling is affected in that case.
I tried setting attributes like setAutoMeasure and setNestedScrollingEnabled which did not help.
Any suggestions?
Recycling of views is not supported inside NestedScrollViews as I understand, so the suggestion would be to try to change your layout.
This help to increase the speed of recycler view scrolling.
SpeedyLinearLayoutManager linearLayoutManager = new SpeedyLinearLayoutManager(MainActivity.this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
myRecyclerView.setLayoutManager(linearLayoutManager);
SpeedyLinearLayoutManager.class
public class SpeedyLinearLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 2f; //default is 25f (bigger = slower)
public SpeedyLinearLayoutManager(Context context) {
super(context);
}
public SpeedyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public SpeedyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
#Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return SpeedyLinearLayoutManager.this.computeScrollVectorForPosition(targetPosition);
}
#Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
}
Use Custom list view instead of Recycler view inside scroll view...
Check this Answer
Custom List View
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ListView;
public class ExpandableHeightListView extends ListView {
boolean expanded = true;
public ExpandableHeightListView(Context context) {
super(context);
}
public ExpandableHeightListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ExpandableHeightListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public boolean isExpanded() {
return expanded;
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// HACK! TAKE THAT ANDROID!
if (isExpanded()) {
// Calculate entire height by providing a very large height hint.
// View.MEASURED_SIZE_MASK represents the largest height possible.
int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
}
you can include app:layout_behavior="#string/appbar_scrolling_view_behavior"
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
or disable the nested scrolling behavior for the recycler view.
recyclerView.setNestedScrollingEnabled(false);
I'm creating a slot machine application and for this I'm using RecycleView as a rail. Therefore all touch\click\scroll events are disabled for RecyclerView.
And to make "spin" I used RecyclerView.fling(...) method since it has better animation.
BUT appearantly fling stops when I touch the recycler view. Why? And how can I disable this?
xml:
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_rail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="#dimen/margin_4"
android:layout_marginStart="#dimen/margin_4"
android:layout_weight="1"
android:padding="#dimen/dp_16"/>
init of RecyclerView:
LinearLayoutManager mRailLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mRailsBinding.rvRail.setHasFixedSize(true);
new LinearSnapHelper().attachToRecyclerView(mRailsBinding.rvRail);
mRailsBinding.rvRail.setLayoutManager(mRailLayoutManager);
mRailsBinding.rvRail.addOnItemTouchListener(new RecyclerViewDisabler());
mRailsBinding.rvRail.addOnScrollListener(railScrollListener); //checking here for time when scroll ended
mRailsBinding.rvRail.setAdapter(mRailAdapter);
Adapter does nothing special. It just inits views and has no click\touch evenets on them.
RecyclerViewDisabler:
public class RecyclerViewDisabler implements RecyclerView.OnItemTouchListener {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
return true;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean state) {}
}
Just read documentation carefull and found solution. I was needed to consume dispatchTouchEvenet manually. See code:
public class UntouchableRecyclerView extends RecyclerView {
public UntouchableRecyclerView(Context context) {
super(context);
}
public UntouchableRecyclerView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public UntouchableRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return true; //consume
}
}
I am making a chat application .
I have a Layout manager which extends LinearLayoutManager and i want to compute the height of the view added to the adapter so that when i call smoothScrollToPosition() then i could change the scroll speed.
For more height i will make MILLISECONDS_PER_INCH to a smaller value and for less height i will make it large.
But i am not able to calculate the height of the newly added view.
public class SmoothLinearLayoutManager extends LinearLayoutManager {
private ChatAdapter mChatAdapter;
private Context mContext;
private final float MILLISECONDS_PER_INCH = 250f;
public SmoothLinearLayoutManager(Context context,ChatAdapter adapter) {
super(context);
mContext = context;
mChatAdapter = adapter;
}
public SmoothLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
mContext = context;
}
public SmoothLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mContext = context;
}
#Override
public void smoothScrollToPosition(final RecyclerView recyclerView, RecyclerView.State state, final int position) {
Log.d("Scroll",mChatAdapter.getChildHeight()+"");
LinearSmoothScroller smoothScroller = new LinearSmoothScroller(mContext) {
#Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return SmoothLinearLayoutManager.this.computeScrollVectorForPosition(position);
}
#Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return (MILLISECONDS_PER_INCH)/displayMetrics.densityDpi;
}
};
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
}
If your purpose of accessing newly added view is to make constant smoothscroll speed, this library may be useful for you.
https://github.com/nshmura/SnappySmoothScroller
like this:
layoutManager = new SnappyLinearLayoutManager(context);
layoutManager.setSnapDuration(500);
layoutManager.setSeekDuration(1000);
I want to disable recyclerview scrolling in landscape mode and enable it in the portrait mode.
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
// Stop only scrolling.
return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING;
}
});
I am using this method to disable scrolling but can't find a way to enable it again.
Thanks for any help!
You have to get it done using a custom RecyclerView. Initialize it programmatically when the user is in landscape mode and add this view to your layout:
public class MyRecycler extends RecyclerView {
private boolean verticleScrollingEnabled = true;
public void enableVersticleScroll (boolean enabled) {
verticleScrollingEnabled = enabled;
}
public boolean isVerticleScrollingEnabled() {
return verticleScrollingEnabled;
}
#Override
public int computeVerticalScrollRange() {
if (isVerticleScrollingEnabled())
return super.computeVerticalScrollRange();
return 0;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if(isVerticleScrollingEnabled())
return super.onInterceptTouchEvent(e);
return false;
}
public MyRecycler(Context context) {
super(context);
}
public MyRecycler(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyRecycler(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
For portrait mode keep using your normal RecyclerView.
For this issue, I use this one line solution! :)
myRecyclerView.isNestedScrollingEnabled = false
I have two activities using AppBarLayout with a Toolbar and TabLayout from support library 22.
The layout of both is pretty similar: A Toolbar at the top, below it TabLayout, below it a ViewPager containing 3 Fragments.
The first activity's Fragment has a RecyclerView,
the second activity's Fragment is using a ListView instead.
The scrollable Toolbar example from https://github.com/chrisbanes/cheesesquare is working fine on the first activity using the RecyclerView, but on with the ListView.
I've tried created a custom ListViewScrollBehavior that extends AppBarLayout.ScrollingViewBehavior, but so far no luck.
The TouchEvents are passed to the custom class only for horizontal scrolling, but not when scrolling the ListView (vertically).
Any way to use a CoordinatorLayout with ListView?
The only solution to make it work now is to use this:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
listView.setNestedScrollingEnabled(true);
}
It will obviously only work on Lollipop.
Alternative solution to Nicolas POMEPUY's answer is to use ViewCompat.setNestedScrollingEnabled(View, boolean)
ViewCompat.setNestedScrollingEnabled(listView, true);
Of course nested scrolling behavior will only work from Lollipop.
I believe that the CoordinatorLayout works only with RecyclerView and NestedScrollView. Try wrapping your ListView in a NestedScrollView or convert it to a RecyclerView with a LinearLayoutManager
To a view able to react on AppBarLayout, it need to implement NestedScrollingChild. ListView is not. But it could be implement by a delegate class easily. Use it, it will do like what RecyclerView did
public class NestedScrollingListView extends ListView implements NestedScrollingChild {
private NestedScrollingChildHelper mNestedScrollingChildHelper;
public NestedScrollingListView(final Context context) {
super(context);
initHelper();
}
public NestedScrollingListView(final Context context, final AttributeSet attrs) {
super(context, attrs);
initHelper();
}
public NestedScrollingListView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHelper();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public NestedScrollingListView(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initHelper();
}
private void initHelper() {
mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
#Override
public void setNestedScrollingEnabled(final boolean enabled) {
mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
}
#Override
public boolean isNestedScrollingEnabled() {
return mNestedScrollingChildHelper.isNestedScrollingEnabled();
}
#Override
public boolean startNestedScroll(final int axes) {
return mNestedScrollingChildHelper.startNestedScroll(axes);
}
#Override
public void stopNestedScroll() {
mNestedScrollingChildHelper.stopNestedScroll();
}
#Override
public boolean hasNestedScrollingParent() {
return mNestedScrollingChildHelper.hasNestedScrollingParent();
}
#Override
public boolean dispatchNestedScroll(final int dxConsumed, final int dyConsumed, final int dxUnconsumed, final int dyUnconsumed, final int[] offsetInWindow) {
return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
}
#Override
public boolean dispatchNestedPreScroll(final int dx, final int dy, final int[] consumed, final int[] offsetInWindow) {
return mNestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
#Override
public boolean dispatchNestedFling(final float velocityX, final float velocityY, final boolean consumed) {
return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
#Override
public boolean dispatchNestedPreFling(final float velocityX, final float velocityY) {
return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
}
ListView ScrollingViewBehavior supports only >= 21.
Otherwise you should to write code as below way:
private int mPreviousVisibleItem;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
listView.setNestedScrollingEnabled(true);
} else {
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem > mPreviousVisibleItem) {
appBarLayout.setExpanded(false, true);
} else if (firstVisibleItem < mPreviousVisibleItem) {
appBarLayout.setExpanded(true, true);
}
mPreviousVisibleItem = firstVisibleItem;
}
});
}
You can add
android:nestedScrollingEnabled="true"
to the ListView from XML, just note that this only supports API 21+. Alternatively, you can swap out your ListView for a RecyclerView, and that should work better.