提高开发效率!5个对开发者有用的Kotlin扩展函数-灵析社区

IT大鲨鱼

Kotlin 中扩展函数是一种允许在已有的类中添加新函数,而无需修改类定义或继承该类。通过使用扩展函数,我们可以轻松地为现有代码添加新功能和增强功能,下面就列举几个有用的扩展函数。

runCatching代替try catch

· try catch 方式:

try {   100 / 0} catch (ex: Throwable) {   ex.printStackTrace()}

· runCatching 方式:

runCatching { 100 / 0 }   .onFailure { ex -> ex.printStackTrace() }

如果不关心返回值,到这里就结束了,使用起来是不是更简单一些。如果需要继续对lambda表达式中的计算结果进行处理,那么继续往下看。

runCatching是在Kotlin 1.3版本新增的,看下源码:

@InlineOnly@SinceKotlin("1.3")public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> {    return try {        Result.success(block())    } catch (e: Throwable) {        Result.failure(e)    }}

可以看到runCatching函数是一个扩展函数,函数接受一个lambda表达式block作为参数,并在T对象上执行这个lambda表达式,函数内部帮我们添加了try catch。

· 如果lambda表达式成功执行并返回结果,则使用Result.success将结果包装成Result类型并返回;

· 如果出现异常,则使用Result.failure将异常包装成Result类型并返回。

看下 Result 里都有什么:

列举一些Result中的常用函数:

runCatching { 100 / 0 }   .onSuccess { value -> log("onSuccess:$value") } //runCatching{}中执行成功,并传入执行结果   .onFailure { exception -> log("onFailure:$exception") } //runCatching{}中执行失败,并传入exception   //.getOrDefault(0) //获取runCatching{}中执行的结果,如果是Failure直接返回默认值   .getOrElse { ex -> //获取runCatching{}中执行的结果,如果是Failure返回else内部的值。相比getOrDefault多了对exception的处理       log("exception:$ex")       100    }    //.getOrThrow()//获取runCatching{}中执行的结果,如果是Failure直接抛异常    //.getOrNull() //获取runCatching{}中执行的结果,如果是Failure返回null    //.exceptionOrNull() //如果有问题则返回exception;否则返回null    .run {       log("result:$this")    }

执行结果:

E/TTT: onFailure:java.lang.ArithmeticException: divide by zeroE/TTT: exception:java.lang.ArithmeticException: divide by zeroE/TTT: result:100

虽然100/0抛出了异常,还是可以通过getOrElse中重新赋值,并最终把值输出出来,如果需要其他处理,可以使用上述示例中的其他函数,按需使用即可。

如果改为runCatching { 100 / 2 },其他代码不变,则输出结果:

E/TTT: onSuccess:50E/TTT: result:50

View的可见性

fun View?.visible() {    if (this?.visibility != View.VISIBLE) {        this?.visibility = View.VISIBLE    }}fun View?.invisible() {    if (this?.visibility != View.INVISIBLE) {        this?.visibility = View.INVISIBLE    }}fun View?.gone() {    if (this?.visibility != View.GONE) {        this?.visibility = View.GONE    }}

使用它们:

val toolbar: Toolbar = findViewById(R.id.toolbar)toolbar.visible() //设置visibletoolbar.invisible() //设置invisibletoolbar.gone()  //设置gone

dp、sp、px之间相互转换

//dp转pxfun Number.dp2px(): Int {    return ScreenUtil.dp2px(MyApplication.getApplication(), toFloat())}//sp转pxfun Number.sp2px(): Int {    return ScreenUtil.sp2px(MyApplication.getApplication(), toFloat())}//px转dpfun Number.px2dp(): Int {    return ScreenUtil.px2dp(MyApplication.getApplication(), toFloat())}//px转spfun Number.px2sp(): Int {    return ScreenUtil.px2sp(MyApplication.getApplication(), toFloat())}object ScreenUtil {    fun dp2px(@NonNull context: Context, dp: Float): Int {        val scale = context.resources.displayMetrics.density        return (dp * scale + 0.5f).toInt()    }    fun px2dp(@NonNull context: Context, px: Float): Int {        val scale = context.resources.displayMetrics.density        return (px / scale + 0.5f).toInt()    }    fun sp2px(@NonNull context: Context, spValue: Float): Int {        val fontScale = context.resources.displayMetrics.scaledDensity        return (spValue * fontScale + 0.5f).toInt()    }    fun px2sp(@NonNull context: Context, pxValue: Float): Int {        val fontScale = context.resources.displayMetrics.scaledDensity        return (pxValue / fontScale + 0.5f).toInt()    }}

使用它们:

100.dp2px()100.sp2px()100.px2dp()100.px2sp()

by lazy 替代findViewById

by lazy是属性延迟委托,关于委托机制的用法参见:Kotlin | 10分钟搞定by委托机制

fun <T : View> Activity.id(id: Int) = lazy {    findViewById<T>(id)}

Activity中使用:

class DemoActivity : AppCompatActivity() {     private val mToolBar: Toolbar by id(R.id.toolbar)     override fun onCreate(savedInstanceState: Bundle?) {          super.onCreate(savedInstanceState)          setContentView(R.layout.activity_xxx)    }}

通过by lazy简化了控件的创建流程,避免每次创建都去调用findViewById(id),跟Butterknife的用法很类似。

如果是在Fragment中使用呢?首先Fragment中并没有findViewById(id)函数,所以需要稍微改造一下:

interface IRootView {    fun rootView(): View}//注意,这里声明的是IRootView的扩展函数fun <T : View> IRootView.id(id: Int) = lazy {    this.rootView().findViewById<T>(id)}abstract class BaseFragment : Fragment(), IRootView {    private var mRootView: View? = null    override fun onCreateView(        inflater: LayoutInflater,        container: ViewGroup?,        savedInstanceState: Bundle?    ): View? {        if (mRootView == null) {            mRootView = inflater.inflate(getLayoutId(), container, false)        }        return mRootView    }    override fun rootView(): View {        return mRootView!!    }    @LayoutRes    abstract fun getLayoutId(): Int}

· IRootView接口中只有一个rootView()方法,返回类型为android.view.View。

· 扩展函数id<T : View>()是针对实现IRootView的对象进行扩展的。该函数需要传入Int类型参数表示控件ID,在调用时会使用lazy委托模式延迟初始化并返回T类型(泛型)控件。

· BaseFragment继承自Fragment并且实现了IRootview接口。同时其内部也维护着mRootview变量用于缓存视图,在 onCreateView 方法中创建视图,并将其保存到变量mRootview中以便后面复用。

子类Fragment中使用:

class DemoFragment : BaseFragment() {    private val mToolBar: Toolbar by id(R.id.toolbar)    override fun getLayoutId(): Int = R.layout.fragment_demo    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {        super.onViewCreated(view, savedInstanceState)        mToolBar.xxx //可以直接使用了    }}

Toast、Log

fun Activity.showToast(msg: String, duration: Int = Toast.LENGTH_SHORT) {    Toast.makeText(this, msg, duration).show()}fun Activity.showToast(@StringRes msg: Int, duration: Int = Toast.LENGTH_SHORT) {    Toast.makeText(this, msg, duration).show()}fun Fragment.showToast(msg: String, duration: Int = Toast.LENGTH_SHORT) {    Toast.makeText(requireContext(), msg, duration).show()}fun Fragment.showToast(@StringRes message: Int, duration: Int = Toast.LENGTH_SHORT) {    Toast.makeText(requireContext(), message, duration).show()}fun log(msg: String, tag: String = "TAG") {    if (!BuildConfig.DEBUG) return    Log.d(tag, msg)}

使用它:

showToast(R.string.action_settings) //1showToast("棒棒哒", Toast.LENGTH_LONG) //2log("log展示") //

阅读量:1090

点赞量:0

收藏量:0