###Android 自定义圆角、立体ImageView – MaterialImageView ###
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VbgFBeuS-1574996603651)(https://img-blog.csdn.net/20160106162243995)]
public class MaterialImageView extends ImageView {
private GradientDrawable mMaskDrawable;
private Paint mMaskedPaint;
private boolean mCacheValid = false;
private Bitmap mCacheBitmap;
private int mCachedWidth;
private int mCachedHeight;
private Path mCornerShadowPath;
private Paint mCornerShadowPaint;
private Paint mEdgeShadowPaint;
private Paint mPaint;
private RectF mBoundsF = new RectF();
private int mInsetShadow;
private float mMaxShadowSize;
private float mRawMaxShadowSize;
private float mShadowSize;
private float mRawShadowSize;
private int radius;
private Rect mBgBounds = new Rect();
private Rect mBounds = new Rect();
float mCornerRadius;
private int mShadowStartColor;
private int mShadowEndColor;
private boolean mUseWhiteBackground = false;
public MaterialImageView(Context context) {
this(context, null);
}
public MaterialImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MaterialImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, context.getResources(), defStyleAttr);
}
public void init(Context context, AttributeSet attrs, Resources resources, int defStyleAttr){
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MaterialImageView,
defStyleAttr, 0);
int shadowSize = a.getInt(R.styleable.MaterialImageView_shadow_size, 8);
radius = a.getInt(R.styleable.MaterialImageView_radius_size, 15);//radius size
mUseWhiteBackground = a.getBoolean(R.styleable.MaterialImageView_use_white_bg, false);
a.recycle();
//遮罩 用于画出圆角图片
mMaskDrawable = new GradientDrawable();
mMaskDrawable.setShape(GradientDrawable.RECTANGLE);
mMaskDrawable.setColor(0xff000000);
mMaskDrawable.setCornerRadius(radius);
mMaskedPaint = new Paint();
mMaskedPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
mMaskedPaint.setAntiAlias(true);
mCacheBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
mPaint = new Paint(Paint.DITHER_FLAG | Paint.HINTING_ON );
mPaint.setColor(0xffffffff);
mShadowStartColor = resources.getColor(R.color.shadow_start_color);
mShadowEndColor = resources.getColor(R.color.shadow_end_color);
mInsetShadow = resources.getDimensionPixelSize(R.dimen.cardview_compat_inset_shadow);
mCornerRadius = ((int) (radius + 0.5F));
mCornerShadowPaint = new Paint(Paint.DITHER_FLAG | Paint.HINTING_ON);
mCornerShadowPaint.setStyle(Paint.Style.FILL);
mCornerShadowPaint.setAntiAlias(true);
mEdgeShadowPaint = new Paint(mCornerShadowPaint);
mEdgeShadowPaint.setAntiAlias(true);
setShadowSize(shadowSize, 20);
}
void setShadowSize(float shadowSize, float maxShadowSize) {
if((shadowSize < 0.0f) || (maxShadowSize < 0.0f)){
throw new IllegalArgumentException("invalid shadow size");
}
if(shadowSize > maxShadowSize){
shadowSize = maxShadowSize;
}
mRawShadowSize = shadowSize;
mRawMaxShadowSize = maxShadowSize;
mShadowSize = ((int) (shadowSize * 1.5F + mInsetShadow + 0.5F));
mMaxShadowSize = (maxShadowSize + mInsetShadow);
invalidate();
}
@Override
protected boolean setFrame(int l, int t, int r, int b) {
final boolean changed = super.setFrame(l, t, r, b);
mBounds.set(0, 0, r - l, b - t);
mBoundsF.set(0, 0, r - l, b - t);
buildShadow(0, 0, r - l, b - t);
mMaskDrawable.setBounds(mBgBounds.left, mBgBounds.top + (int)(mRawShadowSize / 2.0F),
mBgBounds.right, mBgBounds.bottom + (int)(mRawShadowSize / 2.0F));
if (changed) {
mCacheValid = false;
}
return changed;
}
private void buildShadow(int left, int top, int right, int bottom){
float verticalOffset = mRawMaxShadowSize * 1.5F;
mBgBounds.set(left + (int)mRawMaxShadowSize, top +(int)verticalOffset,
right - (int)mRawMaxShadowSize, bottom - (int)verticalOffset);
buildShadowCorners();
}
private void buildShadowCorners() {
RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
RectF outerBounds = new RectF(innerBounds);
outerBounds.inset(-mShadowSize, -mShadowSize);
if (mCornerShadowPath == null)
mCornerShadowPath = new Path();
else {
mCornerShadowPath.reset();
}
mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
mCornerShadowPath.moveTo(-mCornerRadius, 0.0F);
mCornerShadowPath.rLineTo(-mShadowSize, 0.0F);
mCornerShadowPath.arcTo(outerBounds, 180.0F, 90.0F, false);
mCornerShadowPath.arcTo(innerBounds, 270.0F, -90.0F, false);
mCornerShadowPath.close();
float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
mCornerShadowPaint.setShader(
new RadialGradient(0.0F, 0.0F, mCornerRadius + mShadowSize,//阴影半径
new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
new float[]{0.0F, startRatio, 1.0F},
Shader.TileMode.CLAMP));
mEdgeShadowPaint.setShader(
new LinearGradient(
0.0F, -mCornerRadius + mShadowSize, 0.0F, -mCornerRadius - mShadowSize,
new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
new float[]{0.0F, 0.5F, 1.0F}, Shader.TileMode.CLAMP));
mEdgeShadowPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.translate(0.0F, mRawShadowSize / 2.0F);
drawShadow(canvas);
//白色背景
if(mUseWhiteBackground) {
canvas.translate(0.0F, -mRawShadowSize / 2.0F);
canvas.drawRoundRect(
new RectF(mBgBounds.left, mBgBounds.top, mBgBounds.right, mBgBounds.bottom),
mCornerRadius, mCornerRadius, mPaint);
}
canvas.translate(0.0F, 0);
if(mBounds == null){
return ;
}
int width = mBounds.width();
int height= mBounds.height();
if (width == 0 || height == 0) {
return;
}
if (!mCacheValid || width != mCachedWidth || height != mCachedHeight) {
if(width == mCachedWidth && height == mCachedHeight){
mCacheBitmap.eraseColor(0);
}else{
mCacheBitmap.recycle();
mCacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCachedWidth = width;
mCachedHeight = height;
}
Canvas cacheCanvas = new Canvas(mCacheBitmap);
if (mMaskDrawable != null) {
int sc = cacheCanvas.save();
cacheCanvas.translate(0.0F, -mRawShadowSize/2.0f);
mMaskDrawable.draw(cacheCanvas);
cacheCanvas.saveLayer(mBoundsF, mMaskedPaint,
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG);
super.onDraw(cacheCanvas);
cacheCanvas.restoreToCount(sc);
}
}
canvas.drawBitmap(mCacheBitmap, mBounds.left, mBounds.top, null);
}
private void drawShadow(Canvas canvas) {
float edgeShadowTop = -mCornerRadius - mShadowSize;
float inset = mCornerRadius + mInsetShadow + mRawShadowSize / 2.0F;
boolean drawHorizontalEdges = mBgBounds.width() - 2.0F * inset > 0.0F;
boolean drawVerticalEdges = mBgBounds.height() - 2.0F * inset > 0.0F;
int saved;
saved = canvas.save();
canvas.translate(mBgBounds.left + inset, mBgBounds.top + inset);
if (drawHorizontalEdges) {
canvas.drawRect(0.0F, edgeShadowTop, mBgBounds.width() - 2.0F * inset, -mCornerRadius, mEdgeShadowPaint);
}
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);//和下面不同 这句放在这里 才不会出现旋转后阴影缺角
canvas.restoreToCount(saved);
saved = canvas.save();
canvas.translate(mBgBounds.left + inset, mBgBounds.bottom - inset);
canvas.rotate(270.0F);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawVerticalEdges) {
canvas.drawRect(0.0F, edgeShadowTop, mBgBounds.height() - 2.0F * inset, -mCornerRadius, mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
saved = canvas.save();
canvas.translate(mBgBounds.right - inset, mBgBounds.top + inset);
canvas.rotate(90.0F);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawVerticalEdges) {
canvas.drawRect(0.0F, edgeShadowTop, mBgBounds.height() - 2.0F * inset, -mCornerRadius, mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
saved = canvas.save();
canvas.translate(mBgBounds.right - inset, mBgBounds.bottom - inset);
canvas.rotate(180.0F);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawHorizontalEdges) {
canvas.drawRect(0.0F, edgeShadowTop, mBgBounds.width() - 2.0F * inset, -mCornerRadius + mShadowSize, mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
}
}
// attrs
<declare-styleable name="MaterialImageView">
<attr name="shadow_size" format="integer" />
<attr name="radius_size" format="integer" />
<attr name="use_white_bg" format="boolean" />
</declare-styleable>
//colors
<color name="shadow_end_color">#03000000</color>
<color name="shadow_start_color">#37000000</color>