├── app ├── .gitignore ├── debug.jks ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles_widget.xml │ │ │ │ └── styles.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 │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ ├── fragment_empty.xml │ │ │ │ └── adapter_item_simple_list_2.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── xuexiang │ │ │ │ └── templateproject │ │ │ │ ├── activity │ │ │ │ └── MainActivity.java │ │ │ │ ├── fragment │ │ │ │ ├── EmptyFragment.java │ │ │ │ └── MainFragment.java │ │ │ │ ├── MyApp.java │ │ │ │ ├── utils │ │ │ │ ├── service │ │ │ │ │ └── JsonSerializationService.java │ │ │ │ └── sdkinit │ │ │ │ │ ├── XBasicLibInit.java │ │ │ │ │ └── UMengInit.java │ │ │ │ └── core │ │ │ │ ├── SimpleListAdapter.java │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── BaseContainerFragment.java │ │ │ │ ├── BaseSimpleListFragment.java │ │ │ │ └── BaseFragment.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── xuexiang │ │ │ └── templateproject │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── xuexiang │ │ └── templateproject │ │ └── ExampleInstrumentedTest.java ├── channel ├── multiple-channel.gradle ├── x-library-simple.gradle ├── build.gradle └── proguard-rules.pro ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .github └── FUNDING.yml ├── .idea ├── copyright │ ├── profiles_settings.xml │ └── xuexiang.xml └── gradle.xml ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── README.md ├── gradlew ├── versions.gradle └── LICENSE /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/debug.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/debug.jks -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TemplateSimpleProject 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: https://gitee.com/xuexiangjys/Resource/blob/master/doc/sponsor.md 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateSimpleProject/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 28 16:23:16 CST 2019 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-6.6.1-bin.zip 7 | -------------------------------------------------------------------------------- /app/channel: -------------------------------------------------------------------------------- 1 | # 美团 2 | meituan 3 | # 三星 4 | samsungapps 5 | # 小米 6 | xiaomi 7 | # 91助手 8 | 91com 9 | # 魅族 10 | meizu 11 | # 豌豆荚 12 | wandou 13 | # Google Play 14 | googleplay 15 | # 百度 16 | baidu 17 | # 360 18 | 360cn 19 | # 应用宝 20 | myapp 21 | # 华为 22 | huawei 23 | # 蒲公英 24 | pgyer 25 | github -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /LocalRepository 4 | /keystores 5 | /local.properties 6 | /.idea/caches 7 | /.idea/codeStyles 8 | /.idea/inspectionProfiles 9 | /.idea/libraries 10 | /.idea/dictionaries 11 | /.idea/markdown-navigator 12 | /.idea/*.xml 13 | .DS_Store 14 | /build 15 | /captures 16 | .externalNativeBuild -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/multiple-channel.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'walle' 2 | 3 | walle { 4 | // 指定渠道包的输出路径 5 | apkOutputFolder = new File("${project.buildDir}/outputs/channels") 6 | // 定制渠道包的APK的文件名称 7 | apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk' 8 | // 渠道配置文件 9 | channelFile = new File("${project.getProjectDir()}/channel") 10 | } -------------------------------------------------------------------------------- /app/src/test/java/com/xuexiang/templateproject/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.templateproject; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/copyright/xuexiang.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xuexiang/templateproject/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.templateproject; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.xuexiang.templateproject", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #299EE3 4 | #299EE3 5 | #299EE3 6 | 7 | 8 | 9 | @color/xui_config_color_white 10 | @color/xui_config_color_red 11 | @color/colorAccent 12 | #388E3C 13 | @color/xui_config_color_waring 14 | #353A3E 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_empty.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /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 | 15 | # 是否打包APK,打正式包时请设置为true,使用正式的签名 16 | isNeedPackage=false 17 | # 是否使用booster优化APK,这里需要注意gradle的版本,对于最新的gradle版本可能存在兼容问题 18 | isUseBooster=false 19 | android.precompileDependenciesResources=false 20 | 21 | android.useAndroidX=true 22 | android.enableJetifier=true 23 | 24 | android.enableD8=true 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles_widget.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.activity; 19 | 20 | import android.os.Bundle; 21 | 22 | import com.xuexiang.templateproject.core.BaseActivity; 23 | import com.xuexiang.templateproject.fragment.MainFragment; 24 | 25 | /** 26 | * 程序入口,空壳容器 27 | * 28 | * @author xuexiang 29 | * @since 2019-07-07 23:53 30 | */ 31 | public class MainActivity extends BaseActivity { 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | openPage(MainFragment.class); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 16 | 17 | 18 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_item_simple_list_2.xml: -------------------------------------------------------------------------------- 1 | 17 | 26 | 27 | 31 | 32 | 37 | 38 | -------------------------------------------------------------------------------- /app/x-library-simple.gradle: -------------------------------------------------------------------------------- 1 | //引用XAOP插件 2 | apply plugin: 'com.xuexiang.xaop' 3 | //引用XRouter-plugin插件实现自动注册 4 | apply plugin: 'com.xuexiang.xrouter' 5 | 6 | //自动添加依赖 7 | project.configurations.each { configuration -> 8 | def dependencies = getProject().dependencies 9 | if (configuration.name == "implementation") { 10 | //为Project加入X-Library依赖 11 | //XUI框架 12 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xui)) 13 | configuration.dependencies.add(dependencies.create(deps.androidx.appcompat)) 14 | configuration.dependencies.add(dependencies.create(deps.androidx.recyclerview)) 15 | configuration.dependencies.add(dependencies.create(deps.androidx.design)) 16 | configuration.dependencies.add(dependencies.create(deps.glide)) 17 | //XUtil工具类 18 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xutil_core)) 19 | //XAOP切片 20 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xaop_runtime)) 21 | //XPage 22 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xpage_lib)) 23 | //XRouter 24 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xrouter_runtime)) 25 | } 26 | 27 | if (configuration.name == "annotationProcessor") { 28 | //XPage 29 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xpage_compiler)) 30 | //页面路由 31 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xrouter_compiler)) 32 | } 33 | 34 | if (configuration.name == "debugImplementation") { 35 | //内存泄漏监测leak 36 | configuration.dependencies.add(dependencies.create(deps.leakcanary)) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/fragment/EmptyFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.fragment; 19 | 20 | import android.view.LayoutInflater; 21 | import android.view.ViewGroup; 22 | 23 | import androidx.annotation.NonNull; 24 | import androidx.annotation.Nullable; 25 | 26 | import com.xuexiang.templateproject.core.BaseFragment; 27 | import com.xuexiang.templateproject.databinding.FragmentEmptyBinding; 28 | import com.xuexiang.xpage.annotation.Page; 29 | 30 | /** 31 | * 这个只是一个空壳Fragment,只是用于演示而已 32 | * 33 | * @author xuexiang 34 | * @since 2019-07-08 00:52 35 | */ 36 | @Page(name = "空页面") 37 | public class EmptyFragment extends BaseFragment { 38 | 39 | @NonNull 40 | @Override 41 | protected FragmentEmptyBinding viewBindingInflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, boolean attachToRoot) { 42 | return FragmentEmptyBinding.inflate(inflater, container, attachToRoot); 43 | } 44 | 45 | /** 46 | * 初始化控件 47 | */ 48 | @Override 49 | protected void initViews() { 50 | 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/MyApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject; 19 | 20 | import android.app.Application; 21 | import android.content.Context; 22 | 23 | import androidx.multidex.MultiDex; 24 | 25 | import com.xuexiang.templateproject.utils.sdkinit.UMengInit; 26 | import com.xuexiang.templateproject.utils.sdkinit.XBasicLibInit; 27 | 28 | /** 29 | * @author xuexiang 30 | * @since 2018/11/7 下午1:12 31 | */ 32 | public class MyApp extends Application { 33 | 34 | @Override 35 | protected void attachBaseContext(Context base) { 36 | super.attachBaseContext(base); 37 | //解决4.x运行崩溃的问题 38 | MultiDex.install(this); 39 | } 40 | 41 | @Override 42 | public void onCreate() { 43 | super.onCreate(); 44 | initLibs(); 45 | } 46 | 47 | /** 48 | * 初始化基础库 49 | */ 50 | private void initLibs() { 51 | XBasicLibInit.init(this); 52 | UMengInit.init(this); 53 | } 54 | 55 | 56 | /** 57 | * @return 当前app是否是调试开发模式 58 | */ 59 | public static boolean isDebug() { 60 | return BuildConfig.DEBUG; 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/utils/service/JsonSerializationService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.utils.service; 19 | 20 | import android.content.Context; 21 | 22 | import com.xuexiang.xrouter.annotation.Router; 23 | import com.xuexiang.xrouter.facade.service.SerializationService; 24 | import com.xuexiang.xutil.net.JsonUtil; 25 | 26 | import java.lang.reflect.Type; 27 | 28 | /** 29 | * @author XUE 30 | * @since 2019/3/27 16:39 31 | */ 32 | @Router(path = "/service/json") 33 | public class JsonSerializationService implements SerializationService { 34 | /** 35 | * 对象序列化为json 36 | * 37 | * @param instance obj 38 | * @return json string 39 | */ 40 | @Override 41 | public String object2Json(Object instance) { 42 | return JsonUtil.toJson(instance); 43 | } 44 | 45 | /** 46 | * json反序列化为对象 47 | * 48 | * @param input json string 49 | * @param clazz object type 50 | * @return instance of object 51 | */ 52 | @Override 53 | public T parseObject(String input, Type clazz) { 54 | return JsonUtil.fromJson(input, clazz); 55 | } 56 | 57 | /** 58 | * 进程初始化的方法 59 | * 60 | * @param context 上下文 61 | */ 62 | @Override 63 | public void init(Context context) { 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 39 | 40 | 41 | 44 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/fragment/MainFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.fragment; 19 | 20 | import android.view.KeyEvent; 21 | 22 | import com.xuexiang.templateproject.core.BaseContainerFragment; 23 | import com.xuexiang.xpage.annotation.Page; 24 | import com.xuexiang.xpage.enums.CoreAnim; 25 | import com.xuexiang.xui.utils.XToastUtils; 26 | import com.xuexiang.xui.widget.actionbar.TitleBar; 27 | import com.xuexiang.xutil.XUtil; 28 | import com.xuexiang.xutil.common.ClickUtils; 29 | 30 | /** 31 | * @author xuexiang 32 | * @since 2018/11/7 下午1:16 33 | */ 34 | @Page(name = "模版程序", anim = CoreAnim.none) 35 | public class MainFragment extends BaseContainerFragment implements ClickUtils.OnClick2ExitListener { 36 | 37 | @Override 38 | protected Class[] getPagesClasses() { 39 | return new Class[] { 40 | //此处填写fragment 41 | EmptyFragment.class 42 | }; 43 | } 44 | 45 | @Override 46 | protected TitleBar initTitle() { 47 | return super.initTitle().setLeftClickListener(view -> ClickUtils.exitBy2Click(2000, this)); 48 | } 49 | 50 | 51 | /** 52 | * 菜单、返回键响应 53 | */ 54 | @Override 55 | public boolean onKeyDown(int keyCode, KeyEvent event) { 56 | if (keyCode == KeyEvent.KEYCODE_BACK) { 57 | ClickUtils.exitBy2Click(2000, this); 58 | } 59 | return true; 60 | } 61 | 62 | @Override 63 | public void onRetry() { 64 | XToastUtils.toast("再按一次退出程序"); 65 | } 66 | 67 | @Override 68 | public void onExit() { 69 | XUtil.exitApp(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/core/SimpleListAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.core; 19 | 20 | import android.content.Context; 21 | import android.view.View; 22 | import android.widget.TextView; 23 | 24 | import com.xuexiang.templateproject.R; 25 | import com.xuexiang.xui.adapter.listview.BaseListAdapter; 26 | import com.xuexiang.xutil.common.StringUtils; 27 | 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | /** 32 | * 主副标题显示适配器 33 | * 34 | * @author xuexiang 35 | * @since 2018/12/19 上午12:19 36 | */ 37 | public class SimpleListAdapter extends BaseListAdapter, SimpleListAdapter.ViewHolder> { 38 | 39 | public static final String KEY_TITLE = "title"; 40 | public static final String KEY_SUB_TITLE = "sub_title"; 41 | 42 | public SimpleListAdapter(Context context, List> data) { 43 | super(context, data); 44 | } 45 | 46 | @Override 47 | protected ViewHolder newViewHolder(View convertView) { 48 | ViewHolder holder = new ViewHolder(); 49 | holder.mTvTitle = convertView.findViewById(R.id.tv_title); 50 | holder.mTvSubTitle = convertView.findViewById(R.id.tv_sub_title); 51 | return holder; 52 | } 53 | 54 | @Override 55 | protected int getLayoutId() { 56 | return R.layout.adapter_item_simple_list_2; 57 | } 58 | 59 | @Override 60 | protected void convert(ViewHolder holder, Map item, int position) { 61 | holder.mTvTitle.setText(item.get(KEY_TITLE)); 62 | if (!StringUtils.isEmpty(item.get(KEY_SUB_TITLE))) { 63 | holder.mTvSubTitle.setText(item.get(KEY_SUB_TITLE)); 64 | holder.mTvSubTitle.setVisibility(View.VISIBLE); 65 | } else { 66 | holder.mTvSubTitle.setVisibility(View.GONE); 67 | } 68 | } 69 | 70 | public static class ViewHolder { 71 | /** 72 | * 标题 73 | */ 74 | public TextView mTvTitle; 75 | /** 76 | * 副标题 77 | */ 78 | public TextView mTvSubTitle; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TemplateSimpleProject 2 | 3 | 简化版的Android空壳模版工程,快速搭建(集成了XUI、XUtil、XAOP、XPage、友盟统计和walle多渠道打包) 4 | 5 | **【注意】** 本模板适用于Android Studio 4.0及以上版本,如果你的Android Studio低于 4.0版本,可使用[butterknife](https://github.com/xuexiangjys/TemplateSimpleProject/tree/butterknife)分支。 6 | 7 | **【Kotlin版本】** 如果你想使用Kotlin语言,那么请移步[TemplateSimpleProject-kotlin](https://github.com/xuexiangjys/TemplateSimpleProject-kotlin) 8 | 9 | ## 关于我 10 | 11 | | 公众号 | 掘金 | 知乎 | CSDN | 简书 | 思否 | 哔哩哔哩 | 今日头条 12 | |---------|---------|--------- |---------|---------|---------|---------|---------| 13 | | [我的Android开源之旅](https://t.1yb.co/Irse) | [点我](https://juejin.im/user/598feef55188257d592e56ed/posts) | [点我](https://www.zhihu.com/people/xuexiangjys/posts) | [点我](https://xuexiangjys.blog.csdn.net/) | [点我](https://www.jianshu.com/u/6bf605575337) | [点我](https://segmentfault.com/u/xuexiangjys) | [点我](https://space.bilibili.com/483850585) | [点我](https://img.rruu.net/image/5ff34ff7b02dd) 14 | 15 | ## 集成介绍(请star支持) 16 | 17 | > 本项目是项目androidx项目。精选了X系列最最实用的几个库,可大大提高开发的效率。 18 | 19 | * [XUI 一个简洁而优雅的Android原生UI框架,解放你的双手!](https://github.com/xuexiangjys/XUI) 20 | 21 | * [XUtil 一个方便实用的Android工具类库!](https://github.com/xuexiangjys/XUtil) 22 | 23 | * [XAOP 一个轻量级的AOP(Android)应用框架。囊括了最实用的AOP应用。](https://github.com/xuexiangjys/XAOP) 24 | 25 | * [XPage 一个非常方便的fragment页面框架。](https://github.com/xuexiangjys/XPage) 26 | 27 | 除此之外,还集成了其他优秀的第三方库: 28 | 29 | * [AndroidAutoSize 优秀的屏幕适配方案](https://github.com/JessYanCoding/AndroidAutoSize) 30 | 31 | * [ViewBinding Google推荐的视图绑定工具](https://developer.android.google.cn/topic/libraries/view-binding) 32 | 33 | * [leakcanary 内存泄漏检测](https://github.com/square/leakcanary) 34 | 35 | ## 使用方式 36 | 37 | 1.克隆项目 38 | 39 | ``` 40 | git clone https://github.com/xuexiangjys/TemplateSimpleProject.git 41 | ``` 42 | 43 | 2.修改项目名(文件夹名),并删除目录下的.git文件夹(隐藏文件) 44 | 45 | 3.使用AS打开项目,然后修改`包名`、`applicationId`和`app_name` 46 | 47 | * 修改包名 48 | 49 | ![templateproject_1.png](https://raw.githubusercontent.com/xuexiangjys/Resource/master/img/templateproject/1.png) 50 | 51 | ![templateproject_2.png](https://raw.githubusercontent.com/xuexiangjys/Resource/master/img/templateproject/2.png) 52 | 53 | * 修改applicationId 54 | 55 | ![templateproject_3.png](https://raw.githubusercontent.com/xuexiangjys/Resource/master/img/templateproject/3.png) 56 | 57 | * 修改app_name 58 | 59 | ![templateproject_5.png](https://raw.githubusercontent.com/xuexiangjys/Resource/master/img/templateproject/5.png) 60 | 61 | ## 项目打包 62 | 63 | 1.修改工程根目录的`gradle.properties`中的`isNeedPackage=true`。 64 | 65 | 2.添加并配置keystore,在`versions.gradle`中修改`app_release`相关参数。 66 | 67 | 3.如果考虑使用友盟统计的话,在`local.properties`中设置应用的友盟ID:`APP_ID_UMENG`。 68 | 69 | 4.使用`./gradlew clean assembleReleaseChannels`进行多渠道打包。 70 | 71 | ## 如果觉得项目还不错,可以考虑打赏一波 72 | 73 | > 你的打赏是我维护的动力,我将会列出所有打赏人员的清单在下方作为凭证,打赏前请留下打赏项目的备注! 74 | 75 | ![pay.png](https://raw.githubusercontent.com/xuexiangjys/Resource/master/img/pay/pay.png) 76 | 77 | ## 联系方式 78 | 79 | > 更多资讯内容,欢迎扫描关注我的个人微信公众号:【我的Android开源之旅】 80 | 81 | ![](https://s1.ax1x.com/2022/04/27/LbGMJH.jpg) 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/utils/sdkinit/XBasicLibInit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.utils.sdkinit; 19 | 20 | import android.app.Application; 21 | 22 | import com.xuexiang.templateproject.MyApp; 23 | import com.xuexiang.templateproject.core.BaseActivity; 24 | import com.xuexiang.xaop.XAOP; 25 | import com.xuexiang.xpage.PageConfig; 26 | import com.xuexiang.xrouter.launcher.XRouter; 27 | import com.xuexiang.xui.XUI; 28 | import com.xuexiang.xui.utils.XToastUtils; 29 | import com.xuexiang.xutil.XUtil; 30 | import com.xuexiang.xutil.common.StringUtils; 31 | 32 | /** 33 | * X系列基础库初始化 34 | * 35 | * @author xuexiang 36 | * @since 2019-06-30 23:54 37 | */ 38 | public final class XBasicLibInit { 39 | 40 | private XBasicLibInit() { 41 | throw new UnsupportedOperationException("u can't instantiate me..."); 42 | } 43 | 44 | /** 45 | * 初始化基础库SDK 46 | */ 47 | public static void init(Application application) { 48 | //工具类 49 | initXUtil(application); 50 | 51 | //页面框架 52 | initXPage(application); 53 | 54 | //切片框架 55 | initXAOP(application); 56 | 57 | //UI框架 58 | initXUI(application); 59 | 60 | //路由框架 61 | initRouter(application); 62 | } 63 | 64 | /** 65 | * 初始化XUtil工具类 66 | */ 67 | private static void initXUtil(Application application) { 68 | XUtil.init(application); 69 | XUtil.debug(MyApp.isDebug()); 70 | } 71 | 72 | /** 73 | * 初始化XPage页面框架 74 | */ 75 | private static void initXPage(Application application) { 76 | PageConfig.getInstance() 77 | .debug(MyApp.isDebug()) 78 | .setContainActivityClazz(BaseActivity.class) 79 | .init(application); 80 | } 81 | 82 | /** 83 | * 初始化XAOP 84 | */ 85 | private static void initXAOP(Application application) { 86 | XAOP.init(application); 87 | XAOP.debug(MyApp.isDebug()); 88 | //设置动态申请权限切片 申请权限被拒绝的事件响应监听 89 | XAOP.setOnPermissionDeniedListener(permissionsDenied -> XToastUtils.error("权限申请被拒绝:" + StringUtils.listToString(permissionsDenied, ","))); 90 | } 91 | 92 | /** 93 | * 初始化XUI框架 94 | */ 95 | private static void initXUI(Application application) { 96 | XUI.init(application); 97 | XUI.debug(MyApp.isDebug()); 98 | } 99 | 100 | /** 101 | * 初始化路由框架 102 | */ 103 | private static void initRouter(Application application) { 104 | // 这两行必须写在init之前,否则这些配置在init过程中将无效 105 | if (MyApp.isDebug()) { 106 | XRouter.openLog(); // 打印日志 107 | XRouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) 108 | } 109 | XRouter.init(application); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/utils/sdkinit/UMengInit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.utils.sdkinit; 19 | 20 | import android.app.Application; 21 | import android.content.Context; 22 | 23 | import androidx.annotation.NonNull; 24 | 25 | import com.meituan.android.walle.WalleChannelReader; 26 | import com.umeng.analytics.MobclickAgent; 27 | import com.umeng.commonsdk.UMConfigure; 28 | import com.xuexiang.templateproject.BuildConfig; 29 | import com.xuexiang.templateproject.MyApp; 30 | 31 | /** 32 | * UMeng 统计 SDK初始化 33 | * 34 | * @author xuexiang 35 | * @since 2019-06-18 15:49 36 | */ 37 | public final class UMengInit { 38 | 39 | private UMengInit() { 40 | throw new UnsupportedOperationException("u can't instantiate me..."); 41 | } 42 | 43 | private static String DEFAULT_CHANNEL_ID = "github"; 44 | 45 | /** 46 | * 初始化SDK,合规指南【先进行预初始化,如果用户隐私同意后可以初始化UmengSDK进行信息上报】 47 | */ 48 | public static void init(@NonNull Context context) { 49 | Context appContext = context.getApplicationContext(); 50 | if (appContext instanceof Application) { 51 | init((Application) appContext); 52 | } 53 | } 54 | 55 | /** 56 | * 初始化SDK,合规指南【先进行预初始化,如果用户隐私同意后可以初始化UmengSDK进行信息上报】 57 | */ 58 | public static void init(Application application) { 59 | // 运营统计数据调试运行时不初始化 60 | if (MyApp.isDebug()) { 61 | return; 62 | } 63 | UMConfigure.setLogEnabled(false); 64 | UMConfigure.preInit(application, BuildConfig.APP_ID_UMENG, getChannel(application)); 65 | // 用户同意了隐私协议 66 | if (isAgreePrivacy()) { 67 | realInit(application); 68 | } 69 | } 70 | 71 | /** 72 | * @return 用户是否同意了隐私协议 73 | */ 74 | private static boolean isAgreePrivacy() { 75 | // TODO: 2021/5/11 隐私协议设置 76 | return true; 77 | } 78 | 79 | /** 80 | * 真实的初始化UmengSDK【进行设备信息的统计上报,必须在获得用户隐私同意后方可调用】 81 | */ 82 | private static void realInit(Application application) { 83 | // 运营统计数据调试运行时不初始化 84 | if (MyApp.isDebug()) { 85 | return; 86 | } 87 | //初始化组件化基础库, 注意: 即使您已经在AndroidManifest.xml中配置过appkey和channel值,也需要在App代码中调用初始化接口(如需要使用AndroidManifest.xml中配置好的appkey和channel值,UMConfigure.init调用中appkey和channel参数请置为null)。 88 | //第二个参数是appkey,最后一个参数是pushSecret 89 | //这里BuildConfig.APP_ID_UMENG是根据local.properties中定义的APP_ID_UMENG生成的,只是运行看效果的话,可以不初始化该SDK 90 | UMConfigure.init(application, BuildConfig.APP_ID_UMENG, getChannel(application), UMConfigure.DEVICE_TYPE_PHONE, ""); 91 | //统计SDK是否支持采集在子进程中打点的自定义事件,默认不支持 92 | //支持多进程打点 93 | UMConfigure.setProcessEvent(true); 94 | MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO); 95 | } 96 | 97 | /** 98 | * 获取渠道信息 99 | */ 100 | private static String getChannel(final Context context) { 101 | return WalleChannelReader.getChannel(context, DEFAULT_CHANNEL_ID); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'img-optimizer' 3 | //打包时,记得设置true启用 4 | if (isNeedPackage.toBoolean() && isUseBooster.toBoolean()) { 5 | apply plugin: 'com.didiglobal.booster' 6 | } 7 | 8 | android { 9 | compileSdkVersion build_versions.target_sdk 10 | buildToolsVersion build_versions.build_tools 11 | 12 | defaultConfig { 13 | applicationId "com.xuexiang.templateproject" 14 | minSdkVersion 17 15 | targetSdkVersion build_versions.target_sdk 16 | versionCode 1 17 | versionName "1.0" 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | 20 | multiDexEnabled true 21 | vectorDrawables.useSupportLibrary = true 22 | 23 | javaCompileOptions { 24 | annotationProcessorOptions { 25 | arguments = [ moduleName : project.getName() ] 26 | } 27 | } 28 | } 29 | 30 | signingConfigs { 31 | if (isNeedPackage.toBoolean()) { 32 | release { 33 | storeFile file(app_release.storeFile) 34 | storePassword app_release.storePassword 35 | keyAlias app_release.keyAlias 36 | keyPassword app_release.keyPassword 37 | } 38 | } 39 | 40 | debug { 41 | storeFile file("./debug.jks") 42 | storePassword "123456" 43 | keyAlias "debug" 44 | keyPassword "123456" 45 | } 46 | } 47 | 48 | buildTypes { 49 | release { 50 | minifyEnabled true 51 | shrinkResources true 52 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 53 | if (isNeedPackage.toBoolean()) { 54 | signingConfig signingConfigs.release 55 | 56 | Properties properties = new Properties() 57 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 58 | def appID = properties.getProperty("APP_ID_UMENG") 59 | if (appID != null) { 60 | buildConfigField "String", "APP_ID_UMENG", appID 61 | } else { 62 | buildConfigField "String", "APP_ID_UMENG", '""' 63 | } 64 | } else { 65 | signingConfig signingConfigs.debug 66 | buildConfigField "String", "APP_ID_UMENG", '""' 67 | } 68 | } 69 | 70 | debug { 71 | debuggable true 72 | minifyEnabled false 73 | 74 | signingConfig signingConfigs.debug 75 | buildConfigField "String", "APP_ID_UMENG", '""' 76 | } 77 | } 78 | 79 | lintOptions { 80 | abortOnError false 81 | } 82 | 83 | compileOptions { 84 | sourceCompatibility JavaVersion.VERSION_1_8 85 | targetCompatibility JavaVersion.VERSION_1_8 86 | } 87 | buildFeatures { 88 | viewBinding true 89 | } 90 | } 91 | 92 | dependencies { 93 | implementation fileTree(dir: 'libs', include: ['*.jar']) 94 | testImplementation deps.junit 95 | androidTestImplementation deps.runner 96 | androidTestImplementation deps.espresso.core 97 | 98 | implementation deps.androidx.multidex 99 | //屏幕适配AutoSize 100 | implementation 'me.jessyan:autosize:1.2.1' 101 | //umeng统计 102 | implementation 'com.umeng.umsdk:common:9.3.8' 103 | implementation 'com.umeng.umsdk:asms:1.2.1' 104 | 105 | //美团多渠道打包 106 | implementation 'com.meituan.android.walle:library:1.1.6' 107 | } 108 | 109 | //x-library依赖脚本 110 | apply from: 'x-library-simple.gradle' 111 | //walle多渠道打包 112 | apply from: 'multiple-channel.gradle' 113 | 114 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/core/BaseActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.core; 19 | 20 | import android.content.Context; 21 | import android.os.Bundle; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | 25 | import androidx.annotation.Nullable; 26 | import androidx.viewbinding.ViewBinding; 27 | 28 | import com.xuexiang.xpage.base.XPageActivity; 29 | import com.xuexiang.xpage.base.XPageFragment; 30 | import com.xuexiang.xpage.core.CoreSwitchBean; 31 | import com.xuexiang.xrouter.facade.service.SerializationService; 32 | import com.xuexiang.xrouter.launcher.XRouter; 33 | 34 | import io.github.inflationx.viewpump.ViewPumpContextWrapper; 35 | 36 | /** 37 | * 基础容器Activity 38 | * 39 | * @author XUE 40 | * @since 2019/3/22 11:21 41 | */ 42 | public class BaseActivity extends XPageActivity { 43 | 44 | /** 45 | * ViewBinding 46 | */ 47 | protected Binding binding; 48 | 49 | @Override 50 | protected void attachBaseContext(Context newBase) { 51 | //注入字体 52 | super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase)); 53 | } 54 | 55 | @Override 56 | protected View getCustomRootView() { 57 | binding = viewBindingInflate(getLayoutInflater()); 58 | return binding != null ? binding.getRoot() : null; 59 | } 60 | 61 | @Override 62 | protected void onCreate(Bundle savedInstanceState) { 63 | initStatusBarStyle(); 64 | super.onCreate(savedInstanceState); 65 | } 66 | 67 | /** 68 | * 构建ViewBinding 69 | * 70 | * @param inflater inflater 71 | * @return ViewBinding 72 | */ 73 | @Nullable 74 | protected Binding viewBindingInflate(LayoutInflater inflater) { 75 | return null; 76 | } 77 | 78 | /** 79 | * 获取Binding 80 | * 81 | * @return Binding 82 | */ 83 | public Binding getBinding() { 84 | return binding; 85 | } 86 | 87 | /** 88 | * 初始化状态栏的样式 89 | */ 90 | protected void initStatusBarStyle() { 91 | 92 | } 93 | 94 | /** 95 | * 打开fragment 96 | * 97 | * @param clazz 页面类 98 | * @param addToBackStack 是否添加到栈中 99 | * @return 打开的fragment对象 100 | */ 101 | public T openPage(Class clazz, boolean addToBackStack) { 102 | CoreSwitchBean page = new CoreSwitchBean(clazz) 103 | .setAddToBackStack(addToBackStack); 104 | return (T) openPage(page); 105 | } 106 | 107 | /** 108 | * 打开fragment 109 | * 110 | * @return 打开的fragment对象 111 | */ 112 | public T openNewPage(Class clazz) { 113 | CoreSwitchBean page = new CoreSwitchBean(clazz) 114 | .setNewActivity(true); 115 | return (T) openPage(page); 116 | } 117 | 118 | /** 119 | * 切换fragment 120 | * 121 | * @param clazz 页面类 122 | * @return 打开的fragment对象 123 | */ 124 | public T switchPage(Class clazz) { 125 | return openPage(clazz, false); 126 | } 127 | 128 | /** 129 | * 序列化对象 130 | * 131 | * @param object 132 | * @return 133 | */ 134 | public String serializeObject(Object object) { 135 | return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/core/BaseContainerFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.core; 19 | 20 | import android.content.res.Configuration; 21 | import android.view.View; 22 | import android.view.ViewGroup; 23 | import android.widget.AdapterView; 24 | 25 | import com.umeng.analytics.MobclickAgent; 26 | import com.xuexiang.xaop.annotation.SingleClick; 27 | import com.xuexiang.xpage.base.XPageContainerListFragment; 28 | import com.xuexiang.xui.widget.actionbar.TitleBar; 29 | import com.xuexiang.xui.widget.actionbar.TitleUtils; 30 | 31 | import java.util.ArrayList; 32 | import java.util.HashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | 36 | import static com.xuexiang.templateproject.core.SimpleListAdapter.KEY_SUB_TITLE; 37 | import static com.xuexiang.templateproject.core.SimpleListAdapter.KEY_TITLE; 38 | 39 | /** 40 | * 修改列表样式为主副标题显示 41 | * 42 | * @author xuexiang 43 | * @since 2018/11/22 上午11:26 44 | */ 45 | public abstract class BaseContainerFragment extends XPageContainerListFragment { 46 | 47 | @Override 48 | protected void initPage() { 49 | initTitle(); 50 | initViews(); 51 | initListeners(); 52 | } 53 | 54 | protected TitleBar initTitle() { 55 | return TitleUtils.addTitleBarDynamic(getToolbarContainer(), getPageTitle(), v -> popToBack()); 56 | } 57 | 58 | @Override 59 | protected void initData() { 60 | mSimpleData = initSimpleData(mSimpleData); 61 | 62 | List> data = new ArrayList<>(); 63 | for (String content : mSimpleData) { 64 | Map item = new HashMap<>(); 65 | int index = content.indexOf("\n"); 66 | if (index > 0) { 67 | item.put(KEY_TITLE, String.valueOf(content.subSequence(0, index))); 68 | item.put(KEY_SUB_TITLE, String.valueOf(content.subSequence(index + 1, content.length()))); 69 | } else { 70 | item.put(KEY_TITLE, content); 71 | item.put(KEY_SUB_TITLE, ""); 72 | } 73 | data.add(item); 74 | } 75 | 76 | getListView().setAdapter(new SimpleListAdapter(getContext(), data)); 77 | initSimply(); 78 | } 79 | 80 | @Override 81 | public void onItemClick(AdapterView adapterView, View view, int position, long id) { 82 | onItemClick(view, position); 83 | } 84 | 85 | @SingleClick 86 | private void onItemClick(View view, int position) { 87 | onItemClick(position); 88 | } 89 | 90 | @Override 91 | public void onDestroyView() { 92 | getListView().setOnItemClickListener(null); 93 | super.onDestroyView(); 94 | } 95 | 96 | @Override 97 | public void onConfigurationChanged(Configuration newConfig) { 98 | //屏幕旋转时刷新一下title 99 | super.onConfigurationChanged(newConfig); 100 | ViewGroup root = (ViewGroup) getRootView(); 101 | if (root.getChildAt(0) instanceof TitleBar) { 102 | root.removeViewAt(0); 103 | initTitle(); 104 | } 105 | } 106 | 107 | @Override 108 | public void onResume() { 109 | super.onResume(); 110 | MobclickAgent.onPageStart(getPageName()); 111 | } 112 | 113 | @Override 114 | public void onPause() { 115 | super.onPause(); 116 | MobclickAgent.onPageEnd(getPageName()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | #=========================================基础不变的混淆配置=========================================## 2 | #指定代码的压缩级别 3 | -optimizationpasses 5 4 | #包名不混合大小写 5 | -dontusemixedcaseclassnames 6 | #不去忽略非公共的库类 7 | -dontskipnonpubliclibraryclasses 8 | # 指定不去忽略非公共的库的类的成员 9 | -dontskipnonpubliclibraryclassmembers 10 | #优化 不优化输入的类文件 11 | -dontoptimize 12 | #预校验 13 | -dontpreverify 14 | #混淆时是否记录日志 15 | -verbose 16 | # 混淆时所采用的算法 17 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 18 | #保护注解 19 | -keepattributes *Annotation* 20 | #忽略警告 21 | -ignorewarnings 22 | 23 | ##记录生成的日志数据,gradle build时在本项目根目录输出## 24 | #apk 包内所有 class 的内部结构 25 | -dump class_files.txt 26 | #未混淆的类和成员 27 | -printseeds seeds.txt 28 | #列出从 apk 中删除的代码 29 | -printusage unused.txt 30 | #混淆前后的映射 31 | -printmapping mapping.txt 32 | # 并保留源文件名为"Proguard"字符串,而非原始的类名 并保留行号 33 | -keepattributes SourceFile,LineNumberTable 34 | ########记录生成的日志数据,gradle build时 在本项目根目录输出-end##### 35 | 36 | #需要保留的东西 37 | # 保持哪些类不被混淆 38 | -keep public class * extends android.app.Fragment 39 | -keep public class * extends android.app.Activity 40 | -keep public class * extends android.app.Application 41 | -keep public class * extends android.app.Service 42 | -keep public class * extends android.content.BroadcastReceiver 43 | -keep public class * extends android.content.ContentProvider 44 | -keep public class * extends android.app.backup.BackupAgentHelper 45 | -keep public class * extends android.preference.Preference 46 | -keep public class * extends android.support.v4.** 47 | -keep public class com.android.vending.licensing.ILicensingService 48 | 49 | #如果有引用v4包可以添加下面这行 50 | -keep public class * extends android.support.v4.app.Fragment 51 | 52 | ##########JS接口类不混淆,否则执行不了 53 | -dontwarn com.android.JsInterface.** 54 | -keep class com.android.JsInterface.** {*; } 55 | 56 | #极光推送和百度lbs android sdk一起使用proguard 混淆的问题#http的类被混淆后,导致apk定位失败,保持apache 的http类不被混淆就好了 57 | -dontwarn org.apache.** 58 | -keep class org.apache.**{ *; } 59 | 60 | -keep public class * extends android.view.View { 61 | public (android.content.Context); 62 | public (android.content.Context, android.util.AttributeSet); 63 | public (android.content.Context, android.util.AttributeSet, int); 64 | public void set*(...); 65 | } 66 | 67 | #保持 native 方法不被混淆 68 | -keepclasseswithmembernames class * { 69 | native ; 70 | } 71 | 72 | #保持自定义控件类不被混淆 73 | -keepclasseswithmembers class * { 74 | public (android.content.Context, android.util.AttributeSet); 75 | } 76 | 77 | #保持自定义控件类不被混淆 78 | -keepclassmembers class * extends android.app.Activity { 79 | public void *(android.view.View); 80 | } 81 | 82 | #保持 Parcelable 不被混淆 83 | -keep class * implements android.os.Parcelable { 84 | public static final android.os.Parcelable$Creator *; 85 | } 86 | 87 | #保持 Serializable 不被混淆 88 | -keepnames class * implements java.io.Serializable 89 | 90 | #保持 Serializable 不被混淆并且enum 类也不被混淆 91 | -keepclassmembers class * implements java.io.Serializable { 92 | static final long serialVersionUID; 93 | private static final java.io.ObjectStreamField[] serialPersistentFields; 94 | !static !transient ; 95 | !private ; 96 | !private ; 97 | private void writeObject(java.io.ObjectOutputStream); 98 | private void readObject(java.io.ObjectInputStream); 99 | java.lang.Object writeReplace(); 100 | java.lang.Object readResolve(); 101 | } 102 | 103 | #保持枚举 enum 类不被混淆 如果混淆报错,建议直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可 104 | -keepclassmembers enum * { 105 | public static **[] values(); 106 | public static ** valueOf(java.lang.String); 107 | } 108 | 109 | -keepclassmembers class * { 110 | public void *ButtonClicked(android.view.View); 111 | } 112 | 113 | #不混淆资源类 114 | -keep class **.R$* {*;} 115 | 116 | #===================================混淆保护自己项目的部分代码以及引用的第三方jar包library=============================####### 117 | #如果引用了v4或者v7包 118 | -dontwarn android.support.** 119 | 120 | 121 | # AndroidX 防止混淆 122 | -dontwarn com.google.android.material.** 123 | -dontnote com.google.android.material.** 124 | -dontwarn androidx.** 125 | -keep class com.google.android.material.** {*;} 126 | -keep class androidx.** {*;} 127 | -keep public class * extends androidx.** 128 | -keep interface androidx.** {*;} 129 | -keepclassmembers class * { 130 | @androidx.annotation.Keep *; 131 | } 132 | 133 | # zxing 134 | -dontwarn com.google.zxing.** 135 | -keep class com.google.zxing.**{*;} 136 | 137 | #SignalR推送 138 | -keep class microsoft.aspnet.signalr.** { *; } 139 | 140 | # 极光推送混淆 141 | -dontoptimize 142 | -dontpreverify 143 | -dontwarn cn.jpush.** 144 | -keep class cn.jpush.** { *; } 145 | -dontwarn cn.jiguang.** 146 | -keep class cn.jiguang.** { *; } 147 | 148 | # 数据库框架OrmLite 149 | -keepattributes *DatabaseField* 150 | -keepattributes *DatabaseTable* 151 | -keepattributes *SerializedName* 152 | -keep class com.j256.** 153 | -keepclassmembers class com.j256.** { *; } 154 | -keep enum com.j256.** 155 | -keepclassmembers enum com.j256.** { *; } 156 | -keep interface com.j256.** 157 | -keepclassmembers interface com.j256.** { *; } 158 | 159 | #XHttp2 160 | -keep class com.xuexiang.xhttp2.model.** { *; } 161 | -keep class com.xuexiang.xhttp2.cache.model.** { *; } 162 | -keep class com.xuexiang.xhttp2.cache.stategy.**{*;} 163 | -keep class com.xuexiang.xhttp2.annotation.** { *; } 164 | 165 | #okhttp 166 | -dontwarn com.squareup.okhttp3.** 167 | -keep class com.squareup.okhttp3.** { *;} 168 | -dontwarn okio.** 169 | -dontwarn javax.annotation.Nullable 170 | -dontwarn javax.annotation.ParametersAreNonnullByDefault 171 | -dontwarn javax.annotation.** 172 | 173 | #如果用到Gson解析包的,直接添加下面这几行就能成功混淆,不然会报错 174 | -keepattributes Signature 175 | -keep class com.google.gson.stream.** { *; } 176 | -keepattributes EnclosingMethod 177 | -keep class org.xz_sale.entity.**{*;} 178 | -keep class com.google.gson.** {*;} 179 | -keep class com.google.**{*;} 180 | -keep class sun.misc.Unsafe { *; } 181 | -keep class com.google.gson.stream.** { *; } 182 | -keep class com.google.gson.examples.android.model.** { *; } 183 | 184 | # Glide 185 | -keep public class * implements com.bumptech.glide.module.GlideModule 186 | -keep public class * extends com.bumptech.glide.module.AppGlideModule 187 | -keep public enum com.bumptech.glide.load.ImageHeaderParser$** { 188 | **[] $VALUES; 189 | public *; 190 | } 191 | 192 | # Retrofit 193 | -dontwarn retrofit2.** 194 | -keep class retrofit2.** { *; } 195 | -keepattributes Exceptions 196 | 197 | # RxJava RxAndroid 198 | -dontwarn sun.misc.** 199 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { 200 | long producerIndex; 201 | long consumerIndex; 202 | } 203 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { 204 | rx.internal.util.atomic.LinkedQueueNode producerNode; 205 | } 206 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { 207 | rx.internal.util.atomic.LinkedQueueNode consumerNode; 208 | } 209 | 210 | -dontwarn okio.** 211 | -dontwarn javax.annotation.Nullable 212 | -dontwarn javax.annotation.ParametersAreNonnullByDefault 213 | -dontwarn javax.annotation.** 214 | 215 | # fastjson 216 | -dontwarn com.alibaba.fastjson.** 217 | -keep class com.alibaba.fastjson.** { *; } 218 | -keepattributes Signature 219 | 220 | # xpage 221 | -keep class com.xuexiang.xpage.annotation.** { *; } 222 | -keep class com.xuexiang.xpage.config.** { *; } 223 | 224 | # xaop 225 | -keep @com.xuexiang.xaop.annotation.* class * {*;} 226 | -keep @org.aspectj.lang.annotation.* class * {*;} 227 | -keep class * { 228 | @com.xuexiang.xaop.annotation.* ; 229 | @org.aspectj.lang.annotation.* ; 230 | } 231 | -keepclassmembers class * { 232 | @com.xuexiang.xaop.annotation.* ; 233 | @org.aspectj.lang.annotation.* ; 234 | } 235 | 236 | # xrouter 237 | -keep public class com.xuexiang.xrouter.routes.**{*;} 238 | -keep class * implements com.xuexiang.xrouter.facade.template.ISyringe{*;} 239 | # 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口 240 | -keep interface * implements com.xuexiang.xrouter.facade.template.IProvider 241 | # 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现 242 | -keep class * implements com.xuexiang.xrouter.facade.template.IProvider 243 | 244 | # xupdate 245 | -keep class com.xuexiang.xupdate.entity.** { *; } 246 | 247 | # xvideo 248 | -keep class com.xuexiang.xvideo.jniinterface.** { *; } 249 | 250 | # xipc 251 | -keep @com.xuexiang.xipc.annotation.* class * {*;} 252 | -keep class * { 253 | @com.xuexiang.xipc.annotation.* ; 254 | } 255 | -keepclassmembers class * { 256 | @com.xuexiang.xipc.annotation.* ; 257 | } 258 | 259 | # umeng统计 260 | -keep class com.umeng.** {*;} 261 | -keepclassmembers class * { 262 | public (org.json.JSONObject); 263 | } 264 | -keepclassmembers enum * { 265 | public static **[] values(); 266 | public static ** valueOf(java.lang.String); 267 | } 268 | 269 | # materialedittext 270 | -keep class com.xuexiang.xui.widget.edittext.materialedittext.** { *; } 271 | -------------------------------------------------------------------------------- /versions.gradle: -------------------------------------------------------------------------------- 1 | import java.util.regex.Matcher 2 | import java.util.regex.Pattern 3 | 4 | ext.deps = [:] 5 | def versions = [:] 6 | 7 | versions.android_gradle_plugin = "4.1.0" 8 | versions.android_maven_gradle_plugin = "2.0" 9 | versions.gradle_bintray_plugin = "1.8.0" 10 | versions.booster = "3.1.0" 11 | versions.booster_all = "1.1.1" 12 | versions.support = "28.0.0" 13 | versions.annotation = "1.2.0" 14 | versions.androidx = "1.3.1" 15 | versions.recyclerview = "1.2.1" 16 | versions.material = "1.4.0" 17 | versions.junit = "4.12" 18 | versions.espresso = "3.2.0" 19 | versions.constraint_layout = "2.1.0" 20 | versions.glide = "4.11.0" 21 | versions.rxjava2 = "2.2.20" 22 | versions.rxandroid = "2.1.1" 23 | versions.rxbinding = "2.2.0" 24 | versions.butterknife = "10.1.0" 25 | versions.runner = "1.2.0" 26 | versions.gson = "2.8.5" 27 | versions.leakcanary = "2.6" 28 | 29 | //========xlibrary start========// 30 | 31 | versions.xui = "1.2.0" 32 | versions.xupdate = "2.1.3" 33 | versions.xaop = "1.1.0" 34 | versions.xutil = "2.0.0" 35 | versions.xhttp2 = "2.0.4" 36 | versions.xpage = "3.4.0" 37 | versions.xrouter = "1.1.0" 38 | 39 | //========xlibrary end========// 40 | 41 | def deps = [:] 42 | 43 | def support = [:] 44 | support.annotations = "com.android.support:support-annotations:$versions.support" 45 | support.app_compat = "com.android.support:appcompat-v7:$versions.support" 46 | support.recyclerview = "com.android.support:recyclerview-v7:$versions.support" 47 | support.cardview = "com.android.support:cardview-v7:$versions.support" 48 | support.design = "com.android.support:design:$versions.support" 49 | support.v4 = "com.android.support:support-v4:$versions.support" 50 | support.core_utils = "com.android.support:support-core-utils:$versions.support" 51 | deps.support = support 52 | 53 | def androidx = [:] 54 | androidx.annotations = "androidx.annotation:annotation:$versions.annotation" 55 | androidx.appcompat = "androidx.appcompat:appcompat:$versions.androidx" 56 | androidx.recyclerview = "androidx.recyclerview:recyclerview:$versions.recyclerview" 57 | androidx.design = "com.google.android.material:material:$versions.material" 58 | androidx.multidex = 'androidx.multidex:multidex:2.0.1' 59 | deps.androidx = androidx 60 | 61 | def booster = [:] 62 | booster.gradle_plugin = "com.didiglobal.booster:booster-gradle-plugin:$versions.booster" 63 | booster.task_all = "com.didiglobal.booster:booster-task-all:$versions.booster_all" 64 | booster.transform_all = "com.didiglobal.booster:booster-transform-all:$versions.booster_all" 65 | //采用 cwebp 对资源进行压缩 66 | booster.task_compression_cwebp = "com.didiglobal.booster:booster-task-compression-cwebp:$versions.booster" 67 | //采用 pngquant 对资源进行压缩 68 | booster.task_compression_pngquant = "com.didiglobal.booster:booster-task-compression-pngquant:$versions.booster" 69 | //ap_ 文件压缩 70 | booster.task_processed_res = "com.didiglobal.booster:booster-task-compression-processed-res:$versions.booster" 71 | //去冗余资源 72 | booster.task_resource_deredundancy = "com.didiglobal.booster:booster-task-resource-deredundancy:$versions.booster" 73 | //检查 SNAPSHOT 版本 74 | booster.task_check_snapshot = "com.didiglobal.booster:booster-task-check-snapshot:$versions.booster" 75 | //性能瓶颈检测 76 | booster.transform_lint = "com.didiglobal.booster:booster-transform-lint:$versions.booster" 77 | //多线程优化 78 | booster.transform_thread = "com.didiglobal.booster:booster-transform-thread:$versions.booster" 79 | //资源索引内联 80 | booster.transform_r_inline = "com.didiglobal.booster:booster-transform-r-inline:$versions.booster" 81 | //WebView 预加载 82 | booster.transform_webview = "com.didiglobal.booster:booster-transform-webview:$versions.booster" 83 | //SharedPreferences 优化 84 | booster.transform_shared_preferences = "com.didiglobal.booster:booster-transform-shared-preferences:$versions.booster" 85 | //检查覆盖安装导致的 Resources 和 Assets 未加载的 Bug 86 | booster.transform_res_check = "com.didiglobal.booster:booster-transform-res-check:$versions.booster" 87 | //修复 Toast 在 Android 7.1 上的 Bug 88 | booster.transform_toast = "com.didiglobal.booster:booster-transform-toast:$versions.booster" 89 | //处理系统 Crash 90 | booster.transform_activity_thread = "com.didiglobal.booster:booster-transform-activity-thread:$versions.booster" 91 | deps.booster = booster 92 | 93 | def butterknife = [:] 94 | butterknife.runtime = "com.jakewharton:butterknife:$versions.butterknife" 95 | butterknife.compiler = "com.jakewharton:butterknife-compiler:$versions.butterknife" 96 | 97 | deps.butterknife = butterknife 98 | 99 | def espresso = [:] 100 | espresso.core = "androidx.test.espresso:espresso-core:$versions.espresso" 101 | espresso.contrib = "androidx.test.espresso:espresso-contrib:$versions.espresso" 102 | espresso.intents = "androidx.test.espresso:espresso-intents:$versions.espresso" 103 | deps.espresso = espresso 104 | 105 | deps.android_gradle_plugin = "com.android.tools.build:gradle:$versions.android_gradle_plugin" 106 | deps.android_maven_gradle_plugin = "com.github.dcendents:android-maven-gradle-plugin:$versions.android_maven_gradle_plugin" 107 | deps.gradle_bintray_plugin = "com.jfrog.bintray.gradle:gradle-bintray-plugin:$versions.gradle_bintray_plugin" 108 | deps.glide = "com.github.bumptech.glide:glide:$versions.glide" 109 | deps.constraint_layout = "androidx.constraint:constraint-layout:$versions.constraint_layout" 110 | deps.junit = "junit:junit:$versions.junit" 111 | deps.runner = "androidx.test:runner:$versions.runner" 112 | deps.rxjava2 = "io.reactivex.rxjava2:rxjava:$versions.rxjava2" 113 | deps.rxandroid = "io.reactivex.rxjava2:rxandroid:$versions.rxandroid" 114 | deps.rxbinding = "com.jakewharton.rxbinding2:rxbinding:$versions.rxbinding" 115 | deps.gson = "com.google.code.gson:gson:$versions.gson" 116 | deps.leakcanary = "com.squareup.leakcanary:leakcanary-android:$versions.leakcanary" 117 | 118 | //========xlibrary start=================// 119 | 120 | def xlibrary = [:] 121 | 122 | xlibrary.xui = "com.github.xuexiangjys:XUI:$versions.xui" 123 | xlibrary.xupdate = "com.github.xuexiangjys:XUpdate:$versions.xupdate" 124 | xlibrary.xaop_runtime = "com.github.xuexiangjys.XAOP:xaop-runtime:$versions.xaop" 125 | xlibrary.xaop_plugin = "com.github.xuexiangjys.XAOP:xaop-plugin:$versions.xaop" 126 | xlibrary.xutil_core = "com.github.xuexiangjys.XUtil:xutil-core:$versions.xutil" 127 | xlibrary.xhttp2 = "com.github.xuexiangjys:XHttp2:$versions.xhttp2" 128 | xlibrary.xpage_lib = "com.github.xuexiangjys.XPage:xpage-lib:$versions.xpage" 129 | xlibrary.xpage_compiler = "com.github.xuexiangjys.XPage:xpage-compiler:$versions.xpage" 130 | xlibrary.xrouter_runtime = "com.github.xuexiangjys.XRouter:xrouter-runtime:$versions.xrouter" 131 | xlibrary.xrouter_compiler = "com.github.xuexiangjys.XRouter:xrouter-compiler:$versions.xrouter" 132 | xlibrary.xrouter_plugin = "com.github.xuexiangjys.XRouter:xrouter-plugin:$versions.xrouter" 133 | 134 | deps.xlibrary = xlibrary 135 | 136 | //========xlibrary end=================// 137 | 138 | ext.deps = deps 139 | 140 | def build_versions = [:] 141 | build_versions.min_sdk = 19 142 | build_versions.target_sdk = 29 143 | build_versions.build_tools = "29.0.3" 144 | ext.build_versions = build_versions 145 | 146 | def app_release = [:] 147 | app_release.storeFile = "../keystores/android.keystore" 148 | app_release.storePassword = "xuexiang" 149 | app_release.keyAlias = "android.keystore" 150 | app_release.keyPassword = "xuexiang" 151 | 152 | ext.app_release = app_release 153 | 154 | /** 155 | * @return 是否为release 156 | */ 157 | def isRelease() { 158 | Gradle gradle = getGradle() 159 | String tskReqStr = gradle.getStartParameter().getTaskRequests().toString() 160 | 161 | Pattern pattern 162 | if (tskReqStr.contains("assemble")) { 163 | println tskReqStr 164 | pattern = Pattern.compile("assemble(\\w*)(Release|Debug)") 165 | } else { 166 | pattern = Pattern.compile("generate(\\w*)(Release|Debug)") 167 | } 168 | Matcher matcher = pattern.matcher(tskReqStr) 169 | 170 | if (matcher.find()) { 171 | String task = matcher.group(0).toLowerCase() 172 | println("[BuildType] Current task: " + task) 173 | return task.contains("release") 174 | } else { 175 | println "[BuildType] NO MATCH FOUND" 176 | return true 177 | } 178 | } 179 | 180 | ext.isRelease = this.&isRelease 181 | 182 | //默认添加代码仓库路径 183 | static def addRepos(RepositoryHandler handler) { 184 | handler.mavenLocal() 185 | handler.google { url 'https://maven.aliyun.com/repository/google' } 186 | handler.jcenter { url 'https://maven.aliyun.com/repository/jcenter' } 187 | handler.mavenCentral { url 'https://maven.aliyun.com/repository/central' } 188 | handler.maven { url "https://jitpack.io" } 189 | handler.maven { url 'https://maven.aliyun.com/repository/public' } 190 | handler.maven { url "https://repo1.maven.org/maven2/" } 191 | handler.maven { url 'https://oss.sonatype.org/content/repositories/public' } 192 | //Add the Local repository 193 | handler.maven { url 'LocalRepository' } 194 | } 195 | 196 | ext.addRepos = this.&addRepos 197 | 198 | 199 | //自动添加XAOP和XRouter插件 200 | project.buildscript.configurations.each { configuration -> 201 | def dependencies = getProject().dependencies 202 | if (configuration.name == "classpath") { 203 | //XAOP插件 204 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xaop_plugin)) 205 | //XRouter插件 206 | configuration.dependencies.add(dependencies.create(deps.xlibrary.xrouter_plugin)) 207 | } 208 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/core/BaseSimpleListFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.core; 19 | 20 | import android.content.res.Configuration; 21 | import android.os.Parcelable; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | 25 | import androidx.annotation.NonNull; 26 | import androidx.fragment.app.Fragment; 27 | 28 | import com.umeng.analytics.MobclickAgent; 29 | import com.xuexiang.xpage.base.XPageActivity; 30 | import com.xuexiang.xpage.base.XPageFragment; 31 | import com.xuexiang.xpage.base.XPageSimpleListFragment; 32 | import com.xuexiang.xpage.core.PageOption; 33 | import com.xuexiang.xpage.enums.CoreAnim; 34 | import com.xuexiang.xrouter.facade.service.SerializationService; 35 | import com.xuexiang.xrouter.launcher.XRouter; 36 | import com.xuexiang.xui.widget.actionbar.TitleBar; 37 | import com.xuexiang.xui.widget.actionbar.TitleUtils; 38 | 39 | import java.io.Serializable; 40 | 41 | /** 42 | * @author xuexiang 43 | * @since 2018/12/29 下午12:41 44 | */ 45 | public abstract class BaseSimpleListFragment extends XPageSimpleListFragment { 46 | 47 | @Override 48 | protected void initPage() { 49 | initTitle(); 50 | initViews(); 51 | initListeners(); 52 | } 53 | 54 | protected TitleBar initTitle() { 55 | return TitleUtils.addTitleBarDynamic(getToolbarContainer(), getPageTitle(), v -> popToBack()); 56 | } 57 | 58 | @Override 59 | public void onConfigurationChanged(@NonNull Configuration newConfig) { 60 | //屏幕旋转时刷新一下title 61 | super.onConfigurationChanged(newConfig); 62 | ViewGroup root = (ViewGroup) getRootView(); 63 | if (root.getChildAt(0) instanceof TitleBar) { 64 | root.removeViewAt(0); 65 | initTitle(); 66 | } 67 | } 68 | 69 | @Override 70 | public void onResume() { 71 | super.onResume(); 72 | MobclickAgent.onPageStart(getPageName()); 73 | } 74 | 75 | @Override 76 | public void onPause() { 77 | super.onPause(); 78 | MobclickAgent.onPageEnd(getPageName()); 79 | } 80 | 81 | //==============================页面跳转api===================================// 82 | 83 | /** 84 | * 打开一个新的页面【建议只在主tab页使用】 85 | * 86 | * @param clazz 页面的类 87 | * @param 88 | * @return 89 | */ 90 | public Fragment openNewPage(Class clazz) { 91 | return new PageOption(clazz) 92 | .setNewActivity(true) 93 | .open(this); 94 | } 95 | 96 | /** 97 | * 打开一个新的页面【建议只在主tab页使用】 98 | * 99 | * @param pageName 页面名 100 | * @param 101 | * @return 102 | */ 103 | public Fragment openNewPage(String pageName) { 104 | return new PageOption(pageName) 105 | .setAnim(CoreAnim.slide) 106 | .setNewActivity(true) 107 | .open(this); 108 | } 109 | 110 | 111 | /** 112 | * 打开一个新的页面【建议只在主tab页使用】 113 | * 114 | * @param clazz 页面的类 115 | * @param containActivityClazz 页面容器 116 | * @param 117 | * @return 118 | */ 119 | public Fragment openNewPage(Class clazz, @NonNull Class containActivityClazz) { 120 | return new PageOption(clazz) 121 | .setNewActivity(true) 122 | .setContainActivityClazz(containActivityClazz) 123 | .open(this); 124 | } 125 | 126 | /** 127 | * 打开一个新的页面【建议只在主tab页使用】 128 | * 129 | * @param clazz 页面的类 130 | * @param key 入参的键 131 | * @param value 入参的值 132 | * @param 133 | * @return 134 | */ 135 | public Fragment openNewPage(Class clazz, String key, Object value) { 136 | PageOption option = new PageOption(clazz).setNewActivity(true); 137 | return openPage(option, key, value); 138 | } 139 | 140 | public Fragment openPage(PageOption option, String key, Object value) { 141 | if (value instanceof Integer) { 142 | option.putInt(key, (Integer) value); 143 | } else if (value instanceof Float) { 144 | option.putFloat(key, (Float) value); 145 | } else if (value instanceof String) { 146 | option.putString(key, (String) value); 147 | } else if (value instanceof Boolean) { 148 | option.putBoolean(key, (Boolean) value); 149 | } else if (value instanceof Long) { 150 | option.putLong(key, (Long) value); 151 | } else if (value instanceof Double) { 152 | option.putDouble(key, (Double) value); 153 | } else if (value instanceof Parcelable) { 154 | option.putParcelable(key, (Parcelable) value); 155 | } else if (value instanceof Serializable) { 156 | option.putSerializable(key, (Serializable) value); 157 | } else { 158 | option.putString(key, serializeObject(value)); 159 | } 160 | return option.open(this); 161 | } 162 | 163 | /** 164 | * 打开页面 165 | * 166 | * @param clazz 页面的类 167 | * @param addToBackStack 是否加入回退栈 168 | * @param key 入参的键 169 | * @param value 入参的值 170 | * @param 171 | * @return 172 | */ 173 | public Fragment openPage(Class clazz, boolean addToBackStack, String key, String value) { 174 | return new PageOption(clazz) 175 | .setAddToBackStack(addToBackStack) 176 | .putString(key, value) 177 | .open(this); 178 | } 179 | 180 | /** 181 | * 打开页面 182 | * 183 | * @param clazz 页面的类 184 | * @param key 入参的键 185 | * @param value 入参的值 186 | * @param 187 | * @return 188 | */ 189 | public Fragment openPage(Class clazz, String key, Object value) { 190 | return openPage(clazz, true, key, value); 191 | } 192 | 193 | /** 194 | * 打开页面 195 | * 196 | * @param clazz 页面的类 197 | * @param addToBackStack 是否加入回退栈 198 | * @param key 入参的键 199 | * @param value 入参的值 200 | * @param 201 | * @return 202 | */ 203 | public Fragment openPage(Class clazz, boolean addToBackStack, String key, Object value) { 204 | PageOption option = new PageOption(clazz).setAddToBackStack(addToBackStack); 205 | return openPage(option, key, value); 206 | } 207 | 208 | /** 209 | * 打开页面 210 | * 211 | * @param clazz 页面的类 212 | * @param key 入参的键 213 | * @param value 入参的值 214 | * @param 215 | * @return 216 | */ 217 | public Fragment openPage(Class clazz, String key, String value) { 218 | return new PageOption(clazz) 219 | .putString(key, value) 220 | .open(this); 221 | } 222 | 223 | /** 224 | * 打开页面,需要结果返回 225 | * 226 | * @param clazz 页面的类 227 | * @param key 入参的键 228 | * @param value 入参的值 229 | * @param requestCode 请求码 230 | * @param 231 | * @return 232 | */ 233 | public Fragment openPageForResult(Class clazz, String key, Object value, int requestCode) { 234 | PageOption option = new PageOption(clazz).setRequestCode(requestCode); 235 | return openPage(option, key, value); 236 | } 237 | 238 | /** 239 | * 打开页面,需要结果返回 240 | * 241 | * @param clazz 页面的类 242 | * @param key 入参的键 243 | * @param value 入参的值 244 | * @param requestCode 请求码 245 | * @param 246 | * @return 247 | */ 248 | public Fragment openPageForResult(Class clazz, String key, String value, int requestCode) { 249 | return new PageOption(clazz) 250 | .setRequestCode(requestCode) 251 | .putString(key, value) 252 | .open(this); 253 | } 254 | 255 | /** 256 | * 打开页面,需要结果返回 257 | * 258 | * @param clazz 页面的类 259 | * @param requestCode 请求码 260 | * @param 261 | * @return 262 | */ 263 | public Fragment openPageForResult(Class clazz, int requestCode) { 264 | return new PageOption(clazz) 265 | .setRequestCode(requestCode) 266 | .open(this); 267 | } 268 | 269 | /** 270 | * 序列化对象 271 | * 272 | * @param object 需要序列化的对象 273 | * @return 序列化结果 274 | */ 275 | public String serializeObject(Object object) { 276 | return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "{}" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright 2018 xuexiangjys 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateproject/core/BaseFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateproject.core; 19 | 20 | import android.content.res.Configuration; 21 | import android.os.Parcelable; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | 26 | import androidx.annotation.NonNull; 27 | import androidx.annotation.Nullable; 28 | import androidx.fragment.app.Fragment; 29 | import androidx.viewbinding.ViewBinding; 30 | 31 | import com.umeng.analytics.MobclickAgent; 32 | import com.xuexiang.xpage.base.XPageActivity; 33 | import com.xuexiang.xpage.base.XPageFragment; 34 | import com.xuexiang.xpage.core.PageOption; 35 | import com.xuexiang.xpage.enums.CoreAnim; 36 | import com.xuexiang.xpage.utils.Utils; 37 | import com.xuexiang.xrouter.facade.service.SerializationService; 38 | import com.xuexiang.xrouter.launcher.XRouter; 39 | import com.xuexiang.xui.utils.WidgetUtils; 40 | import com.xuexiang.xui.widget.actionbar.TitleBar; 41 | import com.xuexiang.xui.widget.actionbar.TitleUtils; 42 | import com.xuexiang.xui.widget.progress.loading.IMessageLoader; 43 | 44 | import java.io.Serializable; 45 | import java.lang.reflect.Type; 46 | 47 | /** 48 | * 基础fragment 49 | * 50 | * @author xuexiang 51 | * @since 2018/5/25 下午3:44 52 | */ 53 | public abstract class BaseFragment extends XPageFragment { 54 | 55 | private IMessageLoader mIMessageLoader; 56 | 57 | /** 58 | * ViewBinding 59 | */ 60 | protected Binding binding; 61 | 62 | @Nullable 63 | @Override 64 | protected View onCreateContentView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, boolean attachToRoot) { 65 | binding = viewBindingInflate(inflater, container, attachToRoot); 66 | return binding.getRoot(); 67 | } 68 | 69 | /** 70 | * 构建ViewBinding 71 | * 72 | * @param inflater inflater 73 | * @param container 容器 74 | * @return ViewBinding 75 | */ 76 | @NonNull 77 | protected abstract Binding viewBindingInflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, boolean attachToRoot); 78 | 79 | /** 80 | * 获取Binding 81 | * 82 | * @return Binding 83 | */ 84 | public Binding getBinding() { 85 | return binding; 86 | } 87 | 88 | @Override 89 | protected void initPage() { 90 | initTitle(); 91 | initViews(); 92 | initListeners(); 93 | } 94 | 95 | protected TitleBar initTitle() { 96 | return TitleUtils.addTitleBarDynamic(getToolbarContainer(), getPageTitle(), v -> popToBack()); 97 | } 98 | 99 | @Override 100 | protected void initListeners() { 101 | 102 | } 103 | 104 | public IMessageLoader getMessageLoader() { 105 | if (mIMessageLoader == null) { 106 | mIMessageLoader = WidgetUtils.getMiniLoadingDialog(getContext()); 107 | } 108 | return mIMessageLoader; 109 | } 110 | 111 | public IMessageLoader getMessageLoader(String message) { 112 | if (mIMessageLoader == null) { 113 | mIMessageLoader = WidgetUtils.getMiniLoadingDialog(getContext(), message); 114 | } else { 115 | mIMessageLoader.updateMessage(message); 116 | } 117 | return mIMessageLoader; 118 | } 119 | 120 | @Override 121 | public void onConfigurationChanged(Configuration newConfig) { 122 | //屏幕旋转时刷新一下title 123 | super.onConfigurationChanged(newConfig); 124 | ViewGroup root = (ViewGroup) getRootView(); 125 | if (root.getChildAt(0) instanceof TitleBar) { 126 | root.removeViewAt(0); 127 | initTitle(); 128 | } 129 | } 130 | 131 | @Override 132 | public void onDestroyView() { 133 | if (mIMessageLoader != null) { 134 | mIMessageLoader.dismiss(); 135 | } 136 | super.onDestroyView(); 137 | binding = null; 138 | } 139 | 140 | 141 | @Override 142 | public void onResume() { 143 | super.onResume(); 144 | MobclickAgent.onPageStart(getPageName()); 145 | } 146 | 147 | @Override 148 | public void onPause() { 149 | super.onPause(); 150 | MobclickAgent.onPageEnd(getPageName()); 151 | } 152 | 153 | 154 | //==============================页面跳转api===================================// 155 | 156 | /** 157 | * 打开一个新的页面【建议只在主tab页使用】 158 | * 159 | * @param clazz 页面的类 160 | * @param 161 | * @return 162 | */ 163 | public Fragment openNewPage(Class clazz) { 164 | return new PageOption(clazz) 165 | .setNewActivity(true) 166 | .open(this); 167 | } 168 | 169 | /** 170 | * 打开一个新的页面【建议只在主tab页使用】 171 | * 172 | * @param pageName 页面名 173 | * @param 174 | * @return 175 | */ 176 | public Fragment openNewPage(String pageName) { 177 | return new PageOption(pageName) 178 | .setAnim(CoreAnim.slide) 179 | .setNewActivity(true) 180 | .open(this); 181 | } 182 | 183 | 184 | /** 185 | * 打开一个新的页面【建议只在主tab页使用】 186 | * 187 | * @param clazz 页面的类 188 | * @param containActivityClazz 页面容器 189 | * @param 190 | * @return 191 | */ 192 | public Fragment openNewPage(Class clazz, @NonNull Class containActivityClazz) { 193 | return new PageOption(clazz) 194 | .setNewActivity(true) 195 | .setContainActivityClazz(containActivityClazz) 196 | .open(this); 197 | } 198 | 199 | /** 200 | * 打开一个新的页面【建议只在主tab页使用】 201 | * 202 | * @param clazz 页面的类 203 | * @param key 入参的键 204 | * @param value 入参的值 205 | * @param 206 | * @return 207 | */ 208 | public Fragment openNewPage(Class clazz, String key, Object value) { 209 | PageOption option = new PageOption(clazz).setNewActivity(true); 210 | return openPage(option, key, value); 211 | } 212 | 213 | public Fragment openPage(PageOption option, String key, Object value) { 214 | if (value instanceof Integer) { 215 | option.putInt(key, (Integer) value); 216 | } else if (value instanceof Float) { 217 | option.putFloat(key, (Float) value); 218 | } else if (value instanceof String) { 219 | option.putString(key, (String) value); 220 | } else if (value instanceof Boolean) { 221 | option.putBoolean(key, (Boolean) value); 222 | } else if (value instanceof Long) { 223 | option.putLong(key, (Long) value); 224 | } else if (value instanceof Double) { 225 | option.putDouble(key, (Double) value); 226 | } else if (value instanceof Parcelable) { 227 | option.putParcelable(key, (Parcelable) value); 228 | } else if (value instanceof Serializable) { 229 | option.putSerializable(key, (Serializable) value); 230 | } else { 231 | option.putString(key, serializeObject(value)); 232 | } 233 | return option.open(this); 234 | } 235 | 236 | /** 237 | * 打开页面 238 | * 239 | * @param clazz 页面的类 240 | * @param addToBackStack 是否加入回退栈 241 | * @param key 入参的键 242 | * @param value 入参的值 243 | * @param 244 | * @return 245 | */ 246 | public Fragment openPage(Class clazz, boolean addToBackStack, String key, String value) { 247 | return new PageOption(clazz) 248 | .setAddToBackStack(addToBackStack) 249 | .putString(key, value) 250 | .open(this); 251 | } 252 | 253 | /** 254 | * 打开页面 255 | * 256 | * @param clazz 页面的类 257 | * @param key 入参的键 258 | * @param value 入参的值 259 | * @param 260 | * @return 261 | */ 262 | public Fragment openPage(Class clazz, String key, Object value) { 263 | return openPage(clazz, true, key, value); 264 | } 265 | 266 | /** 267 | * 打开页面 268 | * 269 | * @param clazz 页面的类 270 | * @param addToBackStack 是否加入回退栈 271 | * @param key 入参的键 272 | * @param value 入参的值 273 | * @param 274 | * @return 275 | */ 276 | public Fragment openPage(Class clazz, boolean addToBackStack, String key, Object value) { 277 | PageOption option = new PageOption(clazz).setAddToBackStack(addToBackStack); 278 | return openPage(option, key, value); 279 | } 280 | 281 | /** 282 | * 打开页面 283 | * 284 | * @param clazz 页面的类 285 | * @param key 入参的键 286 | * @param value 入参的值 287 | * @param 288 | * @return 289 | */ 290 | public Fragment openPage(Class clazz, String key, String value) { 291 | return new PageOption(clazz) 292 | .putString(key, value) 293 | .open(this); 294 | } 295 | 296 | /** 297 | * 打开页面,需要结果返回 298 | * 299 | * @param clazz 页面的类 300 | * @param key 入参的键 301 | * @param value 入参的值 302 | * @param requestCode 请求码 303 | * @param 304 | * @return 305 | */ 306 | public Fragment openPageForResult(Class clazz, String key, Object value, int requestCode) { 307 | PageOption option = new PageOption(clazz).setRequestCode(requestCode); 308 | return openPage(option, key, value); 309 | } 310 | 311 | /** 312 | * 打开页面,需要结果返回 313 | * 314 | * @param clazz 页面的类 315 | * @param key 入参的键 316 | * @param value 入参的值 317 | * @param requestCode 请求码 318 | * @param 319 | * @return 320 | */ 321 | public Fragment openPageForResult(Class clazz, String key, String value, int requestCode) { 322 | return new PageOption(clazz) 323 | .setRequestCode(requestCode) 324 | .putString(key, value) 325 | .open(this); 326 | } 327 | 328 | /** 329 | * 打开页面,需要结果返回 330 | * 331 | * @param clazz 页面的类 332 | * @param requestCode 请求码 333 | * @param 334 | * @return 335 | */ 336 | public Fragment openPageForResult(Class clazz, int requestCode) { 337 | return new PageOption(clazz) 338 | .setRequestCode(requestCode) 339 | .open(this); 340 | } 341 | 342 | /** 343 | * 序列化对象 344 | * 345 | * @param object 需要序列化的对象 346 | * @return 序列化结果 347 | */ 348 | public String serializeObject(Object object) { 349 | return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); 350 | } 351 | 352 | /** 353 | * 反序列化对象 354 | * 355 | * @param input 反序列化的内容 356 | * @param clazz 类型 357 | * @return 反序列化结果 358 | */ 359 | public T deserializeObject(String input, Type clazz) { 360 | return XRouter.getInstance().navigation(SerializationService.class).parseObject(input, clazz); 361 | } 362 | 363 | @Override 364 | protected void hideCurrentPageSoftInput() { 365 | if (getActivity() == null) { 366 | return; 367 | } 368 | // 记住,要在xml的父布局加上android:focusable="true" 和 android:focusableInTouchMode="true" 369 | Utils.hideSoftInputClearFocus(getActivity().getCurrentFocus()); 370 | } 371 | 372 | } 373 | --------------------------------------------------------------------------------