简述: 关注我的Kotlin浅谈系列文章的小伙伴就知道关于Kotlin语法篇的内容已经发布了一些。然后就会有小伙伴问了一直都在讲语法是否来一波实战了,毕竟一切一切的学习都是为了解决实际问题的,所以准备来一波Kotlin实战篇,主要是用Kotlin来实现一些常见的功能和需求。实现同一个功能相比Java实现你也许会更钟爱于使用kotlin。
针对这个问题的回答一般是给正在学习Kotlin或者Kotlin学习的新手而言,如果是刚刚学习Kotlin的时候,让你去用Kotlin实现一个自定义View,可能会有些不习惯,比如在Kotlin定义View的构造器重载怎么实现?是否要像Java暴露很多的set方法属性给外部调用,然后重绘页面呢?由于这是第一篇Kotlin实战篇,也就比较简单主要针对新手。
实现图片圆形和圆角这个需求有很多种方式,经过开发试验最终比较稳的还是自定义View来实现。图片圆形或者圆角在一些图片加载框架中就集成好了,比如Glide中就有BitmapTransformation,开发者可以去继承BitmapTransformation,然后去实现Bitmap绘制逻辑在图片层面来达到图片圆角或者圆形的效果。有两点原因让我放弃使用它:
第一,单从面向对象的角度,库单一职责来说,图片加载库就是负责从网络源加载图片的,至于这个ImageView长得什么形状,则是通过ImageView来呈现的。
第二, 使用自定义BitmapTransformation来定义形状发现有bug,就是一张来自网络端的图片当它没有加载完成的时候是无法拿到图片尺寸的,而在BitmapTransformation中需要拿到图片宽和高。所以用post,Runnable机制等待加载完毕后就去定义形状,这样的实现在大部分场景是可以满足的。但是在一个需要刷新的列表中就会明显发现,每次刷新图片去加载,图片会有空白的过程很影响体验。
这个自定义View实现原理很简单,主要有三个比较重要的点,第一就是构建定义圆角矩形的Path;第二是利用Matrix矩阵变换按比例缩小或放大使得图片大小和ImageView大小保持一致;第三就是使用BitmapShader对已经定义好的path,用带shader的画笔进行渲染。
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) {
}
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()
}
}
开放属性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 | 右上角圆点半径 |
<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>
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)
}
}
}
运行效果截图:
欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不定期翻译一篇Kotlin国外技术文章。如果你也喜欢Kotlin,欢迎加入我们~~~
Kotlin邂逅设计模式系列:
数据结构与算法系列:
翻译系列:
原创系列:
Effective Kotlin翻译系列
实战系列: