简体   繁体   English

如何防止我的自定义视图绘图被视图边界剪裁?

[英]How can I prevent my custom view drawing from being clipped by the view bounds?

I'm attempting to extend ImageView and add a shadow.我正在尝试扩展 ImageView 并添加阴影。 I'm running into a problem where the shadow is being clipped by the view bounds and looks quite bad:我遇到了一个问题,即阴影被视图边界裁剪并且看起来很糟糕:

剪下的阴影

I've attempted to set the width/height via LayoutParams programmatically as well as trying different XML properties like android:adjustViewBounds , but there is no change in the display.我试图通过LayoutParams编程方式设置宽度/高度,并尝试不同的 XML 属性,如android:adjustViewBounds ,但显示没有变化。 Similarly, setting a android:layout_margin is ineffective in preventing the shadow from being clipped.同样,设置android:layout_margin对防止阴影被剪裁无效。

Can anyone help me figure out how to avoid this clipping?谁能帮我弄清楚如何避免这种剪辑? I'm sure there is something obvious I'm overlooking here.我敢肯定我在这里忽略了一些明显的东西。

Update:更新:

My code is very specific at this time to exactly one case: I'm trying to draw a circle "shadow" underneath a circle bitmap.我的代码此时非常特定于一种情况:我试图在圆形位图下方绘制一个圆形“阴影”。 It is obvious that the view bounds are causing the clipping, but I have not been able to find a solution that will allow me to expand the view bounds.很明显,视图边界导致了裁剪,但我一直无法找到允许我扩展视图边界的解决方案。

It has been claimed on #android-dev that my math is simply wrong. #android-dev 上有人声称我的数学完全错误。 I am accounting for screen density, which is a common problem.我正在考虑屏幕密度,这是一个常见问题。 I have triple checked my math on all counts and cannot find where it might be wrong.我已经对我的数学进行了三重检查,但找不到可能出错的地方。

Initially, on an xxhdpi screen, density 3.0, the 56dp image is exactly 168px wide and 168px high.最初,在 xxhdpi 屏幕上,密度为 3.0,56dp 图像正好是 168px 宽和 168px 高。 After adding 2dp to the width and height to account for the offset, the layoutParams have width=174 and height=174.在将宽度和高度添加 2dp 以考虑偏移后,layoutParams 具有 width=174 和 height=174。

My basic approach is to let the super ImageView do its thing and draw the bitmap specified in xml and all I want to do is draw a little something additionally.我的基本方法是让超级 ImageView 做它的事情并绘制 xml 中指定的位图,我想要做的就是额外绘制一些东西。 Is this approach fundamentally flawed?这种方法是否存在根本性缺陷?

I use the largest of width or height in onLayout to determine what the radius of my shadow circle should be: radius = Max(width, height) / 2. I draw a circle with this radius and center point at (Cx, Cy) where Cx is the midpoint of the width plus a x-offset and Cy is the midpoint of the height plus a y-offset to create the shadow effect.我在onLayout使用最大的宽度或高度来确定我的阴影圆的半径应该是多少:radius = Max(width, height) / 2.我用这个半径和中心点在 (Cx, Cy) 处绘制一个圆Cx 是宽度的中点加上 x 偏移量,Cy 是高度的中点加上 y 偏移量以创建阴影效果。 I draw the additional circle using a canvas to a bitmap and later in onDraw I place my circle on the canvas before allowing the ImageView super onDraw to take care of the source bitmap.我使用画布将额外的圆绘制到位图,稍后在onDraw我将我的圆放在画布上,然后让ImageView super onDraw处理源位图。

Additionally, in my onLayout I attempt to account for the x- and y-offset distances and add those to my view's width and height via LayoutParams , but no change in the size of the view is evidenced when the view is drawn.此外,在我的onLayout我尝试考虑 x 和 y 偏移距离,并通过LayoutParams将它们添加到我的视图的宽度和高度,但在绘制视图时没有证明视图大小的变化。

Here is the code that I'm using: https://gitlab.com/dm78/ImageViewWithShadowExample/这是我正在使用的代码: https : //gitlab.com/dm78/ImageViewWithShadowExample/

Here is the relevant code:这是相关的代码:

activity_main.xml活动_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>

CustomShadowImageView.java 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);
    }
}

By default, Views are only allowed by their parent to draw within their bounds and not beyond.默认情况下,视图只允许其父级在其边界内绘制,而不能超出。

You have two options:您有两个选择:

  • Either you add some padding to your Imageview to enlarge its bounds instead of using layout_margin and you draw within these bounds.要么向Imageview添加一些填充以扩大其边界,而不是使用layout_margin并在这些边界内进行绘制。
  • Either you disable the child clipping behavior by setting android:clipChildren="false" to your FrameLayout .您可以通过将android:clipChildren="false"FrameLayout来禁用子剪辑行为。

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

android:clipToPadding="false"

android:clipChildren="false"在父布局中为我工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在Android视图剪辑边界外绘图时:如何防止底层视图在我的自定义视图上绘制? - When drawing outside the view clip bounds with Android: how do I prevent underling views from drawing on top of my custom view? 如果其位置不是(0,0),我的自定义视图将被剪裁 - My custom view is being clipped if its position is not (0,0) 如何滚动自定义视图? 我想看到在屏幕边界上绘制的形状 - How can I scroll my custom view? I want to see the shapes drawn over the bounds of the screen 如何防止ListvView中的视图被推出视图 - How do I prevent a view in my ListvView from being pushed out of view 如何在具有许多图形形状的自定义视图上创建边框 - How can i create a border on custom view with many drawing shapes 如何在旋转屏幕时防止当前标签视图丢失? - How can I prevent the current tab view from being lost when rotating the screen? 我的自定义视图没有显示,我应该如何修复它? - My custom view is not being shown how should I fixed it? 自定义视图中的ScaleAnimation被裁剪 - ScaleAnimation in custom View getting clipped 如何防止Android View绘制背景 - How do you prevent an Android View from drawing its Background 查看外部边界未正确绘制 - View outside bounds not drawing properly
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM