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.
Related
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...
Hello I have a RecyclerView, and I use HorizontalScrollView in children of theRecyclerView`. I need to scroll all of them when I scrolling one. Anyone can tell me How to make that,thanks!
I'm going to assume you're doing something similar to what I did where you have some sort of tabular view and the HorizontalScrollViews are all the same width.
This is how I did it:
First I made a customization to the HorizontalScrollView so I could get event notifications when the view was swiped:
public class HorizontalScrollView extends android.widget.HorizontalScrollView {
private OnScrollListener mListener;
public HorizontalScrollView(Context context) {
super(context);
}
public HorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setOnScrollListener(OnScrollListener listener) {
mListener = listener;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mListener != null) {
mListener.onScrollChanged(this, l);
}
}
public interface OnScrollListener {
public void onScrollChanged(View view, int scrollX);
}
}
Then when I create the ViewHolders I add a listener that will set all the views to the same scrollX:
view.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollChanged(View scrollView, int scrollX) {
for (int i = 0; i < recyclerView.getChildCount(); i++) {
View child = recyclerView.getChildAt(i);
if (child instanceof HorizontalScrollView && child != scrollView) {
HorizontalScrollView scrollView2 = (HorizontalScrollView) child;
if (scrollView2.getScrollX() != scrollX) {
scrollView2.setScrollX(scrollX);
}
}
}
}
});
This code is just for illustration purposes; don't expect to copy/paste this and have it work.
I'm assuming that your ViewHolder can get a reference to your RecyclerView to access all the current list items.
This code had some problems, when you swiped one view then swiped another view while everything was still moving from the first swipe, things could get out of sync. But this is a basic idea to get you started in a positive direction.
Does anyone know how to implement the Collapsing Toolbar using A listview instead of a recycler view?
To make it woks you should to:
Implement NestedScrollingChild in your custom ListView implementation.
Add field private final NestedScrollingChildHelper mScrollingChildHelper; and init it in constructors
Delegate to it methods from NestedScrollingChild
Invoke setNestedScrollingEnabled(true); after mScrollingChildHelper initialization
Here is my list view implementation for example:
public class NestedScrollingListView extends ListView implements NestedScrollingChild {
private final NestedScrollingChildHelper mScrollingChildHelper;
public NestedScrollingListView(Context context) {
super(context);
mScrollingChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
public NestedScrollingListView(Context context, AttributeSet attrs) {
super(context, attrs);
mScrollingChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
#Override
public void setNestedScrollingEnabled(boolean enabled) {
mScrollingChildHelper.setNestedScrollingEnabled(enabled);
}
#Override
public boolean isNestedScrollingEnabled() {
return mScrollingChildHelper.isNestedScrollingEnabled();
}
#Override
public boolean startNestedScroll(int axes) {
return mScrollingChildHelper.startNestedScroll(axes);
}
#Override
public void stopNestedScroll() {
mScrollingChildHelper.stopNestedScroll();
}
#Override
public boolean hasNestedScrollingParent() {
return mScrollingChildHelper.hasNestedScrollingParent();
}
#Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow) {
return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, offsetInWindow);
}
#Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
#Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
#Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
}
Only add this code to your project. It only work in Lollipop devices and later.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
listView.setNestedScrollingEnabled(true);
listView.startNestedScroll(View.OVER_SCROLL_ALWAYS);
}
If you want to make nested scrolling work on pre-Lollipop devices, which you probably do, you have to use corresponding utility classes from the Support library. First you have to replace you ScrollView with NestedScrollView. The latter implements both NestedScrollingParent and NestedScrollingChild so it can be used as a parent or a child scroll container.
But ListView doesn't support nested scrolling, therefore you need to subclass it and implement NestedScrollingChild. Fortunately, the Support library provides NestedScrollingChildHelper class, so you just have to create an instance of this class and call its methods from the corresponding methods of your view class.
I have this litle custom ListView witch is actually a 'ListView' with some call backs. When i dynamically change padding when starting the Activity (Height of the Toolbar), Items dose not respond to padding, and i have to scroll to the top some how.
This is my ListWiew.
<somesustom.cutom.ObservableListView
android:id="#+id/listview"
android:clipToPadding="false"
android:theme="#style/ListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
So the question is How to scroll fully to the top from code?
So i have found the solution
first i deleted android:clipToPadding="false" from xml.
then i configured my ObservableListView like this
public class ObservableListView extends ListView {
private boolean clipPadding = true;
public ObservableListView(Context context) {
this(context, null, 0);
setScrollListner();
}
public ObservableListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
setScrollListner();
}
public ObservableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setScrollListner();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(clipPadding) /// this is the trick, if clip to padding is true, then set selection 0
setSelection(0);
}
public void setScrollListner()
{
clipPadding = true;
super.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(clipPadding && scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
setClipToPadding(false);
clipPadding = false; // if ii begin to scroll clipPadding
// turns to false and onMeasure set selection not being called.
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
}
Not sure if this method wouldn't work if i change padding on button click i guess. But in my case, where i set padding from ViewTreeObserver.OnGlobalLayoutListener this works quite well.
You can try one of two things or both:
listview.smoothScrollToPosition(0);
or
listview.setSelection(0):
I have two ScrollView's side by side and by using the code below I can scroll them simultaneously but I still can scroll them each independently throwing off the scroll positions. How can I make each view scroll simultaneously and disable scrolling each view by itself? I apologize if there's any confusion in my question. Any help would be appreciated, thanks.
ScrollView sv1;
ScrollView sv2;
View clickSource;
View touchSource;
sv1.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(touchSource == null)
touchSource = v;
if(v == touchSource) {
sv2.dispatchTouchEvent(event);
if(event.getAction() == MotionEvent.ACTION_UP) {
clickSource = v;
touchSource = null;
}
}
return false;
}
});
sv2.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
if(touchSource == null)
touchSource = v;
if(v == touchSource) {
sv1.dispatchTouchEvent(event);
if(event.getAction() == MotionEvent.ACTION_UP) {
clickSource = v;
touchSource = null;
}
}
return false;
}
});
Hopefully I understand your question correctly. If you want both ScrollViews to scroll simultaneously then the code below should do the trick (untested):
First create an interface to listen to scroll events:
public interface ScrollChangeListener {
public void onScrollChanged(View view, int x, int y, int oldx, int oldy);
}
Next, create a custom view so you can listen for scroll changes:
public class ObservableScrollView extends ScrollView {
private ScrollChangeListener mScrollChangeListener;
public ObservableScrollView(Context context) {
super(context);
}
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void setScrollChangeListener(ScrollChangeListener listener) {
mScrollChangeListener = listener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
if (mScrollChangeListener != null) {
mScrollChangeListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
}
Use your custom view and create a listener for both ScrollViews.
ObservableScrollView mScrollView1;
ObservableScrollView mScrollView2;
...
ScrollChangeListener listener = new ScrollChangeListener() {
#Override
public void onScrollChanged(View view, int x, int y, int oldx, int oldy) {
ScrollView scrollView;
if (view == mScrollView1) {
scrollView = mScrollView2;
} else if (view == mScrollView2) {
scrollView = mScrollView1;
} else {
return;
}
scrollView.scrollTo(x, y);
}
};
...
mScrollView1.setScrollChangeListener(listener);
mScrollView2.setScrollChangeListener(listener);
Try this
sv1.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollX = sv1.getScrollX(); // for horizontalScrollView
int scrollY = sv1.getScrollY(); // for verticalScrollView
// DO SOMETHING WITH THE SCROLL COORDINATES
sv2.scrollTo(scrollX, scrollY);
}
});