RoundShadowImageView

RoundShadowImageView is a custom control for adding shadows to the ImageView of round images.

GitHub

RoundShadowImageView

Why write this library

  1. Android does not provide a tool to customize the color of control shadows
  2. Existing libraries in the open source community, which use a ViewGroup wrapper around a View, add layers to the layout
  3. With paint.setShadowLayer, the opacity of the color changes so fast that you can only see the gradient in a narrow range

The advantage of RoundShadowImageView

  1. Performance is better without increasing the layout level
  2. Shadow color, initial transparency, position, relative center Angle, shadow display size can be customized.

The limitations of RoundShadowImageView

The scope is narrow and only suitable for customizing shadows for the round image ImageView.

Use steps:

Step 1:

Copy the source code to your project.

Step 2:

Declare it in a layout file, or create an instance of RoundShadowImageView directly from Java code.

Step 3:

Set its shade-related properties directly in XML or through Java methods.

Example:

Source:


      
<resources>
    <declare-styleable name="RoundShadowImageView">
        <! -- Ratio of shadow width to content area radius -->
        <attr name="shadowRatio" format="float" />
        <! -- The Angle of the shadow center relative to the center of the content area, 0 degrees from the content area vertical down/starting Angle -->
        <attr name="shadowCircleAngle" format="float" />
        <! -- Shadow color -->
        <attr name="shadowColor" format="color|reference" />
        <! Initial opacity of shadow color -->
        <attr name="shadowStartAlpha" format="float" />
        <! -- Shadow position -->
        <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>
Copy the code
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
 * @date2020/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.30 F;
    private float shadowRadius = 0.0 F;
    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.5 F;
    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(a) {
        red = Color.red(shadowColor);
        green = Color.green(shadowColor);
        blue = Color.blue(shadowColor);
    }

    private void gainShadowCenterAndShader(a) {
        gainShadowCenter();
        gainShader();
    }

    private void gainShadowCenter(a) {
        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(a) {
        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.95 F.1F - shadowRatio,
                1F - shadowRatio * 0.50 F.1F
        };
        shader = new RadialGradient(shadowCenterX, shadowCenterY, shadowRadius, colors, stops, Shader.TileMode.CLAMP);
    }

    private void contentSizeChanged(a) {
        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.0 F;
        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(a) {
        return shadowRatio;
    }

    public float getShadowCircleAngle(a) {
        return shadowCircleAngle;
    }

    public int getShadowColor(a) {
        return shadowColor;
    }

    public float getShadowStartAlpha(a) {
        return shadowStartAlpha;
    }

    public int getShadowPosition(a) {
        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(); }}}Copy the code

Refer to the article

  • Problem 0011 – Android Shadow Outline Outline


Like the classmate point a star ha!!RoundShadowImageView