Android compose 的基本环境搭建
1.创建项目
导入版本
1.gradle/libs.versions.toml
[versions]
accompanistPermissions = "0.36.0"
agp = "8.5.0-beta01"
coilCompose = "2.7.0"
constraintlayoutComposeVersion = "1.0.1"
hiltAndroid = "2.51.1"
hiltNavigationCompose = "1.2.0"
kotlin = "1.9.25"
coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
activityCompose = "1.9.2"
composeBom = "2024.09.01"
kotlinxSerializationJson = "1.6.3"
lifecycleViewmodelKtx = "2.8.5"
composeRuntime = "1.7.1"
lingver = "1.3.0"
loggingInterceptorVersion = "4.12.0"
navigationCompose = "2.8.0"
retrofit = "2.11.0"
roomRuntime = "2.6.1"
rxandroid = "2.1.1"
rxandroidVersion = "3.0.1"
rxjava = "2.2.21"
rxjava3 = "3.1.9"
rxlifecycle = "3.1.0"[libraries]androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
#region viewmodel livedata compose
androidx-lifecycle-extensions = { module = "androidx.lifecycle:lifecycle-extensions", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "lifecycleViewmodelKtx" }
constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayoutComposeVersion" }
runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata", version.ref = "composeRuntime" }
androidx-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "composeRuntime" }
androidx-runtime-rxjava2 = { module = "androidx.compose.runtime:runtime-rxjava2" }
#endregion compose#region kotlin serialization
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
#endregion#region hilt
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
#endregion#region navigation
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
#endregion#region retrofit2 and okhttp3
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptorVersion" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
converter-scalars = { module = "com.squareup.retrofit2:converter-scalars", version.ref = "retrofit" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }#endregion okhttp3#region rxjava 2
adapter-rxjava2 = { module = "com.squareup.retrofit2:adapter-rxjava2", version.ref = "retrofit" }
rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroid" }
rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" }
#endregion#region rxjava 3
rxjava3-rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroidVersion" }
rxjava3-rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava3" }
adapter-rxjava3 = { module = "com.squareup.retrofit2:adapter-rxjava3", version.ref = "retrofit" }
rxlifecycle = { module = "com.trello.rxlifecycle3:rxlifecycle", version.ref = "rxlifecycle" }
rxlifecycle-android-lifecycle-kotlin = { module = "com.trello.rxlifecycle3:rxlifecycle-android-lifecycle-kotlin", version.ref = "rxlifecycle" }
rxlifecycle-components = { module = "com.trello.rxlifecycle3:rxlifecycle-components", version.ref = "rxlifecycle" }#endregion#region room
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }#endregion#region coil
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
#endregion#region accompanist
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
#endregion#region 国际化
lingver = { module = "com.github.YarikSOffice:lingver", version.ref = "lingver" }
#endregionjunit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
我整理好的版本,都用过了适配
2.模块目录下的 build.gradle.kts
plugins {alias(libs.plugins.android.application)alias(libs.plugins.jetbrains.kotlin.android)id("kotlin-kapt")id("com.google.dagger.hilt.android")kotlin("plugin.serialization")id("androidx.room")
}android {namespace = "com.composeapp"compileSdk = 34defaultConfig {applicationId = "com.composeapp"minSdk = 24targetSdk = 34versionCode = 1versionName = "1.0"testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"vectorDrawables {useSupportLibrary = true}}room {schemaDirectory("$projectDir/schemas")}buildTypes {release {isMinifyEnabled = falseproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),"proguard-rules.pro")}}compileOptions {sourceCompatibility = JavaVersion.VERSION_1_8targetCompatibility = JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = "1.8"}buildFeatures {compose = true}composeOptions {kotlinCompilerExtensionVersion = "1.5.15"}packaging {resources {excludes += "/META-INF/{AL2.0,LGPL2.1}"}}}dependencies {implementation(libs.androidx.core.ktx)implementation(libs.androidx.activity.compose)implementation(platform(libs.androidx.compose.bom))implementation(libs.androidx.ui)implementation(libs.androidx.ui.graphics)implementation(libs.androidx.ui.tooling.preview)implementation(libs.androidx.material3)testImplementation(libs.junit)androidTestImplementation(libs.androidx.junit)androidTestImplementation(libs.androidx.espresso.core)androidTestImplementation(platform(libs.androidx.compose.bom))androidTestImplementation(libs.androidx.ui.test.junit4)debugImplementation(libs.androidx.ui.tooling)debugImplementation(libs.androidx.ui.test.manifest)implementation(libs.kotlinx.serialization.json)//region ViewModel Livedata compose runtimeimplementation(libs.androidx.lifecycle.viewmodel.ktx)implementation(libs.androidx.lifecycle.viewmodel.compose)implementation(libs.androidx.lifecycle.livedata.ktx)implementation(libs.androidx.lifecycle.runtime.ktx)implementation(libs.androidx.lifecycle.lifecycle.runtime.compose)implementation(libs.androidx.lifecycle.viewmodel.savedstate)implementation(libs.androidx.runtime)implementation(libs.runtime.livedata)//endregion// region hiltimplementation(libs.hilt.android)kapt(libs.hilt.android.compiler)implementation(libs.androidx.hilt.navigation.compose)// endregion//region navigationimplementation(libs.androidx.navigation.compose)//endregion navigation//region retrofit2 okhttp//Retrofit 核心库implementation(libs.retrofit)//响应数据自动序列化//JSONimplementation(libs.converter.gson)//String类型implementation(libs.converter.scalars)//拦截器 loggingimplementation(libs.okhttp3.logging.interceptor)//endregion//region rxjava2
// implementation(libs.androidx.runtime.rxjava2)
// implementation(libs.adapter.rxjava2)
// implementation(libs.rxjava)
// implementation(libs.rxandroid)//endregion//region rxjava3implementation(libs.rxjava3.rxjava)implementation(libs.adapter.rxjava3)implementation(libs.rxlifecycle)implementation(libs.rxlifecycle.android.lifecycle.kotlin)implementation(libs.rxlifecycle.components)implementation(libs.rxjava3.rxandroid)//endregion//region roomimplementation(libs.androidx.room.runtime)annotationProcessor(libs.androidx.room.compiler)kapt(libs.androidx.room.compiler)//endregion//region coil 异步网络图片加载implementation(libs.coil.compose)//endregion//region accompanistimplementation(libs.accompanist.permissions)//endregion//region 国际化implementation(libs.lingver)// endregion//region compose 约束布局implementation (libs.constraintlayout.compose)//endregion}
3.工程目录下的 build.gradle.kts
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {alias(libs.plugins.android.application) apply falsealias(libs.plugins.jetbrains.kotlin.android) apply falseid("com.google.dagger.hilt.android") version "2.51.1" apply falsekotlin("kapt") version "1.9.25" apply falsekotlin("jvm") version "1.9.25" apply false // or kotlin("multiplatform") or any other kotlin pluginkotlin("plugin.serialization") version "1.9.25" apply falseval room_version = "2.6.1"id("androidx.room") version room_version apply false
}
4.工程目录下setting.gradle.kts
pluginManagement {repositories {google {content {includeGroupByRegex("com\\.android.*")includeGroupByRegex("com\\.google.*")includeGroupByRegex("androidx.*")}}mavenCentral()gradlePluginPortal()}
}
dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()mavenCentral()maven("https://jitpack.io")}
}rootProject.name = "ComposeApp"
include(":app")
2.搭建Hilt环境
1.创建Application
@HiltAndroidApp
class MyApplication : Application() {companion object {const val SharedPreferencesFileName = "MyApplication";private lateinit var application: Application;lateinit var sharedPreferences: SharedPreferencesfun getApplication(): Application {return application;}}@Overrideoverride fun onCreate() {super.onCreate()
// Log.i("测试","app启动了 child="+child)application = thissharedPreferences =getSharedPreferences(SharedPreferencesFileName, Context.MODE_PRIVATE);/*** 国际化绑定 已经帮存入 sharedPreferences里了*/Lingver.init(this)}}
加上注解 @HiltAndroidApp
2.在用到注入的Activity上加上注解
@AndroidEntryPoint
class MainActivity : ComponentActivity() {/*** 将依赖注入好的viewModel放入这里*/private val viewModel: XianPageViewModel by viewModels()@Injectlateinit var child: Child;override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContent {val sharedPreferences = MyApplication.sharedPreferencesLog.i("share", "这是shared 全局 $sharedPreferences")// sharedPreferences.edit().putString("token", "123456").apply()//关闭 导航状态栏// 隐藏状态栏if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {window.insetsController?.hide(WindowInsets.Type.statusBars())} else {@Suppress("DEPRECATION")window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN}this.lifecycleScopeval string = sharedPreferences.getString("token", "")Log.i("share", "获取token 全局 $string")ComposeAppTheme {
// ScaffoldExample()
// MyApp()
// NamePageRxjava3()
// NamePage()
// RoomTestPage()
// NfcPage()
// NfcMain()
// ImageIconPage()
// PermissionPage()// LiveDataTest()
// LanguageSelector()LanguageSelectorKuangjia()}}// Log.i("测试","activity启动了 this="+this)
// Log.i("测试","activity启动了 child="+child.activity)
// Log.i("测试","activity启动了 child="+child.application)Log.i("测试", "activity启动了 viewModel=" + viewModel)}}
3.测试注入
/*** 第一种: 相当于spring @Component*/
@ActivityScoped
class Child @Inject constructor(@ActivityContext val activity: Context,@ApplicationContext val application: Context){val name = "你好"
// val applicationContext = application}
3.注入网络模块
1.单例类
/*** 全局唯一网络模块*/
@Module
@InstallIn(SingletonComponent::class)
object NetModel {private const val Tag = "Retrofit:";private const val URL = "http://192.168.202.57:8080";@Singleton@Providesfun provideOkHttpClient(tokenInterceptor: TokenInterceptor): OkHttpClient {//构建日志拦截器val httpLoggingInterceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {override fun log(message: String) {Log.i(Tag, message)}}).setLevel(HttpLoggingInterceptor.Level.BODY)//构建return OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).addInterceptor(tokenInterceptor).addInterceptor(httpLoggingInterceptor).build()}@RxJava2Inject@Singleton@Providesfun provideRetrofitRxJava2(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(URL).client(okHttpClient)// .addConverterFactory(ScalarsConverterFactory.create())//添加Gson转换器.addConverterFactory(GsonConverterFactory.create())//添加Gson转换器//添加Rxjava适配
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()}@RxJava3Inject@Singleton@Providesfun provideRetrofitRxJava3(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(URL).client(okHttpClient).addCallAdapterFactory(RxJava3CallAdapterFactory.create())
// .addConverterFactory(ScalarsConverterFactory.create())//添加Gson转换器.addConverterFactory(GsonConverterFactory.create())//添加Gson转换器//添加Rxjava适配.build()}@Singleton@Providesfun provideMainTestService(@RxJava3Inject retrofit: Retrofit): MainTestService {return retrofit.create(MainTestService::class.java)}}
2.Token拦截器
@Singleton
class TokenInterceptor @Inject constructor() : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()val token: String = MyApplication.sharedPreferences.getString("token", "2001").toString()val url = request.url.newBuilder().addQueryParameter("token1", token).build()val header = request.headers.newBuilder().add("token2", token).build()val requestNew = request.newBuilder().url(url).headers(header).build()return chain.proceed(requestNew);}
}
3.要是注入两个相同类型的
创建自定义注解
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class RxJava2Inject
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class RxJava3Inject
在构造的时候声明
@RxJava2Inject@Singleton@Providesfun provideRetrofitRxJava2(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(URL).client(okHttpClient)// .addConverterFactory(ScalarsConverterFactory.create())//添加Gson转换器.addConverterFactory(GsonConverterFactory.create())//添加Gson转换器//添加Rxjava适配
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()}@RxJava3Inject@Singleton@Providesfun provideRetrofitRxJava3(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(URL).client(okHttpClient).addCallAdapterFactory(RxJava3CallAdapterFactory.create())
// .addConverterFactory(ScalarsConverterFactory.create())//添加Gson转换器.addConverterFactory(GsonConverterFactory.create())//添加Gson转换器//添加Rxjava适配.build()}
注入的时候声明注入哪个
@Singleton@Providesfun provideMainTestService(@RxJava3Inject retrofit: Retrofit): MainTestService {return retrofit.create(MainTestService::class.java)}
4.声明接口
interface MainTestService {// @GET("/phone/gis/test")
// suspend fun getTestData(): Response<String>@GET("/phone/gis/test")suspend fun getTestDataCall(): Call<TestDto>@GET("/phone/gis/test")suspend fun getTestData(): Response<TestDto>@GET("/phone/gis/test/{name}")suspend fun getTestDataTruth(@Path("name") name: String, @Query("age") age: Int): TestDto/*** 使用Rxjava 不允许加 suspend 关键字*/@GET("/phone/gis/test/{name}")fun getTestDataRx(@Path("name") name: String, @Query("age") age: Int): Observable<TestDto>}
RxJava写 接口,千万不要声明suspend ,要不然序列化JSON 会报错,无法实例化 Observable
5.要是发送http请求还需要权限
network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><base-config cleartextTrafficPermitted="true" />
</network-security-config>
6.网络权限
<uses-permission android:name="android.permission.INTERNET"/>
4.ViewModel
/*** viewmodel不能设置作用域,否则会报错** ViewModel 部分中提到的 viewModel() 函数会自动使用 Hilt 通过 @HiltViewModel 注解构建的 ViewModel*/
@HiltViewModel
class ScopePageViewModel @Inject constructor():ViewModel() {init {val job: Job = viewModelScope.launch { }//可以单独取消}}
然后注入就可以了
5.LiveData
@HiltViewModel
class LiveDataTestViewModel @Inject constructor() : ViewModel() {val items:MutableLiveData<MutableList<Item>> = MutableLiveData(mutableListOf())init {val item1 = Item("测试模块1",true)items.value?.add(item1)val item2 = Item("测试模块2",true)items.value?.add(item2)}fun updateItem(item:Item){item.updateIsShow(!item.isShow.value!!)}}
用法
@Composable
fun LiveDataTest(viewModel: LiveDataTestViewModel = hiltViewModel()) {val items = viewModel.items.observeAsState(mutableListOf())LazyColumn(modifier = Modifier.fillMaxSize().padding(30.dp)) {item {Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround) {val width = Modifier.width(150.dp).height(50.dp)Button(onClick = {viewModel.updateItem(items.value[0])}, modifier = width) {Text(text = stringResource(R.string._1))}Button(onClick = { viewModel.updateItem(items.value[1]) }, modifier = width) {Text(text = "隐藏或打开测试模块2")}}}items(items.value) {ItemComponent(item = it)}}}
6.整合navigation
@Serializable
data class Profile(val name: String)@Serializable
object FriendsList@Serializable
object ProfileScreenChild2@Serializable
data class ProfileScreenChild1(val param: String)@Serializable
data class Aaa(val param: String)// Define the ProfileScreen composable.
@Composable
fun ProfileScreen(profile: Profile,parentViewModel: XianPageViewModel?,viewModel: XianPageViewModel = hiltViewModel(),onNavigateToFriendsList: () -> Unit) {// ProfileScreen启动了com.composeapp.ui.view.XianPageViewModel@841f9de这是viewmodelLog.i("测试", "ProfileScreen启动了" + viewModel.toString() + "这是viewmodel")Log.i("测试","ProfileScreen启动了 这是父级的viewModel实例" + parentViewModel?.toString() + "这是父类viewmodel")Column {Text("导航参数: ${profile.name}")ProfileScreenChild()Button(onClick = { onNavigateToFriendsList() }) {Text("Go to Friends List")}}
}@Composable
fun ProfileScreenChild(viewModel: XianPageViewModel = hiltViewModel()) {
// ProfileScreenChild启动了com.composeapp.ui.view.XianPageViewModel@841f9de这是viewmodelLog.i("测试", "ProfileScreenChild启动了" + viewModel.toString() + "这是viewmodel") //在同一个导航中是一致的
// Text("子组件的值: ${viewModel.data.value}")val childNavController = rememberNavController()NavHost(childNavController, startDestination = ProfileScreenChild1("你好测试子组件Pchild1")) {composable<ProfileScreenChild2> { backStackEntry ->//专门用来拿到当前导航的堆栈里的ViewModelval parentEntry = remember(backStackEntry) {//必须类型和值都一样才能找到,否则抛异常childNavController.getBackStackEntry(ProfileScreenChild1("你好测试子组件Pchild1"))//必须值要一样才能找到,值不一致就找不到,跟序列化JSON比}
// viewmodel 当组件移除组件树的时候,将会被销毁val parentViewModel = hiltViewModel<XianPageViewModel>(parentEntry)ProfileScreenChild2(parentViewModel)}composable<ProfileScreenChild1> { backStackEntry ->val child1 = backStackEntry.toRoute<ProfileScreenChild1>()ProfileScreenChild1(profileScreenChild1 = child1) {childNavController.navigate(ProfileScreenChild2)}}}}@Composable
fun ProfileScreenChild2(parentViewModel: XianPageViewModel,viewModel: XianPageViewModel = hiltViewModel()
) {Log.i("测试","ProfileScreenChild2启动了" + parentViewModel.toString() + "这是parent viewmodel") //在同一个导航中是一致的Log.i("测试","ProfileScreenChild2启动了" + viewModel.toString() + "这是viewmodel") //在同一个导航中是一致的Log.i("测试","ProfileScreenChild2启动了" + (viewModel === parentViewModel) + "这是viewmodel") //在同一个导航中是一致的Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {Button(onClick = { /*TODO*/ }) {Text(text = "ProfileScreenChild2")}}
}@Composable
fun ProfileScreenChild1(viewModel: XianPageViewModel = hiltViewModel(),profileScreenChild1: ProfileScreenChild1,toProChild2: () -> Unit
) {Log.i("测试","ProfileScreenChild1启动了" + viewModel.toString() + "这是viewmodel 这是param${profileScreenChild1.param}") //在同一个导航中是一致的Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {Button(onClick = { toProChild2() }) {Text(text = "ProfileScreenChild1")}}}// Define the FriendsListScreen composable.
@Composable
fun FriendsListScreen(onNavigateToProfile: (String) -> Unit,viewModel: XianPageViewModel = hiltViewModel()
) {Log.i("测试", "FriendsListScreen启动了" + viewModel.toString() + "这是viewmodel")val (text, setText) = remember { mutableStateOf("") }Column {Text("Friends List")TextField(value = text, onValueChange = setText)Button(onClick = { onNavigateToProfile(text) }) {Text("Go to Profile")}}
}// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp(viewModel: XianPageViewModel = viewModel()) {Log.i("测试", "MyApp启动了 viewmodel=$viewModel")//是Activity同一个val navController = rememberNavController()NavHost(navController, startDestination = FriendsList) {composable<Profile> { backStackEntry ->
// 获取路由对象实例val profile: Profile = backStackEntry.toRoute()Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {ProfileScreen(profile = profile,parentViewModel = null,onNavigateToFriendsList = {navController.navigate(route = FriendsList)})}}composable<FriendsList> {Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {FriendsListScreen(onNavigateToProfile = { it ->navController.navigate(route = Profile(name = "传入了新的参数:${it}"))})}}}
}
7.整合room
1.实体类
@Entity
@Serializable
data class User(@PrimaryKey val uid:String,@ColumnInfo(name = "name")val name:String,val time:Long,val date:String?=null,val age :Int
) {
}
2.dao
@Dao
interface UserDao {@Query("SELECT * FROM user")fun getAll(): List<User>@Query("SELECT * FROM user WHERE uid = :userId")fun loadAllByIds(userId: String): List<User>@Query("SELECT * FROM user WHERE name LIKE :name")fun findByName(name: String): List<User>@Insertfun insertAll(vararg users: User)@Deletefun delete(user: User)}
3.数据库
@Database(entities = [User::class], version = 3, exportSchema = true)
abstract class AppDataBase: RoomDatabase() {companion object{private const val DB_NAME = "my_app.db"fun getInstance(context: Context): AppDataBase{val db = Room.databaseBuilder(context,AppDataBase::class.java, DB_NAME).addMigrations(MIGRATION_1_2,MIGRATION_2_3).build()return db;}val MIGRATION_1_2 = object : Migration(1, 2) {override fun migrate(db: SupportSQLiteDatabase) {// 执行 SQL 语句来迁移数据库db.execSQL("ALTER TABLE user ADD COLUMN date TEXT")}}val MIGRATION_2_3 = object : Migration(2, 3) {override fun migrate(db: SupportSQLiteDatabase) {// 执行 SQL 语句来迁移数据库db.execSQL("ALTER TABLE user ADD COLUMN age INTEGER NOT NULL default 1")}}}/*** 数据库当中的一张表*/abstract fun UserDao():UserDao}
4.Dao实例注入
/*** 全局唯一数据库模块*/
@Module
@InstallIn(SingletonComponent::class)
object DataBaseModel {@Singleton@Providesfun provideAppDataBase(@ApplicationContext context:Context): AppDataBase {return AppDataBase.getInstance(context)}@Singleton@Providesfun provideUserDao(appDataBase: AppDataBase): UserDao {return appDataBase.UserDao()}}
8.Permission
@SuppressLint("CheckResult")
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun PermissionPage(){// Camera permission stateval cameraPermissionState = rememberPermissionState(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)Observable.fromCallable {Log.i("执行","执行在"+Thread.currentThread().name)"true"}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe({Log.i("执行","执行成功$it "+Thread.currentThread().name)},{Log.i("执行","执行失败"+it.message)})Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){if (cameraPermissionState.status.isGranted) {Text("相机权限授予成功")} else {Column {val textToShow = if (cameraPermissionState.status.shouldShowRationale) {// If the user has denied the permission but the rationale can be shown,// then gently explain why the app requires this permission"相机权限是核心. Please grant the permission."} else {// If it's the first time the user lands on this feature, or the user// doesn't want to be asked again for this permission, explain that the// permission is required"必须给予相机权限. " +"Please grant the permission"}Text(textToShow)Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {Text("Request permission")}}}}}
9.Kotlin json 序列化
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json@Serializable
data class Project(val name: String, val language: String)fun main() {// Serializing objectsval data = Project("kotlinx.serialization", "Kotlin")val string = Json.encodeToString(data)println(string) // {"name":"kotlinx.serialization","language":"Kotlin"}// Deserializing back into objectsval obj = Json.decodeFromString<Project>(string)println(obj) // Project(name=kotlinx.serialization, language=Kotlin)
}
10.kotlin 冷流
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlockingfun main() {runBlocking {val coldFlow = flow {for (i in 1..5){delay(3000)emit(i)}}// 订阅者 1launch {coldFlow.collect { value ->println("Subscriber 1 received: $value")}}// 订阅者 2launch {delay(2000) // 延迟订阅,确保第二个订阅者在第一个订阅者之后coldFlow.collect { value ->println("Subscriber 2 received${Thread.currentThread().name}: $value")}}println("执行")}}
11.kotlin 热流
import androidx.compose.runtime.collectAsState
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlockingfun main() {runBlocking {// 创建一个 StateFlow 实例,初始状态为 0val stateFlow = MutableStateFlow(0)// 启动一个协程来更新 StateFlow 的状态launch {repeat(5) {delay(500) // 模拟数据生成stateFlow.value += 1}}// 订阅 StateFlow 并打印最新状态launch {stateFlow.collect { value ->println("Subscriber received: $value")}}
}}
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlockingfun main() {runBlocking {// 创建一个 SharedFlowval sharedFlow = MutableSharedFlow<Int>()// 启动一个协程来发射数据launch {repeat(10) {delay(100) // 模拟异步操作sharedFlow.emit(it) // 发射数据}}// 订阅 SharedFlowlaunch {sharedFlow.collect { value ->println("Subscriber 1 received: $value")}}// 另一个订阅者launch {sharedFlow.collect { value ->println("Subscriber 2 received: $value")}}}
}
12.全局语言切换
在 Application created 方法
/*** 国际化绑定 已经帮存入 sharedPreferences里了*/Lingver.init(this)
@Composable
fun LanguageSelectorKuangjia(localeViewmodel: LocaleViewmodel= hiltViewModel()) {// val locale = MyApplication.sharedPreferences.getString("locale",null)
//
// val localeState = localeViewmodel.locale.observeAsState(if (locale == null) Locale.getDefault() else Locale(locale)
// locale?.let { Locale(it) }?:Locale.getDefault()
// )val current = LocalContext.currentColumn(modifier = Modifier.fillMaxSize().padding(30.dp)) {// Example buttons to switch languagesRow {Button(onClick = {Lingver.getInstance().setLocale(current,Locale.SIMPLIFIED_CHINESE)(current as Activity).recreate()}) {Text("中文")}Button(onClick = {Lingver.getInstance().setLocale(current,Locale.ENGLISH)(current as Activity).recreate()}) {Text("英文")}}// Apply the selected language
// LanguageSwitcherKuangjia(localeState.value) {// Your UI content here, which will reflect the selected languageText(text = stringResource(id = R.string._1))
// }}}
自己写的扩展函数
/*** 转变语言扩展函数 会保留在当前的Navigation 导航里*/
fun Context.changeLocale(locale: Locale) {Lingver.getInstance().setLocale(this,locale)(this as Activity).recreate()
}
在 导航中 重创建,也会跟随导航目的地,不会重置
13.使用 阿里矢量图
iconfont-阿里巴巴矢量图标库
1.存一个地方
2.下载这个插件
3.右键一个空文件夹
4.操作
14.文件分享
1.文件提供者
<providerandroid:authorities="${applicationId}.fileprovider"android:name="androidx.core.content.FileProvider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /></provider>
2. 外部存储空间权限文件
file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths><external-path name="external_files" path="."/>
</paths>
3. MediaStore
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;public class ExportMedia {public static Uri saveExcelFileToMediaStoreInExternalDownloadExport(Context context, String fileName) {// 创建 ContentValues 对象ContentValues values = new ContentValues();values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);values.put(MediaStore.MediaColumns.MIME_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS+"/EXPORT"); // 下载目录// 插入文件到 MediaStorereturn context.getContentResolver().insert(MediaStore.Files.getContentUri("external"), values);}public Uri queryExcelFiles(Context context, String fileName) {String[] projection = {MediaStore.Files.FileColumns._ID,MediaStore.Files.FileColumns.DISPLAY_NAME,MediaStore.Files.FileColumns.MIME_TYPE};String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=?";String[] selectionArgs = {"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}; // .xlsx 的 MIME 类型Cursor cursor = context.getContentResolver().query(MediaStore.Files.getContentUri("external"),projection,selection,selectionArgs,null);try {while (cursor.moveToNext()) {long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID));String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME));if (displayName.equals(fileName)){// 处理查询结果,例如分享文件return ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id);}}} catch (IllegalArgumentException e) {e.printStackTrace();}finally {if (cursor != null){cursor.close();}}return null;}
}
public class ShareUtils {public static void shareExcel(Context context,String path){// 文件路径,确保文件存在File fileToShare = new File(path);// 创建分享意图Intent shareIntent = new Intent(Intent.ACTION_SEND);shareIntent.setType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // MIME 类型// 使用 FileProvider 获取 content URIUri fileUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", fileToShare);shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);// 允许临时读取 URI 权限shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 启动分享对话框context.startActivity(Intent.createChooser(shareIntent, "分享一个Excel"));}public static void shareExcel(Context context,Uri uri){// 创建分享意图Intent shareIntent = new Intent(Intent.ACTION_SEND);shareIntent.setType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // MIME 类型// 使用 FileProvider 获取 content URIshareIntent.putExtra(Intent.EXTRA_STREAM, uri);// 允许临时读取 URI 权限shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 启动分享对话框context.startActivity(Intent.createChooser(shareIntent, "分享一个Excel"));}
}
<!--在sdcard中创建/删除文件的权限 --><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" tools:ignore="ProtectedPermissions" /><uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"tools:ignore="ScopedStorage" />
15. NFC
1.开启权限
<uses-permission android:name="android.permission.NFC" />
2.创建工具类
import android.nfc.tech.NfcA
import java.security.MessageDigestobject NfcHelper {private fun readUid(nfcA: NfcA): ByteArray {val command = byteArrayOf(xxxx.toByte(), xxxxx.toByte())val uid = nfcA.transceive(command)return byteArrayOf(uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7])}/*** 处理密码** @param nfcA* @return*/private fun handlePwd(nfcA: NfcA): ByteArray {val uidByte = readUid(nfcA).copyOf(7)val secret = byteArrayOf(xxxxxxxxxxxxxxxxxx)//两个数组重载运算符 拼接在一起val byteUnique = uidByte + secret//加密SHA -256val digest = MessageDigest.getInstance("SHA-256")// 更新 MessageDigest 实例,传入要哈希的数据digest.update(byteUnique)// 计算哈希值并返回 取前四位val pwdBytes = digest.digest().copyOf(4)return pwdBytes}/*** 新nfc芯片验证密码** @param nfcA* @return*/fun authNew(nfcA: NfcA) {val pwd = handlePwd(nfcA)//拼接两个数组val command = byteArrayOf(0x1B.toByte()) + pwdnfcA.transceive(command)}/*** 新nfc 芯片读方法** @param nfcA* @param startPage* @param endPage* @return*/fun readTag(nfcA: NfcA, startPage: Int, endPage: Int): List<Byte> {val list = arrayListOf<Byte>()for (i in startPage..endPage) {val data = nfcA.transceive(byteArrayOf(0x30, i.toByte()))list.addAll(data.asList().subList(0,4))}return list;}/*** 新nfc 芯片写方法** @param nfcA* @param writeByte* @param block 扇区,也就是页*/fun writeTag(nfcA: NfcA, writeByte: ByteArray, page: Int) {val cmd = byteArrayOf(0xA2.toByte(), page.toByte()) + writeBytenfcA.transceive(cmd)}}
3.Model
/*** 全局唯一NFC模块*/
@Module
@InstallIn(SingletonComponent::class)
object NfcModel {@Singleton@Providesfun provideNfcAdapter(@ApplicationContext context: Context): NfcAdapter {return NfcAdapter.getDefaultAdapter(context)}}
4.viewModel
import android.nfc.NfcAdapter
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.scopes.ActivityScoped
import javax.inject.Inject/*** viewmodel不能设置作用域,否则会报错*/
@HiltViewModel
class NfcViewModel @Inject constructor(val nfcAdapter: NfcAdapter):ViewModel() {// 使用 MutableLiveData 来持有数据}
5.页面
@Composable
fun NfcMain(){var open by remember { mutableStateOf(false) }Column(Modifier.fillMaxSize()) {Box(modifier = Modifier.fillMaxWidth().height(100.dp), contentAlignment = Alignment.Center){Button(onClick = { open = !open }) {Text(text = if (open) "点击关闭NFC" else "点击开启NFC")}}if (open){NfcPage()}else{Box(modifier = Modifier.fillMaxSize().background(Color.Blue), contentAlignment = Alignment.Center){Text(text = "NFC未开启")}}}}@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NfcPage(modifier: Modifier = Modifier, nfcViewModel: NfcViewModel = hiltViewModel()) {var presses by remember { mutableIntStateOf(0) }val readOrWrite = remember {mutableStateOf(false)}val (text, setText) = remember { mutableStateOf("") }val nfcAdapter = nfcViewModel.nfcAdapterval list = remember {mutableStateListOf<String>()}val context = LocalContext.currentLaunchedEffect(key1 = Unit) {val pendingIntent = PendingIntent.getActivity(context, 0, Intent(MyApplication.getApplication(), context::class.java).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_IMMUTABLE)val filters = arrayOf(IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED))val arrayOf = arrayOf(arrayOf<String>(NfcA::class.java.name))nfcAdapter.enableReaderMode(context as Activity?, { tag ->val nfcA = NfcA.get(tag)Log.i("NfcA", "NfcA: $nfcA")nfcA.timeout = 3000try {nfcA.connect()Log.i("测试","nfc超时时间 ${nfcA.timeout} ")Log.i("测试","nfc最大长度 ${nfcA.maxTransceiveLength} ")NfcHelper.authNew(nfcA)// NewNfcHelper.authNew(nfcA)if (readOrWrite.value){val readTag = NfcHelper.writeTag(nfcA, byteArrayOf(2,3,4,1), 4)Log.i("测试","写入成功 $readTag")}else{val readTag = NfcHelper.readTag(nfcA, 4, 4)Log.i("测试","读取成功 $readTag")}} catch (e: Exception) {e.printStackTrace()} finally {nfcA.close()}}, NfcAdapter.FLAG_READER_NFC_A, null);nfcAdapter.enableForegroundDispatch(context, pendingIntent, filters, arrayOf)}DisposableEffect(key1 = Unit) {this.onDispose {nfcAdapter.disableForegroundDispatch(context as Activity)nfcAdapter.disableReaderMode(context as Activity)}}Scaffold(topBar = {TopAppBar(colors = topAppBarColors(containerColor = MaterialTheme.colorScheme.primaryContainer,titleContentColor = MaterialTheme.colorScheme.primary,),title = {Box(modifier = Modifier.fillMaxWidth(),contentAlignment = Alignment.Center // 设置 Box 的内容居中) {TextField(value = text, onValueChange = setText,modifier = Modifier.width(200.dp).height(50.dp),// 调整宽度// 调整高度textStyle = MaterialTheme.typography.bodyLarge.copy(fontSize = 16.sp,lineHeight = TextUnit(200f, TextUnitType.Sp)), // 调整字体大小) // 调整内边距}})},bottomBar = {BottomAppBar(containerColor = MaterialTheme.colorScheme.primaryContainer,contentColor = MaterialTheme.colorScheme.primary,) {Row(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),horizontalArrangement = Arrangement.spacedBy(space = 12.dp),verticalAlignment = Alignment.CenterVertically) {Button(modifier = Modifier.weight(1f), onClick = {}) {Text(text = "添加")}Button(modifier = Modifier.weight(1f), onClick = {readOrWrite.value = !readOrWrite.value}) {Text(text = if (readOrWrite.value) "写入" else "读取")}Button(modifier = Modifier.weight(1f), onClick = {list.clear()}) {Text(text = "清空")}}}},floatingActionButton = {FloatingActionButton(onClick = { presses++ }) {Icon(Icons.Default.Build, contentDescription = "Add")}}) { innerPadding ->LazyColumn(modifier = Modifier.padding(innerPadding)) {items(items = list, key = { it }) {Text(text = it,modifier = Modifier.fillMaxWidth().height(40.dp),textAlign = TextAlign.Center)}}}
}
相关文章:
Android compose 的基本环境搭建
1.创建项目 导入版本 1.gradle/libs.versions.toml [versions] accompanistPermissions "0.36.0" agp "8.5.0-beta01" coilCompose "2.7.0" constraintlayoutComposeVersion "1.0.1" hiltAndroid "2.51.1" hiltNavi…...
git | 合并 commit 的两种方法
比如你最近的 3 次提交分别为 A B C,你想将它们合并成 X。 方案一 使用 git rebase -i HEAD~3 进入编辑: pick 0148079 A pick 29cae72 B pick bf8572a C修改: r 0148079 A f 29cae72 B f bf8572a C:wq 保存进入 commit 编辑页面,输入 X …...
Grafana链接iframe嵌入Web前端一直跳登录页面的问题记录
概述 公司有个项目使用到Grafana作为监控界面,因为项目方的环境极其复杂,仅物理隔离的环境就有三四个,而且每个都得部署项目,今天在某个环境测试,查看界面遇到一个比较奇怪的Grafana问题,后面针对该问题进行跟踪分析并解决,故而博文记录,用于备忘。 问题 登录项目We…...
后端Java-SpringBoot整合MyBatisPlus步骤(超详细)
1.新建项目。 2.点击完上一步的next之后,选择pom.xml文件中的依赖。 3.点击pom文件进行项目初始化。 按照下面的俩步骤刷新一下maven ,让文件生效 4.新建一个application.yml文件 5. 新建一个数据库mp,在数据库中新建一张user表 6.连接数据…...
8609 哈夫曼树
### 思路 1. **选择最小权值节点**:在哈夫曼树构建过程中,选择两个权值最小且父节点为0的节点。 2. **构建哈夫曼树**:根据权值构建哈夫曼树,确保左子树权值小于右子树权值。 3. **生成哈夫曼编码**:从叶子节点到根节点…...
docker的harbor仓库登录问题
目录 一、问题描述 二、证书信任问题 三、DNS解析问题 四、解决 参考链接:Docker login Harbor报错解决:Error response from daemon: Get https:..-阿里云开发者社区 一、问题描述 问题: 挂机或者挂机重启之后harbor登录不上 查看日…...
ENV | docker 安装使用(简单实操版)
1. 详细步骤 1.1 安装 sudo apt update sudo apt install docker.io1.2 验证(可跳过) docker -v1.3 使用 1.3.1 拉取镜像 # 镜像源,如使用腾讯云服务器,可使用 https://mirror.ccs.tencentyun.com docker pull xxx1.3.2 运行…...
【Golang】深入解读Go语言中的错误(error)与异常(panic)
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
DMDSC更换DCR和VOTE磁盘
DMDSC更换DCR和VOTE磁盘 为了提高DMDSC集群运行速度和节点之间通信协调的效率,需要将运行在机械盘上的dcr和vote磁盘替换到SSD高效磁盘上。将原来200M的dcr和vote机械磁盘,换成500M的SSD高效磁盘。 磁盘替换规划信息如下所示: 信息说明 替…...
国产化框架PaddleYOLO结合Swanlab进行作物检测
1. 项目介绍 粮食安全,作为人类生存与发展的基石,始终是全球关注的焦点。它不仅仅关乎粮食的充足供应,更涉及粮食的质量安全、营养健康以及可持续生产等多个维度。在全球化、气候变化和资源环境约束日益加剧的背景下,如何确保粮食…...
Linux编译部署PHP环境
1.准备工作 安装前我们需要设置防护墙,开放端口,更新yum源 # 1.防火墙 systemctl status firewalld 看到active(running)就意味着防火墙打开了 systemctl stop firewalld 看到inactive(dead)就意味着防火墙关闭了 systemctl start fire…...
Win11禁止搜索栏查找互联网内容
禁止任务栏和开始菜单的搜索栏查找互联网内容的方法如下: 使用组策略:WinR键,或菜单框,输入gpedit.msc回车,启动本地组策略编辑器。使用左侧的边栏导航到“计算机配置”>“管理模板”>“Windows组件”>“搜索…...
dig和nmap的区别
dig和nmap是两种在网络管理和安全领域广泛使用的工具,它们在功能、用途和原理上存在显著差异。 dig 定义与功能: dig(Domain Information Groper)是一个用于查询DNS(域名系统)信息的命令行工具。它允许用…...
无人机飞手入伍当兵技术优势分析
随着现代战争形态的不断演变,无人机技术在军事领域的应用日益广泛,成为提升军队作战能力的重要手段。对于无人机飞手而言,其专业技能和实战经验在入伍当兵后能够转化为显著的技术优势,为国防事业贡献重要力量。以下是从专业技能优…...
[Everything] 文件搜索工具的下载及详细安装使用过程(附有下载文件)
快速搜索文件名及其所在路径 下载链接在文末 下载压缩包后解压 !!安装路径不要有中文 解压后得到文件 双击exe文件得到 选择简体中文,点击OK 点击“我接受” 更改安装目录,最好不要放在C盘,点击下一步 点击下一步 点…...
HIRI-ViT:使用高分辨率输入的视觉Transformer扩展
摘要 https://arxiv.org/pdf/2403.11999 视觉Transformer( V i T \mathrm{ViT} ViT)与卷积神经网络(CNN)的混合深度模型已成为视觉任务中一类强大的骨干网络。自然地,提高此类混合骨干网络的输入分辨率会增强模型容量…...
TI DSP TMS320F280025 Note15:串口SCI的使用
TMS320F280025 串口SCI的使用 ` 文章目录 TMS320F280025 串口SCI的使用框图分析串口特点可编程数据格式SCI端口中断非FIFO/FIFO模式下SCI中断的操作/配置UartDriver.cUartDriver.h串口时钟由PCLKCR7控制使能,默认位系统时钟4分频 串口接收与发送都可以触发中断 串口使用的引脚…...
[Bandzip] 文件解压工具的下载及详细安装使用过程(附有下载文件)
文件解压工具,避免解压出错,双击即可解压文件 下载链接在文末 下载压缩包后解压 !!安装路径不要有中文 解压得到文件 双击exe文件 同意并安装 安装完成后,点击关闭, 右键点击需要解压的压缩包࿰…...
微服务MongoDB解析部署使用全流程
目录 1、什么是MongoDB 1、非关系型数据库 2、非关系型数据库分类 3、MongoDB?bson格式什么样? 2、MongoDB的优势 3、MongoDB应用场景 4、术语 5、操作 1、安装MongoDB 1、查询镜像文件【不操作】 2、拉取镜像文件 3、创建数据挂载目录 4、启…...
string为什么存储在堆里
在 Java 中,字符串对象存储在堆内存中而不是栈内存中,这是由于 Java 的内存管理和对象生命周期的特性决定的。以下是详细解释: 1. Java 内存模型 Java 的内存模型主要分为以下几个部分: 堆(Heap)&#x…...
Python和C++及MATLAB距离相关性生物医学样本统计量算法及数据科学
🎯要点 统计观测值之间距离计算代谢组学和脂质组学分析相关距离矩阵计算卡方检验偏差校正快速计算距离协方差算法大规模生物系统分析距离矩阵相关性测试石油勘探统计学关系 Python距离矩阵 在数学、计算机科学,尤其是图论中,距离矩阵是一…...
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
文章目录 C模板进阶编程前言第一章: 非类型模板参数1.1 什么是非类型模板参数?1.1.1 非类型模板参数的定义 1.2 非类型模板参数的注意事项1.3 非类型模板参数的使用场景示例:静态数组的实现 第二章: 模板的特化2.1 什么是模板特化?2.1.1 模板…...
golang学习笔记20-面向对象(二):方法与结构体【重要】
本节内容是面向对象的核心与基础,很重要。 注意:由于导包语句已经在19讲(笔记19:面向对象的引入)展示过了,所以这里就不展示了。 一、方法的定义与细节 方法是与特定类型(通常是结构体&#x…...
广州C++信奥老师解一本通题 1919:【02NOIP普及组】选数
【题目描述】 已知nn个整数x1,x2,……xn 以及一个整数K(K<n)。从n个整数中任选K个整数相加,可分别 得到一系列的和。例如当n4, k3 4个整数分别为3,7,12,19 3, 7,12,19时,可得全部的组合与它们的和为: 371222 371929 7121938 3121934 现在,要求你计算出和为…...
cas5.3统一登录前后端分离改造方案(源码)
cas5.3 为什么选择cas5.3 版本 5.3 版本是最新的支持jdk8 的版本,并且项目架构基于maven ,商业项目中jdk8 仍是主流,在考虑商业收益的情况下,如果客户不要求,不会升级jdk, 基于Maven的项目比gradle 简单,…...
【ComfyUI】控制光照节点——ComfyUI-IC-Light-Native
原始代码(非comfyui):https://github.com/lllyasviel/IC-Light comfyui实现1(600星):https://github.com/kijai/ComfyUI-IC-Light comfyui实现2(500星):https://github.c…...
LVS+keepalived整合负载均衡配置
准备三台服务器;负载均衡服务器192.168.45.136,后端机器1,92.168.45.135,后端机器2,192.168.45.138 在两台后端机器上安装nginx服务,yum install -y nginx,并且启动nginx服务,systemctl start …...
Goland无法使用debug的修复
现象goland在debug的时候,一直卡在打的断点处,释放断点程序就执行结束。 查看其他博客说是当使用老版本的goland,golang版本为高版本时就会出现debug问题,需要手动更换dlv。 接下来以window为例子: 第一步࿱…...
MySQL和Doris开窗函数LAG执行时的区别
目标表的表结构 CREATE TABLE point_value_status_color_tmp ( id bigint NOT NULL, org_id varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, device_id varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, origin_ti…...
都是小憨憨!
周五晚上约了俩哥们儿吃饭,天南海北地聊着。其中一个哥们儿 L 讲,他在外面如何辛苦跑业务,维系客户,拿单子……但却被合伙人说是合伙人拉了 L 一把,而且可以分分钟灭了 L。所以 L 感觉到很委屈。然后我们俩就劝说趁早决…...
做汽车网站怎么挣钱吗/网络宣传方式有哪些
Windows Server2012R2已经是很老的版本了,估计还在用的人不多。今天恰好碰到一台内部用的服务器,出现了启动以后任务栏卡死的情况,经过几番努力,终于解决了问题,记录一下,估计其他版本的系统可能也能用得上…...
深圳市做网站公司/wap网站html5
原文链接:Numpy Axes, Explained - Sharp Sight Numpy Axes,解释 本教程将解释 NumPy 轴。 它将解释什么是 NumPy 轴。本教程还将解释轴的工作原理,以及我们如何将它们与 NumPy 函数一起使用。 尽管您最好阅读完整的教程,但如果您…...
wordpress安装完怎么恢复数据库/优化神马网站关键词排名价格
可能是工作的时间长了,加上自己也是个有点心的人,最近一年开始思考一些技术周边的事情。 团队建设。 团队如何高效。 如何提高团队成员的水平。 如何让团队保持进取心,保持积极的工作态度,保持他们对于技术的渴望和追求。 如何激励…...
浏阳做网站报价/seo线下培训班
文章首发于跳跳糖:SnakeYaml反序列化及不出网利用 SPI 正文之前先了解一下SPI机制。 SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的…...
上海做网站的企业/福州seo推广外包
构建数据库写程序避免不了使用日期和时间,对于数据库来说,有多种日期时间字段可供选择,如 timestamp 和 datetime 以及使用 int 来存储 unix timestamp。经常会有人用字符串存储日期型的数据(不正确的做法)缺点 1:无法用日期函数进…...
企业网站建设必要性/天津网络推广公司
原文地址:ELF文件在带加载器的OS中和裸奔的加载及运行 作者:lelee007 工作关系,这个周花了一天时间好好研究了以下ELF文件及可执行ELF文件的加载。中间过程可谓收获不小,呵呵,因为之前搞linux驱动、ARM裸奔始终没有认真…...