回收器视图:如何在顶部模拟列表视图的绘制选择器?



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

我对按下状态特别感兴趣,即当(且仅当)按下一个项目时,应该画出一些东西。

CCD_ 3能够绘制CCD_ 4的项目。以下是我迄今为止所尝试的:

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并从其onInterceptTouchEvent()方法调用recyclerView.invalidate(),但没有成功。

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

android:foreground,而不是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>

我找到了一个似乎有效的解决方案。为了使视图无效并强制重新绘制项目装饰,我不得不对RecyclerView进行子类化。

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

我不知道这样做是否正确。如果你有更好的答案,请提供。

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

我最终使用了一种完全不同的方法。我没有实现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);
    }
}

最新更新