RecyclerView: how to simulate DrawView ListView selector on top?

I would like to create a RecyclerView that draws a selector on top of its elements. It should be displayed on top of the elements, which means that I cannot just set StateListDrawable as the background of the element.

I am particularly interested in the pressed state, i.e. something needs to be drawn if (and only if) an element is clicked.

RecyclerView.ItemDecoration is capable of handling RecyclerView elements. Here is what I have tried so far:

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

The problem is that RecyclerView does not seem to redraw the decoration of the element if the one-time state of one of its child elements changes. So how can I do this?

I tried adding RecyclerView.OnItemTouchListener and calling recyclerView.invalidate() from my onInterceptTouchEvent() method, but that didn't work.

+5
source share
3 answers

If you can use FrameLayout as root and your selector is translucent, the location below may be useful.

android:foreground , not 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> 
+5
source

I found a solution that seems to work. I had to subclass RecyclerView to invalidate the view and force the elements to draw again.

 public class ChildDrawableStateRecyclerView extends RecyclerView { /* constructors omitted */ @Override public void childDrawableStateChanged(View child) { super.childDrawableStateChanged(child); // force ItemDecorations to be drawn invalidate(); } } 

I have no idea if this is the right thing to do. Please give better answers if you have any.

I will also need to check if the ripple effect can be realized this way.

+2
source

As a result, I used a completely different approach. Instead of implementing RecyclerView.ItemDecoration I wrote a subclass of RelativeLayout that can draw a foreground selector. I use this layout as a container for all list items that need a selector at the top.

This approach also works well with rows.

 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); } } 
+2
source

Source: https://habr.com/ru/post/1206028/


All Articles