AndroidKTX

Introduction: Some very useful kotlin extensions for android developping!好用到爆的 Kotlin 扩展,加速你的 Android 开发!
More: Author   ReportBugs   
Tags:

Some very useful kotlin extensions for android development !

一系列非常有用的 Kotlin 扩展和组件,专注于通用业务的封装,目标提高 Android 开发速度!注意这个不是官方的 AndroidKTX !

Gradle

Step1. Add it in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Step2. Add the dependency

dependencies {
    implementation 'com.github.li-xiaojun.AndroidKTX:library:最新版本'
    implementation 'com.github.li-xiaojun.AndroidKTX:circleprogress:最新版本'
    implementation 'com.github.li-xiaojun.AndroidKTX:videoplayer:最新版本'
    implementation 'com.github.li-xiaojun.AndroidKTX:audioplayer:最新版本'
    implementation 'com.github.li-xiaojun.AndroidKTX:matisse:最新版本'
    implementation 'com.github.li-xiaojun.AndroidKTX:pay:最新版本'
    implementation 'com.github.li-xiaojun.AndroidKTX:share:最新版本'
    implementation 'com.github.li-xiaojun.AndroidKTX:audio-recorder:最新版本'
}

Optional permission:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

Usage

初始化

使用之前需要先初始化,初始化的作用是初始默认配置和注入 Context。

// 简单注册,保持默认配置
AndroidKTX.init(this)
// 也可以详细注册,自定义配置
AndroidKTX.init(context = this,
        isDebug = BuildConfig.DEBUG,
        defaultLogTag = "logTag",
        sharedPrefName = "spName")

Hash 相关

我们使用 hash 的时候,大都是对字符串操作,所以给 String 增加了扩展方法,用法如下:

"123456".md5()  // E10ADC3949BA59ABBE56E057F20F883E
"123456".sha1()  // 7C4A8D09CA3762AF61E59520943DC26494F8941B
"123456".sha256()  // 8D969EEF6ECAD3C29A3A629280E686CF0C3F5D5A86AFF3CA12020C923ADC6C92
"123456".sha512()  // BA3253876AED6BC22D4A6FF53D8406C6AD864195ED144AB5C87621B6C233B548BAEAE6956DF346EC8C17F5EA10F35EE3CBC514797ED7DDD3145464E2A0BAB413
// 随机数增强哈希
"123456".md5Hmac(salt)  // 4270C88B36C8D232DC2098FE0287A490
"123456".sha1Hmac(salt)  // E3DDE36BBDF2CA05A96DBE11BDE696FEF00C4299
"123456".sha256Hmac(salt)  // 12FC58997EA2FBD1861E1FBD4AFD5D69321A122BAB6FBD1695A2820B42D7F7B8

// 对称加密解密
//DES 的 key 必须是 8 位
"123456".encryptDES("aaaabbbb")  //TQEoRuLPiHo=
"TQEoRuLPiHo=".decryptDES("aaaabbbb")  //123456
//AES 的 key 必须是 16 位
"123456".encryptAES("aaaabbbbaaaabbbb")  //3FiQmdsD3GCuAManeaW/yg==
"3FiQmdsD3GCuAManeaW/yg==".decryptAES("aaaabbbbaaaabbbb")  //123456

Span 相关

封装了颜色,大小,背景色,删除线和点击等常用的文本装饰,使用对象是 TextView 和 String。用法如下:

val str = "我是测试文字"
textView.sizeSpan(str, 0..2)
textView.sizeSpan(str, 0..2, scale = .7f) //改变 scale 可以控制放大或缩小,scale 默认是 1.5

weather_humidity

textView.colorSpan(str,2..6)

weather_humidity

textView.backgroundColorSpan(str,2..6)

weather_humidity

textView.strikeThrougthSpan(str,2..6)

weather_humidity

textView.clickSpan(str = str, range = 2..6, color = Color.BLUE, clickAction = {
    toast("哈哈我被点击了".toColorSpan(0..2))
})

weather_humidity

textView.styleSpan(str, range) //加粗,斜体等效果

appendXX系列方法,其实在实际项目中 append 系列方法会用的更多,用法按如下:

tv.text = "演示一下 appendXX 方法的用法"
tv.appendSizeSpan("变大变大")
        .appendColorSpan("我要变色", color = Color.parseColor("#f0aafc"))
        .appendBackgroundColorSpan("我是有底色的", color = Color.parseColor("#cacee0"))
        .appendStrikeThrougthSpan("添加删除线哦哦哦哦")
        .appendClickSpan("来点我一下试试啊", isUnderlineText = true, clickAction = {
            toast("哎呀,您点到我了呢,嘿嘿")
        } )
        .appendStyleSpan("我是粗体的")

appendxx

View 相关

view.width(100)           // 设置 View 的宽度为 100
view.widthAndHeight(100)  // 改变 View 的宽度和高度为 100
view.animateWidthAndHeight(600,900) //带有动画
view.animateWidth(600,900, action = { //监听动画进度,执行可选操作 }) //带有动画
view.margin(leftMargin = 100)  // 设置 View 左边距为 100
view.click { toast("aa") }    // 设置点击监听,已实现事件节流,350 毫秒内只能点击一次
view.longClick {             // 设置长按监听
    toast("long click")
    true
}
view.gone()
view.visible()
view.invisible()
view.isGone  // 属性
view.isVisible // 属性
view.isInvisible // 属性
view.toggleVisibility() // 切换可见性
view.toBitmap()           // 获取 View 的截图,支持 RecyclerView 长列表截图
// 遍历子 View
layout.children.forEachIndexed { index, view ->

}

ImageView 相关

// 底层是封装 Glide 来加载图片,内部已解决 Glide 的 CenterCrop 导致圆角失效的问题
imageView.load(url)
imageView.load(url, placeholder = R.mipmap.ic_launcher, isCircle = true)
imageView.load(url, placeholder = R.mipmap.ic_launcher, roundRadius = 20)

RecyclerView 相关

对 RecyclerView 的使用进行了极其简洁的封装,再也不用写 Adapter。

recyclerView.vertical() //设置垂直
            //.vertical(spanCount = 2, isStaggered = true) //设置垂直 2 列瀑布流
            .divider(color = Color.RED, size = 1) //设置分割线
            // 绑定数据,只需传数据和布局,然后实现绑定的方法,再也不用写 Adapter
            .bindData(data, R.layout.adapter_rv) { holder, t, position ->
                holder.setText(R.id.text, "模拟数据 - $t")
                        .setImageResource(R.id.image, R.mipmap.ic_launcher_round)
            }
            .addHeader(headerView) //必须在 bindData 之后调用
            .addFooter(footerView) //必须在 bindData 之后调用
            .itemClick<String> { data, holder, position ->
                toast("click ${data[position]}")
            }
            //.itemLongClick<String> { data, holder, position ->
            //    toast("click ${data[position]}")
            //}

//notify
//recyclerView.adapter.notifyItemChanged()


// 多条目类型
recyclerView.vertical()
            .divider(color = Color.RED)
            .multiTypes(data, listOf(OneItem(), TwoItem()))
            .addHeader(headerView) //必须在 multiTypes 之后调用
            .addFooter(footerView) //必须在 multiTypes 之后调用
            .itemClick<String> { data, holder, position ->
                toast("click ${data[position]}")
            }
// 实现多个 Item 类型
class OneItem : ItemViewDelegate<String>{
        override fun bind(holder: ViewHolder, t: String, position: Int) {
                    holder.setText(android.R.id.text1, t)
        }
        override fun isThisType(item: String, position: Int): Boolean {
            return item != "header" && item != "footer"
        }
        override fun getLayoutId(): Int {
            return android.R.layout.simple_list_item_1
        }
    }

SharedPref 相关

使用范围:Context

// 便捷处理
sp().getString("a", "default")
sp().getBoolean("b", false)
sp(name = "xxx.cfg").getBoolean("b", false)
//...

// 批处理
sp().edit {
    putString("a", "1")
    putBoolean("b", true)
}
// 清除
sp().clear()

Activity 相关

使用范围:Activity 和 Fragment,以及 Context 对象,会自动检测 Context 是否为 Activity,并自动添加FLAG_ACTIVITY_NEW_TASK

startActivity<MainActivity>()
// 启动 Activity 并传参
startActivity<MainActivity>(flag = Intent.FLAG_ACTIVITY_CLEAR_TOP, bundle = arrayOf(
        "a" to 1,
        "b" to "lala"
))
// 在 Fragment 中启动 Activity
fragment.start<MainActivity>()
// 使用非 Activity 的 Context 启动,内部会自动检测并添加 FLAG_ACTIVITY_NEW_TASK,不会导致崩溃
applicitionCtx.startActivity<MainActivity>(bundle = arrayOf(
        "a" to 1,
        "b" to "lala"
))
// 在 View 中启动
view.startActivity<MainActivity>()

Fragment 相关

使用范围:Activity 和 Fragment

//替换一个 Fragment 不传参
replace(R.id.frame1, TempFragment())
//替换一个 Fragment 并传参数
replace(R.id.frame1, TempFragment(), arrayOf(
                TempFragment.Key1 to "我是第一个 Fragment",
                TempFragment.Key2 to "床前明月光"
        ))
//添加 Fragment
add(R.id.frame1, AFragment())
//其他方法,show, hide, remove 略过

//fragment 批处理,自动 commit
fragmentManager {
    add(R.id.container, TempFragment())
    hide(fragment)
    replace(R.id.container, TempFragment())
}

时间日期处理

// 默认格式:yyyy-MM-dd HH:mm:ss
"2018-12-07 17:28:39".toDateMills()  // 字符串日期转毫秒
(1544174919000L).toDateString()      // 毫秒转字符串日期

// 自定义格式
"2018-12-07".toDateMills(format = "yyyy-MM-dd")
(1544174919000L).toDateString(format = "yyyy-MM-dd")

OkHttp 极简封装

OkHttpUtilsOkGo都不满意,于是造了一个。

  • 请求示例
    GlobalScope/viewModelScope.launch {
      //Get 请求
      val user = "http://192.168.1.103:3000/json".http().get<User>().await()
      //Post 请求,传递 header 和 params
      val user = "http://192.168.1.103:3000/json".http()
                      .headers("device" to "HuaWeiMate20", ...)
                      .params("token" to "188sas9cf99a9d",
                          "file" to file,  //上传文件,使用 Array<File>对象来传多个文件
                           ...)
                      .post<User>()
                      //.postJson(json) 直接传 json 字符串,目前 post 和 put 支持直传 json
                      //.putJson(json) 直接传 json 字符串,目前 post 和 put 支持直传 json
                      .await()
    }
    
    GlobalScope.launch开启的协程并不会自动取消,如果想要自动取消协程,请看看我的文章:https://juejin.im/post/5ceb3d6ef265da1bb47d4180

上面的示例需要在协程中使用;也是我最喜欢和最推荐的方式。如果你不用协程,则可以用callback style

"http://192.168.1.103:3000/json".http().get(object : HttpCallback<String> {
        override fun onSuccess(t: String) {
        }
        override fun onFail(e: IOException) {
            super.onFail(e)
        }
    })

下载文件和进度:

"https://github.com/li-xiaojun/XPopup/archive/master.zip"
            .http()
            .savePath(Environment.getExternalStorageDirectory().toString() + "/xxx.zip")
            .downloadListener(onProgress = {
                LogUtils.d("download progress: ${it?.percent}")
            })
            .get<File>()
            .await()
  • Http 日志

内置了简洁实用的 Http 日志打印器,效果如下: Http 日志

  • 其他 ```kotlin
      // 设置自定义的 Client
    
    OkExt.setClient(...)
      // 设置全局 header
      .headers("header1" to "a", "header2" to "b", ...)
      // 设置拦截器
      .interceptors(...)
      .baseUrl("https://lixiaojun.xin/")
    

// 取消请求 "http://192.168.1.103:3000/json".http(tag = "abc").get() //需要先指定 tag OkExt.cancel("abc")


### LiveData 和 Lifecycle 相关
内置了一些实用类:
1. OnceLiveData: 只执行一次的 LiveData
2. StateLiveData: 携带状态的 LiveData
```kotlin
// observe data.
vm.userData.observe(this, Observer { 
    // data change
})
// observe state
vm.userData.state.observe(this, Observer { 
    when(it){
        StateLiveData.State.Idle -> toast("idle")
        StateLiveData.State.Loading -> showProgress("idle")
        StateLiveData.State.Success -> toast("success")
        StateLiveData.State.Error -> toast("fail")
    }
})
  1. LifecycleHandler: 自动在 UI 销毁时移除 msg 和任务,天然不会内存泄露的 Handler

通用扩展

  • Json 转换相关:

    User("李晓俊", 25).toJson()   // {"age":25,"name":"李晓俊"}
    // 底层是 Gson 解析,但是不用传 class 或者 TypeToken 了,得益于 Kotlin 的 reified 功能
    "{\"age\":25,\"name\":\"李晓俊\"}".toBean<User>()
    // 集合类型需要这样写
    "[{\"age\":25,\"name\":\"李晓俊\"}]".toBean<List<User>>()
    
  • toast 相关

    ctx/fragment/view.toast("测试短吐司")
    ctx/fragment/view.longToast("测试长吐司")
    
  • dp 和 px 转换:

    ctx/fragment/view/holder.dp2px(100)
    ctx/fragment/view/holder.px2dp(100)
    ctx/fragment/view/holder.sp2px(100)
    ctx/fragment/view/holder.px2sp(100)
    //动态创建图片
    ctx/fragment/view/holder.createDrawable(color = Color.GREEN,
                                           strokeColor = Color.RED,
                                           strokeWidth = dp2px(2f),
                                           radius = dp2px(30f).toFloat(),
                                           enableRipple = true)
    
  • Resource 获取相关

    context/fragment/view/holder.string(R.string.app_name) // 获取字符串
    context/fragment/view/holder.stringArray(R.array.array) // 获取字符串数组
    context/fragment/view/holder.color(R.id.color)    //获取颜色
    context/fragment/view/holder.drawable(R.mipmap.ic_launcher) // 获取图片
    context/fragment/view/holder.dimenPx(R.dimen.abc) // 获取 dp 值
    
  • TextView 相关

给 TextView 增加 sizeDrawable 方法,用于给 TextView 的 drawable 设置大小:

tv.sizeDrawable(dp2px(20), topDrawable = R.mipmap.ic_launcher)
  • ViewPager 相关

让 ViewPager 变成卡片缩放效果:

pager.asCard() //通过参数可以调节卡片的距离和大小

设置 adapter 绑定数据:

viewPager.bind(10, bindView = {container, position ->
    return@bind TextView(this)
})
  • Bitmap 相关

将 Bitmap 保存到相册:

bitmap.saveToAlbum()

通用控件

  • TitleBar 通用的标题栏控件,具体使用看属性便知,右边最多支持 3 个按钮。 TitleBar

使用示例:

<com.lxj.androidktx.widget.TitleBar
    android:layout_marginTop="15dp"
    app:title="嘻嘻"
    android:id="@+id/tt"
    app:titleAlignLeft="true"
    app:leftImageSrc="@mipmap/fh2"
    app:rightImageSrc="@mipmap/setting"
    app:rightImagePadding="15dp"
    app:rightImage2Src="@mipmap/zhifuwacheng"
    app:rightImage3Src="@mipmap/fx"
    app:titleColor="#fff"
    android:background="#2079E5"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

点击监听:

//titleBar 点击事件
tt.clickListener(object : TitleBar.ClickListener{
    override fun leftTextClick() {
        toast("left text")
    }
    override fun leftImageClick() {
        toast("left image")
    }
    override fun rightTextClick() {
        toast("right text")
    }
    override fun rightImageClick() {
        toast("right image")
    }
    override fun rightImage2Click() {
        toast("right image22")
    }
    override fun rightImage3Click() {
        toast("right image333")
    }
})
  • SuperLayout 用来实现常见的横向图文布局,使用它配合几个属性,可以轻松实现以下效果: SuperLayout SuperLayout

基本使用:

<com.lxj.androidktx.widget.SuperLayout
    android:layout_marginLeft="20dp"
    android:layout_marginRight="20dp"
    android:paddingLeft="14dp"
    android:paddingRight="15dp"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    android:layout_marginTop="15dp"
    app:sl_leftImageSrc="@drawable/avatar"
    app:sl_leftText="头像"
    app:sl_leftTextColor="#222"
    app:sl_leftTextSize="18sp"
    app:sl_rightImageSrc="@mipmap/jt"
    app:sl_solid="#3EDCE9"
    app:sl_corner="15dp"
    app:sl_strokeWidth="2dp"
    app:sl_stroke="#f00"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

其他用法看属性名即可,无需多说。

  • ShapeFrameLayout,ShapeLinearLayout,ShapeRelativeLayout,ShapeTextView 等可以设置背景,圆角,水波纹的布局

分享相关

对微信好友和朋友圈分享,QQ 分享和微博分享和登录的封装。

//分享相关 appKey 和 appId 配置
 Share.init(this, BuildConfig.DEBUG, umengAppKey = BuildConfig.UmengAppKey,
            wxAppId = BuildConfig.WeChatAppId, wxAppKey = BuildConfig.WeChatAppKey,
            qqAppId = BuildConfig.qqAppId, qqAppKey = BuildConfig.qqAppKey,
            weiboAppId = BuildConfig.weiboAppId, weiboAppKey = BuildConfig.weiboAppKey, weiboCallbackUrl = BuildConfig.weiboCallbackUrl)

三方分享:

Share.share(this, SHARE_MEDIA.WEIXIN_CIRCLE, text = "",
            title = "" , url = "https://iandroid.xyz", bitmap = null)

三方登录:

Share.wechatLogin(this@AccountSettingActivity, object : Share.ShareLoginCallback {
    override fun onComplete(platform: SHARE_MEDIA, loginData: LoginData) {

    }
})

图片和视频选择,拍照

  • 拍照
    ImagePicker.startCamera()
    
  • 选择照片或视频
    ImagePicker.startPicker()
    
  • 获取结果
    ImagePicker.fetchResult(intent) //在 onActivityResult 方法中使用
    

支付

  • 支付宝支付
    PayVM.aliPay(...)
    
  • 微信支付
    PayVM.wxPay(...)
    

二维码扫描

QrCodeUtil.start(this, 1)

文档

文档懒得写了,主要自用。

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools