Android ListView – Stoppen Sie das Blättern in der 'ganzen' Zeilenposition

Tut mir leid für den verwirrenden Titel, ich kann das Problem nicht ganz prägnant ausdrücken …

Ich habe eine Android-App mit einem ListView, die einen kreisförmigen / "unendlichen" Adapter verwendet, was im Grunde bedeutet, dass ich es nach oben oder unten scrollen kann, so viel wie ich will und die Elemente werden umwickeln, wenn es die Spitze oder den Boden erreicht, so dass es scheinen An den Benutzer, als ob er eine unendlich lange Liste von (~ 100) wiederholenden Gegenständen dreht.

  • Android Action Bar wie twitter Probe
  • Senden Sie SMS über PhoneGap auf Android
  • Taille DrawerLayout mit nur Titel (kein App Icon)?
  • ExpandableListView in Fragment Issue
  • Entfernen Sie alle Debug-Logging-Anrufe vor der Veröffentlichung: Gibt es Werkzeuge, um dies zu tun?
  • Android-Volley-Zugang http Antwort-Header-Felder
  • Der Punkt dieses Setups ist, dass der Benutzer ein zufälliges Element auswählen kann, einfach durch Spinnen / Flinging der Listenansicht und warten, um zu sehen, wo es aufhört. Ich habe die Reibung der Listview verringert, damit es ein bisschen schneller und länger ist und das scheint sehr schön zu arbeiten. Schließlich habe ich ein teilweise transparentes Bild auf die ListView gelegt, um die oberen und unteren Elemente zu blockieren (mit einem Übergang von transparent nach schwarz), so dass es so aussieht, als ob der Benutzer das Element in der Mitte "auswählt", als ob sie Waren auf einem rotierenden "Rad", das sie kontrollieren, indem sie fliegen.

    Es gibt ein offensichtliches Problem: Nach dem Aufblättern startet das ListView nicht an einem bestimmten Item, aber es kann aufhören, zwischen zwei Items zu schweben (wo das erste sichtbare Element dann nur teilweise angezeigt wird). Ich möchte das vermeiden, denn in diesem Fall ist es nicht offensichtlich, welches Item "zufällig ausgewählt" wurde.

    Lange Geschichte kurz: Nachdem die ListView nach dem Blättern mit dem Scrollen fertig ist, möchte ich, dass sie auf einer "ganzen" Zeile anstatt auf einer teilweise sichtbaren Zeile aufhört.

    Im Moment habe ich dieses Verhalten implementiert, indem ich überprüfe, wann das Scrollen gestoppt hat, und dann das erste sichtbare Element als solches auswählen:

    lv = this.getListView(); lv.setFriction(0.005f); lv.setOnScrollListener(new OnScrollListener() { public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {} public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { if (isAutoScrolling) return; isAutoScrolling = true; int pos = lv.getFirstVisiblePosition(); lv.setSelection(pos); isAutoScrolling = false; } } }); 

    Das funktioniert einigermaßen gut, abgesehen von einem augenfälligen Problem … Das erste sichtbare Element könnte nur für ein Pixel oder zwei sichtbar sein. In diesem Fall möchte ich, dass das ListView für diese beiden Pixel "up" springt, so dass das zweite sichtbare Element ausgewählt ist. Stattdessen wird natürlich das erste sichtbare Element ausgewählt, was bedeutet, dass die ListView fast eine ganze Zeile hinunter springt (abzüglich dieser beiden Pixel).

    Kurz gesagt, anstatt auf den ersten sichtbaren Gegenstand zu springen, möchte ich, dass er auf das am meisten sichtbare Element springt. Wenn das erste sichtbare Element weniger als halb sichtbar ist, möchte ich es zum zweiten sichtbaren Gegenstand springen.

    Hier ist eine Illustration, die hoffentlich meinen Punkt vermittelt. Die linke ListView (von jedem Paar) zeigt den Zustand, nachdem das Flinging gestoppt hat (wo es zum Stillstand kommt), und das richtige ListView zeigt, wie es sich um den "Sprung" handelt, indem er das erste sichtbare Element auswählt. Auf der linken Seite zeige ich die aktuelle (falsche) Situation: Punkt B ist nur noch sichtbar, aber es ist immer noch der erste sichtbare Gegenstand, so dass die listView springt, um diesen Eintrag auszuwählen – was nicht logisch ist, weil er fast eine ganze Positionshöhe scrollen muss um dorthin zu kommen. Es wäre viel logischer, zu Item C zu blättern (was auf der rechten Seite dargestellt ist), weil das "näher" ist.

    Bild http://nickthissen.nl/Images/lv.jpg

    Wie kann ich dieses Verhalten erreichen? Der einzige Weg, den ich mir vorstellen kann, ist, irgendwie zu messen, wie viel von dem ersten sichtbaren Gegenstand sichtbar ist. Wenn das mehr als 50% ist, dann springe ich zu dieser Position. Wenn es weniger als 50% ist, springe ich zu dieser Position + 1. Allerdings habe ich keine Ahnung, wie man das mißt …

    Irgendwelche Ideen?

  • Kopiere Rohdatei in SDCard?
  • Fügen Sie benutzerdefiniertes Layout zu Symbolleiste hinzu
  • Was sind die zehn tödlichsten Berechtigungen?
  • Android ListView: Hebt nicht auf Berührung, sondern funktioniert, wenn ich den Trackball verwende
  • AsyncTask, der von Handler angerufen wird, führt DoInBackground nicht aus
  • Drehen einer Kamera SurfaceView zum Portrait
  • 7 Solutions collect form web for “Android ListView – Stoppen Sie das Blättern in der 'ganzen' Zeilenposition”

    Sie können die sichtbaren Dimensionen eines Kindes mit der Methode getChildVisibleRect . Wenn du das hast und du die Gesamthöhe des Kindes bekommst, kannst du zum passenden Kind blättern.

    Im folgenden Beispiel überprüfe ich, ob mindestens die Hälfte des Kindes sichtbar ist:

     View child = lv.getChildAt (0); // first visible child Rect r = new Rect (0, 0, child.getWidth(), child.getHeight()); // set this initially, as required by the docs double height = child.getHeight () * 1.0; lv.getChildVisibleRect (child, r, null); if (Math.abs (r.height ()) < height / 2.0) { // show next child } else { // show this child } 

    Folgen Sie diesen 3 Schritten, dann können Sie genau das bekommen, was Sie wollen !!!!

    1.Initialisieren Sie die beiden Variablen für das Scrollen nach oben und unten:

     int scrollingUp=0,scrollingDown=0; 

    2.Schneiden Sie den Wert der Variablen basierend auf dem Scrollen:

     @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if(mLastFirstVisibleItem<firstVisibleItem) { scrollingDown=1; } if(mLastFirstVisibleItem>firstVisibleItem) { scrollingUp=1; } mLastFirstVisibleItem=firstVisibleItem; } 

    3.Schreiben Sie die Änderungen in der onScrollStateChanged ():

     @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case SCROLL_STATE_IDLE: if(scrollingUp==1) { mainListView.post(new Runnable() { public void run() { View child = mainListView.getChildAt (0); // first visible child Rect r = new Rect (0, 0, child.getWidth(), child.getHeight()); // set this initially, as required by the docs double height = child.getHeight () * 1.0; mainListView.getChildVisibleRect (child, r, null); int dpDistance=Math.abs (r.height()); double minusDistance=dpDistance-height; if (Math.abs (r.height()) < height/2) { mainListView.smoothScrollBy(dpDistance, 1500); } else { mainListView.smoothScrollBy((int)minusDistance, 1500); } scrollingUp=0; } }); } if(scrollingDown==1) { mainListView.post(new Runnable() { public void run() { View child = mainListView.getChildAt (0); // first visible child Rect r = new Rect (0, 0, child.getWidth(), child.getHeight()); // set this initially, as required by the docs double height = child.getHeight () * 1.0; mainListView.getChildVisibleRect (child, r, null); int dpDistance=Math.abs (r.height()); double minusDistance=dpDistance-height; if (Math.abs (r.height()) < height/2) { mainListView.smoothScrollBy(dpDistance, 1500); } else { mainListView.smoothScrollBy((int)minusDistance, 1500); } scrollingDown=0; } }); } break; case SCROLL_STATE_TOUCH_SCROLL: break; } } 

    Hier ist mein letzter Code von Shades Antwort inspiriert.

    Ich habe vergessen, "if(Math.abs(r.height())!=height)" anfangs "if(Math.abs(r.height())!=height)" hinzuzufügen. Dann blättert es nur zweimal, nachdem es zur korrekten Position scrollen kann, weil es immer größer als die Höhe / 2 von childView ist. Ich hoffe es hilft.

     listView.setOnScrollListener(new AbsListView.OnScrollListener(){ @Override public void onScrollStateChanged(AbsListView view,int scrollState) { if (scrollState == SCROLL_STATE_IDLE){ View child = listView.getChildAt (0); // first visible child Rect r = new Rect (0, 0, child.getWidth(), child.getHeight()); // set this initially, as required by the docs double height = child.getHeight () * 1.0; listView.getChildVisibleRect (child, r, null); if(Math.abs(r.height())!=height){//only smooth scroll when not scroll to correct position if (Math.abs (r.height ()) < height / 2.0) { listView.smoothScrollToPosition(listView.getLastVisiblePosition()); } else if(Math.abs (r.height ()) > height / 2.0){ listView.smoothScrollToPosition(listView.getFirstVisiblePosition()); } else{ listView.smoothScrollToPosition(listView.getFirstVisiblePosition()); } } } } @Override public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) { }}); 

    Sobald Sie die erste sichtbare Position kennen, sollten Sie in der Lage sein, View.getLocationinWindow() oder View.getLocationOnScreen() in der Ansicht der nächsten Position zu verwenden, um die sichtbare Höhe des ersten zu erhalten . Vergleichen Sie das mit der Höhe der View und blättern Sie zur nächsten Position.

    Möglicherweise müssen Sie es zwicken, um die Auffüllung zu berücksichtigen, je nachdem, wie Ihre Zeilen aussehen.


    Ich habe das oben nicht ausprobiert, aber es scheint, als ob es funktionieren sollte. Wenn es nicht geht, hier ist eine andere, wohl weniger robuste Idee:

    getLastVisiblePosition() . Wenn Sie den Unterschied zwischen dem last und dem first , können Sie sehen, wie viele Positionen auf dem Bildschirm sichtbar sind. Vergleichen Sie das, wie viele Positionen sichtbar waren, als die Liste zuerst belegt wurde (Bildlaufposition 0).

    Wenn die gleiche Anzahl von Positionen sichtbar ist, blättern Sie einfach zu der ersten sichtbaren Position, wie Sie es tun. Wenn noch ein sichtbarer ist, blättern Sie zu "first + 1" Position.

    Wenn Sie die Position der Zeile erhalten können, die gescrollt werden soll, können Sie die Methode verwenden:

    SmoothScrollToPosition

    So etwas wie:

     int pos = lv.getFirstVisiblePosition(); lv.smoothScrollToPosition(pos); 

    Bearbeiten
    Versuchen Sie dies, sorry ich habe keine Zeit zu testen, ich bin unterwegs.

     ImageView iv = //Code to find the image view Rect rect = new Rect(iv.getLeft(), iv.getTop(), iv.getRight(), iv.getBottom()); lv.requestChildRectangleOnScreen(lv, rect, false); 

    Sie haben wahrscheinlich dieses Problem gelöst, aber ich denke, dass diese Lösung funktionieren sollte

     if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { View firstChild = lv.getChildAt(0); int pos = lv.getFirstVisiblePosition(); //if first visible item is higher than the half of its height if (-firstChild.getTop() > firstChild.getHeight()/2) { pos++; } lv.setSelection(pos); } 

    getTop() für die erste Artikelansicht immer wieder positiven Wert, so dass ich nicht verwenden Math.abs(firstChild.getTop()) aber nur -firstChild.getTop() . Auch wenn dieser Wert> 0 ist, dann funktioniert diese Bedingung noch.

    Wenn du das glatter machen möchtest, dann kannst du versuchen, lv.smoothScrollToPosition(pos) zu benutzen und alle oben genannten Code einzuschließen

     if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { post(new Runnable() { @Override public void run() { //put above code here //and change lv.setSelection(pos) to lv.smoothScrollToPosition(pos) } }); } 

    Meine Lösung:

     @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (swipeLayout.isRefreshing()) { swipeLayout.setRefreshing(false); } else { int pos = firstVisibleItem; if (pos == 0 && lv_post_list.getAdapter().getCount()>0) { int topOfNext = lv_post_list.getChildAt(pos + 1).getTop(); int heightOfFirst = lv_post_list.getChildAt(pos).getHeight(); if (topOfNext > heightOfFirst) { swipeLayout.setEnabled(true); } else { swipeLayout.setEnabled(false); } } else swipeLayout.setEnabled(false); } } 
    Das Android ist ein Google Android Fan-Website, Alles ├╝ber Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.