5

Vorrei creare uno RecyclerView che disegna un selettore sopra i suoi elementi. Dovrebbe essere visualizzato sopra gli articoli, il che significa che non posso semplicemente impostare uno StateListDrawable come sfondo dell'elemento.RecyclerView: come simulare il selettore di disegno di ListView in cima?

Sono particolarmente interessato allo stato premuto, vale a dire qualcosa che deve essere disegnato se (e solo se) viene premuto un elemento.

RecyclerView.ItemDecoration è in grado di disegnare su articoli di RecyclerView. Ecco quello che ho provato finora:

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(); 
    } 
} 

Il problema è che RecyclerView non sembra ridisegnare la decorazione oggetto se lo stato drawable di uno dei suoi figli cambiamenti. Quindi, come faccio a farlo?

Ho provato ad aggiungere uno RecyclerView.OnItemTouchListener e chiamare recyclerView.invalidate() dal suo metodo onInterceptTouchEvent() ma quello non ha funzionato.

risposta

2

Ho trovato una soluzione che sembra funzionare. Ho dovuto sottoclasse lo RecyclerView per invalidare la vista e forzare nuovamente le decorazioni degli oggetti.

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

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

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

Non ho idea se questo è il modo giusto per farlo. Si prega di fornire risposte migliori se ne avete.

Devo anche verificare se gli effetti a catena possono essere implementati in questo modo.

2

Ho finito per utilizzare un approccio completamente diverso. Invece di implementare un RecyclerView.ItemDecoration ho scritto una sottoclasse RelativeLayout che può disegnare un selettore in primo piano. Io uso questo layout come contenitore per tutti gli elementi della lista che hanno bisogno di un selettore in cima.

Anche questo approccio sembra funzionare bene con i ripple drawable.

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

Se è possibile utilizzare FrameLayout come radice e il selettore è semitrasparente, sotto cabine possono utile.

android:foreground, non 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

lavorato per me .. –

Problemi correlati