RoundShadowImageView

RoundShadowImageView 是1个为圆形图片的ImageView增加暗影的自定义控件.

GitHub

RoundShadowImageView

为什么写这个库

  1. Android未提供现成的工具,自定义控件暗影的色彩
  2. 开源社区中现有的库,应用了ViewGroup包装子View的模式,会减少布局层级
  3. 应用Paint.setShadowLayer,色彩的透明度变动太快,只能在很窄的范畴能看到色彩突变

RoundShadowImageView的劣势

  1. 不减少布局层级,性能绝对更好
  2. 暗影的色彩,初始透明度,地位,绝对中心点角度,暗影的显示尺寸 均可自在定制.

RoundShadowImageView的局限

适用范围较窄,仅实用于为圆形图片ImageView定制暗影.

应用步骤:

步骤1:

将源码拷贝至你的我的项目.

步骤2:

在布局文件中申明,或者间接通过java代码创立RoundShadowImageView实例.

步骤3:

在xml中间接设置其暗影相干属性,或通过java办法进行设置.

示例:

源码:

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="RoundShadowImageView">        <!--暗影宽度绝对于内容区域半径的比例-->        <attr name="shadowRatio" format="float" />        <!--暗影核心绝对于内容区域核心的角度,以内容区域垂直向下为0度/起始角度-->        <attr name="shadowCircleAngle" format="float" />        <!--暗影色彩-->        <attr name="shadowColor" format="color|reference" />        <!--暗影色彩初始透明度-->        <attr name="shadowStartAlpha" format="float" />        <!--暗影地位-->        <attr name="shadowPosition" format="enum">            <enum name="start" value="1" />            <enum name="top" value="2" />            <enum name="end" value="3" />            <enum name="bottom" value="4" />        </attr>    </declare-styleable></resources>
import static com.huanhailiuxin.jet2020.othertest.shadow.ShadowPosition.BOTTOM;import static com.huanhailiuxin.jet2020.othertest.shadow.ShadowPosition.END;import static com.huanhailiuxin.jet2020.othertest.shadow.ShadowPosition.START;import static com.huanhailiuxin.jet2020.othertest.shadow.ShadowPosition.TOP;@IntDef({        START,        TOP,        END,        BOTTOM})@Retention(RetentionPolicy.SOURCE)@Target({ElementType.FIELD, ElementType.PARAMETER})@interface ShadowPosition {    int START = 1;    int TOP = 2;    int END = 3;    int BOTTOM = 4;}/** * @author HuanHaiLiuXin * @github https://github.com/HuanHaiLiuXin * @date 2020/11/23 */public class RoundShadowImageView extends AppCompatImageView {    private Paint paint;    private Shader shader;    int[] colors;    float[] stops;    private float contentSize;    @FloatRange(from = 0.0F, to = 1.0F)    private float shadowRatio = 0.30F;    private float shadowRadius = 0.0F;    private float shadowCenterX, shadowCenterY;    @ShadowPosition    private int shadowPosition = ShadowPosition.BOTTOM;    private float shadowCircleAngle = 0F;    private boolean useShadowCircleAngle = false;    private int red, green, blue;    private int shadowColor = Color.RED;    private @FloatRange(from = 0F, to = 1F)    float shadowStartAlpha = 0.5F;    private boolean isLtr = true;    public RoundShadowImageView(Context context) {        this(context, null, 0);    }    public RoundShadowImageView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public RoundShadowImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initAttrs(context, attrs);    }    private void initAttrs(Context context, @Nullable AttributeSet attrs) {        setLayerType(LAYER_TYPE_SOFTWARE, null);        paint = new Paint(Paint.ANTI_ALIAS_FLAG);        paint.setStyle(Paint.Style.FILL);        isLtr = getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;        if (attrs != null) {            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundShadowImageView);            shadowRatio = typedArray.getFloat(R.styleable.RoundShadowImageView_shadowRatio, shadowRatio);            shadowCircleAngle = typedArray.getFloat(R.styleable.RoundShadowImageView_shadowCircleAngle, shadowCircleAngle);            if (shadowCircleAngle > 0F) {                useShadowCircleAngle = true;            }            if (!useShadowCircleAngle) {                shadowPosition = typedArray.getInt(R.styleable.RoundShadowImageView_shadowPosition, shadowPosition);            }            shadowColor = typedArray.getColor(R.styleable.RoundShadowImageView_shadowColor, shadowColor);            gainRGB();            shadowStartAlpha = typedArray.getFloat(R.styleable.RoundShadowImageView_shadowStartAlpha, shadowStartAlpha);            typedArray.recycle();        }    }    private void gainRGB() {        red = Color.red(shadowColor);        green = Color.green(shadowColor);        blue = Color.blue(shadowColor);    }    private void gainShadowCenterAndShader() {        gainShadowCenter();        gainShader();    }    private void gainShadowCenter() {        shadowRadius = contentSize / 2F;        if (useShadowCircleAngle) {            double radians = Math.toRadians(shadowCircleAngle + 90);            shadowCenterX = (float) (getWidth() / 2 + Math.cos(radians) * shadowRadius * shadowRatio);            shadowCenterY = (float) (getHeight() / 2 + Math.sin(radians) * shadowRadius * shadowRatio);        } else {            switch (shadowPosition) {                case ShadowPosition.START:                    if (isLtr) {                        shadowCenterX = getWidth() / 2 - shadowRadius * shadowRatio;                    } else {                        shadowCenterX = getWidth() / 2 + shadowRadius * shadowRatio;                    }                    shadowCenterY = getHeight() / 2;                    break;                case ShadowPosition.TOP:                    shadowCenterY = getHeight() / 2 - shadowRadius * shadowRatio;                    shadowCenterX = getWidth() / 2;                    break;                case ShadowPosition.END:                    if (isLtr) {                        shadowCenterX = getWidth() / 2 + shadowRadius * shadowRatio;                    } else {                        shadowCenterX = getWidth() / 2 - shadowRadius * shadowRatio;                    }                    shadowCenterY = getHeight() / 2;                    break;                case ShadowPosition.BOTTOM:                    shadowCenterY = getHeight() / 2 + shadowRadius * shadowRatio;                    shadowCenterX = getWidth() / 2;                    break;                default:                    shadowCenterY = getHeight() / 2 + shadowRadius * shadowRatio;                    shadowCenterX = getWidth() / 2;                    break;            }        }    }    private void gainShader() {        colors = new int[]{                Color.TRANSPARENT,                Color.argb((int) (shadowStartAlpha * 255), red, green, blue),                Color.argb((int) (shadowStartAlpha * 255 / 2), red, green, blue),                Color.argb(0, red, green, blue)        };        stops = new float[]{                (1F - shadowRatio) * 0.95F,                1F - shadowRatio,                1F - shadowRatio * 0.50F,                1F        };        shader = new RadialGradient(shadowCenterX, shadowCenterY, shadowRadius, colors, stops, Shader.TileMode.CLAMP);    }    private void contentSizeChanged() {        contentSize = Math.min(getWidth(), getHeight()) / (1 + this.shadowRatio);        setPadding((int) (getWidth() - contentSize) / 2, (int) (getHeight() - contentSize) / 2, (int) (getWidth() - contentSize) / 2, (int) (getHeight() - contentSize) / 2);        gainShadowCenterAndShader();    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        contentSizeChanged();    }    public void setShadowRatio(@FloatRange(from = 0.0F, to = 1.0F) float shadowRatio) {        shadowRatio = shadowRatio % 1F;        if (shadowRatio != this.shadowRatio) {            this.shadowRatio = shadowRatio;            contentSizeChanged();            invalidate();        }    }    public void setShadowColor(@ColorInt int shadowColor) {        if (shadowColor != this.shadowColor) {            this.shadowColor = shadowColor;            gainRGB();            gainShader();            invalidate();        }    }    public void setShadowStartAlpha(@FloatRange(from = 0F, to = 1F) float shadowStartAlpha) {        shadowStartAlpha = shadowStartAlpha % 1F;        if (shadowStartAlpha != this.shadowStartAlpha) {            this.shadowStartAlpha = shadowStartAlpha;            gainShader();            invalidate();        }    }    public void setShadowCircleAngle(float shadowCircleAngle) {        shadowCircleAngle = Math.abs(shadowCircleAngle) % 360.0F;        if (shadowCircleAngle != this.shadowCircleAngle) {            this.shadowCircleAngle = shadowCircleAngle;            if (this.shadowCircleAngle > 0F) {                useShadowCircleAngle = true;            }            gainShadowCenterAndShader();            invalidate();        }    }    public void setShadowPosition(@ShadowPosition int shadowPosition){        if(useShadowCircleAngle || shadowPosition != this.shadowPosition){            useShadowCircleAngle = false;            this.shadowPosition = shadowPosition;            gainShadowCenterAndShader();            invalidate();        }    }    public float getShadowRatio() {        return shadowRatio;    }    public float getShadowCircleAngle() {        return shadowCircleAngle;    }    public int getShadowColor() {        return shadowColor;    }    public float getShadowStartAlpha() {        return shadowStartAlpha;    }    public int getShadowPosition() {        return shadowPosition;    }    @Override    protected void onDraw(Canvas canvas) {        paint.setShader(shader);        canvas.drawCircle(shadowCenterX, shadowCenterY, shadowRadius, paint);        paint.setShader(null);        super.onDraw(canvas);    }    @Override    protected void onConfigurationChanged(Configuration newConfig) {        super.onConfigurationChanged(newConfig);        boolean newLtr = getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;        if (newLtr != isLtr) {            this.isLtr = newLtr;            gainShadowCenterAndShader();            invalidate();        }    }}

参考文章

  • 问题0011 - Android 暗影 轮廓 Outline


喜爱的同学点个star哈!! RoundShadowImageView