mirror of
https://github.com/BetterAndroid/Hikage.git
synced 2025-09-05 10:15:37 +08:00
Compare commits
15 Commits
1b26604506
...
main
Author | SHA1 | Date | |
---|---|---|---|
8ee3ae17ba
|
|||
0d99806004
|
|||
1cb95849ab
|
|||
714c8552ab
|
|||
18ccf196b6
|
|||
76df8fa06c
|
|||
8e25430d68
|
|||
0529d6a2b6
|
|||
51a59b672c
|
|||
59d09b9611
|
|||
b6c46b7240
|
|||
646e5e5056
|
|||
14ba71cc2b
|
|||
ee97222bcb
|
|||
8990e03e97
|
4
.github/workflows/docs-deploy.yml
vendored
4
.github/workflows/docs-deploy.yml
vendored
@@ -29,10 +29,10 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Prepare Java 17
|
||||
- name: Prepare Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
java-package: jdk
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
|
1
.idea/fileTemplates/includes/copyright-name.template
generated
Normal file
1
.idea/fileTemplates/includes/copyright-name.template
generated
Normal file
@@ -0,0 +1 @@
|
||||
2019 HighCapable
|
13
.idea/fileTemplates/includes/license-content.template
generated
Normal file
13
.idea/fileTemplates/includes/license-content.template
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
13
.idea/fileTemplates/includes/open-source-license-header.template
generated
Normal file
13
.idea/fileTemplates/includes/open-source-license-header.template
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* #parse("project-name") - #parse("project-description")
|
||||
|
||||
* Copyright (C) #parse("copyright-name")
|
||||
|
||||
* #parse("project-url")
|
||||
|
||||
*
|
||||
#parse("license-content")
|
||||
|
||||
*
|
||||
* This file is created by $USER on $DATE.
|
||||
*/
|
1
.idea/fileTemplates/includes/project-description.template
generated
Normal file
1
.idea/fileTemplates/includes/project-description.template
generated
Normal file
@@ -0,0 +1 @@
|
||||
An Android responsive UI building tool.
|
1
.idea/fileTemplates/includes/project-name.template
generated
Normal file
1
.idea/fileTemplates/includes/project-name.template
generated
Normal file
@@ -0,0 +1 @@
|
||||
Hikage
|
1
.idea/fileTemplates/includes/project-url.template
generated
Normal file
1
.idea/fileTemplates/includes/project-url.template
generated
Normal file
@@ -0,0 +1 @@
|
||||
https://github.com/BetterAndroid/Hikage
|
6
.idea/fileTemplates/internal/Kotlin Annotation.kt
generated
Normal file
6
.idea/fileTemplates/internal/Kotlin Annotation.kt
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
#end
|
||||
|
||||
annotation class ${NAME}
|
7
.idea/fileTemplates/internal/Kotlin Class.kt
generated
Normal file
7
.idea/fileTemplates/internal/Kotlin Class.kt
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
|
||||
#end
|
||||
class ${NAME} {
|
||||
}
|
6
.idea/fileTemplates/internal/Kotlin Data Class.kt
generated
Normal file
6
.idea/fileTemplates/internal/Kotlin Data Class.kt
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
#end
|
||||
|
||||
data class ${NAME}()
|
7
.idea/fileTemplates/internal/Kotlin Enum.kt
generated
Normal file
7
.idea/fileTemplates/internal/Kotlin Enum.kt
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
|
||||
#end
|
||||
enum class ${NAME} {
|
||||
}
|
5
.idea/fileTemplates/internal/Kotlin File.kt
generated
Normal file
5
.idea/fileTemplates/internal/Kotlin File.kt
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
|
||||
#end
|
7
.idea/fileTemplates/internal/Kotlin Interface.kt
generated
Normal file
7
.idea/fileTemplates/internal/Kotlin Interface.kt
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
|
||||
#end
|
||||
interface ${NAME} {
|
||||
}
|
7
.idea/fileTemplates/internal/Kotlin Object.kt
generated
Normal file
7
.idea/fileTemplates/internal/Kotlin Object.kt
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
#parse("open-source-license-header")
|
||||
|
||||
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
|
||||
#end
|
||||
|
||||
object ${NAME} {
|
||||
}
|
@@ -2,6 +2,8 @@ import com.android.build.gradle.LibraryExtension
|
||||
import com.vanniktech.maven.publish.AndroidSingleVariantLibrary
|
||||
import com.vanniktech.maven.publish.MavenPublishBaseExtension
|
||||
import org.jetbrains.dokka.gradle.DokkaTask
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||
|
||||
plugins {
|
||||
autowire(libs.plugins.android.application) apply false
|
||||
@@ -65,6 +67,20 @@ libraryProjects {
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
tasks.withType<KotlinJvmCompile>().configureEach {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_17
|
||||
freeCompilerArgs.addAll(
|
||||
"-opt-in=kotlin.ExperimentalStdlibApi",
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun libraryProjects(action: Action<in Project>) {
|
||||
val libraries = listOf(
|
||||
Libraries.HIKAGE_CORE,
|
||||
|
@@ -18,7 +18,16 @@ Time zone of version release date: **UTC+8**
|
||||
|
||||
## hikage-core
|
||||
|
||||
### 1.0.1 | 2025.05.06  <Badge type="tip" text="latest" vertical="middle" />
|
||||
### 1.0.2 | 2025.08.24  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- Migrated Java reflection related behaviors from [YukiReflection](https://github.com/HighCapable/YukiReflection) to [KavaRef](https://github.com/HighCapable/KavaRef)
|
||||
- Adapted to Android 16 (API 36), fixed the `XmlBlock` crash issue on Android 16
|
||||
- Optimized layout performance, removed unnecessary inline operations, added caching for reflection operations
|
||||
- Added `final` parameter to `HikageView` and `HikageViewDeclaration` to support new features in `hikage-compiler`
|
||||
- Added `SurfaceView` and `WebView` built-in components to `Widgets`
|
||||
- Adjusted some components in `Widgets` to be `final`
|
||||
|
||||
### 1.0.1 | 2025.05.06  <Badge type="warning" text="stale" vertical="middle" />
|
||||
|
||||
- Fixed the issue where the KSP source code was not successfully released
|
||||
- Added states management feature
|
||||
@@ -29,19 +38,32 @@ Time zone of version release date: **UTC+8**
|
||||
|
||||
## hikage-compiler
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="latest" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- Added support for the `final` parameter of `HikageView` and `HikageViewDeclaration`, please refer to the relevant usage in the documentation
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="stale" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
||||
|
||||
## hikage-extension
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="latest" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- Migrated Java reflection related behaviors from [YukiReflection](https://github.com/HighCapable/YukiReflection) to [KavaRef](https://github.com/HighCapable/KavaRef)
|
||||
- Added generic `ViewGroup.LayoutParams` support for `addView` in `ViewGroup`
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="stale" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
||||
|
||||
## hikage-extension-betterandroid
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="latest" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- Adapted to decoupled `ui-component` and `ui-component-adapter` in `BetterAndroid`
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="stale" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
||||
|
||||
@@ -53,12 +75,21 @@ Time zone of version release date: **UTC+8**
|
||||
|
||||
## hikage-widget-androidx
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="latest" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- Added `MotionLayout`, `ImageFilterButton`, `ImageFilterView`, `MockView`, `MotionButton`, `MotionLabel`, `MotionTelltales` components to `ConstraintLayout`
|
||||
- Adjusted some components to be `final`
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="stale" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
||||
|
||||
## hikage-widget-material
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="latest" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- Adjusted some components to be `final`
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="stale" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
@@ -139,12 +139,13 @@ Hikage can automatically generate the `Hikageable` function corresponding to the
|
||||
|
||||
You can add the `HikageView` annotation on your custom `View` to mark it as a Hikage layout component.
|
||||
|
||||
| Parameter Name | Description |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `lparams` | LayoutParams `Class` object, if your custom `View` is a subclass of `ViewGroup`, you can declare or leave it blank to use the default value |
|
||||
| `alias` | The alias of the layout component, that is, the function name to be generated, gets the name of the current Class by default |
|
||||
| `requireInit` | Whether to fill in the initialization method block of the layout, the default is the omitted parameters |
|
||||
| `requirePerformer` | Whether to fill in the `performer` method block of the layout, the default is an omitted parameter, which only takes effect when your custom `View` is a subclass of `ViewGroup` |
|
||||
| Parameter Name | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `lparams` | LayoutParams `Class` object, if your custom `View` is a subclass of `ViewGroup`, you can declare or leave it blank to use the default value |
|
||||
| `alias` | The alias of the layout component, that is, the function name to be generated, gets the name of the current Class by default |
|
||||
| `requireInit` | Whether to fill in the initialization method block of the layout, the default is the omitted parameters |
|
||||
| `requirePerformer` | Whether to fill in the `performer` method block of the layout, the default is an omitted parameter, which only takes effect when your custom `View` is a subclass of `ViewGroup` |
|
||||
| `final` | Whether to declare the layout as "final layout", the default is false, that is, whether this layout is `ViewGroup` or its subclasses will not generate the `performer` method block. After set to `true`, `lparams` and `requirePerformer` will no longer be valid. |
|
||||
|
||||
> The following example
|
||||
|
||||
@@ -177,13 +178,14 @@ Hikageable {
|
||||
|
||||
Hikage can also automatically generate layout component functions for the `View` component provided by third parties, and you can use the `HikageViewDeclaration` annotation to complete it.
|
||||
|
||||
| Parameter Name | Description |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `view` | Class object of layout component that needs to be declared |
|
||||
| `lparams` | LayoutParams `Class` object, if your custom `View` is a subclass of `ViewGroup`, you can declare or leave it blank to use the default value |
|
||||
| `alias` | The alias of the layout component, that is, the name of the function to be generated, obtains the name of the `view` Class by default |
|
||||
| `requireInit` | Whether to fill in the initialization method block of the layout, the default is the omitted parameters |
|
||||
| `requirePerformer` | Whether to fill in the `performer` method block of the layout, the default is an omitted parameter, which only takes effect when your custom `View` is a subclass of `ViewGroup` |
|
||||
| Parameter Name | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `view` | Class object of layout component that needs to be declared |
|
||||
| `lparams` | LayoutParams `Class` object, if your custom `View` is a subclass of `ViewGroup`, you can declare or leave it blank to use the default value |
|
||||
| `alias` | The alias of the layout component, that is, the name of the function to be generated, obtains the name of the `view` Class by default |
|
||||
| `requireInit` | Whether to fill in the initialization method block of the layout, the default is the omitted parameters |
|
||||
| `requirePerformer` | Whether to fill in the `performer` method block of the layout, the default is an omitted parameter, which only takes effect when your custom `View` is a subclass of `ViewGroup` |
|
||||
| `final` | Whether to declare the layout as "final layout", the default is false, that is, whether this layout is `ViewGroup` or its subclasses will not generate the `performer` method block. After set to `true`, `lparams` and `requirePerformer` will no longer be valid. |
|
||||
|
||||
> The following example
|
||||
|
||||
|
@@ -65,7 +65,7 @@ You can view the KDoc [click here](kdoc://hikage-extension-betterandroid).
|
||||
|
||||
### Adapter Extension
|
||||
|
||||
Hikage provides layout extension function for BetterAndroid's [Adapter](https://betterandroid.github.io/BetterAndroid/en/library/ui-component#adapter),
|
||||
Hikage provides layout extension function for BetterAndroid's [Adapter](https://betterandroid.github.io/BetterAndroid/en/library/ui-component-adapter),
|
||||
you can use the Hikage layout directly on the original extension method of the adapter.
|
||||
|
||||
It uses the `ViewHolderDelegate` provided by BetterAndroid to create extension methods.
|
||||
@@ -76,9 +76,9 @@ Here is a simple example based on `RecyclerView`.
|
||||
|
||||
```kotlin
|
||||
// Assume this is the dataset you need to bind to.
|
||||
val listData = ArrayList<CustomBean>()
|
||||
val listData = ArrayList<MyEntity>()
|
||||
// Create and bind to a custom RecyclerView.Adapter.
|
||||
val adapter = recyclerView.bindAdapter<CustomBean> {
|
||||
val adapter = recyclerView.bindAdapter<MyEntity> {
|
||||
onBindData { listData }
|
||||
onBindItemView(
|
||||
Hikageable = {
|
||||
@@ -87,8 +87,8 @@ val adapter = recyclerView.bindAdapter<CustomBean> {
|
||||
textSize = 16f
|
||||
}
|
||||
}
|
||||
) { hikage, bean, position ->
|
||||
hikage.get<TextView>("text_view").text = bean.name
|
||||
) { hikage, entity, position ->
|
||||
hikage.get<TextView>("text_view").text = entity.name
|
||||
}
|
||||
}
|
||||
```
|
@@ -219,7 +219,7 @@ Or, use in a custom `View`.
|
||||
class CustomView(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
|
||||
|
||||
init {
|
||||
addView {
|
||||
addView<FrameLayout.LayoutParams> {
|
||||
TextView {
|
||||
text = "Hello, World!"
|
||||
textSize = 16f
|
||||
|
@@ -10,7 +10,16 @@
|
||||
|
||||
## hikage-core
|
||||
|
||||
### 1.0.1 | 2025.05.06  <Badge type="tip" text="最新" vertical="middle" />
|
||||
### 1.0.2 | 2025.08.24  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 将 Java 反射相关行为由 [YukiReflection](https://github.com/HighCapable/YukiReflection) 迁移至 [KavaRef](https://github.com/HighCapable/KavaRef)
|
||||
- 适配 Android 16 (API 36),解决了 Android 16 上 `XmlBlock` 的崩溃问题
|
||||
- 优化布局性能,移除了不必要的内联操作,对反射操作增加缓存
|
||||
- `HikageView` 和 `HikageViewDeclaration` 新增 `final` 参数以配合 `hikage-compiler` 实现新功能
|
||||
- `Widgets` 新增 `SurfaceView` 和 `WebView` 内置组件
|
||||
- `Widgets` 调整部分组件为 `final`
|
||||
|
||||
### 1.0.1 | 2025.05.06  <Badge type="warning" text="过旧" vertical="middle" />
|
||||
|
||||
- 修复 KSP 源码没有成功发布的问题
|
||||
- 新增状态管理功能
|
||||
@@ -21,19 +30,32 @@
|
||||
|
||||
## hikage-compiler
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="最新" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 新增对 `HikageView` 和 `HikageViewDeclaration` 的 `final` 参数的支持,详情请参考文档的相关用法
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="过旧" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
||||
|
||||
## hikage-extension
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="最新" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 将 Java 反射相关行为由 [YukiReflection](https://github.com/HighCapable/YukiReflection) 迁移至 [KavaRef](https://github.com/HighCapable/KavaRef)
|
||||
- `ViewGroup` 新增对 `addView` 的泛型 `ViewGroup.LayoutParams` 支持
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="过旧" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
||||
|
||||
## hikage-extension-betterandroid
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="最新" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 适配了 `BetterAndroid` 解耦合后的 `ui-component` 和 `ui-component-adapter`
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="过旧" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
||||
|
||||
@@ -45,12 +67,21 @@
|
||||
|
||||
## hikage-widget-androidx
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="最新" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- `ConstraintLayout` 新增 `MotionLayout`、`ImageFilterButton`、`ImageFilterView`、`MockView`、`MotionButton`、`MotionLabel`、`MotionTelltales` 组件
|
||||
- 调整部分组件为 `final`
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="过旧" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
||||
|
||||
## hikage-widget-material
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="tip" text="最新" vertical="middle" />
|
||||
### 1.0.1 | 2025.08.24  <Badge type="tip" text="最新" vertical="middle" />
|
||||
|
||||
- 调整部分组件为 `final`
|
||||
|
||||
### 1.0.0 | 2025.04.20  <Badge type="warning" text="过旧" vertical="middle" />
|
||||
|
||||
- 首个版本提交至 Maven
|
@@ -136,12 +136,13 @@ Hikage 可以在编译时为指定的布局组件自动生成布局组件对应
|
||||
|
||||
你可以在你的自定义 `View` 上加入 `HikageView` 注解,以标记它生成为 Hikage 布局组件。
|
||||
|
||||
| 参数名称 | 描述 |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| `lparams` | 布局参数 `ViewGroup.LayoutParams` Class 对象,如果你的自定义 `View` 是 `ViewGroup` 的子类,则可以声明或留空使用默认值 |
|
||||
| `alias` | 布局组件的别名,即要生成的函数名称,默认获取当前 Class 的名称 |
|
||||
| `requireInit` | 是否要求填写布局的初始化方法块,默认为可省略的参数 |
|
||||
| `requirePerformer` | 是否要求填写布局的 `performer` 方法块,默认为可省略的参数,仅在你的自定义 `View` 是 `ViewGroup` 的子类时生效 |
|
||||
| 参数名称 | 描述 |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `lparams` | 布局参数 `ViewGroup.LayoutParams` Class 对象,如果你的自定义 `View` 是 `ViewGroup` 的子类,则可以声明或留空使用默认值 |
|
||||
| `alias` | 布局组件的别名,即要生成的函数名称,默认获取当前 Class 的名称 |
|
||||
| `requireInit` | 是否要求填写布局的初始化方法块,默认为可省略的参数 |
|
||||
| `requirePerformer` | 是否要求填写布局的 `performer` 方法块,默认为可省略的参数,仅在你的自定义 `View` 是 `ViewGroup` 的子类时生效 |
|
||||
| `final` | 是否将布局声明为 “最终布局”,默认否,即此布局是否是 `ViewGroup` 还是从其继承都将不会生成 `performer` 方法块,设置为 `true` 之后,`lparams` 和 `requirePerformer` 将不再有效。 |
|
||||
|
||||
> 示例如下
|
||||
|
||||
@@ -174,13 +175,14 @@ Hikageable {
|
||||
|
||||
Hikage 同样可以为第三方提供的 `View` 组件自动生成布局组件函数,你可以使用 `HikageViewDeclaration` 注解来完成。
|
||||
|
||||
| 参数名称 | 描述 |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| `view` | 需要声明的布局组件的 Class 对象 |
|
||||
| `lparams` | 布局参数 `ViewGroup.LayoutParams` Class 对象,如果你的自定义 `View` 是 `ViewGroup` 的子类,则可以声明或留空使用默认值 |
|
||||
| `alias` | 布局组件的别名,即要生成的函数名称,默认获取 `view` Class 的名称 |
|
||||
| `requireInit` | 是否要求填写布局的初始化方法块,默认为可省略的参数 |
|
||||
| `requirePerformer` | 是否要求填写布局的 `performer` 方法块,默认为可省略的参数,仅在你的自定义 `View` 是 `ViewGroup` 的子类时生效 |
|
||||
| 参数名称 | 描述 |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `view` | 需要声明的布局组件的 Class 对象 |
|
||||
| `lparams` | 布局参数 `ViewGroup.LayoutParams` Class 对象,如果你的自定义 `View` 是 `ViewGroup` 的子类,则可以声明或留空使用默认值 |
|
||||
| `alias` | 布局组件的别名,即要生成的函数名称,默认获取 `view` Class 的名称 |
|
||||
| `requireInit` | 是否要求填写布局的初始化方法块,默认为可省略的参数 |
|
||||
| `requirePerformer` | 是否要求填写布局的 `performer` 方法块,默认为可省略的参数,仅在你的自定义 `View` 是 `ViewGroup` 的子类时生效 |
|
||||
| `final` | 是否将布局声明为 “最终布局”,默认否,即此布局是否是 `ViewGroup` 还是从其继承都将不会生成 `performer` 方法块,设置为 `true` 之后,`lparams` 和 `requirePerformer` 将不再有效。 |
|
||||
|
||||
> 示例如下
|
||||
|
||||
|
@@ -65,7 +65,7 @@ implementation("com.highcapable.hikage:hikage-extension-betterandroid:<version>"
|
||||
|
||||
### 适配器 (Adapter) 扩展
|
||||
|
||||
Hikage 为 BetterAndroid 提供的 [适配器](https://betterandroid.github.io/BetterAndroid/zh-cn/library/ui-component#%E9%80%82%E9%85%8D%E5%99%A8-adapter)
|
||||
Hikage 为 BetterAndroid 提供的 [适配器](https://betterandroid.github.io/BetterAndroid/zh-cn/library/ui-component-adapter)
|
||||
提供了布局扩展功能,你可以直接在适配器的原始扩展方法上使用 Hikage 布局。
|
||||
|
||||
它使用了 BetterAndroid 提供的 `ViewHolderDelegate` 来创建扩展方法。
|
||||
@@ -76,9 +76,9 @@ Hikage 为 BetterAndroid 提供的 [适配器](https://betterandroid.github.io/B
|
||||
|
||||
```kotlin
|
||||
// 假设这就是你需要绑定的数据集
|
||||
val listData = ArrayList<CustomBean>()
|
||||
val listData = ArrayList<MyEntity>()
|
||||
// 创建并绑定到自定义的 RecyclerView.Adapter
|
||||
val adapter = recyclerView.bindAdapter<CustomBean> {
|
||||
val adapter = recyclerView.bindAdapter<MyEntity> {
|
||||
onBindData { listData }
|
||||
onBindItemView(
|
||||
Hikageable = {
|
||||
@@ -87,8 +87,8 @@ val adapter = recyclerView.bindAdapter<CustomBean> {
|
||||
textSize = 16f
|
||||
}
|
||||
}
|
||||
) { hikage, bean, position ->
|
||||
hikage.get<TextView>("text_view").text = bean.name
|
||||
) { hikage, entity, position ->
|
||||
hikage.get<TextView>("text_view").text = entity.name
|
||||
}
|
||||
}
|
||||
```
|
@@ -217,7 +217,7 @@ root.addView {
|
||||
class CustomView(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
|
||||
|
||||
init {
|
||||
addView {
|
||||
addView<FrameLayout.LayoutParams> {
|
||||
TextView {
|
||||
text = "Hello, World!"
|
||||
textSize = 16f
|
||||
|
@@ -3,35 +3,34 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
android.useAndroidX=true
|
||||
android.nonTransitiveRClass=true
|
||||
kotlin.code.style=official
|
||||
kotlin.incremental.useClasspathSnapshot=true
|
||||
# Project Configuration
|
||||
project.name=Hikage
|
||||
project.url=https://github.com/BetterAndroid/Hikage
|
||||
project.groupName=com.highcapable.hikage
|
||||
project.android.compileSdk=35
|
||||
project.android.compileSdk=36
|
||||
project.android.minSdk=21
|
||||
project.android.targetSdk=35
|
||||
project.android.targetSdk=36
|
||||
project.samples-app.packageName=com.highcapable.hikage.demo
|
||||
project.samples-app.versionName=universal
|
||||
project.samples-app.versionCode=1
|
||||
project.hikage-core.namespace=${project.groupName}.core
|
||||
project.hikage-core.version="1.0.1"
|
||||
project.hikage-core.version="1.0.2"
|
||||
project.hikage-core-lint.namespace=${project.groupName}.core.lint
|
||||
project.hikage-core-lint.identifier=${project.groupName}:hikage-core:${project.hikage-core.version}
|
||||
project.hikage-core-lint.min-api=9
|
||||
project.hikage-core-lint.registry-v2-class="${project.hikage-core-lint.namespace}.HikageIssueRegistry"
|
||||
project.hikage-extension.namespace=${project.groupName}.extension
|
||||
project.hikage-extension.version="1.0.0"
|
||||
project.hikage-extension.version="1.0.1"
|
||||
project.hikage-extension-betterandroid.namespace=${project.groupName}.extension.betterandroid
|
||||
project.hikage-extension-betterandroid.version="1.0.0"
|
||||
project.hikage-extension-betterandroid.version="1.0.1"
|
||||
project.hikage-extension-compose.namespace=${project.groupName}.extension.androidx.compose
|
||||
project.hikage-extension-compose.version="1.0.0"
|
||||
project.hikage-compiler.namespace="${project.groupName}.compiler"
|
||||
project.hikage-compiler.version="1.0.0"
|
||||
project.hikage-compiler.version="1.0.1"
|
||||
project.hikage-widget-androidx.namespace=${project.groupName}.widget.androidx
|
||||
project.hikage-widget-androidx.version="1.0.0"
|
||||
project.hikage-widget-androidx.version="1.0.1"
|
||||
project.hikage-widget-material.namespace=${project.groupName}.widget.google.material
|
||||
project.hikage-widget-material.version="1.0.0"
|
||||
project.hikage-widget-material.version="1.0.1"
|
||||
# Maven Publish Configuration
|
||||
SONATYPE_HOST=CENTRAL_PORTAL
|
||||
RELEASE_SIGNING_ENABLED=true
|
||||
|
@@ -24,7 +24,7 @@ plugins:
|
||||
auto-update: false
|
||||
com.android.application:
|
||||
alias: android-application
|
||||
version: 8.9.3
|
||||
version: 8.12.1
|
||||
com.android.library:
|
||||
alias: android-library
|
||||
version-ref: android-application
|
||||
@@ -37,7 +37,7 @@ plugins:
|
||||
auto-update: false
|
||||
com.vanniktech.maven.publish:
|
||||
alias: maven-publish
|
||||
version: 0.33.0
|
||||
version: 0.34.0
|
||||
|
||||
libraries:
|
||||
org.jetbrains.kotlin:
|
||||
@@ -59,11 +59,13 @@ libraries:
|
||||
version-ref: <this>::kotlinpoet
|
||||
com.highcapable.betterandroid:
|
||||
ui-component:
|
||||
version: 1.0.7
|
||||
version: 1.0.8
|
||||
ui-component-adapter:
|
||||
version: 1.0.0
|
||||
ui-extension:
|
||||
version: 1.0.6
|
||||
version: 1.0.7
|
||||
system-extension:
|
||||
version: 1.0.2
|
||||
version: 1.0.3
|
||||
org.lsposed.hiddenapibypass:
|
||||
hiddenapibypass:
|
||||
version: 6.1
|
||||
@@ -74,10 +76,10 @@ libraries:
|
||||
version: 1.0.1
|
||||
com.highcapable.pangutext:
|
||||
pangutext-android:
|
||||
version: 1.0.2
|
||||
version: 1.0.4
|
||||
androidx.core:
|
||||
core:
|
||||
version: 1.16.0
|
||||
version: 1.17.0
|
||||
core-ktx:
|
||||
version-ref: <this>::core
|
||||
androidx.appcompat:
|
||||
@@ -117,19 +119,20 @@ libraries:
|
||||
version: 1.4.0
|
||||
androidx.compose.ui:
|
||||
ui:
|
||||
version: 1.8.3
|
||||
version: 1.9.0
|
||||
junit:
|
||||
junit:
|
||||
version: 4.13.2
|
||||
androidx.test.ext:
|
||||
junit:
|
||||
version: 1.2.1
|
||||
version: 1.3.0
|
||||
androidx.test.espresso:
|
||||
espresso-core:
|
||||
version: 3.6.1
|
||||
version: 3.7.0
|
||||
com.android.tools.lint:
|
||||
lint:
|
||||
version: 31.11.0
|
||||
version: 31.9.0
|
||||
auto-update: false
|
||||
lint-api:
|
||||
version-ref: <this>::lint
|
||||
lint-checks:
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
@@ -39,15 +39,20 @@ fun KSDeclaration.getSimpleNameString(): String {
|
||||
fun KSClassDeclaration.isSubclassOf(superType: KSType?): Boolean {
|
||||
if (superType == null) return false
|
||||
if (this == superType.declaration) return true
|
||||
|
||||
superTypes.forEach { parent ->
|
||||
val resolvedParent = parent.resolve()
|
||||
// Direct match.
|
||||
if (resolvedParent == superType) return true
|
||||
|
||||
val parentDeclaration = resolvedParent.declaration as? KSClassDeclaration
|
||||
?: return@forEach
|
||||
|
||||
// Recursively check the parent class.
|
||||
if (parentDeclaration.isSubclassOf(superType)) return true
|
||||
}; return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun KSClassDeclaration.asType() = asType(emptyList())
|
||||
|
@@ -71,6 +71,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
|
||||
override fun startProcess(resolver: Resolver) {
|
||||
Processor.init(logger, resolver)
|
||||
|
||||
val dependencies = Dependencies(aggregating = true, *resolver.getAllFiles().toList().toTypedArray())
|
||||
resolver.getSymbolsWithAnnotation(HikageViewSpec.CLASS)
|
||||
.filterIsInstance<KSClassDeclaration>()
|
||||
@@ -79,9 +80,11 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
ksClass.annotations.forEach {
|
||||
// Get annotation parameters.
|
||||
val (annotation, declaration) = HikageViewSpec.create(resolver, it, ksClass)
|
||||
|
||||
performers += Performer(annotation, declaration)
|
||||
}
|
||||
}
|
||||
|
||||
resolver.getSymbolsWithAnnotation(HikageViewDeclarationSpec.CLASS)
|
||||
.filterIsInstance<KSClassDeclaration>()
|
||||
.distinctBy { it.qualifiedName }
|
||||
@@ -89,14 +92,17 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
ksClass.annotations.forEach {
|
||||
// Get annotation parameters.
|
||||
val (annotation, declaration) = HikageViewDeclarationSpec.create(resolver, it, ksClass)
|
||||
|
||||
performers += Performer(annotation, declaration)
|
||||
}
|
||||
}
|
||||
|
||||
processPerformer(dependencies)
|
||||
}
|
||||
|
||||
private fun processPerformer(dependencies: Dependencies) {
|
||||
val duplicatedItems = performers.groupBy { it.declaration.key }.filter { it.value.size > 1 }.flatMap { it.value }
|
||||
|
||||
require(duplicatedItems.isEmpty()) {
|
||||
"Discover duplicate @HikageView or @HikageViewDeclaration's class name or alias definitions, " +
|
||||
"you can re-specify the class name using the `alias` parameter.\n" +
|
||||
@@ -109,25 +115,30 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
private fun generateCodeFile(dependencies: Dependencies, performer: Performer) {
|
||||
val classNameSet = performer.declaration.alias ?: performer.declaration.className
|
||||
val fileName = "_$classNameSet"
|
||||
|
||||
val viewClass = performer.declaration.toClassName().let {
|
||||
val packageName = it.packageName
|
||||
val simpleName = it.simpleName
|
||||
val topClassName = if (simpleName.contains(".")) simpleName.split(".")[0] else null
|
||||
|
||||
// com.example.MyViewScope
|
||||
// com.example.MyViewScope.MyView
|
||||
topClassName?.let { name -> ClassName(packageName, name) } to it
|
||||
}
|
||||
|
||||
val lparamsClass = performer.annotation.lparams?.let {
|
||||
val packageName = it.packageName.asString()
|
||||
val subClassName = it.getSimpleNameString()
|
||||
val simpleName = it.simpleName.asString()
|
||||
val topClassName = subClassName.replace(".$simpleName", "")
|
||||
|
||||
// android.view.ViewGroup
|
||||
// android.view.ViewGroup.LayoutParams
|
||||
(if (topClassName != subClassName)
|
||||
ClassName(packageName, topClassName)
|
||||
else null) to ClassName(packageName, subClassName)
|
||||
}
|
||||
|
||||
val packageName = "$PACKAGE_NAME_PREFIX.${performer.declaration.packageName}"
|
||||
val fileSpec = FileSpec.builder(packageName, fileName).apply {
|
||||
addFileComment(
|
||||
@@ -140,6 +151,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
**DO NOT EDIT THIS FILE MANUALLY**
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
addAnnotation(
|
||||
AnnotationSpec.builder(Suppress::class)
|
||||
.addMember("%S, %S, %S", "unused", "FunctionName", "DEPRECATION")
|
||||
@@ -150,17 +162,22 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
.addMember("%S", "${classNameSet}Performer")
|
||||
.build()
|
||||
)
|
||||
|
||||
addImport(DeclaredSymbol.ANDROID_VIEW_PACKAGE_NAME, DeclaredSymbol.ANDROID_VIEW_GROUP_CLASS_NAME)
|
||||
addImport(DeclaredSymbol.HIKAGE_CORE_PACKAGE_NAME, DeclaredSymbol.HIKAGE_CLASS_NAME)
|
||||
|
||||
// Kotlin's import rule is to introduce the parent class that also needs to be introduced at the same time.
|
||||
// If a child class exists, it needs to import the parent class,
|
||||
// and kotlinpoet will not perform this operation automatically.
|
||||
viewClass.first?.let { addImport(it.packageName, it.simpleName) }
|
||||
lparamsClass?.first?.let { addImport(it.packageName, it.simpleName) }
|
||||
|
||||
// May conflict with other [LayoutParams].
|
||||
lparamsClass?.second?.let { addAliasedImport(it, it.getTypedSimpleName()) }
|
||||
|
||||
addAliasedImport(ViewGroupLpClass, ViewGroupLpClass.getTypedSimpleName())
|
||||
addAliasedImport(HikageLparamClass, HikageLparamClass.getTypedSimpleName())
|
||||
|
||||
addFunction(FunSpec.builder(classNameSet).apply {
|
||||
addKdoc(
|
||||
"""
|
||||
@@ -170,10 +187,12 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
@return [${performer.declaration.className}]
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
addAnnotation(HikageableClass)
|
||||
addModifiers(KModifier.INLINE)
|
||||
addTypeVariable(TypeVariableName(name = "LP", ViewGroupLpClass).copy(reified = true))
|
||||
receiver(PerformerClass.parameterizedBy(TypeVariableName("LP")))
|
||||
|
||||
addParameter(
|
||||
ParameterSpec.builder(name = "lparams", HikageLparamClass.copy(nullable = true))
|
||||
.defaultValue("null")
|
||||
@@ -192,7 +211,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
if (!performer.annotation.requireInit) defaultValue("{}")
|
||||
}.build()
|
||||
)
|
||||
lparamsClass?.second?.let {
|
||||
lparamsClass?.second?.takeIf { !performer.annotation.final }?.let {
|
||||
addParameter(
|
||||
ParameterSpec.builder(
|
||||
name = "performer",
|
||||
@@ -203,9 +222,11 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
)
|
||||
addStatement("return ViewGroup<${performer.declaration.className}, ${it.simpleName}>(lparams, id, init, performer)")
|
||||
} ?: addStatement("return View<${performer.declaration.className}>(lparams, id, init)")
|
||||
|
||||
returns(viewClass.second)
|
||||
}.build())
|
||||
}.build()
|
||||
|
||||
// May already exists, no need to generate.
|
||||
runCatching {
|
||||
fileSpec.writeTo(codeGenerator, dependencies)
|
||||
@@ -239,6 +260,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
ksType.declaration.qualifiedName?.asString() != Any::class.qualifiedName
|
||||
}
|
||||
var resolvedLparams = lparamsType?.declaration?.getClassDeclaration(resolver)
|
||||
|
||||
when {
|
||||
// If the current view is not a view group but the lparams parameter is declared,
|
||||
// remove the lparams parameter.
|
||||
@@ -253,6 +275,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
// set the default type parameter for it.
|
||||
declaration.isViewGroup && resolvedLparams == null -> resolvedLparams = lparamsDeclaration
|
||||
}
|
||||
|
||||
// Verify layout parameter class.
|
||||
if (resolvedLparams != null) require(resolvedLparams.isSubclassOf(lparamsDeclaration.asType())) {
|
||||
val resolvedLparamsName = resolvedLparams.qualifiedName?.asString()
|
||||
@@ -271,17 +294,21 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
val packageName = ksClass.packageName.asString()
|
||||
val className = ksClass.getSimpleNameString()
|
||||
val isViewGroup = ksClass.isSubclassOf(viewGroupDeclaration.asType())
|
||||
|
||||
var _alias = alias
|
||||
// If no alias name is set, if the class name contains a subclass,
|
||||
// replace it with an underscore and use it as an alias name.
|
||||
if (_alias.isNullOrBlank() && className.contains("."))
|
||||
_alias = className.replace(".", "_")
|
||||
_alias = _alias?.takeIf { it.isNotBlank() }
|
||||
|
||||
val declaration = ViewDeclaration(packageName, className, _alias, isViewGroup, baseType)
|
||||
|
||||
// Verify the legality of the class name.
|
||||
if (!_alias.isNullOrBlank()) require(ClassDetector.verify(_alias)) {
|
||||
"Declares @$tagName's alias \"$_alias\" is illegal.\n${declaration.locateDesc}"
|
||||
}
|
||||
|
||||
// [ViewGroup] cannot be new instance.
|
||||
require(ksClass != viewGroupDeclaration) {
|
||||
"Declares @$tagName's class must not be a directly \"${DeclaredSymbol.ANDROID_VIEW_GROUP_CLASS}\".\n${declaration.locateDesc}"
|
||||
@@ -290,6 +317,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
require(ksClass.isSubclassOf(viewDeclaration.asType())) {
|
||||
"Declares @$tagName's class must be subclass of \"${DeclaredSymbol.ANDROID_VIEW_CLASS}\".\n${declaration.locateDesc}"
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
}
|
||||
@@ -315,7 +343,8 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
override val lparams: KSClassDeclaration?,
|
||||
override val alias: String?,
|
||||
override val requireInit: Boolean,
|
||||
override val requirePerformer: Boolean
|
||||
override val requirePerformer: Boolean,
|
||||
override val final: Boolean
|
||||
) : HikageAnnotationSpec {
|
||||
|
||||
companion object {
|
||||
@@ -332,10 +361,13 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
val alias = annotation.arguments.getOrNull<String>("alias")
|
||||
val requireInit = annotation.arguments.getOrNull<Boolean>("requireInit") ?: false
|
||||
val requirePerformer = annotation.arguments.getOrNull<Boolean>("requirePerformer") ?: false
|
||||
val final = annotation.arguments.getOrNull<Boolean>("final") ?: false
|
||||
|
||||
// Solve the actual content of the annotation parameters.
|
||||
val declaration = Processor.createViewDeclaration(NAME, alias, ksClass)
|
||||
val resolvedLparams = Processor.resolvedLparamsDeclaration(NAME, resolver, declaration, lparams)
|
||||
return HikageViewSpec(resolvedLparams, alias, requireInit, requirePerformer) to declaration
|
||||
|
||||
return HikageViewSpec(resolvedLparams, alias, requireInit, requirePerformer, final) to declaration
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,7 +377,8 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
override val lparams: KSClassDeclaration?,
|
||||
override val alias: String?,
|
||||
override val requireInit: Boolean,
|
||||
override val requirePerformer: Boolean
|
||||
override val requirePerformer: Boolean,
|
||||
override val final: Boolean
|
||||
) : HikageAnnotationSpec {
|
||||
|
||||
companion object {
|
||||
@@ -363,9 +396,12 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
val alias = annotation.arguments.getOrNull<String>("alias")
|
||||
val requireInit = annotation.arguments.getOrNull<Boolean>("requireInit") ?: false
|
||||
val requirePerformer = annotation.arguments.getOrNull<Boolean>("requirePerformer") ?: false
|
||||
val final = annotation.arguments.getOrNull<Boolean>("final") ?: false
|
||||
|
||||
// Solve the actual content of the annotation parameters.
|
||||
val resolvedView = view?.declaration?.getClassDeclaration(resolver) ?: error("Internal error.")
|
||||
val declaration = Processor.createViewDeclaration(NAME, alias, resolvedView, ksClass)
|
||||
|
||||
// Only object classes can be used as view declarations.
|
||||
require(ksClass.classKind == ClassKind.OBJECT) {
|
||||
"Declares @$NAME's class must be an object.\n${declaration.locateDesc}"
|
||||
@@ -373,8 +409,9 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
require(!ksClass.isCompanionObject) {
|
||||
"Declares @$NAME's class must not be a companion object.\n${declaration.locateDesc}"
|
||||
}
|
||||
|
||||
val resolvedLparams = Processor.resolvedLparamsDeclaration(NAME, resolver, declaration, lparams)
|
||||
return HikageViewDeclarationSpec(resolvedView, resolvedLparams, alias, requireInit, requirePerformer) to declaration
|
||||
return HikageViewDeclarationSpec(resolvedView, resolvedLparams, alias, requireInit, requirePerformer, final) to declaration
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,6 +421,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
|
||||
val alias: String?
|
||||
val requireInit: Boolean
|
||||
val requirePerformer: Boolean
|
||||
val final: Boolean
|
||||
}
|
||||
|
||||
private data class Performer(
|
||||
|
@@ -60,6 +60,7 @@ class HikageSafeTypeCastDetector : Detector(), Detector.UastScanner {
|
||||
|
||||
override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) {
|
||||
if (node.selector !is UBinaryExpressionWithType) return
|
||||
|
||||
val castExpr = node.selector as UBinaryExpressionWithType
|
||||
visitAndLint(context, castExpr, node)
|
||||
}
|
||||
@@ -75,11 +76,14 @@ class HikageSafeTypeCastDetector : Detector(), Detector.UastScanner {
|
||||
) {
|
||||
// Get the parent node, if it is wrapped with brackets will also be included.
|
||||
val locationNode = node.uastParent as? UParenthesizedExpression ?: node
|
||||
|
||||
val receiver = parent?.receiver ?: node.operand
|
||||
val receiverType = (node.operand as? UArrayAccessExpression)?.receiver?.getExpressionType() ?: return
|
||||
val receiverClass = receiverType.canonicalText
|
||||
|
||||
// Filter retains results that meet the conditions.
|
||||
if (receiverClass != DeclaredSymbol.HIKAGE_CLASS) return
|
||||
|
||||
// Like `hikage["your_id"] as YourView`.
|
||||
val exprText = node.sourcePsi?.text ?: return
|
||||
// Like `hikage["your_id"]`.
|
||||
@@ -88,11 +92,15 @@ class HikageSafeTypeCastDetector : Detector(), Detector.UastScanner {
|
||||
val receiverNameText = receiverText.split("[")[0]
|
||||
// Like `"your_id"`.
|
||||
val receiverContent = runCatching { receiverText.split("[")[1].split("]")[0] }.getOrNull() ?: return
|
||||
|
||||
val isSafeCast = exprText.contains("as?") || exprText.endsWith("?")
|
||||
|
||||
// Like `YourView`.
|
||||
val castTypeContent = node.typeReference?.sourcePsi?.text?.removeSuffix("?") ?: return
|
||||
|
||||
val replacement = "$receiverNameText.${if (isSafeCast) "getOrNull" else "get"}<$castTypeContent>($receiverContent)"
|
||||
val replaceSuggestion = if (isSafeCast) "Hikage.getOrNull<$castTypeContent>" else "Hikage.get<$castTypeContent>"
|
||||
|
||||
val location = context.getLocation(locationNode)
|
||||
val lintFix = LintFix.create()
|
||||
.name("Replace with '$replacement'")
|
||||
@@ -101,6 +109,7 @@ class HikageSafeTypeCastDetector : Detector(), Detector.UastScanner {
|
||||
.with(replacement)
|
||||
.reformat(true)
|
||||
.build()
|
||||
|
||||
context.report(
|
||||
ISSUE, locationNode, location,
|
||||
message = "Can be replaced with safe type cast `$replaceSuggestion`.",
|
||||
|
@@ -70,6 +70,7 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
|
||||
override fun visitCallExpression(node: UCallExpression) {
|
||||
val callExpr = node.sourcePsi as? KtCallExpression ?: return
|
||||
val method = node.resolve() ?: return
|
||||
|
||||
startLint(callExpr, method)
|
||||
organizeAndReport()
|
||||
}
|
||||
@@ -78,6 +79,7 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
|
||||
val className = method.containingClass?.qualifiedName ?: ""
|
||||
val hasHikageable = method.hasHikageable()
|
||||
val hasLayoutParams = className == DeclaredSymbol.HIKAGE_PERFORMER_CLASS && method.name == "LayoutParams"
|
||||
|
||||
if (hasHikageable || hasLayoutParams) visitAndLint(callExpr, method)
|
||||
}
|
||||
|
||||
@@ -85,6 +87,7 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
|
||||
reports.forEach {
|
||||
// Check if the call has been reported before reporting.
|
||||
if (reportedNodes.contains(it.callExpr)) return@forEach
|
||||
|
||||
val location = context.getLocation(it.callExpr)
|
||||
val lintFix = LintFix.create()
|
||||
.name("Delete Call Expression")
|
||||
@@ -93,6 +96,7 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
|
||||
// Delete the call expression.
|
||||
.with("")
|
||||
.build()
|
||||
|
||||
context.report(ISSUE, it.callExpr, location, it.message, lintFix)
|
||||
reportedNodes.add(it.callExpr)
|
||||
}
|
||||
@@ -102,28 +106,37 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
|
||||
val bodyBlocks = mutableMapOf<String, KtExpression>()
|
||||
val parameters = method.parameterList.parameters
|
||||
val valueArguments = callExpr.valueArgumentList?.arguments ?: emptyList()
|
||||
|
||||
fun visitValueArg(arg: KtValueArgument) {
|
||||
val name = arg.getArgumentName()?.asName?.identifier ?: ""
|
||||
val expr = arg.getArgumentExpression()
|
||||
|
||||
val parameter = parameters.firstOrNull { it.name == name }
|
||||
// If the last bit is a lambda expression, then `parameter` must have a lambda parameter defined by the last bit.
|
||||
?: if (arg is KtLambdaArgument) parameters.lastOrNull() else null
|
||||
|
||||
val isMatched = parameter?.type?.canonicalText?.matches(DeclaredSymbol.HIKAGE_VIEW_REGEX) == true &&
|
||||
!parameter.type.canonicalText.contains(DeclaredSymbol.HIKAGE_PERFORMER_CLASS)
|
||||
|
||||
if (expr is KtLambdaExpression && isMatched)
|
||||
expr.bodyExpression?.let { bodyBlocks[name] = it }
|
||||
}
|
||||
|
||||
// Get the last lambda expression.
|
||||
val lastLambda = callExpr.lambdaArguments.lastOrNull()
|
||||
if (lastLambda != null) visitValueArg(lastLambda)
|
||||
|
||||
valueArguments.forEach { arg -> visitValueArg(arg) }
|
||||
|
||||
bodyBlocks.toList().flatMap { (_, value) -> value.children.filterIsInstance<KtCallExpression>() }.forEach {
|
||||
val expression = it.toUElementOfType<UCallExpression>() ?: return@forEach
|
||||
val sCallExpr = expression.sourcePsi as? KtCallExpression ?: return@forEach
|
||||
val sMethod = expression.resolve() ?: return@forEach
|
||||
|
||||
if (sMethod.hasHikageable()) {
|
||||
val message = "Performers are not allowed to appear in `${method.name}` DSL creation process."
|
||||
reports.add(ReportDetail(message, expression))
|
||||
|
||||
// Recursively to visit next level.
|
||||
visitAndLint(sCallExpr, sMethod)
|
||||
}
|
||||
|
@@ -65,6 +65,7 @@ class HikageableFunctionsDetector : Detector(), Detector.UastScanner {
|
||||
|
||||
override fun visitMethod(node: UMethod) {
|
||||
val uastBody = node.uastBody as? UBlockExpression ?: return
|
||||
|
||||
val bodyHasHikageable = uastBody.expressions.any {
|
||||
when (it) {
|
||||
is UCallExpression -> it.resolve()?.hasHikageable() ?: false
|
||||
@@ -73,17 +74,22 @@ class HikageableFunctionsDetector : Detector(), Detector.UastScanner {
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
if (!node.hasHikageable() && bodyHasHikageable) {
|
||||
val location = context.getLocation(node)
|
||||
val nameLocation = context.getNameLocation(node)
|
||||
|
||||
val message = "Function `${node.name}` must be marked with the `@Hikageable` annotation."
|
||||
|
||||
val functionText = node.asSourceString()
|
||||
val hasDoubleSlash = functionText.startsWith("//")
|
||||
|
||||
val replacement = functionRegex.replace(functionText) { result ->
|
||||
val functionBody = result.groupValues.getOrNull(0) ?: functionText
|
||||
val prefix = if (hasDoubleSlash) "\n" else ""
|
||||
"$prefix@Hikageable $functionBody"
|
||||
}
|
||||
|
||||
val lintFix = LintFix.create()
|
||||
.name("Add '@Hikageable' to '${node.name}'")
|
||||
.replace()
|
||||
@@ -92,6 +98,7 @@ class HikageableFunctionsDetector : Detector(), Detector.UastScanner {
|
||||
.imports(DeclaredSymbol.HIKAGEABLE_ANNOTATION_CLASS)
|
||||
.reformat(true)
|
||||
.build()
|
||||
|
||||
context.report(ISSUE, node, nameLocation, message, lintFix)
|
||||
}
|
||||
}
|
||||
|
@@ -135,18 +135,23 @@ class WidgetsUsageDetector : Detector(), Detector.UastScanner {
|
||||
importRefs.getOrNull(1)?.trim()
|
||||
else null
|
||||
val importPrefix = importRefs[0]
|
||||
|
||||
val hasPrefix = importPrefix.contains(".")
|
||||
|
||||
val name = if (hasPrefix)
|
||||
importPrefix.split(".").last()
|
||||
else importPrefix
|
||||
|
||||
val packagePrefix = importPrefix.replace(if (hasPrefix) ".$name" else name, "")
|
||||
val reference = ImportReference(packagePrefix, name, alias)
|
||||
|
||||
importReferences.add(reference)
|
||||
}
|
||||
|
||||
override fun visitCallExpression(node: UCallExpression) {
|
||||
val callExpr = node.sourcePsi as? KtCallExpression ?: return
|
||||
val method = node.resolve() ?: return
|
||||
|
||||
startLint(callExpr, method)
|
||||
}
|
||||
|
||||
@@ -165,7 +170,9 @@ class WidgetsUsageDetector : Detector(), Detector.UastScanner {
|
||||
type.canonicalText == VIEW_GROUP_CLASS_NAME
|
||||
}
|
||||
}
|
||||
|
||||
val isTypedViewFunction = typedViewFunctionIndex >= 0
|
||||
|
||||
val imports = typeArgumentsText.mapNotNull { typeName ->
|
||||
when {
|
||||
// Like `TextView`.
|
||||
@@ -179,18 +186,24 @@ class WidgetsUsageDetector : Detector(), Detector.UastScanner {
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val matchedIndex = builtInWidgets.indexOfFirst { imports.any { e -> e.alias == it || e.name == it } }
|
||||
val isBuiltInWidget = matchedIndex >= 0
|
||||
|
||||
if (isTypedViewFunction && isBuiltInWidget) {
|
||||
val widgetName = builtInWidgets[matchedIndex]
|
||||
|
||||
val sourceLocation = context.getLocation(callExpr)
|
||||
val sourceText = callExpr.toUElement()?.asSourceString() ?: return
|
||||
val callExprElement = callExpr.toUElement() ?: return
|
||||
|
||||
// Matchs '>' and like `View<TextView`'s length + 1.
|
||||
val callExprLength = sourceText.split(">")[0].trim().length + 1
|
||||
val nameLocation = context.getRangeLocation(callExprElement, fromDelta = 0, callExprLength)
|
||||
|
||||
// Only replace the first one, because there may be multiple sub-functions in DSL.
|
||||
val replacement = sourceText.replaceFirst(viewExpressionRegex, widgetName)
|
||||
|
||||
val lintFix = LintFix.create()
|
||||
.name("Replace with '$widgetName'")
|
||||
.replace()
|
||||
@@ -199,6 +212,7 @@ class WidgetsUsageDetector : Detector(), Detector.UastScanner {
|
||||
.imports("$WIDGET_FUNCTION_PREFIX.$widgetName")
|
||||
.reformat(true)
|
||||
.build()
|
||||
|
||||
val message = "Can be simplified to `$widgetName`."
|
||||
context.report(ISSUE, callExpr, nameLocation, message, lintFix)
|
||||
}
|
||||
|
@@ -28,15 +28,6 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
"-opt-in=kotlin.ExperimentalStdlibApi",
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@@ -45,6 +45,9 @@ import kotlin.reflect.KClass
|
||||
* @param requireInit whether to force the `init` parameter to be called, default is false.
|
||||
* @param requirePerformer whether to force the `performer` parameter to be called, default is false,
|
||||
* this parameter will be ignored when no `performer` parameter is needed here.
|
||||
* @param final whether to declare this layout as "final layout", default is false, that is,
|
||||
* whether this layout inherits from or is [ViewGroup], the `performer` parameter will not be generated.
|
||||
* After set to `true`, [lparams] and [requirePerformer] will no longer be valid.
|
||||
*/
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@@ -53,5 +56,6 @@ annotation class HikageView(
|
||||
val lparams: KClass<*> = Any::class,
|
||||
val alias: String = "",
|
||||
val requireInit: Boolean = false,
|
||||
val requirePerformer: Boolean = false
|
||||
val requirePerformer: Boolean = false,
|
||||
val final: Boolean = false
|
||||
)
|
@@ -48,6 +48,9 @@ import kotlin.reflect.KClass
|
||||
* @param requireInit whether to force the `init` parameter to be called, default is false.
|
||||
* @param requirePerformer whether to force the `performer` parameter to be called, default is false,
|
||||
* this parameter will be ignored when no `performer` parameter is needed here.
|
||||
* @param final whether to declare this layout as "final layout", default is false, that is,
|
||||
* whether this layout inherits from or is [ViewGroup], the `performer` parameter will not be generated.
|
||||
* After set to `true`, [lparams] and [requirePerformer] will no longer be valid.
|
||||
*/
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@@ -57,5 +60,6 @@ annotation class HikageViewDeclaration(
|
||||
val lparams: KClass<*> = Any::class,
|
||||
val alias: String = "",
|
||||
val requireInit: Boolean = false,
|
||||
val requirePerformer: Boolean = false
|
||||
val requirePerformer: Boolean = false,
|
||||
val final: Boolean = false
|
||||
)
|
@@ -31,7 +31,7 @@ import android.content.res.loader.AssetsProvider
|
||||
import android.content.res.loader.ResourcesProvider
|
||||
import android.util.AttributeSet
|
||||
import androidx.annotation.StyleRes
|
||||
import com.highcapable.betterandroid.system.extension.tool.SystemVersion
|
||||
import com.highcapable.betterandroid.system.extension.tool.AndroidVersion
|
||||
import com.highcapable.betterandroid.ui.extension.view.inflateOrNull
|
||||
import com.highcapable.betterandroid.ui.extension.view.layoutInflater
|
||||
import com.highcapable.hikage.core.R
|
||||
@@ -116,12 +116,12 @@ internal object XmlBlockBypass {
|
||||
private val resolver = object : MemberProcessor.Resolver() {
|
||||
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> =
|
||||
SystemVersion.require(SystemVersion.P, super.getDeclaredConstructors(declaringClass)) {
|
||||
AndroidVersion.require(AndroidVersion.P, super.getDeclaredConstructors(declaringClass)) {
|
||||
HiddenApiBypass.getDeclaredMethods(declaringClass).filterIsInstance<Constructor<T>>().toList()
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> =
|
||||
SystemVersion.require(SystemVersion.P, super.getDeclaredMethods(declaringClass)) {
|
||||
AndroidVersion.require(AndroidVersion.P, super.getDeclaredMethods(declaringClass)) {
|
||||
HiddenApiBypass.getDeclaredMethods(declaringClass).filterIsInstance<Method>().toList()
|
||||
}
|
||||
}
|
||||
@@ -143,6 +143,7 @@ internal object XmlBlockBypass {
|
||||
fun init(context: Context) {
|
||||
// Context may be loaded from the preview and other non-Android platforms, ignoring this.
|
||||
if (context.javaClass.name.endsWith("BridgeContext")) return
|
||||
|
||||
init(context.applicationContext.applicationInfo)
|
||||
}
|
||||
|
||||
@@ -151,11 +152,12 @@ internal object XmlBlockBypass {
|
||||
* @param info the application info.
|
||||
*/
|
||||
private fun init(info: ApplicationInfo) {
|
||||
if (SystemVersion.isLowOrEqualsTo(SystemVersion.P)) return
|
||||
if (AndroidVersion.isAtMost(AndroidVersion.P)) return
|
||||
if (isInitOnce) return
|
||||
|
||||
val sourceDir = info.sourceDir
|
||||
xmlBlock = when {
|
||||
SystemVersion.isHighOrEqualsTo(SystemVersion.R) ->
|
||||
AndroidVersion.isAtLeast(AndroidVersion.R) ->
|
||||
// private static native long nativeLoad(@FormatType int format, @NonNull String path,
|
||||
// @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
|
||||
ApkAssetsClass.resolve()
|
||||
@@ -166,7 +168,7 @@ internal object XmlBlockBypass {
|
||||
parameters(Int::class, String::class, Int::class, AssetsProvider::class)
|
||||
modifiers(Modifiers.NATIVE)
|
||||
}?.invokeQuietly(FORMAT_APK, sourceDir, PROPERTY_SYSTEM, null)
|
||||
SystemVersion.isHighOrEqualsTo(SystemVersion.P) ->
|
||||
AndroidVersion.isAtLeast(AndroidVersion.P) ->
|
||||
// private static native long nativeLoad(
|
||||
// @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
|
||||
// throws IOException;
|
||||
@@ -180,18 +182,20 @@ internal object XmlBlockBypass {
|
||||
}?.invokeQuietly(sourceDir, false, false, false)
|
||||
else -> error("Unsupported Android version.")
|
||||
} as? Long? ?: error("Failed to create ApkAssets.")
|
||||
|
||||
blockParser = XmlBlockClass.resolve()
|
||||
.processor(resolver)
|
||||
.optional()
|
||||
.firstConstructorOrNull {
|
||||
if (SystemVersion.isHighOrEqualsTo(36))
|
||||
if (AndroidVersion.isAtLeast(AndroidVersion.BAKLAVA))
|
||||
parameters(AssetManager::class, Long::class, Boolean::class)
|
||||
else parameters(AssetManager::class, Long::class)
|
||||
}?.let {
|
||||
if (SystemVersion.isHighOrEqualsTo(36))
|
||||
if (AndroidVersion.isAtLeast(AndroidVersion.BAKLAVA))
|
||||
it.createQuietly(null, xmlBlock, false)
|
||||
else it.createQuietly(null, xmlBlock)
|
||||
} ?: error("Failed to create XmlBlock\$Parser.")
|
||||
|
||||
isInitOnce = true
|
||||
}
|
||||
|
||||
@@ -208,8 +212,10 @@ internal object XmlBlockBypass {
|
||||
*/
|
||||
fun createViewAttrs() = context.layoutInflater.inflateOrNull<HikageAttrsView>(R.layout.layout_hikage_attrs_view)?.attrs
|
||||
as? XmlResourceParser? ?: error("Failed to create AttributeSet.")
|
||||
return if (SystemVersion.isHighOrEqualsTo(SystemVersion.P)) {
|
||||
|
||||
return if (AndroidVersion.isAtLeast(AndroidVersion.P)) {
|
||||
if (!isInitOnce) return createViewAttrs()
|
||||
|
||||
require(blockParser != null) { "Hikage initialization failed." }
|
||||
newParser?.copy()?.of(blockParser)
|
||||
?.invokeQuietly<XmlResourceParser>(resId)
|
||||
|
@@ -53,7 +53,6 @@ import com.highcapable.betterandroid.ui.extension.view.inflate
|
||||
import com.highcapable.betterandroid.ui.extension.view.layoutInflater
|
||||
import com.highcapable.hikage.annotation.Hikageable
|
||||
import com.highcapable.hikage.bypass.XmlBlockBypass
|
||||
import com.highcapable.hikage.core.Hikage.LayoutParamsBody
|
||||
import com.highcapable.hikage.core.base.HikageFactory
|
||||
import com.highcapable.hikage.core.base.HikageFactoryBuilder
|
||||
import com.highcapable.hikage.core.base.HikagePerformer
|
||||
@@ -167,7 +166,9 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
if (parent != null && attachToParent) {
|
||||
val parentId = generateRandomViewId()
|
||||
provideView(parent, parentId)
|
||||
}; newPerformer(lpClass, parent, attachToParent, context).apply(performer)
|
||||
}
|
||||
|
||||
newPerformer(lpClass, parent, attachToParent, context).apply(performer)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,11 +318,13 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
*/
|
||||
private fun <V : View> createView(viewClass: Class<V>, id: String?, context: Context): V {
|
||||
val attrs = createAttributeSet(context)
|
||||
|
||||
val view = createViewFromFactory(viewClass, id, context, attrs) ?: getViewConstructor(viewClass)?.build(context, attrs)
|
||||
if (view == null) throw PerformerException(
|
||||
"Create view of type ${viewClass.name} failed. " +
|
||||
"Please make sure the view class has a constructor with a single parameter of type Context."
|
||||
)
|
||||
|
||||
provideView(view, id)
|
||||
return view
|
||||
}
|
||||
@@ -334,16 +337,20 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
private fun <V : View> getViewConstructor(viewClass: Class<V>) =
|
||||
viewConstructors[viewClass.name] as? ViewConstructor<V>? ?: run {
|
||||
var parameterCount = 0
|
||||
|
||||
val twoParams = viewClass.resolve()
|
||||
.optional(silent = true)
|
||||
.firstConstructorOrNull { parameters(Context::class, AttributeSet::class) }
|
||||
val onceParam = viewClass.resolve()
|
||||
.optional(silent = true)
|
||||
.firstConstructorOrNull { parameters(Context::class) }
|
||||
|
||||
val constructor = onceParam?.apply { parameterCount = 1 }
|
||||
?: twoParams?.apply { parameterCount = 2 }
|
||||
|
||||
val viewConstructor = constructor?.let { ViewConstructor(it, parameterCount) }
|
||||
if (viewConstructor != null) viewConstructors[viewClass.name] = viewConstructor
|
||||
|
||||
viewConstructor
|
||||
}
|
||||
|
||||
@@ -358,15 +365,20 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
private fun <V : View> createViewFromFactory(viewClass: Class<V>, id: String?, context: Context, attrs: AttributeSet): V? {
|
||||
val parent = performers.firstOrNull()?.parent
|
||||
var processed: V? = null
|
||||
|
||||
factories.forEach { factory ->
|
||||
val params = PerformerParams(id, attrs, viewClass as Class<View>)
|
||||
|
||||
val view = factory(parent, processed, context, params)
|
||||
if (view != null && view.javaClass isNotSubclassOf viewClass) throw PerformerException(
|
||||
"HikageFactory cannot cast the created view type \"${view.javaClass}\" to \"${viewClass.name}\", " +
|
||||
"please confirm that the view type you created is correct."
|
||||
)
|
||||
|
||||
if (view != null) processed = view as? V?
|
||||
}; return processed
|
||||
}
|
||||
|
||||
return processed
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,6 +391,7 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
val (requireId, viewId) = generateViewId(id)
|
||||
view.id = viewId
|
||||
views[requireId] = view
|
||||
|
||||
return requireId
|
||||
}
|
||||
|
||||
@@ -395,12 +408,16 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
*/
|
||||
fun doGenerate(id: String): Int {
|
||||
val generateId = View.generateViewId()
|
||||
|
||||
if (viewIds.contains(id)) throw PerformerException("View with id \"$id\" already exists.")
|
||||
viewIds[id] = generateId
|
||||
|
||||
return generateId
|
||||
}
|
||||
|
||||
val requireId = id ?: generateRandomViewId()
|
||||
val viewId = doGenerate(requireId)
|
||||
|
||||
return requireId to viewId
|
||||
}
|
||||
|
||||
@@ -456,6 +473,7 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
*/
|
||||
internal inline fun requireNoPerformers(name: String, block: () -> Unit) {
|
||||
val viewCount = views.size
|
||||
|
||||
block()
|
||||
if (views.size != viewCount) throw PerformerException(
|
||||
"Performers are not allowed to appear in $name DSL creation process."
|
||||
@@ -472,13 +490,16 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
private fun include(delegate: Delegate<*>, context: Context, embedded: Boolean): Hikage {
|
||||
val hikage = delegate.create(context)
|
||||
if (!embedded) return hikage
|
||||
|
||||
val duplicateId = hikage.viewIds.toList().firstOrNull { (k, _) -> viewIds.containsKey(k) }?.first
|
||||
if (duplicateId != null) throw PerformerException(
|
||||
"Embedded layout view IDs conflict, the view id \"$duplicateId\" is already exists."
|
||||
)
|
||||
|
||||
viewIds.putAll(hikage.viewIds)
|
||||
views.putAll(hikage.views)
|
||||
performers.addAll(hikage.performers)
|
||||
|
||||
return hikage
|
||||
}
|
||||
|
||||
@@ -544,9 +565,11 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
val lpDelegate = LayoutParams.from(current, lpClass, parent, lparams)
|
||||
val view = createView(classOf<V>(), id, context)
|
||||
view.layoutParams = lpDelegate.create()
|
||||
|
||||
requireNoPerformers(classOf<V>().name) { view.init() }
|
||||
startProvide(id, classOf<V>())
|
||||
addToParentIfRequired(view)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -587,10 +610,12 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
val lpDelegate = LayoutParams.from(current, lpClass, parent, lparams)
|
||||
val view = createView(classOf<VG>(), id, context)
|
||||
view.layoutParams = lpDelegate.create()
|
||||
|
||||
requireNoPerformers(classOf<VG>().name) { view.init() }
|
||||
startProvide(id, classOf<VG>())
|
||||
addToParentIfRequired(view)
|
||||
newPerformer<LP>(view).apply(performer)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -625,10 +650,12 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
id: String? = null
|
||||
): View {
|
||||
val view = context.layoutInflater.inflate(resId, parent, attachToRoot = false)
|
||||
|
||||
startProvide(id, view.javaClass, view)
|
||||
lparams?.create()?.let { view.layoutParams = it }
|
||||
provideView(view, id)
|
||||
addToParentIfRequired(view)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -645,14 +672,17 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
): VB {
|
||||
val viewBinding = ViewBinding<VB>().inflate(context.layoutInflater, parent, attachToParent = false)
|
||||
val view = viewBinding.root
|
||||
|
||||
startProvide(id, view.javaClass, view)
|
||||
if (view.parent != null) throw ProvideException(
|
||||
"The ViewBinding($view) already has a parent, " +
|
||||
"it may have been created using layout root node <merge> or <include>, cannot be provided."
|
||||
)
|
||||
|
||||
lparams?.create()?.let { view.layoutParams = it }
|
||||
provideView(view, id)
|
||||
addToParentIfRequired(view)
|
||||
|
||||
return viewBinding
|
||||
}
|
||||
|
||||
@@ -670,11 +700,14 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
id: String? = null
|
||||
): View {
|
||||
if (view.parent != null) throw ProvideException("The view $view already has a parent, cannot be provided.")
|
||||
|
||||
startProvide(id, view.javaClass, view)
|
||||
val lpDelegate = LayoutParams.from(current = this@Hikage, lpClass, parent, lparams, view.layoutParams)
|
||||
view.layoutParams = lpDelegate.create()
|
||||
|
||||
provideView(view, id)
|
||||
addToParentIfRequired(view)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -708,13 +741,17 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
): Hikage {
|
||||
val view = hikage.root
|
||||
startProvide(id, view.javaClass, view)
|
||||
|
||||
val lpDelegate = LayoutParams.from(current = this@Hikage, lpClass, parent, lparams, view.layoutParams)
|
||||
if (view.parent != null) throw ProvideException(
|
||||
"The Hikage layout root view $view already has a parent, cannot be provided."
|
||||
)
|
||||
|
||||
view.layoutParams = lpDelegate.create()
|
||||
|
||||
provideView(view, id)
|
||||
addToParentIfRequired(view)
|
||||
|
||||
return hikage
|
||||
}
|
||||
|
||||
@@ -783,6 +820,7 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
*/
|
||||
private fun <V : View> startProvide(id: String?, viewClass: Class<V>, view: V? = null) {
|
||||
provideCount++
|
||||
|
||||
if (provideCount > 1 && (parent == null || !attachToParent)) throw ProvideException(
|
||||
"Provide view ${view?.javaClass ?: viewClass}(${id?.let { "\"$it\""} ?: "<anonymous>"}) failed. ${
|
||||
if (parent == null) "No parent view group found"
|
||||
@@ -900,6 +938,7 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
superclass()
|
||||
}?.invokeQuietly<ViewGroup.LayoutParams>(it)
|
||||
} ?: lparams
|
||||
|
||||
return wrapped
|
||||
// Build a default.
|
||||
?: lpClass.createInstanceOrNull(LayoutParamsWrapContent, LayoutParamsWrapContent)
|
||||
@@ -912,12 +951,15 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
|
||||
*/
|
||||
fun create(): ViewGroup.LayoutParams {
|
||||
if (bodyBuilder == null && wrapperBuilder == null) throw PerformerException("No layout params builder found.")
|
||||
|
||||
return bodyBuilder?.let {
|
||||
val lparams = ViewLayoutParams(lpClass, it.width, it.height, it.matchParent, it.widthMatchParent, it.heightMatchParent)
|
||||
current.requireNoPerformers(lparams.javaClass.name) { it.body(lparams) }
|
||||
|
||||
lparams
|
||||
} ?: wrapperBuilder?.let {
|
||||
val lparams = it.delegate?.create() ?: it.lparams
|
||||
|
||||
createDefaultLayoutParams(lparams)
|
||||
} ?: throw PerformerException("Internal error of build layout params.")
|
||||
}
|
||||
|
@@ -47,6 +47,7 @@ abstract class HikagePreview(context: Context, attrs: AttributeSet? = null) : Fr
|
||||
@CallSuper
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
removeAllViews()
|
||||
build().create(context, parent = this)
|
||||
}
|
||||
|
@@ -24,7 +24,8 @@
|
||||
|
||||
package com.highcapable.hikage.widget.android
|
||||
|
||||
import android.widget.AbsListView
|
||||
import android.view.SurfaceView
|
||||
import android.webkit.WebView
|
||||
import android.widget.ActionMenuView
|
||||
import android.widget.AutoCompleteTextView
|
||||
import android.widget.Button
|
||||
@@ -101,7 +102,7 @@ private object SpaceDeclaration
|
||||
@HikageViewDeclaration(CheckedTextView::class)
|
||||
private object CheckedTextViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(ExpandableListView::class, AbsListView.LayoutParams::class)
|
||||
@HikageViewDeclaration(ExpandableListView::class, final = true)
|
||||
private object ExpandableListViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(Spinner::class)
|
||||
@@ -143,13 +144,13 @@ private object TextSwitcherDeclaration
|
||||
@HikageViewDeclaration(ActionMenuView::class, ActionMenuView.LayoutParams::class)
|
||||
private object ActionMenuViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(CalendarView::class, FrameLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(CalendarView::class, final = true)
|
||||
private object CalendarViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(DatePicker::class, FrameLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(DatePicker::class, final = true)
|
||||
private object DatePickerDeclaration
|
||||
|
||||
@HikageViewDeclaration(TimePicker::class, FrameLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(TimePicker::class, final = true)
|
||||
private object TimePickerDeclaration
|
||||
|
||||
@HikageViewDeclaration(RatingBar::class)
|
||||
@@ -173,19 +174,25 @@ private object ViewFlipperDeclaration
|
||||
@HikageViewDeclaration(ViewAnimator::class, FrameLayout.LayoutParams::class)
|
||||
private object ViewAnimatorDeclaration
|
||||
|
||||
@HikageViewDeclaration(SurfaceView::class)
|
||||
private object SurfaceVieweclaration
|
||||
|
||||
@HikageViewDeclaration(VideoView::class)
|
||||
private object VideoViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(Toolbar::class, Toolbar.LayoutParams::class)
|
||||
@HikageViewDeclaration(WebView::class, final = true)
|
||||
private object WebViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(Toolbar::class, final = true)
|
||||
private object ToolbarDeclaration
|
||||
|
||||
@HikageViewDeclaration(GridLayout::class, GridLayout.LayoutParams::class)
|
||||
private object GridLayoutDeclaration
|
||||
|
||||
@HikageViewDeclaration(GridView::class, AbsListView.LayoutParams::class)
|
||||
@HikageViewDeclaration(GridView::class, final = true)
|
||||
private object GridViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(ListView::class, AbsListView.LayoutParams::class)
|
||||
@HikageViewDeclaration(ListView::class, final = true)
|
||||
private object ListViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(ImageView::class)
|
||||
@@ -200,10 +207,10 @@ private object TableLayoutDeclaration
|
||||
@HikageViewDeclaration(TableRow::class, TableRow.LayoutParams::class)
|
||||
private object TableRowDeclaration
|
||||
|
||||
@HikageViewDeclaration(NumberPicker::class, LinearLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(NumberPicker::class, final = true)
|
||||
private object NumberPickerDeclaration
|
||||
|
||||
@HikageViewDeclaration(SearchView::class, FrameLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(SearchView::class, final = true)
|
||||
private object SearchViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(Switch::class)
|
||||
@@ -212,5 +219,5 @@ private object SwitchDeclaration
|
||||
@HikageViewDeclaration(TabHost::class, FrameLayout.LayoutParams::class)
|
||||
private object TabHostDeclaration
|
||||
|
||||
@HikageViewDeclaration(TabWidget::class, LinearLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(TabWidget::class, final = true)
|
||||
private object TabWidgetDeclaration
|
@@ -27,14 +27,6 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -42,6 +34,7 @@ dependencies {
|
||||
implementation(com.highcapable.kavaref.kavaref.core)
|
||||
implementation(com.highcapable.kavaref.kavaref.extension)
|
||||
implementation(com.highcapable.betterandroid.ui.component)
|
||||
implementation(com.highcapable.betterandroid.ui.component.adapter)
|
||||
implementation(com.highcapable.betterandroid.ui.extension)
|
||||
implementation(com.highcapable.betterandroid.system.extension)
|
||||
implementation(androidx.core.core.ktx)
|
||||
|
@@ -25,7 +25,7 @@
|
||||
package com.highcapable.hikage.extension.betterandroid.ui.component.adapter
|
||||
|
||||
import android.view.ViewGroup
|
||||
import com.highcapable.betterandroid.ui.component.adapter.CommonAdapterBuilder
|
||||
import com.highcapable.betterandroid.ui.component.adapter.BaseAdapterBuilder
|
||||
import com.highcapable.hikage.core.Hikage
|
||||
import com.highcapable.hikage.core.base.HikagePerformer
|
||||
import com.highcapable.hikage.core.base.Hikageable
|
||||
@@ -51,26 +51,26 @@ import com.highcapable.hikage.extension.betterandroid.ui.component.adapter.viewh
|
||||
* hikage.get<TextView>("text").text = "Item ${entity.name} of ${position + 1}"
|
||||
* }
|
||||
* ```
|
||||
* @see CommonAdapterBuilder.onBindItemView
|
||||
* @receiver [CommonAdapterBuilder]<[E]>
|
||||
* @see BaseAdapterBuilder.onBindItemView
|
||||
* @receiver [BaseAdapterBuilder]<[E]>
|
||||
* @param Hikageable the performer body.
|
||||
* @return [CommonAdapterBuilder]<[E]>
|
||||
* @return [BaseAdapterBuilder]<[E]>
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun <E> CommonAdapterBuilder<E>.onBindItemView(
|
||||
fun <E> BaseAdapterBuilder<E>.onBindItemView(
|
||||
Hikageable: HikagePerformer<ViewGroup.LayoutParams>,
|
||||
viewHolder: (hikage: Hikage, entity: E, position: Int) -> Unit = { _, _, _ -> }
|
||||
) = onBindItemView(Hikageable(performer = Hikageable), viewHolder)
|
||||
|
||||
/**
|
||||
* Create and add view holder from [Hikage.Delegate].
|
||||
* @see CommonAdapterBuilder.onBindItemView
|
||||
* @receiver [CommonAdapterBuilder]<[E]>
|
||||
* @see BaseAdapterBuilder.onBindItemView
|
||||
* @receiver [BaseAdapterBuilder]<[E]>
|
||||
* @param delegate the delegate.
|
||||
* @return [CommonAdapterBuilder]<[E]>
|
||||
* @return [BaseAdapterBuilder]<[E]>
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun <E> CommonAdapterBuilder<E>.onBindItemView(
|
||||
fun <E> BaseAdapterBuilder<E>.onBindItemView(
|
||||
delegate: Hikage.Delegate<*>,
|
||||
viewHolder: (hikage: Hikage, entity: E, position: Int) -> Unit = { _, _, _ -> }
|
||||
) = onBindItemView(HikageHolderDelegate(delegate), viewHolder)
|
@@ -28,14 +28,6 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@@ -27,14 +27,6 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@@ -30,6 +30,18 @@ import com.highcapable.hikage.core.base.HikageFactoryBuilder
|
||||
import com.highcapable.hikage.core.base.HikagePerformer
|
||||
import com.highcapable.hikage.core.base.Hikageable
|
||||
|
||||
/**
|
||||
* @see ViewGroup.addView
|
||||
* @see Hikageable
|
||||
* @return [Hikage]
|
||||
*/
|
||||
@JvmName("addViewTyped")
|
||||
inline fun <reified LP : ViewGroup.LayoutParams> ViewGroup.addView(
|
||||
index: Int = -1,
|
||||
factory: HikageFactoryBuilder.() -> Unit = {},
|
||||
performer: HikagePerformer<LP>
|
||||
) = Hikageable<LP>(context = context, factory = factory, performer = performer).apply { addView(root, index) }
|
||||
|
||||
/**
|
||||
* @see ViewGroup.addView
|
||||
* @see Hikageable
|
||||
@@ -39,7 +51,7 @@ inline fun ViewGroup.addView(
|
||||
index: Int = -1,
|
||||
factory: HikageFactoryBuilder.() -> Unit = {},
|
||||
performer: HikagePerformer<ViewGroup.LayoutParams>
|
||||
) = Hikageable(context = context, factory = factory, performer = performer).apply { addView(root, index) }
|
||||
) = addView<ViewGroup.LayoutParams>(index, factory, performer)
|
||||
|
||||
/**
|
||||
* @see ViewGroup.addView
|
||||
|
@@ -27,14 +27,6 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@@ -78,7 +78,7 @@ private object AppCompatTextViewDeclaration
|
||||
@HikageViewDeclaration(AppCompatToggleButton::class)
|
||||
private object AppCompatToggleButtonDeclaration
|
||||
|
||||
@HikageViewDeclaration(AppCompatToolbar::class, AppCompatToolbar.LayoutParams::class)
|
||||
@HikageViewDeclaration(AppCompatToolbar::class, final = true)
|
||||
private object AppCompatToolbarDeclaration
|
||||
|
||||
@HikageViewDeclaration(AppCompatCheckedTextView::class)
|
||||
@@ -87,7 +87,7 @@ private object AppCompatCheckedTextViewDeclaration
|
||||
@HikageViewDeclaration(SwitchCompat::class)
|
||||
private object SwitchCompatDeclaration
|
||||
|
||||
@HikageViewDeclaration(AppCompatSearchView::class)
|
||||
@HikageViewDeclaration(AppCompatSearchView::class, final = true)
|
||||
private object AppCompatSearchViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(LinearLayoutCompat::class, LinearLayoutCompat.LayoutParams::class)
|
||||
|
@@ -24,8 +24,36 @@
|
||||
|
||||
package com.highcapable.hikage.widget.androidx.constraintlayout
|
||||
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterButton
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
import androidx.constraintlayout.utils.widget.MockView
|
||||
import androidx.constraintlayout.utils.widget.MotionButton
|
||||
import androidx.constraintlayout.utils.widget.MotionLabel
|
||||
import androidx.constraintlayout.utils.widget.MotionTelltales
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(ConstraintLayout::class, ConstraintLayout.LayoutParams::class)
|
||||
private object ConstraintLayoutDeclaration
|
||||
private object ConstraintLayoutDeclaration
|
||||
|
||||
@HikageViewDeclaration(MotionLayout::class, ConstraintLayout.LayoutParams::class)
|
||||
private object MotionLayoutDeclaration
|
||||
|
||||
@HikageViewDeclaration(ImageFilterButton::class)
|
||||
private object ImageFilterButtonDeclaration
|
||||
|
||||
@HikageViewDeclaration(ImageFilterView::class)
|
||||
private object ImageFilterViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(MockView::class)
|
||||
private object MockViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(MotionButton::class)
|
||||
private object MotionButtonDeclaration
|
||||
|
||||
@HikageViewDeclaration(MotionLabel::class)
|
||||
private object MotionLabelDeclaration
|
||||
|
||||
@HikageViewDeclaration(MotionTelltales::class)
|
||||
private object MotionTelltalesDeclaration
|
@@ -27,5 +27,5 @@ package com.highcapable.hikage.widget.androidx.recyclerview
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(RecyclerView::class, RecyclerView.LayoutParams::class)
|
||||
@HikageViewDeclaration(RecyclerView::class, final = true)
|
||||
private object RecyclerViewDeclaration
|
@@ -28,8 +28,8 @@ import androidx.viewpager.widget.ViewPager
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(ViewPager::class, ViewPager.LayoutParams::class)
|
||||
@HikageViewDeclaration(ViewPager::class, final = true)
|
||||
private object ViewPagerDeclaration
|
||||
|
||||
@HikageViewDeclaration(ViewPager2::class)
|
||||
@HikageViewDeclaration(ViewPager2::class, final = true)
|
||||
private object ViewPager2Declaration
|
@@ -27,14 +27,6 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@@ -24,7 +24,6 @@
|
||||
|
||||
package com.highcapable.hikage.widget.google.material.appbar
|
||||
|
||||
import android.widget.Toolbar
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
@@ -33,7 +32,7 @@ import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
@HikageViewDeclaration(AppBarLayout::class, AppBarLayout.LayoutParams::class)
|
||||
private object AppBarLayoutDeclaration
|
||||
|
||||
@HikageViewDeclaration(MaterialToolbar::class, Toolbar.LayoutParams::class)
|
||||
@HikageViewDeclaration(MaterialToolbar::class, final = true)
|
||||
private object MaterialToolbarDeclaration
|
||||
|
||||
@HikageViewDeclaration(CollapsingToolbarLayout::class, CollapsingToolbarLayout.LayoutParams::class)
|
||||
|
@@ -24,9 +24,8 @@
|
||||
|
||||
package com.highcapable.hikage.widget.google.material.bottomappbar
|
||||
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import com.google.android.material.bottomappbar.BottomAppBar
|
||||
import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(BottomAppBar::class, Toolbar.LayoutParams::class)
|
||||
@HikageViewDeclaration(BottomAppBar::class, final = true)
|
||||
private object BottomAppBarDeclaration
|
@@ -24,9 +24,8 @@
|
||||
|
||||
package com.highcapable.hikage.widget.google.material.bottomnavigation
|
||||
|
||||
import android.widget.FrameLayout
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(BottomNavigationView::class, FrameLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(BottomNavigationView::class, final = true)
|
||||
private object BottomNavigationViewDeclaration
|
@@ -24,9 +24,8 @@
|
||||
|
||||
package com.highcapable.hikage.widget.google.material.navigation
|
||||
|
||||
import android.widget.FrameLayout
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(NavigationView::class, FrameLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(NavigationView::class, final = true)
|
||||
private object NavigationViewDeclaration
|
@@ -24,9 +24,8 @@
|
||||
|
||||
package com.highcapable.hikage.widget.google.material.navigationrail
|
||||
|
||||
import android.widget.FrameLayout
|
||||
import com.google.android.material.navigationrail.NavigationRailView
|
||||
import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(NavigationRailView::class, FrameLayout.LayoutParams::class)
|
||||
@HikageViewDeclaration(NavigationRailView::class, final = true)
|
||||
private object NavigationRailViewDeclaration
|
@@ -25,12 +25,11 @@
|
||||
package com.highcapable.hikage.widget.google.material.search
|
||||
|
||||
import android.widget.FrameLayout
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import com.google.android.material.search.SearchBar
|
||||
import com.google.android.material.search.SearchView
|
||||
import com.highcapable.hikage.annotation.HikageViewDeclaration
|
||||
|
||||
@HikageViewDeclaration(SearchBar::class, Toolbar.LayoutParams::class)
|
||||
@HikageViewDeclaration(SearchBar::class, final = true)
|
||||
private object SearchBarDeclaration
|
||||
|
||||
@HikageViewDeclaration(SearchView::class, FrameLayout.LayoutParams::class)
|
||||
|
@@ -27,14 +27,6 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
viewBinding = true
|
||||
|
@@ -28,6 +28,7 @@ class DemoApp : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||
}
|
||||
}
|
@@ -52,9 +52,11 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView {
|
||||
var username = ""
|
||||
var password = ""
|
||||
|
||||
CoordinatorLayout(
|
||||
lparams = LayoutParams(matchParent = true)
|
||||
) {
|
||||
|
@@ -31,6 +31,7 @@ abstract class BaseActivity : AppViewsActivity() {
|
||||
@CallSuper
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
PanguTextFactory2.inject(this)
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@ pluginManagement {
|
||||
}
|
||||
plugins {
|
||||
id("com.highcapable.sweetdependency") version "1.0.4"
|
||||
id("com.highcapable.sweetproperty") version "1.0.5"
|
||||
id("com.highcapable.sweetproperty") version "1.0.8"
|
||||
}
|
||||
sweetProperty {
|
||||
rootProject { all { isEnable = false } }
|
||||
|
Reference in New Issue
Block a user