RoundShadowImageView
RoundShadowImageView 是1个为圆形图片的ImageView增加暗影的自定义控件.
GitHub
RoundShadowImageView
为什么写这个库
- Android未提供现成的工具,自定义控件暗影的色彩
- 开源社区中现有的库,应用了ViewGroup包装子View的模式,会减少布局层级
- 应用Paint.setShadowLayer,色彩的透明度变动太快,只能在很窄的范畴能看到色彩突变
RoundShadowImageView的劣势
- 不减少布局层级,性能绝对更好
- 暗影的色彩,初始透明度,地位,绝对中心点角度,暗影的显示尺寸 均可自在定制.
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