美文网首页
Compose 项目笔记概要

Compose 项目笔记概要

作者: Afra55 | 来源:发表于2023-06-27 10:38 被阅读0次
1. 网址导航
  1.1 Compose与Kotlin的兼容对应关系
  1.2 快速入门
  1.3 Navigation
  1.4 图片加载
  1.5 Gradle国内镜像
  1.6 动画
  1.7 手势
2. 设置 Compose
  2.1 app级build.gradle配置:
  2.2 添加依赖
3. 使用导航处理页面跳转
  3.1 统一管理路由
  3.2 集中处理页面跳转
  3.3 NavHost 处理路由
4 使用 BottomNavigation 搭建TAB菜单
  4.1 统一管理底部菜单
  4.2 使用脚手架构建布局
  4.3 使用 ViewModel 切换tab
5 网络请求
  5.1 不使用协程的处理方法
  5.2 推荐写法
6 使用Page+Flow请求分页数据
7 沉浸式状态栏
8 ViewModel 三板斧
9 添加 Android View
10 XML使用 ComposeView
~其他
  ~.1 判断横竖屏

[toc]

基于玩安卓:https://www.wanandroid.com

1 网址导航

1.1 Compose与Kotlin的兼容对应关系

Kotlin 预发布版本兼容的 Compose Compiler 版本

1.2 快速入门

1.3 Navigation

1.4 图片加载

1.5 Gradle国内镜像

1.6 动画

1.7 手势


2 设置 Compose

2.1 app级build.gradle配置:

android {
    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.4.2" // 对应 Kotlin 版本 1.8.10 
    }
}

2.2 添加依赖

 def composeBom = platform('androidx.compose:compose-bom:2023.01.00')
    implementation composeBom
    androidTestImplementation composeBom

    // Choose one of the following:
    // Material Design 3
    implementation 'androidx.compose.material3:material3'
    // or Material Design 2
    implementation 'androidx.compose.material:material'
    // or skip Material Design and build directly on top of foundational components
    implementation 'androidx.compose.foundation:foundation'
    // or only import the main APIs for the underlying toolkit systems,
    // such as input and measurement/layout
    implementation 'androidx.compose.ui:ui'

    // Android Studio Preview support
    implementation 'androidx.compose.ui:ui-tooling-preview'
    debugImplementation 'androidx.compose.ui:ui-tooling'

    // UI Tests
    androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
    debugImplementation 'androidx.compose.ui:ui-test-manifest'

    // Optional - Included automatically by material, only add when you need
    // the icons but not the material library (e.g. when using Material3 or a
    // custom design system based on Foundation)
    implementation 'androidx.compose.material:material-icons-core'
    // Optional - Add full set of material icons
    implementation 'androidx.compose.material:material-icons-extended'
    // Optional - Add window size utils
    implementation 'androidx.compose.material3:material3-window-size-class'

    // Optional - Integration with activities
    implementation 'androidx.activity:activity-compose:1.6.1'
    // Optional - Integration with ViewModels
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
    // Optional - Integration with LiveData
    implementation 'androidx.compose.runtime:runtime-livedata'
    // Optional - Integration with RxJava
    implementation 'androidx.compose.runtime:runtime-rxjava2'

    // 网络请求
    def retrofit_version = "2.9.0"
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
    api "com.squareup.retrofit2:converter-scalars:$retrofit_version"
    api 'com.squareup.okhttp3:logging-interceptor:4.9.1'
    // 导航
    implementation "androidx.navigation:navigation-compose:2.5.3"
    // 图片加载
    implementation("io.coil-kt:coil-compose:2.4.0")

3 使用导航处理页面跳转

3.1 统一管理路由

object AppDestinations {
    const val HOME_PAGE_ROUTE = "home_page_route"
    const val SETTING_PAGE_ROUTE = "setting_page_route"
}

3.2 集中处理页面跳转

class AppActions(navController: NavHostController) {

    val toSetting: (HomeBean) -> Unit = { homeBean ->
  
        homeBean.msg = "go setting"
        val gson = Gson().toJson(homeBean).trim()
        val result = URLEncoder.encode(gson, "utf-8")
        toAnimView(navController = navController, "${AppDestinations.SETTING_PAGE_ROUTE}/$result")
    }

    val toHome: () -> Unit = {
        toAnimView(navController = navController, AppDestinations.HOME_PAGE_ROUTE)
    }

    val upPress: () -> Unit = {
        navController.navigateUp()
    }

    /**
     * 跳转动画,之后Navigation如果支持转场动画的话即可生效
     *
     * @param navController /
     * @param route 跳转路径
     */
    private fun toAnimView(navController: NavHostController, route: String) {
        navController.navigate(route) {
            anim {
                enter = R.anim.activity_push_in
                exit = R.anim.activity_push_out
                popEnter = R.anim.center_zoom_in
                popExit = R.anim.center_zoom_out
            }
        }
    }

}

3.3 NavHost 处理路由

@Composable
fun NavGraph(
    startDestination: String = PlayDestinations.HOME_PAGE_ROUTE
) {
    val navController = rememberNavController()

    val actions = remember(navController) { AppActions(navController) }
    NavHost(
        navController = navController,
        startDestination = startDestination
    ) {
        composable(AppDestinations.HOME_PAGE_ROUTE) {
            // viewModel 用于切换 tab 页面  
          val viewModel: HomeViewModel = viewModel() 
            val position by viewModel.position.observeAsState()
            MainPage(actions, position) { tab ->
                viewModel.onPositionChanged(tab)
            }
        }
       
        composable(
            "${PlayDestinations.SETTING_PAGE_ROUTE}/{result}",
            arguments = listOf(navArgument("result") {
                type = NavType.StringType
            })
        ) { backStackEntry ->
            val arguments = requireNotNull(backStackEntry.arguments)
            val parcelable = arguments.getString(result)
            val fromJson = Gson().fromJson(parcelable, HomeBean::class.java)
            SettingPage(
                article = fromJson,
                onBack = actions.upPress
            )
        }
    }
}

4 使用 BottomNavigation 搭建TAB菜单

4.1 统一管理底部菜单

enum class AppTabs(
    @StringRes val title: Int,
    @DrawableRes val icon: Int,
    @DrawableRes val selectIcon: Int
) {
    HOME_PAGE(R.string.home_page, R.drawable.ic_nav_home_normal, R.drawable.ic_nav_home_actived),
    MINE(R.string.mine, R.drawable.ic_nav_my_normal, R.drawable.ic_nav_my_pressed)
}

4.2 使用脚手架构建布局

@Composable
fun MainPage(
    actions: AppActions, position: AppTabs?,
    onPositionChanged: (AppTabs) -> Unit
) {
    val tabs = AppTabs.values()
    Scaffold(
        backgroundColor = MaterialTheme.colors.primary,
        bottomBar = {
            BottomNavigation {
                tabs.forEach { tab ->
                    BottomNavigationItem(
                        modifier = Modifier
                            .background(MaterialTheme.colors.primary),
                        icon = {
                            val painter: Painter = if (tab == position) {
                                painterResource(tab.selectIcon)
                            } else {
                                painterResource(tab.icon)
                            }
                            Icon(painter, contentDescription = null)
                        },
                        label = { Text(stringResource(tab.title).uppercase(Locale.ROOT)) },
                        selected = tab == position,
                        onClick = {
                            onPositionChanged(tab)
                        },
                        alwaysShowLabel = true,
                    )
                }
            }
        }
    ) { innerPadding ->
        val modifier = Modifier.padding(innerPadding)
        // 当前是否为横屏
        val isLand = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
        // 淡入淡出布局切换动画
        Crossfade(targetState = position) { screen ->
            when (screen) {
                AppTabs.HOME_PAGE -> {
                    HomePage()
                }
               
                AppTabs.MINE -> {
                    ProfilePage()
                }
            }
        }
    }
}

4.3 使用 ViewModel 切换tab

class HomeViewModel : ViewModel(){

    private val _position = MutableLiveData(AppTabs.HOME_PAGE)
    val position: LiveData<AppTabs> = _position

    fun onPositionChanged(position: AppTabs) {
        _position.value = position
    }

}
         
           val viewModel: HomeViewModel = viewModel()
            val position by viewModel.position.observeAsState()
            MainPage(actions, position) { tab ->
                viewModel.onPositionChanged(tab)
            }

5 网络请求

5.1 不使用协程的处理方法(不推荐)

interface ApiServices{
    @Headers(value = ["Content-type:application/json;charset=UTF-8"])
    @POST
    fun post(@Url url: String, @Body params: RequestBody): ApiCall<String>

    @GET
    fun get(@Url url: String): ApiCall<String>

}
object ApiNetwork{
    private val service = ServiceCreator.create(ApiServices::class.java)

    fun get(url: String){
        service.get(url).enqueue(object : ApiCallback<String> {
            override fun success(response: Response<String>?) {
                super.success(response)
                // String 结果进行处理
            }

            override fun error(response: Response<*>?, t: Throwable?) {
                super.error(response, t)
            }

            override fun onComplete() {

            }
        })
    }
}
object ServiceCreator {

    fun <T> create(service: Class<T>): T = create().create(service)

    private fun create(): Retrofit {
        val okHttpClient = OkHttpClient().newBuilder()
        if (BuildConfig.NetTest) {
            okHttpClient.addInterceptor(HttpLoggingInterceptor().apply {
                this.level = HttpLoggingInterceptor.Level.BODY
            })
        }
        val build = okHttpClient
            .retryOnConnectionFailure(true)
            .writeTimeout(60, TimeUnit.SECONDS)
            .connectTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .proxy(Proxy.NO_PROXY) // 有效避免抓包,请求不走任何协议, 未测试,谨慎使用
            .build()
        return Retrofit.Builder()
            .client(build)
            .baseUrl("https://www.google.com/")
            .addCallAdapterFactory(ErrorHandlingCallAdapterFactory())
            .addConverterFactory(ScalarsConverterFactory.create())
//                .addConverterFactory(MoshiConverterFactory.create())
//                .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}
interface ApiCallback<T> {
    /** Called for [200, 300) responses.  */
    fun success(response: Response<T>?) {
    }

    /** Called for 401 responses.  */
    fun unauthenticated(response: Response<*>?) {
        error(response)
    }

    /** Called for [400, 500) responses, except 401.  */
    fun clientError(response: Response<*>?) {
        error(response)
    }

    /** Called for [500, 600) response.  */
    fun serverError(response: Response<*>?) {
        error(response)
    }

    /** Called for network errors while making the call.  */
    fun networkError(e: Exception?) {
        error(t = e)
    }

    /** Called for unexpected errors while making the call.  */
    fun unexpectedError(t: Throwable?) {
        error(t = t)
    }

    fun error(response: Response<*>? = null, t: Throwable? = null) {
    }

    fun onComplete()
}
interface ApiCall<T> {
    fun cancel()
    fun enqueue(callback: ApiCallback<T>?)
    fun clone(): ApiCall<T> // Left as an exercise for the reader...
    fun isRunning(): Boolean
}
class ErrorHandlingCallAdapterFactory : CallAdapter.Factory() {
    @Nullable
    override fun get(
        returnType: Type,
        annotations: Array<Annotation>,
        retrofit: Retrofit
    ): CallAdapter<*, *>? {
        if (getRawType(returnType) != ApiCall::class.java) {
            return null
        }
        check(returnType is ParameterizedType) { "ApiCall must have generic type (e.g., ApiCall<ResponseBody>)" }
        val responseType =
            getParameterUpperBound(
                0,
                returnType
            )
        val callbackExecutor = retrofit.callbackExecutor()
        return ErrorHandlingCallAdapter<Any>(
            responseType,
            callbackExecutor
        )
    }

    private class ErrorHandlingCallAdapter<R> internal constructor(
        private val responseType: Type,
        private val callbackExecutor: Executor?
    ) :
        CallAdapter<R, ApiCall<R>> {
        override fun responseType(): Type {
            return responseType
        }

        override fun adapt(call: Call<R>): ApiCall<R> {
            return ApiCallAdapter(
                call,
                callbackExecutor
            )
        }

    }
}

/** Adapts a [Call] to [ApiCall].  */
internal class ApiCallAdapter<T>(
    private val call: Call<T>,
    private val callbackExecutor: Executor?
) :
    ApiCall<T> {
    override fun cancel() {
        call.cancel()
    }

    var isAsyncRunning = false
    override fun enqueue(callback: ApiCallback<T>?) {
        isAsyncRunning = true
        call.enqueue(
            object : Callback<T> {
                override fun onResponse(
                    call: Call<T>,
                    response: Response<T>
                ) {
                    try {
                        when (response.code()) {
                            in 200..299 -> {
                                callback?.success(response)
                            }
                            401 -> {
                                callback?.unauthenticated(response)
                            }
                            in 400..499 -> {
                                callback?.clientError(response)
                            }
                            in 500..599 -> {
                                callback?.serverError(response)
                            }
                            else -> {
                                callback?.unexpectedError(RuntimeException("Unexpected response $response"))
                            }
                        }
                    } catch (e: Exception) {

                    }
                    try {
                        callback?.onComplete()
                    } catch (e: Exception) {
                    }
                    isAsyncRunning = false
                }

                override fun onFailure(
                    call: Call<T>,
                    t: Throwable
                ) {
                    try {
                        if (t is Exception) {
                            callback?.networkError(t)
                        } else {
                            callback?.unexpectedError(t)
                        }
                    } catch (e: Exception) {

                    }
                    try {
                        callback?.onComplete()
                    } catch (e: Exception) {
                    }
                    isAsyncRunning = false
                }
            })
    }

    override fun clone(): ApiCall<T> {
        return ApiCallAdapter(
            call.clone(),
            callbackExecutor
        )
    }

    override fun isRunning(): Boolean {
        return isAsyncRunning
    }


}

5.2 推荐写法

AndroidStudio 插件需求:JSON To Kotlin Class

可以对下面代码进行修改,返回String自己转换(推荐,能更灵活的混淆)

val viewModel: HomePageViewModel = viewModel()
val bannerData by viewModel.bannerState.observeAsState(PlayLoading)

              if (bannerData !is PlaySuccess<*>) {
                            viewModel.getBanner()
              }
data class BaseModel<T>(
    val `data`: T,
    val errorCode: Int,
    val errorMsg: String
)
sealed class PlayState<out R> {
    fun isLoading() = this is PlayLoading
    fun isSuccessful() = this is PlaySuccess

    override fun toString(): String {
        return when (this) {
            is PlaySuccess<*> -> "Success[data=$data]"
            is PlayError -> "Error[exception=${error}]"
            PlayLoading -> "Loading"
        }
    }
}

data class PlaySuccess<out T>(val data: T) : PlayState<T>()
data class PlayError(val error: Throwable) : PlayState<Nothing>()
object PlayLoading : PlayState<Nothing>()

/**
 * [PlayState.data] if [Result] is of query [PlayState]
 */
fun <T> PlayState<T>?.successOr(fallback: T): T {
    if (this == null) return fallback
    return (this as? PlaySuccess<T>)?.data ?: fallback
}

val <T> PlayState<T>.data: T?
    get() = (this as? PlaySuccess)?.data
abstract class BaseArticleViewModel(application: Application) : AndroidViewModel(application) {

    abstract val repositoryArticle: BaseArticlePagingRepository

}
class HomePageViewModel(application: Application) : BaseArticleViewModel(application) {

    override val repositoryArticle: BaseArticlePagingRepository
        get() = HomeArticlePagingRepository()

    private var bannerJob: Job? = null

    private val _bannerState = MutableLiveData<PlayState<List<BannerBean>>>()

    val bannerState: LiveData<PlayState<List<BannerBean>>>
        get() = _bannerState



    fun getBanner() {
        bannerJob?.cancel()
        bannerJob = viewModelScope.launch(Dispatchers.IO) {
            (repositoryArticle as HomeArticlePagingRepository).getBanner(_bannerState)
        }
    }

}
abstract class BaseArticlePagingRepository {}
class HomeArticlePagingRepository : BaseArticlePagingRepository() {

    suspend fun getBanner(state: MutableLiveData<PlayState<List<BannerBean>>>) {
        state.postValue(PlayLoading)
        try {
            val bannerResponse = PlayAndroidNetwork.getBanner()
            if (bannerResponse.errorCode == 0) {
                val bannerList = bannerResponse.data
                bannerList.forEach {
                    it.data = it.imagePath
                }
                state.postValue(PlaySuccess(bannerList))
            } else {
                state.postValue(PlayError(RuntimeException("response status is ${bannerResponse.errorCode}  msg is ${bannerResponse.errorMsg}")))
            }
        } catch (e: Exception) {
            if (e is HttpException) {
                state.postValue(PlayError(RuntimeException("response status is ${e.code()}  msg is ${e.message()}")))
            } else {
                state.postValue(PlayError(RuntimeException("response status is unKnow"))) 
            }
        }
    }

}
object PlayAndroidNetwork{
      private val homePageService = ServiceCreator.create(HomePageService::class.java)

    suspend fun getBanner() = homePageService.getBanner()
}
interface HomePageService {
    @GET
    suspend fun get(@Url url: String): String
    @GET("banner/json")
    suspend fun getBanner(): BaseModel<List<BannerBean>>

}
object ServiceCreator {


    private fun create(): Retrofit {
        // okHttpClientBuilder
        val okHttpClientBuilder = OkHttpClient().newBuilder().apply {
            connectTimeout(30L, TimeUnit.SECONDS)
            readTimeout(10L, TimeUnit.SECONDS)
            addInterceptor(HttpLoggingInterceptor().apply {
                this.level = HttpLoggingInterceptor.Level.BODY
            })
           
        }

        return RetrofitBuild(
            url = "https://www.google.com/",
            client = okHttpClientBuilder.build(),
            gsonFactory = GsonConverterFactory.create()
        ).retrofit
    }

    /**
     * get ServiceApi
     */
    fun <T> create(service: Class<T>): T = create().create(service)



}
class RetrofitBuild(
    url: String, client: OkHttpClient,
    gsonFactory: GsonConverterFactory
) {
    val retrofit: Retrofit = Retrofit.Builder().apply {
        baseUrl(url)
        client(client)
        addConverterFactory(ScalarsConverterFactory.create())
        addConverterFactory(gsonFactory)
  
    }.build()
}

6 使用Page+Flow请求分页数据

class HomePagingSource : PagingSource<Int, ItemBean>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ItemBean> {
        return try {
            val page = params.key ?: 1 // set page 1 as default
            val articleList = getPageData(page)
            val prevKey = if (page > 1) page - 1 else null
            val nextKey = if (articleList.isNotEmpty()) page + 1 else null
            LoadResult.Page(articleList, prevKey, nextKey)
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, ItemBean>): Int? =null

    suspend fun getPageData(page: Int): List<ItemBean> {
        val apiResponse = PlayAndroidNetwork.getPageData(page)
        return apiResponse.data.datas
    }
}
class HomeArticlePagingRepository : BaseArticlePagingRepository() {
 @ExperimentalPagingApi
    override fun getPagingData(query: Query) = Pager(
        PagingConfig(
            pageSize = 15,
            enablePlaceholders = false
        )
    ) {
        HomePagingSource()
    }.flow

}
class HomePageViewModel(application: Application) : AndroidViewModel(application) {

    val repositoryArticle = HomeArticlePagingRepository()

    private val searchResults = MutableSharedFlow<Query>(replay = 1)

    @OptIn(ExperimentalCoroutinesApi::class)
    val dataResult: Flow<PagingData<ItemBean>> =   repositoryArticle.getPagingData(Query()).cachedIn(viewModelScope)


}
val viewModel: HomePageViewModel = viewModel()
val lazyPagingItems = viewModel.dataResult.collectAsLazyPagingItems()
 
 val listState = rememberLazyListState()
 val context = LocalContext.current
    LazyColumn(
        modifier = modifier,
        state = listState
    ) {

        items(lazyPagingItems) { data ->
            // do what you do
        }
        val loadStates = lazyPagingItems.loadState
        when {
            loadStates.refresh is LoadState.Loading -> {
               
            }
            loadStates.append is LoadState.Loading -> {
                
            }
            loadStates.refresh is LoadState.Error -> {
               
            }
            loadStates.append is LoadState.Error -> {
                 val e = lazyPagingItems.loadState.append as LoadState.Error
                showToast(context, e.error.localizedMessage ?: "")
                item {
                    Row(
                        modifier = Modifier.fillMaxWidth().padding(8.dp),
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.Center,
                    ) {
                        Button(
                            onClick = { lazyPagingItems.retry() }) {
                            Text("Retry")
                        }
                    }
                }
            }
        }
    }

7 沉浸式状态栏


/**
 * 设置透明状态栏
 */
fun Activity.transparentStatusBar() {
    transparentStatusBar(window)
}

private fun transparentStatusBar(window: Window) {
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
    val option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    val vis = window.decorView.systemUiVisibility
    window.decorView.systemUiVisibility = option or vis
    window.statusBarColor = Color.TRANSPARENT
}


/**
 * 状态栏反色
 */
fun Activity.setAndroidNativeLightStatusBar() {
    val decor = window.decorView
    val isDark = resources.configuration.uiMode == 0x21
    if (!isDark) {
        decor.systemUiVisibility =
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    } else {
        decor.systemUiVisibility =
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    }
}
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        transparentStatusBar()
        setAndroidNativeLightStatusBar()
        setContent {
            PlayAndroidTheme {
                ProvideWindowInsets { // 沉浸式状态栏 
                     Column(modifier = Modifier.background(color = MaterialTheme.colors.primary)) {
        Spacer(Modifier.statusBarsHeight()). // 状态栏高度
                  }
                }
            }
        }
    }

8 ViewModel 三板斧

class BaseViewModel(application: Application) : AndroidViewModel(application) {


    private val _position = MutableLiveData(0)
    val position: LiveData<Int> = _position

    fun onPositionChanged(position: Int) {
        _position.value = position
    }

}
val treePosition by viewModel.position.observeAsState(0)

9 添加 Android View


@Composable
fun rememberWebViewWithLifecycle(): WebView {
    val context = LocalContext.current
    val webView = remember {
        WebView(context)
    }
    val lifecycleObserver = rememberWebViewLifecycleObserver(webView)
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    DisposableEffect(lifecycle) {
        lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycle.removeObserver(lifecycleObserver)
        }
    }

    return webView
}

@Composable
private fun rememberWebViewLifecycleObserver(webView: WebView): LifecycleEventObserver =
    remember(webView) {
        LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_RESUME -> webView.onResume()
                Lifecycle.Event.ON_PAUSE -> webView.onPause()
                Lifecycle.Event.ON_DESTROY -> webView.destroy()
                else -> Log.e("WebView", event.name)
            }
        }
    }
    val webView = rememberWebViewWithLifecycle() 

 AndroidView(
                factory = { webView },
                modifier = Modifier
                    .fillMaxSize()
            ) { view ->
                view.webViewClient = object : WebViewClient() {
                    override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
                        return try {
                            if (url.startsWith("http:") || url.startsWith("https:")) {
                                view!!.loadUrl(url)
                            } else {
                                val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
                                webView.context.startActivity(intent)
                            }
                            true
                        } catch (e: Exception) {
                            false
                        }
                    }
                }
                val settings: WebSettings = view.settings
                settings.mixedContentMode =
                    WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
                settings.javaScriptEnabled = true //启用js
                settings.blockNetworkImage = false //解决图片不显示
                view.loadUrl("https://www.baidu.com")
            }

10 XML使用 ComposeView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <EditText
        android:id="@+id/mainEditName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="50dp"
        android:layout_marginBottom="20dp"
        android:hint="name" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/composeView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="50dp" />

</LinearLayout>
composeView.setContent {
                Button(onClick = {
                    Toast.makeText(this@MainActivity,mainEditName.text.toString(),Toast.LENGTH_LONG).show()
                }){
                    Text("ComposeView")
                }
            }

~其他

~.1 判断横竖屏

// 当前是否为横屏
val isLand = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE

END

相关文章

网友评论

      本文标题:Compose 项目笔记概要

      本文链接:https://www.haomeiwen.com/subject/qfxnydtx.html