再见二丁目 | yitimo的个人日志

再见二丁目

Android中使用帶圓角佈局

发布于: 2018-08-06 09:08

此文章久未修订,请自行甄别内容准确性。

在Android中個人認爲有幾個支持不足的能力,其中就包括圓角陰影這兩個。先説一個在Web中再平常不過的需求——實現一個帶圓角的圖片輪播。筆者不才,先後試了shape方式、CardView方式、直接裁剪圖片方式,均達不到預想的效果,表現分別如下:

自定義View并切割方式

最終達到筆者想要效果的只有一種方式,即自定義一個View容器,可以繼承想要的佈局,然後在這個View中對最終的效果進行圓角切割。代碼非常簡短,所以直接貼出:

class RoundedCornerLayout(context: Context, attributeSet: AttributeSet?): FrameLayout(context, attributeSet) {
    private var _cornerRadius = 10.0f
    private var cornerRadius: Float = 0.toFloat()
    init {
        val ta = context.obtainStyledAttributes(attributeSet, R.styleable.RoundedCornerLayout, 0, 0)
        try {
            _cornerRadius = ta.getDimension(R.styleable.RoundedCornerLayout_corner, 10.0f)
        } finally {
            ta.recycle()
        }

        val metrics = context.resources.displayMetrics
        cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, _cornerRadius, metrics)
        setLayerType(View.LAYER_TYPE_SOFTWARE, null)
    }
    override fun dispatchDraw(canvas: Canvas) {
        val count = canvas.save()

        val path = Path()
        path.addRoundRect(RectF(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat()), cornerRadius, cornerRadius, Path.Direction.CW)
        canvas.clipPath(path)
        super.dispatchDraw(canvas)
        canvas.restoreToCount(count)
    }
}

這裏在類中定義了默認的圓角弧度,並支持在layout中傳入,做的事情也很簡單,重寫dispatchDraw對畫布進行圓角切割,這樣得到的圓角容器,裏面的子組件就再超不出圓角了(其實還是超出了,但是超出部分被切掉了)。

總結

對於網上許多不建議自定義視圖並切割,而是使用新版本引入的CardView能力的言論,筆者實在無才讓子組件不超出CardView的圓角,而筆者的需求也很簡單,僅僅是要一個圓角的輪播圖片組件罷了,相比切割每一張輪播圖片,肯定是切割容器在性能與效果上會好很多,客觀來説此種方式犧牲的有以下幾點:

  1. 潛在的相對的性能開支(目前沒發現)
  2. 不支持邊框
  3. 設置陰影會發現陰影是按照矩形輪廓來呈現的,需要另作適配

參考鏈接

但這在web中就是個overflow:hidden + border-radius的事情啊拜托。