淺談Kotlin實戰篇之自定義View圖片圓角簡單應用(一)

極客熊貓發表於2018-04-26

簡述: 關注我的Kotlin淺談系列文章的小夥伴就知道關於Kotlin語法篇的內容已經發布了一些。然後就會有小夥伴問了一直都在講語法是否來一波實戰了,畢竟一切一切的學習都是為了解決實際問題的,所以準備來一波Kotlin實戰篇,主要是用Kotlin來實現一些常見的功能和需求。實現同一個功能相比Java實現你也許會更鐘愛於使用kotlin。

  • 1、為什麼要用Kotlin去實現Android中的自定義View?
  • 2、為什麼要去自定義View實現圖片圓角定製化?
  • 3、實現該功能需要具備的知識
  • 4、圖片圓角定製化需要滿足哪些需求
  • 5、實現原理和思路分析
  • 6、自定義View中Java和Kotlin實現的對比
  • 7、具體的程式碼實現

一、為什麼要用Kotlin去實現Android中的自定義View?

針對這個問題的回答一般是給正在學習Kotlin或者Kotlin學習的新手而言,如果是剛剛學習Kotlin的時候,讓你去用Kotlin實現一個自定義View,可能會有些不習慣,比如在Kotlin定義View的構造器過載怎麼實現?是否要像Java暴露很多的set方法屬性給外部呼叫,然後重繪頁面呢?由於這是第一篇Kotlin實戰篇,也就比較簡單主要針對新手。

二、為什麼要去自定義View實現圖片圓角定製化?

實現圖片圓形和圓角這個需求有很多種方式,經過開發試驗最終比較穩的還是自定義View來實現。圖片圓形或者圓角在一些圖片載入框架中就整合好了,比如Glide中就有BitmapTransformation,開發者可以去繼承BitmapTransformation,然後去實現Bitmap繪製邏輯在圖片層面來達到圖片圓角或者圓形的效果。有兩點原因讓我放棄使用它:

第一,單從物件導向的角度,庫單一職責來說,圖片載入庫就是負責從網路源載入圖片的,至於這個ImageView長得什麼形狀,則是通過ImageView來呈現的。

第二, 使用自定義BitmapTransformation來定義形狀發現有bug,就是一張來自網路端的圖片當它沒有載入完成的時候是無法拿到圖片尺寸的,而在BitmapTransformation中需要拿到圖片寬和高。所以用post,Runnable機制等待載入完畢後就去定義形狀,這樣的實現在大部分場景是可以滿足的。但是在一個需要重新整理的列表中就會明顯發現,每次重新整理圖片去載入,圖片會有空白的過程很影響體驗。

三、實現該功能需要具備知識

  • 1、Kotlin中基本語法知識
  • 2、Kotlin中自定義屬性訪問器
  • 3、Kotlin中預設值引數實現構造器函式過載以及@JvmOverloads註解的使用
  • 4、Kotlin標準庫中常見的apply,run,with函式的使用
  • 5、Kotlin中預設值引數函式的使用
  • 6、自定義View的基本知識
  • 7、Path的使用
  • 8、Matrix的使用
  • 9、BitmapShader的使用

四、圖片圓角定製化需要滿足哪些需求

  • 1、支援圖片圓形的定製化
  • 2、支援圖片圓角以及每個角的X,Y方向值的定製化
  • 3、支援形狀邊框寬度顏色的定製化
  • 4、支援圖片圓角或者圓形右上角訊息圓點定製化(一般用於圓形或者圓角頭像)

五、自定義View實現的原理和思路分析

這個自定義View實現原理很簡單,主要有三個比較重要的點,第一就是構建定義圓角矩形的Path;第二是利用Matrix矩陣變換按比例縮小或放大使得圖片大小和ImageView大小保持一致;第三就是使用BitmapShader對已經定義好的path,用帶shader的畫筆進行渲染。

六、自定義View中Java和Kotlin實現的對比

  • 1、自定義View構造器過載對比

java實現,這樣的寫法是java實現自定義View常用套路

public class PrettyImageView extends ImageView {
    public PrettyImageView(Context context) {
        this(context, null);
    }

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

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

複製程式碼

kotlin實現,使用到了之前部落格講過的預設值引數實現函式過載以及使用@JvmOverloads註解是為了在Java中可以呼叫Kotlin中定義過載構造器方法。(這兩個知識點都是之前部落格有專門分析包括其原理)

class PrettyImageView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defAttrStyle: Int = 0)
	: ImageView(context, attributeSet, defAttrStyle) {
	    
	}
複製程式碼
  • 2、自定義View 屬性改變的setter暴露

java實現,需要實現對應屬性的setter方法,然後內部呼叫invalidate重繪

public class PrettyImageView extends ImageView {
    private boolean mIsShowBorder;
    private float mBorderWidth;
    private int mBorderColor;
    private boolean mIsShowDot;
    private float mDotRadius;
    private int mDotColor;

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

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

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

    public void setmIsShowBorder(boolean mIsShowBorder) {
        this.mIsShowBorder = mIsShowBorder;
        invalidate();
    }

    public void setmBorderWidth(float mBorderWidth) {
        this.mBorderWidth = mBorderWidth;
        invalidate();
    }

    public void setmBorderColor(int mBorderColor) {
        this.mBorderColor = mBorderColor;
        invalidate();
    }

    public void setmIsShowDot(boolean mIsShowDot) {
        this.mIsShowDot = mIsShowDot;
        invalidate();
    }

    public void setmDotRadius(float mDotRadius) {
        this.mDotRadius = mDotRadius;
        invalidate();
    }

    public void setmDotColor(int mDotColor) {
        this.mDotColor = mDotColor;
        invalidate();
    }
}
複製程式碼

Kotlin實現則不需要定義那麼多setter方法,因為Kotlin中var變數就自帶setter和getter方法,可以我們又想達到當重新改變值後需要呼叫invalidate函式。這是就需要用之前講過自定義變數訪問器。

class PrettyImageView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defAttrStyle: Int = 0)
	: ImageView(context, attributeSet, defAttrStyle) {
	private var mBorderWidth: Float = 20f
		set(value) {
			field = value
			invalidate()
		}
	private var mBorderColor: Int = Color.parseColor("#ff9900")
		set(value) {
			field = value
			invalidate()
		} 
			private var mShowBorder: Boolean = true
		set(value) {
			field = value
			invalidate()
		}
	private var mShowCircleDot: Boolean = false
		set(value) {
			field = value
			invalidate()
		}
	private var mCircleDotColor: Int = Color.RED
		set(value) {
			field = value
			invalidate()
		}

	private var mCircleDotRadius: Float = 20f
		set(value) {
			field = value
			invalidate()
		}
}
複製程式碼

七、具體程式碼實現

  • 1、開放的自定義View屬性
開放屬性name 開放屬性含義
shape_type 形狀型別,目前只有圓角和圓形兩種型別
left_top_radiusX 左上角X軸方向半徑
left_top_radiusY 左上角Y軸方向半徑
right_top_radiusX 右上角X軸方向半徑
right_top_radiusY 右上角Y軸方向半徑
right_bottom_radiusX 右下角X軸方向半徑
right_bottom_radiusY 右下角Y軸方向半徑
left_bottom_radiusX 左下角X軸方向半徑
left_bottom_radiusY 左下角Y軸方向半徑
show_border 是否顯示邊框
border_width 邊框寬度
border_color 邊框顏色
show_circle_dot 是否顯示右上角圓點
circle_dot_color 右上角圓點顏色
circle_dot_radius 右上角圓點半徑
  • 2、attrs.xml的定義宣告
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PrettyImageView">
        <attr name="shape_type">
            <enum name="SHAPE_CIRCLE" value="0"></enum>
            <enum name="SHAPE_ROUND" value="1"></enum>
        </attr>
        <attr name="border_width" format="dimension"/>
        <attr name="border_color" format="color"/>
        <attr name="left_top_radiusX" format="dimension"/>
        <attr name="left_top_radiusY" format="dimension"/>
        <attr name="right_top_radiusX" format="dimension"/>
        <attr name="right_top_radiusY" format="dimension"/>
        <attr name="right_bottom_radiusX" format="dimension"/>
        <attr name="right_bottom_radiusY" format="dimension"/>
        <attr name="left_bottom_radiusX" format="dimension"/>
        <attr name="left_bottom_radiusY" format="dimension"/>
        <attr name="show_border" format="boolean"/>
        <attr name="show_circle_dot" format="boolean"/>
        <attr name="circle_dot_color" format="color"/>
        <attr name="circle_dot_radius" format="dimension"/>
    </declare-styleable>
</resources>
複製程式碼
  • 3、具體實現程式碼
class PrettyImageView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defAttrStyle: Int = 0)
	: ImageView(context, attributeSet, defAttrStyle) {

	enum class ShapeType {
		SHAPE_CIRCLE,
		SHAPE_ROUND
	}

	//defAttr var
	private var mShapeType: ShapeType = ShapeType.SHAPE_CIRCLE
		set(value) {
			field = value
			invalidate()
		}
	private var mBorderWidth: Float = 20f
		set(value) {
			field = value
			invalidate()
		}
	private var mBorderColor: Int = Color.parseColor("#ff9900")
		set(value) {
			field = value
			invalidate()
		}

	private var mLeftTopRadiusX: Float = 0f
		set(value) {
			field = value
			invalidate()
		}
	private var mLeftTopRadiusY: Float = 0f
		set(value) {
			field = value
			invalidate()
		}
	private var mRightTopRadiusX: Float = 0f
		set(value) {
			field = value
			invalidate()
		}
	private var mRightTopRadiusY: Float = 0f
		set(value) {
			field = value
			invalidate()
		}
	private var mLeftBottomRadiusX: Float = 0f
		set(value) {
			field = value
			invalidate()
		}
	private var mLeftBottomRadiusY: Float = 0f
		set(value) {
			field = value
			invalidate()
		}
	private var mRightBottomRadiusX: Float = 0f
		set(value) {
			field = value
			invalidate()
		}
	private var mRightBottomRadiusY: Float = 0f
		set(value) {
			field = value
			invalidate()
		}
	private var mShowBorder: Boolean = true
		set(value) {
			field = value
			invalidate()
		}
	private var mShowCircleDot: Boolean = false
		set(value) {
			field = value
			invalidate()
		}
	private var mCircleDotColor: Int = Color.RED
		set(value) {
			field = value
			invalidate()
		}

	private var mCircleDotRadius: Float = 20f
		set(value) {
			field = value
			invalidate()
		}

	//drawTools var
	private lateinit var mShapePath: Path
	private lateinit var mBorderPath: Path
	private lateinit var mBitmapPaint: Paint
	private lateinit var mBorderPaint: Paint
	private lateinit var mCircleDotPaint: Paint
	private lateinit var mMatrix: Matrix

	//temp var
	private var mWidth: Int = 200//View的寬度
	private var mHeight: Int = 200//View的高度
	private var mRadius: Float = 100f//圓的半徑

	init {
		initAttrs(context, attributeSet, defAttrStyle)//獲取自定義屬性的值
		initDrawTools()//初始化繪製工具
	}

	private fun initAttrs(context: Context, attributeSet: AttributeSet?, defAttrStyle: Int) {
		val array = context.obtainStyledAttributes(attributeSet, R.styleable.PrettyImageView, defAttrStyle, 0)
		(0..array.indexCount)
				.asSequence()
				.map { array.getIndex(it) }
				.forEach {
					when (it) {
						R.styleable.PrettyImageView_shape_type ->
							mShapeType = when {
								array.getInt(it, 0) == 0 -> ShapeType.SHAPE_CIRCLE
								array.getInt(it, 0) == 1 -> ShapeType.SHAPE_ROUND
								else -> ShapeType.SHAPE_CIRCLE
							}
						R.styleable.PrettyImageView_border_width ->
							mBorderWidth = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4f, resources.displayMetrics))
						R.styleable.PrettyImageView_border_color ->
							mBorderColor = array.getColor(it, Color.parseColor("#ff0000"))
						R.styleable.PrettyImageView_left_top_radiusX ->
							mLeftTopRadiusX = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics))
						R.styleable.PrettyImageView_left_top_radiusY ->
							mLeftTopRadiusY = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics))
						R.styleable.PrettyImageView_left_bottom_radiusX ->
							mLeftBottomRadiusX = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics))
						R.styleable.PrettyImageView_left_bottom_radiusY ->
							mLeftBottomRadiusY = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics))
						R.styleable.PrettyImageView_right_bottom_radiusX ->
							mRightBottomRadiusX = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics))
						R.styleable.PrettyImageView_right_bottom_radiusY ->
							mRightBottomRadiusY = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics))
						R.styleable.PrettyImageView_right_top_radiusX ->
							mRightTopRadiusX = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics))
						R.styleable.PrettyImageView_right_top_radiusY ->
							mRightTopRadiusY = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics))
						R.styleable.PrettyImageView_show_border ->
							mShowBorder = array.getBoolean(it, false)
						R.styleable.PrettyImageView_show_circle_dot ->
							mShowCircleDot = array.getBoolean(it, false)
						R.styleable.PrettyImageView_circle_dot_color ->
							mCircleDotColor = array.getColor(it, Color.parseColor("#ff0000"))
						R.styleable.PrettyImageView_circle_dot_radius ->
							mCircleDotRadius = array.getDimension(it, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics))
					}
				}
		array.recycle()
	}

	private fun initDrawTools() {
		mBitmapPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {//最終繪製圖片的畫筆,需要設定BitmapShader著色器,從而實現把圖片繪製在不同形狀圖形上
			style = Paint.Style.FILL
		}
		mBorderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {//繪製邊框畫筆
			style = Paint.Style.STROKE
			color = mBorderColor
			strokeCap = Paint.Cap.ROUND
			strokeWidth = mBorderWidth
		}
		mCircleDotPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {//繪製右上角圓點畫筆
			style = Paint.Style.FILL
			color = mCircleDotColor
		}
		mShapePath = Path()//描述形狀輪廓的path路徑
		mBorderPath = Path()//描述圖片邊框輪廓的path路徑
		mMatrix = Matrix()//用於縮放圖片的矩陣
		scaleType = ScaleType.CENTER_CROP
	}

	override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {//View的測量
		super.onMeasure(widthMeasureSpec, heightMeasureSpec)
		if (mShapeType == ShapeType.SHAPE_CIRCLE) {
			mWidth = Math.min(measuredWidth, measuredHeight)
			mRadius = mWidth / 2.0f
			setMeasuredDimension(mWidth, mWidth)
		} else {
			mWidth = measuredWidth
			mHeight = measuredHeight
			setMeasuredDimension(mWidth, mHeight)
		}
	}

	override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {//確定了最終View的尺寸
		super.onSizeChanged(w, h, oldw, oldh)
		mBorderPath.reset()
		mShapePath.reset()
		when (mShapeType) {
			ShapeType.SHAPE_ROUND -> {
				mWidth = w
				mHeight = h
				buildRoundPath()
			}
			ShapeType.SHAPE_CIRCLE -> {
				buildCirclePath()
			}
		}
	}

	private fun buildCirclePath() {//構建圓形型別的Path路徑
		if (!mShowBorder) {//繪製不帶邊框的圓形實際上只需要把一個圓形扔進path即可
			mShapePath.addCircle(mRadius, mRadius, mRadius, Path.Direction.CW)
		} else {//繪製帶邊框的圓形需要把內部圓形和外部圓形邊框都要扔進path
			mShapePath.addCircle(mRadius, mRadius, mRadius - mBorderWidth, Path.Direction.CW)
			mBorderPath.addCircle(mRadius, mRadius, mRadius - mBorderWidth / 2.0f, Path.Direction.CW)
		}
	}

	private fun buildRoundPath() {//構建圓角型別的Path路徑
		if (!mShowBorder) {//繪製不帶邊框的圓角實際上只需要把一個圓角矩形扔進path即可
			floatArrayOf(mLeftTopRadiusX, mLeftTopRadiusY,
					mRightTopRadiusX, mRightTopRadiusY,
					mRightBottomRadiusX, mRightBottomRadiusY,
					mLeftBottomRadiusX, mLeftBottomRadiusY).run {
				mShapePath.addRoundRect(RectF(0f, 0f, mWidth.toFloat(), mHeight.toFloat()), this, Path.Direction.CW)
			}

		} else {//繪製帶邊框的圓角實際上只需要把一個圓角矩形和一個圓角矩形的變數都扔進path即可
			floatArrayOf(mLeftTopRadiusX - mBorderWidth / 2.0f, mLeftTopRadiusY - mBorderWidth / 2.0f,
					mRightTopRadiusX - mBorderWidth / 2.0f, mRightTopRadiusY - mBorderWidth / 2.0f,
					mRightBottomRadiusX - mBorderWidth / 2.0f, mRightBottomRadiusY - mBorderWidth / 2.0f,
					mLeftBottomRadiusX - mBorderWidth / 2.0f, mLeftBottomRadiusY - mBorderWidth / 2.0f).run {
				mBorderPath.addRoundRect(RectF(mBorderWidth / 2.0f, mBorderWidth / 2.0f, mWidth.toFloat() - mBorderWidth / 2.0f, mHeight.toFloat() - mBorderWidth / 2.0f), this, Path.Direction.CW)
			}

			floatArrayOf(mLeftTopRadiusX - mBorderWidth, mLeftTopRadiusY - mBorderWidth,
					mRightTopRadiusX - mBorderWidth, mRightTopRadiusY - mBorderWidth,
					mRightBottomRadiusX - mBorderWidth, mRightBottomRadiusY - mBorderWidth,
					mLeftBottomRadiusX - mBorderWidth, mLeftBottomRadiusY - mBorderWidth).run {
				mShapePath.addRoundRect(RectF(mBorderWidth, mBorderWidth, mWidth.toFloat() - mBorderWidth, mHeight.toFloat() - mBorderWidth),
						this, Path.Direction.CW)
			}

		}
	}

	override fun onDraw(canvas: Canvas?) {//由於經過以上根據不同邏輯構建了boderPath和shapePath,path中已經儲存相應的形狀,現在只需要把相應shapePath中形狀用帶BitmapShader畫筆繪製出來,boderPath用普通畫筆繪製出來即可
		drawable ?: return
		mBitmapPaint.shader = getBitmapShader()//獲得相應的BitmapShader著色器物件
		when (mShapeType) {
			ShapeType.SHAPE_CIRCLE -> {
				if (mShowBorder) {
					canvas?.drawPath(mBorderPath, mBorderPaint)//繪製圓形圖片邊框path
				}
				canvas?.drawPath(mShapePath, mBitmapPaint)//繪製圓形圖片形狀path
				if (mShowCircleDot) {
					drawCircleDot(canvas)//繪製圓形圖片右上角圓點
				}
			}
			ShapeType.SHAPE_ROUND -> {
				if (mShowBorder) {
					canvas?.drawPath(mBorderPath, mBorderPaint)//繪製圓角圖片邊框path
				}
				canvas?.drawPath(mShapePath, mBitmapPaint)//繪製圓角圖片形狀path
			}
		}

	}

	private fun drawCircleDot(canvas: Canvas?) {
		canvas?.run {
			drawCircle((mRadius + mRadius * (Math.sqrt(2.0) / 2.0f)).toFloat(), (mRadius - mRadius * (Math.sqrt(2.0) / 2.0f)).toFloat(), mCircleDotRadius, mCircleDotPaint)
		}
	}

	private fun getBitmapShader(): BitmapShader {
		val bitmap = drawableToBitmap(drawable)
		return BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP).apply {
			var scale = 1.0f
			if (mShapeType == ShapeType.SHAPE_CIRCLE) {
				scale = (mWidth * 1.0f / Math.min(bitmap.width, bitmap.height))
			} else if (mShapeType == ShapeType.SHAPE_ROUND) {
				// 如果圖片的寬或者高與view的寬高不匹配,計算出需要縮放的比例;縮放後的圖片的寬高,一定要大於我們view的寬高;所以我們這裡取大值;
				if (!(width == bitmap.width && width == bitmap.height)) {
					scale = Math.max(width * 1.0f / bitmap.width, height * 1.0f / bitmap.height)
				}
			}
			// shader的變換矩陣,我們這裡主要用於放大或者縮小
			mMatrix.setScale(scale, scale)
			setLocalMatrix(mMatrix)
		}
	}

	private fun drawableToBitmap(drawable: Drawable): Bitmap {
		if (drawable is BitmapDrawable) {
			return drawable.bitmap
		}
		return Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888).apply {
			drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
			drawable.draw(Canvas(this@apply))
		}
	}

	companion object {
		private const val STATE_INSTANCE = "state_instance"
		private const val STATE_INSTANCE_SHAPE_TYPE = "state_shape_type"
		private const val STATE_INSTANCE_BORDER_WIDTH = "state_border_width"
		private const val STATE_INSTANCE_BORDER_COLOR = "state_border_color"
		private const val STATE_INSTANCE_RADIUS_LEFT_TOP_X = "state_radius_left_top_x"
		private const val STATE_INSTANCE_RADIUS_LEFT_TOP_Y = "state_radius_left_top_y"
		private const val STATE_INSTANCE_RADIUS_LEFT_BOTTOM_X = "state_radius_left_bottom_x"
		private const val STATE_INSTANCE_RADIUS_LEFT_BOTTOM_Y = "state_radius_left_bottom_y"
		private const val STATE_INSTANCE_RADIUS_RIGHT_TOP_X = "state_radius_right_top_x"
		private const val STATE_INSTANCE_RADIUS_RIGHT_TOP_Y = "state_radius_right_top_y"
		private const val STATE_INSTANCE_RADIUS_RIGHT_BOTTOM_X = "state_radius_right_bottom_x"
		private const val STATE_INSTANCE_RADIUS_RIGHT_BOTTOM_Y = "state_radius_right_bottom_y"
		private const val STATE_INSTANCE_RADIUS = "state_radius"
		private const val STATE_INSTANCE_SHOW_BORDER = "state_radius_show_border"
	}

	//View State Save
	override fun onSaveInstanceState(): Parcelable = Bundle().apply {
		putParcelable(STATE_INSTANCE, super.onSaveInstanceState())
		putInt(STATE_INSTANCE_SHAPE_TYPE, when (mShapeType) {
			ShapeType.SHAPE_CIRCLE -> 0
			ShapeType.SHAPE_ROUND -> 1
		})
		putFloat(STATE_INSTANCE_BORDER_WIDTH, mBorderWidth)
		putInt(STATE_INSTANCE_BORDER_COLOR, mBorderColor)
		putFloat(STATE_INSTANCE_RADIUS_LEFT_TOP_X, mLeftTopRadiusX)
		putFloat(STATE_INSTANCE_RADIUS_LEFT_TOP_Y, mLeftTopRadiusY)
		putFloat(STATE_INSTANCE_RADIUS_LEFT_BOTTOM_X, mLeftBottomRadiusX)
		putFloat(STATE_INSTANCE_RADIUS_LEFT_BOTTOM_Y, mLeftBottomRadiusY)
		putFloat(STATE_INSTANCE_RADIUS_RIGHT_TOP_X, mRightTopRadiusX)
		putFloat(STATE_INSTANCE_RADIUS_RIGHT_TOP_Y, mRightTopRadiusY)
		putFloat(STATE_INSTANCE_RADIUS_RIGHT_BOTTOM_X, mRightBottomRadiusX)
		putFloat(STATE_INSTANCE_RADIUS_RIGHT_BOTTOM_Y, mRightBottomRadiusY)
		putFloat(STATE_INSTANCE_RADIUS, mRadius)
		putBoolean(STATE_INSTANCE_SHOW_BORDER, mShowBorder)
	}

	//View State Restore
	override fun onRestoreInstanceState(state: Parcelable?) {
		if (state !is Bundle) {
			super.onRestoreInstanceState(state)
			return
		}

		with(state) {
			super.onRestoreInstanceState(getParcelable(STATE_INSTANCE))
			mShapeType = when {
				getInt(STATE_INSTANCE_SHAPE_TYPE) == 0 -> ShapeType.SHAPE_CIRCLE
				getInt(STATE_INSTANCE_SHAPE_TYPE) == 1 -> ShapeType.SHAPE_ROUND
				else -> ShapeType.SHAPE_CIRCLE
			}
			mBorderWidth = getFloat(STATE_INSTANCE_BORDER_WIDTH)
			mBorderColor = getInt(STATE_INSTANCE_BORDER_COLOR)
			mLeftTopRadiusX = getFloat(STATE_INSTANCE_RADIUS_LEFT_TOP_X)
			mLeftTopRadiusY = getFloat(STATE_INSTANCE_RADIUS_LEFT_TOP_Y)
			mLeftBottomRadiusX = getFloat(STATE_INSTANCE_RADIUS_LEFT_BOTTOM_X)
			mLeftBottomRadiusY = getFloat(STATE_INSTANCE_RADIUS_LEFT_BOTTOM_Y)
			mRightTopRadiusX = getFloat(STATE_INSTANCE_RADIUS_RIGHT_TOP_X)
			mRightTopRadiusY = getFloat(STATE_INSTANCE_RADIUS_RIGHT_TOP_Y)
			mRightBottomRadiusX = getFloat(STATE_INSTANCE_RADIUS_RIGHT_BOTTOM_X)
			mRightBottomRadiusY = getFloat(STATE_INSTANCE_RADIUS_RIGHT_BOTTOM_Y)
			mRadius = getFloat(STATE_INSTANCE_RADIUS)
			mShowBorder = getBoolean(STATE_INSTANCE_SHOW_BORDER)
		}
	}
}
複製程式碼

專案GitHub地址

執行效果截圖:

淺談Kotlin實戰篇之自定義View圖片圓角簡單應用(一)

淺談Kotlin實戰篇之自定義View圖片圓角簡單應用(一)

歡迎關注Kotlin開發者聯盟,這裡有最新Kotlin技術文章,每週會不定期翻譯一篇Kotlin國外技術文章。如果你也喜歡Kotlin,歡迎加入我們~~~

相關文章