ScrollView
を使用するアクティビティを持つアプリがあります。ユーザーがScrollView
の一番下に到達したことを検出する必要があります。私はいくつかのグーグル検索を行いましたが、私は このページ が説明されています。しかし、例では、その人はScrollView
を拡張します。先ほど言ったように、アクティビティを拡張する必要があります。
だから、「OK、ScrollView
を拡張するカスタムクラスを作成し、onScrollChanged()
メソッドをオーバーライドして、スクロールの終わりを検出し、それに応じて行動しよう」と言いました。
私はやったが、この行で:
scroll = (ScrollViewExt) findViewById(R.id.scrollView1);
Java.lang.ClassCastException
をスローします。 XMLの<ScrollView>
タグを変更しましたが、明らかに機能しません。私の質問は:なぜ、ScrollViewExt
がScrollView
を拡張する場合、私の顔にClassCastException
を投げるのですか?あまり混乱することなくスクロールの終了を検出する方法はありますか?
人々に感謝します。
編集:約束されたように、ここに私の重要なXMLの一部があります:
<ScrollView
Android:id="@+id/scrollView1"
Android:layout_width="match_parent"
Android:layout_height="match_parent" >
<WebView
Android:id="@+id/textterms"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:gravity="center_horizontal"
Android:textColor="@Android:color/black" />
</ScrollView>
内部のテキストを正当化できるように、TextView
からWebView
に変更しました。私が達成したいのは、「契約の条件が完全に読まれるまで、「Accept」ボタンがアクティブにならない」ということです。私の拡張クラスはScrollViewEx
tと呼ばれます。 ScrollView
のタグScrollViewExt
を変更すると、
Android.view.InflateException: Binary XML file line #44: Error inflating class ScrollViewExt
タグScrollViewEx
を理解しないためです。私はそれが解決策を持っているとは思わない...
ご回答ありがとうございます!
それをやった!
Alexandreが提供してくれた修正は別として、インターフェイスを作成する必要がありました。
public interface ScrollViewListener {
void onScrollChanged(ScrollViewExt scrollView,
int x, int y, int oldx, int oldy);
}
次に、ScrollViewExtのScrollViewからOnScrollChangedメソッドをオーバーライドする必要がありました。
public class ScrollViewExt extends ScrollView {
private ScrollViewListener scrollViewListener = null;
public ScrollViewExt(Context context) {
super(context);
}
public ScrollViewExt(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ScrollViewExt(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, l, t, oldl, oldt);
}
}
}
今、アレクサンドルが言ったように、パッケージ名をXMLタグに入れて(私の障害)、Activityクラスに前に作成したインターフェイスを実装させ、それをすべてまとめます:
scroll = (ScrollViewExt) findViewById(R.id.scrollView1);
scroll.setScrollViewListener(this);
そして、OnScrollChangedメソッドでは、インターフェースから...
@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
// We take the last son in the scrollview
View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));
// if diff is zero, then the bottom has been reached
if (diff == 0) {
// do stuff
}
}
そしてそれはうまくいきました!
アレクサンドル、助けてくれてありがとう!
これを検出する簡単な方法を見つけました:
scrollView.getViewTreeObserver()
.addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
if (scrollView.getChildAt(0).getBottom()
<= (scrollView.getHeight() + scrollView.getScrollY())) {
//scroll view is at bottom
} else {
//scroll view is not at bottom
}
}
});
これらの答えはすべて非常に複雑ですが、これを実現する簡単な組み込みメソッドがあります。 canScrollVertically(int)
例えば:
_@Override
public void onScrollChanged() {
if (!scrollView.canScrollVertically(1)) {
// bottom of scroll view
}
if (!scrollView.canScrollVertically(-1)) {
// top of scroll view
}
}
_
メソッドはView
に実装されているため、RecyclerView、ListView、および実際には他のビューでも機能します。
水平のScrollViewがある場合、 canScrollHorizontally(int)
で同じことが実現できます。
Fustigadorの答えは素晴らしかったが、計算後にいくつかのデバイス(Samsung Galaxy Note Vなど)が0に到達せず、2ポイント残っていることがわかった。以下のような小さなバッファーを追加することをお勧めします。
@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
// We take the last son in the scrollview
View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));
// if diff is zero, then the bottom has been reached
if (diff <= 10) {
// do stuff
}
}
[〜#〜] edit [〜#〜]
XMLのコンテンツを見ると、ScrollViewを使用していることがわかります。カスタムビューを使用する場合は、com.your.packagename.ScrollViewExtを記述する必要があり、コードで使用できるようになります。
<com.your.packagename.ScrollViewExt
Android:id="@+id/scrollView1"
Android:layout_width="match_parent"
Android:layout_height="match_parent" >
<WebView
Android:id="@+id/textterms"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:gravity="center_horizontal"
Android:textColor="@Android:color/black" />
</com.your.packagename.ScrollViewExt>
編集終了
Xmlコンテンツを投稿できますか?
スクロールリスナーを追加して、表示された最後の項目がリストビューの最後の項目であるかどうかを確認できると思います。
mListView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
if(view.getLastVisiblePosition()==(totalItemCount-1)){
//dosomething
}
}
});
サポートライブラリのNestedScrollView
とNestedScrollView.OnScrollChangeListener
インターフェイス。
https://developer.Android.com/reference/Android/support/v4/widget/NestedScrollView.html
または、アプリがAPI 23以上をターゲットにしている場合、ScrollView
で次のメソッドを使用できます。
View.setOnScrollChangeListener(OnScrollChangeListener listener)
次に、@ Fustigadorが答えで説明した例に従ってください。ただし、@ Willが説明したように、何らかの理由でユーザーまたはシステムがリストの一番下に到達できない場合に備えて、小さなバッファーを追加することを検討してください。
また、スクロール変更リスナーは、負の値またはビューの高さより大きい値で呼び出されることもあります。おそらく、これらの値はスクロールアクションの「運動量」を表します。ただし、適切に処理しない限り(床/腹筋)、ビューが範囲の上部または下部にスクロールされると、スクロール方向の検出に問題が発生する可能性があります。
スクロールビューの一部はXMLファイルにパディングがあり、動作しない場合があるため、常にscrollView.getPaddingBottom()
を追加してスクロールビューの全高に一致させる必要があります。
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
if (scrollView != null) {
View view = scrollView.getChildAt(scrollView.getChildCount()-1);
int diff = (view.getBottom()+scrollView.getPaddingBottom()-(scrollView.getHeight()+scrollView.getScrollY()));
// if diff is zero, then the bottom has been reached
if (diff == 0) {
// do stuff
}
}
}
});
scrollView = (ScrollView) findViewById(R.id.scrollView);
scrollView.getViewTreeObserver()
.addOnScrollChangedListener(new
ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
if (!scrollView.canScrollVertically(1)) {
// bottom of scroll view
}
if (!scrollView.canScrollVertically(-1)) {
// top of scroll view
}
}
});
ほとんどの答えは実際、uが一番下までスクロールすると、リスナーが数回トリガーされる、これは望ましくないです。この動作を回避するために、メソッドを再度呼び出す前にスクロール位置が変更されたかどうかをチェックするフラグscrollPositionChangedを追加しました。
public class EndDetectingScrollView extends ScrollView {
private boolean scrollPositionChanged = true;
private ScrollEndingListener scrollEndingListener;
public interface ScrollEndingListener {
void onScrolledToEnd();
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
View view = this.getChildAt(this.getChildCount() - 1);
int diff = (view.getBottom() - (this.getHeight() + this.getScrollY()));
if (diff <= 0) {
if (scrollPositionChanged) {
scrollPositionChanged = false;
if (scrollEndingListener != null) {
scrollEndingListener.onScrolledToEnd();
}
}
} else {
scrollPositionChanged = true;
}
}
public void setScrollEndingListener(ScrollEndingListener scrollEndingListener) {
this.scrollEndingListener = scrollEndingListener;
}
}
次に、リスナーを設定します
scrollView.setScrollEndingListener(new EndDetectingScrollView.ScrollEndingListener() {
@Override
public void onScrolledToEnd() {
//do your stuff here
}
});
あなたが好きなら同じことをするかもしれません
scrollView.getViewTreeObserver().addOnScrollChangedListener(...)
ただし、このリスナーを追加するクラスからフラグを提供する必要があります。
カスタムScrollView
の最後にいるかどうかを判断するには、メンバー変数を使用して最後のy位置を保存することもできます。次に、最後のy位置を現在のスクロール位置と比較できます。
private int scrollViewPos;
...
@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
//reached end of scrollview
if (y > 0 && scrollViewPos == y){
//do something
}
scrollViewPos = y;
}
私はスクロールビューの一番下の前にオフセットを持つFABを表示/非表示にしたかった。これは私が思いついた解決策です(Kotlin):
scrollview.viewTreeObserver.addOnScrollChangedListener {
if (scrollview.scrollY < scrollview.computeVerticalScrollRange() - scrollview.height - offset) {
// fab.hide()
} else {
// fab.show()
}
}