❯ wow... is kotlin
- 提供Application类用于初始化逻辑。
- 提供Window管理,其被包装为Activity类,方便开发。
- 提供IScreenComponent组件,类似于Fragment,将界面实现和逻辑拆分。
- 使用IScreenComponent组件包装DialogWindow,更易于使用。
- Activity及其子类、IScreenComponent组件实现了ViewModelStoreOwner、LifecycleOwner等接口,可以提供ViewModel实例,支持SavedStateHandle等。
- 使rememberSaveable正常工作。
- clone项目
git clone https://github.com/Knightwood/desktop-runtime.git
- 将项目打包到本地仓库
./gradlew publishToMavenLocal
- 依赖并使用
implementation("com.github.knightwood:desktop-runtime:1.0.0"){ isChanging=true }
implementation("com.github.knightwood:jvm-system-spi:1.0.0"){ isChanging=true }
implementation("com.github.knightwood:jvm-system-win:1.0.0")
创建一个Application类和MainActivity类,并使用startApplication函数启动项目。
- MainApplication
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ctx = this
//通过修改locale,修改软件的语言显示
Locale.setDefault(Locale.US)
AppInfoProvider.provide {
appName = "测试"
isDevMode = true
}
}
companion object {
lateinit var ctx: Application
}
}
- MainActivity
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val state = rememberWindowState(position = WindowPosition.Aligned(Alignment.Center), size = DpSize(300.dp, 300.dp))
ComposeView(
onCloseRequest = { finish() },
state = state,
alwaysOnTop = true,
title = "标题",
undecorated = true,
transparent = true,
) {
Text("主页", fontSize = 64.sp)
}
}
}
}
- startApplication函数作为入口
fun main() = startApplication<SplashActivity, MainApplication>(
applicationContent = {
//这里可以直接访问ApplicationScope,从这里创建系统托盘
val painter = painterResource("icons/app_icon.svg")
val icon1 = rememberVectorPainter(Icons.Default.Settings)
val icon2 = rememberVectorPainter(Icons.Default.ExitToApp)
FixedSystemTray(icon = painter, tooltip = "hello",
menu = remember {
buildTrayMenu {
this + FixedTrayMenuItem("settings", icon = icon1)
this + TraySeparator
this + FixedTrayMenuItem("exit", icon = icon2) {
log.info("exit")
MainApplication.ctx.exitApp()
}
}
})
}
)
可以查看composeApp模块下的示例:VMTestActivity、StateTestActivity
最简单的使用方式,这跟在android中使用是一样的。 需要继承自ComponentActivity
class TestViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {}
class VMTestActivity : ComponentActivity() {
val vm1: TestViewModel by viewModels<TestViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeView {
MaterialTheme {
//其余代码省略
}
}
}
}
//其余代码省略
}
若需要在关闭activity时保存状态,再次打开时恢复状态:
- 需要配置启动此activity的intent
- closeActivity 需要为真
val intent = Intent()
//因为大多数时候我们不需要保存恢复数据,clearSaveState默认为true
//但我们这里希望保存恢复数据,因此需要设置clearSaveState为false
intent.clearSaveState = false
//需要注意,activity取回保存起来的数据依靠uuid,因此需要在这里设置uuid
intent.uuid = "11"
startActivity(SaveStateTestActivity::class.java, intent)
class SaveStateTestActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
//由于状态存储发生在onDestroy阶段,closeActivity为true时才会触发onDestroy
ComposeView(closeActivity = true) {
MaterialTheme {
Column {
val text = rememberSaveable() {
mutableStateOf("ede")
}
Text(text = text.value)
}
}
}
}
}
//其余代码省略
}
可以查看composeApp模块下的示例:TestFragmentActivity 需要activity继承自ComponentActivity,或者你可以参照FragmentActivity的实现自由发挥。
fragment继承自IScreenComponent,与activity相似,也实现了ViewModelStoreOwner, LifecycleOwner, LifecycleEventObserver, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner等接口。
class TestFragmentActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//注册fragment和dialogFragment
register<Fragment1>("123")
register<Fragment1>("124")
register<TestDialog>("dialog1")
setContent {
ComposeView() {
//可以通过screen函数获取fragment
var screen1 by remember {
mutableStateOf<Fragment?>(screen("123"))
}
MaterialTheme {
Column {
screen1?.invoke()//显示fragment
Row {
SampleButton("关闭") {
unregister("123") //关闭fragment
screen1 = null
}
SampleButton("再打开") {
//重新生成一个fragment
screen1 = register<Fragment1>("123")
}
}
//弹窗
val dialog by remember {
mutableStateOf(screen("dialog1"))
}
dialog()
Row {
SampleButton("打开dialog1") {
dialog.show()
}
SampleButton("销毁dialog1") {
(dialog as DialogFragment).dismiss()
}
}
//可以直接使用,不需要注册的弹窗
val dialog2 by remember {
mutableStateOf(
DialogFragment.makeDialog(
TestDialog::class.java,
this@TestFragmentActivity.lifecycle
)
)
}
dialog2()
Row {
SampleButton("打开dialog2") {
dialog2.show()
}
SampleButton("销毁dialog2") {
(dialog2 as DialogFragment).dismiss()
}
}
}
}
}
}
}
}
class Fragment1 : Fragment() {
init {
//如果为true,则不会在onDestroy时保存数据,也不会在 onCreate中恢复数据
//默认为true
clearBundle = false
}
override fun onCreateView(): ComponentViewHolder {
return ComposeView {
MaterialTheme {
}
}
}
}
class TestDialog : DialogFragment() {
override fun onCreateView(): ComponentViewHolder {
return ComposeView {
Dialog() {
MaterialTheme {
}
}
}
}
}
使用下面的命令运行项目
gradle desktopRun -DmainClass=com.github.knightwood.example.MainKt --quiet
❯ gradle desktopRun -DmainClass=com.github.knightwood.example.MainKt --quiet
- 💬 Join the Discussions: Share your insights, provide feedback, or ask questions.
- 🐛 Report Issues: Submit bugs found or log feature requests for the
desktop-runtime
project. - 💡 Submit Pull Requests: Review open PRs, and submit your own PRs.
Contributing Guidelines
- Fork the Repository: Start by forking the project repository to your github account.
- Clone Locally: Clone the forked repository to your local machine using a git client.
git clone https://github.com/Knightwood/desktop-runtime
- Create a New Branch: Always work on a new branch, giving it a descriptive name.
git checkout -b new-feature-x
- Make Your Changes: Develop and test your changes locally.
- Commit Your Changes: Commit with a clear message describing your updates.
git commit -m 'Implemented new feature x.'
- Push to github: Push the changes to your forked repository.
git push origin new-feature-x
- Submit a Pull Request: Create a PR against the original project repository. Clearly describe the changes and their motivations.
- Review: Once your PR is reviewed and approved, it will be merged into the main branch. Congratulations on your contribution!
This project is protected under the SELECT-A-LICENSE License. For more details, refer to the LICENSE file.
- List any resources, contributors, inspiration, etc. here.