package com.zzt.library; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.GradientDrawable; import android.util.AttributeSet; import android.widget.ImageView; /** * Created by zzt on 2015/2/26. */ 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); } }