5

Ich möchte eine RecyclerView erstellen, die einen Selektor über seine Elemente zeichnet. Es sollte auf den Items gerendert werden, das bedeutet, ich kann nicht einfach einen StateListDrawable als Item-Hintergrund setzen.RecyclerView: Wie simuliert man den Draw Selector von ListView oben?

Ich bin besonders interessiert an der gedrückten Zustand, d. H. Etwas sollte gezeichnet werden, wenn (und nur wenn) ein Element gedrückt wird.

RecyclerView.ItemDecoration kann Objekte einer RecyclerView überzeichnen. Hier ist, was ich versucht habe, so weit:

public final class ItemPressedDecoration extends RecyclerView.ItemDecoration { 
    private final Rect rect = new Rect(); 

    @Override 
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 
     final int count = parent.getChildCount(); 
     for (int index = 0; index < count; index++) { 
      final View child = parent.getChildAt(index); 
      if (child.isPressed()) { 
       drawOverlay(c, child); 
      } 
     } 
    } 

    private void drawOverlay(Canvas c, View child) { 
     c.save(); 
     rect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); 
     c.clipRect(rect); 
     c.drawColor(0x80ff0000); 
     c.restore(); 
    } 
} 

Das Problem ist, dass RecyclerView das Element nicht Dekoration, wenn der ziehbar Zustand eines seiner Kinder Veränderungen scheint neu zu zeichnen. Also, wie bekomme ich das hin?

Ich habe versucht, eine RecyclerView.OnItemTouchListener hinzufügen und recyclerView.invalidate() von seiner onInterceptTouchEvent() Methode aufrufen, aber das hat nicht funktioniert.

Antwort

2

Ich habe eine Lösung gefunden, die zu funktionieren scheint. Ich musste die Klasse RecyclerView ableiten, um die Ansicht ungültig zu machen und die Objektdekorationen erneut zu zeichnen.

public class ChildDrawableStateRecyclerView extends RecyclerView { 
    /* constructors omitted */ 

    @Override 
    public void childDrawableStateChanged(View child) { 
     super.childDrawableStateChanged(child); 

     // force ItemDecorations to be drawn 
     invalidate(); 
    } 
} 

Ich habe keine Ahnung, ob dies der richtige Weg ist. Bitte geben Sie bessere Antworten, wenn Sie welche haben.

Ich muss auch prüfen, ob Ripple-Effekte auf diese Weise implementiert werden können.

2

Ich endete mit einem völlig anderen Ansatz. Anstatt eine RecyclerView.ItemDecoration zu implementieren, schrieb ich eine Unterklasse, die einen Selektor im Vordergrund zeichnen kann. Ich verwende dieses Layout als Container für alle Listenelemente, die oben einen Selektor benötigen.

Dieser Ansatz scheint auch mit Ripple Drawables gut zu funktionieren.

public class SelectorRelativeLayout extends RelativeLayout { 
    public static final int[] ATTRS_LIST_SELECTOR = { android.R.attr.listSelector }; 

    private final Drawable selector; 

    public SelectorRelativeLayout(Context context) { 
     this(context, null); 
    } 

    public SelectorRelativeLayout(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
    } 

    public SelectorRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 

     TypedArray a = context.obtainStyledAttributes(attrs, ATTRS_LIST_SELECTOR, 0, 0); 
     selector = a.getDrawable(0); 
     a.recycle(); 

     if (selector != null) { 
      setWillNotDraw(false); 
      selector.setCallback(this); 
     } 
    } 

    @Override 
    protected void drawableStateChanged() { 
     super.drawableStateChanged(); 

     final Drawable d = selector; 
     if (d != null && d.isStateful()) { 
      d.setState(getDrawableState()); 
     } 
    } 

    @Override 
    public void jumpDrawablesToCurrentState() { 
     super.jumpDrawablesToCurrentState(); 

     final Drawable d = selector; 
     if (d != null) { 
      d.jumpToCurrentState(); 
     } 
    } 

    @Override 
    @TargetApi(Build.VERSION_CODES.LOLLIPOP) 
    public void drawableHotspotChanged(float x, float y) { 
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 
      return; 
     } 

     super.drawableHotspotChanged(x, y); 

     final Drawable d = selector; 
     if (d != null) { 
      d.setHotspot(x, y); 
     } 
    } 

    @Override 
    public void draw(Canvas canvas) { 
     super.draw(canvas); 

     final Drawable d = selector; 
     if (d != null) { 
      d.setBounds(0, 0, getWidth(), getHeight()); 

      d.draw(canvas); 
     } 
    } 

    @Override 
    protected boolean verifyDrawable(Drawable who) { 
     return who == selector || super.verifyDrawable(who); 
    } 
} 
5

Wenn Sie FrameLayout als root verwenden können und Ihre Wähler ist halbtransparent, unter Layout kann nützlich.

android:foreground, nicht android:background

Layout/view_listitem.xml

<FrameLayout 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:foreground="@drawable/your_selector"> 

    <!-- your list items --> 

</FrameLayout> 
+0

für mich gearbeitet .. –