网话说UI设计有三宝 :透明,阴影,加圆角。不少UI在作设计的时候都喜欢作卡片形式,而后添加阴影。卡片UI确实挺好看,可是对Android开发者来讲,显示阴影却并不那么手到擒来,由于Android对阴影没有作出很好的支持。java
谷歌也许早就注意到了UI的三宝之一阴影,因而开发了一个继承FrameLayout的CardView公开发这使用,这个控件虽然在v7包里,可是须要单独添加依赖才能够使用,就好像不是亲生的似的!android
CardView本质上继承FrameLayout,须要添加依赖才能够使用:面试
compile 'com.android.support:cardview-v7:25.3.1'
复制代码
当你知道它继承FrameLayout的时候你就知道怎么使用了,可是这个CardView有不少局限性,好比不能修改阴影的颜色,不能修改阴影的深浅。这就很诡异了,根本没法知足UI设计潮流的心里。
那为了产品蒸蒸日上,早日走上人生巅峰,实现财富自由,应该如何让你的APP支持修改阴影的颜色呢?canvas
有个很暴力的办法,就是吧CardView的代码本身抠出来,而后本身定制.
可是如今我本身探索了一个新的较为简单的添加阴影的实现方案,仅供参考性能优化
思路:首先要明确阴影的实现思路是什么,其实就是颜色致使的视觉错觉。说白了就是在你的Card周围画一个渐变的体现立体感的颜色。
基于上述思路,咱们在一个在一个view上画一个矩形的图形,让他周围有渐变色的阴影便可。因而咱们想起几个API:bash
方法:paint.setShadowLayer能够给绘制的图形增长阴影,还能够设置阴影的颜色架构
如上图咱们,红色部分使咱们绘制的图形,边框之内,红色以外的是阴影的显示部分。
有了以上的知识点已经知道能够本身画阴影了,可是什么时机开始画呢?ide
咱们知道谷歌开发的CardView的控件继承了FrameLayout,方便咱们自由扩展。那么咱们也须要继承FrameLayout。性能
ShadowCardView继承FrameLayout以后,能够重写其一个方法:优化
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
}
复制代码
这个方法是ViewGroup在绘制子View的时候调用的,那么咱们能够在这个时候进行阴影的绘制。
首先,这个方法已经为咱们提供了这个View的画布:Canvas,咱们能够直接在上面进行阴影的绘制,代码以下:
Paint shadowPaint = new Paint();
shadowPaint.setColor(Color.RED);
shadowPaint.setStyle(Paint.Style.FILL);
shadowPaint.setAntiAlias(true);
float left = 45;
float top = 45;
float right = getWidth() - 45;
float bottom = getHeight() - 45;
shadowPaint.setShadowLayer(45, 0, 0, getContext().getResources().getColor(R.color.color_000000));
RectF rectF = new RectF(left, top, right, bottom);
canvas.drawRoundRect(rectF, 0, 0, shadowPaint);
canvas.save();
复制代码
这样阴影就能够成功绘制了,这个方法代码量不多,很简单,也很实用。
效果如上图
为了更好的封装,咱们能够为上面须要的参数进行定制,好比阴影的颜色,阴影的宽度,阴影的上下偏移,阴影的模糊度。
所有代码:
public class ShadowViewCard extends FrameLayout {
private static final int DEFAULT_VALUE_SHADOW_COLOR = R.color.shadow_default_color;
private static final int DEFAULT_VALUE_SHADOW_CARD_COLOR = R.color.shadow_card_default_color;
private static final int DEFAULT_VALUE_SHADOW_ROUND = 0;
private static final int DEFAULT_VALUE_SHADOW_RADIUS = 10;
private static final int DEFAULT_VALUE_SHADOW_TOP_HEIGHT = 5;
private static final int DEFAULT_VALUE_SHADOW_LEFT_HEIGHT = 5;
private static final int DEFAULT_VALUE_SHADOW_RIGHT_HEIGHT = 5;
private static final int DEFAULT_VALUE_SHADOW_BOTTOM_HEIGHT = 5;
private static final int DEFAULT_VALUE_SHADOW_OFFSET_Y = 0;
private static final int DEFAULT_VALUE_SHADOW_OFFSET_X = DEFAULT_VALUE_SHADOW_TOP_HEIGHT / 3;
private int shadowRound;
private int shadowColor;
private int shadowCardColor;
private int shadowRadius;
private int shadowOffsetY;
private int shadowOffsetX;
private int shadowTopHeight;
private int shadowLeftHeight;
private int shadowRightHeight;
private int shadowBottomHeight;
public ShadowViewCard(Context context) {
this(context, null);
}
public ShadowViewCard(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ShadowViewCard(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);
}
private void initView(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShadowViewCard);
shadowRound = a.getDimensionPixelSize(R.styleable.ShadowViewCard_shadowRound, DEFAULT_VALUE_SHADOW_ROUND);
shadowColor = a.getColor(R.styleable.ShadowViewCard_shadowColor, getResources().getColor(DEFAULT_VALUE_SHADOW_COLOR));
shadowCardColor = a.getColor(R.styleable.ShadowViewCard_shadowCardColor, getResources().getColor(DEFAULT_VALUE_SHADOW_CARD_COLOR));
shadowTopHeight = a.getDimensionPixelSize(R.styleable.ShadowViewCard_shadowTopHeight, dp2px(getContext(), DEFAULT_VALUE_SHADOW_TOP_HEIGHT));
shadowRightHeight = a.getDimensionPixelSize(R.styleable.ShadowViewCard_shadowRightHeight, dp2px(getContext(), DEFAULT_VALUE_SHADOW_RIGHT_HEIGHT));
shadowLeftHeight = a.getDimensionPixelSize(R.styleable.ShadowViewCard_shadowLeftHeight, dp2px(getContext(), DEFAULT_VALUE_SHADOW_LEFT_HEIGHT));
shadowBottomHeight = a.getDimensionPixelSize(R.styleable.ShadowViewCard_shadowBottomHeight, dp2px(getContext(), DEFAULT_VALUE_SHADOW_BOTTOM_HEIGHT));
shadowOffsetY = a.getDimensionPixelSize(R.styleable.ShadowViewCard_shadowOffsetY, dp2px(getContext(), DEFAULT_VALUE_SHADOW_OFFSET_Y));
shadowOffsetX = a.getDimensionPixelSize(R.styleable.ShadowViewCard_shadowOffsetX, dp2px(getContext(), DEFAULT_VALUE_SHADOW_OFFSET_X));
shadowRadius = a.getInteger(R.styleable.ShadowViewCard_shadowRadius, DEFAULT_VALUE_SHADOW_RADIUS);
a.recycle();
setPadding(shadowLeftHeight, shadowTopHeight, shadowRightHeight, shadowBottomHeight);
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
public static int dp2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
protected void dispatchDraw(Canvas canvas) {
Paint shadowPaint = new Paint();
shadowPaint.setColor(Color.WHITE);
shadowPaint.setStyle(Paint.Style.FILL);
shadowPaint.setAntiAlias(true);
float left = shadowLeftHeight;
float top = shadowTopHeight;
float right = getWidth() - shadowRightHeight;
float bottom = getHeight() - shadowBottomHeight;
shadowPaint.setShadowLayer(shadowRadius, shadowOffsetX, shadowOffsetX, shadowColor);
RectF rectF = new RectF(left, top, right, bottom);
canvas.drawRoundRect(rectF, shadowRound, shadowRound, shadowPaint);
canvas.save();
super.dispatchDraw(canvas);
}
}
复制代码
attr.xml
<declare-styleable name="ShadowViewCard">
<!--圆角度-->
<attr name="shadowRound" format="dimension" />
<!--阴影的高度-->
<attr name="shadowLeftHeight" format="dimension" />
<attr name="shadowTopHeight" format="dimension" />
<attr name="shadowRightHeight" format="dimension" />
<attr name="shadowBottomHeight" format="dimension" />
<!--上方阴影的偏离度-->
<attr name="shadowOffsetY" format="dimension" />
<attr name="shadowOffsetX" format="dimension" />
<!--阴影颜色-->
<attr name="shadowCardColor" format="color" />
<attr name="shadowColor" format="color" />
<!--阴影颜色模糊度,越大越模糊-->
<attr name="shadowRadius" format="integer" />
</declare-styleable>
复制代码
color.xml
<color name="shadow_default_color">#1a000000</color>
<color name="shadow_card_default_color">#ffffff</color>复制代码
喜欢个人系列性文章的话,动动你的小手指点个赞的
最后送福利了,如今关注我而且加入群聊能够获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,天天只需花上十几分钟阅读便可。
你们能够跟我一块儿探讨,欢迎加群探讨,有flutter—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿~
群号:925019412