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.
Related
Write a Custom ListView like:
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
Log.d("onLayout","onLayout=====");
}
}
As I know, when the layout attribute of view has changed, in order to apply the change ( invalide() or requestLayout() ),its parent's onLayout method should be called and layout its children.
So when I scroll the ListView, the layout attribute of its child view has changed, but onLayout doesn't called at all. Why?
Finally I realized relayout a ViewGroup don't need always call onLayout()/layout() method.There are many ways to change views position in ViewGroup,but each way must call onDraw() to write the changed position in FrameBuffer in order to show it in Screen.(Please tell whether I'm wrong with this)
In ListView,I had debug the source code,and when scroll ListView, the stack trace is:
`
trackMotionScroll:5023, AbsListView (android.widget)
scrollIfNeeded:3424, AbsListView (android.widget)
startScrollIfNeeded:3352, AbsListView (android.widget)
onTouchMove:3793, AbsListView (android.widget)
onTouchEvent:3651, AbsListView (android.widget)
dispatchTouchEvent:9294, View (android.view)
in trackMotionScroll,it will call ViewGroup#offsetChildrenTopAndBottom(incrementalDeltaY)
public void offsetChildrenTopAndBottom(int offset) {
final int count = mChildrenCount;
final View[] children = mChildren;
boolean invalidate = false;
for (int i = 0; i < count; i++) {
final View v = children[i];
v.mTop += offset;
v.mBottom += offset;
if (v.mRenderNode != null) {
invalidate = true;
v.mRenderNode.offsetTopAndBottom(offset);
}
}
if (invalidate) {
invalidateViewProperty(false, false);
}
notifySubtreeAccessibilityStateChangedIfNeeded();
}
This will cause the reLayout of ListView.
So I got a conclusion,you don't need obey the framework's measure()–layout()–draw() procedure,but only change view's layout attribute and invalidate,it will also change view's layout.
And I guess ListView dispense with layout() when scroll will improve its efficiency
Can't find any information on how to create a rotating ScrollView in Android. With this I mean a ScrollView that restarts when reaching the last element.
I have started to implement my own custom ScrollView that scrolls to the beginning when reaching bottom. But there are still many corner cases that I need to take care off to make it smooth. (Have just put a few minutes on it so far)
public class CardScrollView extends ScrollView {
public CardScrollView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public CardScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CardScrollView(Context context) {
super(context);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
View view = (View) getChildAt(getChildCount()-1);
int diff = (view.getBottom()-(getHeight()+getScrollY()));
if (diff == 0) {
scrollTo(0, -300);
}
super.onScrollChanged(l, t, oldl, oldt);
}
}
I was thinking there should be many people tried doing this before me but can find much information on google. Can anyone point me in the right direction before I spend days into developing my own view?
Turned out a ViewPager is better for my purpose. I found some example code from here.
https://github.com/acbelter/DirectionalCarousel
I am using this library to create a floating action button in my android application. What I need is to hide the floating action button when I scroll down, and show it again when I scroll up. The problem is I have a FrameLayout and a ScrollView that doesn't contain a setOnScrollListener()!
I read this solution, but supposedly it is laggy.
Can anyone tell me how to reach get the functionality I'm looking for without losing performance?
Try this library.
It provides a floating action button that disappears when you scroll down :-)
One option is to create an ObservableScrollView that has a scroll listener. Google uses this approach in the IOSched14 app. One way of creating this might be:
public class ObservableScrollView extends ScrollView {
private boolean mScrollingEnabled = true;
private ArrayList<Callbacks> mCallbacks = new ArrayList<Callbacks>();
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
for (Callbacks c : mCallbacks) {
c.onScrollChanged(l - oldl, t - oldt);
}
}
#Override
public int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
public void addCallbacks(Callbacks listener) {
if (!mCallbacks.contains(listener)) {
mCallbacks.add(listener);
}
}
public static interface Callbacks {
public void onScrollChanged(int deltaX, int deltaY);
}
public void setScrollingEnabled(boolean scrollingEnabled) {
mScrollingEnabled = scrollingEnabled;
}
// #Override
// public boolean onTouchEvent(MotionEvent ev) {
// if (!mScrollingEnabled) return false;
// return super.onTouchEvent(ev);
// }
}
Simply add this instead of your ScrollView and then attach a listener using the addCallbacks method:
ObservableScrollView scrollView = new ObservableScrollView(context);
scrollView.addCallbacks(this);
This question already has answers here:
Detect end of ScrollView
(13 answers)
Closed 7 years ago.
I have done the vertical scroll view.what is my requirement is,I just need to detect the end of the scroll view and make the button visible.Can anybody tell me how to detect that the scroll view has reached the bottom?
public class LockableVerScroll extends ScrollView {
private boolean isScrollable;
private ScrollListener scrollListener=null;
public LockableVerScroll(Context context)
{
super(context);
isScrollable=true;
}
public LockableVerScroll(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
isScrollable=true;
}
public LockableVerScroll(Context context, AttributeSet attrs,int defStyle)
{
super(context, attrs, defStyle);
isScrollable=true;
}
public boolean isScrollable()
{
return isScrollable;
}
public boolean setScrollable(boolean mScrollable)
{
return mScrollable=isScrollable;
}
public void setScrollViewListener(ScrollListener mscrollListener)
{
this.scrollListener=mscrollListener;
}
need help..thanks in advance.!!
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
View view = (View) getChildAt(getChildCount()-1);
int diff = (view.getBottom()-(getHeight()+getScrollY()+view.getTop()));// Calculate the scrolldiff
if( diff == 0 ){ // if diff is zero, then the bottom has been reached
Log.d(ScrollTest.LOG_TAG, "MyScrollView: Bottom has been reached" );
}
super.onScrollChanged(l, t, oldl, oldt);
}
Override the onScrollChanged() method in your extension of ScrollView
#Override
protected void onScrollChanged(int l1, int t1, int l2, int t2) {
int count = getChildCount();
View view = (View) getChildAt(count - 1);
int delta = (view.getBottom() - (getHeight() + getScrollY() + view.getTop()));
if( delta == 0 )
{
// scrollview has reached bottom, do whatever you want here.
}
super.onScrollChanged(l1, t1, l2, t1);
}
Try this. This will work.
EDIT:
if( delta == 0 )
{
// define interface in Activity / Fragment and call its method here
mScrollListener.onScrollViewHitsBottom();
}
I want to implement an activity which have search functionality that searches contents of a list view.The search bar should be on top of list view and it should hide from user when he scroll down the list view. And when he is searching for something , it should always be on top of list view!!! How can I implement it.
Best Regards!
I assume you know how to create XML layout with SearchView on top of a ListView, it could be placed inside vertical LinearLayout. The tricky part is how to manipulate SearchView, right?
You can register a listener on your ListView like this:
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case SCROLL_STATE_IDLE:
//scroll was stopped, let's show search bar again
break;
case SCROLL_STATE_TOUCH_SCROLL:
//user is scrolling, let's hide search bar
break;
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem > 0) {
//user scrolled down, first element is hidden
}
}
});
As you can see, you will be informed about state changes and you can use it if you want hide searchview during touch events. Or, you can listen for changing visible elements. This simple if statement checking firstVisibleItem > 0 will tell you when user scrolled down. You can also track disappearing list items and react whenever any item is shown or hidden.
Another way to listen for scroll changes is extending ListView and override onScrollChanged() method e.g.
class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
for (Callbacks c : mCallbacks) {
c.onScrollChanged(l - oldl, t - oldt);
}
}
#Override
public int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
public void addCallbacks(Callbacks listener) {
if (!mCallbacks.contains(listener)) {
mCallbacks.add(listener);
}
}
public static interface Callbacks {
public void onScrollChanged(int deltaX, int deltaY);
}
}
Google folks did something like it in IOSched app.