Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/wix/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何在RecyclerView项目装饰中在画布上绘制CardView阴影?_Android_Android Recyclerview - Fatal编程技术网

Android 如何在RecyclerView项目装饰中在画布上绘制CardView阴影?

Android 如何在RecyclerView项目装饰中在画布上绘制CardView阴影?,android,android-recyclerview,Android,Android Recyclerview,我最近在使用具有多种视图类型的RecyclerView和PagedListAdapter时遇到了各种问题。不同视图类型的原因本质上是为了添加节标题。切换到使用物品装饰似乎更稳定和更好的模式 所以我想我应该对另一个RecyclerView做同样的事情,尝试消除多个视图类型,这样RecyclerView中的每一行都对应于基础PagedList中的一个项目。问题是,这次它不是一个简单的节头文本视图。这是一张卡片 我在获得正确的宽度时遇到了一点困难(这个CardView是用来匹配父母的)。我想我知道了,

我最近在使用具有多种视图类型的RecyclerView和PagedListAdapter时遇到了各种问题。不同视图类型的原因本质上是为了添加节标题。切换到使用物品装饰似乎更稳定和更好的模式

所以我想我应该对另一个RecyclerView做同样的事情,尝试消除多个视图类型,这样RecyclerView中的每一行都对应于基础PagedList中的一个项目。问题是,这次它不是一个简单的节头文本视图。这是一张卡片

我在获得正确的宽度时遇到了一点困难(这个CardView是用来匹配父母的)。我想我知道了,但我还有一个问题。CardView正在绘制,但没有背景阴影。我从StackOverflow的问题中看到,其他人也有同样的问题。似乎没有使用常规布局/测量/绘制功能绘制立面阴影

如何在项目装饰中获得我的CardView阴影?有办法吗

这就是我目前拥有的:

class CardItemDecoration(val adapter: ReservationAdapter) : RecyclerView.ItemDecoration() {
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        super.getItemOffsets(outRect, view, parent, state)

        val position = parent.getChildAdapterPosition(view)

        if (adapter.hasCard && position == 0) {
            outRect.top = getcardView(parent.context, parent).measuredHeight
        }
    }

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)

        val left = parent.paddingLeft
        val right = parent.width - parent.paddingRight

        if (adapter.hascard && parent.childCount > 0) {
            val child = parent.getChildAt(0)

            val layout = getCardView(parent.context, parent)

            // Draw in the space made by getItemOffsets()
            layout.layout(left, 0, right, layout.measuredHeight)
            c.save()
            // Adjust Y coordinates, as they'll be different for each row
            val top = child.top - layout.measuredHeight
            c.translate(0f, top.toFloat())
            layout.draw(c)
            c.restore()
        }
    }

    private lateinit var cardView: ViewGroup

    private fun getCardView(context: Context, parent: RecyclerView): View {
        if (!::cardView.isInitialized) {
            cardView = LinearLayout(context)
            LayoutInflater.from(context).inflate(R.layout.call_out_bis_profile, cardView, true)

            cardView.apply {
                findViewById<TextView>(R.id.infoTextView).text = context.getString(R.string.card_description)

            }

            val width = parent.width - parent.paddingLeft - parent.paddingRight
            cardView.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
        }
        return cardView
    }
}
类CardItemDecoration(val适配器:ReservationAdapter):RecyclerView.ItemDecoration(){ 覆盖有趣的GetItemOffset(outRect:Rect,view:view,父对象:RecyclerView,状态:RecyclerView.state){ super.getItemOffset(outRect、view、parent、state) val position=parent.getChildAdapterPosition(视图) if(adapter.hasCard&&position==0){ outRect.top=getcardView(parent.context,parent).measuredHeight } } 重写onDraw(c:Canvas,父对象:RecyclerView,状态:RecyclerView.state){ super.onDraw(c、父级、状态) val left=parent.paddingLeft val right=parent.width-parent.paddingRight if(adapter.hascard&&parent.childCount>0){ val child=parent.getChildAt(0) val layout=getCardView(parent.context,parent) //在getItemOffsets()生成的空间中绘制 布局。布局(左、0、右、布局。测量高度) c、 保存() //调整Y坐标,因为每行的坐标都不同 val top=child.top-layout.measuredHeight c、 转换(0f,top.toFloat()) 布局图(c) c、 还原() } } 私有lateinit变量cardView:ViewGroup private fun getCardView(上下文:上下文,父:RecyclerView):视图{ 如果(!::cardView.i初始化){ cardView=LinearLayout(上下文) LayoutFlater.from(上下文)。充气(R.layout.call_out_bis_profile,cardView,true) cardwiew.apply{ findviewbyd(R.id.infoTextView).text=context.getString(R.string.card\u说明) } val width=parent.width-parent.paddingLeft-parent.paddingRight cardView.measure(视图.MeasureSpec.makeMeasureSpec(宽度,视图.MeasureSpec.精确), View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED)) } 返回卡视图 } }
这是一个卡视图装饰示例。这是我的参考:

Paint;
静态RoundRectHelper sRoundRectHelper;
油漆mCornerShadowPaint;
油漆,油漆,油漆;
最终RectF mPreShadowBounds;
漂浮麦考内拉迪乌斯;
路径mCornerShadowPath;
浮动mShadowSize;
私有布尔mDirty=true;
私人最终int mShadowStartColor;
私有最终int mShadowEndColor;
私人浮动支付;
公共CardViewDecoration(资源、int背景色、浮动半径){
mShadowStartColor=resources.getColor(R.color.cardwiew\u shadow\u start\u color);
mShadowEndColor=resources.getColor(R.color.cardwiew\u shadow\u end\u color);
mShadowSize=resources.getDimension(R.dimen.cardview\u shadow\u size)*shadow\u乘数;
mPaint=新油漆(油漆.防伪别名标志|油漆.抖动标志);
mPaint.setColor(背景色);
mCornerShadowPaint=新油漆(油漆.防伪别名标志|油漆.抖动标志);
mCornerShadowPaint.setStyle(Paint.Style.FILL);
mCornerShadowPaint.setDither(真);
mCornerRadius=半径;
mPreShadowBounds=new RectF();
mEdgeShadowPaint=新油漆(mCornerShadowPaint);
构建阴影角();
}
@凌驾
公共void onDrawOver(画布c、RecyclerView父级、RecyclerView.State){
Rect bounds=new Rect();
浮动edgeShadowTop=-mCornerRadius-mShadowSize;
RecycleView.LayoutManager lm=parent.getLayoutManager();
浮子尺寸16DP=16f;
int padding16dp=(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,size16dp,parent.getContext().getResources().getDisplayMetrics());
对于(int i=0;iPaint mPaint;

static RoundRectHelper sRoundRectHelper;

Paint mCornerShadowPaint;

Paint mEdgeShadowPaint;

final RectF mPreShadowBounds;

float mCornerRadius;

Path mCornerShadowPath;

float mShadowSize;

private boolean mDirty = true;

private final int mShadowStartColor;

private final int mShadowEndColor;
private float mPadding;


public CardViewDecoration(Resources resources, int backgroundColor, float radius) {
    mShadowStartColor = resources.getColor(R.color.cardview_shadow_start_color);
    mShadowEndColor = resources.getColor(R.color.cardview_shadow_end_color);
    mShadowSize = resources.getDimension(R.dimen.cardview_shadow_size) * SHADOW_MULTIPLIER;

    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    mPaint.setColor(backgroundColor);
    mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    mCornerShadowPaint.setStyle(Paint.Style.FILL);
    mCornerShadowPaint.setDither(true);
    mCornerRadius = radius;
    mPreShadowBounds = new RectF();
    mEdgeShadowPaint = new Paint(mCornerShadowPaint);

    buildShadowCorners();
}

@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    Rect bounds = new Rect();
    float edgeShadowTop = -mCornerRadius - mShadowSize;

    RecyclerView.LayoutManager lm = parent.getLayoutManager();
    float size16dp = 16f;
    int padding16dp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size16dp, parent.getContext().getResources().getDisplayMetrics());

    for (int i = 0; i < parent.getChildCount(); i++) {
        int save = c.save();

        // using decorated values, remove what we set before
        View child = parent.getChildAt(i);
        bounds.set(lm.getDecoratedLeft(child) + padding16dp - (int) mPadding,
                lm.getDecoratedTop(child),
                lm.getDecoratedRight(child) - padding16dp + (int) mPadding,
                lm.getDecoratedBottom(child));

        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
        int position = params.getViewAdapterPosition();
        int viewType = parent.getAdapter().getItemViewType(position);


        if (viewType == HeaderItemTestAdapter.HEADER) {
            bounds.top = (int) (bounds.top + padding16dp - mPadding);

            // LT
            c.translate(bounds.left + mCornerRadius, bounds.top + mCornerRadius);
            c.drawPath(mCornerShadowPath, mCornerShadowPaint);
            c.drawRect(0, edgeShadowTop, bounds.width() - 2 * mCornerRadius, -mCornerRadius, mEdgeShadowPaint);

            // RT
            c.rotate(90f);
            c.translate(0, -bounds.width() + 2 * mCornerRadius);
            c.drawPath(mCornerShadowPath, mCornerShadowPaint);
            c.drawRect(0, edgeShadowTop, bounds.height() - mCornerRadius, -mCornerRadius, mEdgeShadowPaint);

            // LBorder
            c.rotate(180f);
            c.translate(-bounds.height(), -bounds.width() + 2 * mCornerRadius);
            c.drawRect(mCornerRadius, edgeShadowTop, bounds.height(), -mCornerRadius, mEdgeShadowPaint);


        } else {
            if (parent.getAdapter().getItemViewType(position + 1) == HeaderItemTestAdapter.HEADER) {
                bounds.bottom = (int) (bounds.bottom - padding16dp + mPadding);

                // last item before next header
                c.rotate(180f);
                c.translate(-bounds.left - bounds.width() + mCornerRadius, -bounds.top - bounds.height() + mCornerRadius);

                c.drawPath(mCornerShadowPath, mCornerShadowPaint);
                c.drawRect(0, edgeShadowTop, bounds.width() - 2 * mCornerRadius, -mCornerRadius, mEdgeShadowPaint);

                // RT / Right border
                c.rotate(90f);
                c.translate(0, -bounds.width() + 2 * mCornerRadius);
                c.drawPath(mCornerShadowPath, mCornerShadowPaint);
                c.drawRect(0, edgeShadowTop, bounds.height() - mCornerRadius, -mCornerRadius, mEdgeShadowPaint);

                // Left border
                c.rotate(180f);
                c.translate(-bounds.height(), -bounds.width() + 2 * mCornerRadius);
                c.drawRect(mCornerRadius, edgeShadowTop, bounds.height(), -mCornerRadius, mEdgeShadowPaint);
            } else {
                // Right border
                c.translate(bounds.left, bounds.top);
                c.rotate(90f);
                c.translate(0, -bounds.width() + mCornerRadius);
                c.drawRect(0, edgeShadowTop, bounds.height(), -mCornerRadius, mEdgeShadowPaint);

                // Left border
                c.rotate(180f);
                c.translate(-bounds.height(), -bounds.width() + 2 * mCornerRadius);
                c.drawRect(0, edgeShadowTop, bounds.height(), -mCornerRadius, mEdgeShadowPaint);
            }
        }
        c.restoreToCount(save);
    }
}

private void buildShadowCorners() {

    mPadding = 0f;

    RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
    RectF outerBounds = new RectF(innerBounds);
    outerBounds.inset(-mShadowSize, -mShadowSize);

    if (mCornerShadowPath == null) {
        mCornerShadowPath = new Path();
    } else {
        mCornerShadowPath.reset();
    }
    mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
    mCornerShadowPath.moveTo(-mCornerRadius, 0);
    mCornerShadowPath.rLineTo(-mShadowSize, 0);
    // outer arc
    mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
    // inner arc
    mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
    mCornerShadowPath.close();

    float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
    mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize, new int[]{
            mShadowStartColor, mShadowStartColor, mShadowEndColor}, new float[]{0f, startRatio, 1f},
            Shader.TileMode.CLAMP));

    // we offset the content shadowSize/2 pixels up to make it more realistic.
    // this is why edge shadow shader has some extra space
    // When drawing bottom edge shadow, we use that extra space.
    mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0, -mCornerRadius - mShadowSize,
            new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor}, new float[]{0f, .5f, 1f},
            Shader.TileMode.CLAMP));
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
    Resources resources = parent.getContext().getResources();

    float size16dp = 16f;
    int padding16dp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size16dp, resources.getDisplayMetrics());

    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
    int position = params.getViewAdapterPosition();
    int viewType = parent.getAdapter().getItemViewType(position);

    if (viewType == HeaderItemTestAdapter.HEADER) {
        // header
        outRect.set(0, (int) (padding16dp), 0, 0);
    } else {
        if (parent.getAdapter().getItemViewType(position + 1) == HeaderItemTestAdapter.HEADER) {
            // last item before next header
            outRect.set(0, 0, 0, (int) (padding16dp));
        }
    }

    outRect.left = (int) padding16dp;
    outRect.right = (int) padding16dp;
}
}