Reputation: 522
Mine is a native app where I want to show/ hide a button if the user scrolls to the end of webview. I looked at an answer here and got the entire idea of how to register the callback via interface. My major issue is that I am not able to get my calculations right inside the onScrollChanged method. I have tried combination of things like getHeight(), getContentHeight(), top etc but it just seems to fire too early. I tried with a simple page like google's with comparatively lesser content as well as a news page.
These logics work fine for a normal scroll view. Since the webpage has a lot of content, it could be messing up the calculations. Pasting a sample code: does not work.
@Override
protected void onScrollChanged(int left, int top, int oldLeft, int oldTop) {
if ( mOnWebViewBottomReachedListener != null ) {
//if ( (getContentHeight() - (top + getHeight())) <= mMinDistance )
int diff = (getBottom() - (getHeight() + getScrollY()));
Log.e("values ", diff+" o");
if ((int) Math.floor(getContentHeight() * getScaleY()) == top)
mOnWebViewBottomReachedListener.onBottomReached(this);
}
super.onScrollChanged(left, top, oldLeft, oldTop);
}
Need help with this. Thanks.
Upvotes: 14
Views: 32686
Reputation: 156
May a bit late to the party:
enum class ScrollDirection(val direction: Int) {
Upwards(-1),
Downwards(1)
}
private fun detectOnScrollMaxDown() = with(binding) {
webView.setOnScrollChangeListener { _, _, _, _, _ ->
val isDownDirectPossible = webView.canScrollVertically(ScrollDirection.Downwards.direction)
if (!isDownDirectPossible) Log.d("Reached the bottom? ","${!isDownDirectPossible}")
}
}
Upvotes: 3
Reputation: 389
The following Kotlin codes work for me:
web_view.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
val measuredHeight: Int = web_view.measuredHeight
if(measuredHeight + scrollY == web_view.computeVerticalScrollRange()){
Log.d("WebView","Reach bottom")
}
}
Tested for Android 12 & Android 10. API level 31.
Upvotes: 1
Reputation: 21
Checked this on android version 8-11. it works 100%. I have achieved it by using computeVerticalScrollRange
& computeVerticalScrollExtent
.Following is an example of webview inside an activity/fragment.
Create a custom webview class.
public class MyWebView extends WebView {
public MyWebView(Context context) {
super(context);
}
public MyWebView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
@Override
protected int computeVerticalScrollExtent() {
return super.computeVerticalScrollExtent();
}
}
In your layout file , implement webview like this and include this layout file into your activity
or fragment
view file if you need to.
<in.package.folder.MyWebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="0dp"/>
In your activity/Fragment where you want to have the webview
private MyWebView webView;
webView = (MyWebView) findViewById(R.id.webview);
computeVerticalScrollRange
& computeVerticalScrollExtent
methods defined in the webview
class to calculate the end of scroll. 3rd parameter i1 ( Scroll Y) will be equal to (computeVerticalScrollRange
- computeVerticalScrollExtent
) if the scroll has reached the end of the webview content webView.setOnScrollChangeListener((view, i, i1, i2, i3) -> {
int r1 = webView.computeVerticalScrollRange();
int r2 = webView.computeVerticalScrollExtent();
if (i1 == (r1 - r2)) {
"REACHED END OF THE SCROLL"
}
});
Upvotes: 2
Reputation: 3398
This three method combination work excellent for me
Create two boolean variable two detect topReached or bottomReached.
use computeVerticalScrollRange() to get vertical scroll range. computeVerticalScrollRange() will help you when content height is less(In such case onScrollChanged not called).
- use onScrollChanged to detect scrolling is happing. scrolling is happing means content height is greater than webView measured height.Now detect bottom end using newt or detect top using newt.
private boolean topReached = false, bottomReached = false;
@Override
protected int computeVerticalScrollRange() {
int readerViewHeight = getMeasuredHeight();
int verticalScrollRange = super.computeVerticalScrollRange();
if (readerViewHeight >= verticalScrollRange) {
topReached = true;
bottomReached = true;
}
return verticalScrollRange;
}
@Override
protected void onScrollChanged(int newLeft, int newTop, int oldLeft, int oldTop) {
Logger.i(TAG, "onScrollChanged");
topReached = false;
bottomReached = false;
Log.i(TAG, "onScrollChanged newLeft : " + newLeft);
Log.i(TAG, "onScrollChanged newTop : " + newTop);
Log.i(TAG, "onScrollChanged oldLeft : " + oldLeft);
Log.i(TAG, "onScrollChanged oldTop : " + oldTop);
int readerViewHeight = getMeasuredHeight();
int contentHeight = getContentHeight();
if (newTop == 0) {
Log.d(TAG, "onScrollChanged : Top End");
topReached = true;
} else if (newTop + readerViewHeight >= contentHeight) {
Log.d(TAG, "onScrollChanged : Bottom End");
bottomReached = true;
}
super.onScrollChanged(newLeft, newTop, oldLeft, oldTop);
}
Upvotes: 3
Reputation: 141
After a lot of google search all the solutions I found were calculating the height and determining the scrollOfset, but most of them are not reliable in most cases resulting in issues like described here Weird miscalculation when trying to detect if WebView scrolled to bottom
I have found the solution that works 100% is to determine it based on overScroll
webView.setOnOverScrolledCallback(new WebViewCustom.OnOverScrolledCallback() {
@Override
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
if(clampedY)
if(scrollY == 0) {
//top
}
else {
//bottom
}
}
});
Upvotes: 14
Reputation: 263
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
int height = (int) Math.floor(this.getContentHeight() * this.getScale());
int webViewHeight = this.getMeasuredHeight();
if(this.getScrollY() + webViewHeight >= height){
Log.i("THE END", "reached");
}
super.onScrollChanged(l, t, oldl, oldt);
}
This logic works fine for a webview.
Upvotes: 17
Reputation: 2585
Firstly extend your webview from this class. https://stackoverflow.com/a/14753235/3027225 After this upgrade your webView like this
<com.YourPackageName.ObservableWebView
android:id="@+id/webView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true" />
And finally override the onScroll method like the following:
webView.setOnScrollChangedCallback(new OnScrollChangedCallback() {
@Override
public void onScroll(int l, int t) {
int tek = (int) Math.floor(webView.getContentHeight() * webView.getScale());
if(tek - webView.getScrollY() == webView.getHeight())
Toast.makeText(getActivity(), "End", Toast.LENGTH_SHORT).show();
}
});
Upvotes: 4