Ich habe eine App, die eine Aktivität hat, die a verwendet ScrollView
. Ich muss erkennen, wann der Benutzer dem auf den Grund geht ScrollView
. Ich habe ein bisschen gegoogelt und diese Seite gefunden, auf der es erklärt wird. Aber im Beispiel erweitert sich dieser Typ ScrollView
. Wie gesagt, ich muss die Aktivität erweitern.
Also sagte ich: "Okay, versuchen wir, eine benutzerdefinierte Klasse zu erweitern ScrollView
, die onScrollChanged()
Methode zu überschreiben , das Ende des Bildlaufs zu erkennen und entsprechend zu handeln."
Ich habe es getan, aber in dieser Zeile:
scroll = (ScrollViewExt) findViewById(R.id.scrollView1);
es wirft ein java.lang.ClassCastException
. Ich habe die <ScrollView>
Tags in meinem XML geändert , aber es funktioniert offensichtlich nicht. Meine Fragen sind: Warum, wenn es sich ScrollViewExt
ausdehnt ScrollView
, wirft es mir ein Gesicht ins Gesicht ClassCastException
? Gibt es eine Möglichkeit, das Ende des Bildlaufs zu erkennen, ohne zu viel durcheinander zu bringen?
Danke Leute.
EDIT: Wie versprochen, hier ist der Teil meines XML, der zählt:
<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>
Habe ich es von TextView
zu der WebView
Lage sein , den Text innerhalb rechtfertigen. Was ich erreichen möchte, ist die Sache "Akzeptieren" wird erst aktiviert, wenn die Vertragsbedingungen vollständig gelesen sind. Meine erweiterte Klasse heißt ScrollViewEx
t. Wenn ich das Tag ScrollView
dafür ändere ScrollViewExt
, wirft es ein
android.view.InflateException: Binary XML file line #44: Error inflating class ScrollViewExt
weil es das Tag nicht versteht ScrollViewEx
. Ich glaube nicht, dass es eine Lösung gibt ...
Danke für deine Antworten!
quelle
canScrollVertically(int)
Antworten:
Erledigt!
Abgesehen von dem Fix, den Alexandre mir freundlicherweise zur Verfügung stellte, musste ich ein Interface erstellen:
public interface ScrollViewListener { void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy); }
Dann musste ich die OnScrollChanged-Methode von ScrollView in meinem ScrollViewExt überschreiben:
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); } } }
Fügen Sie nun, wie Alexandre sagte, den Paketnamen in das XML-Tag ein (mein Fehler), lassen Sie meine Activity-Klasse die zuvor erstellte Schnittstelle implementieren und fügen Sie dann alles zusammen:
scroll = (ScrollViewExt) findViewById(R.id.scrollView1); scroll.setScrollViewListener(this);
Und in der Methode OnScrollChanged von der Schnittstelle ...
@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 } }
Und es hat funktioniert!
Vielen Dank für Ihre Hilfe, Alexandre!
quelle
scrollView
es nur ein Kind gibtview = scrollView.getChildAt(0)
undscrollView.getScrollY()==y
in Ihrer Funktion, könnte die Funktion oben damit geändert werden, anstatt auf redundante Daten zuzugreifen.Ich habe einen einfachen Weg gefunden, dies zu erkennen:
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 } } });
quelle
Alle diese Antworten sind so kompliziert, aber es gibt eine einfache integrierte Methode, die dies erreicht:
canScrollVertically(int)
Zum Beispiel:
@Override public void onScrollChanged() { if (!scrollView.canScrollVertically(1)) { // bottom of scroll view } if (!scrollView.canScrollVertically(-1)) { // top of scroll view } }
Dies funktioniert auch mit RecyclerView, ListView und jeder anderen Ansicht, da die Methode in implementiert ist
View
.Wenn Sie eine horizontale ScrollView haben, kann das gleiche mit erreicht werden
canScrollHorizontally(int)
quelle
!scrollView.canScrollHorizontally(1)
zu prüfen, ob es sich bereits am rechten und!scrollView.canScrollHorizontally(-1)
am linken Ende befindet.Die Antwort von Fustigador war großartig, aber ich fand, dass ein Gerät (wie das Samsung Galaxy Note V) nicht 0 erreichen kann und nach der Berechnung noch 2 Punkte übrig hat. Ich schlage vor, einen kleinen Puffer wie unten hinzuzufügen:
@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 } }
quelle
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 } } });
quelle
BEARBEITEN
Mit dem Inhalt Ihres XML kann ich sehen, dass Sie eine ScrollView verwenden. Wenn Sie Ihre benutzerdefinierte Ansicht verwenden möchten, müssen Sie com.your.packagename.ScrollViewExt schreiben, damit Sie sie in Ihrem Code verwenden können.
<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>
EDIT END
Könnten Sie den XML-Inhalt posten?
Ich denke, Sie könnten einfach einen Scroll-Listener hinzufügen und prüfen, ob das zuletzt angezeigte Element das letzte in der Listenansicht ist, wie:
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 } } });
quelle
Sie können die Support-Bibliothek
NestedScrollView
und ihreNestedScrollView.OnScrollChangeListener
Benutzeroberfläche verwenden.https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html
Wenn Ihre App auf API 23 oder höher abzielt, können Sie alternativ die folgende Methode verwenden
ScrollView
:View.setOnScrollChangeListener(OnScrollChangeListener listener)
Folgen Sie dann dem Beispiel, das @Fustigador in seiner Antwort beschrieben hat. Beachten Sie jedoch, dass Sie, wie bei @Will beschrieben, in Betracht ziehen sollten, einen kleinen Puffer hinzuzufügen, falls der Benutzer oder das System aus irgendeinem Grund nicht in der Lage ist, das vollständige Ende der Liste zu erreichen.
Erwähnenswert ist auch, dass der Listener für Bildlaufänderungen manchmal mit negativen Werten oder Werten aufgerufen wird, die größer als die Ansichtshöhe sind. Vermutlich repräsentieren diese Werte den "Impuls" der Bildlaufaktion. Wenn sie jedoch nicht richtig gehandhabt werden (Boden / Bauch), können sie Probleme beim Erkennen der Bildlaufrichtung verursachen, wenn die Ansicht zum oberen oder unteren Rand des Bereichs gescrollt wird.
https://developer.android.com/reference/android/view/View.html#setOnScrollChangeListener(android.view.View.OnScrollChangeListener)
quelle
Wir sollten immer hinzufügen
scrollView.getPaddingBottom()
, um der vollen Höhe der Bildlaufansicht zu entsprechen, da die Bildlaufansicht manchmal eine Auffüllung in der XML-Datei aufweist, sodass dieser Fall nicht funktioniert.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 } } } });
quelle
Um festzustellen, ob Sie sich am Ende Ihrer benutzerdefinierten
ScrollView
Position befinden, können Sie auch eine Elementvariable verwenden und die letzte y-Position speichern. Dann können Sie die letzte y-Position mit der aktuellen Bildlaufposition vergleichen.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; }
quelle
Die meisten Antworten funktionieren neben der Tatsache, dass der Listener mehrmals ausgelöst wird, wenn Sie nach unten scrollen , was in meinem Fall unerwünscht ist . Um dieses Verhalten zu vermeiden, habe ich das Flag scrollPositionChanged hinzugefügt , das prüft, ob sich die Bildlaufposition überhaupt geändert hat, bevor die Methode erneut aufgerufen wird.
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; } }
Dann einfach den Listener einstellen
scrollView.setScrollEndingListener(new EndDetectingScrollView.ScrollEndingListener() { @Override public void onScrolledToEnd() { //do your stuff here } });
Sie können das Gleiche tun, wenn Sie es mögen
scrollView.getViewTreeObserver().addOnScrollChangedListener(...)
Sie müssen jedoch ein Flag aus der Klasse angeben, um diesen Listener hinzuzufügen.
quelle
Ich wollte ein FAB mit einem Versatz ganz unten in der Bildlaufansicht ein- / ausblenden. Dies ist die Lösung, die ich mir ausgedacht habe (Kotlin):
scrollview.viewTreeObserver.addOnScrollChangedListener { if (scrollview.scrollY < scrollview.getChildAt(0).bottom - scrollview.height - offset) { // fab.hide() } else { // fab.show() } }
quelle
Ich habe die Lösungen im Internet durchgesehen. In dem Projekt, an dem ich arbeite, haben die meisten Lösungen nicht funktioniert. Die folgenden Lösungen funktionieren gut für mich.
Verwenden von onScrollChangeListener (funktioniert mit API 23):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() { @Override public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { int bottom = (scrollView.getChildAt(scrollView.getChildCount() - 1)).getHeight()-scrollView.getHeight()-scrollY; if(scrollY==0){ //top detected } if(bottom==0){ //bottom detected } } }); }
Verwenden von scrollChangeListener auf TreeObserver
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { int bottom = (scrollView.getChildAt(scrollView.getChildCount() - 1)).getHeight()-scrollView.getHeight()-scrollView.getScrollY(); if(scrollView.getScrollY()==0){ //top detected } if(bottom==0) { //bottom detected } } });
Hoffe diese Lösung hilft :)
quelle