我想创建一个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);
}
}