26
2017
09

Android之控件阴影模糊效果死磕Paint.setShadowLayer()

IOS界面那么好看,不服气呀,android应该也是支持模糊阴影的, Paint.setShadowLayer官方文档
上一篇可以实现阴影,但是没有模糊效果,本文加上模糊效果
本文只是一个测试代码,属于抛砖引玉的,读者可以通过修改完善源码实现更好的效果

Paint.setShadowLayer是支持设置阴影的,不过有几个坑

  • 需要关闭硬件加速,否则没有效果
  • 阴影的颜色是需要带透明度的,否则没有效果

思路

  • 比如你有一个 200dp*100dp的矩形空间需要显示边界阴影,先通过计算布局大小,给这个区域设置padding, 这个padding的目的是给阴影留下位置,阴影区域的大小是原空间的大小,给出的代码是一个ViewGroup,可以直接使用
  • 效果还是可以的这里写图片描述 (测试代码,只是右侧阴影和底部多一点阴影)
  • 使用方法, 里面可以放任意的布局,是一个FrameLayout
<XXX.XXX.XXX.ShadowLayout2
     android:layout_width="wrap_content"
     android:layout_height="wrap_content">
     <TextView
         android:layout_width="200dp"
         android:layout_height="100dp"
         android:background="#6c6b6b"
         android:gravity="center"
         android:text="测试阴影"/>
 </XXX.XXX.XXX.ShadowLayout2>

附上全部代码

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import com.benqu.wuta.R;

/** * 控件阴影效果,只是最外层空间加阴影,不处理内部子控件 * 使用 Paint.setShadowLayer(),这是一个Layout, * 思路: 比如你有一个 200dp*100dp的矩形空间需要显示边界阴影,先通过计算onLayout,给这个区域设置padding * 这个padding的目的是给阴影留下位置,阴影区域的大小是原空间的大小 * 这里默认处理的是设置右边和下边的模糊阴影 * 当然,通过修改onDraw 里的代码,可以实现圆形边界阴影,圆角矩形边界阴影,Bitmap的边界阴影等等。。。 * Created by slack * on 17/9/25 下午1:28 */

public class ShadowLayout2 extends FrameLayout {

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private RectF mRectF = new RectF();

    /** * 阴影的颜色, 需要带透明 */
    private int mShadowColor = Color.argb(128, 249, 94, 94);

    /** * 阴影的大小范围 radius越大越模糊,越小越清晰 */
    private float mShadowRadius = 10;

    /** * 阴影的宽度,比如底部的阴影,那就是底部阴影的高度 */
    private float mShadowWidth = 15;

    /** * 阴影 x 轴的偏移量, 计算padding时需要计算在内 */
    private float mShadowDx = 0;

    /** * 阴影 y 轴的偏移量,计算padding时需要计算在内,比如想底部的阴影多一些,这个设置值就可以了 */
    private float mShadowDy = 10;

    public ShadowLayout2(Context context) {
        this(context, null);
    }

    public ShadowLayout2(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ShadowLayout2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }


    /** * 为 ShadowLayout 设置 Padding 以为显示阴影留出空间 */
    private void resetShadowPadding() {
        float rectLeft = 0;
        float rectTop = 0;
        float rectRight = 0;
        float rectBottom = 0;
        int paddingLeft = 0;
        int paddingTop = 0;
        int paddingRight = 0;
        int paddingBottom = 0;

        // todo 测试代码,待完善,暂时是右侧和底部的阴影
        rectRight = this.getWidth() - mShadowWidth - mShadowDx;
        paddingRight = (int) mShadowWidth + (int) mShadowDx;

        rectBottom = this.getHeight() - mShadowWidth - mShadowDy;
        paddingBottom = (int) mShadowWidth  + (int) mShadowDy;

        mRectF.left = rectLeft;
        mRectF.top = rectTop;
        mRectF.right = rectRight;
        mRectF.bottom = rectBottom;
        this.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
    }

    /** * 决定View在ViewGroup中的位置 , 此处left ,top...是相对于父视图 */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        resetShadowPadding();
    }

    /** * 决定View的大小 */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /** * 如何绘制这个View, 真正绘制阴影的方法 */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(mRectF, mPaint);
    }

    /** * 读取设置的阴影的属性 * * @param attrs 从其中获取设置的值 */
    private void init(AttributeSet attrs) {
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);  // 关闭硬件加速,setShadowLayer 才会有效
        this.setWillNotDraw(false);                    // 调用此方法后,才会执行 onDraw(Canvas) 方法
        // todo 从AttributeSet获取设置的值
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.TRANSPARENT);
        mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
    }

}
上一篇:Toolbar使用详解 下一篇:Android解析ClassLoader(一)Java中的ClassLoader