Kotlin的拓展功能

650 阅读6分钟

说实话这个我是最近才知道的 虽然学习kotlin还没有多长时间 但是我已经深深的被他吸引了 进入正题

IntentExt


/**
 * 跳转Activity
 */
inline fun <reified T : Activity> Context.startActivity(vararg params: Pair<String, Any?>) =
    internalStartActivity(this, T::class.java, params)

/**
 * 跳转Activity并且请求结果
 */
inline fun <reified T : Activity> Activity.startActivityForResult(vararg params: Pair<String, Any?>, requestCode: Int) =
    internalStartActivityForResult(this, T::class.java, params, requestCode)

/**
 * 关闭当前Activity并且附带结果
 */
fun Activity.finishActivityWithResult(vararg params: Pair<String, Any?>) {
    val intent = Intent()
    fillIntentArguments(intent, params)
    setResult(RESULT_OK, intent)
    finish()
}

/**
 * 跳转Activity
 */
inline fun <reified T : Activity> Fragment.startActivity(vararg params: Pair<String, Any?>) =
    internalStartActivity(activity!!, T::class.java, params)

/**
 * 跳转Activity并且请求结果
 */
inline fun <reified T : Activity> Fragment.startActivityForResult(vararg params: Pair<String, Any?>, requestCode: Int) =
    internalStartActivityForResult(this, T::class.java, params, requestCode)


// ---------------------------------------- 一般来说不要使用下面的方法,可以对下面的方法进行一次封装来使用 ----------------------------------------

fun internalStartActivity(context: Context, activity: Class<out Activity>, params: Array<out Pair<String, Any?>>) =
    context.startActivity(createIntent(context, activity, params))

fun internalStartActivityForResult(
    activity: Activity,
    clazz: Class<out Activity>,
    params: Array<out Pair<String, Any?>>,
    requestCode: Int
) =
    activity.startActivityForResult(createIntent(activity, clazz, params), requestCode)

fun internalStartActivityForResult(
    fragment: Fragment,
    clazz: Class<out Activity>,
    params: Array<out Pair<String, Any?>>,
    requestCode: Int
) =
    fragment.startActivityForResult(createIntent(fragment.activity!!, clazz, params), requestCode)

fun <T> createIntent(context: Context, clazz: Class<out T>, params: Array<out Pair<String, Any?>>): Intent {
    val intent = Intent(context, clazz)
    if (params.isNotEmpty()) fillIntentArguments(intent, params)
    return intent
}

fun fillIntentArguments(intent: Intent, params: Array<out Pair<String, Any?>>) {
    params.forEach {
        when (val value = it.second) {
            null -> intent.putExtra(it.first, null as Serializable?)
            is Int -> intent.putExtra(it.first, value)
            is Long -> intent.putExtra(it.first, value)
            is CharSequence -> intent.putExtra(it.first, value)
            is String -> intent.putExtra(it.first, value)
            is Float -> intent.putExtra(it.first, value)
            is Double -> intent.putExtra(it.first, value)
            is Char -> intent.putExtra(it.first, value)
            is Short -> intent.putExtra(it.first, value)
            is Boolean -> intent.putExtra(it.first, value)
            is Serializable -> intent.putExtra(it.first, value)
            is Bundle -> intent.putExtra(it.first, value)
            is Parcelable -> intent.putExtra(it.first, value)
            is Array<*> -> when {
                value.isArrayOf<CharSequence>() -> intent.putExtra(it.first, value)
                value.isArrayOf<String>() -> intent.putExtra(it.first, value)
                value.isArrayOf<Parcelable>() -> intent.putExtra(it.first, value)
            }
            is IntArray -> intent.putExtra(it.first, value)
            is LongArray -> intent.putExtra(it.first, value)
            is FloatArray -> intent.putExtra(it.first, value)
            is DoubleArray -> intent.putExtra(it.first, value)
            is CharArray -> intent.putExtra(it.first, value)
            is ShortArray -> intent.putExtra(it.first, value)
            is BooleanArray -> intent.putExtra(it.first, value)
        }
        return@forEach
    }
}

ResourceExt


/**
 * 从Context中获取资源
 */
fun color(id: Int): Int = ContextCompat.getColor(BaseApp.app.applicationContext, id)

fun string(id: Int): String = BaseApp.app.applicationContext.resources.getString(id)

fun stringArray(id: Int): Array<String> =
    BaseApp.app.applicationContext.resources.getStringArray(id)

fun drawable(id: Int) = ContextCompat.getDrawable(BaseApp.app.applicationContext, id)

fun dimen(id: Int) = BaseApp.app.applicationContext.resources.getDimension(id)

fun dp2px(dp: Float): Int =
    (dp * BaseApp.app.applicationContext.resources.displayMetrics.density + 0.5f).toInt()

fun px2dp(px: Float): Int =
    (px / BaseApp.app.applicationContext.resources.displayMetrics.density + 0.5f).toInt()

fun sp2px(sp: Float): Int =
    (sp * BaseApp.app.applicationContext.resources.displayMetrics.scaledDensity + 0.5f).toInt()

fun px2sp(px: Float): Int =
    (px / BaseApp.app.applicationContext.resources.displayMetrics.scaledDensity + 0.5f).toInt()

fun Context.inflaterView(@LayoutRes layoutRes: Int, parent: ViewGroup? = null): View =
    View.inflate(this, layoutRes, parent)

fun Fragment.inflaterView(@LayoutRes layoutRes: Int, parent: ViewGroup? = null): View =
    View.inflate(context, layoutRes, parent)

fun Dialog.inflaterView(@LayoutRes layoutRes: Int, parent: ViewGroup? = null): View =
    View.inflate(context, layoutRes, parent)

fun View.inflaterView(@LayoutRes layoutRes: Int, parent: ViewGroup? = null): View =
    View.inflate(context, layoutRes, parent)

fun RecyclerView.ViewHolder.inflaterView(@LayoutRes layoutRes: Int, parent: ViewGroup? = null): View =
    View.inflate(itemView.context, layoutRes, parent)

SharedPreferencesExt

fun sp(): SharedPreferences =
    BaseApp.app.applicationContext.getSharedPreferences(
        AppUtils.getAppName(),
        Context.MODE_PRIVATE
    )

/**
 * 批处理
 */
fun SharedPreferences.edit(action: SharedPreferences.Editor.() -> Unit) {
    edit().apply { action() }.apply()
}

fun SharedPreferences.put(key: String, value: String) {
    edit().putString(key, value).apply()
}

fun SharedPreferences.put(key: String, value: Int) {
    edit().putInt(key, value).apply()
}

fun SharedPreferences.put(key: String, value: Boolean) {
    edit().putBoolean(key, value).apply()
}

fun SharedPreferences.put(key: String, value: Float) {
    edit().putFloat(key, value).apply()
}

fun SharedPreferences.put(key: String, value: Long) {
    edit().putLong(key, value).apply()
}

fun SharedPreferences.put(key: String, value: MutableSet<String>) {
    edit().putStringSet(key, value).apply()
}

fun SharedPreferences.clear() {
    edit { clear() }
}

StringExt

/**
 * 是否是手机号
 * 1+后面10位
 */
fun String.isPhone(): Boolean = "1\\d{10}$".toRegex().matches(this)

/**
 * 是否是邮箱地址
 */
fun String.isEmail(): Boolean = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?".toRegex().matches(this)

/**
 * 是否是身份证号码
 */
fun String.isIDCard(): Boolean = "[1-9]\\d{16}[a-zA-Z0-9]".toRegex().matches(this)

/**
 * 是否是中文字符
 */
fun String.isChinese(): Boolean = "^[\u4E00-\u9FA5]+$".toRegex().matches(this)

/**
 * 获取当前字符串的md5
 */
fun String.md5(): String = EncryptUtils.encryptMD5ToString(this)

/**
 * 判断字符串是否为空或者是null的任意变化
 * 曾经出现过后台返回"Null" 然后判断isNullOrEmpty()通过 显示在界面上的时候悲剧了
 */
fun String?.isNull() = isNullOrEmpty() || this!!.toLowerCase(Locale.getDefault()).trim() == "null"

TimeExt


/**
 *  2018-4-6 11:11:11转为毫秒
 *  @param format 时间的格式,默认是按照yyyy-MM-dd HH:mm:ss来转换
 */
fun String.toMills(format: String = "yyyy-MM-dd HH:mm:ss"): Long =
    SimpleDateFormat(format, Locale.getDefault()).parse(this).time

/**
 * Long类型时间戳转为字符串的日期格式
 * @param format 时间的格式,默认是按照yyyy-MM-dd HH:mm:ss来转换
 */
fun Long.toDate(format: String = "yyyy-MM-dd HH:mm:ss"): String =
    SimpleDateFormat(format, Locale.getDefault()).format(Date(this))

/**
 * 到秒
 */
fun Long.toDateUntilSec(): String = toDate("yyyy-MM-dd HH:mm:ss")

/**
 * 到分
 */
fun Long.toDateUntilMin(): String = toDate("yyyy-MM-dd HH:mm")

/**
 * 到日
 */
fun Long.toDateUntilDay(): String = toDate("yyyy-MM-dd")

/**
 * 获取友好时间描述
 */
@SuppressLint("SimpleDateFormat")
fun Long.toTimeDescribe(): String {
    val now = System.currentTimeMillis()
    val span = now - this
    return when {
        span < TimeConstants.MIN -> "刚刚"
        span < TimeConstants.HOUR -> String.format(Locale.getDefault(), "%d分钟前", span / TimeConstants.MIN)
        span < TimeConstants.DAY -> String.format(Locale.getDefault(), "%d小时前", span / TimeConstants.HOUR)
        span < 2 * TimeConstants.DAY -> String.format("昨天 %tR", this)
        span < 3 * TimeConstants.DAY -> String.format("前天 %tR", this)
        else -> SimpleDateFormat("MM - dd").format(Date(this))
    }
}

/**
 * 获取友好时间描述
 */
@SuppressLint("SimpleDateFormat")
fun String.toTimeDescribe(format: String = "yyyy-MM-dd HH:mm:ss"): String {
    return this.toMills(format).toTimeDescribe()
}

ViewExt

/**
 * 设置View的高度
 * @param height 设置的高度
 */
fun View.height(height: Int): View {
    val params = layoutParams
    params.height = height
    layoutParams = params
    return this
}

/**
 * 设置View的宽度
 * @param width 设置的宽度
 */
fun View.width(width: Int): View {
    val params = layoutParams
    params.width = width
    layoutParams = params
    return this
}

/**
 * 设置View的宽度和高度
 * @param width 要设置的宽度
 * @param height 要设置的高度
 */
fun View.widthAndHeight(width: Int, height: Int): View {
    val params: ViewGroup.LayoutParams? = layoutParams
    if (params == null) {
        layoutParams = ViewGroup.LayoutParams(width, height)
    } else {
        params.width = width
        params.height = height
        layoutParams = params
    }
    return this
}

/**
 * 设置背景颜色
 * @param color Int
 * @return View
 */
fun View.backgroundColor(@ColorInt color: Int): View {
    setBackgroundColor(color)
    return this
}

/**
 * 设置View的margin  默认保留原设置
 * @param leftMargin 距离左的距离
 * @param topMargin 距离上的距离
 * @param rightMargin 距离右的距离
 * @param bottomMargin 距离下的距离
 */
fun View.margin(
    leftMargin: Int = Int.MAX_VALUE,
    topMargin: Int = Int.MAX_VALUE,
    rightMargin: Int = Int.MAX_VALUE,
    bottomMargin: Int = Int.MAX_VALUE
): View {
    val params = layoutParams as ViewGroup.MarginLayoutParams
    if (leftMargin != Int.MAX_VALUE)
        params.leftMargin = leftMargin
    if (topMargin != Int.MAX_VALUE)
        params.topMargin = topMargin
    if (rightMargin != Int.MAX_VALUE)
        params.rightMargin = rightMargin
    if (bottomMargin != Int.MAX_VALUE)
        params.bottomMargin = bottomMargin
    layoutParams = params
    return this
}

/**
 * 设置控件Visible
 */
fun View.visible() {
    visibility = View.VISIBLE
}

/**
 * 设置控件Gone
 */
fun View.gone() {
    visibility = View.GONE
}

/**
 * 设置控件Invisible
 */
fun View.invisible() {
    visibility = View.INVISIBLE
}

/**
 * 设置控件Visible
 */
fun visible(vararg views: View?) {
    for (view in views) {
        view?.visible()
    }
}

/**
 * 设置控件Invisible
 */
fun invisible(vararg views: View?) {
    for (view in views) {
        view?.invisible()
    }
}

/**
 * 设置控件Gone
 */
fun gone(vararg views: View?) {
    for (view in views) {
        view?.gone()
    }
}

/**
 * 设置是否可见
 * @param visibleOrGone true - Visible false - Gone
 */
fun View.setVisible(visibleOrGone: Boolean) {
    visibility = if (visibleOrGone) View.VISIBLE else View.GONE
}

/**
 * 判断控件是否为Gone
 */
val View.isGone: Boolean
    get() {
        return visibility == View.GONE
    }

/**
 * 判断控件是否为Visible
 */
val View.isVisible: Boolean
    get() {
        return visibility == View.VISIBLE
    }

/**
 * 判断控件是否为InVisible
 */
val View.isInvisible: Boolean
    get() {
        return visibility == View.INVISIBLE
    }

/**
 * 获取View的Bitmap
 * 支持RecyclerView ScrollView 基础控件 不支持ListView了
 * 注意:使用这个方法的时候必须要在View测量完毕之后才能进行
 */
fun View.toBitmap(): Bitmap {
    if (measuredWidth == 0 || measuredHeight == 0) {
        throw RuntimeException("警告⚠️警告⚠️ 这个时候View还没有测量完毕")
    }
    return when (this) {
        is RecyclerView -> {
            this.scrollToPosition(0)
            this.measure(
                View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            )

            val screenshot = Bitmap.createBitmap(width, measuredHeight, Bitmap.Config.ARGB_8888)
            val canvas = Canvas(screenshot)

            if (background != null) {
                background.setBounds(0, 0, width, measuredHeight)
                background.draw(canvas)
            } else {
                canvas.drawColor(Color.WHITE)
            }
            this.draw(canvas)
            this.measure(
                View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
            )
            screenshot
        }
        is ScrollView -> {
            var totalHeight = 0
            for (i in 0 until this.childCount) {
                totalHeight += this.getChildAt(i).height
            }
            val screenshot =
                Bitmap.createBitmap(this.getWidth(), totalHeight, Bitmap.Config.ARGB_4444)
            val canvas = Canvas(screenshot)
            this.draw(canvas)
            screenshot
        }
        else -> {
            val screenshot =
                Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_4444)
            val canvas = Canvas(screenshot)
            if (background != null) {
                background.setBounds(0, 0, width, measuredHeight)
                background.draw(canvas)
            } else {
                canvas.drawColor(Color.WHITE)
            }
            draw(canvas)
            screenshot
        }
    }
}

/**
 * 设置控件的点击时间
 * @param method 点击事件
 */
fun View.onClick(method: (view: View) -> Unit) {
    setOnClickListener {
        if (ClickHelper.isNotFastClick) {
            method(it)
        }
    }
}

/**
 * 对控件设置点击事件
 * @receiver View
 * @param listener OnNotFastClickListener
 */
fun View.onClick(listener: OnNotFastClickListener) {
    setOnClickListener(listener)
}

/**
 * 对多个控件设置点击事件
 * @param views 控件列表
 * @param onClick 点击方法
 */
fun onViewsClickListener(vararg views: View, onClick: (View) -> Unit) {
    val listener = View.OnClickListener {
        if (ClickHelper.isNotFastClick) {
            onClick(it)
        }
    }
    for (view in views) {
        view.setOnClickListener(listener)
    }
}

/**
 * 防止过快点击监听
 */
interface OnNotFastClickListener : View.OnClickListener {
    fun onNotFastClick(view: View)
    override fun onClick(v: View) {
        if (ClickHelper.isNotFastClick) {
            onNotFastClick(v)
        }
    }
}

/**
 * 控件在多少秒之后可用
 * @param second 秒 3秒 10秒 60秒
 */
@SuppressLint("CheckResult")
fun View.enableAfter(second: Long) {
    Observable.interval(0, 1, TimeUnit.SECONDS)
        .take(second + 1)
        .map { second - it }
        .compose(RxHelper.io2Main())
        .bindToLifecycle(this)
        .doOnSubscribe { isEnabled = false }
        .subscribe({}, {}, { isEnabled = true })
}

/**
 * 重新发送验证码
 * @param second 默认60秒
 */
@SuppressLint("SetTextI18n", "CheckResult")
fun TextView.resendVerificationCodeAfter(second: Long = 60) {
    Observable.interval(0, 1, TimeUnit.SECONDS)
        .take(second + 1)
        .map { second - it }
        .compose(RxHelper.io2Main())
        .bindToLifecycle(this)
        .doOnSubscribe { isEnabled = false }
        .subscribe({
            text = "(${it}秒)"
            setTextColor(color(R.color.color_F5F5F5))
        }, {}) {
            isEnabled = true
            text = "发送验证码"
            setTextColor(color(R.color.color_666666))
        }
}

// ---------------------------------------- TextView ----------------------------------------

/**
 * 安全的设置控件文字
 * @param content 内容文字可以为空
 */
fun TextView.setSafeText(content: CharSequence?) {
    text = content ?: ""
}