第一次提交

This commit is contained in:
HJQ 2018-10-25 09:57:43 +08:00
commit 8c7834b2c3
158 changed files with 7215 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
/.idea

66
AndroidBug.md Normal file
View File

@ -0,0 +1,66 @@
# Android普遍存在的问题
> 这些问题已经在模板项目中已经被修复好了,具体修复过程如下
#### 修复 Button 在Android 5.1 之后英文字符串自动大写的问题
> 给 Button 添加如下属性即可模板工程已经把该属性封装到Style中直接引用style="@style/ButtonStyle"即可
android:textAllCaps="false"
#### 修复 Button 在设置状态选择器后仍然残留按压阴影的问题
> 给 Button 设置样式如下即可模板工程已经把该属性封装到Style中直接引用style="@style/ButtonStyle"即可
style="Widget.AppCompat.Button.Borderless"
#### 修复某些低配置机型启动页停留在白屏的时间比较长的问题
> 某些低配置机型上出现该问题比较明显如果配置好的机型则看不出来添加一个透明的Activity主题样式
<!-- 解决启动页白屏的问题 -->
<style name="LauncherTheme" parent="AppTheme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
> 在清单文件中给启动页的Activity设置主题样式
<!-- 启动页面因为使用了LauncherTheme所以不要给这个Activity设置screenOrientation属性会导致崩溃 -->
<activity
android:name=".ui.activity.LauncherActivity"
android:theme="@style/LauncherTheme">
<!-- 程序入口 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
> 还有一点需要特别注意Android 8.0 及之后不允许透明主题的Activity设置屏幕方向所以请不要给Activity设置该属性否则会导致崩溃
#### 修复某些机型在 WebView 长按时布局被顶下来的问题
> 这个问题在只要界面有 WebView 的情况才会发生,在 Android 5.1 经过测试,在 WebView 中长按选择复制文字时,会显示一个类似于 Actionbar 的控件但是这个东西叫做ActionMode会将当前 Activity 所在布局顶下去,这时会和我们项目中的标题栏出现冲突,类似于一个界面同时出现了两个标题栏的效果,解决的方法很简单,就是让出现的 ActionMode 悬浮在 Activity上这样就把项目中的标题栏遮挡住了不会出现那种类似一个界面出现两种标题栏的效果当 WebView 取消长按复制文字后ActionMode也会随之消失
> 如何让 ActionMode 悬浮在 Activity 上呢?其实很简单,在 Application 主题中加入以下属性
<!-- ActionMode覆盖Actionbar不顶下来 -->
<item name="windowActionModeOverlay">true</item>
<item name="android:windowContentOverlay">@null</item>
<!-- ActionMode的颜色 -->
<item name="actionModeBackground">@color/colorPrimary</item>
#### 修复 任务栈中 首页Activity 被重复启动的问题
> 这个问题导致是因为LauncherActivity作为APP的第一个界面销毁后没有保存任务栈的状态导致我们在桌面上启动的时候系统误认为当前启动LauncherActivity的任务栈已经被销毁所以重新创建了新的任务栈并且跳转到LauncherActivity最终导致用户从桌面点击APP图标时总是跳转到LauncherActivity而不是HomeActivity
<!-- 主页界面 -->
<activity
android:name=".ui.activity.HomeActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTop" />

BIN
AndroidProject.apk Normal file

Binary file not shown.

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

68
ProjectDetails.md Normal file
View File

@ -0,0 +1,68 @@
# 项目介绍
## Module 介绍
> 这里的项目 Module 关系十分简单,只有两个:
* app关于 APP 功能实现的业务逻辑代码,集成了一些常用的框架
* baselibrary只存放关于技术实现的代码不集成任何框架
> 本项目基于最新的 Android SDK 28 编译,[点击此处查看配置](build.gradle),最低安装要求为 Android 4.0
> 其中 Android Studio 的版本为3.2Gradle的版本为 4.4
targetSdkVersion = 28
compileSdkVersion = 28
buildToolsVersion = '28.0.0'
## Activity关系
> 模板项目中的Activity有三层继承关系Fragment 和 Application 继承关系也是雷同,这里不再赘述
* [BaseActivity](baselibrary/src/main/java/com/hjq/baselibrary/base/BaseActivity.java)继承至AppCompatActivity主要做一些简单的代码封装
* [UIActivity](app/src/main/java/com/hjq/demo/common/UIActivity.java)继承至BaseActivity主要加入了界面侧滑和状态栏沉浸式
* [CommonActivity](app/src/main/java/com/hjq/demo/common/CommonActivity.java)继承至UIActivity主要实现项目中的业务逻辑代码
## View 样式
* 普通的圆角 Button 样式style="@style/ButtonStyle"
* 普通不带圆角 Button 样式style="@style/RectButtonStyle"
* 普通 EditText 样式style="@style/EditTextStyle"
* 普通的水平分割线 View 样式style="@style/HorizontalLineStyle"
* 普通的垂直分割线 View 样式style="@style/VerticalLineStyle"
## View 使用
> 具体用法可以[点击此处查看示例](app/src/main/res/layout/fragment_test_b.xml)
![](picture/2.png)
## 框架使用
> 具体用法可以[点击此处查看示例](app/src/main/res/layout/fragment_test_c.xml)
![](picture/3.png)
## 友盟多渠道打包
> 具体配置可以[点击此处查看](app/build.gradle)
![](picture/flavors_1.jpg)
![](picture/flavors_2.jpg)
![](picture/flavors_3.jpg)
> 使用友盟多渠道统计时需要[更换清单文件中的key](app/src/main/AndroidManifest.xml)
<!-- 友盟统计 -->
<meta-data
android:name="UMENG_APPKEY"
android:value="XXXXXXXXXXXXXXXXXXXXXXXX" /><!-- 要注意更换key -->

89
README.md Normal file
View File

@ -0,0 +1,89 @@
# 模板工程
> 当我们日复一日年复一年的搬砖的时候,你是否曾想过提升一下开发效率,如果一个模板的项目摆在你的面前,你还会选择自己搭架构么
> 但是做出一个好的模板项目并非易事,有多少人愿意选择去做,还有多少人选择努力去做好,可能寥寥无几,但是你今天看到的,正是你所想要的,一个真正能解决你开发新项目时最大痛点的模板工程,你不需要再麻木 Copy 原有旧项目的代码,只需改动少量代码就能得到想要的效果,你会发现开发新项目其实是一件很快乐的事
> 已经正式投入公司新项目开发多时,暂时没有其他毛病,[点击此处下载Demo](https://raw.githubusercontent.com/getActivity/AndroidProject/master/AndroidProject.apk)
![](picture/0.png)
![](picture/1.gif)
![](picture/2.png)
![](picture/3.png)
![](picture/4.png)
![](picture/5.png)
![](picture/6.png)
![](picture/7.png)
![](picture/8.png)
## 集成框架
* 状态栏沉浸:[https://github.com/gyf-dev/ImmersionBar](https://github.com/gyf-dev/ImmersionBar)
* 侧滑功能:[https://github.com/bingoogolapple/BGASwipeBackLayout-Android](https://github.com/bingoogolapple/BGASwipeBackLayout-Android)
* 权限请求框架:[https://github.com/getActivity/XXPermissions](https://github.com/getActivity/XXPermissions)
* 标题栏:[https://github.com/getActivity/TitleBar](https://github.com/getActivity/TitleBar)
* 吐司工具类:[https://github.com/getActivity/ToastUtils](https://github.com/getActivity/ToastUtils)
* 圆形的ImageView[https://github.com/hdodenhof/CircleImageView](https://github.com/hdodenhof/CircleImageView)
* ButterKnife注解[https://github.com/JakeWharton/butterknife](https://github.com/JakeWharton/butterknife)
#### 模板项目亮点,[查看详细](ProjectDetails.md)
* APP用户体验已经集成界面侧滑以及状态栏沉浸框架
* 必备优秀框架危险权限处理标题栏控件吐司工具类圆形ImageView
* 常用页面模板:启动界面,主页界面,登录界面,注册界面,关于界面,浏览器界面
* 集成友盟统计集成友盟统计并且加入了友盟多渠道打包在发布release包时可选择渠道包
* 常用自定义View圆形ImageView验证码点击倒计时View带清除按钮的EditText正方形的FrameLayout、LinearLayout、RelativeLayout、ImageView
* 界面样式规范:项目的严格按照 Material Design 设计进行配色统一和规范Button和EditText控件样式
* 代码注释规范:代码严格按照谷歌级规范来做,如需寻找友盟相关的代码,全局搜索 "友盟" 即可,任何一处不关于原生的 API 都有非常完善的注释
#### 修复Android普遍存在的问题[查看详细](AndroidBug.md)
* 修复 Button 在Android 5.1 之后英文字符串自动大写的问题
* 修复 Button 在设置状态选择器后仍然残留按压阴影的问题
* 修复某些低配置机型启动页停留在白屏的时间比较长的问题
* 修复某些机型在 WebView 长按时布局被顶下来的问题
* 修复 任务栈中 首页Activity 被重复启动的问题
#### Android技术讨论Q群78797078
## License
```text
Copyright 2018 Huang Jinqun
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

93
app/build.gradle Normal file
View File

@ -0,0 +1,93 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.hjq.demo"
minSdkVersion 14
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 10
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// Log
buildConfigField "boolean", "LOG_DEBUG", "false"
// resource文件minifyEnabled必须打开
shrinkResources true
// ZipAlign优化
zipAlignEnabled true
//
minifyEnabled true
//
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
//ZipAlign优化
zipAlignEnabled false
//
minifyEnabled false
//
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
flavorDimensions "default"//便
//
productFlavors {
kuan {} //
tencent {} //
baidu {} //
xiaomi {} //
huawei {} //
qihu {} //360
productFlavors.all { flavor ->
flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
api project(':baselibrary')
// aar
//api(name: 'password_dialog', ext: 'aar')
api "com.android.support:appcompat-v7:$rootProject.ext.supportLibraryVersion"
api "com.android.support:design:$rootProject.ext.supportLibraryVersion"
api "com.android.support:support-v4:$rootProject.ext.supportLibraryVersion"
api "com.android.support:cardview-v7:$rootProject.ext.supportLibraryVersion"
api "com.android.support.constraint:constraint-layout:$rootProject.ext.constraintlayoutVersion"
// https://github.com/gyf-dev/ImmersionBar
api 'com.gyf.barlibrary:barlibrary:2.3.0'
// https://github.com/bingoogolapple/BGASwipeBackLayout-Android
api 'cn.bingoogolapple:bga-swipebacklayout:1.1.9@aar'
// https://github.com/getActivity/XXPermissions
api 'com.hjq:xxpermissions:5.2'
// https://github.com/getActivity/TitleBar
api 'com.hjq:titlebar:3.2'
// https://github.com/getActivity/ToastUtils
api 'com.hjq:toast:2.5'
// ImageViewhttps://github.com/hdodenhof/CircleImageView
api 'de.hdodenhof:circleimageview:2.2.0'
// ButterKnife注解库https://github.com/JakeWharton/butterknife
api "com.jakewharton:butterknife:$rootProject.ext.butterknifeVersion"
annotationProcessor "com.jakewharton:butterknife-compiler:$rootProject.ext.butterknifeVersion"
//
compileOnly 'com.umeng.analytics:analytics:6.1.4'
}
repositories {
flatDir {
dirs 'libs' //aar的目录地址
}
}

Binary file not shown.

225
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,225 @@
#指定代码的压缩级别
-optimizationpasses 5
#包明不混合大小写
-dontusemixedcaseclassnames
#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses
#优化 不优化输入的类文件
-dontoptimize
#预校验
-dontpreverify
#混淆时是否记录日志
-verbose
# 混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#保护注解
-keepattributes *Annotation*
# 保持哪些类不被混淆
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
#如果有引用v4包可以添加下面这行
-keep public class * extends android.support.v4.app.Fragment
#忽略警告
-ignorewarning
#####################记录生成的日志数据,gradle build时在本项目根目录输出################
#apk 包内所有 class 的内部结构
-dump class_files.txt
#未混淆的类和成员
-printseeds seeds.txt
#列出从 apk 中删除的代码
-printusage unused.txt
#混淆前后的映射
-printmapping mapping.txt
#####################记录生成的日志数据gradle build时 在本项目根目录输出-end################
################混淆保护自己项目的部分代码以及引用的第三方jar包library#########################
#-libraryjars libs/umeng-analytics-v5.2.4.jar
#-libraryjars libs/alipaysdk.jar
#-libraryjars libs/alipaysecsdk.jar
#-libraryjars libs/alipayutdid.jar
#-libraryjars libs/wup-1.0.0-SNAPSHOT.jar
#-libraryjars libs/weibosdkcore.jar
#三星应用市场需要添加:sdk-v1.0.0.jar,look-v1.0.1.jar
#-libraryjars libs/sdk-v1.0.0.jar
#-libraryjars libs/look-v1.0.1.jar
#我是以libaray的形式引用了一个图片加载框架,如果不想混淆 keep
-keep class com.nostra13.universalimageloader.** { *; }
#友盟
-keep class com.umeng.**{*;}
#支付宝
-keep class com.alipay.android.app.IAliPay{*;}
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.lib.ResourceMap{*;}
#信鸽推送
-keep class com.tencent.android.tpush.** {* ;}
-keep class com.tencent.mid.** {* ;}
#自己项目特殊处理代码
#忽略警告
-dontwarn com.veidy.mobile.common.**
#保留一个完整的包
-keep class com.veidy.mobile.common.** {
*;
}
-keep class com.veidy.activity.login.WebLoginActivity{*;}
-keep class com.veidy.activity.UserInfoFragment{*;}
-keep class com.veidy.activity.HomeFragmentActivity{*;}
-keep class com.veidy.activity.CityActivity{*;}
-keep class com.veidy.activity.ClinikActivity{*;}
#如果引用了v4或者v7包
-dontwarn android.support.**
############混淆保护自己项目的部分代码以及引用的第三方jar包library-end##################
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
#保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
#保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
#保持自定义控件类不被混淆
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
#保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#保持 Serializable 不被混淆
-keepnames class * implements java.io.Serializable
#保持 Serializable 不被混淆并且enum 类也不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
#保持枚举 enum 类不被混淆 如果混淆报错,建议直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可
#-keepclassmembers enum * {
# public static **[] values();
# public static ** valueOf(java.lang.String);
#}
-keepclassmembers class * {
public void *ButtonClicked(android.view.View);
}
#不混淆资源类
-keepclassmembers class **.R$* {
public static <fields>;
}
#避免混淆泛型 如果混淆报错建议关掉
#keepattributes Signature
#移除log 测试了下没有用还是建议自己定义一个开关控制是否输出日志
#-assumenosideeffects class android.util.Log {
# public static boolean isLoggable(java.lang.String, int);
# public static int v(...);
# public static int i(...);
# public static int w(...);
# public static int d(...);
# public static int e(...);
#}
# webview + js
-keepattributes *JavascriptInterface*
# keep 使用 webview 的类
-keepclassmembers class com.veidy.activity.WebViewActivity {
public *;
}
# keep 使用 webview 的类的所有的内部类
-keepclassmembers class com.veidy.activity.WebViewActivity$*{
*;
}
# 不混淆WebChromeClient中的openFileChooser方法
-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}
#友盟统计
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
#极光推送
-dontoptimize
-dontpreverify
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-keep class * extends cn.jpush.android.helpers.JPushMessageReceiver { *; }
-dontwarn cn.jiguang.**
-keep class cn.jiguang.** { *; }
# support-v7-appcompat
-keep public class android.support.v7.widget.** { *; }
-keep public class android.support.v7.internal.widget.** { *; }
-keep public class android.support.v7.internal.view.menu.** { *; }
-keep public class * extends android.support.v4.view.ActionProvider {
public <init>(android.content.Context);
}
# support-design
-dontwarn android.support.design.**
-keep class android.support.design.** { *; }
-keep interface android.support.design.** { *; }
-keep public class android.support.design.R$* { *; }

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hjq.demo">
<!-- 外部存储读写权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 拍照权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 联网权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 友盟统计 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:name=".common.CommonApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<!-- 友盟统计 -->
<meta-data
android:name="UMENG_APPKEY"
android:value="XXXXXXXXXXXXXXXXXXXXXXXX" /><!-- 要注意更换key -->
<!-- 友盟多渠道打包 -->
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}" />
<!-- 启动页面因为使用了LauncherTheme所以不要给这个Activity设置screenOrientation属性会导致崩溃 -->
<activity
android:name=".ui.activity.LauncherActivity"
android:theme="@style/LauncherTheme">
<!-- 程序入口 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 主页界面 -->
<activity
android:name=".ui.activity.HomeActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTop" />
<!-- 登录界面 -->
<activity
android:name=".ui.activity.LoginActivity"
android:launchMode="singleTop" />
<!-- 注册界面 -->
<activity
android:name=".ui.activity.RegisterActivity"
android:launchMode="singleTop" />
<!-- 关于界面 -->
<activity
android:name=".ui.activity.AboutActivity"
android:launchMode="singleTop" />
<!-- 浏览器界面 -->
<activity
android:name=".ui.activity.WebActivity"
android:launchMode="singleTop" />
</application>
</manifest>

View File

@ -0,0 +1,132 @@
package com.hjq.demo.common;
import android.content.pm.ActivityInfo;
import android.view.View;
import com.hjq.bar.OnTitleBarListener;
import com.hjq.bar.TitleBar;
import com.umeng.analytics.MobclickAgent;
import butterknife.ButterKnife;
import butterknife.Unbinder;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 项目中的Activity基类
*/
public abstract class CommonActivity extends UIActivity
implements OnTitleBarListener {
private Unbinder mButterKnife;//View注解
@Override
public void init() {
//初始化标题栏的监听
if (getTitleBarId() > 0) {
if (findViewById(getTitleBarId()) instanceof TitleBar) {
((TitleBar) findViewById(getTitleBarId())).setOnTitleBarListener(this);
}
}
mButterKnife = ButterKnife.bind(this);
initOrientation();
super.init();
}
/**
* 初始化横竖屏方向会和 LauncherTheme 主题样式有冲突注意不要同时使用
*/
protected void initOrientation() {
//如果没有指定屏幕方向则默认为竖屏
if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
/**
* 设置标题栏的标题
*/
@Override
public void setTitle(int titleId) {
setTitle(getText(titleId));
}
/**
* 设置标题栏的标题
*/
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
TitleBar titleBar = getTitleBar();
if (titleBar != null) {
titleBar.setTitle(title);
}
}
protected TitleBar getTitleBar() {
if (getTitleBarId() > 0 && findViewById(getTitleBarId()) instanceof TitleBar) {
return findViewById(getTitleBarId());
}
return null;
}
@Override
public boolean statusBarDarkFont() {
//返回true表示黑色字体
return true;
}
/**
* {@link OnTitleBarListener}
*/
/**
* 标题栏左边的View被点击了
*/
@Override
public void onLeftClick(View v) {
onBackPressed();
}
/**
* 标题栏中间的View被点击了
*/
@Override
public void onTitleClick(View v) {}
/**
* 标题栏右边的View被点击了
*/
@Override
public void onRightClick(View v) {}
@Override
protected void onResume() {
super.onResume();
// 手动统计页面
MobclickAgent.onPageStart(getClass().getSimpleName());
// 友盟统计
MobclickAgent.onResume(this);
}
@Override
protected void onPause() {
super.onPause();
// 手动统计页面必须保证 onPageEnd onPause 之前调用因为SDK会在 onPause 中保存onPageEnd统计到的页面数据
MobclickAgent.onPageEnd(getClass().getSimpleName());
// 友盟统计
MobclickAgent.onPause(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mButterKnife != null) mButterKnife.unbind();
}
}

View File

@ -0,0 +1,24 @@
package com.hjq.demo.common;
import com.hjq.toast.ToastUtils;
import com.umeng.analytics.MobclickAgent;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 项目中的Activity基类
*/
public class CommonApplication extends UIApplication {
@Override
public void onCreate() {
super.onCreate();
// 初始化吐司工具类
ToastUtils.init(getApplicationContext());
// 友盟统计
MobclickAgent.setScenarioType(getApplicationContext(), MobclickAgent.EScenarioType.E_UM_NORMAL);
}
}

View File

@ -0,0 +1,50 @@
package com.hjq.demo.common;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.umeng.analytics.MobclickAgent;
import butterknife.ButterKnife;
import butterknife.Unbinder;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 项目中Fragment懒加载基类
*/
public abstract class CommonLazyFragment extends UILazyFragment {
private Unbinder mButterKnife;// View注解
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
mButterKnife = ButterKnife.bind(this, view);
return view;
}
@Override
public void onResume() {
super.onResume();
// 友盟统计
MobclickAgent.onResume(getContext());
}
@Override
public void onPause() {
super.onPause();
// 友盟统计
MobclickAgent.onPause(getContext());
}
@Override
public void onDestroy() {
super.onDestroy();
mButterKnife.unbind();
}
}

View File

@ -0,0 +1,173 @@
package com.hjq.demo.common;
import android.os.Build;
import android.os.Bundle;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import com.gyf.barlibrary.ImmersionBar;
import com.hjq.baselibrary.base.BaseActivity;
import com.hjq.demo.R;
import cn.bingoogolapple.swipebacklayout.BGASwipeBackHelper;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 支持沉浸式和侧滑的Activity基类默认开启沉浸式状态栏和侧滑功能
*/
public abstract class UIActivity extends BaseActivity
implements BGASwipeBackHelper.Delegate, ViewTreeObserver.OnGlobalLayoutListener {
private ImmersionBar mImmersionBar;//状态栏沉浸
private BGASwipeBackHelper mSwipeBackHelper;//侧滑返回
@Override
protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState) 之前调用该方法
initSwipeBackFinish();
super.onCreate(savedInstanceState);
}
public void init(){
//初始化沉浸式状态栏
if (isStatusBarEnabled()) {
statusBarConfig().init();
}
//设置标题栏
if (getTitleBarId() > 0) {
ImmersionBar.setTitleBar(this, findViewById(getTitleBarId()));
}
initView();
initData();
}
public BGASwipeBackHelper getSwipeBackHelper() {
return mSwipeBackHelper;
}
/**
* 初始化滑动返回 super.onCreate(savedInstanceState) 之前调用该方法
*/
private void initSwipeBackFinish() {
mSwipeBackHelper = new BGASwipeBackHelper(this, this);
// 必须在 Application onCreate 方法中执行 BGASwipeBackHelper.init 来初始化滑动返回
// 下面几项可以不配置这里只是为了讲述接口用法
// 设置滑动返回是否可用默认值为 true
mSwipeBackHelper.setSwipeBackEnable(true);
// 设置是否仅仅跟踪左侧边缘的滑动返回默认值为 true
mSwipeBackHelper.setIsOnlyTrackingLeftEdge(true);
// 设置是否是微信滑动返回样式默认值为 true
mSwipeBackHelper.setIsWeChatStyle(true);
// 设置阴影资源 id默认值为 R.drawable.bga_sbl_shadow
mSwipeBackHelper.setShadowResId(R.drawable.bga_sbl_shadow);
// 设置是否显示滑动返回的阴影效果默认值为 true
mSwipeBackHelper.setIsNeedShowShadow(true);
// 设置阴影区域的透明度是否根据滑动的距离渐变默认值为 true
mSwipeBackHelper.setIsShadowAlphaGradient(true);
// 设置触发释放后自动滑动返回的阈值默认值为 0.3f
mSwipeBackHelper.setSwipeBackThreshold(0.3f);
// 设置底部导航条是否悬浮在内容上默认值为 false
mSwipeBackHelper.setIsNavigationBarOverlap(false);
}
/**
* {@link BGASwipeBackHelper.Delegate}
*/
/**
* 是否支持滑动返回这里在父类中默认返回 true 来支持滑动返回如果某个界面不想支持滑动返回则重写该方法返回 false 即可
*/
@Override
public boolean isSupportSwipeBack() {
return true;
}
/**
* 正在滑动返回
*
* @param slideOffset 0 1
*/
@Override
public void onSwipeBackLayoutSlide(float slideOffset) {}
/**
* 没达到滑动返回的阈值取消滑动返回动作回到默认状态
*/
@Override
public void onSwipeBackLayoutCancel() {}
/**
* 滑动返回执行完毕销毁当前 Activity
*/
@Override
public void onSwipeBackLayoutExecuted() {
mSwipeBackHelper.swipeBackward();
}
@Override
public void onBackPressed() {
// 正在滑动返回的时候取消返回按钮事件
if (mSwipeBackHelper.isSliding()) {
return;
}
mSwipeBackHelper.backward();
super.onBackPressed();
}
/**
* 是否使用沉浸式状态栏
*/
public boolean isStatusBarEnabled() {
return true;
}
/**
* 获取状态栏沉浸的配置对象
*/
public ImmersionBar getStatusBarConfig() {
return mImmersionBar;
}
/**
* 初始化沉浸式状态栏
*/
private ImmersionBar statusBarConfig() {
//在BaseActivity里初始化
mImmersionBar = ImmersionBar.with(this)
.statusBarDarkFont(statusBarDarkFont()) //默认状态栏字体颜色为黑色
.keyboardEnable(false, WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); //解决软键盘与底部输入框冲突问题默认为false还有一个重载方法可以指定软键盘mode
//必须设置View树布局变化监听否则软键盘无法顶上去还有模式必须是SOFT_INPUT_ADJUST_PAN
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(this);
return mImmersionBar;
}
/**
* {@link ViewTreeObserver.OnGlobalLayoutListener}
*/
@Override
public void onGlobalLayout() {}//不用写任何方法
/**
* 获取状态栏字体颜色
*/
public boolean statusBarDarkFont() {
//返回false表示白色字体
return true;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mImmersionBar != null) mImmersionBar.destroy();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
}

View File

@ -0,0 +1,25 @@
package com.hjq.demo.common;
import android.app.Application;
import cn.bingoogolapple.swipebacklayout.BGASwipeBackHelper;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 支持侧滑的UIApplication基类
*/
public abstract class UIApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
/**
* 必须在 Application onCreate 方法中执行 BGASwipeBackHelper.init 来初始化滑动返回
* 第一个参数应用程序上下文
* 第二个参数如果发现滑动返回后立即触摸界面时应用崩溃请把该界面里比较特殊的 View class 添加到该集合中目前在库中已经添加了 WebView SurfaceView
*/
BGASwipeBackHelper.init(this, null);
}
}

View File

@ -0,0 +1,83 @@
package com.hjq.demo.common;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import com.gyf.barlibrary.ImmersionBar;
import com.hjq.baselibrary.base.BaseLazyFragment;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 支持沉浸式Fragment懒加载基类默认不开启沉浸式
*/
public abstract class UILazyFragment extends BaseLazyFragment {
private ImmersionBar mImmersionBar;//状态栏沉浸
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//初始化沉浸式状态栏
if (isVisibleToUser() && isStatusBarEnabled() && isLazyLoad()) {
statusBarConfig().init();
}
//设置标题栏
if (!isLazyLoad() && getTitleBarId() > 0) {
ImmersionBar.setTitleBar(mActivity, view.findViewById(getTitleBarId()));
}
}
/**
* 是否在Fragment使用沉浸式
*/
public boolean isStatusBarEnabled() {
return false;
}
/**
* 获取状态栏沉浸的配置对象
*/
protected ImmersionBar getStatusBarConfig() {
return mImmersionBar;
}
/**
* 初始化沉浸式
*/
private ImmersionBar statusBarConfig() {
//在BaseActivity里初始化
mImmersionBar = ImmersionBar.with(this)
.statusBarDarkFont(statusBarDarkFont()) //默认状态栏字体颜色为黑色
.keyboardEnable(true); //解决软键盘与底部输入框冲突问题默认为false还有一个重载方法可以指定软键盘mode
return mImmersionBar;
}
/**
* 获取状态栏字体颜色
*/
protected boolean statusBarDarkFont() {
//返回true表示黑色字体
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mImmersionBar != null) mImmersionBar.destroy();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && isStatusBarEnabled() && isLazyLoad()) {
// 重新初始化状态栏
statusBarConfig().init();
}
}
}

View File

@ -0,0 +1,33 @@
package com.hjq.demo.ui.activity;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonActivity;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 关于界面
*/
public class AboutActivity extends CommonActivity {
@Override
protected int getLayoutId() {
return R.layout.activity_about;
}
@Override
protected int getTitleBarId() {
return R.id.tb_about_title;
}
@Override
protected void initView() {
}
@Override
protected void initData() {
}
}

View File

@ -0,0 +1,33 @@
package com.hjq.demo.ui.activity;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonActivity;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 可进行拷贝的副本
*/
public class CopyActivity extends CommonActivity {
@Override
protected int getLayoutId() {
return R.layout.activity_copy;
}
@Override
protected int getTitleBarId() {
return R.id.tb_copy_title;
}
@Override
protected void initView() {
}
@Override
protected void initData() {
}
}

View File

@ -0,0 +1,137 @@
package com.hjq.demo.ui.activity;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.view.ViewPager;
import android.view.MenuItem;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonActivity;
import com.hjq.demo.ui.adapter.HomeViewPagerAdapter;
import com.hjq.baselibrary.utils.OnClickUtils;
import com.hjq.toast.ToastUtils;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 主页界面
*/
public class HomeActivity extends CommonActivity implements
ViewPager.OnPageChangeListener, BottomNavigationView.OnNavigationItemSelectedListener {
@BindView(R.id.vp_home_pager)
ViewPager mViewPager;
@BindView(R.id.bv_home_navigation)
BottomNavigationView mBottomNavigationView;
@Override
protected int getLayoutId() {
return R.layout.activity_home;
}
@Override
protected int getTitleBarId() {
return 0;
}
@Override
protected void initView() {
// 限制页面数量
mViewPager.setOffscreenPageLimit(4);
mViewPager.addOnPageChangeListener(this);
// 不使用图标默认变色
mBottomNavigationView.setItemIconTintList(null);
mBottomNavigationView.setOnNavigationItemSelectedListener(this);
}
@Override
protected void initData() {
mViewPager.setAdapter(new HomeViewPagerAdapter(this));
}
/**
* {@link ViewPager.OnPageChangeListener}
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageSelected(int position) {
switch (position) {
case 0:
mBottomNavigationView.setSelectedItemId(R.id.menu_home);
break;
case 1:
mBottomNavigationView.setSelectedItemId(R.id.home_found);
break;
case 2:
mBottomNavigationView.setSelectedItemId(R.id.home_message);
break;
case 3:
mBottomNavigationView.setSelectedItemId(R.id.home_me);
break;
}
}
@Override
public void onPageScrollStateChanged(int state) {}
/**
* {@link BottomNavigationView.OnNavigationItemSelectedListener}
*/
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_home:
mViewPager.setCurrentItem(0);
return true;
case R.id.home_found:
mViewPager.setCurrentItem(1);
return true;
case R.id.home_message:
mViewPager.setCurrentItem(2);
return true;
case R.id.home_me:
mViewPager.setCurrentItem(3);
return true;
}
return false;
}
@Override
public void onBackPressed() {
if (OnClickUtils.isOnDoubleClick()) {
//移动到上一个任务栈避免侧滑引起的不良反应
moveTaskToBack(false);
getWindow().getDecorView().postDelayed(new Runnable() {
@Override
public void run() {
//销毁掉当前界面
finish();
}
}, 300);
} else {
ToastUtils.show(getResources().getString(R.string.home_exit_hint));
}
}
@Override
protected void onDestroy() {
mViewPager.removeOnPageChangeListener(this);
mViewPager.setAdapter(null);
mBottomNavigationView.setOnNavigationItemSelectedListener(null);
super.onDestroy();
}
@Override
public boolean isSupportSwipeBack() {
// 不使用侧滑功能
return !super.isSupportSwipeBack();
}
}

View File

@ -0,0 +1,160 @@
package com.hjq.demo.ui.activity;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.widget.ImageView;
import android.widget.TextView;
import com.gyf.barlibrary.BarHide;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonActivity;
import com.hjq.permissions.OnPermission;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.hjq.toast.ToastUtils;
import java.util.List;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 启动界面
*/
public class LauncherActivity extends CommonActivity
implements OnPermission, Animation.AnimationListener {
@BindView(R.id.iv_launcher_bg)
ImageView mImageView;
@BindView(R.id.iv_launcher_icon)
ImageView mIconView;
@BindView(R.id.iv_launcher_name)
TextView mTextView;
@Override
protected int getLayoutId() {
return R.layout.activity_launcher;
}
@Override
protected int getTitleBarId() {
return 0;
}
@Override
protected void initView() {
//初始化动画
initStartAnim();
//设置状态栏和导航栏参数
getStatusBarConfig()
.fullScreen(true)//有导航栏的情况下activity全屏显示也就是activity最下面被导航栏覆盖不写默认非全屏
.hideBar(BarHide.FLAG_HIDE_STATUS_BAR)//隐藏状态栏
.transparentNavigationBar()//透明导航栏不写默认黑色(设置此方法fullScreen()方法自动为true)
.init();
}
@Override
protected void initData() {}
private static final int ANIM_TIME = 1000;
/**
* 启动动画
*/
private void initStartAnim() {
// 渐变展示启动屏
AlphaAnimation aa = new AlphaAnimation(0.4f, 1.0f);
aa.setDuration(ANIM_TIME * 2);
aa.setAnimationListener(this);
mImageView.startAnimation(aa);
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
sa.setDuration(ANIM_TIME);
mIconView.startAnimation(sa);
RotateAnimation ra = new RotateAnimation(180, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
ra.setDuration(ANIM_TIME);
mTextView.startAnimation(ra);
}
private void requestFilePermission() {
XXPermissions.with(this)
.permission(Permission.Group.STORAGE)
.request(this);
}
/**
* {@link OnPermission}
*/
@Override
public void hasPermission(List<String> granted, boolean isAll) {
startActivity(HomeActivity.class);
finish();
}
@Override
public void onBackPressed() {
//禁用返回键
//super.onBackPressed();
}
@Override
protected void onRestart() {
super.onRestart();
if (XXPermissions.isHasPermission(LauncherActivity.this, Permission.Group.STORAGE)) {
hasPermission(null, true);
}else {
requestFilePermission();
}
}
@Override
public boolean isSupportSwipeBack() {
//不使用侧滑功能
return !super.isSupportSwipeBack();
}
@Override
public void noPermission(List<String> denied, boolean quick) {
if (quick) {
ToastUtils.show("没有权限访问文件,请手动授予权限");
XXPermissions.gotoPermissionSettings(LauncherActivity.this, true);
}else {
ToastUtils.show("请先授予文件读写权限");
getWindow().getDecorView().postDelayed(new Runnable() {
@Override
public void run() {
requestFilePermission();
}
}, 2000);
}
}
/**
* {@link Animation.AnimationListener}
*/
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
requestFilePermission();
}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
protected void initOrientation() {
//Android 8.0踩坑记录Only fullscreen opaque activities can request orientation
// https://www.jianshu.com/p/d0d907754603
//super.initOrientation();
}
}

View File

@ -0,0 +1,83 @@
package com.hjq.demo.ui.activity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.hjq.baselibrary.utils.TextInputHelper;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonActivity;
import com.hjq.toast.ToastUtils;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 登录界面
*/
public class LoginActivity extends CommonActivity
implements View.OnClickListener {
@BindView(R.id.et_login_phone)
EditText mPhoneView;
@BindView(R.id.et_login_password)
EditText mPasswordView;
@BindView(R.id.btn_login_commit)
Button mCommitView;
private TextInputHelper mTextInputHelper;
@Override
protected int getLayoutId() {
return R.layout.activity_login;
}
@Override
protected int getTitleBarId() {
return R.id.tb_login_bar;
}
@Override
protected void initView() {
mCommitView.setOnClickListener(this);
mTextInputHelper = new TextInputHelper(mCommitView, false);
mTextInputHelper.addViews(mPhoneView, mPasswordView);
}
@Override
protected void initData() {
}
@Override
public void onRightClick(View v) {
// 跳转到注册界面
startActivity(RegisterActivity.class);
}
@Override
protected void onDestroy() {
mTextInputHelper.removeViews();
super.onDestroy();
}
@Override
public boolean isSupportSwipeBack() {
//不使用侧滑功能
return !super.isSupportSwipeBack();
}
/**
* {@link View.OnClickListener}
*/
@Override
public void onClick(View v) {
if (v == mCommitView) {
if (mPhoneView.getText().toString().length() != 11) {
ToastUtils.show(getResources().getString(R.string.phone_input_error));
}
}
}
}

View File

@ -0,0 +1,99 @@
package com.hjq.demo.ui.activity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.hjq.baselibrary.utils.TextInputHelper;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonActivity;
import com.hjq.baselibrary.widget.CountdownView;
import com.hjq.toast.ToastUtils;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 注册界面
*/
public class RegisterActivity extends CommonActivity
implements View.OnClickListener {
@BindView(R.id.et_register_phone)
EditText mPhoneView;
@BindView(R.id.cv_register_countdown)
CountdownView mCountdownView;
@BindView(R.id.et_register_code)
EditText mCodeView;
@BindView(R.id.et_register_password1)
EditText mPasswordView1;
@BindView(R.id.et_register_password2)
EditText mPasswordView2;
@BindView(R.id.btn_register_commit)
Button mCommitView;
private TextInputHelper mTextInputHelper;
@Override
protected int getLayoutId() {
return R.layout.activity_register;
}
@Override
protected int getTitleBarId() {
return R.id.tb_register_bar;
}
@Override
protected void initView() {
mCountdownView.setOnClickListener(this);
mCommitView.setOnClickListener(this);
mTextInputHelper = new TextInputHelper(mCommitView, false);
mTextInputHelper.addViews(mPhoneView, mCodeView, mPasswordView1, mPasswordView2);
}
@Override
protected void initData() {
}
/**
* {@link View.OnClickListener}
*/
@Override
public void onClick(View v) {
if (v == mCountdownView) { //获取验证码
if (mPhoneView.getText().toString().length() != 11) {
ToastUtils.show(getResources().getString(R.string.phone_input_error));
return;
}
ToastUtils.show(getResources().getString(R.string.countdown_code_send_succeed));
}else if (v == mCommitView) { //提交注册
if (mPhoneView.getText().toString().length() != 11) {
ToastUtils.show(getResources().getString(R.string.phone_input_error));
return;
}
if (!mPasswordView1.getText().toString().equals(mPasswordView2.getText().toString())) {
ToastUtils.show(getResources().getString(R.string.two_password_input_error));
return;
}
}
}
@Override
protected void onDestroy() {
mTextInputHelper.removeViews();
super.onDestroy();
}
}

View File

@ -0,0 +1,179 @@
package com.hjq.demo.ui.activity;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import com.hjq.baselibrary.utils.WebViewLifecycleUtils;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonActivity;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 浏览器界面
*/
public class WebActivity extends CommonActivity {
@BindView(R.id.pb_web_progress)
ProgressBar mProgressBar;
@BindView(R.id.wv_web_view)
WebView mWebView;
@Override
protected int getLayoutId() {
return R.layout.activity_web;
}
@Override
protected int getTitleBarId() {
return R.id.tb_web_title;
}
@Override
protected void initView() {
// 不显示滚动条
mWebView.setVerticalScrollBarEnabled(false);
mWebView.setHorizontalScrollBarEnabled(false);
WebSettings settings = mWebView.getSettings();
//允许文件访问
settings.setAllowFileAccess(true);
//支持javaScript
settings.setJavaScriptEnabled(true);
//允许网页定位
settings.setGeolocationEnabled(true);
//允许保存密码
settings.setSavePassword(true);
//支持播放gif动画
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//解决Android 5.0上Webview默认不允许加载Http与Https混合内容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//两者都可以
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//加快HTML网页加载完成的速度等页面finish再加载图片
if(Build.VERSION.SDK_INT >= 19) {
settings.setLoadsImagesAutomatically(true);
} else {
settings.setLoadsImagesAutomatically(false);
}
}
@Override
protected void initData() {
mWebView.setWebViewClient(new MyWebViewClient());
mWebView.setWebChromeClient(new MyWebChromeClient());
mWebView.loadUrl("https://github.com/getActivity/AndroidProject");
}
@Override
public void onLeftClick(View v) {
finish();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
// 后退网页并且拦截该事件
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onResume() {
WebViewLifecycleUtils.onResume(mWebView);
super.onResume();
}
@Override
protected void onPause() {
WebViewLifecycleUtils.onPause(mWebView);
super.onPause();
}
@Override
protected void onDestroy() {
WebViewLifecycleUtils.onDestroy(mWebView);
super.onDestroy();
}
private class MyWebViewClient extends WebViewClient {
//网页加载错误时回调这个方法会在onPageFinished之前调用
@Override
public void onReceivedError(WebView view, int errorCode, String description, final String failingUrl) {
}
//开始加载网页
@Override
public void onPageStarted(final WebView view, final String url, Bitmap favicon) {
mProgressBar.setVisibility(View.VISIBLE);
}
//完成加载网页
@Override
public void onPageFinished(WebView view, String url) {
mProgressBar.setVisibility(View.GONE);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//super.onReceivedSslError(view, handler, error);注意一定要去除这行代码否则设置无效
// handler.cancel();// Android默认的处理方式
handler.proceed();// 接受所有网站的证书
// handleMessage(Message msg);// 进行其他处理
}
//跳转到其他链接
@Override
public boolean shouldOverrideUrlLoading(WebView view, final String url) {
String scheme = Uri.parse(url).getScheme();
if (scheme != null) {
scheme = scheme.toLowerCase();
}
if ("http".equals(scheme) || "https".equals(scheme)) {
mWebView.loadUrl(url);
}
return true;
}
}
private class MyWebChromeClient extends WebChromeClient {
//收到网页标题
@Override
public void onReceivedTitle(WebView view, String title) {
if (title != null) {
setTitle(title);
}
}
//收到加载进度变化
@Override
public void onProgressChanged(WebView view, int newProgress) {
mProgressBar.setProgress(newProgress);
}
}
}

View File

@ -0,0 +1,34 @@
package com.hjq.demo.ui.adapter;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import com.hjq.demo.common.CommonLazyFragment;
import com.hjq.baselibrary.base.BaseFragmentPagerAdapter;
import com.hjq.demo.ui.fragment.TestFragmentA;
import com.hjq.demo.ui.fragment.TestFragmentC;
import com.hjq.demo.ui.fragment.TestFragmentB;
import com.hjq.demo.ui.fragment.TestFragmentD;
import java.util.List;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 主页界面 ViewPager + Fragment 适配器
*/
public final class HomeViewPagerAdapter extends BaseFragmentPagerAdapter<CommonLazyFragment> {
public HomeViewPagerAdapter(FragmentActivity activity) {
super(activity);
}
@Override
protected void init(FragmentManager fm, List<CommonLazyFragment> list) {
list.add(TestFragmentA.newInstance());
list.add(TestFragmentB.newInstance());
list.add(TestFragmentC.newInstance());
list.add(TestFragmentD.newInstance());
}
}

View File

@ -0,0 +1,37 @@
package com.hjq.demo.ui.fragment;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonLazyFragment;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 可进行拷贝的副本
*/
public class CopyFragment extends CommonLazyFragment {
public static CopyFragment newInstance() {
return new CopyFragment();
}
@Override
protected int getLayoutId() {
return R.layout.fragment_copy;
}
@Override
protected int getTitleBarId() {
return R.id.tb_copy_bar;
}
@Override
protected void initView() {
}
@Override
protected void initData() {
}
}

View File

@ -0,0 +1,92 @@
package com.hjq.demo.ui.fragment;
import android.support.design.widget.AppBarLayout;
import android.support.v7.widget.Toolbar;
import android.widget.TextView;
import com.gyf.barlibrary.ImmersionBar;
import com.hjq.bar.TitleBar;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonLazyFragment;
import com.hjq.demo.widget.XCollapsingToolbarLayout;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 项目炫酷效果示例
*/
public class TestFragmentA extends CommonLazyFragment
implements XCollapsingToolbarLayout.OnScrimsListener {
@BindView(R.id.abl_test_bar)
AppBarLayout mAppBarLayout;
@BindView(R.id.ctl_test_bar)
XCollapsingToolbarLayout mCollapsingToolbarLayout;
@BindView(R.id.t_test_title)
Toolbar mToolbar;
@BindView(R.id.tb_test_bar)
TitleBar mTitleBar;
@BindView(R.id.tv_test_address)
TextView mAddressView;
@BindView(R.id.tv_test_search)
TextView mSearchView;
public static TestFragmentA newInstance() {
return new TestFragmentA();
}
@Override
protected int getLayoutId() {
return R.layout.fragment_test_a;
}
@Override
protected int getTitleBarId() {
return 0;
}
@Override
protected void initView() {
ImmersionBar.setTitleBar(getSupportActivity(), mToolbar);
ImmersionBar.setTitleBar(getSupportActivity(), mTitleBar);
//设置渐变监听
mCollapsingToolbarLayout.setOnScrimsListener(this);
}
@Override
protected void initData() {
}
@Override
public boolean isStatusBarEnabled() {
// 使用沉浸式状态栏
return !super.isStatusBarEnabled();
}
@Override
public boolean statusBarDarkFont() {
return mCollapsingToolbarLayout.isScrimsShown();
}
/**
* {@link XCollapsingToolbarLayout.OnScrimsListener}
*/
@Override
public void onScrimsStateChange(boolean shown) {
if (shown) {
mAddressView.setTextColor(getResources().getColor(R.color.black));
mSearchView.setBackgroundResource(R.drawable.bg_home_search_bar_gray);
getStatusBarConfig().statusBarDarkFont(true).init();
}else {
mAddressView.setTextColor(getResources().getColor(R.color.white));
mSearchView.setBackgroundResource(R.drawable.bg_home_search_bar_transparent);
getStatusBarConfig().statusBarDarkFont(false).init();
}
}
}

View File

@ -0,0 +1,63 @@
package com.hjq.demo.ui.fragment;
import android.view.View;
import com.hjq.baselibrary.widget.CountdownView;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonLazyFragment;
import com.hjq.toast.ToastUtils;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 项目自定义控件展示
*/
public class TestFragmentB extends CommonLazyFragment
implements View.OnClickListener {
@BindView(R.id.cv_test_countdown)
CountdownView mCountdownView;
public static TestFragmentB newInstance() {
return new TestFragmentB();
}
@Override
protected int getLayoutId() {
return R.layout.fragment_test_b;
}
@Override
protected int getTitleBarId() {
return R.id.tb_test_c_bar;
}
@Override
protected void initView() {
mCountdownView.setOnClickListener(this);
}
@Override
protected void initData() {
}
/**
* {@link View.OnClickListener}
*/
@Override
public void onClick(View v) {
if (v == mCountdownView) {
ToastUtils.show(getResources().getString(R.string.countdown_code_send_succeed));
}
}
@Override
public boolean isStatusBarEnabled() {
// 使用沉浸式状态栏
return !super.isStatusBarEnabled();
}
}

View File

@ -0,0 +1,125 @@
package com.hjq.demo.ui.fragment;
import android.view.View;
import android.widget.Button;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonLazyFragment;
import com.hjq.demo.common.UIActivity;
import com.hjq.permissions.OnPermission;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.hjq.toast.ToastUtils;
import java.util.List;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 项目框架使用示例
*/
public class TestFragmentC extends CommonLazyFragment
implements View.OnClickListener {
@BindView(R.id.btn_test_toast)
Button mToastView;
@BindView(R.id.btn_test_permission)
Button mPermissionView;
@BindView(R.id.btn_test_state_black)
Button mStateBlackView;
@BindView(R.id.btn_test_state_white)
Button mStateWhiteView;
@BindView(R.id.btn_test_swipe_enabled)
Button mSwipeEnabledView;
@BindView(R.id.btn_test_swipe_disable)
Button mSwipeDisableView;
public static TestFragmentC newInstance() {
return new TestFragmentC();
}
@Override
protected int getLayoutId() {
return R.layout.fragment_test_c;
}
@Override
protected int getTitleBarId() {
return R.id.tb_test_b_bar;
}
@Override
protected void initView() {
mToastView.setOnClickListener(this);
mPermissionView.setOnClickListener(this);
mStateBlackView.setOnClickListener(this);
mStateWhiteView.setOnClickListener(this);
mSwipeEnabledView.setOnClickListener(this);
mSwipeDisableView.setOnClickListener(this);
}
@Override
protected void initData() {
}
@Override
public boolean isStatusBarEnabled() {
// 使用沉浸式状态栏
return !super.isStatusBarEnabled();
}
/**
* {@link View.OnClickListener}
*/
@Override
public void onClick(View v) {
if (v == mToastView) {
ToastUtils.show("我是吐司");
}else if (v == mPermissionView) {
XXPermissions.with(getSupportActivity())
//.constantRequest() //可设置被拒绝后继续申请直到用户授权或者永久拒绝
//.permission(Permission.SYSTEM_ALERT_WINDOW, Permission.REQUEST_INSTALL_PACKAGES) //支持请求6.0悬浮窗权限8.0请求安装权限
.permission(Permission.CAMERA) //不指定权限则自动获取清单中的危险权限
.request(new OnPermission() {
@Override
public void hasPermission(List<String> granted, boolean isAll) {
if (isAll) {
ToastUtils.show("获取权限成功");
}else {
ToastUtils.show("获取权限成功,部分权限未正常授予");
}
}
@Override
public void noPermission(List<String> denied, boolean quick) {
if(quick) {
ToastUtils.show("被永久拒绝授权,请手动授予权限");
//如果是被永久拒绝就跳转到应用权限系统设置页面
XXPermissions.gotoPermissionSettings(getSupportActivity());
}else {
ToastUtils.show("获取权限失败");
}
}
});
}else if (v == mStateBlackView) {
UIActivity activity = (UIActivity) getSupportActivity();
activity.getStatusBarConfig().statusBarDarkFont(true).init();
}else if (v == mStateWhiteView) {
UIActivity activity = (UIActivity) getSupportActivity();
activity.getStatusBarConfig().statusBarDarkFont(false).init();
}else if (v == mSwipeEnabledView) {
UIActivity activity = (UIActivity) getSupportActivity();
activity.getSwipeBackHelper().setSwipeBackEnable(true);
ToastUtils.show("当前界面不会生效,其他界面调用才会有效果");
}else if (v == mSwipeDisableView) {
UIActivity activity = (UIActivity) getSupportActivity();
activity.getSwipeBackHelper().setSwipeBackEnable(false);
ToastUtils.show("当前界面不会生效,其他界面调用才会有效果");
}
}
}

View File

@ -0,0 +1,81 @@
package com.hjq.demo.ui.fragment;
import android.view.View;
import android.widget.Button;
import com.hjq.demo.R;
import com.hjq.demo.common.CommonLazyFragment;
import com.hjq.demo.ui.activity.AboutActivity;
import com.hjq.demo.ui.activity.LoginActivity;
import com.hjq.demo.ui.activity.RegisterActivity;
import com.hjq.demo.ui.activity.WebActivity;
import butterknife.BindView;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 项目界面跳转示例
*/
public class TestFragmentD extends CommonLazyFragment
implements View.OnClickListener {
@BindView(R.id.btn_test_login)
Button mLoginView;
@BindView(R.id.btn_test_register)
Button mRegisterView;
@BindView(R.id.btn_test_about)
Button mAboutView;
@BindView(R.id.btn_test_browser)
Button mBrowserView;
public static TestFragmentD newInstance() {
return new TestFragmentD();
}
@Override
protected int getLayoutId() {
return R.layout.fragment_test_d;
}
@Override
protected int getTitleBarId() {
return R.id.tb_test_d_bar;
}
@Override
protected void initView() {
mLoginView.setOnClickListener(this);
mRegisterView.setOnClickListener(this);
mAboutView.setOnClickListener(this);
mBrowserView.setOnClickListener(this);
}
@Override
protected void initData() {
}
/**
* {@link View.OnClickListener}
*/
@Override
public void onClick(View v) {
if (v == mLoginView) {
startActivity(LoginActivity.class);
}else if (v == mRegisterView) {
startActivity(RegisterActivity.class);
}else if (v == mAboutView) {
startActivity(AboutActivity.class);
}else if (v == mBrowserView) {
startActivity(WebActivity.class);
}
}
@Override
public boolean isStatusBarEnabled() {
// 使用沉浸式状态栏
return !super.isStatusBarEnabled();
}
}

View File

@ -0,0 +1,69 @@
package com.hjq.demo.widget;
import android.content.Context;
import android.support.design.widget.CollapsingToolbarLayout;
import android.util.AttributeSet;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : 支持监听渐变的CollapsingToolbarLayout
*/
public class XCollapsingToolbarLayout extends CollapsingToolbarLayout {
private OnScrimsListener mListener; // 渐变监听
private boolean isScrimsShown; // 当前渐变状态
public XCollapsingToolbarLayout(Context context) {
super(context);
}
public XCollapsingToolbarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public XCollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void setScrimsShown(boolean shown, boolean animate) {
super.setScrimsShown(shown, true);
// 判断渐变状态是否改变了
if (isScrimsShown != shown) {
// 如果是就记录并且回调监听器
isScrimsShown = shown;
if (mListener != null) {
mListener.onScrimsStateChange(isScrimsShown);
}
}
}
/**
* 获取当前的渐变状态
*/
public boolean isScrimsShown() {
return isScrimsShown;
}
/**
* 设置CollapsingToolbarLayout渐变监听
*/
public void setOnScrimsListener(OnScrimsListener l) {
mListener = l;
}
/**
* CollapsingToolbarLayout渐变监听器
*/
public interface OnScrimsListener {
/**
* 渐变状态变化
*
* @param shown 渐变开关
*/
void onScrimsStateChange(boolean shown);
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#f4f4f4"/>
<corners android:radius="@dimen/space_40"/>
</shape>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/space_40"/>
<stroke
android:width="1px"
android:color="#DDDDDD" />
</shape>

View File

@ -0,0 +1,26 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="@dimen/space_20" />
<gradient
android:angle="270"
android:centerColor="#E6E6E6"
android:endColor="#E6E6E6"
android:startColor="#E6E6E6" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="@dimen/space_20" />
<gradient
android:centerColor="@color/colorAccent"
android:endColor="@color/colorAccent"
android:startColor="@color/colorAccent" />
</shape>
</clip>
</item>
</layer-list>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按压状态 -->
<item android:drawable="@mipmap/tab_ico_found" android:state_checked="true" />
<!-- 默认状态 -->
<item android:drawable="@mipmap/tab_ico_found_off" />
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按压状态 -->
<item android:drawable="@mipmap/tab_ico_home" android:state_checked="true" />
<!-- 默认状态 -->
<item android:drawable="@mipmap/tab_ico_home_off" />
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按压状态 -->
<item android:drawable="@mipmap/tab_ico_me" android:state_checked="true" />
<!-- 默认状态 -->
<item android:drawable="@mipmap/tab_ico_me_off" />
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按压状态 -->
<item android:drawable="@mipmap/tab_ico_message" android:state_checked="true" />
<!-- 默认状态 -->
<item android:drawable="@mipmap/tab_ico_message_off" />
</selector>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 默认圆角按钮样式 -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按钮禁用状态 -->
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="@dimen/button_round_size" />
<solid android:color="@color/colorButtonDisable" />
</shape>
</item>
<!-- 按钮按压状态 -->
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="@dimen/button_round_size" />
<solid android:color="@color/colorButtonPressed" />
</shape>
</item>
<!-- 按钮默认状态 -->
<item>
<shape android:shape="rectangle">
<corners android:radius="@dimen/button_round_size" />
<solid android:color="@color/colorAccent" />
</shape>
</item>
</selector>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 不带圆角按钮样式 -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按钮禁用状态 -->
<item android:drawable="@color/colorButtonDisable" android:state_enabled="false" />
<!-- 按钮按压状态 -->
<item android:drawable="@color/colorButtonPressed" android:state_pressed="true" />
<!-- 按钮默认状态 -->
<item android:drawable="@color/colorAccent" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/black10" android:state_pressed="true" />
<item android:drawable="@color/black10" android:state_selected="true" />
<item android:drawable="@color/transparent" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/black10" android:state_pressed="true" />
<item android:drawable="@color/black10" android:state_selected="true" />
<item android:drawable="@color/white" />
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 选中状态 -->
<item android:color="#000000" android:state_checked="true" />
<!-- 默认状态 -->
<item android:color="#a4a4a4" />
</selector>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".ui.activity.AboutActivity">
<com.hjq.bar.TitleBar
android:id="@+id/tb_about_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/about_text" />
<ImageView
android:layout_width="@dimen/space_200"
android:layout_height="@dimen/space_200"
android:layout_marginTop="@dimen/space_80"
android:layout_marginBottom="@dimen/space_80"
android:src="@mipmap/ic_logo" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="#222222"
android:textSize="@dimen/font_size_32px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:autoLink="all"
android:gravity="center_horizontal"
android:lineSpacingExtra="@dimen/space_20"
android:text="Github地址\nhttps://github.com/getActivity/AndroidProject"
android:textColor="#222222"
android:textSize="@dimen/font_size_28px" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_40"
android:layout_marginBottom="@dimen/space_40"
android:text="@string/about_copyright"
android:textColor="#BDBDBD"
android:textSize="@dimen/font_size_28px" />
</LinearLayout>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.activity.CopyActivity">
<com.hjq.bar.TitleBar
android:id="@+id/tb_copy_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="标题" />
</LinearLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.activity.HomeActivity">
<com.hjq.baselibrary.widget.NoScrollViewPager
android:id="@+id/vp_home_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.design.widget.BottomNavigationView
android:id="@+id/bv_home_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:itemTextColor="@drawable/txt_home_bottom_nav_selector"
app:labelVisibilityMode="labeled"
app:menu="@menu/menu_home_bottom_nav" />
</LinearLayout>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.activity.LauncherActivity">
<ImageView
android:id="@+id/iv_launcher_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@mipmap/bg_launcher" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="@dimen/space_100"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_launcher_icon"
android:layout_width="@dimen/space_400"
android:layout_height="@dimen/space_400"
android:src="@mipmap/ic_logo" />
<TextView
android:id="@+id/iv_launcher_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_100"
android:text="@string/app_name"
android:textColor="@color/white90"
android:textSize="@dimen/font_size_40px" />
</LinearLayout>
</FrameLayout>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".ui.activity.LoginActivity">
<com.hjq.bar.TitleBar
android:id="@+id/tb_login_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/space_100"
app:bar_style="transparent"
app:color_right="@color/colorAccent"
app:icon_back="false"
app:title=""
app:title_right="@string/login_register" />
<ImageView
android:layout_width="@dimen/space_200"
android:layout_height="@dimen/space_200"
android:layout_margin="@dimen/space_60"
android:src="@mipmap/ic_logo" />
<com.hjq.baselibrary.widget.ClearEditText
android:id="@+id/et_login_phone"
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginTop="@dimen/space_20"
android:layout_marginRight="@dimen/space_80"
android:hint="@string/login_phone_input_hint"
android:inputType="phone"
android:maxLength="11"
android:maxLines="1" />
<View
style="@style/HorizontalLineStyle"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginRight="@dimen/space_80" />
<com.hjq.baselibrary.widget.ClearEditText
android:id="@+id/et_login_password"
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginTop="@dimen/space_40"
android:layout_marginRight="@dimen/space_80"
android:hint="@string/login_password_input_hint"
android:inputType="textPassword"
android:maxLength="20"
android:maxLines="1" />
<View
style="@style/HorizontalLineStyle"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginRight="@dimen/space_80" />
<TextView
android:id="@+id/tv_login_forget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginTop="@dimen/space_30"
android:layout_marginRight="@dimen/space_120"
android:text="@string/login_forget"
android:textColor="@color/colorAccent" />
<Button
android:id="@+id/btn_login_commit"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginTop="@dimen/space_60"
android:layout_marginRight="@dimen/space_80"
android:text="@string/login_text" />
</LinearLayout>

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".ui.activity.RegisterActivity">
<com.hjq.bar.TitleBar
android:id="@+id/tb_register_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/space_100"
app:bar_style="transparent"
app:color_left="@color/colorAccent"
app:icon_back="false"
app:title=""
app:title_left="@string/register_login" />
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginRight="@dimen/space_80"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_40"
android:text="@string/register_text"
android:textColor="@color/black60"
android:textSize="@dimen/font_size_46px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_40"
android:text="@string/register_hint"
android:textColor="@color/black20"
android:textSize="@dimen/font_size_28px" />
<LinearLayout
android:id="@+id/ll_register_edit1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="@+id/et_register_phone"
style="@style/EditTextStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/register_phone_input_hint"
android:inputType="phone"
android:maxLength="11"
android:maxLines="1" />
<com.hjq.baselibrary.widget.CountdownView
android:id="@+id/cv_register_countdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/space_20"
android:text="@string/register_send_code"
android:textColor="@color/colorAccent" />
</LinearLayout>
<View style="@style/HorizontalLineStyle" />
<EditText
android:id="@+id/et_register_code"
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_40"
android:hint="@string/register_code_input_hint"
android:inputType="number"
android:maxLength="8"
android:maxLines="1" />
<View style="@style/HorizontalLineStyle" />
<EditText
android:id="@+id/et_register_password1"
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_40"
android:hint="@string/register_password_input_hint1"
android:inputType="textPassword"
android:maxLength="20"
android:maxLines="1" />
<View style="@style/HorizontalLineStyle" />
<EditText
android:id="@+id/et_register_password2"
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_40"
android:hint="@string/register_password_input_hint2"
android:inputType="textPassword"
android:maxLength="20"
android:maxLines="1" />
<View style="@style/HorizontalLineStyle" />
<Button
android:id="@+id/btn_register_commit"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="@string/register_text" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.activity.WebActivity">
<com.hjq.bar.TitleBar
android:id="@+id/tb_web_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/pb_web_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="3dip"
android:progressDrawable="@drawable/bg_web_progress_bar"
android:visibility="gone" />
<WebView
android:id="@+id/wv_web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -0,0 +1,16 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.fragment.CopyFragment">
<com.hjq.bar.TitleBar
android:id="@+id/tb_copy_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon_back="false"
app:title="标题" />
</LinearLayout>

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragment.TestFragmentA">
<android.support.design.widget.AppBarLayout
android:id="@+id/abl_test_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="0dp">
<com.hjq.demo.widget.XCollapsingToolbarLayout
android:id="@+id/ctl_test_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/space_512"
app:contentScrim="@color/white"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:scrimVisibleHeightTrigger="@dimen/space_240">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/space_300"
android:scaleType="centerCrop"
android:src="@mipmap/bg_launcher"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="@+id/t_test_title"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_marginRight="20dp"
app:layout_collapseMode="pin" />
<com.hjq.bar.TitleBar
android:id="@+id/tb_test_bar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
app:bar_style="transparent"
app:color_title="@color/white"
app:icon_back="false"
app:layout_collapseMode="pin"
app:line="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="@dimen/space_20">
<TextView
android:id="@+id/tv_test_address"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/space_20"
android:gravity="center_vertical"
android:text="地区"
android:textColor="@color/white"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/tv_test_search"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/space_40"
android:layout_marginRight="@dimen/space_100"
android:background="@drawable/bg_home_search_bar_transparent"
android:gravity="center"
android:text="请输入搜索关键字"
android:textColor="#a4a4a4"
android:textSize="@dimen/font_size_28px" />
</LinearLayout>
</com.hjq.bar.TitleBar>
</com.hjq.demo.widget.XCollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/space_60"
android:layout_marginRight="@dimen/space_60"
android:orientation="vertical">
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,184 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.fragment.TestFragmentB">
<com.hjq.bar.TitleBar
android:id="@+id/tb_test_c_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon_back="false"
app:title="@string/home_nav_found" />
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:padding="@dimen/space_40">
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="@dimen/space_180"
android:layout_height="@dimen/space_180"
android:src="@mipmap/bg_launcher" />
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="@dimen/space_180"
android:layout_height="@dimen/space_180"
android:layout_marginLeft="@dimen/space_40"
android:src="@mipmap/ic_logo"
app:civ_border_color="@color/white60"
app:civ_border_width="@dimen/space_2" />
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="@dimen/space_180"
android:layout_height="@dimen/space_180"
android:layout_marginLeft="@dimen/space_40"
android:src="@mipmap/ic_logo"
app:civ_border_color="@color/black60"
app:civ_border_width="@dimen/space_2" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_30"
android:gravity="center"
android:orientation="horizontal">
<Button
style="@style/ButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="普通按钮" />
<Button
style="@style/ButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/space_20"
android:enabled="false"
android:text="禁用了的按钮" />
<Button
style="@style/RectButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/space_20"
android:text="不带圆角的" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginTop="@dimen/space_40"
android:layout_marginRight="@dimen/space_80"
android:orientation="horizontal">
<EditText
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="请输入验证码" />
<com.hjq.baselibrary.widget.CountdownView
android:id="@+id/cv_test_countdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取验证码" />
</LinearLayout>
<com.hjq.baselibrary.widget.ClearEditText
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/space_80"
android:layout_marginTop="@dimen/space_40"
android:layout_marginRight="@dimen/space_80"
android:text="这是一个带清除的按钮的EditText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/space_40"
android:layout_marginRight="@dimen/space_20"
android:layout_marginBottom="@dimen/space_20"
android:text="这是一条华丽的分割线" />
<View style="@style/HorizontalLineStyle" />
<com.hjq.baselibrary.widget.square.SquareFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_40"
android:background="@color/colorButtonPressed">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="这是一个正方形的FrameLayout"
android:textColor="@color/white" />
</com.hjq.baselibrary.widget.square.SquareFrameLayout>
<com.hjq.baselibrary.widget.square.SquareLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_horizontal"
android:text="这是一个正方形的LinearLayout"
android:textColor="@color/white" />
</com.hjq.baselibrary.widget.square.SquareLinearLayout>
<com.hjq.baselibrary.widget.square.SquareRelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorButtonPressed">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="这是一个正方形的RelativeLayout"
android:textColor="@color/white" />
</com.hjq.baselibrary.widget.square.SquareRelativeLayout>
<com.hjq.baselibrary.widget.square.SquareImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@mipmap/bg_launcher" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,78 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.fragment.TestFragmentC">
<com.hjq.bar.TitleBar
android:id="@+id/tb_test_b_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon_back="false"
app:title="@string/home_nav_message" />
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/space_60"
android:layout_marginRight="@dimen/space_60"
android:orientation="vertical">
<Button
android:id="@+id/btn_test_toast"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="显示吐司" />
<Button
android:id="@+id/btn_test_permission"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="请求权限" />
<Button
android:id="@+id/btn_test_state_black"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="状态栏黑色字体" />
<Button
android:id="@+id/btn_test_state_white"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="状态栏白色字体" />
<Button
android:id="@+id/btn_test_swipe_enabled"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="开启界面侧滑" />
<Button
android:id="@+id/btn_test_swipe_disable"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="关闭界面侧滑" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,63 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.fragment.TestFragmentD">
<com.hjq.bar.TitleBar
android:id="@+id/tb_test_d_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon_back="false"
app:title="@string/home_nav_me" />
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/space_60"
android:layout_marginRight="@dimen/space_60"
android:orientation="vertical">
<Button
android:id="@+id/btn_test_login"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="点我进入登录界面" />
<Button
android:id="@+id/btn_test_register"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="点我进入注册界面" />
<Button
android:id="@+id/btn_test_about"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="点我进入关于界面" />
<Button
android:id="@+id/btn_test_browser"
style="@style/ButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/space_60"
android:text="点我进入浏览器界面" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_home"
android:icon="@drawable/ic_selector_selectable_home_index"
android:title="@string/home_nav_index"/>
<item
android:id="@+id/home_found"
android:icon="@drawable/ic_selector_selectable_home_found"
android:title="@string/home_nav_found"/>
<item
android:id="@+id/home_message"
android:icon="@drawable/ic_selector_selectable_home_message"
android:title="@string/home_nav_message"/>
<item
android:id="@+id/home_me"
android:icon="@drawable/ic_selector_selectable_home_me"
android:title="@string/home_nav_me"/>
</menu>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="windowBackground">#F4F4F4</color>
<color name="colorPrimary">#FFFFFF</color>
<color name="colorPrimaryDark">#000000</color>
<color name="colorAccent">#5A8DDF</color>
<!-- 按钮按压时的颜色 -->
<color name="colorButtonPressed">#1976d2</color>
<!-- 按钮禁用时的颜色 -->
<color name="colorButtonDisable">#DDDDDD</color>
<!-- 分割线的颜色 -->
<color name="colorLine">#ECECEC</color>
</resources>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 默认按钮的圆角大小 -->
<dimen name="button_round_size">5dp</dimen>
<!-- 默认分割线的宽度或者高度 -->
<dimen name="line_size">1px</dimen>
</resources>

View File

@ -0,0 +1,37 @@
<resources>
<string name="app_name">模板工程</string>
<!-- 通用 -->
<string name="countdown_code_send_succeed">验证码已发送,请注意查收</string>
<string name="two_password_input_error">两次密码输入不一致,请重新输入</string>
<string name="phone_input_error">手机号输入不正确</string>
<!-- 主页 -->
<string name="home_exit_hint">再按一次退出</string>
<string name="home_nav_index">首页</string>
<string name="home_nav_found">发现</string>
<string name="home_nav_message">消息</string>
<string name="home_nav_me">我的</string>
<!-- 登录 -->
<string name="login_register">注册</string>
<string name="login_phone_input_hint">请输入手机号</string>
<string name="login_password_input_hint">请输入密码</string>
<string name="login_forget">忘记密码?</string>
<string name="login_text">登录</string>
<!-- 注册 -->
<string name="register_text">注册</string>
<string name="register_hint">手机号仅用于登录和保护账号安全</string>
<string name="register_login">登录</string>
<string name="register_phone_input_hint">输入手机号码</string>
<string name="register_code_input_hint">输入验证码</string>
<string name="register_send_code">发送验证码</string>
<string name="register_password_input_hint1">设置密码</string>
<string name="register_password_input_hint2">再次输入密码</string>
<!-- 关于 -->
<string name="about_text">关于我们</string>
<string name="about_copyright">Copyright © 2018</string>
</resources>

View File

@ -0,0 +1,67 @@
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- 窗口背景颜色 -->
<item name="android:windowBackground">@color/windowBackground</item>
<!-- 应用的主要色调actionBar默认使用该颜色Toolbar导航栏的底色 -->
<item name="colorPrimary">@color/colorPrimary</item>
<!-- 应用的主要暗色调statusBarColor默认使用该颜色 -->
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<!-- 应用的强调色CheckBoxRadioButtonSwitchCompat等一般控件的选中效果默认采用该颜色 -->
<item name="colorAccent">@color/colorAccent</item>
<!-- ActionMode覆盖Actionbar不顶下来 -->
<item name="windowActionModeOverlay">true</item>
<item name="android:windowContentOverlay">@null</item>
<!-- ActionMode的颜色 -->
<item name="actionModeBackground">@color/colorPrimary</item>
</style>
<!-- 解决启动页白屏的问题 -->
<style name="LauncherTheme" parent="AppTheme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<!-- 默认圆角按钮样式 -->
<style name="ButtonStyle" parent="Widget.AppCompat.Button.Borderless">
<item name="android:paddingLeft">@dimen/space_30</item>
<item name="android:paddingRight">@dimen/space_30</item>
<item name="android:textSize">@dimen/font_size_30px</item>
<item name="android:textColor">@color/white90</item>
<item name="android:gravity">center</item>
<item name="android:background">@drawable/selector_button</item>
<item name="android:foreground">@null</item>
<item name="android:focusable">false</item>
<!-- 解决 Android 5.1 及以上版本 Button 英文字符串自动变大写的问题 -->
<item name="android:textAllCaps">false</item>
</style>
<!-- 不带圆角按钮样式 -->
<style name="RectButtonStyle" parent="ButtonStyle">
<item name="android:background">@drawable/selector_button_rect</item>
</style>
<!-- 默认文本框样式 -->
<style name="EditTextStyle">
<item name="android:background">@null</item>
<item name="android:textSize">@dimen/font_size_30px</item>
<item name="android:textColorHint">#A4A4A4</item>
<item name="android:textColor">#333333</item>
<item name="android:padding">@dimen/space_20</item>
</style>
<!-- 默认水平分割线样式 -->
<style name="HorizontalLineStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">1px</item>
<item name="android:background">@color/colorLine</item>
</style>
<!-- 默认垂直分割线样式 -->
<style name="VerticalLineStyle">
<item name="android:layout_width">1px</item>
<item name="android:layout_height">match_parent</item>
<item name="android:background">@color/colorLine</item>
</style>
</resources>

1
baselibrary/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

55
baselibrary/build.gradle Normal file
View File

@ -0,0 +1,55 @@
//
apply plugin: 'com.android.library'
apply plugin: 'com.novoda.bintray-release'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion 14
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
}
}
dependencies {
api "com.android.support:appcompat-v7:$rootProject.ext.supportLibraryVersion"
api "com.android.support:design:$rootProject.ext.supportLibraryVersion"
}
publish {
userOrg = 'getactivity'//bintray用户名
groupId = 'com.hjq'//maven group id最终引用形式
artifactId = 'formwork'//maven的artifact id
version = '1.0'//maven
description = 'An advanced template project'//
website = "https://github.com/getActivity/AndroidProject"//github中的地址
}
tasks.withType(Javadoc) {//
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('encoding', 'UTF-8')
options.addStringOption('charSet', 'UTF-8')
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}

25
baselibrary/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\SDK\Studio\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1 @@
<manifest package="com.hjq.baselibrary" />

View File

@ -0,0 +1,92 @@
package com.hjq.baselibrary.base;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import com.hjq.baselibrary.utils.KeyboardUtils;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : Activity基类
*/
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getLayoutId() > 0) {
setContentView(getLayoutId());
}
init();
}
public void init(){
initView();
initData();
}
//引入布局
protected abstract int getLayoutId();
//标题栏id用于状态栏沉浸
protected abstract int getTitleBarId();
//初始化控件
protected abstract void initView();
//初始化数据
protected abstract void initData();
@Override
public void finish() {
// 隐藏软键盘避免软键盘引发的内存泄露
KeyboardUtils.hideKeyboard(getCurrentFocus());
super.finish();
}
/**
* 跳转到其他Activity
*
* @param cls 目标Activity的Class
*/
public void startActivity(Class<?> cls) {
startActivity(new Intent(this, cls));
}
/**
* 延迟执行某个任务
*
* @param action Runnable对象
*/
public boolean post(Runnable action) {
return getWindow().getDecorView().post(action);
}
/**
* 延迟某个时间执行某个任务
*
* @param action Runnable对象
* @param delayMillis 延迟的时间
*/
public boolean postDelayed(Runnable action, long delayMillis) {
return getWindow().getDecorView().postDelayed(action, delayMillis);
}
/**
* 删除某个延迟任务
* @param action Runnable对象
*/
public boolean removeCallbacks(Runnable action) {
if(getWindow().getDecorView() != null) {
return getWindow().getDecorView().removeCallbacks(action);
}else {
return true;
}
}
}

View File

@ -0,0 +1,37 @@
package com.hjq.baselibrary.base;
import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : Activity中优化Handler基类
*/
public abstract class BaseActivityHandler<T extends Activity> extends Handler {
private final WeakReference<T> mActivity;
public BaseActivityHandler(T activity) {
mActivity = new WeakReference<>(activity);
}
/**
* 判断当前Handler是否可用
*/
public boolean isEnabled() {
return mActivity.get() != null && !mActivity.get().isFinishing();
}
/**
* 在Activity销毁前移除所有的任务
*/
public void onDestroy() {
//删除所有的回调函数和消息
removeCallbacksAndMessages(null);
}
}

View File

@ -0,0 +1,78 @@
package com.hjq.baselibrary.base;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : FragmentPagerAdapter基类
*/
public abstract class BaseFragmentPagerAdapter<T extends Fragment> extends FragmentPagerAdapter {
private List<T> mFragments = new ArrayList<>(); // Fragment集合
private T mCurrentFragment; // 当前显示的Fragment
/**
* 在Activity中使用ViewPager适配器
*/
public BaseFragmentPagerAdapter(FragmentActivity activity) {
this(activity.getSupportFragmentManager());
}
/**
* 在Fragment中使用ViewPager适配器
*/
public BaseFragmentPagerAdapter(Fragment fragment) {
this(fragment.getChildFragmentManager());
}
public BaseFragmentPagerAdapter(FragmentManager fm) {
super(fm);
init(fm, mFragments);
}
//初始化Fragment
protected abstract void init(FragmentManager fm, List<T> list);
@Override
public T getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (getCurrentFragment() != object) {
// 记录当前的Fragment对象
mCurrentFragment = (T) object;
}
super.setPrimaryItem(container, position, object);
}
/**
* 获取Fragment集合
*/
public List<T> getAllFragment() {
return mFragments;
}
/**
* 获取当前的Fragment
*/
public T getCurrentFragment() {
return mCurrentFragment;
}
}

View File

@ -0,0 +1,166 @@
package com.hjq.baselibrary.base;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.lang.reflect.Field;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : Fragment懒加载基类
*/
public abstract class BaseLazyFragment extends Fragment {
private boolean isLazyLoad = false;//是否已经懒加载
private View mRootView;//根布局
public Activity mActivity;//Activity对象
/**
* 获得全局的防止使用getActivity()为空
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity) context;
}
public Activity getSupportActivity() {
return mActivity;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (mRootView == null && getLayoutId() > 0) {
mRootView = inflater.inflate(getLayoutId(), null);
}
ViewGroup parent = (ViewGroup) mRootView.getParent();
if (parent != null) {
parent.removeView(mRootView);
}
return mRootView;
}
@Override
public View getView() {
return mRootView;
}
protected boolean isLazyLoad() {
return isLazyLoad;
}
/**
* 是否在Fragment使用沉浸式
*/
protected boolean isStatusBarEnabled() {
return false;
}
@Override
public void onDestroy() {
super.onDestroy();
mActivity = null;
mRootView = null;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (isVisibleToUser && !isLazyLoad() && getView() != null) {
isLazyLoad = true;
init();
}
}
private boolean isVisibleToUser;
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
this.isVisibleToUser = isVisibleToUser;
if (isVisibleToUser && !isLazyLoad() && getView() != null) {
isLazyLoad = true;
init();
}
super.setUserVisibleHint(isVisibleToUser);
}
public boolean isVisibleToUser() {
return isVisibleToUser;
}
@Override
public void onDetach() {
super.onDetach();
//解决java.lang.IllegalStateException: Activity has been destroyed 的错误
try {
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
protected void init() {
initView();
initData();
}
//引入布局
protected abstract int getLayoutId();
//标题栏id用于状态栏沉浸
protected abstract int getTitleBarId();
//初始化控件
protected abstract void initView();
//初始化数据
protected abstract void initData();
/**
* 根据资源id获取一个View
*/
protected <T extends View> T findViewById(@IdRes int id) {
return (T) getView().findViewById(id);
}
protected <T extends View> T findActivityViewById(@IdRes int id) {
return (T) mActivity.findViewById(id);
}
/**
* 跳转到其他Activity
*
* @param cls 目标Activity的Class
*/
public void startActivity(Class<?> cls) {
startActivity(new Intent(getContext(), cls));
}
/**
* Fragment返回键被按下时回调
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
//默认不拦截按键事件传递给Activity
return false;
}
}

View File

@ -0,0 +1,205 @@
package com.hjq.baselibrary.base;
import android.content.Context;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.IdRes;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : ListView适配器基类
*/
public abstract class BaseListViewAdapter<T, VH extends BaseListViewAdapter.ViewHolder> extends BaseAdapter {
private List<T> mDataSet;
private Context mContext;
public BaseListViewAdapter(Context context) {
this.mContext = context;
}
@Override
public int getCount() {
return mDataSet == null ? 0 : mDataSet.size();
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = onCreateViewHolder(parent, getItemViewType(position));
convertView = holder.getItemView();
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
onBindViewHolder(holder, position);
return holder.getItemView();
}
public abstract ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
public abstract void onBindViewHolder(ViewHolder holder, int position);
/**
* 设置新的数据
*/
public void setData(List<T> data) {
mDataSet = data;
notifyDataSetChanged();
}
/**
* 获取当前数据
*/
public List<T> getData() {
return mDataSet;
}
/**
* 追加一些数据
*/
public void addData(List<T> data) {
if (mDataSet != null) {
mDataSet.addAll(data);
} else {
mDataSet = data;
}
notifyDataSetChanged();
}
/**
* 清空当前数据
*/
public void clearData() {
//当前的数据不能为空
if (mDataSet == null || mDataSet.size() == 0) return;
mDataSet.clear();
notifyDataSetChanged();
}
/**
* 获取某个位置上的数据
*/
@Override
public T getItem(int position) {
return mDataSet.get(position);
}
/**
* 更新某个位置上的数据
*/
public void setItem(int position, T item) {
if (mDataSet == null) mDataSet = new ArrayList<>();
mDataSet.set(position, item);
notifyDataSetChanged();
}
/**
* 添加单条数据
*/
public void addItem(T item) {
addItem(mDataSet.size() - 1, item);
}
/**
* 添加单条数据
*/
public void addItem(int position, T item) {
if (mDataSet == null) mDataSet = new ArrayList<>();
//如果是在for循环添加后要记得position++
if (position < mDataSet.size()) {
mDataSet.add(position, item);
} else {
mDataSet.add(item);
}
notifyDataSetChanged();
}
/**
* 删除单条数据
*/
public void removeItem(T item) {
int index = mDataSet.indexOf(item);
if (index != -1) {
removeItem(index);
}
}
public void removeItem(int position) {
//如果是在for循环删除后要记得i--
mDataSet.remove(position);
notifyDataSetChanged();
}
public Context getContext() {
return mContext;
}
public static class ViewHolder {
public final View itemView;
public ViewHolder(View itemView) {
this.itemView = itemView;
}
public final View getItemView() {
return itemView;
}
public final <T extends View> T findView(@IdRes int id) {
return (T) itemView.findViewById(id);
}
public final ViewHolder setText(@IdRes int id, String text) {
if (text == null) text = "";
View view = findView(id);
if (view instanceof TextView) {
((TextView) view).setText(text);
}
return this;
}
public final ViewHolder setVisibility(@IdRes int id, int visibility) {
View view = findView(id);
if (view != null) {
view.setVisibility(visibility);
}
return this;
}
public final ViewHolder setColor(@IdRes int id, @ColorInt int color) {
View view = findView(id);
if (view instanceof TextView) {
((TextView) view).setTextColor(color);
}
return this;
}
public final ViewHolder setImage(@IdRes int id, @DrawableRes int resID) {
View view = findView(id);
if (view instanceof ImageView) {
((ImageView) view).setImageResource(resID);
}
return this;
}
}
}

View File

@ -0,0 +1,382 @@
package com.hjq.baselibrary.base;
import android.content.Context;
import android.support.annotation.ColorInt;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.hjq.baselibrary.listener.OnItemClickListener;
import com.hjq.baselibrary.listener.OnItemLongClickListener;
import com.hjq.baselibrary.listener.OnScrollingListener;
import java.util.ArrayList;
import java.util.List;
/**
* author : HJQ
* github : https://github.com/getActivity/AndroidProject
* time : 2018/10/18
* desc : RecyclerView适配器基类
*/
public abstract class BaseRecyclerViewAdapter<T, VH extends BaseRecyclerViewAdapter.ViewHolder>
extends RecyclerView.Adapter<VH> {
//列表数据
private List<T> mDataSet;
//当前列表的页码默认为第一页用于分页加载功能
private int mPageNumber = 1;
//是否是最后一页默认为false用于分页加载功能
private boolean mLastPage;
//RecyclerView对象
private RecyclerView mRecyclerView;
//上下文对象注意不要在构造函数中使用
private Context mContext;
//标记对象
private Object mTag;
@Override
public int getItemCount() {
return mDataSet == null ? 0 : mDataSet.size();
}
@Override
public long getItemId(int position) {
return position;
}
/**
* 设置新的数据
*/
public void setData(List<T> data) {
mDataSet = data;
notifyDataSetChanged();
}
/**
* 获取当前数据
*/
public List<T> getData() {
return mDataSet;
}
/**
* 追加一些数据
*/
public void addData(List<T> data) {
//追加的数据不能为空
if (data == null || data.size() == 0) return;
if (mDataSet == null || mDataSet.size() == 0) {
setData(data);
}else {
mDataSet.addAll(data);
notifyItemRangeInserted(mDataSet.size() - data.size(), data.size());
}
}
/**
* 清空当前数据
*/
public void clearData() {
//当前的数据不能为空
if (mDataSet == null || mDataSet.size() == 0) return;
mDataSet.clear();
notifyDataSetChanged();
}
/**
* 获取某个位置上的数据
*/
public T getItem(int position) {
return mDataSet.get(position);
}
/**
* 更新某个位置上的数据
*/
public void setItem(int position, T item) {
if (mDataSet == null) mDataSet = new ArrayList<>();
mDataSet.set(position, item);
notifyItemChanged(position);
}
/**
* 添加单条数据
*/
public void addItem(T item) {
addItem(mDataSet.size() - 1, item);
}
public void addItem(int position, T item) {
if (mDataSet == null) mDataSet = new ArrayList<>();
//如果是在for循环添加后要记得position++
if (position < mDataSet.size()) {
mDataSet.add(position, item);
}else {
mDataSet.add(item);
position = mDataSet.size() - 1;
}
//告诉适配器添加数据的位置会有动画效果
notifyItemInserted(position);
}
/**
* 删除单条数据
*/
public void removeItem(T item) {
int index = mDataSet.indexOf(item);
if (index != -1) {
removeItem(index);
}
}
public void removeItem(int position) {
//如果是在for循环删除后要记得i--
mDataSet.remove(position);
//告诉适配器删除数据的位置会有动画效果
notifyItemRemoved(position);
}
/**
* 获取RecyclerView对象需要在setAdapter之后绑定
*/
public RecyclerView getRecyclerView() {
return mRecyclerView;
}
/**
* 获取上下文对象注意不要在构造方法中调用
*/
public Context getContext() {
return mContext;
}
/**
* 如果非要在构造方法中使用上下文对象可以提前设置否则只能setAdapter之后才能获取
*/
public void setContext(Context context) {
mContext = context;
}
/**
* 获取当前的页码
*/
public int getPageNumber() {
return mPageNumber;
}
/**
* 设置当前的页码
*/
public void setPageNumber(int pageNumber) {
mPageNumber = pageNumber;
}
/**
* 当前是否为最后一页
*/
public boolean isLastPage() {
return mLastPage;
}
/**
* 设置是否为最后一页
*/
public void setLastPage(boolean lastPage) {
mLastPage = lastPage;
}
/**
* 获取标记
*/
public Object getTag() {
return mTag;
}
/**
* 设置标记
*/
public void setTag(Object tag) {
mTag = tag;
}
/**
* 条目ViewHolder需要子类ViewHolder继承
*/
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnLongClickListener {
public ViewHolder(ViewGroup parent, int layoutId) {
this(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false));
}
public ViewHolder(View itemView) {
super(itemView);
//这里可以设置条目的监听事件
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
@Override
public void onClick(View v) {
if(onItemClickListener != null) {
onItemClickListener.onItemClick(v, getLayoutPosition());
}
}
@Override
public boolean onLongClick(View v) {
if (onItemLongClickListener != null) {
return onItemLongClickListener.onItemLongClick(v, getLayoutPosition());
}
return false;
}
public final <T extends View> T findView(@IdRes int id) {
return (T) itemView.findViewById(id);
}
public final ViewHolder setText(@IdRes int id, String text) {
if (text == null) text = "";
View view = findView(id);
if (view instanceof TextView) {
((TextView) view).setText(text);
}
return this;
}
public final ViewHolder setVisibility(@IdRes int id, int visibility) {
View view = findView(id);
if (view != null) {
view.setVisibility(visibility);
}
return this;
}
public final ViewHolder setColor(@IdRes int id, @ColorInt int color) {
View view = findView(id);
if (view instanceof TextView) {
((TextView) view).setTextColor(color);
}
return this;
}
public final ViewHolder setImage(@IdRes int id, int resID) {
View view = findView(id);
if (view instanceof ImageView) {
((ImageView) view).setImageResource(resID);
}
return this;
}
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
mRecyclerView = recyclerView;
if (mContext == null) {
mContext = recyclerView.getContext();
}
//用户设置了滚动监听需要给RecyclerView设置监听
if (mScrollListener != null) {
//添加滚动监听
mRecyclerView.addOnScrollListener(mScrollListener);
}
//判断当前的布局管理器是否为空如果为空则设置默认的布局管理器
if (mRecyclerView.getLayoutManager() == null) {
RecyclerView.LayoutManager manager = getDefaultLayoutManager(mContext);
if (manager != null) {
mRecyclerView.setLayoutManager(manager);
}
}
}
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
//移除滚动监听
if (mScrollListener != null) {
mRecyclerView.removeOnScrollListener(mScrollListener);
}
mRecyclerView = null;
mContext = null;
}
/**
* 获取默认的布局摆放器
*/
public RecyclerView.LayoutManager getDefaultLayoutManager(Context context) {
return new LinearLayoutManager(context);
}
/**
* 设置RecyclerView条目点击监听
*/
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener l) {
onItemClickListener = l;
}
/**
* 设置RecyclerView条目长按监听
*/
private OnItemLongClickListener onItemLongClickListener;
public void setOnItemLongClickListener(OnItemLongClickListener l) {
onItemLongClickListener = l;
}
/**
* 设置RecyclerView条目滚动监听
*/
private OnScrollingListener onScrollingListener;
public void setOnScrollingListener(OnScrollingListener l) {
onScrollingListener = l;
//如果当前已经有设置滚动监听再次设置需要移除原有的监听器
if (mScrollListener == null) {
mScrollListener = new ScrollListener();
}else {
mRecyclerView.removeOnScrollListener(mScrollListener);
}
//用户设置了滚动监听需要给RecyclerView设置监听
if (mRecyclerView != null) {
//添加滚动监听
mRecyclerView.addOnScrollListener(mScrollListener);
}
}
//自定义滚动监听器
private ScrollListener mScrollListener;
private class ScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (onScrollingListener == null) return;
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (!recyclerView.canScrollVertically(1)) {
//是否能向下滚动false表示已经滚动到底部
onScrollingListener.onScrollDown(recyclerView);
}else if (!recyclerView.canScrollVertically(-1)) {
//是否能向上滚动false表示已经滚动到顶部
onScrollingListener.onScrollTop(recyclerView);
}
}else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
//正在滚动中
onScrollingListener.onScrolling(recyclerView);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More