【发布时间】:2016-01-19 06:22:02
【问题描述】:
我正在尝试扩展 ImageView 并添加阴影。我遇到了一个问题,阴影被视图边界剪裁并且看起来很糟糕:
我尝试通过LayoutParams 以编程方式设置宽度/高度,并尝试了不同的XML 属性,如android:adjustViewBounds,但显示没有变化。同样,设置android:layout_margin 对防止阴影被剪裁也是无效的。
谁能帮我弄清楚如何避免这种剪裁?我确定我在这里忽略了一些明显的东西。
更新:
此时,我的代码非常具体地针对一种情况:我正在尝试在圆形位图下方绘制圆形“阴影”。很明显,视图边界导致了裁剪,但我一直无法找到允许我扩展视图边界的解决方案。
#android-dev 上声称我的数学完全错误。我正在考虑屏幕密度,这是一个常见问题。我已经对所有方面的数学进行了三次检查,但找不到可能出错的地方。
最初,在 xxhdpi 屏幕上,密度为 3.0,56dp 的图像正好是 168px 宽和 168px 高。在将 2dp 添加到宽度和高度以考虑偏移后,layoutParam 的宽度=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>
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);
}
}
【问题讨论】:
-
尝试设置填充?
-
@pskink 是的,我尝试设置填充。它没有帮助......至少对我目前拥有的代码没有帮助。
-
你能在这里贴一些代码吗?
-
@ci_ 在上面的问题中有一个 gitlab repo 的链接。
-
this 有什么帮助吗?父级上的
android:clipChildren="false"怎么样?
标签: android android-custom-view