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
Related
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
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 have created a scrollview and an imageview is placed within it. I'd like on scroll for it to resize in the same fashion it is done in the image below but so far I've had little success.
In my attempts the image is resized on scroll but, there is space left over after the resize. How would you modify the below:
Image:
My Code so far:
activity_main.xml
<ImageView
android:layout_gravity="center"
android:adjustViewBounds="true"
android:layout_width="601dp"
android:layout_height="250dp"
android:paddingTop="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:scaleType="fitXY"
android:id="#+id/contactPic"
android:src="#drawable/stock"
android:clickable="true"/>
MainActivity:
#Override
public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
final ImageView contactPicture = (ImageView) findViewById(R.id.contactPic);
final RelativeLayout contactLayout = (RelativeLayout) findViewById(R.id.ContactRLayout);
if (scrollView == contactScrollView) {
View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
int distanceFromPageEnd = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));
Log.e("onScrollChanged", "distance from bottom = " + String.valueOf(distanceFromPageEnd));
if (distanceFromPageEnd >= 1408)
{
contactPicture.getLayoutParams().height = (distanceFromPageEnd - 1408);
contactPicture.requestLayout();
}
}
ScrollViewListener:
public interface ScrollViewListener {
void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);
}
ObservableScrollView:
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;
}
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
#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);
}
}
}
There is simple example below that shows how to achieve parallax effect.
First, put your ImageView and other views into FrameLayout:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/flWrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/contactPic"
android:layout_width="match_parent"
android:layout_height="#dimen/contact_photo_height"
android:scaleType="centerCrop"
android:src="#drawable/stock" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/contact_photo_height">
<!-- Other Views -->
</LinearLayout>
</FrameLayout>
</ScrollView>
LinearLayout's top margin is equal to the ImageViews's height (#dimen/contact_photo_height in our example).
Then we should listen scroll position of the ScrollView and change the position of ImageView:
#Override
protected void onCreate(Bundle savedInstanceState) {
<...>
mScrollView = (ScrollView) findViewById(R.id.scrollView);
mPhotoIV = (ImageView) findViewById(R.id.contactPic);
mWrapperFL = (FrameLayout) findViewById(R.id.flWrapper);
mScrollView.getViewTreeObserver().addOnScrollChangedListener(new ScrollPositionObserver());
<...>
}
private class ScrollPositionObserver implements ViewTreeObserver.OnScrollChangedListener {
private int mImageViewHeight;
public ScrollPositionObserver() {
mImageViewHeight = getResources().getDimensionPixelSize(R.dimen.contact_photo_height);
}
#Override
public void onScrollChanged() {
int scrollY = Math.min(Math.max(mScrollView.getScrollY(), 0), mImageViewHeight);
// changing position of ImageView
mPhotoIV.setTranslationY(scrollY / 2);
// alpha you could set to ActionBar background
float alpha = scrollY / (float) mImageViewHeight;
}
}
I'm trying to make the following effect:
I have a custom ScrollView (in oder to get the onScrollChanged listener) and a custom View inside it. In the custom View I succeed to place the item as I want.
Here my customView:
public class CustomView extends FrameLayout {
private TextView nameView;
private TextView emailView;
private ImageView addressView;
private Tracks track ;
private double scrollProgress = 0.0;
private double topViewScaleFactor = 2.0;
private double collapsedViewHeight = 200.0;
private double expandedViewHeight = 700.0;
private double scrollProgressPerView = expandedViewHeight;
View v;
View firstItem;
View secondView;
int itemMinHeight = 200;
int itemMaxHeight = 700;
public CustomView(MyScrollView paramEventListView, Context paramContext){
super(paramContext);
}
public CustomView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
int m = getMeasuredWidth();
int itemWidth = (r-l)/getChildCount();
// int itemHeight = (b-t)/getChildCount();
firstItem = getChildAt(0);
//firstItem.layout(0, 0, r-l, itemMaxHeight);
firstItem.measure(View.MeasureSpec.makeMeasureSpec(m, MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(itemMaxHeight, MeasureSpec.EXACTLY));
firstItem.layout(0, 0, r-l, itemMaxHeight);
secondView = getChildAt(1);
secondView.measure(View.MeasureSpec.makeMeasureSpec(m, MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(itemMinHeight, MeasureSpec.EXACTLY));
secondView.layout(0, itemMaxHeight, r-l, itemMinHeight+itemMaxHeight);
int FirstAndSEcondItemHeight = firstItem.getHeight() + secondView.getHeight();
for(int i=2; i< this.getChildCount(); i++){
v = getChildAt(i);
// v.layout(itemWidth*i, 0, (i+1)*itemWidth, b-t);
v.layout(0, FirstAndSEcondItemHeight + (itemMinHeight*(i-2)), r-l, FirstAndSEcondItemHeight + ((i-1)*itemMinHeight));
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightMeasured = 0;
/*for each child get height and
heightMeasured += childHeight;*/
for(int i=0; i< this.getChildCount(); i++){
heightMeasured += getChildAt(i).getHeight();
}
//If I am in a scrollview i got heightmeasurespec == 0, so
if(heightMeasureSpec == 0){
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightMeasured, MeasureSpec.EXACTLY);
}
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(this.getSuggestedMinimumHeight(), heightMeasureSpec));
}
Here my Custom ScrollView:
public class MyScrollView extends ScrollView{
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
add(context);
}
public MyScrollView(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);
// How can I acces to each child in the customView class, and change their height depending on the scrollChanged
}
But now I need to change the item height when I scroll the scrollview. I don't know what to put in the onScrollChanged...
If someone has an idea?
Thanks
Perhaps you want to force the view to redraw by calling invalidate()?
From comment: I think you need to move your code in the onMeasure method to replace the "TODO" stub, so that super.onMeasure() is called after your manipulations. And make sure you're passing the new width/height calculations to it.