AndroidView网页加载

    @Composable
    fun SecondOnePage(): Unit {
        var progression by remember {
            mutableFloatStateOf(0f)
        }
        Surface {
            Column {
                //进度组件
                LinearProgressIndicator(
                    progress = progression / 100,
                    modifier = Modifier.fillMaxWidth(),
                    color = Color.Red
                )
                //利用AndroidView加载网页
                AndroidView(
                    factory = { context ->
                        var webView = WebView(context)
                        webView.apply {
                            settings.javaScriptEnabled = true
                            webViewClient = object : WebViewClient() {}
                            loadUrl("https://m.jd.com/")
                        }

                    },
                    modifier = Modifier.fillMaxSize(),
                    update = { webview ->
                        //监听加载进度。
                        webview.webChromeClient = object :WebChromeClient(){
                            override fun onProgressChanged(view: WebView?, newProgress: Int) {
                                progression = newProgress.toFloat()
                                super.onProgressChanged(view, newProgress)
                            }
                        }
                    }
                )
            }
        }
    }

Android Jetpack Compose开发中经常导入的依赖(可能持续更新)

    //网络请求
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
    implementation("com.squareup.okhttp3:okhttp:4.9.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")

    //fastjson2解析json格式数据。
    implementation("com.alibaba.fastjson2:fastjson2:2.0.42")

    //ViewModel依赖
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
    //liveData依赖
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
    //lifecycle依赖
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")

    //添加协程
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")


    //room,如果kapt,需要在上面插件处导入    id("kotlin-kapt")
    implementation("androidx.room:room-runtime:2.6.1")
    annotationProcessor("androidx.room:room-compiler:2.6.1")
    implementation("androidx.room:room-ktx:2.6.1")
    kapt("androidx.room:room-compiler:2.6.1")


    //相机
    implementation("androidx.camera:camera-core:1.2.2")
    implementation("androidx.camera:camera-camera2:1.2.2")
    implementation("androidx.camera:camera-lifecycle:1.2.2")
    implementation("androidx.camera:camera-video:1.2.2")
    implementation("androidx.camera:camera-view:1.2.2")
    implementation("androidx.camera:camera-extensions:1.2.2")

    //navigation-kotlin
    implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
    implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
    //navigation-compose
    implementation("androidx.navigation:navigation-compose:2.5.3")

    //workManager
    implementation("androidx.work:work-runtime-ktx:2.9.0")

JetPack Room 入门使用完整示例

具体数据可以参考官方文档:https://developer.android.com/codelabs/kotlin-android-training-room-database?hl=zh-cn#0

第一步:导入依赖

1、插件

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("kotlin-kapt")
}

2、依赖

implementation("androidx.room:room-runtime:2.6.1")
annotationProcessor("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
kapt("androidx.room:room-compiler:2.6.1")
版本问题(可能存在)
    compileOptions {
        //JavaVersion.VERSION_1_8这个版本可能不兼容,根据环境的版本来更换
        sourceCompatibility = JavaVersion.VERSION_17 
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = "17"
    }

二、定义实体类

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(

    @PrimaryKey(autoGenerate = true)
    var nightId: Long = 0L,

    @ColumnInfo(name = "start_time_milli")
    val startTimeMilli: Long = System.currentTimeMillis(),

    @ColumnInfo(name = "end_time_milli")
    var endTimeMilli: Long = startTimeMilli,

    @ColumnInfo(name = "quality_rating")
    var sleepQuality: Int = -1
)

三、定义Dao访问类

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update

@Dao
interface SleepDatabaseDao {

    @Insert
    fun insert(night: SleepNight)

    @Update
    fun update(night: SleepNight)

    @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
    fun get(key: Long): SleepNight?


    @Query("DELETE FROM daily_sleep_quality_table")
    fun clear()

    @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
    fun getTonight(): SleepNight?

    @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
    fun getAllNights(): LiveData<List<SleepNight>>

四、定义DataBase操作类

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

    abstract val sleepDatabaseDao: SleepDatabaseDao

    companion object {

        @Volatile
        private var INSTANCE: SleepDatabase? = null

        fun getInstance(context: Context): SleepDatabase {
            synchronized(this) {
                var instance = INSTANCE

                if (instance == null) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        SleepDatabase::class.java,
                        "sleep_history_database"
                    )
                        .fallbackToDestructiveMigration()
                        .build()
                    INSTANCE = instance
                }
                return instance
            }
        }
    }
}

五、使用(必须在子线程中或者协程中使用)

在项目中我们一般是结合LiveData和ViewModel一起使用。

class MainActivity2 : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            MyApplicationTheme {
                GetMessage(this)
            }
        }
    }
}

@Composable
fun GetMessage(mainActivity2: MainActivity2) {
    Column {
        Button(onClick = {
            GlobalScope.launch{
                try {
                    SleepDatabase.getInstance(mainActivity2)
                        .sleepDatabaseDao
                        .insert(SleepNight())
                }catch (e:Exception){
                    Log.e("Xiang","出现了错误:${e.message}")
                }
            }
        }) {
            Text(text = "添加数据")
        }
    }
}

六、利用Android Studio工具查看

打开菜单【view】->【Tool Windows】->【App Inspection】

Retrofit框架的简单使用(下)

一、Post提交

在ApiService接口中定义Post请求,其他代码书接上回 Retrofit框架的简单使用(上)

 /**
     * Post提交方式,请求参数goodsId=xxx
     * @FormUrlEncoded :表单形式提交
     */
    @POST("goods/relative")
    @FormUrlEncoded //表单形式提交
    fun testPost2(@Field("goodsId") goodsId: String): Call<String>


    /**
     * 参数为map形式。请求参数: password=xxx&username=xxxxx
     */
    @POST("goods/relative")
    @FormUrlEncoded //表单形式提交
    fun testPost3(@FieldMap mapData: Map<String,String>): Call<String>


    /**
     * (@Body data:HashMap<String,String>):这里不一定是String ,你可以传入具体类型,比如数据库表的实体类。
     * ,要想传入的参数是Json格式的话,需要修改请求格式,
     * 把ScalarsConverterFactory.create() 替换成 GsonConverterFactory.create()
     *     var retrofit = Retrofit.Builder()
     *         .baseUrl(ApiService.BASE_URL)
     *         .addConverterFactory(GsonConverterFactory.create()) //替换处
     *         .client(okHttpClient)
     *         .build()
     */
    @POST("goods/relative")
    fun testPost(@Body data:HashMap<String,String>): Call<String>

Retrofit框架的简单使用(上)

一,导入依赖

implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
在AndroidManifest.xml中记得申请网络权限
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

二,定义个请求接口

import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query

interface ApiService {

    companion object {
        const val BASE_URL = "https://api.sunofbeaches.com/shop/"
    }

    //最后组合成完整地址:https://api.sunofbeaches.com/shop/discovery/categories
    @GET("discovery/categories")
    //@Query注解是查询参数,如果多个参数就写多个注解,用逗号分开
    fun test(@Query("test") str: String): Call<String>

}

三,开始使用

    var TAG: String = "Xiang"

    /**
     * 创建拦截器
     */
    var intercept = HttpLoggingInterceptor {
        Log.e(TAG, "onHttpRetrofit: $it")
    }.setLevel(HttpLoggingInterceptor.Level.BODY)

    /**
     * 添加拦截器
     */
    var okHttpClient = OkHttpClient.Builder().addInterceptor(intercept).build()


    var retrofit = Retrofit.Builder()
        .baseUrl(ApiService.BASE_URL)
        .addConverterFactory(ScalarsConverterFactory.create())
        .client(okHttpClient)
        .build()

    /**
     * 获取动态代理实例
     */
    var apiService = retrofit.create(ApiService::class.java)


    /**
     * 异步请求,不用协程或者子线程。
     */
//    apiService.test("100").enqueue(object : retrofit2.Callback<String> {
//        override fun onResponse(
//            call: retrofit2.Call<String>,
//            response: retrofit2.Response<String>
//        ) {
//            /**
//             * 请求成功
//             */
//            if (response.isSuccessful) {
//                Log.e(TAG, "onResponse: ${response.body()}")
//            }
//        }
//
//        override fun onFailure(call: retrofit2.Call<String>, t: Throwable) {
//            //请求是吧
//            Log.e(TAG, "onFailure: ${t.message}")
//        }
//
//    })

    /**
     * 同步请求,必须用协程或者线程去执行,不然会阻塞UI(主)线程
     */
    GlobalScope.async {
        var response = apiService.test("100").execute();
        if (response.isSuccessful) {
            Log.e(TAG, "onResponse: ${response.body()}")
        } else {
            Log.e(TAG, "onFailure: ${response.message()}")
        }
    }

关于Post请求,请跳转 Retrofit框架的简单使用(下)

HttpOk3通过Kotlin 请求简单Demo

开启网络权限

需要在AndroidManifest.xml 的manifest下添加网络权限

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

第一步:引入依赖

 implementation("com.squareup.okhttp3:okhttp:4.9.0")
 implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")

添加过滤器

import android.util.Log
import okhttp3.Interceptor
import okhttp3.Response

class LogIntercept : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        var url = request.url
        Log.e("Xiang", "intercept: 这里是请求过滤器,请求地址:${url}")
        return chain.proceed(request)
    }
}

二:GET请求

        GlobalScope.async {
            var okHttpClient = OkHttpClient.Builder().addInterceptor(LogIntercept()).build()
            var request: Request = Request.Builder()
                .url("https://api.admin.com/shop/discovery/categories")
                .build()
            okHttpClient.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {

                }
                override fun onResponse(call: Call, response: Response) {
                    if (response.isSuccessful) {
                        response.body?.let {
                            val dataBody = it.string()
                            Log.e("Xiang", dataBody)
                        }
                    }
                }
            })
        }

三:form表单提交

 var okHttpClient = OkHttpClient.Builder().addInterceptor(LogIntercept()).build()
    var formBody = FormBody.Builder()
        .add("AD_user","admin")
        .add("AD_pass","888888")
        .build()

    var request: Request = Request.Builder()
        .url("https://api.admin.com/admin/Chk_login.asp")
        .post(formBody)
        .build()

    okHttpClient.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            e.message?.let {
                Log.e("Xiang", it.toString())
            }
        }
        override fun onResponse(call: Call, response: Response) {
            if (response.isSuccessful) {
                response.body?.let {
                    val dataBody = it.string()
                    Log.e("Xiang", dataBody)
                }
            }
        }
    })

四:JSON格式提交

    var okHttpClient = OkHttpClient.Builder().addInterceptor(LogIntercept()).build()
    var stringBody = "{\"username\":\"123456\",\"password\":\"123456789\"}"
        .toRequestBody("application/json;charset=utf-8".toMediaType())
    var request: Request = Request.Builder()
        .url("https://auth.admin.com/login")
        .post(stringBody)
        .build()

LiveData和ViewModel联合使用,简单入门

我们得首先明白ViewModel只是一个记录业务逻辑和屏幕状态的容器,LiveData是一个可以被观察数据集合

第一步,创建容器

我们新建一个ViewModel容器类。比如MyViewModel

class MyViewModel : ViewModel() {}  

第二步,在容器中创建LiveData可被观察的数据,比如一个String类型的数据。

class MyViewModel : ViewModel() {

    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }
}

第三步,在界面组件(Activity或Fragment)中去使用。

首先添加一个观察者,这样就可以实时的去关键currentName的变动。

//我们先在Activity中添加ViewModel 
private val model: MyViewModel by viewModels()

//在组件中使用。
@Composable
fun GetMessage(model: MyViewModel, cotnent: ComponentActivity) {
    var message by remember {
        mutableStateOf("")
    }
//建立一个观察者
    var observer = Observer<String> {
        message = it
    }

//添加观察者,cotnent:Activity对象,observer观察者对象
model.currentName.observe(cotnent, observer)

    Text(text = "内容:$message")
}

第四步,设置改变LiveData数据

 model.currentName.setValue("这是更新的内容")

Lifecycle简单使用方法。

Lifecycle的组件作用就是解耦Activity的生命周期,没当Activity的生命周期改变之后通知Lifecycle观察者。

第一,导入Lifecycle依赖

implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")

第二,建立一个观察者,实现DefaultLifecycleObserver接口

重写DefaultLifecycleObserver的生命周期函数,比如我们要观察Activity的onStart、onStop周期函数,那么就重写这两个函数。

import android.util.Log
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner

class TestModel(var names:String):DefaultLifecycleObserver {

    lateinit var name:String

    init{
        this.name = names
    }

    override fun onStart(owner: LifecycleOwner) {
        Log.e("Xiang","执行了start $name")
    }

    override fun onStop(owner: LifecycleOwner) {
        Log.e("Xiang","执行了stop $name")
    }


}

第三,绑定到Activity去。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //添加观察者
        lifecycle.addObserver(TestModel("kt"))
        setContent {
            Text(text = "hi Android")
        }
    }
}
  • 当MainActivity 这个Activity的生命周期发生变化之后,我们可以在观察者TestModel中同步对应的方法。

Android开发在layout添加子目录方法(Kotlin版本)

如果把所有的布局文件和fragment文件都放在layout这一个目录下面,随着文件增多,越发越乱,那么我们在layout下再见几个文件对布局文件进行分组分类。

第一步 显示Android目录为Project状态,在 build.gradle.kts 文件中添加一下代码

android {    
.......
    sourceSets {
        getByName("main") {
            res {
                srcDirs(
                    "src/main/res/layout/activity",
                    "src/main/res/layout/fragment",
                    "src/main/res"
                )
            }
        }
    }
}

那么对应的文件布局分为active和fragment,对应的文件目录格式如下

--res/layout
----res/layout/activity/layout
----res/layout/fragment/layout

xml布局文件可以放在res/layout、res/layout/activity/layout、res/layout/fragment/layout目录下,如果自己有其他文件分类,可以自行添加。