{"id":20321894,"url":"https://github.com/javakam/andotemplate","last_synced_at":"2025-07-08T11:42:25.206Z","repository":{"id":57720383,"uuid":"344386748","full_name":"javakam/AndoTemplate","owner":"javakam","description":"Android 开发模板项目","archived":false,"fork":false,"pushed_at":"2023-08-31T09:14:02.000Z","size":2930,"stargazers_count":14,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-25T15:04:28.373Z","etag":null,"topics":["androidtemplate","template-project"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/javakam.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-03-04T07:26:56.000Z","updated_at":"2024-11-23T11:16:41.000Z","dependencies_parsed_at":"2022-08-27T19:11:13.875Z","dependency_job_id":null,"html_url":"https://github.com/javakam/AndoTemplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/javakam%2FAndoTemplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/javakam%2FAndoTemplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/javakam%2FAndoTemplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/javakam%2FAndoTemplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/javakam","download_url":"https://codeload.github.com/javakam/AndoTemplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248465344,"owners_count":21108244,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["androidtemplate","template-project"],"created_at":"2024-11-14T19:17:46.594Z","updated_at":"2025-04-11T19:10:52.527Z","avatar_url":"https://github.com/javakam.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ando Template\n\n\u003e **Ando 项目汇总**👉\u003chttps://juejin.cn/post/6934981195583356965/\u003e\n\n## 预览\n\n\u003cimg src=\"https://raw.githubusercontent.com/javakam/AndoTemplate/master/screenshot/Screenshot_20230831_165617.png\" width=\"320\" height=\"620\"/\u003e\n\n## 全部导入\n\n\u003e 版本号位于 config.gradle\n\n```groovy\n// https://github.com/javakam/DialogManager\nimplementation 'com.github.javakam:dialog.core:x.0.0@aar'\nimplementation 'com.github.javakam:dialog.usage:x.0.0@aar'\nimplementation 'com.github.javakam:dialog.bottomsheet:x.0.0@aar'\n\n// https://github.com/javakam/FileOperator\nimplementation 'com.github.javakam:file.core:x.0.0@aar'\nimplementation 'com.github.javakam:file.selector:x.0.0@aar'\nimplementation 'com.github.javakam:file.compressor:x.0.0@aar'\nimplementation 'com.github.javakam:file.android-q:x.0.0@aar'\nimplementation 'androidx.documentfile:documentfile:1.0.1'\n\n// https://github.com/javakam/PickerView\nimplementation 'com.github.javakam:widget.pickerview:x.0.0@aar'\n\nimplementation 'com.github.javakam:library:x.0.0@aar'\nimplementation 'com.github.javakam:toolkit:x.0.0@aar'\nimplementation 'com.github.javakam:widget.banner:x.0.0@aar'\nimplementation 'com.github.javakam:widget.indicator.core:x.0.0@aar'\nimplementation 'com.github.javakam:widget.indicator.usage:x.0.0@aar'\nimplementation 'com.github.javakam:webview:x.0.0@aar'\n```\n\n## ando_library/ando_toolkit\n\n```\nimplementation 'ando.library:library:x.0.0'\nimplementation 'ando.toolkit:toolkit:x.0.0'\n```\n\n### `Library`(ando_library)\n\n#### 1.导入\n\n```groovy\nimplementation 'com.github.javakam:library:x.0.0@aar'\n```\n\n#### 2.内容\n\n##### 基类(`base`) BaseActivity/BaseFragment/BaseApplication 等\n\n##### 图片(`glide`) GlideUtils\n\n##### 工具类(`utils`)\n\n```kotlin\n网络工具\n顺序执行异步任务\nBase64Utils\nClearCacheUtils\nCrashHandler\nGsonUtils\n```\n\n##### 组件(`widget`)\n\n```kotlin\n透明处理(alpha)\ndatetime\n悬浮窗(float)\n圆角ImageView(RoundImageView)\n状态视图(loader)\nRecycleView(BaseQuickAdapter / BaseViewHolder / RecyclerDecorationProvider)\n阴影视图(ShadowDrawable)\n开关视图(SwitchButton)\n灰色应用(GrayFrameLayout)\n水波纹视图(RippleView)\n多属性自定义Button(SuperButton)\n```\n\n##### 音视频框架\n\nhttps://github.com/yangjie10930/EpMedia\n\n##### 顺序执行异步任务\n\n参考项目: https://github.com/ddnosh/android-tiny-task\n\n```kotlin\nprivate var count: Int = 0\nprivate fun test() {\n    count = 0\n    val task1 = object : BaseSyncTask() {\n        override fun doTask() {\n            count++\n            Log.i(\"2333\", \"task1... $count\")\n            TinySyncExecutor.getInstance().finish()\n        }\n    }\n    val task2 = object : BaseSyncTask() {\n        override fun doTask() {\n            count++\n            Log.e(\"2333\", \"task2... $count\")\n            TinySyncExecutor.getInstance().finish()\n        }\n    }\n    val task3 = object : BaseSyncTask() {\n        override fun doTask() {\n            count++\n            Log.w(\"2333\", \"task3... $count\")\n            TinySyncExecutor.getInstance().finish()\n        }\n    }\n    TinySyncExecutor.getInstance().enqueue(task1)\n    TinySyncExecutor.getInstance().enqueue(task3)\n    TinySyncExecutor.getInstance().enqueue(task2)\n}\n```\n\n##### SwitchButton\n\nhttps://github.com/kyleduo/SwitchButton\n\n### `ToolKit`(ando_toolkit)\n\n工具类集合, 还包括常用`Kotlin`扩展函数文件\n\n```kotlin\nimplementation 'com.github.javakam:toolkit:x.0.0@aar'\n```\n\neg:\n\n```kotlin\nfun View.invisible() {\n    this.run {\n        if (isVisible) visibility = View.INVISIBLE\n    }\n}\n\nfun View.gone() {\n    this.run {\n        if (isVisible) visibility = View.GONE\n    }\n}\n\nfun View.noShake(interval: Long = 500L, block: (v: View) -\u003e Unit) {\n    this.apply {\n        setOnClickListener(object : NoShakeClickListener(interval) {\n            override fun onSingleClick(v: View) {\n                block.invoke(v)\n            }\n        })\n    }\n}\n```\n\n### `Banner`(ando_ui_banner)\n\n#### 1.导入\n\n```groovy\nimplementation 'com.github.javakam:widget.banner:x.0.0@aar'\n```\n\n#### 2.不结合`RecyclerView`使用\n\n```kotlin\nval banner = rootView.findViewById\u003cImageBanner\u003e(R.id.bannerImage)\nbanner.setSource(mBannerData)\nbanner.imageLoader = BannerImageLoader()\nbanner.colorDrawable = ColorDrawable(ContextCompat.getColor(requireActivity(), R.color.transparent))\nbanner.setOnItemClickListener {\n    toastShort(\"Index : $it\")\n}\nbanner.startScroll()\n\nclass BannerImageLoader : IBannerImageLoadStrategy {\n    void loadImage(@NonNull ImageView imageView, Object path);\n    ...\n}\n\nval mBannerData: List\u003cBannerItem\u003e = mutableListOf(\n    BannerItem(\n        \"推荐\",\n        \"http://pic.ntimg.cn/20130129/11507979_020415120167_2.jpg\"\n    ),\n    BannerItem(\n        \"热点\",\n        \"http://pic.ntimg.cn/file/20210320/29633157_104445000089_2.jpg\"\n    ),\n    BannerItem(\n        \"动态\",\n        \"http://pic.ntimg.cn/20130224/11507979_230737207196_2.jpg\"\n    ),\n)\n```\n\n🌴配合`Indicator`一起使用\n\n```kotlin\nval indicator = headerView.findViewById\u003cMagicIndicator\u003e(R.id.indicator)\nval roundNavigator = RoundRectNavigator(this)\nroundNavigator.isFollowTouch = true //是否跟随手指滑动\nroundNavigator.totalCount = mBannerData.size\nroundNavigator.itemColor = Color.LTGRAY\nroundNavigator.indicatorColor = ContextCompat.getColor(this, R.color.color_main_blue)\nroundNavigator.setItemWidth(18.0)\nroundNavigator.setItemSpacing(4.0)\nroundNavigator.setItemHeight(4.0)\nroundNavigator.setItemRadius(4.0)\nroundNavigator.setOnItemClickListener { index -\u003e banner.viewPager?.currentItem = index }\n//roundNavigator.notifyDataSetChanged()\nindicator.navigator = roundNavigator\n//Banner 和 Indicator 绑定到一起, 同步滑动\nbanner.viewPager?.apply { ViewPagerHelper.bind(indicator, this) }\n```\n\n#### 3.结合`RecyclerView`使用\n\n```java\n@Override\npublic void onViewDetachedFromWindow(BaseViewHolder holder){\nfinal SimpleImageBanner banner=holder.getView(R.id.bannerImage);\n        if(banner!=null){\n        banner.pauseScroll();\n        }\n        super.onViewDetachedFromWindow(holder);\n        }\n\n@Override\npublic void onViewAttachedToWindow(@NotNull BaseViewHolder holder){\n        super.onViewAttachedToWindow(holder);\nfinal SimpleImageBanner banner=holder.getView(R.id.bannerImage);\n        if(banner!=null){\n        banner.startScroll();\n        }\n        }\n```\n\n#### 4.制作引导页面\n\n```kotlin\nval guideBanner = GuideBanner(this).apply {\n    layoutParams = FrameLayout.LayoutParams(\n        FrameLayout.LayoutParams.MATCH_PARENT,\n        FrameLayout.LayoutParams.MATCH_PARENT\n    )\n    //动画\n    //viewPager.setPageTransformer(true, FadeSlideTransformer())\n    imageLoader = BannerImageLoader()\n    //图片\n    setSource(\n        mutableListOf\u003cAny\u003e(\n            //Remote\n            //\"http://pic.ntimg.cn/20130129/11507979_020415120167_2.jpg\",\n            //\"http://pic.ntimg.cn/file/20210320/29633157_104445000089_2.jpg\",\n            //\"http://pic.ntimg.cn/20130224/11507979_230737207196_2.jpg\",\n            //Local\n            R.mipmap.pic1,\n            R.mipmap.pic2,\n            R.mipmap.pic3,\n        )\n    )\n    startScroll()\n    setOnJumpClickListener {\n        toastShort(\"jump\")\n    }\n}\n```\n\n#### 5.参考项目: \u003chttps://github.com/H07000223/FlycoBanner_Master\u003e\n\n### `Indicator`(ando_ui_indicator)\n\n导入\n\n```groovy\nimplementation 'com.github.javakam:widget.indicator.core:x.0.0@aar'\nimplementation 'com.github.javakam:widget.indicator.usage:x.0.0@aar'\n```\n\n`ando_ui_indicator_usage`为一些做好的控件类型\n\n🌴比`Google TabLayout`好用的方案, 参考自 \u003chttps://github.com/hackware1993/MagicIndicator\u003e\n\n### CoordinatorLayout\n\n- 🍎注意: ViewPager中必须有可以滑动的组件如:RecycleView或者ScrollView等, 才能实现效果\n\n- 用 CoordinatorLayout 处理滚动 👉 https://juejin.cn/post/6844903519598690311\n\n    - 对应源码 👉 https://github.com/chrisbanes/cheesesquare\n\n- 动图展示app:layout_scrollFlags的5种滑动属性 👉 https://blog.csdn.net/LosingCarryJie/article/details/78917423\n\n### 沉浸式状态栏\n\nhttps://juejin.cn/post/6844903518982111245\n\n### RecyclerView\n\nRecyclerViewItemDecoration From https://github.com/zyyoona7/RecyclerViewItemDecoration\n\n### Manage all files on a storage device\n\nhttps://developer.android.com/training/data-storage/manage-all-files\n\n```xml\nandroid:requestLegacyExternalStorage=\"true\"\n```\n\n## 文件File \u0026 WebView\n\n```gradle\napi 'androidx.documentfile:documentfile:1.0.1'\n//api 'com.ando.file:FileOperator:0.9.3-beta1'\napi 'ando.file:core:1.2.0'         //核心库必选\napi 'ando.file:android-q:1.2.0'    //AndroidQ \u0026 Android 11 兼容库\napi 'ando.file:compressor:1.2.0'   //图片压缩,核心算法采用 Luban\napi 'ando.file:selector:1.2.0'     //文件选择器\n\napi 'ando.webview:webview:1.0.0'\napi 'com.ando.string:StringExpandUtils:1.0.0'\n```\n\n## 网络框架(Retrofit \u0026 LiveData)\n\nLiveData Adapter for Retrofit\n\nhttps://gist.github.com/AkshayChordiya/15cfe7ca1842d6b959e77c04a073a98f\n\nhttps://github.com/googlesamples/android-architecture-components/tree/master/GithubBrowserSample\n\n## MVVM + Hilt\n\nhttps://itnext.io/android-architecture-hilt-mvvm-kotlin-coroutines-live-data-room-and-retrofit-ft-8b746cab4a06\n\nhttps://github.com/sdwfqin/AndroidQuick/tree/4.x/app-kt\n\n## androidx Fragment 懒加载\n\nhttps://juejin.im/post/6844904050698223624\n\n## Glide-KTX\n\nhttps://github.com/champChayangkoon/Glide-KTX\n\n## GreenDao Gradle\n\n```\ngradlew.bat greendao --warning-mode all --stacktrace\n```\n\n## BottomNavigationView show/hide 而不是 replace\n\nhttps://stackoverflow.com/questions/54087740/how-to-hide-bottomnavigationview-on-android-navigation-lib\n\n\u003e You could do something like this in your activity's onCreate. When ever an item in the nav bar is selected it will show or hide the nav based on the fragment id's.\n\n```kotlin\nprivate fun setupNav() {\n    val navController = findNavController(R.id.nav_host_fragment)\n    findViewById\u003cBottomNavigationView\u003e(R.id.bottomNav)\n        .setupWithNavController(navController)\n\n    navController.addOnDestinationChangedListener { _, destination, _ -\u003e\n        when (destination.id) {\n            R.id.mainFragment -\u003e showBottomNav()\n            R.id.mineFragment -\u003e showBottomNav()\n            else -\u003e hideBottomNav()\n        }\n    }\n}\n\nprivate fun showBottomNav() {\n    bottomNav.visibility = View.VISIBLE\n}\n\nprivate fun hideBottomNav() {\n    bottomNav.visibility = View.GONE\n}\n```\n\n## 混淆\n\n## DataBinding 在XML中的具体使用方式\n\n```xml\n\n\u003candroidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id=\"@+id/swipeRefresh\"\n    bind:colorSchemeResources=\"@{resId}\" bind:onRefreshListener=\"@{() -\u003e viewModel.onRefresh()}\"\n    bind:refreshing=\"@{viewModel.refreshing}\" android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\u003e\u003c/androidx.swiperefreshlayout.widget.SwipeRefreshLayout\u003e\n\n\u003cTextView android:id=\"@+id/tv_article_tabs\" android:layout_width=\"match_parent\"\nandroid:layout_height=\"wrap_content\"\nandroid:text='@{\"复杂的表达式显示结果  \"+@string/app_name+\" -\u003e \"  + viewModel.wxArticleTabs.size()}' /\u003e\n\n\u003cImageView android:id=\"@+id/iv_article_tabs_bind\" loadPic=\"@{viewModel.tempImageUrl}\"\nandroid:layout_width=\"35dp\" android:layout_height=\"35dp\" android:layout_marginTop=\"3dp\"\nandroid:scaleType=\"centerCrop\" /\u003e\n\n```\n\n## 添加矢量图SVG\n\nhttps://developer.android.com/studio/write/vector-asset-studio?hl=zh-cn\n\n## 参考项目\n\nhttps://github.com/k3marek/GithubBrowser\n\nhttps://github.com/omjoonkim/GitHubBrowserApp\n\nhttps://github.com/zyyoona7/KExtensions/blob/master/lib/src/main/java/com/zyyoona7/extensions/\n\nhttps://github.com/shiweibsw/Android-kotlin-extend-utils/blob/master/app/src/main/java/com/kd/kotlin/extend/utils/\n\n## Tips\n\n1. NavController,BottomNavigationView 不同步问题\n\nhttps://medium.com/@freedom.chuks7/how-to-use-jet-pack-components-bottomnavigationview-with-navigation-ui-19fb120e3fb9\n\n2. xml中 fragment -\u003e FragmentContainerView 异常\n\nhttps://stackoverflow.com/questions/58320487/using-fragmentcontainerview-with-navigation-component\n\n```kotlin\nerror:\nnavController = Navigation.findNavController(this, R.id.nav_host)\n\nsuccess:\nval navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host) as NavHostFragment\nnavController = navHostFragment.navController\n```\n\n3. Retrofit 姿势错误\n\n```\n URL query string \"{page}\" must not have replace block. For dynamic query parameters use @Query.\n     for method ApiService.getRecommendProjects\n```\n\n4. Glide AppGlideModule\n\n```\nFailed to find GeneratedAppGlideModule. You should include an annotationProcessor compile dependency on com.github.bumptech.glide:compiler in your application and a @GlideModule annotated AppGlideModule implementation or LibraryGlideModules will be silently ignored\n```\n\n🍎 LiveData + Retrofit\nhttps://github.com/pivincii/livedata_retrofit\n\nhttps://www.ericdecanini.com/2019/11/11/3-ways-to-use-retrofit-with-livedata-in-the-mvvm-android-architecture/\n\nhttps://github.com/square/retrofit/issues/3075\n\nhttps://medium.com/@pivincii/using-retrofit-with-livedata-5c5a49544ba3\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjavakam%2Fandotemplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjavakam%2Fandotemplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjavakam%2Fandotemplate/lists"}