RecyclerView:如何在顶部模拟ListView的绘图选择器?

时间:2014-11-01 21:56:34

标签: android android-support-library android-appcompat android-recyclerview

我想创建一个RecyclerView,在其项目之上绘制一个选择器。它应该呈现在项目之上,这意味着我不能简单地将StateListDrawable设置为项目背景。

我对压制状态特别感兴趣,即如果(并且仅当)按下某个项目,应该绘制一些东西。

RecyclerView.ItemDecoration能够绘制RecyclerView的项目。这是我到目前为止所尝试的:

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

问题是RecyclerView似乎没有重绘项目装饰,如果其中一个孩子的可绘制状态发生变化。那么如何让它做到呢?

我尝试添加RecyclerView.OnItemTouchListener并从recyclerView.invalidate()方法调用onInterceptTouchEvent(),但这不起作用。

3 个答案:

答案 0 :(得分:7)

如果您可以使用FrameLayout作为root用户,并且您的选择器是半透明的,则下面的布局可能会有用。

android:foreground,而不是android:background

布局/ view_listitem.xml

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

  <!-- your list items -->

</FrameLayout>

答案 1 :(得分:2)

我找到了一个似乎有用的解决方案。我必须继承RecyclerView以使视图无效并强制再次绘制项目装饰。

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

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

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

我不知道这是否是正确的方法。如果您有任何答案,请提供更好的答案。

我还必须检查是否可以通过这种方式实现涟漪效应。

答案 2 :(得分:2)

我最终使用了一种完全不同的方法。我没有实现RecyclerView.ItemDecoration,而是编写了一个可以在前台绘制选择器的RelativeLayout子类。我将此布局用作所有需要选择器的列表项的容器。

这种方法似乎也适用于波纹绘制。

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