Synchronize scrolling of two ScrollViews inside a RelativeLayout - android

I need to synchronize the scrolling positions of two ScrollViews.
Both ScrollViews contain their individual RecyclerView (I can't put them inside same ScrollView due to design requirements).
How can I achieve that?

I highly recommend that you CHANGE YOUR DESIGN but there is a way to achieve this
for first scroll view, define two variable x,y and get current scroll position
x = firstScrollView.getScrollX();
y = firstScrollView.getScrollY();
and pass these values to other scrollView "scrollTo" method.
like:
ScrollView secondScrollView = (ScrollView)findViewById(R.id.scrollView);
secondScrollView.scrollTo(x, y);
if didn't work try:
secondScrollView.post(new Runnable() {
#Override
public void run() {
secondScrollView.scrollTo(x, y);
}
});

First of all, use NestedScrollView from Support library since it enables us to listen scroll events easily. Then, set onScrollChange listeners for both NestedScrollView you have. When you receive scroll change for scrollView1 for instance, call scrollTo(...) for scrollView2:
scrollView1.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
scrollView2.setOnScrollChangeListener(null);
scrollView2.scrollTo(scrollX, scrollY);
scrollView2.setOnScrollChangeListener(...); //SET SCROLL LISTENER AGAIN
}
});
Before calling scrollTo(..) for scrollView2, remove its listener, then add it again. Otherwise, scrollTo(..) you call can cause infinite calls in both listeners of NestedScrollViews.
And of course, you need to write similar above code for your scrollView2.

Attach an RecyclerView.OnScrollListener to both RecyclerViews via addOnScrollListener(RecyclerView.OnScrollListener listener).
Update the position of the other one when the event is fired via: scrollTo(int x, int y).
Remember to somehow distinguish between a user-triggered scroll and a programmtic scroll (from the other RecyclerViews event).
One way would be, like Ugurcan Yildirim suggested, to detach the listener of the ScrollView you want to update.
Another option is a boolean flag isUserTriggered. A third way would be to determine which ScrollView currently has the focus to distinguish (see here).

Related

RecyclerView onScrollStateChanged/onScrolled event is not fired when no scrolling occurred

Doesn't sound like a problem, right?) Let me explain the use case. Let say at the end of every scroll event you want to animate/highlight the target item:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
#Override
public void onScrollStateChanged(final RecyclerView recyclerView,final int newState){
super.onScrollStateChanged(recyclerView,newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) { // on scroll stop
blink();
}
}
}
...
recyclerView.scrollToPosition(targetPosition);
Works as expected, as long as current position is different from the target one and there is something to scroll actually. What is also very possible is that the list is already in the right position and calling scrollToPosition() has no effect, onScrollStateChanged() is not triggered, as well as our animation logic. As a workaround I can think about this:
final int firstVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
if (firstVisibleItemPosition == targetPosition){
blink();
} else {
[previous code block]
}
but is it really the best possible solution? It sounds natural to me to have some consistent onScrollStop() callback after every scrollToPosition() call, regardless of the visual list movement, because it's none of my business to predict what is going to happen on the screen (will it move, or not?) and try to hack that. All I know, as an API user is that scrollToPosition() was called and I expect the appropriate callback to be fired.
Fun fact: if you extend RecyclerView.SmoothScroller and override onStop() method then you're actually notified all the time. The issue with this approach is that it's triggered slightly before the list stop scrolling ¯\_(ツ)_/¯.

Programmatically "swipe-to-dismiss" an item in a ListView/RecyclerView?

I need to be able to programmatically dismiss an item inside a RecyclerView without the user actually swiping (instead I want to dismiss the item when they tap a button in the card). A lot of libraries I've seen only seem to support actual swipes.
I've tried using an existing library and just simulate a MotionEvent by creating a swipe on my own programmatically, but this interferes with another horizontal-swipe listener, so I'm wondering how else this could be done, ideally for a RecyclerView but if anyone knows how to for a ListView instead I can try to adapt that.
I've looked at this library as well as others for inspiration but I can't figure out how to trigger the swipes programmatically instead.
Use a ListView or RecyclerView with custom adapter, and call notifyDataSetChanged after removing an item from the datalist:
private void removeListItem(View rowView, final int position) {
Animation anim = AnimationUtils.loadAnimation(this,
android.R.anim.slide_out_right);
anim.setDuration(500);
rowView.startAnimation(anim);
new Handler().postDelayed(new Runnable() {
public void run() {
values.remove(position); //Remove the current content from the array
adapter.notifyDataSetChanged(); //Refresh list
}
}, anim.getDuration());
}
Use one of the libraries that offer the swipe to dissmis funcionality ad extract the animation part, if im not mistaken its at the action_up at the onTouch(). Then call it from your onClick of the button.

Estimate the final scroll position after flinging ends in Android?

I would like to achieve a "snap" effect on an Android ListView. Specifically, when the ListView stops scrolling I would like it to stop at certain positions so that the first visible item is completely shown. To do that, I need to be able to estimate the final position that the ListView will stop at when the user stops dragging or flinging.
In the case of dragging, when we receive the ACTION_UP or ACTION_CANCEL event, I can simply read off the current Y offset as the final position.
In the case of flinging, however, when we receive the ACTION_UP or ACTION_CANCEL event, I will need some extra information to determine the final position that the ListView will stop scrolling at because of the deceleration period.
iOS provides such information through the targetContentOffset parameter in willEndDragging. Is there any equivalent information available in Android?
This is related to another question I asked but got no response: In Android, how to achieve snap effect on a listview while respecting acceleration caused by fling?
I would override the OnScroll event of your TestListView like:
#Override
public void onScroll(AbsListView lw, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) {
// Determine when the last row item is fully visible.
final int lastItem = firstVisibleItem + visibleItemCount;
if(lastItem >= totalItemCount) {
Log.d("onScroll", "Reached last item");
}
}
}
Notice this listener gives you the first visible position/item, and the number of row items in the Listview.
I would use this listener because sometimes getFirstVisiblePosition() is not accurate, especially on a virtual ListView like what we're using.
Another advantage of onScroll override method over onTouchEvent is that onScroll covers less triggers than onTouchEvent. And it is fast in my experience with it.
Keep us posted and I am interested on this issue. Regards, Tommy Kwee

listview alternative to smoothScrollToPositionFromTop in android

I need to set the firstVisibleItem. right now when I use smoothScrollToPositionFromTop there seems to be some visible scrolling (of course). I am drawing a blank on what the alternative method is. I have used it before, but I just can't seem to find it. Basically instead of smooth scrolling, it "instantaneously" sets the position that I select plus whatever offset I pass it. Does anyone remember which method does this? It's something like
listView.setAsTop(position, offset)
Use AbsListView#setSelection(int position):
new Handler().post(new Runnable() {
#Override
public void run() {
listView.setSelection(position);
}
});
Make sure listView and position are member variables or declared as final.

Android ScrollView isFinished?

Just looking for an easy answer that I can't find on Google. Simply put. Is there a way to tell when ScrollView has stopped scrolling after a fling?
Yes, after fling it comes in ideal state which means that your fling is over. For this you have to put listener on your list view Like list.setOnScrollListener(this). Then you have to check in the listener that if it's in ideal state or not. like this
public void onScrollStateChanged(AbsListView view, int scrollState)
{
if(OnScrollListener.SCROLL_STATE_IDLE)
{
your code;
}
}

Categories

Resources