├── baseAF
├── .gitignore
├── consumer-rules.pro
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── foundation
│ │ └── app
│ │ └── arc
│ │ ├── app
│ │ └── BaseVMApplication.kt
│ │ ├── utils
│ │ ├── param
│ │ │ ├── BundleParams.kt
│ │ │ └── ParamsUtils.kt
│ │ ├── ext
│ │ │ ├── Ext.kt
│ │ │ ├── AFViewModelLazy.kt
│ │ │ ├── ActivityViewBindingDelegate.kt
│ │ │ ├── FragmentViewDelegate.kt
│ │ │ ├── LiveDataExt.kt
│ │ │ └── FragmentViewBindingDelegate.kt
│ │ └── FragmentSwitchHelper.kt
│ │ ├── activity
│ │ ├── BaseParamsActivity.kt
│ │ ├── BaseFragmentManagerActivity.kt
│ │ └── BaseVMVBActivity.kt
│ │ └── fragment
│ │ ├── BaseParamsFragment.kt
│ │ ├── BaseFragmentManagerFragment.kt
│ │ ├── BaseViewBindingFragment.kt
│ │ ├── BaseViewBindingFragmentInJava.kt
│ │ ├── InternalBasicFragment.kt
│ │ ├── BaseVMFragment.kt
│ │ └── BaseVisibilityFragment.kt
├── proguard-rules.pro
└── build.gradle.kts
├── simple
├── .gitignore
├── test.jks
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── girl.webp
│ │ │ ├── avatar.webp
│ │ │ ├── netoff.webp
│ │ │ ├── old_man.webp
│ │ │ ├── img_skeleton_screen.png
│ │ │ ├── sp_loading_content_bg.xml
│ │ │ ├── dw_loading.xml
│ │ │ └── ic_loading_svg.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── values
│ │ │ ├── styles.xml
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── themes.xml
│ │ ├── animator
│ │ │ ├── animator_shrug.xml
│ │ │ └── anim_scale.xml
│ │ ├── layout
│ │ │ ├── act_multi_fragment_visible_test.xml
│ │ │ ├── act_single_fragment_visible_test.xml
│ │ │ ├── vb_child.xml
│ │ │ ├── fail.xml
│ │ │ ├── act_vb_test.xml
│ │ │ ├── frag_user_info.xml
│ │ │ ├── frag_fragment_manager_visible.xml
│ │ │ ├── frag_view_pager_visible.xml
│ │ │ ├── frag_view_pager2_visible.xml
│ │ │ ├── act_vb.xml
│ │ │ ├── item_news.xml
│ │ │ ├── act_user_info.xml
│ │ │ ├── act_sticky.xml
│ │ │ ├── act_home_wanandroid.xml
│ │ │ └── activity_loading.xml
│ │ ├── values-v23
│ │ │ └── themes.xml
│ │ └── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ └── com
│ │ │ └── foundation
│ │ │ └── app
│ │ │ └── simple
│ │ │ ├── demo
│ │ │ ├── net
│ │ │ │ ├── api
│ │ │ │ │ ├── ApiUrl.kt
│ │ │ │ │ └── WanAndroidService.kt
│ │ │ │ ├── WanAndroidResException.kt
│ │ │ │ ├── RetrofitFactory.kt
│ │ │ │ └── WanAndroidNetStateHandler.kt
│ │ │ ├── home
│ │ │ │ ├── data
│ │ │ │ │ ├── NewsFeedInfo.kt
│ │ │ │ │ ├── BannerEntity.kt
│ │ │ │ │ └── PageInfo.kt
│ │ │ │ ├── HomeRepo.kt
│ │ │ │ ├── NewsAdapter.kt
│ │ │ │ ├── HomeVM.kt
│ │ │ │ └── HomeActivity.kt
│ │ │ └── base
│ │ │ │ ├── BaseApiResponse.kt
│ │ │ │ └── BaseWanAndroidVM.kt
│ │ │ ├── vm
│ │ │ ├── Test.java
│ │ │ ├── AppVM.kt
│ │ │ └── ReflectionTest.kt
│ │ │ ├── utils
│ │ │ └── Utils.kt
│ │ │ ├── ui
│ │ │ ├── fragment
│ │ │ │ ├── visible
│ │ │ │ │ ├── EmptyVisibleTestFragment.kt
│ │ │ │ │ ├── FragmentManagerVisibleTestFragment.kt
│ │ │ │ │ ├── ViewPager2VisibleTestFragment.kt
│ │ │ │ │ ├── ViewPagerVisibleTestFragment.kt
│ │ │ │ │ └── AbstractVisibleTestFragment.kt
│ │ │ │ └── SkillListFragment.kt
│ │ │ ├── SkillListActivity.kt
│ │ │ ├── data
│ │ │ │ ├── BundleProducer.java
│ │ │ │ └── UserData.kt
│ │ │ ├── adapter
│ │ │ │ ├── BaseFragmentPagerAdapter.kt
│ │ │ │ ├── BaseFragmentStatePagerAdapter.kt
│ │ │ │ └── ViewPager2FragmentAdapter.kt
│ │ │ ├── EmptyActivity.kt
│ │ │ ├── UserInfoFragment.java
│ │ │ ├── MultiFragmentVisibleTestActivity.kt
│ │ │ ├── StickyLiveDataActivity.kt
│ │ │ ├── ParcelableActivity.kt
│ │ │ ├── SerializableActivity.kt
│ │ │ └── UserInfoActivity.kt
│ │ │ ├── architecture
│ │ │ ├── BaseActivity.kt
│ │ │ ├── BaseFragment.kt
│ │ │ └── BaseFragmentWithLayoutId.kt
│ │ │ ├── CustomApplication.kt
│ │ │ ├── VBIncludeTestActivity.kt
│ │ │ ├── Ext.kt
│ │ │ ├── kcp
│ │ │ ├── PermissionCallback.kt
│ │ │ ├── PermissionExt.kt
│ │ │ ├── PermissionFragment.kt
│ │ │ └── KPermission.kt
│ │ │ └── backup
│ │ │ └── ActVbBindingCopy.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle.kts
├── settings.gradle.kts
├── images
└── uml.jpg
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── encodings.xml
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── compiler.xml
├── kotlinc.xml
├── kotlinScripting.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── jarRepositories.xml
└── misc.xml
├── common.gradle
├── gradle.properties
├── LICENSE
├── .gitignore
├── ActVbBindingCopy.java
├── gradlew.bat
├── README.md
└── gradlew
/baseAF/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/simple/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/baseAF/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | include(":baseAF")
2 | include(":simple")
3 |
--------------------------------------------------------------------------------
/images/uml.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/images/uml.jpg
--------------------------------------------------------------------------------
/simple/test.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/test.jks
--------------------------------------------------------------------------------
/baseAF/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/simple/src/main/res/drawable/girl.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/drawable/girl.webp
--------------------------------------------------------------------------------
/simple/src/main/res/drawable/avatar.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/drawable/avatar.webp
--------------------------------------------------------------------------------
/simple/src/main/res/drawable/netoff.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/drawable/netoff.webp
--------------------------------------------------------------------------------
/simple/src/main/res/drawable/old_man.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/drawable/old_man.webp
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simple/src/main/res/drawable/img_skeleton_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/drawable/img_skeleton_screen.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/simple/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/AndroidBaseArchitecture/HEAD/simple/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/simple/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/simple/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | baseAf
3 | 海词词典,最权威的学习词典,专业出版无版权的英文,最权威的学习词典,专业出版无版权的英文,无版权翻译最权威的学习词典,专业出版无版权的英文,,无版权英语怎么说等详细讲解。海词词典:学习变容易,记忆很深刻。
4 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/net/api/ApiUrl.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.net.api
2 |
3 | /**
4 | * create by zhusw on 5/20/21 15:37
5 | */
6 | object ApiUrl {
7 | const val wanandroid_base_url = "https://www.wanandroid.com"
8 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/net/WanAndroidResException.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.net
2 |
3 | /**
4 | * create by zhusw on 5/24/21 20:20
5 | */
6 | class WanAndroidResException(val code: Int, val msg: String) : Throwable() {
7 |
8 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Feb 08 14:08:48 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
7 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/vm/Test.java:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.vm;
2 |
3 | /**
4 | * create by zhusw on 5/21/21 15:04
5 | */
6 | public class Test {
7 | public static int work() {
8 | int i = 1;
9 | return i + 1;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/simple/src/main/res/drawable/sp_loading_content_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/vm/AppVM.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.vm
2 | import androidx.lifecycle.MutableLiveData
3 | import androidx.lifecycle.ViewModel
4 |
5 | /**
6 | *-模拟粘性事件
7 | *create by zhusw on 5/18/21 11:02
8 | */
9 | class AppVM : ViewModel() {
10 | val data: MutableLiveData = MutableLiveData()
11 |
12 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/home/data/NewsFeedInfo.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.home.data
2 |
3 | /**
4 | * create by zhusw on 5/27/21 15:11
5 | */
6 | data class NewsFeedInfo(
7 | val title: String = "",
8 | val author: String = "",
9 | val shareUser: String = "",
10 | val niceShareDate: String = "",
11 | val link: String = "",
12 | )
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/home/data/BannerEntity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.home.data
2 |
3 | data class BannerEntity(
4 | val desc: String = "",
5 | val id: Int = 0,
6 | val imagePath: String? = null,
7 | val isVisible: Int = 0,
8 | val order: Int = 0,
9 | val title: String = "",
10 | val type: Int = 0,
11 | val url: String? = null
12 | )
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.utils
2 |
3 | import android.app.Application
4 |
5 | /**
6 | * create by zhusw on 5/24/21 17:20
7 | */
8 | object Utils {
9 |
10 | private var _application: Application? = null
11 | val app: Application get() = _application!!
12 | fun init(app: Application) {
13 | _application = app
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/home/HomeRepo.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.home
2 |
3 | import com.foundation.app.simple.demo.net.api.WanAndroidService
4 | import com.foundation.service.net.NetManager
5 | import com.foundation.service.net.getApiService
6 |
7 | /**
8 | * create by zhusw on 5/24/21 09:58
9 | */
10 | class HomeRepo {
11 | val homeApi = NetManager.getApiService()
12 | }
--------------------------------------------------------------------------------
/.idea/kotlinScripting.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 2147483647
6 | true
7 |
8 |
9 |
--------------------------------------------------------------------------------
/simple/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/simple/src/main/res/animator/animator_shrug.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/act_multi_fragment_visible_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/act_single_fragment_visible_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/fragment/visible/EmptyVisibleTestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.fragment.visible
2 |
3 | import android.view.View
4 | import android.widget.TextView
5 |
6 | class EmptyVisibleTestFragment :
7 | AbstractVisibleTestFragment(0) {
8 | override fun onSwitchFragment(index: Int) {
9 | }
10 |
11 | override fun getBtnOpenNewPage(): View? = null
12 | override fun getTvFragmentTitle(): TextView? = null
13 | override fun getBtnClickSwitch(): View? = null
14 | }
15 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/home/data/PageInfo.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.home.data
2 |
3 | /**
4 | * create by zhusw on 5/27/21 15:15
5 | */
6 | data class PageInfo(
7 | val datas: List,
8 | val curPage: Int = 0,
9 | val offset: Int = 0,
10 | val over: Boolean = false,
11 | val pageCount: Int = 0,
12 | val size: Int = 0,
13 | val total: Int = 0,
14 | //test 字段
15 | val testString: String,
16 | val testInt: Int,
17 | val testBoolean: Boolean,
18 | val testLong: Long
19 | )
--------------------------------------------------------------------------------
/simple/src/main/res/layout/vb_child.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/base/BaseApiResponse.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.base
2 |
3 | /**
4 | * create by zhusw on 5/20/21 14:20
5 | */
6 | class BaseApiResponse {
7 | object Code {
8 | const val CODE_NORMAL = -123456
9 | const val CODE_SUCCESS = 0
10 | }
11 | /* {
12 | "data": ...,
13 | "errorCode": 0,
14 | "errorMsg": ""
15 | }*/
16 | val data: T? = null
17 | val errorCode: Int = Code.CODE_NORMAL
18 | val errorMsg: String = ""
19 |
20 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/SkillListActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui
2 |
3 | import android.os.Bundle
4 | import com.foundation.app.simple.architecture.BaseActivity
5 | import com.foundation.app.simple.ui.fragment.SkillListFragment
6 |
7 | /**
8 | *create by zhusw on 5/11/21 14:42
9 | */
10 | class SkillListActivity : BaseActivity() {
11 |
12 | override fun init(savedInstanceState: Bundle?) {
13 | switchFragment(SkillListFragment(), android.R.id.content)
14 | }
15 |
16 | override fun bindData() {
17 |
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/architecture/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.architecture
2 |
3 | import android.os.Bundle
4 | import com.foundation.app.arc.activity.BaseFragmentManagerActivity
5 |
6 | /**
7 | *create by zhusw on 5/18/21 18:38
8 | */
9 | abstract class BaseActivity : BaseFragmentManagerActivity() {
10 | override fun beforeSuperOnCreate(savedInstanceState: Bundle?) {
11 |
12 | }
13 |
14 | override fun afterSuperOnCreate(savedInstanceState: Bundle?) {
15 |
16 | }
17 |
18 | override fun initViewModel() {
19 |
20 | }
21 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/architecture/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.architecture
2 |
3 | import android.os.Bundle
4 | import androidx.viewbinding.ViewBinding
5 | import com.foundation.app.arc.fragment.BaseViewBindingFragmentInJava
6 |
7 | /**
8 | *create by zhusw on 5/18/21 18:38
9 | */
10 | open class BaseFragment : BaseViewBindingFragmentInJava() {
11 | override fun initViewModel() {
12 |
13 | }
14 |
15 | override fun init(savedInstanceState: Bundle?) {
16 |
17 | }
18 |
19 | override fun bindData() {
20 |
21 | }
22 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/architecture/BaseFragmentWithLayoutId.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.architecture
2 |
3 | import android.os.Bundle
4 | import androidx.annotation.LayoutRes
5 | import com.foundation.app.arc.fragment.BaseViewBindingFragment
6 |
7 | /**
8 | *create by zhusw on 5/18/21 18:38
9 | */
10 | open class BaseFragmentWithLayoutId(@LayoutRes id: Int) : BaseViewBindingFragment(id) {
11 | override fun initViewModel() {
12 |
13 | }
14 |
15 | override fun init(savedInstanceState: Bundle?) {
16 |
17 | }
18 |
19 | override fun bindData() {
20 | }
21 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/CustomApplication.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple
2 |
3 | import com.foundation.app.arc.app.BaseVMApplication
4 | import com.foundation.app.simple.demo.net.RetrofitFactory
5 | import com.foundation.app.simple.utils.Utils
6 | import com.foundation.service.net.NetManager
7 |
8 | /**
9 | *create by zhusw on 5/18/21 11:26
10 | */
11 | class CustomApplication : BaseVMApplication() {
12 |
13 | override fun onCreate() {
14 | super.onCreate()
15 | Utils.init(this)
16 | NetManager.init(RetrofitFactory.create(), this, BuildConfig.DEBUG)
17 |
18 | }
19 | }
--------------------------------------------------------------------------------
/simple/src/main/res/animator/anim_scale.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
14 |
15 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/app/BaseVMApplication.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.app
2 |
3 | import android.app.Application
4 | import androidx.lifecycle.ViewModelStore
5 | import androidx.lifecycle.ViewModelStoreOwner
6 | import com.foundation.app.arc.utils.ext.lazyAtomic
7 |
8 | /**
9 | *- 你并不一定要继承[BaseVMApplication] ,仅需要实现[ViewModelStoreOwner]
10 | *create by zhusw on 5/17/21 14:19
11 | */
12 | open class BaseVMApplication : Application(), ViewModelStoreOwner {
13 | private val vmStore: ViewModelStore by lazyAtomic {
14 | ViewModelStore()
15 | }
16 |
17 | override fun getViewModelStore(): ViewModelStore = vmStore
18 | }
--------------------------------------------------------------------------------
/simple/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/data/BundleProducer.java:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.data;
2 | import android.os.Bundle;
3 |
4 | /**
5 | * @Desc: -
6 | * - 测试java 基本参数类型
7 | * create by zhusw on 5/18/21 10:39
8 | */
9 | public class BundleProducer {
10 | public static Bundle create() {
11 | Bundle bundle = new Bundle();
12 | bundle.putInt("userId", 100001);
13 | bundle.putString("userName", "张三");
14 | UserAddress address = new UserAddress("inJava", 1);
15 | UserDesc desc = new UserDesc("inJava", 1);
16 | bundle.putParcelable("address", address);
17 | bundle.putParcelable("desc", desc);
18 | return bundle;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/simple/src/main/res/values-v23/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/param/BundleParams.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.utils.param
2 |
3 | /**
4 | * activity,fragment 自动初始化参数注解
5 | *
6 | * 支持类型:
7 | * String
8 | * 全部基础数据类型(兼容kotlin与java)
9 | * Parcelable
10 | * 不支持类型:Serializable
11 | * create by zhusw on 5/17/21 16:26
12 | */
13 | @Target(AnnotationTarget.FIELD)
14 | @Retention(AnnotationRetention.RUNTIME)
15 | annotation class BundleParams(val value: String)
16 |
17 | /**
18 | * 强制使用Serializable
19 | */
20 | @Target(AnnotationTarget.FIELD)
21 | @Retention(AnnotationRetention.RUNTIME)
22 | annotation class BundleParamsUseSerializable()
23 |
24 | /*在java中需要显式的赋值 key 字段
25 | annotation class BundleParams(val key: String = "") {
26 |
27 | }*/
28 |
--------------------------------------------------------------------------------
/simple/src/main/res/drawable/dw_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
13 |
16 |
19 |
--------------------------------------------------------------------------------
/common.gradle:
--------------------------------------------------------------------------------
1 | import com.buildsrc.kts.AndroidConfig
2 | import com.buildsrc.kts.Publish
3 |
4 | apply plugin: 'com.android.library'
5 |
6 | android {
7 | compileSdkVersion AndroidConfig.compileSdkVersion
8 | defaultConfig {
9 | minSdkVersion AndroidConfig.minSdkVersion
10 | targetSdkVersion AndroidConfig.targetSdkVersion
11 | versionCode = Publish.Version.versionCode
12 | versionName = Publish.Version.versionName
13 | }
14 |
15 |
16 | compileOptions {
17 | sourceCompatibility = AndroidConfig.Language.sourceCompatibility
18 | targetCompatibility = AndroidConfig.Language.targetCompatibility
19 | }
20 | kotlinOptions {
21 | jvmTarget = AndroidConfig.Language.jvmTarget
22 | }
23 | }
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/ext/Ext.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.utils.ext
2 |
3 | import androidx.annotation.MainThread
4 | import com.foundation.app.arc.BuildConfig
5 |
6 | /**
7 | *create by zhusw on 5/11/21 14:24
8 | */
9 | private const val TAG = "baseAF"
10 | internal fun String.log(secTag: String = "") {
11 | if (BuildConfig.DEBUG) {
12 | println("$TAG $secTag $this")
13 | }
14 | }
15 |
16 | internal object UNINIT_VALUE
17 |
18 | /**
19 | * 无锁开销的单例加载
20 | * 多线程场景下保证返回首次创建的实例
21 | */
22 | fun lazyAtomic(initializer: () -> T) = lazy(LazyThreadSafetyMode.PUBLICATION, initializer)
23 |
24 | @MainThread
25 | fun lazyOnUI(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer)
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/VBIncludeTestActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple
2 |
3 | import android.os.Bundle
4 | import com.foundation.app.simple.architecture.BaseActivity
5 | import com.foundation.app.simple.databinding.ActVbTestBinding
6 |
7 | /**
8 | * create by zhusw on 6/10/21 17:47
9 | */
10 | class VBIncludeTestActivity : BaseActivity() {
11 | val vb by lazyAndSetRoot()
12 | override fun init(savedInstanceState: Bundle?) {
13 | vb.include1.tvChild.setOnClickListener {
14 | "click:vb.include1.tvChild".toast()
15 | }
16 | vb.tvParent.setOnClickListener {
17 | "click:vb.tvParent".toast()
18 | }
19 | }
20 |
21 | override fun bindData() {
22 |
23 | }
24 | }
--------------------------------------------------------------------------------
/simple/src/main/res/layout/fail.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/baseAF/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle-backup.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/simple/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle-backup.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/home/NewsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.home
2 |
3 | import com.chad.library.adapter.base.BaseQuickAdapter
4 | import com.chad.library.adapter.base.viewholder.BaseViewHolder
5 | import com.foundation.app.simple.R
6 | import com.foundation.app.simple.databinding.ItemNewsBinding
7 | import com.foundation.app.simple.demo.home.data.NewsFeedInfo
8 |
9 | /**
10 | * create by zhusw on 5/27/21 15:34
11 | */
12 | class NewsAdapter : BaseQuickAdapter(R.layout.item_news) {
13 | override fun convert(holder: BaseViewHolder, item: NewsFeedInfo) {
14 | val binding = ItemNewsBinding.bind(holder.itemView)
15 | binding.tvAuthor.text = if (item.author.isNotEmpty()) item.author else item.shareUser
16 | binding.tvTitle.text = item.title
17 | }
18 | }
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/ext/AFViewModelLazy.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.utils.ext
2 |
3 | import androidx.annotation.MainThread
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.ViewModelProvider
6 | import kotlin.reflect.KClass
7 |
8 | /**
9 | * ViewModel 相关 拓展
10 | *create by zhusw on 5/17/21 16:02
11 | */
12 |
13 | @MainThread
14 | class AFViewModelLazy(
15 | private val viewModelClass: KClass,
16 | private val viewModelProvider: () -> ViewModelProvider
17 | ) : Lazy {
18 | private var cached: VM? = null
19 | override val value: VM
20 | get() {
21 | return cached ?: viewModelProvider.invoke().get(viewModelClass.java).also {
22 | cached = it
23 | }
24 | }
25 |
26 | override fun isInitialized(): Boolean = cached != null
27 | }
28 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/adapter/BaseFragmentPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.adapter
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentPagerAdapter
6 |
7 | /**
8 | * create by zhusw on 5/19/21 15:19
9 | */
10 | class BaseFragmentPagerAdapter(
11 | fm: FragmentManager,
12 | fragList: List,
13 | behavior: Int = BEHAVIOR_SET_USER_VISIBLE_HINT
14 | ) : FragmentPagerAdapter(fm, behavior) {
15 | var fragList: List = fragList
16 | set(value) {
17 | field = value
18 | notifyDataSetChanged()
19 | }
20 |
21 | override fun getItem(position: Int): Fragment {
22 | return fragList[position]
23 | }
24 |
25 | override fun getCount(): Int {
26 | return fragList.size
27 | }
28 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/net/api/WanAndroidService.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.net.api
2 |
3 | import com.foundation.app.simple.demo.base.BaseApiResponse
4 | import com.foundation.app.simple.demo.home.data.BannerEntity
5 | import com.foundation.app.simple.demo.home.data.NewsFeedInfo
6 | import com.foundation.app.simple.demo.home.data.PageInfo
7 | import retrofit2.Response
8 | import retrofit2.http.GET
9 | import retrofit2.http.Path
10 |
11 | /**
12 | * create by zhusw on 5/20/21 14:34
13 | */
14 | interface WanAndroidService {
15 |
16 | @GET("banner/json")
17 | suspend fun getBanner(): Response>>
18 |
19 | /**
20 | * @param pageNo 0 开始
21 | */
22 | @GET("article/list/{page}/json")
23 | suspend fun getNews(@Path("page") pageNo: Int)
24 | : Response>>
25 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/EmptyActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui
2 |
3 | import android.os.Bundle
4 | import android.view.Gravity
5 | import android.view.ViewGroup
6 | import android.widget.TextView
7 | import com.foundation.app.simple.architecture.BaseActivity
8 |
9 | /**
10 | * create by zhusw on 5/19/21 15:59
11 | */
12 | class EmptyActivity : BaseActivity() {
13 | override fun afterSuperOnCreate(savedInstanceState: Bundle?) {
14 | super.afterSuperOnCreate(savedInstanceState)
15 | setContentView(TextView(this).apply {
16 | textSize = 90F
17 | text = "空白页面"
18 | gravity = Gravity.CENTER
19 | layoutParams = ViewGroup.LayoutParams(-1, -1)
20 | })
21 | }
22 |
23 | override fun init(savedInstanceState: Bundle?) {
24 |
25 | }
26 |
27 | override fun bindData() {
28 |
29 | }
30 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/fragment/visible/FragmentManagerVisibleTestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.fragment.visible
2 |
3 | import com.foundation.app.simple.R
4 | import com.foundation.app.simple.databinding.FragFragmentManagerVisibleBinding
5 |
6 | class FragmentManagerVisibleTestFragment :
7 | AbstractVisibleTestFragment(R.layout.frag_fragment_manager_visible) {
8 |
9 | private val vb by lazyVB()
10 |
11 | override fun bindData() {
12 | super.bindData()
13 |
14 | onSwitchFragment(0)
15 | }
16 |
17 | override fun onSwitchFragment(index: Int) {
18 | switchFragment(frags[index], vb.flFrag.id)
19 | }
20 |
21 | override fun getBtnOpenNewPage() = vb.btnOpenNewPage
22 | override fun getTvFragmentTitle() = vb.tvFragmentTitle
23 | override fun getBtnClickSwitch() = vb.btnClickSwitch
24 | }
25 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/act_vb_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
20 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/Ext.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.widget.Toast
6 | import androidx.fragment.app.Fragment
7 | import com.foundation.app.arc.BuildConfig
8 | import com.foundation.app.simple.utils.Utils
9 |
10 | /**
11 |
12 | *-
13 | *-
14 | *create by zhusw on 5/19/21 14:04
15 | */
16 | private const val TAG = "simple-baseAF"
17 | internal fun String.log(secTag: String = "") {
18 | if (BuildConfig.DEBUG) {
19 | println("$TAG $secTag $this")
20 | }
21 | }
22 |
23 | fun Activity.jump(clz: Class) {
24 | val intent = Intent(this, clz)
25 | startActivity(intent)
26 | }
27 |
28 | fun Fragment.jump(clz: Class) {
29 | requireActivity().jump(clz)
30 | }
31 |
32 | fun String.toast() {
33 | Toast.makeText(Utils.app, this, Toast.LENGTH_LONG).show()
34 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/adapter/BaseFragmentStatePagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.adapter
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentStatePagerAdapter
6 |
7 | /**
8 | * create by zhusw on 5/19/21 15:19
9 | */
10 | class BaseFragmentStatePagerAdapter(
11 | fm: FragmentManager,
12 | fragList: List,
13 | behavior: Int = BEHAVIOR_SET_USER_VISIBLE_HINT
14 | ) :
15 | FragmentStatePagerAdapter(fm, behavior) {
16 | var fragList: List = fragList
17 | set(value) {
18 | field = value
19 | notifyDataSetChanged()
20 | }
21 |
22 | override fun getItem(position: Int): Fragment {
23 | return fragList[position]
24 | }
25 |
26 | override fun getCount(): Int {
27 | return fragList.size
28 | }
29 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/net/RetrofitFactory.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.net
2 |
3 | import com.foundation.app.simple.demo.net.api.ApiUrl
4 | import com.foundation.service.net.utils.addDynamicDomainSkill
5 | import okhttp3.OkHttpClient
6 | import retrofit2.Retrofit
7 | import retrofit2.converter.gson.GsonConverterFactory
8 |
9 | object RetrofitFactory {
10 |
11 | private val okHttpClientBuilder: OkHttpClient.Builder
12 | get() {
13 | return OkHttpClient.Builder()
14 | }
15 |
16 | fun create(): Retrofit {
17 | okHttpClientBuilder.addDynamicDomainSkill()
18 | val okHttpClient = okHttpClientBuilder.build()
19 | return Retrofit.Builder()
20 | .client(okHttpClient)
21 | .addConverterFactory(GsonConverterFactory.create())
22 | .baseUrl(ApiUrl.wanandroid_base_url)
23 | .build()
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/kcp/PermissionCallback.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.kcp
2 |
3 | /**
4 |
5 | *-
6 | *-
7 | *create by zhusw on 2020/11/20 11:07
8 | */
9 | class PermissionCallback(
10 | val eachResult: (pre: Permission) -> Unit,
11 | val allGranted: (granteds: List) -> Unit = {},
12 | /**
13 | * granteds :同意的授权
14 | * rejects :被拒绝的授权
15 | */
16 | val leastOneReject: (granteds: List, rejects: List) -> Unit = { _: List, _: List -> },
17 | val onCancel: () -> Unit,
18 | val onError: (e: Throwable?) -> Unit,
19 | val onComplete: () -> Unit = {}
20 | ) : KCJob {
21 | var canceled: Boolean = false
22 | private set
23 |
24 | override fun cancel() {
25 | if (!canceled) {
26 | canceled = true
27 | onCancel()
28 | }
29 | }
30 | }
31 |
32 | interface KCJob {
33 | fun cancel()
34 | }
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/activity/BaseParamsActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.activity
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.foundation.app.arc.utils.param.ParamsUtils
7 |
8 | /**
9 | * create by zhusw on 5/17/21 15:12
10 | */
11 | abstract class BaseParamsActivity : AppCompatActivity() {
12 |
13 | override fun onNewIntent(intent: Intent?) {
14 | super.onNewIntent(intent)
15 | //此intent中可能存在逻辑分叉点,暂不提供参数自动覆盖,否则容易导致隐式逻辑
16 | //如有需要自行重写调用:ParamsUtils.initWithActivity(this,intent)
17 | }
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | if (openAutoBindParams()) {
22 | ParamsUtils.initWithActivity(this, intent)
23 | }
24 | }
25 |
26 | /**
27 | * 是否开启参数自动绑定
28 | */
29 | protected open fun openAutoBindParams(): Boolean = true
30 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | android.useAndroidX=true
15 | # Automatically convert third-party libraries to use AndroidX
16 | android.enableJetifier=true
17 | # Kotlin code style for this project: "official" or "obsolete":
18 | kotlin.code.style=official
19 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/fragment/BaseParamsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.fragment
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.foundation.app.arc.utils.param.ParamsUtils
7 |
8 | /**
9 | * 参数自动绑定
10 | *create by zhusw on 5/17/21 19:10
11 | */
12 | abstract class BaseParamsFragment : BaseVisibilityFragment() {
13 |
14 | protected lateinit var hostActivity: AppCompatActivity
15 | private set
16 |
17 | override fun onAttach(context: Context) {
18 | super.onAttach(context)
19 | hostActivity = context as AppCompatActivity
20 | }
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 | if (openAutoBindParams()) {
25 | ParamsUtils.initWithFragment(this)
26 | }
27 | }
28 |
29 | /**
30 | * 是否开启参数自动绑定
31 | */
32 | protected open fun openAutoBindParams(): Boolean = true
33 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/adapter/ViewPager2FragmentAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.adapter
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import androidx.fragment.app.Fragment
5 | import androidx.fragment.app.FragmentManager
6 | import androidx.lifecycle.Lifecycle
7 | import androidx.viewpager2.adapter.FragmentStateAdapter
8 |
9 | /**
10 | * create by zhusw on 7/12/21 18:05
11 | */
12 | class ViewPager2FragmentAdapter(fm: FragmentManager, lifecycle: Lifecycle) :
13 | FragmentStateAdapter(fm, lifecycle) {
14 | constructor(frag: Fragment) : this(frag.childFragmentManager, frag.lifecycle)
15 | constructor(act: AppCompatActivity) : this(act.supportFragmentManager, act.lifecycle)
16 |
17 | var list = listOf()
18 | set(value) {
19 | field = value
20 | notifyDataSetChanged()
21 | }
22 |
23 | override fun getItemCount(): Int = list.size
24 |
25 | override fun createFragment(position: Int): Fragment = list[position]
26 | }
--------------------------------------------------------------------------------
/simple/src/main/res/layout/frag_user_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
24 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/UserInfoFragment.java:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.foundation.app.arc.utils.param.BundleParams;
6 | import com.foundation.app.simple.architecture.BaseFragment;
7 | import com.foundation.app.simple.databinding.FragUserInfoBinding;
8 |
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | /**
12 | * @Desc: -
13 | * 这是java 示例
14 | * create by zhusw on 5/18/21 09:40
15 | */
16 | public class UserInfoFragment extends BaseFragment {
17 |
18 | @BundleParams("userId")
19 | private int userId = -1;
20 |
21 | @BundleParams("userName")
22 | private String userName = "没自动赋值";
23 |
24 | @Override
25 | public void initViewModel() {
26 |
27 | }
28 |
29 | @Override
30 | public void init(@Nullable Bundle savedInstanceState) {
31 |
32 | }
33 |
34 | @Override
35 | protected void bindData() {
36 | jViewBinding.auiTvUserId.setText("用户ID:" + String.valueOf(userId));
37 | jViewBinding.auiTvUserName.setText("用户名:" + userName);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/MultiFragmentVisibleTestActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui
2 |
3 | import android.os.Bundle
4 | import com.foundation.app.simple.architecture.BaseActivity
5 | import com.foundation.app.simple.databinding.ActMultiFragmentVisibleTestBinding
6 | import com.foundation.app.simple.ui.fragment.visible.FragmentManagerVisibleTestFragment
7 |
8 | /**
9 | * 测试 单个 fragment 可见性回调
10 | * create by zhusw on 5/19/21 13:24
11 | */
12 | class MultiFragmentVisibleTestActivity : BaseActivity() {
13 | private val vb by lazyAndSetRoot()
14 | override fun init(savedInstanceState: Bundle?) {
15 | switchFragment(
16 | FragmentManagerVisibleTestFragment().apply {
17 | arguments = Bundle().apply {
18 | putInt("deep", 0)
19 | putString("deepDesc", "")
20 | putString("fragType", "主页")
21 | }
22 | },
23 | vb.flFrag.id
24 | )
25 | }
26 |
27 | override fun bindData() {
28 |
29 | }
30 |
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Western-parotia
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/StickyLiveDataActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui
2 |
3 | import android.os.Bundle
4 | import com.foundation.app.arc.utils.ext.observerStickyLess
5 | import com.foundation.app.simple.architecture.BaseActivity
6 | import com.foundation.app.simple.databinding.ActStickyBinding
7 | import com.foundation.app.simple.vm.AppVM
8 |
9 | /**
10 | * 粘性测试
11 | * create by zhusw on 5/21/21 10:53
12 | */
13 | class StickyLiveDataActivity : BaseActivity() {
14 | val vm by lazyGlobalVM()
15 | val binding by lazyAndSetRoot()
16 | private var stickyCount = 0
17 | override fun init(savedInstanceState: Bundle?) {
18 | binding.btnSticky.setOnClickListener {
19 | vm.data.value = stickyCount + 1
20 | }
21 |
22 | binding.btnStickyLess.setOnClickListener {
23 | bindData()//重新订阅,测试是否被重复订阅
24 | }
25 |
26 | }
27 |
28 | override fun bindData() {
29 | vm.data.observe(this) {
30 | stickyCount = it
31 | binding.tv.text = "$stickyCount"
32 | }
33 | vm.data.observerStickyLess(this) {
34 | binding.tv2.text = "$it"
35 | }
36 | }
37 |
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/base/BaseWanAndroidVM.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.base
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import com.foundation.app.simple.demo.net.WanAndroidResException
6 | import com.foundation.service.net.NetLoadingEvent
7 | import com.foundation.service.net.NetViewModel
8 | import retrofit2.Response
9 |
10 | /**
11 | * create by zhusw on 5/27/21 17:06
12 | */
13 | open class BaseWanAndroidVM : NetViewModel() {
14 |
15 | protected val _loadEventLiveData: MutableLiveData = MutableLiveData()
16 | val loadEventLiveData: LiveData = _loadEventLiveData
17 |
18 | /**
19 | * 业务层处理
20 | */
21 | protected suspend fun withBusiness(block: suspend () -> Response>): F {
22 | val baseRes: BaseApiResponse = withResponse(block)!!
23 | //过滤业务状态码
24 | return when (baseRes.errorCode) {
25 | 0 -> {
26 | baseRes.data ?: throw WanAndroidResException(-1, "无数据")
27 | }
28 | else -> {
29 | throw WanAndroidResException(baseRes.errorCode, baseRes.errorMsg)
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/ext/ActivityViewBindingDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.utils.ext
2 |
3 | import androidx.activity.ComponentActivity
4 | import androidx.lifecycle.Lifecycle
5 | import androidx.lifecycle.LifecycleObserver
6 | import androidx.lifecycle.OnLifecycleEvent
7 | import androidx.viewbinding.ViewBinding
8 |
9 |
10 | class ActivityViewBindingDelegate(
11 | private val act: ComponentActivity,
12 | private val initializer: () -> T
13 | ) : Lazy {
14 |
15 | private var _value: Any? = UNINIT_VALUE
16 |
17 | private val obs = object : LifecycleObserver {
18 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
19 | fun setContentView() {
20 | // 触发 value 的 get 方法
21 | value.root
22 | }
23 | }
24 |
25 | init {
26 | act.lifecycle.addObserver(obs)
27 | }
28 |
29 | override val value: T
30 | get() {
31 | if (_value == UNINIT_VALUE) {
32 | act.lifecycle.removeObserver(obs)
33 | val vb = initializer()
34 | act.setContentView(vb.root)
35 | _value = vb
36 | }
37 | return _value as T
38 | }
39 |
40 | override fun isInitialized(): Boolean = _value !== UNINIT_VALUE
41 |
42 |
43 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/fragment/visible/ViewPager2VisibleTestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.fragment.visible
2 |
3 | import androidx.viewpager2.widget.ViewPager2
4 | import com.foundation.app.simple.R
5 | import com.foundation.app.simple.databinding.FragViewPager2VisibleBinding
6 | import com.foundation.app.simple.ui.adapter.ViewPager2FragmentAdapter
7 |
8 | class ViewPager2VisibleTestFragment :
9 | AbstractVisibleTestFragment(R.layout.frag_view_pager2_visible) {
10 |
11 | private val vb by lazyVB()
12 |
13 | override fun bindData() {
14 | super.bindData()
15 |
16 | vb.vpPager2.offscreenPageLimit = 1
17 | vb.vpPager2.adapter = ViewPager2FragmentAdapter(this).apply { list = frags }
18 | vb.vpPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
19 | override fun onPageSelected(position: Int) {
20 | currentIndex = position
21 | }
22 | })
23 | }
24 |
25 | override fun onSwitchFragment(index: Int) {
26 | vb.vpPager2.currentItem = index
27 | }
28 |
29 | override fun getBtnOpenNewPage() = vb.btnOpenNewPage
30 | override fun getTvFragmentTitle() = vb.tvFragmentTitle
31 | override fun getBtnClickSwitch() = vb.btnClickSwitch
32 | }
33 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/ParcelableActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import com.foundation.app.arc.utils.param.BundleParams
7 | import com.foundation.app.simple.architecture.BaseActivity
8 | import com.foundation.app.simple.databinding.ActUserInfoBinding
9 | import com.foundation.app.simple.ui.data.UserDesc
10 |
11 |
12 | /**
13 | *create by zhusw on 5/17/21 17:11
14 | * scheme://xxxd/?k-v
15 | */
16 | class ParcelableActivity : BaseActivity() {
17 |
18 | companion object {
19 | fun enter(context: Context, userDesc: UserDesc) {
20 | val intent = Intent(context, ParcelableActivity::class.java)
21 | intent.putExtra("userDesc", userDesc)
22 | context.startActivity(intent)
23 | }
24 | }
25 |
26 | @BundleParams("userDesc")
27 | private val userDesc: UserDesc = UserDesc()
28 |
29 |
30 | private val vbBinding by lazyAndSetRoot()
31 |
32 | override fun init(savedInstanceState: Bundle?) {
33 |
34 | }
35 |
36 | override fun bindData() {
37 |
38 | vbBinding.auiTvUserId.text = "introduce: ${userDesc.introduce}"
39 | vbBinding.auiTvUserName.text = "height: ${userDesc.height}"
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/fragment/BaseFragmentManagerFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.fragment
2 |
3 | import androidx.annotation.IdRes
4 | import androidx.fragment.app.Fragment
5 | import com.foundation.app.arc.utils.FragmentSwitchHelper
6 |
7 | /**
8 | * create by zhusw on 5/28/21 13:40
9 | */
10 | abstract class BaseFragmentManagerFragment : BaseVMFragment() {
11 | private val switchHelper by lazyWithFragment { FragmentSwitchHelper(childFragmentManager) }
12 |
13 | protected fun switchFragment(tag: String, @IdRes frameLayoutId: Int = 0): Fragment? {
14 | return switchHelper.switchFragment(tag, frameLayoutId)
15 | }
16 |
17 | /**
18 | * 切换fragment,如果没有缓存则新建
19 | * 默认使用currentFragment.hasCode 作为tag
20 | */
21 | protected fun switchFragment(
22 | currentFragment: Fragment,
23 | @IdRes frameLayoutId: Int = 0, tag: String? = null
24 | ) {
25 | switchHelper.switchFragment(currentFragment, frameLayoutId, tag)
26 | }
27 |
28 | /**
29 | * 隐藏一个Fragment
30 | */
31 | protected fun hideFragment(clazz: Class) {
32 | switchHelper.hideFragment(clazz)
33 | }
34 |
35 | /**
36 | * 移除一个Fragment
37 | */
38 | protected fun removeFragment(clazz: Class) {
39 | switchHelper.removeFragment(clazz)
40 | }
41 | }
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/fragment/BaseViewBindingFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.fragment
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.annotation.LayoutRes
8 | import androidx.viewbinding.ViewBinding
9 | import com.foundation.app.arc.utils.ext.FragmentViewBindingDelegate
10 | import com.foundation.app.arc.utils.ext.ViewBindingLifecycleListener
11 |
12 | /**
13 | * 约束在子类中使用[lazyVB] 时 必须设置 layoutId
14 | * create by zhusw on 5/18/21 10:46
15 | */
16 | abstract class BaseViewBindingFragment(@LayoutRes private val layoutId: Int) :
17 | BaseFragmentManagerFragment(), ViewBindingLifecycleListener {
18 |
19 | /**
20 | * 懒加载赋值
21 | * viewBinding 销毁前调用 [onViewBindingDestroy]
22 | */
23 | protected inline fun lazyVB() = FragmentViewBindingDelegate {
24 | VB::class.java.getMethod("bind", View::class.java)
25 | .invoke(null, requireView()) as VB
26 | }
27 |
28 |
29 | override fun onCreateView(
30 | inflater: LayoutInflater,
31 | container: ViewGroup?,
32 | savedInstanceState: Bundle?
33 | ): View? =
34 | if (layoutId != 0) {
35 | inflater.inflate(layoutId, container, false)
36 | } else {
37 | null
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/activity/BaseFragmentManagerActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.activity
2 |
3 | import androidx.annotation.IdRes
4 | import androidx.annotation.NonNull
5 | import androidx.fragment.app.Fragment
6 | import com.foundation.app.arc.utils.FragmentSwitchHelper
7 | import com.foundation.app.arc.utils.ext.lazyOnUI
8 |
9 | /**
10 | * 切换Fragment的Activity
11 | * create by zhusw on 5/28/21 11:06
12 | */
13 | abstract class BaseFragmentManagerActivity : BaseVMVBActivity() {
14 | private val switchHelper by lazyOnUI { FragmentSwitchHelper(supportFragmentManager) }
15 |
16 | protected fun switchFragment(tag: String, @IdRes frameLayoutId: Int = 0): Fragment? {
17 | return switchHelper.switchFragment(tag, frameLayoutId)
18 | }
19 |
20 | /**
21 | * 切换fragment,如果没有缓存则新建
22 | * 默认使用currentFragment.hasCode 作为tag
23 | */
24 | protected fun switchFragment(
25 | @NonNull currentFragment: Fragment,
26 | @IdRes frameLayoutId: Int = 0, tag: String? = null
27 | ) {
28 | switchHelper.switchFragment(currentFragment, frameLayoutId, tag)
29 | }
30 |
31 | /**
32 | * 隐藏一个Fragment
33 | */
34 | protected fun hideFragment(@NonNull clazz: Class) {
35 | switchHelper.hideFragment(clazz)
36 | }
37 |
38 | /**
39 | * 移除一个Fragment
40 | */
41 | protected fun removeFragment(@NonNull clazz: Class) {
42 | switchHelper.removeFragment(clazz)
43 | }
44 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/SerializableActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import com.foundation.app.arc.utils.param.BundleParams
7 | import com.foundation.app.arc.utils.param.BundleParamsUseSerializable
8 | import com.foundation.app.simple.architecture.BaseActivity
9 | import com.foundation.app.simple.databinding.ActUserInfoBinding
10 | import com.foundation.app.simple.ui.data.UserDescSerializable
11 |
12 |
13 | /**
14 | *create by zhusw on 5/17/21 17:11
15 | * scheme://xxxd/?k-v
16 | */
17 | class SerializableActivity : BaseActivity() {
18 |
19 | companion object {
20 | fun enter(context: Context, userDesc: UserDescSerializable) {
21 | val intent = Intent(context, SerializableActivity::class.java)
22 | intent.putExtra("userDesc", userDesc)
23 | context.startActivity(intent)
24 | }
25 | }
26 |
27 | @BundleParamsUseSerializable
28 | @BundleParams("userDesc")
29 | private val userDesc: UserDescSerializable = UserDescSerializable()
30 |
31 |
32 | private val vbBinding by lazyAndSetRoot()
33 |
34 | override fun init(savedInstanceState: Bundle?) {
35 |
36 | }
37 |
38 | override fun bindData() {
39 |
40 | vbBinding.auiTvUserId.text = "introduce: ${userDesc.introduce}"
41 | vbBinding.auiTvUserName.text = "height: ${userDesc.height}"
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/fragment/BaseViewBindingFragmentInJava.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.fragment
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.viewbinding.ViewBinding
8 | import com.foundation.app.arc.utils.ext.ViewBindingLifecycleListener
9 | import com.foundation.widget.binding.ViewBindingHelper
10 |
11 | /**
12 | * 完成viewBinding的初始化,设置根布局
13 | * 反射获取获取范型类型后进行初始化
14 | * 这样的实现是为了兼容JAVA,不推荐在kotlin中使用
15 | *create by zhusw on 4/22/21 11:28
16 | */
17 | abstract class BaseViewBindingFragmentInJava : BaseFragmentManagerFragment(),
18 | ViewBindingLifecycleListener {
19 |
20 | private var binding: B? = null
21 |
22 | protected val viewBinding: B get() = binding!!
23 |
24 | @JvmField
25 | protected var jViewBinding: B? = null
26 |
27 | override fun onCreateView(
28 | inflater: LayoutInflater,
29 | container: ViewGroup?,
30 | savedInstanceState: Bundle?
31 | ): View? {
32 | binding = ViewBindingHelper.getViewBindingInstance(this, inflater, container, false)
33 | jViewBinding = binding
34 | val vbCheck = requireNotNull(binding) {
35 | "BaseViewBindingFragment ViewBinding has not init or init failure,please check it"
36 | }
37 | return vbCheck.root
38 | }
39 |
40 | override fun onDestroyView() {
41 | onViewBindingDestroy()
42 | binding = null
43 | super.onDestroyView()
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/UserInfoActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import com.foundation.app.arc.utils.param.BundleParams
6 | import com.foundation.app.arc.utils.param.BundleParamsUseSerializable
7 | import com.foundation.app.simple.R
8 | import com.foundation.app.simple.architecture.BaseActivity
9 | import com.foundation.app.simple.databinding.ActUserInfoBinding
10 | import com.foundation.app.simple.ui.data.BundleProducer
11 |
12 |
13 | /**
14 | *create by zhusw on 5/17/21 17:11
15 | */
16 | class UserInfoActivity : BaseActivity() {
17 |
18 | private val vbBinding by lazyAndSetRoot()
19 |
20 | @BundleParams("userId")
21 | private val userId: Int = 0
22 |
23 | @BundleParams("userName")
24 | private val userName: String = "none"
25 |
26 | @BundleParamsUseSerializable
27 | @BundleParams("clsTest")
28 | private val cls: Class<*> = Any::class.java
29 |
30 |
31 |
32 | override fun init(savedInstanceState: Bundle?) {
33 | vbBinding.auiBtn.visibility = View.VISIBLE
34 | vbBinding.auiBtn.setOnClickListener {
35 | val frag = UserInfoFragment()
36 | val bundle = BundleProducer.create()
37 | frag.arguments = bundle
38 | switchFragment(frag, R.id.aui_fl, "UserInfoFragment")
39 | }
40 | }
41 |
42 | override fun bindData() {
43 |
44 | vbBinding.auiTvUserId.text = "用户ID: $userId"
45 | vbBinding.auiTvUserName.text = "用户名称: $userName"
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/frag_fragment_manager_visible.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
22 |
23 |
28 |
29 |
35 |
36 |
37 |
42 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/frag_view_pager_visible.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
22 |
23 |
28 |
29 |
35 |
36 |
37 |
42 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/frag_view_pager2_visible.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
22 |
23 |
28 |
29 |
35 |
36 |
37 |
42 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/ext/FragmentViewDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.utils.ext
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.lifecycle.Lifecycle
5 | import androidx.lifecycle.LifecycleObserver
6 | import androidx.lifecycle.OnLifecycleEvent
7 |
8 |
9 | /**
10 | * 跟着frag的生命周期走,当destroy时会销毁,当再次create时会创建新的
11 | */
12 | class FragmentViewDelegate(
13 | private val frag: Fragment,
14 | private val initializer: () -> T
15 | ) : Lazy {
16 |
17 | private var _value: Any? = UNINIT_VALUE
18 |
19 | override val value: T
20 | get() {
21 | if (!isInitialized()) {
22 | val curSate = frag.viewLifecycleOwner.lifecycle.currentState
23 | if (curSate == Lifecycle.State.DESTROYED) {
24 | throw IllegalAccessException(
25 | "can not init,because of fragment will be destroy soon," +
26 | " you can implement ViewBindingLifecycleListener on Fragment and override onViewBindingDestroy to work"
27 | )
28 | }
29 | _value = initializer()
30 | frag.viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
31 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
32 | fun onDestroyView() {
33 | frag.viewLifecycleOwner.lifecycle.removeObserver(this)
34 | _value = UNINIT_VALUE
35 | }
36 | })
37 | }
38 | return _value as T
39 | }
40 |
41 | override fun isInitialized(): Boolean = _value !== UNINIT_VALUE
42 |
43 |
44 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/net/WanAndroidNetStateHandler.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.net
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import com.foundation.app.simple.log
5 | import com.foundation.service.net.NetException
6 | import com.foundation.service.net.NetLoadingEvent
7 | import com.foundation.service.net.NetStateListener
8 |
9 | /**
10 | * create by zhusw on 5/26/21 14:06
11 | */
12 | class WanAndroidNetStateHandler(
13 | private val control: Boolean = false,
14 | private val stateLiveData: MutableLiveData
15 | ) :
16 | NetStateListener {
17 | override fun onStart() {
18 | if (control) {
19 | stateLiveData.value = NetLoadingEvent.START
20 | }
21 | }
22 |
23 | override fun onSuccess() {
24 | if (control) {
25 | stateLiveData.value = NetLoadingEvent.STOP
26 | }
27 | }
28 |
29 | override fun onFailure(e: Throwable) {
30 | handlerNetException(e)
31 | }
32 |
33 | private fun handlerNetException(e: Throwable) {
34 | when (e) {
35 | is NetException -> {
36 | stateLiveData.value = NetLoadingEvent.getErrorEvent(e.netCode, e.netMsg)
37 | "NetException: $e".log("net--")
38 | }
39 | is WanAndroidResException -> {
40 | stateLiveData.value = NetLoadingEvent.getErrorEvent(e.code, e.msg)
41 | "WanAndroidResException: $e".log("net--")
42 | }
43 | else -> {
44 | stateLiveData.value = NetLoadingEvent.getErrorEvent(-1, "网络层未知错误")
45 | "else: $e".log("net--")
46 | }
47 | }
48 |
49 | }
50 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/kcp/PermissionExt.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.kcp
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentActivity
5 |
6 | /**
7 | * create by zhusw on 5/20/21 13:49
8 | */
9 | inline fun FragmentActivity.easyRequestPermission(
10 | vararg perNames: String, crossinline onReject: (rejects: List) -> Unit,
11 | crossinline onSuccess: () -> Unit
12 | ) {
13 | KCPermission.prepare()
14 | .with(this)
15 | .target(*perNames)
16 | .build()?.request(
17 | leastOneReject = { g, r ->
18 | onReject.invoke(r)
19 | },
20 | allGranted = {
21 | onSuccess.invoke()
22 | }
23 | )
24 | }
25 |
26 | inline fun FragmentActivity.easyRequestPermission(
27 | vararg perNames: String,
28 | crossinline onSuccess: () -> Unit
29 | ) {
30 | easyRequestPermission(*perNames, onReject = { }, onSuccess = onSuccess)
31 | }
32 |
33 |
34 | inline fun Fragment.easyRequestPermission(
35 | vararg perNames: String,
36 | crossinline onSuccess: () -> Unit
37 | ) {
38 | easyRequestPermission(*perNames, onReject = { }, onSuccess = onSuccess)
39 | }
40 |
41 | inline fun Fragment.easyRequestPermission(
42 | vararg perNames: String, crossinline onReject: (rejects: List) -> Unit,
43 | crossinline onSuccess: () -> Unit
44 | ) {
45 | KCPermission.prepare()
46 | .with(this)
47 | ?.target(*perNames)
48 | ?.build()?.request(
49 | leastOneReject = { g, r ->
50 | onReject.invoke(r)
51 | },
52 | allGranted = {
53 | onSuccess.invoke()
54 | }
55 | )
56 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # IntelliJ
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/assetWizardSettings.xml
45 | .idea/dictionaries
46 | .idea/libraries
47 | # Android Studio 3 in .gitignore file.
48 | .idea/caches
49 | .idea/modules.xml
50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
51 | .idea/navEditor.xml
52 |
53 | # Keystore files
54 | # Uncomment the following lines if you do not want to check your keystore files in.
55 | #*.jks
56 | #*.keystore
57 |
58 | # External native build folder generated in Android Studio 2.2 and later
59 | .externalNativeBuild
60 | .cxx/
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | # google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | fastlane/report.xml
72 | fastlane/Preview.html
73 | fastlane/screenshots
74 | fastlane/test_output
75 | fastlane/readme.md
76 |
77 | # Version control
78 | vcs.xml
79 |
80 | # lint
81 | lint/intermediates/
82 | lint/generated/
83 | lint/outputs/
84 | lint/tmp/
85 | # lint/reports/
86 |
--------------------------------------------------------------------------------
/simple/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/act_vb.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
18 |
19 |
25 |
26 |
31 |
32 |
37 |
38 |
43 |
44 |
49 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/item_news.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
31 |
32 |
44 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/act_user_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
16 |
17 |
25 |
26 |
35 |
36 |
44 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/data/UserData.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.data
2 |
3 | import android.os.Parcel
4 | import android.os.Parcelable
5 | import java.io.Serializable
6 |
7 | /**
8 | *create by zhusw on 5/17/21 17:52
9 | */
10 | data class UserAddress(val city: String = "none", val streetNo: Int = 0) : Parcelable {
11 | constructor(parcel: Parcel) : this(
12 | parcel.readString()!!,
13 | parcel.readInt()
14 | ) {
15 | }
16 |
17 | override fun writeToParcel(parcel: Parcel, flags: Int) {
18 | parcel.writeString(city)
19 | parcel.writeInt(streetNo)
20 | }
21 |
22 | override fun describeContents(): Int {
23 | return 0
24 | }
25 |
26 | companion object CREATOR : Parcelable.Creator {
27 | override fun createFromParcel(parcel: Parcel): UserAddress {
28 | return UserAddress(parcel)
29 | }
30 |
31 | override fun newArray(size: Int): Array {
32 | return arrayOfNulls(size)
33 | }
34 | }
35 |
36 | }
37 |
38 | data class UserDescSerializable(val introduce: String = "none", val height: Int = 0) : Serializable
39 |
40 | data class UserDesc(val introduce: String = "none", val height: Int = 0) : Parcelable {
41 | constructor(parcel: Parcel) : this(
42 | parcel.readString() ?: "",
43 | parcel.readInt()
44 | ) {
45 | }
46 |
47 | override fun writeToParcel(parcel: Parcel, flags: Int) {
48 | parcel.writeString(introduce)
49 | parcel.writeInt(height)
50 | }
51 |
52 | override fun describeContents(): Int {
53 | return 0
54 | }
55 |
56 | companion object CREATOR : Parcelable.Creator {
57 | override fun createFromParcel(parcel: Parcel): UserDesc {
58 | return UserDesc(parcel)
59 | }
60 |
61 | override fun newArray(size: Int): Array {
62 | return arrayOfNulls(size)
63 | }
64 | }
65 |
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/simple/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/home/HomeVM.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.home
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import com.foundation.app.simple.demo.base.BaseWanAndroidVM
6 | import com.foundation.app.simple.demo.home.data.BannerEntity
7 | import com.foundation.app.simple.demo.home.data.NewsFeedInfo
8 | import com.foundation.app.simple.demo.net.WanAndroidNetStateHandler
9 | import kotlinx.coroutines.delay
10 |
11 | class HomeVM : BaseWanAndroidVM() {
12 |
13 | private val homeRepo by lazy {
14 | HomeRepo()
15 | }
16 |
17 | /**
18 | * 核心架构 思想:保证单一可信源
19 | * view层只能订阅状态,不可修改状态
20 | */
21 | private val _bannerData = MutableLiveData>()
22 | val bannerData: LiveData> = _bannerData
23 |
24 |
25 | private val _newsLiveData = MutableLiveData>()
26 | val newsLiveData: LiveData> = _newsLiveData
27 |
28 | fun loadBanner() {
29 | netLaunch({
30 | delay(3000)
31 | val data = withBusiness {
32 | homeRepo.homeApi.getBanner()
33 | }
34 | _bannerData.value = data
35 | }, WanAndroidNetStateHandler(true, _loadEventLiveData), "加载 banner")
36 | }
37 |
38 | private var pageCount = -1
39 |
40 | private val _cleanAdapterLiveData = MutableLiveData()
41 | val cleanAdapterLiveData: LiveData = _cleanAdapterLiveData
42 |
43 | fun loadNews(refresh: Boolean = true) {
44 | if (refresh) {
45 | _cleanAdapterLiveData.value = Unit
46 | pageCount = 0
47 | } else {
48 | pageCount++
49 | }
50 | netLaunch(
51 | {
52 | val data = withBusiness {
53 | homeRepo.homeApi.getNews(pageCount)
54 | }
55 | _newsLiveData.value = data.datas
56 | }, WanAndroidNetStateHandler(stateLiveData = _loadEventLiveData),
57 | "加载 列表"
58 | )
59 | }
60 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/vm/ReflectionTest.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.vm
2 |
3 | /**
4 | * create by zhusw on 5/21/21 15:24
5 | count = 1_0000
6 | I/System.out: --反射:无 1 ms
7 | I/System.out: --反射:java 17 ms
8 | I/System.out: --反射:java 带缓存 3 ms
9 | I/System.out: --反射:kotlin 13 ms
10 | I/System.out: --反射:kotlin 带缓存 6 ms
11 | */
12 | object ReflectionTest {
13 | fun test() {
14 | val count = 1_0000
15 | val nt = System.currentTimeMillis()
16 | for (i in 0..count) {
17 | Test.work()
18 | }
19 | val r1 = System.currentTimeMillis() - nt
20 | println("--反射:无 $r1 ms")
21 | /////////////////////java/////////////////////////
22 | val nt2 = System.currentTimeMillis()
23 | for (i in 0..count) {
24 | val method = Test::class.java.getMethod("work")
25 | method.invoke(null)
26 | }
27 | val r2 = System.currentTimeMillis() - nt2
28 | println("--反射:java $r2 ms")
29 | //------------------------缓存------------------------
30 | val nt5 = System.currentTimeMillis()
31 | val method = Test::class.java.getMethod("work")
32 | for (i in 0..count) {
33 | method.invoke(null)
34 | }
35 | val r5 = System.currentTimeMillis() - nt5
36 | println("--反射:java 带缓存 $r5 ms")
37 | /////////////////////java/////////////////////////
38 | val nt3 = System.currentTimeMillis()
39 | for (i in 0..count) {
40 | Foo.javaClass.getMethod("foo").invoke(Foo)
41 | }
42 | val r3 = System.currentTimeMillis() - nt3
43 | println("--反射:kotlin $r3 ms")
44 | //------------------------缓存------------------------
45 | val nt4 = System.currentTimeMillis()
46 | val m = Foo.javaClass.getMethod("foo")
47 | for (i in 0..count) {
48 | m.invoke(Foo)
49 | }
50 | val r4 = System.currentTimeMillis() - nt4
51 | println("--反射:kotlin 带缓存 $r4 ms")
52 | }
53 | }
54 |
55 | object Foo {
56 | fun foo(): Int {
57 | val i = 1
58 | return i + 1
59 | }
60 | }
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/ext/LiveDataExt.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("LiveDataExt")
2 | @file:kotlin.jvm.JvmMultifileClass
3 | package com.foundation.app.arc.utils.ext
4 |
5 | import androidx.lifecycle.LifecycleOwner
6 | import androidx.lifecycle.LiveData
7 | import androidx.lifecycle.Observer
8 | import java.lang.reflect.Field
9 |
10 | /**
11 | * 同一observer对象多次订阅[observerStickyLess]和[LiveData.observe]只会有一次生效
12 | * 订阅无粘性消息,使用到了反射 liveData的 mVersion
13 | * 反射谣言:在Android 大可不必考虑反射的性能开销。在个位数指令执行测试中,反射1w次
14 | * 裸调 多消耗 16 ms, 使用缓存 多消耗 2ms
15 | * 订阅前尝试了移除,避免同一个Observer 分别订阅在了[LiveData.observe] 与 [observerStickyLess] 中
16 | * 造成重复收到结果
17 | * create by zhusw on 5/21/21 14:34
18 | */
19 | fun LiveData.observerStickyLess(owner: LifecycleOwner, observer: Observer) {
20 | removeObserver(observer)
21 | observe(owner, StickLessWrapperObserver(this, observer))
22 | }
23 |
24 | private class StickLessWrapperObserver(
25 | private val liveData: LiveData,
26 | private val realObs: Observer
27 | ) :
28 | Observer {
29 | private val initVersion: Int
30 | private val versionField: Field = LiveData::class.java.getDeclaredField("mVersion")
31 |
32 | init {
33 | initVersion = getVersion()
34 | }
35 |
36 | //
37 | override fun hashCode(): Int {
38 | return realObs.hashCode()
39 | }
40 |
41 | override fun equals(other: Any?): Boolean {
42 | if (other is StickLessWrapperObserver<*>) {
43 | return realObs == other.realObs
44 | } else if (other is Observer<*>) {
45 | return realObs == other
46 | }
47 | return super.equals(other)
48 | }
49 | //
50 |
51 | override fun onChanged(t: T) {
52 | val curVersion = getVersion()
53 | if (curVersion > initVersion) {
54 | realObs.onChanged(t)
55 | }
56 | }
57 |
58 | private fun getVersion(): Int {
59 | if (!versionField.isAccessible) {
60 | versionField.isAccessible = true
61 | }
62 | return versionField.getInt(liveData)
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/ActVbBindingCopy.java:
--------------------------------------------------------------------------------
1 | // Generated by view binder compiler. Do not edit!
2 | package com.foundation.app.simple.backup;
3 |
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.Button;
8 | import android.widget.LinearLayout;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import androidx.viewbinding.ViewBinding;
13 |
14 | import com.foundation.app.simple.R;
15 |
16 | public final class ActVbBindingCopy implements ViewBinding {
17 | @NonNull
18 | private final LinearLayout rootView;
19 |
20 | @NonNull
21 | public final Button btn;
22 |
23 | private ActVbBindingCopy(@NonNull LinearLayout rootView, @NonNull Button btn) {
24 | this.rootView = rootView;
25 | this.btn = btn;
26 | }
27 |
28 | @Override
29 | @NonNull
30 | public LinearLayout getRoot() {
31 | return rootView;
32 | }
33 |
34 | @NonNull
35 | public static ActVbBindingCopy inflate(@NonNull LayoutInflater inflater) {
36 | return inflate(inflater, null, false);
37 | }
38 |
39 | @NonNull
40 | public static ActVbBindingCopy inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent,
41 | boolean attachToParent) {
42 | View root = inflater.inflate(R.layout.act_vb, parent, false);
43 | if (attachToParent) {
44 | parent.addView(root);
45 | }
46 | return bind(root);
47 | }
48 |
49 | @NonNull
50 | public static ActVbBindingCopy bind(@NonNull View rootView) {
51 | System.out.println("ActVbBindingCopy bind baseAF");
52 | // The body of this method is generated in a way you would not otherwise write.
53 | // This is done to optimize the compiled bytecode for size and performance.
54 | int id;
55 | missingId:
56 | {
57 | id = R.id.btn;
58 | Button btn = rootView.findViewById(id);
59 | if (btn == null) {
60 | break missingId;
61 | }
62 |
63 | return new ActVbBindingCopy((LinearLayout) rootView, btn);
64 | }
65 | String missingId = rootView.getResources().getResourceName(id);
66 | throw new NullPointerException("Missing required view with ID: ".concat(missingId));
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/backup/ActVbBindingCopy.java:
--------------------------------------------------------------------------------
1 | // Generated by view binder compiler. Do not edit!
2 | package com.foundation.app.simple.backup;
3 |
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.Button;
8 | import android.widget.LinearLayout;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import androidx.viewbinding.ViewBinding;
13 |
14 | import com.foundation.app.simple.R;
15 |
16 | public final class ActVbBindingCopy implements ViewBinding {
17 | @NonNull
18 | private final LinearLayout rootView;
19 |
20 | @NonNull
21 | public final Button btn;
22 |
23 | private ActVbBindingCopy(@NonNull LinearLayout rootView, @NonNull Button btn) {
24 | this.rootView = rootView;
25 | this.btn = btn;
26 | }
27 |
28 | @Override
29 | @NonNull
30 | public LinearLayout getRoot() {
31 | return rootView;
32 | }
33 |
34 | @NonNull
35 | public static ActVbBindingCopy inflate(@NonNull LayoutInflater inflater) {
36 | return inflate(inflater, null, false);
37 | }
38 |
39 | @NonNull
40 | public static ActVbBindingCopy inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent,
41 | boolean attachToParent) {
42 | View root = inflater.inflate(R.layout.act_vb, parent, false);
43 | if (attachToParent) {
44 | parent.addView(root);
45 | }
46 | return bind(root);
47 | }
48 |
49 | @NonNull
50 | public static ActVbBindingCopy bind(@NonNull View rootView) {
51 | System.out.println("ActVbBindingCopy bind baseAF");
52 | // The body of this method is generated in a way you would not otherwise write.
53 | // This is done to optimize the compiled bytecode for size and performance.
54 | int id;
55 | missingId:
56 | {
57 | id = R.id.btn;
58 | Button btn = rootView.findViewById(id);
59 | if (btn == null) {
60 | break missingId;
61 | }
62 |
63 | return new ActVbBindingCopy((LinearLayout) rootView, btn);
64 | }
65 | String missingId = rootView.getResources().getResourceName(id);
66 | throw new NullPointerException("Missing required view with ID: ".concat(missingId));
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/act_sticky.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
19 |
20 |
28 |
29 |
30 |
31 |
36 |
37 |
41 |
42 |
49 |
50 |
58 |
59 |
60 |
61 |
66 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/fragment/visible/ViewPagerVisibleTestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.fragment.visible
2 |
3 | import androidx.fragment.app.FragmentPagerAdapter
4 | import androidx.fragment.app.FragmentStatePagerAdapter
5 | import androidx.viewpager.widget.ViewPager
6 | import com.foundation.app.simple.R
7 | import com.foundation.app.simple.databinding.FragViewPagerVisibleBinding
8 | import com.foundation.app.simple.ui.adapter.BaseFragmentPagerAdapter
9 | import com.foundation.app.simple.ui.adapter.BaseFragmentStatePagerAdapter
10 |
11 | class ViewPagerVisibleTestFragment :
12 | AbstractVisibleTestFragment(R.layout.frag_view_pager_visible) {
13 |
14 | private val vb by lazyVB()
15 |
16 | override fun bindData() {
17 | super.bindData()
18 |
19 | vb.vpPager.offscreenPageLimit = 1
20 | vb.vpPager.adapter = when (fragType) {
21 | TYPE_VP_PAGER_HINT_ALL -> BaseFragmentPagerAdapter(
22 | childFragmentManager,
23 | frags,
24 | FragmentPagerAdapter.BEHAVIOR_SET_USER_VISIBLE_HINT
25 | )
26 | TYPE_VP_PAGER_HINT_ONLY -> BaseFragmentPagerAdapter(
27 | childFragmentManager,
28 | frags,
29 | FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
30 | )
31 | TYPE_VP_STATE_HINT_ALL -> BaseFragmentStatePagerAdapter(
32 | childFragmentManager,
33 | frags,
34 | FragmentStatePagerAdapter.BEHAVIOR_SET_USER_VISIBLE_HINT
35 | )
36 | TYPE_VP_STATE_HINT_ONLY -> BaseFragmentStatePagerAdapter(
37 | childFragmentManager,
38 | frags,
39 | FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
40 | )
41 | else -> throw RuntimeException("未知异常:$fragType")
42 | }
43 | vb.vpPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
44 | override fun onPageSelected(position: Int) {
45 | currentIndex = position
46 | }
47 | })
48 | }
49 |
50 | override fun onSwitchFragment(index: Int) {
51 | vb.vpPager.currentItem = index
52 | }
53 |
54 | override fun getBtnOpenNewPage() = vb.btnOpenNewPage
55 | override fun getTvFragmentTitle() = vb.tvFragmentTitle
56 | override fun getBtnClickSwitch() = vb.btnClickSwitch
57 | }
58 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/ext/FragmentViewBindingDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.utils.ext
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.lifecycle.Lifecycle
5 | import androidx.lifecycle.LifecycleObserver
6 | import androidx.lifecycle.OnLifecycleEvent
7 | import androidx.viewbinding.ViewBinding
8 | import kotlin.properties.ReadOnlyProperty
9 | import kotlin.reflect.KProperty
10 |
11 | /**
12 | * 使用此委托fragment 必须在BaseFragment中传入 layoutId
13 | * 如果有base层,确保支持布局id,像下面这样来声明BaseFragment
14 | * open class MyBaseFragment(@LayoutRes id:Int=0):Fragment(id){}
15 | * class MyFragment : MyBaseFragment(R.layout.act_vb)
16 | * create by zhusw on 5/12/21 15:19
17 | */
18 | class FragmentViewBindingDelegate(private val initBlock: () -> VB) :
19 | ReadOnlyProperty {
20 |
21 | private var binding: VB? = null
22 |
23 | override fun getValue(thisRef: Fragment, property: KProperty<*>): VB {
24 | return when (binding) {
25 | null -> {
26 | val curSate = thisRef.viewLifecycleOwner.lifecycle.currentState
27 | if (curSate == Lifecycle.State.DESTROYED) {
28 | throw IllegalAccessException(
29 | "can not init binding,because of fragment will be destroy soon," +
30 | " you can implement ViewBindingLifecycleListener on Fragment and override onViewBindingDestroy to work"
31 | )
32 | }
33 | binding = initBlock()
34 | thisRef.viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
35 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
36 | fun onDestroyView() {
37 | thisRef.viewLifecycleOwner.lifecycle.removeObserver(this)
38 | if (thisRef is ViewBindingLifecycleListener) {
39 | thisRef.onViewBindingDestroy()
40 | }
41 | //在 onViewBindingDestroy 之后置空,保证页面不需要再使用 binding
42 | binding = null
43 | }
44 | })
45 | binding!!
46 | }
47 | else -> {
48 | binding!!
49 | }
50 | }
51 | }
52 | }
53 |
54 | interface ViewBindingLifecycleListener {
55 | /**
56 | * 在OnDestroyView执行清理前 处理例如状态保持,释放等逻辑
57 | * 通常并不需要实现
58 | */
59 | fun onViewBindingDestroy() {
60 | }
61 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/FragmentSwitchHelper.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.utils
2 |
3 | import androidx.annotation.IdRes
4 | import androidx.annotation.NonNull
5 | import androidx.fragment.app.Fragment
6 | import androidx.fragment.app.FragmentManager
7 | import androidx.fragment.app.FragmentTransaction
8 |
9 | class FragmentSwitchHelper(private val fm: FragmentManager) {
10 | private var showFragment: Fragment? = null
11 | fun switchFragment(tag: String, @IdRes frameLayoutId: Int = 0): Fragment? {
12 | try {
13 | val fragment: Fragment? = fm.findFragmentByTag(tag)
14 | fragment?.let {
15 | switchFragment(it, frameLayoutId)
16 | return it
17 | }
18 | } catch (e: InstantiationException) {
19 | e.printStackTrace()
20 | } catch (e: IllegalAccessException) {
21 | e.printStackTrace()
22 | }
23 | return null
24 | }
25 |
26 | /**
27 | * 切换fragment,如果没有缓存则新建
28 | * 默认使用currentFragment.hasCode 作为tag
29 | */
30 | fun switchFragment(
31 | currentFragment: Fragment,
32 | @IdRes frameLayoutId: Int = 0, tag: String? = null
33 | ) {
34 | val safeTag = tag ?: currentFragment.hashCode().toString()
35 | val ft: FragmentTransaction = fm.beginTransaction()
36 | //如果选择的fragment 不是第一个也不是正在显示的 则隐藏正在显示的
37 | showFragment?.let {
38 | if (it !== currentFragment) {
39 | ft.hide(it)
40 | }
41 | }
42 | if (currentFragment.isAdded) {
43 | ft.show(currentFragment)
44 | } else if (frameLayoutId != 0) {
45 | ft.add(frameLayoutId, currentFragment, safeTag)
46 | }
47 | ft.commitNow()
48 | showFragment = currentFragment
49 | }
50 |
51 | /**
52 | * 隐藏一个Fragment
53 | */
54 | fun hideFragment(clazz: Class) {
55 | val ft: FragmentTransaction = fm.beginTransaction()
56 | val currentFragment: Fragment? = fm.findFragmentByTag(clazz.name)
57 | currentFragment?.let {
58 | ft.hide(it)
59 | ft.commitNow()
60 | }
61 | }
62 |
63 | /**
64 | * 移除一个Fragment
65 | */
66 | fun removeFragment(@NonNull clazz: Class) {
67 | val ft: FragmentTransaction = fm.beginTransaction()
68 | val currentFragment: Fragment? = fm.findFragmentByTag(clazz.name)
69 | currentFragment?.let {
70 | ft.remove(it)
71 | ft.commitNow()
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/fragment/InternalBasicFragment.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("PackageDirectoryMismatch")//包名警告
2 | package androidx.fragment.app
3 |
4 | import android.view.ViewGroup
5 | import androidx.viewpager.widget.ViewPager
6 | import com.foundation.app.arc.fragment.BaseVisibilityFragment
7 |
8 | /**
9 | * 这是一个服务于[BaseVisibilityFragment]的上层Fragment,用于调用私有方法[mContainer]来更好的确定父布局类型
10 | *
11 | * 当前各版本现状:
12 | * FragmentManager:调用[onHiddenChanged],但第一次不会调用
13 | * ViewPager(2):统一会调用[setMenuVisibility],[setUserVisibleHint]太乱不适合锚点
14 | */
15 | abstract class InternalBasicFragment internal constructor() : Fragment() {
16 |
17 | /**
18 | * null表示未初始化(下面几个成员变量同理),0表示ViewPager(2),1表示FragmentManager
19 | */
20 | private var _isFragmentManager: Boolean? = null
21 |
22 | private var _isFmHidden: Boolean? = null
23 | protected val isFmHidden get() = isFragmentManager() && (_isFmHidden ?: false)
24 |
25 | private var _isVpVisible: Boolean? = null
26 | protected val isVpVisible get() = !isFragmentManager() && (_isVpVisible ?: false)
27 |
28 | private var _isFirstResumed = false
29 | protected val isFirstResumed get() = _isFirstResumed
30 |
31 | /**
32 | * 获取父类类型是FragmentManager还是ViewPager(2)
33 | */
34 | protected fun isFragmentManager(): Boolean {
35 | _isFragmentManager?.let {
36 | return it
37 | }
38 | return when (mContainer) {
39 | is ViewPager -> {
40 | _isFragmentManager = false
41 | false
42 | }
43 | is ViewGroup -> {
44 | //FragmentManager的父布局类似FrameLayout、LinearLayout
45 | _isFragmentManager = true
46 | true
47 | }
48 | else -> false//mContainer null的情况,是ViewPager2(除非自定义)
49 | }
50 | }
51 |
52 | /**
53 | * fm 的 hide 跟 show 的回调,第一次不会调用
54 | */
55 | override fun onHiddenChanged(hidden: Boolean) {
56 | super.onHiddenChanged(hidden)
57 | _isFragmentManager = true
58 | _isFmHidden = hidden
59 | }
60 |
61 | /**
62 | * vp的可见回调,在onCreate前,[setUserVisibleHint]太乱不适合锚点
63 | */
64 | override fun setMenuVisibility(menuVisible: Boolean) {
65 | super.setMenuVisibility(menuVisible)
66 | _isFragmentManager = false
67 | _isVpVisible = menuVisible
68 | }
69 |
70 | override fun onResume() {
71 | super.onResume()
72 | _isFirstResumed = true
73 | }
74 |
75 | override fun onDestroy() {
76 | onClearVisibleState()
77 | super.onDestroy()
78 | }
79 |
80 | override fun onDestroyView() {
81 | super.onDestroyView()
82 | onClearVisibleState()
83 | }
84 |
85 | /**
86 | * 当[onDestroy]或[onDestroyView]时应当重置保存状态
87 | */
88 | protected open fun onClearVisibleState() {
89 | _isFmHidden = null
90 | _isVpVisible = null
91 | _isFirstResumed = false
92 | }
93 | }
--------------------------------------------------------------------------------
/simple/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.buildsrc.kts.Dependencies
2 | plugins {
3 | id("com.android.application")
4 | id("kotlin-android")
5 | }
6 |
7 | android {
8 | compileSdkVersion(31)
9 |
10 | defaultConfig {
11 | applicationId = "com.foundation.app.simple"
12 | minSdkVersion(21)
13 | targetSdkVersion(31)
14 | versionCode = 1
15 | versionName = "1.0"
16 |
17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 | signingConfigs {
20 | create("normalSign") {
21 | storeFile = file("test.jks")
22 | storePassword = "android"
23 | keyAlias = "android"
24 | keyPassword = "android"
25 | }
26 | }
27 | buildTypes {
28 | getByName("release") {
29 | isDebuggable = false
30 | isMinifyEnabled = false
31 | proguardFiles(
32 | getDefaultProguardFile("proguard-android-optimize.txt"),
33 | "proguard-rules.pro"
34 | )
35 | signingConfig = signingConfigs.getByName("normalSign")
36 | }
37 | getByName("debug") {
38 | signingConfig = signingConfigs.getByName("normalSign")
39 | // isMinifyEnabled = true
40 | multiDexEnabled = true
41 | }
42 |
43 | }
44 | buildFeatures {
45 | viewBinding = true
46 | }
47 |
48 | compileOptions {
49 | sourceCompatibility = Dependencies.javaVersion
50 | targetCompatibility = Dependencies.javaVersion
51 | }
52 | kotlinOptions {
53 | jvmTarget = Dependencies.jvmTarget
54 | }
55 | }
56 |
57 | configurations.all {
58 | resolutionStrategy {
59 | //版本号会改变
60 | cacheDynamicVersionsFor(10, TimeUnit.SECONDS)
61 | //版本号不变,1.0-SNAPSHOT 这种应该实时更新
62 | cacheChangingModulesFor(10, TimeUnit.SECONDS)
63 |
64 | }
65 | }
66 | dependencies {
67 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
68 | implementation(Dependencies.Kotlin.kotlin_stdlib)
69 | implementation(Dependencies.JetPack.core_ktx)
70 | implementation(Dependencies.AndroidX.appcompat)
71 | implementation(Dependencies.Material.material)
72 | implementation(Dependencies.AndroidX.constraintLayout)
73 | implementation(Dependencies.Coroutines.coroutines_android)
74 | implementation(Dependencies.JetPack.lifecycle_liveData_ktx)
75 | implementation(Dependencies.JetPack.lifecycle_runtime_ktx)
76 | implementation(Dependencies.JetPack.lifecycle_viewModel_ktx)
77 | implementation(Dependencies.JetPack.fragment_ktx)
78 | implementation(Dependencies.Foundation.loading)
79 | implementation(Dependencies.Retrofit.retorifit)
80 | implementation(Dependencies.Retrofit.converter_gson)
81 | implementation(Dependencies.UI.BaseRecyclerViewAdapterHelper)
82 | implementation(Dependencies.UI.glde)
83 | implementation(project(":baseAF"))
84 | implementation(Dependencies.Foundation.net)
85 | }
86 |
--------------------------------------------------------------------------------
/simple/src/main/res/layout/act_home_wanandroid.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
26 |
27 |
41 |
42 |
50 |
51 |
61 |
62 |
71 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/demo/home/HomeActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.demo.home
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.os.Bundle
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.recyclerview.widget.LinearLayoutManager
9 | import androidx.viewbinding.ViewBinding
10 | import com.bumptech.glide.Glide
11 | import com.foundation.app.simple.architecture.BaseActivity
12 | import com.foundation.app.simple.databinding.ActHomeWanandroidBinding
13 | import com.foundation.app.simple.demo.home.data.NewsFeedInfo
14 | import com.foundation.app.simple.log
15 | import com.foundation.app.simple.toast
16 | import com.foundation.service.net.NetLoadingEvent
17 | import com.foundation.widget.loading.NormalLoadingAdapter
18 |
19 | /**
20 | * create by zhusw on 5/20/21 11:33
21 | */
22 | class HomeActivity : BaseActivity() {
23 |
24 | private val viewBinding by lazyVB()
25 |
26 | override fun getContentVB(): ViewBinding = viewBinding
27 |
28 | private lateinit var homeVM: HomeVM
29 |
30 | override fun initViewModel() {
31 | super.initViewModel()
32 | homeVM = getActivityVM(HomeVM::class.java)
33 | }
34 |
35 | private val adapter = NewsAdapter()
36 | override fun init(savedInstanceState: Bundle?) {
37 | viewBinding.rlNews.adapter = adapter
38 | viewBinding.rlNews.layoutManager = LinearLayoutManager(this)
39 | adapter.setOnItemClickListener { adapter, view, position ->
40 | val data = adapter.getItem(position) as NewsFeedInfo
41 | "点击:${data.title}".toast()
42 |
43 | }
44 | viewBinding.contentLoading.asLoading().setLoadingAdapter(LoadingAdapter(this))
45 |
46 | viewBinding.btnInit.setOnClickListener {
47 | homeVM.loadBanner()
48 | homeVM.loadNews(true)
49 | }
50 | viewBinding.btnListMore.setOnClickListener {
51 | homeVM.loadNews(false)
52 | }
53 | viewBinding.btnListNew.setOnClickListener {
54 | homeVM.loadNews(true)
55 | }
56 | }
57 |
58 | override fun bindData() {
59 | homeVM.loadEventLiveData.observe(this) {
60 | "loadState type :${it.type}".log("net--")
61 | when (it.type) {
62 | NetLoadingEvent.TYPE_START -> {
63 | viewBinding.contentLoading.asLoading().showLoading()
64 | }
65 | NetLoadingEvent.TYPE_STOP -> {
66 | viewBinding.contentLoading.asLoading().stop()
67 | }
68 | NetLoadingEvent.TYPE_ERROR -> {
69 | viewBinding.contentLoading.asLoading().stop()
70 | "${it.msg}:${it.code}".toast()
71 | }
72 | }
73 | }
74 | homeVM.bannerData.observe(this) {
75 | Glide.with(this).load(it[2].imagePath)
76 | .into(viewBinding.ivBanner)
77 | viewBinding.tvBannerTitle.text = it[2].title
78 | }
79 |
80 | homeVM.cleanAdapterLiveData.observe(this) {
81 | adapter.setNewInstance(null)
82 | }
83 | homeVM.newsLiveData.observe(this) {
84 | adapter.addData(it)
85 | }
86 |
87 | }
88 |
89 |
90 | }
91 |
92 | private class LoadingAdapter(private val ctx: Context) : NormalLoadingAdapter() {
93 | override fun getBottomPlateView(): View? {
94 | return View(ctx).apply {
95 | setBackgroundColor(Color.LTGRAY)
96 | layoutParams = ViewGroup.LayoutParams(
97 | ViewGroup.LayoutParams.MATCH_PARENT,
98 | ViewGroup.LayoutParams.MATCH_PARENT
99 | )
100 | }
101 | }
102 |
103 |
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/fragment/BaseVMFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.fragment
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.annotation.CallSuper
6 | import androidx.annotation.MainThread
7 | import androidx.lifecycle.ViewModel
8 | import androidx.lifecycle.ViewModelProvider
9 | import androidx.lifecycle.ViewModelStoreOwner
10 | import com.foundation.app.arc.utils.ext.FragmentViewDelegate
11 | import com.foundation.app.arc.utils.ext.lazyAtomic
12 |
13 | /**
14 | * ViewModel 创建与使用规范
15 | * create by zhusw on 5/18/21 13:36
16 | */
17 | abstract class BaseVMFragment : BaseParamsFragment() {
18 |
19 | /**
20 | * 不能在 onCreate 之前调用
21 | */
22 | private lateinit var _fragmentVMProvider: ViewModelProvider
23 | val fragmentVMProvider get() = _fragmentVMProvider
24 | val activityVMProvider by lazyAtomic {
25 | val activity = requireActivity()
26 | ViewModelProvider(activity)
27 | }
28 | val applicationVMProvider by lazyAtomic {
29 | val activity = requireActivity()
30 | val app = activity.application ?: throw IllegalStateException(
31 | "$hostActivity 还没有 attach application." +
32 | "Fragment 也不可以在 onCreate 之前使用 ViewModel"
33 | )
34 | when (app) {
35 | is ViewModelStoreOwner -> {
36 | ViewModelProvider(
37 | app.viewModelStore,
38 | ViewModelProvider.AndroidViewModelFactory.getInstance(app)
39 | )
40 | }
41 | else -> {
42 | throw IllegalStateException("application:$app 没实现 ViewModelStoreOwner:调用处Fragment为 $this ")
43 | }
44 | }
45 | }
46 |
47 |
48 | protected fun getFragmentVM(clz: Class): VM {
49 | return fragmentVMProvider.get(clz)
50 | }
51 |
52 | protected fun getActivityVM(clz: Class): VM {
53 | return activityVMProvider.get(clz)
54 | }
55 |
56 | protected fun getGlobalVM(clz: Class): VM {
57 | return applicationVMProvider.get(clz)
58 | }
59 |
60 | @MainThread
61 | inline fun lazyFragmentVM(): Lazy {
62 | return lazyWithFragment {
63 | fragmentVMProvider.get(VM::class.java)
64 | }
65 | }
66 |
67 |
68 | @MainThread
69 | inline fun lazyActivityVM(): Lazy {
70 | return lazyWithFragment {
71 | activityVMProvider.get(VM::class.java)
72 | }
73 | }
74 |
75 | @MainThread
76 | inline fun lazyGlobalVM(): Lazy {
77 | return lazyWithFragment {
78 | applicationVMProvider.get(VM::class.java)
79 | }
80 | }
81 |
82 | /**
83 | * @param savedInstanceState
84 | * 注意:由于多嵌套在内部销毁重建时(实现了saveState的,示例:ViewPager套ViewPager),也可能会引起ui错乱
85 | * 此处不可类比Activity强制返null,遂暂未解决,见ViewPager2的FragmentStateAdapter.restoreState
86 | */
87 | override fun onCreate(savedInstanceState: Bundle?) {
88 | super.onCreate(savedInstanceState)
89 | _fragmentVMProvider = ViewModelProvider(this)
90 | }
91 |
92 | @CallSuper
93 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
94 | super.onViewCreated(view, savedInstanceState)
95 | initViewModel()//1
96 | init(savedInstanceState)//2
97 | bindData()//3
98 | }
99 |
100 | /**
101 | * 主要是支持在java中使用,在kotlin中可用[lazyFragmentVM]
102 | */
103 | protected open fun initViewModel() {
104 |
105 | }
106 |
107 | /**
108 | * 建议:
109 | * 1.view初始化,比如是否开启下拉刷新
110 | * 2.初始数据加载执
111 | */
112 | abstract fun init(savedInstanceState: Bundle?)
113 |
114 | /**
115 | * 建议:
116 | * 订阅viewModel的数据并进行绑定
117 | */
118 | protected abstract fun bindData()
119 |
120 | /**
121 | * 加载任意值,跟随frag生命周期销毁、创建
122 | */
123 | fun lazyWithFragment(initializer: () -> T) =
124 | FragmentViewDelegate(this, initializer)
125 | }
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | xmlns:android
18 |
19 | ^$
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | xmlns:.*
29 |
30 | ^$
31 |
32 |
33 | BY_NAME
34 |
35 |
36 |
37 |
38 |
39 |
40 | .*:id
41 |
42 | http://schemas.android.com/apk/res/android
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | .*:name
52 |
53 | http://schemas.android.com/apk/res/android
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | name
63 |
64 | ^$
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | style
74 |
75 | ^$
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | .*
85 |
86 | ^$
87 |
88 |
89 | BY_NAME
90 |
91 |
92 |
93 |
94 |
95 |
96 | .*
97 |
98 | http://schemas.android.com/apk/res/android
99 |
100 |
101 | ANDROID_ATTRIBUTE_ORDER
102 |
103 |
104 |
105 |
106 |
107 |
108 | .*
109 |
110 | .*
111 |
112 |
113 | BY_NAME
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/fragment/BaseVisibilityFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.fragment
2 |
3 | import androidx.annotation.CallSuper
4 | import androidx.fragment.app.Fragment
5 | import androidx.fragment.app.InternalBasicFragment
6 |
7 | /**
8 | * 对 fragment 的状态管理进行优化,简化对fragment可见性的管理,尤其是在Fragment嵌套时的可见状态判断。
9 | * 简化为:首次显示,显示,隐藏。
10 | * create by zhusw on 5/19/21 09:44
11 | */
12 | abstract class BaseVisibilityFragment : InternalBasicFragment(), FragmentVisibilityChild {
13 | private var neverVisibleBefore = true
14 |
15 | private var _currentVisibleState = false
16 | override val currentVisibleState get() = _currentVisibleState
17 |
18 | /**
19 | * 根View是否已创建
20 | */
21 | protected fun viewCreated(): Boolean {
22 | return null != view
23 | }
24 |
25 | /**
26 | * fm 的 hide 跟 show 的回调,第一次不会调用
27 | */
28 | override fun onHiddenChanged(hidden: Boolean) {
29 | super.onHiddenChanged(hidden)
30 | checkVisibleChangeState(!hidden)
31 | }
32 |
33 | /**
34 | * viewPager的可见回调,在onCreate前,[setUserVisibleHint]太乱不适合锚点
35 | */
36 | override fun setMenuVisibility(menuVisible: Boolean) {
37 | super.setMenuVisibility(menuVisible)
38 | if (isFirstResumed) {
39 | checkVisibleChangeState(menuVisible)
40 | }
41 | }
42 |
43 | /** isVisible=$isVisible userVis=$userVisibleHint isAdded=$isAdded
44 | * isHidden=$isHidden
45 | * 在嵌套fragment 中,作为 child fragment 在被重建时 以上全部为true
46 | * 所以真实的状态需要参考父 fragment 是否可见
47 | **/
48 | @CallSuper
49 | protected open fun checkVisibleChangeState(changedVisibleState: Boolean) {
50 | //支持子fragment 完全跟随 父fragment 可见状态
51 | val realVisible = changedVisibleState && isAdded && parentIsVisible()
52 | //区分重复状态 与 用户可见性
53 | if (realVisible != _currentVisibleState) {
54 | _currentVisibleState = realVisible
55 | when (realVisible) {
56 | true -> {
57 | onVisible(neverVisibleBefore)
58 | neverVisibleBefore = false
59 | }
60 | false -> onHidden()
61 | }
62 | }
63 | //每次的parentFragment的变化都需要通知 childFragment
64 | childFragmentManager.fragments.forEach {
65 | when (it) {
66 | is FragmentVisibilityChild -> {
67 | it.onParentVisibleChanged(realVisible)
68 | }
69 | }
70 | }
71 | }
72 |
73 | override fun onParentVisibleChanged(parentIsVisible: Boolean) {
74 | //内部判断过了暂时不需要isParentVisible参数
75 | checkVisibleChangeState(
76 | if (isFragmentManager()) !isFmHidden else isVpVisible
77 | )
78 | }
79 |
80 | /**
81 | * parentFragment 也必须是[FragmentVisibilityChild] 才完全有效
82 | * 如果不是则只判断 isAdded
83 | * @return parentFragment是否可见
84 | *
85 | */
86 | protected fun parentIsVisible(): Boolean {
87 | return when (val pf: Fragment? = parentFragment) {
88 | null -> true
89 | is FragmentVisibilityChild -> {
90 | pf.currentVisibleState
91 | }
92 | else -> {
93 | pf.isAdded
94 | }
95 | }
96 | }
97 |
98 | override fun onResume() {
99 | super.onResume()
100 | checkVisibleChangeState(if (isFragmentManager()) !isFmHidden else isVpVisible)
101 | }
102 |
103 | override fun onPause() {
104 | super.onPause()
105 | if (currentVisibleState) {
106 | checkVisibleChangeState(false)
107 | }
108 | }
109 |
110 | override fun onClearVisibleState() {
111 | super.onClearVisibleState()
112 | _currentVisibleState = false
113 | neverVisibleBefore = true
114 | }
115 | }
116 |
117 | /**
118 | * 可见性状态变化接口,方便其他项目的fragment兼容实现
119 | */
120 | interface FragmentVisibilityChild {
121 | val currentVisibleState: Boolean
122 |
123 | fun onParentVisibleChanged(parentIsVisible: Boolean)
124 |
125 | /**
126 | * @param isFirstVisible 是是首次可见
127 | * 注意:这个布尔值是根据view算的(destroyView后再create就是first)
128 | */
129 | fun onVisible(isFirstVisible: Boolean) {
130 | }
131 |
132 | fun onHidden() {
133 | }
134 | }
--------------------------------------------------------------------------------
/baseAF/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.buildsrc.kts.Dependencies
2 | import com.buildsrc.kts.Publish
3 |
4 | plugins {
5 | id("com.android.library")
6 | id("kotlin-android")
7 | `maven-publish`
8 | }
9 |
10 | apply("../common.gradle")
11 |
12 | val versionTimestamp = com.buildsrc.kts.Publish.Version.getVersionTimestamp()
13 |
14 | android {
15 | buildTypes {
16 | getByName("release") {
17 | isMinifyEnabled = false
18 | proguardFiles(
19 | getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
20 | )
21 | }
22 | getByName("debug") {
23 | isMinifyEnabled = false
24 | // consumerProguardFiles("consumer-rules.pro") lib单独打包 此属性是无效的
25 | proguardFiles(
26 | getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
27 | )
28 | }
29 | }
30 | afterEvaluate {
31 | buildTypes.forEach {
32 | it.buildConfigField("Integer", "versionCode", Publish.Version.versionCode.toString())
33 | it.buildConfigField("String", "versionName", "\"${Publish.Version.versionName}\"")
34 | it.buildConfigField("String", "versionTimeStamp", "\"$versionTimestamp\"")
35 | }
36 | }
37 | sourceSets {
38 | getByName("main") {
39 | java {
40 | srcDirs("src/main/java")
41 | }
42 | }
43 | }
44 | kotlinOptions {
45 | jvmTarget = "11"
46 | //指定kotlin module 唯一标识,避免重复时报错
47 | freeCompilerArgs =
48 | freeCompilerArgs + arrayOf("-module-name", Publish.Maven.getFourPackage(projectDir))
49 |
50 | }
51 | buildFeatures {
52 | viewBinding = true
53 | }
54 | }
55 |
56 | dependencies {
57 | implementation(Dependencies.Kotlin.kotlin_stdlib)
58 | implementation(Dependencies.JetPack.core_ktx)
59 | implementation(Dependencies.AndroidX.appcompat)
60 | implementation(Dependencies.AndroidX.constraintLayout)
61 | implementation(Dependencies.Coroutines.coroutines_android)
62 | implementation(Dependencies.JetPack.lifecycle_viewModel_ktx)
63 | implementation(Dependencies.JetPack.fragment_ktx)
64 | implementation(Dependencies.Foundation.viewBindingHelper)
65 | }
66 |
67 |
68 | val sourceCodeTask: Jar = tasks.register("sourceCode", Jar::class.java) {
69 | from(android.sourceSets.getByName("main").java.srcDirs)
70 | classifier = "sources"
71 | }.get()
72 |
73 |
74 | tasks.register("createGitTagAndPush", Exec::class.java) {
75 | commandLine("git", "push", "origin", versionTimestamp)
76 | }.get().dependsOn(tasks.register("createGitTag", Exec::class.java) {
77 | commandLine("git", "tag", versionTimestamp, "-m", "autoCreateWithMavenPublish")
78 | })
79 |
80 | publishing {
81 | publications {
82 | create("tools") {
83 | groupId = Publish.Maven.getThreePackage(projectDir)
84 | artifactId = Publish.Version.artifactId
85 | version = Publish.Version.versionName
86 | artifact(sourceCodeTask)
87 | afterEvaluate {//在脚本读取完成后绑定
88 | val bundleReleaseAarTask: Task = tasks.getByName("bundleReleaseAar")
89 | bundleReleaseAarTask.finalizedBy("createGitTagAndPush")
90 | artifact(bundleReleaseAarTask)
91 | }
92 | // artifact("$buildDir/outputs/aar/loading-release.aar")//直接制定文件
93 | pom.withXml {
94 | val dependenciesNode = asNode().appendNode("dependencies")
95 | configurations.implementation.get().allDependencies.forEach {
96 | if (it.version != "unspecified" && it.name != "unspecified") {
97 | val depNode = dependenciesNode.appendNode("dependency")
98 | depNode.appendNode("groupId", it.group)
99 | depNode.appendNode("artifactId", it.name)
100 | depNode.appendNode("version", it.version)
101 | }
102 | }
103 | }
104 |
105 | }
106 | repositories {
107 | if (Publish.SNAPSHOT) {
108 | Publish.Maven.aliyunSnapshotRepositories(this)
109 | } else {
110 | Publish.Maven.aliyunReleaseRepositories(this)
111 | }
112 | }
113 | }
114 | }
115 | fun String.log(tag: String) {
116 | println("$tag:$this")
117 |
118 | }
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/utils/param/ParamsUtils.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.utils.param
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.os.Parcelable
7 | import android.util.Log
8 | import androidx.fragment.app.Fragment
9 | import com.foundation.app.arc.utils.ext.log
10 | import java.io.Serializable
11 | import java.lang.reflect.Field
12 |
13 | /**
14 | * create by zhusw on 5/17/21 16:32
15 | */
16 | object ParamsUtils {
17 | private val TAG = ParamsUtils::class.java.simpleName
18 |
19 | fun initWithActivity(activity: Activity, intent: Intent?) {
20 | intent?.extras?.let {
21 | initParams(activity, it)
22 | }
23 | }
24 |
25 | fun initWithFragment(fragment: Fragment) {
26 | fragment.arguments?.let {
27 | initParams(fragment, it)
28 | }
29 | }
30 |
31 | private fun initParams(obj: Any, bundle: Bundle) {
32 | val time = System.currentTimeMillis()
33 | val fields = obj.javaClass.declaredFields
34 | for (f in fields) {
35 | f.isAccessible
36 | if (f.isAnnotationPresent(BundleParams::class.java)) {
37 | f.getAnnotation(BundleParams::class.java)?.let {
38 | bind(it, f, obj, bundle)
39 | }
40 | }
41 | }
42 | "track time=${System.currentTimeMillis() - time}".log(TAG)
43 | }
44 |
45 | private fun bind(p: BundleParams, field: Field, obj: Any, bundle: Bundle) {
46 | val key: String = p.value.ifEmpty { field.name }
47 | "bind key=$key".log(TAG)
48 | if (bundle.containsKey(key)) {
49 | "bind type=${field.type.name}".log(TAG)
50 | val value: Any? = when (field.type) {
51 | String::class.java -> {
52 | bundle.getString(key, "")
53 | }
54 | Boolean::class.javaPrimitiveType -> {
55 | bundle.getBoolean(key, false)
56 | }
57 | Char::class.javaPrimitiveType -> {
58 | bundle.getChar(key, Char.MIN_VALUE)
59 | }
60 | Byte::class.javaPrimitiveType -> {
61 | bundle.getByte(key, 0)
62 | }
63 | Short::class.javaPrimitiveType -> {
64 | bundle.getShort(key, 0)
65 | }
66 | Int::class.javaPrimitiveType -> {
67 | bundle.getInt(key, 0)
68 | }
69 | Float::class.javaPrimitiveType -> {
70 | bundle.getFloat(key, 0F)
71 | }
72 | Long::class.javaPrimitiveType -> {
73 | bundle.getLong(key, 0L)
74 | }
75 | Double::class.javaPrimitiveType -> {
76 | bundle.getDouble(key, 0.0)
77 | }
78 | else -> when {
79 | Parcelable::class.java.isAssignableFrom(field.type) -> {
80 | /*
81 | * 不as的话智能推断无法识别,会被强转成Serializable导致崩溃
82 | * 这一问题已反馈给Kotlin团队,问题已被收录,最快将在1.7之后被修复
83 | */
84 | bundle.getParcelable(key) as? Parcelable
85 | }
86 | Serializable::class.java.isAssignableFrom(field.type) -> {
87 | if (!field.isAnnotationPresent(BundleParamsUseSerializable::class.java)) {
88 | throw IllegalArgumentException("not recommend use Serializable key:$key,you should use Parcelable or add @BundleParamsUseSerializable annotation")
89 | }
90 | bundle.getSerializable(key)
91 | }
92 | else -> {
93 | //理论上不可能走到这里
94 | throw IllegalArgumentException("unknown class type key:$key")
95 | }
96 | }
97 | }
98 | value?.let {
99 | "set value=$it".log(TAG)
100 | val accessible = field.isAccessible
101 | if (!accessible) field.isAccessible = true
102 | field.set(obj, it)
103 | if (!accessible) field.isAccessible = false
104 | }
105 | } else {
106 | Log.e(TAG, "bundle not contains this key=$key")
107 | }
108 | }
109 |
110 | }
--------------------------------------------------------------------------------
/simple/src/main/res/layout/activity_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
14 |
18 |
19 |
23 |
24 |
29 |
30 |
37 |
38 |
39 |
40 |
41 |
45 |
46 |
56 |
57 |
72 |
73 |
80 |
81 |
82 |
83 |
84 |
85 |
89 |
90 |
95 |
96 |
101 |
102 |
107 |
108 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/kcp/PermissionFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.kcp
2 |
3 | import android.annotation.TargetApi
4 | import android.content.Context
5 | import android.content.pm.PackageManager
6 | import android.os.Build
7 | import android.os.Bundle
8 | import android.util.SparseArray
9 | import androidx.core.app.ActivityCompat
10 | import androidx.fragment.app.Fragment
11 | import androidx.fragment.app.FragmentActivity
12 | import java.util.*
13 |
14 | /**
15 |
16 | *
17 | *
18 | *create by zhusw on 2020/11/19 17:48
19 | */
20 | internal class PermissionFragment : Fragment() {
21 | companion object {
22 | val TAG = PermissionFragment::class.java.name
23 | }
24 |
25 | private lateinit var hostActivity: FragmentActivity
26 | private val random: Random = Random()
27 | private val requestMap = SparseArray()
28 | override fun onAttach(context: Context) {
29 | super.onAttach(context)
30 | hostActivity = requireActivity()
31 | }
32 |
33 | override fun onCreate(savedInstanceState: Bundle?) {
34 | super.onCreate(savedInstanceState)
35 | retainInstance = true //activity 异常重建时 ,fragment不重建
36 | }
37 |
38 | override fun onRequestPermissionsResult(
39 | requestCode: Int,
40 | permissions: Array,
41 | grantResults: IntArray
42 | ) {
43 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
44 | val permissionRequestListener = requestMap.get(requestCode)
45 |
46 | try {
47 | requestMap.remove(requestCode)
48 | permissionRequestListener?.let {
49 | handlerPermissionResult(permissions, grantResults, permissionRequestListener)
50 | }
51 | } catch (e: Exception) {
52 | permissionRequestListener?.let {
53 | it.onError(e)
54 | }
55 | }
56 | }
57 |
58 | /**
59 | * 将结果进行分类传递
60 | */
61 | private fun handlerPermissionResult(
62 | permissions: Array,
63 | grantResults: IntArray, permissionCallback: PermissionCallback
64 | ) {
65 | if (!permissionCallback.canceled) {
66 | val rejectList = arrayListOf()
67 | val grantedList = arrayListOf()
68 | var allGrant = true
69 | for (index in grantResults.indices) {
70 | val pName = permissions[index]
71 | val grant = grantResults[index]
72 | val permission = Permission(pName)
73 | val accept = grant == PackageManager.PERMISSION_GRANTED
74 | when (grant) {
75 | PackageManager.PERMISSION_GRANTED -> {
76 | permission.accept = accept
77 | }
78 | else -> {
79 | permission.accept = false
80 | permission.shouldShowRequestPermissionRationale =
81 | ActivityCompat.shouldShowRequestPermissionRationale(hostActivity, pName)
82 | }
83 | }
84 | permissionCallback.eachResult(permission)
85 | if (permission.accept) {
86 | grantedList.add(permission)
87 | } else {
88 | rejectList.add(permission)
89 | allGrant = false
90 | }
91 | }
92 | if (allGrant) {
93 | permissionCallback.allGranted(grantedList)
94 | } else {
95 | permissionCallback.leastOneReject(grantedList, rejectList)
96 | }
97 | }
98 | permissionCallback.onComplete
99 | }
100 |
101 |
102 | @TargetApi(Build.VERSION_CODES.M)
103 | fun easyRequestPermissions(
104 | name: Array,
105 | permissionRequestCallBack: PermissionCallback
106 | ) {
107 | if (KCPermission.IS_BIG_THAN_SDK_M) {
108 | val requestCode = makeRequestCode()
109 | requestMap.put(requestCode, permissionRequestCallBack)
110 | requestPermissions(name, requestCode)
111 | }
112 | }
113 |
114 | private fun makeRequestCode(): Int {
115 | var newCode: Int
116 | var tryCount = 0
117 | do {
118 | newCode = random.nextInt(60000)//最大 65535
119 | tryCount++
120 | } while (requestMap.indexOfKey(newCode) >= 0 && tryCount < 10)
121 | return newCode
122 | }
123 |
124 | }
125 |
126 | class Permission(
127 | var name: String,
128 | var accept: Boolean,
129 | //是否可弹出权限申请窗口
130 | var shouldShowRequestPermissionRationale: Boolean = false
131 | ) {
132 | constructor(name: String) : this(name, false, false)
133 |
134 | override fun toString(): String {
135 | return "name=$name,accept=$accept,shouldShowWindow=$shouldShowRequestPermissionRationale"
136 | }
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/baseAF/src/main/java/com/foundation/app/arc/activity/BaseVMVBActivity.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.arc.activity
2 |
3 | import android.os.Bundle
4 | import androidx.annotation.MainThread
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.ViewModelProvider
7 | import androidx.lifecycle.ViewModelStoreOwner
8 | import androidx.viewbinding.ViewBinding
9 | import com.foundation.app.arc.fragment.BaseVMFragment
10 | import com.foundation.app.arc.utils.ext.AFViewModelLazy
11 | import com.foundation.app.arc.utils.ext.ActivityViewBindingDelegate
12 | import com.foundation.app.arc.utils.ext.lazyAtomic
13 | import com.foundation.widget.binding.ViewBindingHelper
14 |
15 | /**
16 | * ViewModel 创建与使用规范
17 | * ViewBinding 初始化与简化
18 | * create by zhusw on 5/18/21 15:05
19 | */
20 | abstract class BaseVMVBActivity : BaseParamsActivity() {
21 |
22 | val activityVMProvider by lazyAtomic {
23 | ViewModelProvider(this)
24 | }
25 | val applicationVMProvider by lazyAtomic {
26 | val app = application ?: throw IllegalStateException(
27 | "Activity 不可以在 onCreate 之前使用 ViewModel,因为此时 $this 还未绑定 application."
28 | )
29 | when (app) {
30 | is ViewModelStoreOwner -> {
31 | ViewModelProvider(
32 | app.viewModelStore,
33 | ViewModelProvider.AndroidViewModelFactory.getInstance(app)
34 | )
35 | }
36 | else -> {
37 | throw IllegalStateException(
38 | "application:$app 没实现" +
39 | " ViewModelStoreOwner,调用者是 Activity:$this "
40 | )
41 | }
42 | }
43 | }
44 |
45 | protected fun getActivityVM(clz: Class): VM {
46 | return activityVMProvider.get(clz)
47 | }
48 |
49 | protected fun getGlobalVM(clz: Class): VM {
50 | return applicationVMProvider.get(clz)
51 | }
52 |
53 | /**
54 | protected val testStr = ""
55 | protected inline fun testI() {
56 | val t = {
57 | testStr // protected val testStr = ""
58 | }
59 | t.invoke()
60 | }
61 | 以上代码,在跨module中的子类中访问,将会报错
62 | 如果inline 函数 内部使用了 lambda 或者匿名内部类,并在其中访问了外部类非public的成员
63 | 就会导致无法访问的异常(仅在小米k30 os 10 上不复现)
64 | */
65 | @MainThread
66 | inline fun lazyActivityVM(): Lazy {
67 | return AFViewModelLazy(VM::class) {
68 | activityVMProvider
69 | }
70 | }
71 |
72 | @MainThread
73 | inline fun lazyGlobalVM(): Lazy {
74 | return AFViewModelLazy(VM::class) {
75 | applicationVMProvider
76 | }
77 | }
78 |
79 | override fun onCreate(savedInstanceState: Bundle?) {
80 | val support = supportRebuildData()//1
81 | val state = if (support) savedInstanceState else null
82 | beforeSuperOnCreate(state)//2
83 | super.onCreate(state)//3
84 | afterSuperOnCreate(state)//4
85 | getContentVB()?.let { setContentView(it.root) }//5
86 | initViewModel()//6
87 | init(state)//7
88 | bindData()//8
89 | }
90 |
91 | /**
92 | * 是否支持activity被杀死后重建(是否使用 savedInstanceState中相关数据,
93 | * 系统默认在其中保存了Fragment的状态,重建会导致fragment异常展示)
94 | *
95 | * 注意:此处仅限于app杀死重建,对于fragment的内部的重建参考:[BaseVMFragment.onCreate]
96 | *
97 | * @return 默认不支持。如果返回true,则必须测试杀死后重建的流程
98 | */
99 | protected open fun supportRebuildData() = false
100 |
101 | /**
102 | * super.onCreate 之前回调
103 | * 支持一些特殊的窗口配置需要在onCreate之前设置
104 | */
105 | protected abstract fun beforeSuperOnCreate(savedInstanceState: Bundle?)
106 |
107 | /**
108 | * super.onCreate 之后回调
109 | */
110 | abstract fun afterSuperOnCreate(savedInstanceState: Bundle?)
111 |
112 | /**
113 | * 将ViewBinding.root 设置为根布局
114 | */
115 | @Deprecated(message = "不再需要实现 getContentVB", replaceWith = ReplaceWith("lazyAndSetRoot"))
116 | open fun getContentVB(): ViewBinding? = null
117 |
118 | /**
119 | * 主要是支持在java中使用,在kotlin中可用[lazyActivityVM]
120 | */
121 | protected abstract fun initViewModel()
122 |
123 | /**
124 | * 建议:
125 | * 1.view初始化,比如是否开启下拉刷新
126 | */
127 | protected abstract fun init(savedInstanceState: Bundle?)
128 |
129 | /**
130 | * 建议:
131 | * 订阅viewModel的数据并进行绑定
132 | * 调用数据加载
133 | */
134 | protected abstract fun bindData()
135 |
136 | @Deprecated(message = "不再需要实现", replaceWith = ReplaceWith("lazyAndSetRoot"))
137 | protected inline fun lazyVB() = lazyAtomic {
138 | ViewBindingHelper.getViewBindingInstanceByClass(
139 | VB::class.java,
140 | layoutInflater, null
141 | )
142 | }
143 |
144 | protected inline fun lazyAndSetRoot() =
145 | ActivityViewBindingDelegate(this) {
146 | ViewBindingHelper.getViewBindingInstanceByClass(
147 | VB::class.java,
148 | layoutInflater, null
149 | )
150 | }
151 | }
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/kcp/KPermission.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.kcp
2 |
3 | import android.annotation.TargetApi
4 | import android.content.Context
5 | import android.content.pm.PackageManager
6 | import android.os.Build
7 | import android.util.Log
8 | import androidx.core.content.ContextCompat
9 | import androidx.fragment.app.Fragment
10 | import androidx.fragment.app.FragmentActivity
11 | import androidx.fragment.app.FragmentManager
12 |
13 | val TAG = KCPermission::class.java.simpleName
14 |
15 | /**
16 | * 这是一个纯原生的权限请求
17 | * 借助透明Fragment实现,任意页面发起权限请求
18 | * 关于部分手机在主动关闭权限的情况下,走权限申请直接返回true ,适配范围过大,需自行处理
19 | *c reate by zhusw on 2020/11/20 09:54
20 | */
21 | @TargetApi(Build.VERSION_CODES.M)
22 | class KCPermission private constructor(private val builder: Builder) {
23 |
24 | private val permissionFragment: PermissionFragment by lazy {
25 | getPermissionFragment(builder.fragManager)
26 | }
27 |
28 | /*关于targetApi,是为在使用低版本编译时lint可以检查出不可用的方法,所以方法内部还是需要做版本判断的*/
29 | companion object {
30 | val IS_BIG_THAN_SDK_M = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
31 | fun prepare(): Builder {
32 | return Builder()
33 | }
34 |
35 | fun checkAll(context: Context, vararg perNames: String): ArrayList {
36 | val needRequestList = arrayListOf()
37 | if (IS_BIG_THAN_SDK_M) {
38 | perNames.forEach {
39 | val granted = ContextCompat.checkSelfPermission(
40 | context,
41 | it
42 | ) == PackageManager.PERMISSION_GRANTED
43 | if (!granted) {
44 | needRequestList.add(it)
45 | }
46 | }
47 | }
48 | return needRequestList
49 | }
50 |
51 | fun isGranted(context: Context, perName: String): Boolean {
52 | return if (IS_BIG_THAN_SDK_M) {
53 | ContextCompat.checkSelfPermission(
54 | context,
55 | perName
56 | ) == PackageManager.PERMISSION_GRANTED
57 | } else {
58 | true
59 | }
60 | }
61 |
62 | fun isRevoked(activity: FragmentActivity, permission: String): Boolean {
63 | return if (IS_BIG_THAN_SDK_M) {
64 | activity.packageManager.isPermissionRevokedByPolicy(
65 | permission,
66 | activity.packageName
67 | )
68 | } else {
69 | false
70 | }
71 | }
72 |
73 | }
74 |
75 | fun request(
76 | eachResult: (pre: Permission) -> Unit = {},
77 | allGranted: (granteds: List) -> Unit = {},
78 | /**
79 | * granteds :同意的授权
80 | * rejects :被拒绝的授权
81 | */
82 | leastOneReject: (granteds: List, rejects: List) -> Unit = { _: List, _: List -> },
83 | onCancel: () -> Unit = {},
84 | onError: (e: Throwable?) -> Unit = {},
85 | onComplete: () -> Unit = {}
86 | ): KCJob {
87 | val permissionCallback = PermissionCallback(
88 | eachResult,
89 | allGranted,
90 | leastOneReject,
91 | onCancel,
92 | onError,
93 | onComplete
94 | )
95 | permissionFragment.easyRequestPermissions(builder.permissionNames, permissionCallback)
96 | return permissionCallback
97 | }
98 |
99 |
100 | class Builder {
101 | lateinit var fragManager: FragmentManager
102 | private set
103 | lateinit var permissionNames: Array
104 | private set
105 |
106 | fun with(fragmentActivity: FragmentActivity): Builder {
107 | fragManager = fragmentActivity.supportFragmentManager
108 | return this
109 | }
110 |
111 | fun with(fragment: Fragment): Builder? {
112 | fragment.activity?.let {
113 | fragManager = it.supportFragmentManager
114 | return with@ this
115 | }
116 | Log.e(TAG, "invoke with(fragment) but it has no activity attached")
117 | return null
118 | }
119 |
120 | fun target(vararg pNames: String): Builder {
121 | permissionNames = pNames
122 | return this
123 | }
124 |
125 | /**
126 | * 暴露空返回,避免外部错误
127 | */
128 | fun build(): KCPermission? {
129 | if (null != fragManager && !permissionNames.isNullOrEmpty()) {
130 | return KCPermission(this@Builder)
131 | }
132 | Log.e(
133 | TAG,
134 | "miss fragManager or permissionNames is empty,fragManager=$fragManager permissionNames=$permissionNames"
135 | )
136 | return null
137 | }
138 | }
139 |
140 | private fun getPermissionFragment(fm: FragmentManager): PermissionFragment {
141 | var oldPf = fm.findFragmentByTag(TAG)
142 | if (null == oldPf) {
143 | oldPf = PermissionFragment()
144 | fm.beginTransaction()
145 | .add(oldPf, PermissionFragment.TAG)
146 | .commitNowAllowingStateLoss()
147 | }
148 | return oldPf as PermissionFragment
149 | }
150 | }
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/fragment/SkillListFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.fragment
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.view.View
7 | import com.foundation.app.simple.R
8 | import com.foundation.app.simple.VBIncludeTestActivity
9 | import com.foundation.app.simple.architecture.BaseFragmentWithLayoutId
10 | import com.foundation.app.simple.databinding.ActVbBinding
11 | import com.foundation.app.simple.demo.home.HomeActivity
12 | import com.foundation.app.simple.jump
13 | import com.foundation.app.simple.log
14 | import com.foundation.app.simple.ui.*
15 | import com.foundation.app.simple.ui.data.UserDesc
16 | import com.foundation.app.simple.ui.data.UserDescSerializable
17 |
18 | /**
19 | *create by zhusw on 5/19/21 13:32
20 | */
21 | class SkillListFragment : BaseFragmentWithLayoutId(R.layout.act_vb) {
22 |
23 | val actVbBinding by lazyVB()
24 |
25 | val a by lazy { }
26 |
27 | override fun onAttach(context: Context) {
28 | super.onAttach(context)
29 | "1 onAttach".log()
30 | // "onAttach onCreateView 前 不能访问 ${viewLifecycleOwner.lifecycle.currentState}".log()
31 | }
32 |
33 | override fun onCreate(savedInstanceState: Bundle?) {
34 | super.onCreate(savedInstanceState)
35 | "2 onCreate ".log()
36 | // "onCreate onCreateView 前 不能访问 ${viewLifecycleOwner.lifecycle.currentState}".log()
37 | }
38 |
39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
40 | super.onViewCreated(view, savedInstanceState)
41 | "4 onViewCreated Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log()//state INITIALIZED
42 | }
43 |
44 | override fun init(savedInstanceState: Bundle?) {
45 | "init savedInstanceState".log()
46 | actVbBinding.btnParamsTest.setOnClickListener {
47 | val intent = Intent(activity, UserInfoActivity::class.java)
48 | intent.putExtra("userId", 10003)
49 | intent.putExtra("userName", "王二")
50 | intent.putExtra("clsTest", SkillListFragment::class.java)
51 | startActivity(intent)
52 | }
53 | actVbBinding.btnParamsParcelableTest.setOnClickListener {
54 | ParcelableActivity.enter(requireActivity(), UserDesc("来自Parcelable 100", 100))
55 | }
56 | actVbBinding.btnParamsSerializableTest.setOnClickListener {
57 | SerializableActivity.enter(
58 | requireActivity(),
59 | UserDescSerializable("来自Serializable 200", 200)
60 | )
61 | }
62 | actVbBinding.btnViewPageVisible.setOnClickListener {
63 | jump(MultiFragmentVisibleTestActivity::class.java)
64 | }
65 | actVbBinding.btnVm.setOnClickListener {
66 | jump(HomeActivity::class.java)
67 | }
68 | actVbBinding.btnLd.setOnClickListener {
69 | jump(StickyLiveDataActivity::class.java)
70 | }
71 | actVbBinding.btnVbIncludeTest.setOnClickListener {
72 | jump(VBIncludeTestActivity::class.java)
73 | }
74 |
75 |
76 | }
77 |
78 | override fun bindData() {
79 | "init observeData".log()
80 | }
81 |
82 | override fun onActivityCreated(savedInstanceState: Bundle?) {
83 | super.onActivityCreated(savedInstanceState)
84 | "5 onActivityCreated Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log()//state INITIALIZED
85 |
86 | }
87 |
88 | override fun onStart() {
89 | super.onStart()
90 | "6 onStart Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log() //state CREATED
91 |
92 | }
93 |
94 | override fun onResume() {
95 | super.onResume()
96 | "7 onResume Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log()//state STARTED
97 | }
98 |
99 | override fun onPause() {
100 | super.onPause()
101 | "onPause Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log() //state STARTED
102 |
103 | }
104 |
105 | override fun onStop() {
106 | super.onStop()
107 | "onStop Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log() //state CREATED
108 | }
109 |
110 | override fun onViewBindingDestroy() {
111 | super.onViewBindingDestroy()
112 | "onViewBindingDestroy Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log()
113 | }
114 |
115 | //viewBinding 已销毁
116 | override fun onDestroyView() {
117 | super.onDestroyView()
118 | "onDestroyView Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log()
119 | // "onDestroyView actVbBinding = ${actVbBinding.btn.text}".log("")
120 |
121 | }
122 |
123 | //view已销毁
124 | override fun onDestroy() { // Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView()
125 | super.onDestroy()
126 | "onDestroy Fragment".log()
127 | // "onDestroy Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log()
128 | }
129 |
130 | override fun onDetach() {// Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView()
131 | super.onDetach()
132 | "onDetach Fragment ".log()
133 | // "onDetach Fragment state ${viewLifecycleOwner.lifecycle.currentState}".log()
134 | }
135 |
136 |
137 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 | 按照分层架构与数据驱动为架构核心封装的脚手架,可快速搭建出基于Google最新架构建议的项目框架。
3 |
4 | 从View的角色去看待Activity与Fragment,在使用场景上它们承担的职责基本一致即:接收数据>展示数据
5 | 所以为这两者提供高度一致性的API,统一抽象为 init,与 bindData 两个方法。并提供对VB 与 VM的自动初始化
6 |
7 | 核心功能:
8 |
9 | * 1.明确View层在架构中的职责边界,规范初始化方法与简化生命周期
10 | * 2.支持参数注解,自动初始化
11 | * 3.ViewBinding 一行代码快速初始化(fragment 中自动跟随声命周期)
12 | * 4.ViewModel 作用域管理、一行代码快速初始化
13 | * 5.Fragment 可见状态简化管理
14 | * 6.LiveData 支持无粘性消息
15 | * 7.Fragment 快速切换(防重叠)
16 |
17 | ## 更新日志:
18 |
19 | ## 引用:
20 |
21 | ```kotlin
22 | implementation("com.foundation.app:activity-fragment:1.1.1")
23 | ```
24 |
25 | # API
26 |
27 | ## 类图:
28 |
29 | 
30 |
31 | ## 1.明确View层在架构中的职责边界,规范初始化方法与生命周期
32 |
33 | * Base层对初始化方法的编排顺序
34 |
35 | ```kotlin
36 | override fun onCreate(savedInstanceState: Bundle?) {
37 | val support = supportRebuildData()//1
38 | val state = if (support) savedInstanceState else null
39 | beforeSuperOnCreate(state)//2
40 | super.onCreate(state)//3
41 | afterSuperOnCreate(state)//4
42 | getContentVB()?.let { setContentView(it.root) }//5
43 | initViewModel()//6
44 | init(state)//7
45 | bindData()//8
46 | }
47 | ```
48 |
49 | * Fragment 初始化方法顺序
50 |
51 | ```kotlin
52 | override fun onCreate(savedInstanceState: Bundle?) {
53 | super.onCreate(savedInstanceState)
54 | initViewModel()//1
55 | }
56 |
57 | @CallSuper
58 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
59 | super.onViewCreated(view, savedInstanceState)
60 | init(savedInstanceState)//2
61 | bindData()//3
62 | }
63 | ```
64 |
65 | ## 2.参数注解:BundleParams
66 | 是否兼容JAVA:是
67 |
68 | ### 2.1声明绑定
69 |
70 | ```kotlin
71 | @BundleParams("userName")
72 | private String userName = "没自动赋值";
73 |
74 | @BundleParams("userDesc")
75 | private val userDesc: UserDesc = UserDesc()
76 |
77 | @BundleParamsUseSerializable
78 | @BundleParams("userDesc")
79 | private val userDesc: UserDescSerializable = UserDescSerializable()
80 | ```
81 |
82 | ### 2.2手动使用:
83 |
84 | ```kotlin
85 | ParamsUtils.initWithActivity()
86 | ParamsUtils.initWithFragment()
87 | ```
88 |
89 | ### 2.3 单页面降级取消使用
90 |
91 | ```kotlin
92 | openAutoBindParams(): Boolean = false
93 | ```
94 |
95 | Tips:`onNewIntent`中不会进行重新绑定
96 |
97 | ## 3.ViewBinding 自动初始化
98 |
99 | ### 3.1 快速初始化 方式一
100 | 是否兼容JAVA:否
101 |
102 | 参考了默认fragment支持的自动根据布局id创建出根View,耦合性更低。
103 | 语意也更清晰,满足所见即所得。
104 |
105 | ```kotlin
106 | class UserInfoActivity : BaseActivity() {
107 | private val vbBinding by lazyAndSetRoot()
108 | }
109 |
110 | class SkillListFragment : BaseViewBindingFragment(R.layout.act_vb) {
111 |
112 | val actVbBinding by lazyVB()
113 |
114 | fun bindData() {
115 | actVbBinding.yourView
116 | }
117 | }
118 | ```
119 |
120 | ### 3.2 快速初始化 方式二 (不建议)
121 | 是否兼容JAVA:是
122 |
123 | 完全使用反射初始化,耦合性较高,且无法脱离范型限制。
124 |
125 | ```kotlin
126 | class UserInfoFragment : BaseFragment() {
127 |
128 | fun bindData() {
129 | viewBinding.yourView
130 | //在java中使用
131 | jViewBinding.yourView
132 | }
133 | }
134 | ```
135 |
136 | ### 3.3 Fragment 中ViewBinding的生命周期回调
137 |
138 | ```kotlin
139 | fun onViewBindingDestroy()
140 | ```
141 | 此函数用于做一些跟view相关的释放工作。
142 |
143 | Tips:如果在`onDestoryView`中访问 `XXbinding`将得到NPE,因为这是完全不被允许的,详情可参考google 文档建议
144 |
145 | ## 4.ViewModel 作用域管理、快速初始化
146 |
147 | ### 4.1 使用懒加载获取 (建议)
148 | 是否支持JAVA: 否
149 |
150 | ```kotlin
151 | private val homeVM by lazyActivityVM()
152 | ```
153 | ### 4.2 使用方法获取
154 | 是否支持JAVA: 是
155 |
156 | ```kotlin
157 | private lateinit var homeVM:HomeVM
158 |
159 | override fun initViewModel() {
160 | super.initViewModel()
161 | homeVM = getActivityVM(HomeVM::class.java)
162 | }
163 | ```
164 |
165 | ### 4.3 获取不同作用域的ViewModel的函数
166 |
167 | * Activity 两个
168 |
169 | ```kotlin
170 | getActivityVM(clz: Class): VM
171 |
172 | lazyActivityVM(): Lazy
173 | ```
174 | ```kotlin
175 | getGlobalVM(clz: Class): VM
176 |
177 | lazyGlobalVM(): Lazy
178 | ```
179 | * Fragment
180 |
181 | ```kotlin
182 | getFragmentVM(clz: Class): VM
183 |
184 | lazyFragmentVM(): Lazy
185 | ```
186 |
187 | ```kotlin
188 | getActivityVM(clz: Class): VM
189 |
190 | lazyActivityVM(): Lazy
191 | ```
192 | ```kotlin
193 | getGlobalVM(clz: Class): VM
194 |
195 | lazyGlobalVM(): Lazy
196 | ```
197 |
198 | ## 5.Fragment 可见状态完全控制
199 | Tips:使用时对于可见状态完全依赖于底层框架的API,避免使用`onResume` ,`onPause`这两个生命周期,否则将得到不可预期的状态
200 |
201 | 可见时回调
202 |
203 | ```kotlin
204 | abstract fun onVisible(isFirst: Boolean)
205 | ```
206 | 不可见时回调
207 |
208 | ```kotlin
209 | abstract fun onHidden()
210 | ```
211 |
212 | ## 6.LiveData 支持无粘性消息
213 |
214 | kotlin 中使用
215 |
216 | ```kotlin
217 | yourLiveData.observerStickyLess(this) {}
218 | ```
219 |
220 | java 中使用
221 |
222 | ```kotlin
223 | LiveDataExt.observerStickyLess(yourLiveData,this,it->{});
224 | ```
225 |
226 | ## 7.Fragment 快速切换(防重叠)
227 |
228 | Activity 与 fragment中的API 相同
229 |
230 | ```kotlin
231 | fun switchFragment(
232 | @NonNull currentFragment: Fragment,
233 | @IdRes frameLayoutId: Int = 0,
234 | tag: String? = null
235 | )
236 |
237 | fun switchFragment(tag: String, @IdRes frameLayoutId: Int = 0): Fragment?
238 | ```
239 |
240 | Tips:默认关闭了Activity重建
241 |
242 | ```kotlin
243 | /**
244 | * 是否支持activity被杀死后重建(是否使用 savedInstanceState中相关数据,
245 | * 系统默认在其中保存了Fragment的状态,重建会导致fragment异常展示)
246 | *
247 | * @return 默认不支持。如果返回true,则必须测试杀死后重建的流程
248 | */
249 | protected open fun supportRebuildData() = false
250 | ```
--------------------------------------------------------------------------------
/simple/src/main/java/com/foundation/app/simple/ui/fragment/visible/AbstractVisibleTestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.foundation.app.simple.ui.fragment.visible
2 |
3 | import android.os.Bundle
4 | import android.os.Handler
5 | import android.os.Looper
6 | import android.view.View
7 | import android.widget.TextView
8 | import androidx.annotation.LayoutRes
9 | import androidx.fragment.app.Fragment
10 | import com.foundation.app.arc.utils.ext.lazyOnUI
11 | import com.foundation.app.simple.architecture.BaseFragmentWithLayoutId
12 | import com.foundation.app.simple.jump
13 | import com.foundation.app.simple.log
14 | import com.foundation.app.simple.ui.EmptyActivity
15 |
16 | /**
17 | * visible测试基类
18 | * create by zhusw on 5/19/21 14:33
19 | */
20 | abstract class AbstractVisibleTestFragment(@LayoutRes id: Int) : BaseFragmentWithLayoutId(id) {
21 |
22 | companion object {
23 | const val TYPE_VP_PAGER_HINT_ALL = "PagerHintAll"
24 | const val TYPE_VP_PAGER_HINT_ONLY = "PagerHintOnly"
25 | const val TYPE_VP_STATE_HINT_ALL = "StateHintAll"
26 | const val TYPE_VP_STATE_HINT_ONLY = "StateHintOnly"
27 | const val TYPE_VP2 = "VP2"
28 | const val TYPE_FRAGMENT_MANAGER = "FM"
29 | const val TYPE_EMPTY = "Empty"
30 |
31 | fun createNextFrags(
32 | nextDeep: Int,
33 | deepDesc: String,
34 | ): List {
35 | fun Fragment.putArg(fragType: String): Fragment {
36 | arguments = Bundle().apply {
37 | putInt("deep", nextDeep)
38 | putString("deepDesc", deepDesc)
39 | putString("fragType", fragType)
40 | }
41 | return this
42 | }
43 | return when (nextDeep) {
44 | 0, 1, 2 -> {
45 | listOf(
46 | ViewPagerVisibleTestFragment().putArg(TYPE_VP_PAGER_HINT_ONLY),
47 | ViewPagerVisibleTestFragment().putArg(TYPE_VP_PAGER_HINT_ALL),
48 | ViewPagerVisibleTestFragment().putArg(TYPE_VP_STATE_HINT_ALL),
49 | ViewPagerVisibleTestFragment().putArg(TYPE_VP_STATE_HINT_ONLY),
50 | ViewPager2VisibleTestFragment().putArg(TYPE_VP2),
51 | FragmentManagerVisibleTestFragment().putArg(TYPE_FRAGMENT_MANAGER),
52 | )
53 | }
54 | else -> {
55 | listOf(
56 | EmptyVisibleTestFragment().putArg(TYPE_EMPTY),
57 | )
58 | }
59 | }
60 | }
61 | }
62 |
63 | private fun String.vLog(level: Int = 0) {
64 | when (level) {
65 | /*0, 8,*/ 9 -> {
66 | "${deepDesc}--$fragType($id)::$this".log("frag visible")
67 | }
68 | }
69 | }
70 |
71 | init {
72 | Handler(Looper.getMainLooper()).post {
73 | "new".vLog(8)//1
74 | }
75 | }
76 |
77 | protected val deep by lazyOnUI {
78 | arguments?.getInt("deep")!!
79 | }
80 |
81 | protected val deepDesc by lazyOnUI {
82 | arguments?.getString("deepDesc")!!
83 | }
84 |
85 | protected val fragType by lazyOnUI {
86 | arguments?.getString("fragType")!!
87 | }
88 |
89 | private val id get() = Integer.toHexString(System.identityHashCode(this))
90 |
91 | override fun onVisible(isFirstVisible: Boolean) {
92 | super.onVisible(isFirstVisible)
93 | "onVisible isFirst=$isFirstVisible".vLog(9)//1
94 | }
95 |
96 | override fun onHidden() {
97 | super.onHidden()
98 | "onHidden".vLog(9)//3
99 | }
100 |
101 | override fun setUserVisibleHint(isVisibleToUser: Boolean) {
102 | super.setUserVisibleHint(isVisibleToUser)
103 | "setUserVisibleHint=$isVisibleToUser".vLog(0)
104 | }
105 |
106 | override fun onHiddenChanged(hidden: Boolean) {
107 | super.onHiddenChanged(hidden)
108 | "onHiddenChanged=$hidden".vLog(0)
109 | }
110 |
111 | override fun setMenuVisibility(menuVisible: Boolean) {
112 | super.setMenuVisibility(menuVisible)
113 | "setMenuVisibility=$menuVisible".vLog(0)
114 | }
115 |
116 | override fun onCreate(savedInstanceState: Bundle?) {
117 | super.onCreate(savedInstanceState)
118 | "onCreate ".vLog(8)//2
119 | }
120 |
121 | override fun onDestroyView() {
122 | super.onDestroyView()
123 | "onDestroyView".vLog(8)//2
124 | }
125 |
126 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
127 | super.onViewCreated(view, savedInstanceState)
128 | "onViewCreated".vLog(8)//2
129 | }
130 |
131 | override fun onResume() {
132 | super.onResume()
133 | "onResume".vLog(8)//2
134 | }
135 |
136 | override fun onPause() {
137 | super.onPause()
138 | "onPause".vLog(8)//4
139 | }
140 |
141 | override fun onDestroy() {
142 | super.onDestroy()
143 | "onDestroy".vLog(8)//4
144 | }
145 |
146 | protected val frags by lazyOnUI {
147 | createNextFrags(deep + 1, "$deepDesc>$fragType")
148 | }
149 |
150 | protected var currentIndex: Int = 0
151 |
152 | override fun bindData() {
153 | super.bindData()
154 |
155 | getBtnOpenNewPage()?.setOnClickListener {
156 | jump(EmptyActivity::class.java)
157 | }
158 | getTvFragmentTitle()?.text =
159 | "层级${deep},${deepDesc}--$fragType($id)"
160 | getBtnClickSwitch()?.setOnClickListener {
161 | currentIndex = (currentIndex + 1) % frags.size
162 | onSwitchFragment(currentIndex)
163 | }
164 | }
165 |
166 | abstract fun onSwitchFragment(index: Int)
167 |
168 | abstract fun getBtnOpenNewPage(): View?
169 | abstract fun getTvFragmentTitle(): TextView?
170 | abstract fun getBtnClickSwitch(): View?
171 | }
172 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.* > \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/simple/src/main/res/drawable/ic_loading_svg.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
12 |
15 |
16 |
17 |
20 |
21 |
26 |
27 |
28 |
33 |
34 |
39 |
44 |
45 |
50 |
51 |
56 |
57 |
58 |
--------------------------------------------------------------------------------