I have created two ScrollView in same layout. You can say a parallel scrollview. I want to scroll one scrollview manually and in response another scrollview should scroll exactly same way. Scroll length for both view is same.
This should happen same for both the scrollviews. I tried a code for this.
horizontalScrollViewB.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
horizontalScrollViewD.scrollTo(horizontalScrollViewB.getScrollX(), horizontalScrollViewB.getScrollY());
}
});
horizontalScrollViewD.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
horizontalScrollViewB.scrollTo(horizontalScrollViewD.getScrollX(), horizontalScrollViewD.getScrollY());
}
});
Here what happened is for the first "B" scrollview it works fine but for "D" scrollview it creates problem in scrolling.
I understand the problem but couldnot get the solution. So what should I do to avoid one's call when another is calling "onScrollChangeListener()".
Override HorizontalView class
package com.example.shaby.payshare;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.HorizontalScrollView;
/**
* Created by shaby on 3/7/2017.
*/
public class HorizontalScroll extends HorizontalScrollView {
private ScrollViewListener scrollViewListener = null;
public interface ScrollViewListener {
void onScrollChanged(HorizontalScroll scrollView, int x, int y, int oldx, int oldy);
}
public HorizontalScroll(Context context) {
super(context);
}
public HorizontalScroll(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HorizontalScroll(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if(scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
}
Implement the interface in your required class
Related
I have a button to automatically returns to top position into a NestedScrollView. When the scroll is not in progress, it works like a charm, otherwise if the current scrolling is in progress yet, the scroll goes to top but directly scroll a lit bit down just after...
I wrote this method:
private void scrollToTop() {
mScrollView.stopNestedScroll();
mScrollView.postDelayed(new Runnable() {
#Override
public void run() {
mScrollView.scrollTo(0, 0);
}
}, 50);
}
Have you got some ideas guys to stop the current scroll and go to top immediately?
Thank you very much
The correct way would be intercepting user's touch when you scrolling pragmatically. That would require you to extend ScrollView overriding scrollTo and onInterceptTouchEvent methods and disable user touch while list is scrolling, returning touch after scroll is complete.
So the sequence would be like this:
Call scrollTo to scroll to specific position
Enable a flag to disable touch events and begin scrolling
When scroll is finished and state changed to idle disable flag
P.S: I can provide code example but it appears you have more expertise than me in writing codes. Cheers!
Here a code snippet:
import android.content.Context;
import android.support.v4.widget.NestedScrollView;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class MyScrollView extends NestedScrollView {
public static final int MAX_SCROLL_FACTOR = 1;
boolean isAutoScrolling;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void scrollTo(int x, int y) {
isAutoScrolling = true;
super.scrollTo(x, y);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (isAutoScrolling)
return super.onInterceptTouchEvent(event);
return false;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (isAutoScrolling)
return super.onTouchEvent(event);
return false;
}
#Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (isAutoScrolling) {
if (Math.abs(y - oldY) < MAX_SCROLL_FACTOR || y >= getMeasuredHeight() || y == 0
|| Math.abs(x - oldX) < MAX_SCROLL_FACTOR || x >= getMeasuredWidth() || x == 0) {
isAutoScrolling = false;
}
}
}
}
How can I determine the direction of a vertical ScrollView?
I need to hide and display another linear layout accoding to scrollview scroll
You can use the onScrollChanged(int l, int t, int oldl, int oldt) method for that. As there isn't a setter for a listener, but just that method, you will have to create your own ScrollView class and override that method and do your thing in your implementation.
public class ObservableScrollView extends ScrollView {
private static final int DEFAULT_THRESHOLD_DP = 4;
private ScrollDirectionListener scrollDirectionListener;
private int scrollThreshold;
public ObservableScrollView(Context context) {
this(context, null);
}
public ObservableScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
scrollThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_THRESHOLD_DP, context.getResources().getDisplayMetrics());
}
#Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (Math.abs(y - oldY) > scrollThreshold && scrollDirectionListener != null) {
if (y > oldY) {
scrollDirectionListener.onScrollUp(Math.abs(y - oldY));
} else {
scrollDirectionListener.onScrollDown(Math.abs(y - oldY));
}
}
}
public void setScrollThresholdInPx(int px) {
scrollThreshold = px;
}
public void setScrollThresholdInDp(Context context, float dp) {
scrollThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
public void setOnScrollDirectionListener(ScrollDirectionListener listener) {
scrollDirectionListener = listener;
}
public interface ScrollDirectionListener {
void onScrollDown(int pixels);
void onScrollUp(int pixels);
}}
got answer :) use this custom scrollview
ScrollView.setOnScrollDirectionListener(new ObservableScrollView.ScrollDirectionListener() {
#Override
public void onScrollDown(int pixels) {
contenttool.setVisibility(View.INVISIBLE);
}
#Override
public void onScrollUp(int pixels) {
contenttool.setVisibility(View.VISIBLE);
}
});
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);
}
});
I've got a scrollView with dinamically loading content. Sometimes there can be a lot of content, so I would like to load more when user scrolls to bottom.
I searced for suitable metods and found two:
onScrollChanged()
and
getScrollY()
But I dont know how to use it for my purposes.
Please give me some advice.
Scroll view does not provide any method to check if you've reached bottom of the view so the best technique is to extend your own custom view with scroll view . This is how i implemented in my app.
Step 1:Create a custom class for scroll view
public class ObservableScrollView extends ScrollView {
private ScrollViewListener scrollViewListener = null;
public ObservableScrollView(Context context) {
super(context);
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
View view = (View) getChildAt(getChildCount() - 1);
int diff = (view.getBottom() - (getHeight() + getScrollY()));
if (diff == 0) { // if diff is zero, then the bottom has been reached
if (scrollViewListener != null) {
scrollViewListener.onScrollEnded(this, x, y, oldx, oldy);
}
}
super.onScrollChanged(x, y, oldx, oldy);
}
}
Step 2:Create a scroll view Listener Interface
public interface ScrollViewListener {
void onScrollEnded(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);
}
Step 3:Add the view in your layout
<com.platinumapps.facedroid.ObservableScrollView
android:id="#+id/scrollView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</com.platinumapps.facedroid.ObservableScrollView>
Step 4:Implement that interface in your class
public class MyFragment extends Fragment implements ScrollViewListener
#Override
public void onScrollEnded(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
//Perform your action
}
And you are done
I think the better way would be to use ListView.But if you only required ScrollView.
First you fix a threshold height of your ScrollView so whenever you crosses that limit load new data & append to ScrollView & reset your threshold height.
This is how you can try..
Create a customized ScrollView like this.
public class ObservableScrollView extends ScrollView
{
private ScrollViewListener scrollViewListener = null;
public ObservableScrollView(Context context)
{
super(context);
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public ObservableScrollView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener)
{
this.scrollViewListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy)
{
super.onScrollChanged(x, y, oldx, oldy);
if (scrollViewListener != null)
{
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
}
& then create an interface
public interface ScrollViewListener
{
void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);
}
& your XML layout should be like this
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.solve.stackoverflow.ObservableScrollView
android:id="#+id/endless_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="#+id/endless_scrollview_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
</com.solve.stackoverflow.ObservableScrollView>
</LinearLayout>
& finally use all these in your Activity
public class EndLessActivity extends Activity implements ScrollViewListener
{
ObservableScrollView scrollView;
LinearLayout layout;
int threshold = 20; //Setting threshold
#Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.endless_scrollview);
scrollView = (ObservableScrollView) findViewById(R.id.endless_scrollview);
layout = (LinearLayout) findViewById(R.id.endless_scrollview_layout);
scrollView.setScrollViewListener(this);
appendData();
}
#Override
public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy)
{
// TODO Auto-generated method stub
if (y > threshold)
appendData();
}
void appendData()
{
for (int i = 0; i < 15; i++)
{
threshold += 10;//Reseting threshold.
TextView tv = new TextView(this);
tv.setBackgroundResource(R.drawable.ic_launcher);
layout.addView(tv);
}
}
}
Try this & let me know, whether it solve your problem or not.
Thanks
I have 2 ScrollViews in my android layout. How can I synchronise their scroll positions?
There is a method in ScrollView...
protected void onScrollChanged(int x, int y, int oldx, int oldy)
Unfortunately Google never thought that we would need to access it, which is why they made it protected and didn't add a "setOnScrollChangedListener" hook. So we will have to do that for ourselves.
First we need an interface.
package com.test;
public interface ScrollViewListener {
void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);
}
Then we need to override the ScrollView class, to provide the ScrollViewListener hook.
package com.test;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;
public class ObservableScrollView extends ScrollView {
private ScrollViewListener scrollViewListener = null;
public ObservableScrollView(Context context) {
super(context);
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if(scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
}
And we should specify this new ObservableScrollView class in the layout, instead of the existing ScrollView tags.
<com.test.ObservableScrollView
android:id="#+id/scrollview1"
... >
...
</com.test.ObservableScrollView>
Finally, we put it all together in the Layout class.
package com.test;
import android.app.Activity;
import android.os.Bundle;
public class Q3948934 extends Activity implements ScrollViewListener {
private ObservableScrollView scrollView1 = null;
private ObservableScrollView scrollView2 = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.q3948934);
scrollView1 = (ObservableScrollView) findViewById(R.id.scrollview1);
scrollView1.setScrollViewListener(this);
scrollView2 = (ObservableScrollView) findViewById(R.id.scrollview2);
scrollView2.setScrollViewListener(this);
}
public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
if(scrollView == scrollView1) {
scrollView2.scrollTo(x, y);
} else if(scrollView == scrollView2) {
scrollView1.scrollTo(x, y);
}
}
}
The scrollTo() code takes care of any loop conditions for us, so we don't need to worry about that. The only caveat is that this solution is not guaranteed to work in future versions of Android, because we are overriding a protected method.
An improvement to Andy's solution :
In his code, he uses scrollTo, the issue is, if you fling one scrollview in one direction and then fling another one in another direction, you'll notice that the first one doesn't stop his previous fling movement.
This is due to the fact that scrollView uses computeScroll() to do it's flinging gestures, and it enters in conflict with scrollTo.
In order to prevent this, just program the onScrollChanged this way :
public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
if(interceptScroll){
interceptScroll=false;
if(scrollView == scrollView1) {
scrollView2.onOverScrolled(x,y,true,true);
} else if(scrollView == scrollView2) {
scrollView1.onOverScrolled(x,y,true,true);
}
interceptScroll=true;
}
}
with interceptScroll a static boolean initialized to true. (this helps avoid infinite loops on ScrollChanged)
onOverScrolled is the only function I found that could be used to stop the scrollView from flinging (but there might be others I've missed !)
In order to access this function (which is protected) you have to add this to your ObservableScrollViewer
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
Why not just implements OnTouchListener in your activity.
Then override the onTouch method, then get the scroll postion of the first ScrollViewOne.getScrollY() and update ScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());
Just another idea... :)
In the Android support-v4 package, Android provide a new class named NestedScrollView.
we can replace the <ScrollView> node with <android.support.v4.widget.NestedScrollView> in layout xml,
and implements its NestedScrollView.OnScrollChangeListener in Java to handle the scrolling.
That makes things easier.