簡單好用的陰影庫 ShadowLayout

在開發過程當中常會碰見帶陰影效果的控件,經過 SDK 提供的 CardViewandroid:elevation 能夠實現,也能夠經過 .9 圖實現。可是使用這兩種方法會有一些弊端,好比:不能夠控制陰影顏色,若是使用 .9 圖片過多,會增長 APK 安裝文件的體積。針對以上問題,本身寫了一個爲控件添加陰影的庫 ---- ShadowLayout。接下來就 ShadowLayout 展開本文,本文主要分爲如下兩個部分:java

  1. 關於 ShadowLayout 的使用;
  2. 關於 ShadowLayout 的原理。

關於 ShadowLayout 的使用

先來看一張使用 ShadowLayout 庫實現的各類陰影的效果圖,以下圖所示:android

如上圖所示,經過使用 ShadowLayout 能夠控制陰影的顏色、範圍、顯示邊界(上下左右四個邊界)、x 軸和 y 軸的偏移量。git

添加依賴

Gradle:github

compile 'com.lijiankun24:shadowlayout:1.0.0'複製代碼

Maven:canvas

<dependency>
      <groupId>com.lijiankun24</groupId>
      <artifactId>shadowlayout</artifactId>
      <version>1.0.0</version>
      <type>pom</type>
    </dependency>複製代碼

如何使用

在 xml 中添加以下佈局文件:app

<com.lijiankun24.shadowlayout.ShadowLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="30dp" app:shadowColor="#66000000" app:shadowDx="0dp" app:shadowDy="3dp" app:shadowRadius="10dp" app:shadowSide="all">

        <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/white" android:contentDescription="@null" android:src="@mipmap/ic_launcher"/>
    </com.lijiankun24.shadowlayout.ShadowLayout>複製代碼

上面 xml 佈局文件實現的效果以下圖所示:ide

如上面 xml 中代碼顯示的那樣,總共有 5 個自定義屬性,其含義分別以下:佈局

  • app:shadowColor="#66000000" 控制陰影的顏色,注意:顏色必須帶有透明度的值
  • app:shadowDx="0dp" 控制陰影 x 軸的偏移量
  • app:shadowDy="3dp" 控制陰影 y 軸的偏移量
  • app:shadowRadius="10dp" 控制陰影的範圍
  • app:shadowSide="all|left|right|top|bottom" 控制陰影顯示的邊界,共有五個值

關於 ShadowLayout 的原理

ShadowLayout 的原理其實很是簡單,大概能夠分爲如下幾步:this

  1. 經過自定義屬性獲取陰影的相關屬性,包括:陰影顏色、陰影範圍大小、陰影顯示邊界、陰影 x 軸和 y 軸的偏移量;
  2. onLayout() 方法中獲取到陰影應該顯示的範圍,並設置此 ShadowLayoutPadding 值以給陰影的顯示留出空間;
  3. onDraw() 方法中使用 CanvasPaint 的方法繪製陰影。

    繪製陰影最重要的兩個方法:spa

    • Paint.setShadowLayer(float radius, float dx, float dy, int shadowColor) 設置陰影的大小、顏色、x 軸和 y 軸的偏移量
    • canvas.drawRect(RectF rect, Paint paint) 設置陰影顯示的位置

ShadowLayout 庫中只有一個文件 ---- ShadowLayout.javaShadowLayoutRelativeLayout 的子類,其源碼以下所示(有較爲詳細的註釋):

/** * ShadowLayout.java * <p> * Created by lijiankun on 17/8/11. */

public class ShadowLayout extends RelativeLayout {

    public static final int ALL = 0x1111;

    public static final int LEFT = 0x0001;

    public static final int TOP = 0x0010;

    public static final int RIGHT = 0x0100;

    public static final int BOTTOM = 0x1000;

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

    private RectF mRectF = new RectF();

    /** * 陰影的顏色 */
    private int mShadowColor = Color.TRANSPARENT;

    /** * 陰影的大小範圍 */
    private float mShadowRadius = 0;

    /** * 陰影 x 軸的偏移量 */
    private float mShadowDx = 0;

    /** * 陰影 y 軸的偏移量 */
    private float mShadowDy = 0;

    /** * 陰影顯示的邊界 */
    private int mShadowSide = ALL;

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

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

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

    /** * 獲取繪製陰影的位置,併爲 ShadowLayout 設置 Padding 覺得顯示陰影留出空間 */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        float effect = mShadowRadius + dip2px(5);
        float rectLeft = 0;
        float rectTop = 0;
        float rectRight = this.getWidth();
        float rectBottom = this.getHeight();
        int paddingLeft = 0;
        int paddingTop = 0;
        int paddingRight = 0;
        int paddingBottom = 0;

        if (((mShadowSide & LEFT) == LEFT)) {
            rectLeft = effect;
            paddingLeft = (int) effect;
        }
        if (((mShadowSide & TOP) == TOP)) {
            rectTop = effect;
            paddingTop = (int) effect;
        }
        if (((mShadowSide & RIGHT) == RIGHT)) {
            rectRight = this.getWidth() - effect;
            paddingRight = (int) effect;
        }
        if (((mShadowSide & BOTTOM) == BOTTOM)) {
            rectBottom = this.getHeight() - effect;
            paddingBottom = (int) effect;
        }
        if (mShadowDy != 0.0f) {
            rectBottom = rectBottom - mShadowDy;
            paddingBottom = paddingBottom + (int) mShadowDy;
        }
        if (mShadowDx != 0.0f) {
            rectRight = rectRight - mShadowDx;
            paddingRight = paddingRight + (int) mShadowDx;
        }
        mRectF.left = rectLeft;
        mRectF.top = rectTop;
        mRectF.right = rectRight;
        mRectF.bottom = rectBottom;
        this.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
    }

    /** * 真正繪製陰影的方法 */
    @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);  // 關閉硬件加速
        this.setWillNotDraw(false);                    // 調用此方法後,纔會執行 onDraw(Canvas) 方法

        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ShadowLayout);
        if (typedArray != null) {
            mShadowColor = typedArray.getColor(R.styleable.ShadowLayout_shadowColor,
                    ContextCompat.getColor(getContext(), android.R.color.black));
            mShadowRadius = typedArray.getDimension(R.styleable.ShadowLayout_shadowRadius, dip2px(0));
            mShadowDx = typedArray.getDimension(R.styleable.ShadowLayout_shadowDx, dip2px(0));
            mShadowDy = typedArray.getDimension(R.styleable.ShadowLayout_shadowDy, dip2px(0));
            mShadowSide = typedArray.getInt(R.styleable.ShadowLayout_shadowSide, ALL);
            typedArray.recycle();
        }
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.TRANSPARENT);
        mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
    }

    /** * dip2px dp 值轉 px 值 * * @param dpValue dp 值 * @return px 值 */
    private float dip2px(float dpValue) {
        DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
        float scale = dm.density;
        return (dpValue * scale + 0.5F);
    }
}複製代碼

至此,關於 ShadowLayout 庫的使用方法和原理至此所有介紹完畢,庫在 GitHub 上 ShadowLayout,歡迎 star 和 fork,也歡迎經過下面二維碼下載 APK 體驗,若是有什麼問題歡迎指出。個人工做郵箱:jiankunli24@gmail.com


參考資料:

Android 視圖高度和陰影的那點事兒 -- 亦楓

ShadowViewHelper -- 每天_byconan

相關文章
相關標籤/搜索