18

EDIT: Das eigentliche Problem war, dass meine LinearLayout wurde in ein anderes Layout gewickelt, die das falsche Verhalten verursacht. Die akzeptierte Antwort von Sanvywell hat ein besseres, vollständigeres Beispiel dafür, wie man eine Farbe unter Durchsicht zeichnet, als das Code-Snippet, das ich in der Frage bereitgestellt habe.Hinzufügen eines farbigen Hintergrunds mit Text/Symbol unter Swiped-Zeile bei der Verwendung von Android RecyclerView

Jetzt, wo RecyclerView Widget native Unterstützung für Zeile hat mit Hilfe von ItemTouchHelper Klasse klauen, bin ich versucht, es in einer App zu verwenden, in denen Zeilen in ähnlicher Weise auf die Google-App Posteingang verhalten wird. Das bedeutet, dass Wischen nach links eine Aktion ausführt und Wischen nach rechts eine andere ausführt.

Die Implementierung der Aktionen selbst war einfach mit ItemTouchHelper.SimpleCallback 's onSwiped Methode. Ich konnte jedoch keine einfache Methode zum Festlegen von Farbe und Symbol unter der Ansicht finden, die gerade übertragen wird (wie in der Google Mail-App).

, das zu tun, versuche ich ItemTouchHelper.SimpleCallback ‚s onChildDraw Methode wie folgt außer Kraft zu setzen:

@Override 
public void onChildDraw(Canvas c, RecyclerView recyclerView, 
         RecyclerView.ViewHolder viewHolder, float dX, float dY, 
         int actionState, boolean isCurrentlyActive) { 
    RecyclerViewAdapter.ViewHolder vh = (RecyclerViewAdapter.ViewHolder) viewHolder; 
    LinearLayout ll = vh.linearLayout; 

    Paint p = new Paint(); 
    if(dX > 0) { 
     p.setARGB(255, 255, 0, 0); 
    } else { 
     p.setARGB(255, 0, 255, 0); 
    } 

    c.drawRect(ll.getLeft(), ll.getTop(), ll.getRight(), ll.getBottom(), p); 

    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); 
} 

die Wischrichtung von dX ermitteln und die entsprechenden Farbwerkseinstellung als gedacht, aber die Koordinaten ich von die ViewHolder entsprechen immer der Stelle, an der die erste LinearLayout aufgeblasen wurde.

Wie bekomme ich die richtigen Koordinaten für die LinearLayout, die in der aktuell ausgewählten Zeile ist? Gibt es einen einfacheren Weg (um onChildDraw nicht zu überschreiben), um die Hintergrundfarbe und das Symbol zu setzen?

Antwort

37

Ich hatte Schwierigkeiten, dieses Feature zu implementieren, aber Sie haben mich in die richtige Richtung gelenkt.

+5

sollten Sie vermeiden, im Allgemeinen Objekte in OnDraw Methoden instanziieren. Dies kann sich auf die Leistung auswirken, da OnDraw-Methoden oft aufgerufen werden. Sie sollten das Paint-Objekt wahrscheinlich zwischenspeichern, anstatt es jedes Mal neu zu erstellen. Aus der Dokumentation: "Das Erstellen von Objekten im Voraus ist eine wichtige Optimierung. Ansichten werden sehr häufig neu gezeichnet, und viele Zeichnungsobjekte erfordern eine aufwendige Initialisierung. Das Erstellen von Zeichnungsobjekten in Ihrer onDraw() -Methode reduziert die Leistung erheblich und kann dazu führen, dass Ihre Benutzeroberfläche träge erscheint." http://developer.android.com/training/custom-views/custom-drawing.html –

+0

Der Code für dX> 0 muss den Wert "getLeft()" zu dX hinzufügen, um das Auffüllen des RecyclerView c.drawRect (((float) itemView.getLeft(), (Gleitkomma) itemView.getTop(), itemView.getLeft() + dX, (Gleitkomma) itemView.getBottom(), p); –

+0

@Sanvywell Wäre es nicht eine gute Idee, super.onChildDraw zuerst zu nennen? –

21

Die angenommene Antwort macht eine gute Arbeit, den Hintergrund zu färben, aber adressierte das Zeichnen des Symbols nicht.

Dies funktionierte für mich, weil es sowohl die Hintergrundfarbe eingestellt und das Symbol gezeichnet hat, ohne dass das Symbol während des Streichen gedehnt wurde, oder nach dem Streichen eine Lücke zwischen den vorherigen und nächsten Elementen zu lassen.

public static final float ALPHA_FULL = 1.0f; 

public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { 
    if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { 
     // Get RecyclerView item from the ViewHolder 
     View itemView = viewHolder.itemView; 

     Paint p = new Paint(); 
     Bitmap icon; 

     if (dX > 0) { 
      /* Note, ApplicationManager is a helper class I created 
       myself to get a context outside an Activity class - 
       feel free to use your own method */ 

      icon = BitmapFactory.decodeResource(
        ApplicationManager.getContext().getResources(), R.drawable.myleftdrawable); 

      /* Set your color for positive displacement */ 
      p.setARGB(255, 255, 0, 0); 

      // Draw Rect with varying right side, equal to displacement dX 
      c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX, 
        (float) itemView.getBottom(), p); 

      // Set the image icon for Right swipe 
      c.drawBitmap(icon, 
        (float) itemView.getLeft() + convertDpToPx(16), 
        (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - icon.getHeight())/2, 
        p); 
     } else { 
      icon = BitmapFactory.decodeResource(
        ApplicationManager.getContext().getResources(), R.drawable.myrightdrawable); 

      /* Set your color for negative displacement */ 
      p.setARGB(255, 0, 255, 0); 

      // Draw Rect with varying left side, equal to the item's right side 
      // plus negative displacement dX 
      c.drawRect((float) itemView.getRight() + dX, (float) itemView.getTop(), 
        (float) itemView.getRight(), (float) itemView.getBottom(), p); 

      //Set the image icon for Left swipe 
      c.drawBitmap(icon, 
        (float) itemView.getRight() - convertDpToPx(16) - icon.getWidth(), 
        (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - icon.getHeight())/2, 
        p); 
     } 

     // Fade out the view as it is swiped out of the parent's bounds 
     final float alpha = ALPHA_FULL - Math.abs(dX)/(float) viewHolder.itemView.getWidth(); 
     viewHolder.itemView.setAlpha(alpha); 
     viewHolder.itemView.setTranslationX(dX); 

    } else { 
     super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); 
    } 
} 

private int convertDpToPx(int dp){ 
    return Math.round(dp * (getResources().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT)); 
} 
+1

wie man Klick Listener auf diese Symbole hinzufügen? Wie gmail app – penguin

+0

@penguin Es gibt eine Diskussion über nur dieses Thema hier http://StackOverflow.com/Questions/6845129/android-how-to-create-onclick-events-in-canvas-ondraw-method. Wenn Sie in der Lage sein müssen, mit den zugrunde liegenden Elementen zu interagieren, ist es wahrscheinlich besser, wenn Sie eine Ansicht unter dem Element mit dem Swiped erstellen. – HappyKatz

+0

Hallo, was ist hier der Wert Ihrer 'ALPHA_FULL' Variable? –

3

HappyKatz Lösung hat einen kniffligen Fehler. Gibt es einen Grund, Bitmap zu zeichnen, wenn dX == 0 ?? In einigen Fällen führt dies zu einer dauerhaften Symbolsichtbarkeit über dem Listenelement. Auch Symbole werden über dem Listenelement sichtbar, wenn Sie das Listenelement gerade berühren und dX == 1. Um dies zu beheben diese:

 if (dX > rectOffset) { 
      c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX, 
        (float) itemView.getBottom(), leftPaint); 
      if (dX > iconOffset) { 
       c.drawBitmap(leftBitmap, 
         (float) itemView.getLeft() + padding, 
         (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - leftBitmap.getHeight())/2, 
         leftPaint); 
      } 
     } else if (dX < -rectOffset) { 
      c.drawRect((float) itemView.getRight() + dX, (float) itemView.getTop(), 
        (float) itemView.getRight(), (float) itemView.getBottom(), rightPaint); 
      if (dX < -iconOffset) { 
       c.drawBitmap(rightBitmap, 
         (float) itemView.getRight() - padding - rightBitmap.getWidth(), 
         (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - rightBitmap.getHeight())/2, 
         rightPaint); 
      } 
     } 
+0

Wie kann ich Symbol mit Text zeichnen –

+0

@HRaval Haben Sie etwas über ein Symbol mit Text gefunden? – rookieDeveloper

+0

Ja, ich habe es mit drawText() -Methode von Canvas –

5

Ich bin nicht sicher, wie diese Lösungen (von @Sanvywell, @HappyKatz und @ user2410066) arbeiten für euch aber in meinem Fall musste ich eine weitere Prüfung in der onChildDraw Methode.

Sieht aus wie ItemTouchHelper hält ViewHolder s von entfernten Zeilen für den Fall, dass sie wiederhergestellt werden müssen. Es ruft auch onChildDraw für diese VHs zusätzlich zu dem VH, der geklaut wird. Ich bin mir nicht sicher über die Auswirkungen dieses Verhaltens auf die Speicherverwaltung, aber ich brauchte eine zusätzliche Überprüfung am Anfang von onChildDraw, um zu vermeiden, dass ich für "Fantom" -Zeilen zeichne.

if (viewHolder.getAdapterPosition() == -1) { 
    return; 
} 

BONUS TEIL:

Ich wollte auch in ihre neuen Positionen animieren Zeichnung als andere Reihen fortzusetzen, nachdem eine Reihe Swipe gelöscht ist, und ich konnte es nicht innerhalb ItemTouchHelper und onChildDraw tun. Am Ende musste ich noch einen anderen Dekorateur dazu hinzufügen. Es geht in diese Zeilen:

UPDATE: Ich schrieb einen Blog-Post auf Recycler Ansicht wischen, um Feature zu löschen. Jemand könnte es nützlich finden. Keine 3rd Party Lib notwendig.

blog post git repo

+1

Nice blog post. Es klappt! –

+1

Ich habe verschiedene Methoden dafür ausprobiert, und Ihre (im Blog) hat am besten funktioniert und schien auch die einfachste zu sein! – Aidin