package com.owen.tvrecyclerview.example; import android.content.Context; 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.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.Shader; import android.os.Build; import android.util.AttributeSet; import android.widget.FrameLayout; /** * 倒影,圆角控件. * * @author hailongqiu 356752238@qq.com */ public class ReflectItemView extends FrameLayout { private static final String TAG = "ReflectItemView"; private static final int DEFUALT_REFHEIGHT = 80; private static final int DEFUALT_RADIUS = 12; private Paint mClearPaint = null; private Paint mShapePaint = null; private Paint mRefPaint = null; private int mRefHeight = DEFUALT_REFHEIGHT; private boolean mIsDrawShape = false; private boolean mIsReflection = false; private float mRadius = DEFUALT_RADIUS; private int mRefleSpacing = 0; private BitmapMemoryCache mBitmapMemoryCache = BitmapMemoryCache.getInstance(); private static int sViewIDNum = 0; private int viewIDNum = 0; public ReflectItemView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } public ReflectItemView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public ReflectItemView(Context context) { super(context); init(context, null); } private void init(Context context, AttributeSet attrs) { setClipChildren(false); setClipToPadding(false); setWillNotDraw(false); // 初始化属性. if (attrs != null) { TypedArray tArray = context.obtainStyledAttributes(attrs, R.styleable.reflectItemView);// 获取配置属性 mIsReflection = tArray.getBoolean(R.styleable.reflectItemView_isReflect, false); mRefHeight = (int) tArray.getDimension(R.styleable.reflectItemView_reflect_height, DEFUALT_REFHEIGHT); mIsDrawShape = tArray.getBoolean(R.styleable.reflectItemView_isShape, false); mRadius = tArray.getDimension(R.styleable.reflectItemView_radius, DEFUALT_RADIUS); // 圆角半径. mRefleSpacing = (int) tArray.getDimension(R.styleable.reflectItemView_refle_spacing, 0); setRadius(mRadius); tArray.recycle(); } // 初始化圆角矩形. initShapePaint(); // 初始化倒影参数. initRefPaint(); } private void initShapePaint() { mShapePaint = new Paint(); mShapePaint.setAntiAlias(true); // 取两层绘制交集。显示下层。 mShapePaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); } private void initRefPaint() { if (mRefPaint == null) { mRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 倒影渐变. mRefPaint.setShader( new LinearGradient(0, 0, 0, mRefHeight, new int[]{0x77000000, 0x66AAAAAA, 0x0500000, 0x00000000}, new float[]{0.0f, 0.1f, 0.9f, 1.0f}, Shader.TileMode.CLAMP)); mRefPaint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY)); } if (mClearPaint == null) { mClearPaint = new Paint(); mClearPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR)); } } /** * 设置倒影的shader属性. */ public void setReflectionShader(Shader shader) { if (mRefPaint != null) { mRefPaint.setShader(shader); invalidate(); } } public void setReflectionSpacing(int spacing) { mRefleSpacing = spacing; invalidate(); } /** * 设置是否倒影. */ public void setReflection(boolean ref) { mIsReflection = ref; invalidate(); } public boolean isReflection() { return this.mIsReflection; } /** * 设置绘制奇形怪状的形状. */ public void setDrawShape(boolean isDrawShape) { mIsDrawShape = isDrawShape; invalidate(); } public boolean isDrawShape() { return this.mIsDrawShape; } /** * 倒影高度. */ public void setRefHeight(int height) { this.mRefHeight = height; } public int getRefHeight() { return this.mRefHeight; } @Override public void invalidate() { super.invalidate(); } /** * 获取缓存ID. */ private int getViewCacheID() { if (viewIDNum == 0) { sViewIDNum++; viewIDNum = sViewIDNum; } return viewIDNum; } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (viewIDNum != 0) { mBitmapMemoryCache.removeImageCache(viewIDNum + ""); } } public Path getShapePath(int width, int height, float radius) { return addRoundPath3(getWidth(), getHeight(), radius); } private Path addRoundPath3(int width, int height, float radius) { Path path = new Path(); path.addRoundRect(new RectF(0, 0, width, height), radius, radius, Path.Direction.CW); return path; } @Override public void draw(Canvas canvas) { try { if (canvas != null) { if (mIsDrawShape && (mRadius > 0)) { drawShapePathCanvas(canvas); } else { super.draw(canvas); } /** * 绘制倒影. 4.3 SDK-18,有问题,<br> * 在使用Canvas.translate(dx, dy)会出现BUG. <br> */ if (Build.VERSION.SDK_INT == 18) { drawRefleCanvas4_3_18(canvas); } else if (Build.VERSION.SDK_INT == 17) { // 4.2 不需要倒影,绘制有问题,暂时屏蔽. drawRefleCanvas(canvas); } else { // 性能高速-倒影(4.3有问题). drawRefleCanvas(canvas); } } } catch (Exception e) { e.printStackTrace(); } } /** * 绘制圆角控件. 修复使用clipPath有锯齿问题. */ private void drawShapePathCanvas(Canvas shapeCanvas) { if (shapeCanvas != null) { int width = getWidth(); int height = getHeight(); if (width == 0 || height == 0) return; int count = shapeCanvas.save(); int count2 = shapeCanvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG); // Path path = getShapePath(width, height, mRadius); super.draw(shapeCanvas); shapeCanvas.drawPath(path, mShapePaint); // if (count2 > 0) { shapeCanvas.restoreToCount(count2); } shapeCanvas.restoreToCount(count); } } /** * 绘制倒影. 修复原先使用bitmap卡顿的问题. */ private void drawRefleCanvas(Canvas refleCanvas) { if (mIsReflection) { refleCanvas.save(); int dy = getHeight(); int dx = 0; refleCanvas.translate(dx, dy + mRefleSpacing); drawReflection(refleCanvas); refleCanvas.restore(); } } private void drawRefleCanvas4_3_18(Canvas canvas) { if (mIsReflection) { // 创建一个画布. String cacheID = getViewCacheID() + ""; // Bitmap reflectBitmap = mBitmapMemoryCache.getBitmapFromMemCache(cacheID); if (reflectBitmap == null) { reflectBitmap = Bitmap.createBitmap(getWidth(), mRefHeight, Bitmap.Config.ARGB_8888); mBitmapMemoryCache.addBitmapToMemoryCache(cacheID, reflectBitmap); } Canvas reflectCanvas = new Canvas(reflectBitmap); reflectCanvas.drawPaint(mClearPaint); // 清空画布. /** * 如果设置了圆角,倒影也需要圆角. */ int width = reflectCanvas.getWidth(); int height = reflectCanvas.getHeight(); RectF outerRect = new RectF(0, 0, width, height); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); if (mIsDrawShape) { reflectCanvas.drawPath(getShapePath(width, height + 50, mRadius), paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); } reflectCanvas.saveLayer(outerRect, paint, Canvas.ALL_SAVE_FLAG); drawReflection4_3_18(reflectCanvas); reflectCanvas.restore(); canvas.save(); int dy = getHeight(); int dx = 0; canvas.translate(dx, dy + mRefleSpacing); canvas.drawBitmap(reflectBitmap, 0, 0, null); canvas.restore(); } } public void drawReflection4_3_18(Canvas canvas) { canvas.save(); canvas.clipRect(0, 0, getWidth(), mRefHeight); canvas.save(); canvas.scale(1, -1); canvas.translate(0, -getHeight()); super.draw(canvas); canvas.restore(); canvas.drawRect(0, 0, getWidth(), mRefHeight, mRefPaint); canvas.restore(); } /** * 绘制倒影. */ public void drawReflection(Canvas reflectionCanvas) { int width = getWidth(); int height = getHeight(); int count = reflectionCanvas.save(); int count2 = reflectionCanvas.saveLayer(0, 0, width, mRefHeight, null, Canvas.ALL_SAVE_FLAG); // reflectionCanvas.save(); reflectionCanvas.clipRect(0, 0, getWidth(), mRefHeight); reflectionCanvas.save(); reflectionCanvas.scale(1, -1); reflectionCanvas.translate(0, -getHeight()); super.draw(reflectionCanvas); if (mIsDrawShape) { Path path = getShapePath(width, height, mRadius); reflectionCanvas.drawPath(path, mShapePaint); } reflectionCanvas.restore(); reflectionCanvas.drawRect(0, 0, getWidth(), mRefHeight, mRefPaint); reflectionCanvas.restore(); // if (count2 > 0) { reflectionCanvas.restoreToCount(count2); } reflectionCanvas.restoreToCount(count); } /* * 设置/获取-圆角的角度. */ public void setRadius(float radius) { this.mRadius = radius; invalidate(); } }