如何防止自定义视图图形被视图边界裁剪?

dm7*_*m78 5 android android-custom-view

我正在尝试扩展ImageView并添加阴影。我遇到一个问题,其中阴影被视图边界裁剪,看起来很糟糕:

修剪的阴影

我尝试通过LayoutParams编程方式设置宽度/高度,并尝试使用其他XML属性(如)android:adjustViewBounds,但显示没有变化。同样,设置a android:layout_margin在防止阴影被修剪方面无效。

谁能帮助我找出如何避免这种剪裁?我确定这里有些明显的东西我可以俯瞰。

更新:

目前,我的代码针对一种情况非常具体:我正在尝试在圆位图下方绘制一个圆“阴影”。显然,视图边界会导致裁剪,但是我一直无法找到一种解决方案来扩大视图边界。

在#android-dev上声称我的数学根本是错误的。我正在考虑屏幕密度,这是一个常见问题。我已经对所有方面的数学知识进行了三重检查,但找不到可能错误的地方。

最初,在密度为3.0的xxhdpi屏幕上,56dp图像的宽度恰好为168px,高度为168px。在宽度和高度上添加2dp以解决偏移后,layoutParams的宽度为174,高度为174。

我的基本方法是让超级ImageView完成其工作并绘制xml中指定的位图,而我要做的就是另外绘制一些内容。这种方法有根本的缺陷吗?

我使用最大的宽度或高度onLayout来确定我的阴影圆的半径应为:radius = Max(width,height)/2。我绘制一个半径和中心为(Cx,Cy)的圆,其中Cx是宽度的中点加上x偏移,Cy是高度的中点加上y偏移以创建阴影效果。我使用画布onDraw在位图上绘制了另外一个圆,然后在允许ImageView超级onDraw控件处理源位图之前,将圆放置在画布上。

另外,在我中,onLayout我尝试考虑x和y偏移距离,并通过将这些距离添加到视图的宽度和高度中LayoutParams,但是在绘制视图时,没有发现视图大小的变化。

这是我正在使用的代码:https : //gitlab.com/dm78/ImageViewWithShadowExample/

以下是相关代码:

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context=".MainActivity">

    <dm78.example.imageviewwithshadowexample.CustomShadowImageView
        android:id="@+id/circle"
        android:layout_gravity="center"
        android:src="@drawable/circle"
        android:layout_margin="16dp"
        android:layout_width="56dp"
        android:layout_height="56dp"/>

</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

CustomShadowImageView.java

package dm78.example.imageviewwithshadowexample;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.widget.FrameLayout;
import android.widget.ImageView;

public class CustomShadowImageView extends ImageView {

    public static final String TAG = CustomShadowImageView.class.getSimpleName();
    public static final float SHADOW_RADIUS_DP = 3f;
    public static final float SHADOW_X_OFFSET_DP = 2f;
    public static final float SHADOW_Y_OFFSET_DP = 2f;

    private Paint mPaint;
    private float mShadowRadius;
    private float radius;
    private float cx;
    private float cy;
    private float mShadowXOffset;
    private float mShadowYOffset;
    private Bitmap mShadowBitmap;
    private FrameLayout.LayoutParams layoutParams;
    private boolean expanded;

    public CustomShadowImageView(Context context) {
        super(context);
        init();
    }

    public CustomShadowImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomShadowImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        Log.d(TAG, "init " + this.hashCode());

        DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
        mShadowRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_RADIUS_DP, dm);
        mShadowXOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_X_OFFSET_DP, dm);
        mShadowYOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_Y_OFFSET_DP, dm);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //noinspection deprecation
        int shadowColor = getContext().getResources().getColor(R.color.shadow);
        mPaint.setColor(shadowColor);

        expanded = false;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d(TAG, String.format("onMeasure %d w: %d, h: %d", this.hashCode(), MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)));
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        Log.d(TAG, String.format("onLayout %d changed: %b, l: %d, t: %d, r: %d, b: %d", this.hashCode(), changed, left, top, right, bottom));
        super.onLayout(changed, left, top, right, bottom);

        if (changed) {
            if (!expanded) {
                layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
                layoutParams.width = (int) (layoutParams.width + mShadowXOffset);
                layoutParams.height = (int) (layoutParams.height + mShadowYOffset);
                expanded = true;
            }

            cx = (right - left) / 2 + mShadowXOffset;
            cy = (bottom - top) / 2 + mShadowYOffset;

            boolean widthGreater = (right - left) > (bottom - top);
            radius = (widthGreater ? right - left : bottom - top) / 2;

            if (mShadowBitmap == null) {
                Bitmap bitmap = Bitmap.createBitmap(right - left, bottom - top, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);
                canvas.drawCircle(cx, cy, radius, mPaint);

                if (Build.VERSION.SDK_INT >= 17 && !isInEditMode()) {
                    RenderScript rs = RenderScript.create(getContext());
                    Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
                    Allocation output = Allocation.createTyped(rs, input.getType());
                    ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
                    script.setRadius(mShadowRadius);
                    script.setInput(input);
                    script.forEach(output);
                    output.copyTo(bitmap);
                }

                mShadowBitmap = bitmap;
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.d(TAG, "onDraw " + this.hashCode());
        canvas.drawBitmap(mShadowBitmap, mShadowXOffset, mShadowYOffset, null);
        super.onDraw(canvas);
    }
}
Run Code Online (Sandbox Code Playgroud)

Bla*_*der 5

默认情况下,视图只允许其父视图在其边界内绘制,而不能超出其边界。

您有两个选择:

  • 要么添加一些填充Imageview来扩大其边界,而不是使用layout_margin并在这些边界内绘制。
  • 您可以通过设置android:clipChildren="false"为您的FrameLayout.


mac*_*usz 2

如果原因是视图父级的填充,请将以下行添加到父级 xml 元素:

android:clipToPadding="false"
Run Code Online (Sandbox Code Playgroud)