Repository: JessYanCoding/AndroidAutoSize Branch: master Commit: e402ecdd998e Files: 106 Total size: 258.1 KB Directory structure: gitextract_fstq_3r4/ ├── .github/ │ └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README-zh.md ├── README.md ├── _config.yml ├── autosize/ │ ├── .gitignore │ ├── bintray.gradle │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── me/ │ └── jessyan/ │ └── autosize/ │ ├── ActivityLifecycleCallbacksImpl.java │ ├── AutoAdaptStrategy.java │ ├── AutoSize.java │ ├── AutoSizeCompat.java │ ├── AutoSizeConfig.java │ ├── DefaultAutoAdaptStrategy.java │ ├── DisplayMetricsInfo.java │ ├── FragmentLifecycleCallbacksImpl.java │ ├── FragmentLifecycleCallbacksImplToAndroidx.java │ ├── InitProvider.java │ ├── WrapperAutoAdaptStrategy.java │ ├── external/ │ │ ├── ExternalAdaptInfo.java │ │ └── ExternalAdaptManager.java │ ├── internal/ │ │ ├── CancelAdapt.java │ │ └── CustomAdapt.java │ ├── onAdaptListener.java │ ├── unit/ │ │ ├── Subunits.java │ │ └── UnitsManager.java │ └── utils/ │ ├── AutoSizeLog.java │ ├── AutoSizeUtils.java │ ├── Preconditions.java │ └── ScreenUtils.java ├── build.gradle ├── demo/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── jessyan/ │ │ └── autosize/ │ │ └── demo/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── jessyan/ │ │ │ └── autosize/ │ │ │ └── demo/ │ │ │ ├── BaseApplication.java │ │ │ ├── CustomAdaptActivity.java │ │ │ ├── CustomFragment1.java │ │ │ ├── CustomFragment2.java │ │ │ ├── CustomFragment3.java │ │ │ ├── FragmentHost.java │ │ │ └── MainActivity.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_custom_adapt.xml │ │ │ ├── activity_host.xml │ │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── me/ │ └── jessyan/ │ └── autosize/ │ └── demo/ │ └── ExampleUnitTest.java ├── demo-androidx/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── jessyan/ │ │ └── autosize/ │ │ └── demo/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── jessyan/ │ │ │ └── autosize/ │ │ │ └── demo/ │ │ │ └── androidx/ │ │ │ ├── BaseApplication.java │ │ │ ├── CustomAdaptActivity.java │ │ │ ├── CustomFragment1.java │ │ │ ├── CustomFragment2.java │ │ │ ├── CustomFragment3.java │ │ │ ├── FragmentHost.java │ │ │ └── MainActivity.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_custom_adapt.xml │ │ │ ├── activity_host.xml │ │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── me/ │ └── jessyan/ │ └── autosize/ │ └── demo/ │ └── ExampleUnitTest.java ├── demo-subunits/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── me/ │ │ └── jessyan/ │ │ └── autosize/ │ │ └── demo/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── me/ │ │ │ └── jessyan/ │ │ │ └── autosize/ │ │ │ └── demo/ │ │ │ └── subunits/ │ │ │ ├── BaseApplication.java │ │ │ ├── CustomAdaptActivity.java │ │ │ └── MainActivity.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_custom_adapt.xml │ │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── me/ │ └── jessyan/ │ └── autosize/ │ └── demo/ │ └── ExampleUnitTest.java ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ ### Environment - [x] Autosize Version: - [x] Target Android Version: - [x] Device Model: - [x] Device Resolution: - [x] Design Size On AndroidManifest: ### Bug Description: ### Log: ```log ``` ### Screenshot ### Related Code: ```java ``` ### Others: ================================================ FILE: .gitignore ================================================ # Built application files *.apk *.ap_ # Files for the ART/Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ out/ # Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log # Android Studio Navigation editor temp files .navigation/ # Android Studio captures folder captures/ # Intellij *.iml .idea # Keystore files *.jks # MacOS .DS_Store ================================================ FILE: .travis.yml ================================================ language: android jdk: oraclejdk8 before_install: - yes | sdkmanager "platforms;android-29" env: global: - ANDROID_API_LEVEL=29 - ANDROID_BUILD_TOOLS_VERSION=29.0.2 - TRAVIS_SECURE_ENV_VARS=true android: components: # The BuildTools version used by your project - tools - platform-tools - build-tools-$ANDROID_BUILD_TOOLS_VERSION - extra-android-m2repository - extra-google-android-support # The SDK version used to compile your project - android-$ANDROID_API_LEVEL licenses: - '.+' script: - ./gradlew clean - ./gradlew assembleDebug ================================================ FILE: CHANGELOG.md ================================================ [ChangeLog](https://github.com/JessYanCoding/AndroidAutoSize/releases) ================================================ FILE: LICENSE ================================================ 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. ================================================ FILE: README-zh.md ================================================ ![Logo](art/autosize_banner.jpg) ![Official](https://raw.githubusercontent.com/JessYanCoding/MVPArms/master/image/official.jpeg)

Latest Stable Version Build Status Min Sdk Version License Author QQ Group

## 今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案. ## Overview ### Pixel 2 XL | 1440 x 2880 | 560dpi:

### Pixel XL | 1440 x 2560 | 560dpi:

### Nexus 5X | 1080 x 1920 | 420dpi:

### Nexus 4 | 768 x 1280 | 320dpi:

### Nexus S | 480 x 800 | 240dpi:

## Notice * [主流机型设备信息,可以作为参考](https://material.io/tools/devices/) * [功能介绍](https://juejin.im/post/5bce688e6fb9a05cf715d1c2) * [原理分析](https://juejin.im/post/5b7a29736fb9a019d53e7ee2) * [今日头条屏幕适配方案常见问题汇总](https://github.com/JessYanCoding/AndroidAutoSize/issues/13) * [Android 进阶框架](https://github.com/JessYanCoding/MVPArms) * 对于老项目,如果使用了 **AndroidAutoLayout**,请参考 [**AndroidAutoLayout** 迁移指南](https://github.com/JessYanCoding/AndroidAutoSize/issues/90),**AndroidAutoSize** 可以和 [**AndroidAutoLayout**](https://github.com/hongyangAndroid/AndroidAutoLayout) 一起使用,因为 **AndroidAutoLayout** 使用的是 **px**,所以 **AndroidAutoSize** 对它不会产生任何影响,如果老项目的某些页面之前使用了 **dp** 进行布局,并且 **AndroidAutoSize** 对这些页面已经产生了不良影响,可以让之前使用了 **dp** 的旧 **Activity** 实现 **CancelAdapt** 取消适配,**建议使用副单位,使用副单位则不会影响之前使用了 dp 的页面 (也不会影响三方库和系统控件),详情请查看 [demo-subunits](https://github.com/JessYanCoding/AndroidAutoSize/tree/master/demo-subunits)** ## Download ### Jcenter ([ ⚠️ 已停止维护: 2022 年 2 月 1 日之后 JCenter 远程仓库将无法使用](https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter)): ``` gradle implementation 'me.jessyan:autosize:1.2.1' ``` ### JitPack: Step 1. Add the JitPack repository in your root [build.gradle](https://github.com/JessYanCoding/AndroidAutoSize/blob/master/build.gradle#L20) at the end of repositories: ```gradle allprojects { repositories { ... maven { url "https://jitpack.io" } } } ``` Step 2. Add the dependency ```gradle dependencies { implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1' } ``` ## Usage ### Step 1 (真的不吹牛逼,只需要以下这一步,框架就可以对项目中的所有页面进行适配) * **请在 AndroidManifest 中填写全局设计图尺寸 (单位 dp),如果使用副单位,则可以直接填写像素尺寸,不需要再将像素转化为 dp,详情请查看 [demo-subunits](https://github.com/JessYanCoding/AndroidAutoSize/tree/master/demo-subunits)** ```xml ``` ## Preview * 布局时的实时预览在开发阶段是一个很重要的环节,很多情况下 **Android Studio** 提供的默认预览设备并不能完全展示我们的设计图,所以我们就需要自己创建模拟设备,下面就介绍下 **dp、pt、in、mm** 这四种单位的模拟设备创建方法 * 如果您在预览时不希望在 **Preview** 中出现状态栏和导航栏, 则可以根据下图选择 **panel** 主题,使用该主题后纵向分辨率刚好填充整个预览页面,显示效果完全和设计图一致 ![theme](art/theme_panel.png) * 为了方便广大新手,所以还是将创建模拟设备的步骤贴出来,为大家操碎了心,如果觉得 **AndroidAutoSize** 不错,请一定记得 **star**,并将 **AndroidAutoSize** 推荐给您的伙伴们 ![create step](art/create_step.png) ### DP * 如果您在 **layout** 文件中使用 **dp** 作为单位进行布局 (**AndroidAutoSize** 默认支持 **dp、sp** 进行布局),则可以根据公式 **(sqrt(纵向分辨率^2+横向分辨率^2))/dpi** 求出屏幕尺寸,然后创建模拟设备 (**只用填写屏幕尺寸和分辨率**) ![dp](art/unit_dp.png) ### PT * 如果您在 **layout** 文件中使用 **pt** 作为单位进行布局 (需要通过 **AutoSizeConfig.getInstance().getUnitsManager().setSupportSubunits(Subunits.PT);** 打开对单位 **pt** 的支持),则可以根据公式 **(sqrt(纵向分辨率^2+横向分辨率^2))/72** 求出屏幕尺寸,然后创建模拟设备 (**只用填写屏幕尺寸和分辨率**) ![pt](art/unit_pt.png) ### IN * 如果您在 **layout** 文件中使用 **in** 作为单位进行布局 (需要通过 **AutoSizeConfig.getInstance().getUnitsManager().setSupportSubunits(Subunits.IN);** 打开对单位 **in** 的支持),则可以根据公式 **sqrt(纵向分辨率^2+横向分辨率^2)** 求出屏幕尺寸,然后创建模拟设备 (**只用填写屏幕尺寸和分辨率**) ![in](art/unit_in.png) ### MM * 如果您在 **layout** 文件中使用 **mm** 作为单位进行布局 (需要通过 **AutoSizeConfig.getInstance().getUnitsManager().setSupportSubunits(Subunits.MM);** 打开对单位 **mm** 的支持),则可以根据公式 **(sqrt(纵向分辨率^2+横向分辨率^2))/25.4** 求出屏幕尺寸,然后创建模拟设备 (**只用填写屏幕尺寸和分辨率**) ![mm](art/unit_mm.png) ## Advanced (以下用法看不懂?答应我,认真看 demo 好不好?) ### Activity * **当某个 Activity 的设计图尺寸与在 AndroidManifest 中填写的全局设计图尺寸不同时,可以实现 CustomAdapt 接口扩展适配参数** ```java public class CustomAdaptActivity extends AppCompatActivity implements CustomAdapt { @Override public boolean isBaseOnWidth() { return false; } @Override public float getSizeInDp() { return 667; } } ``` * **当某个 Activity 想放弃适配,请实现 CancelAdapt 接口** ```java public class CancelAdaptActivity extends AppCompatActivity implements CancelAdapt { } ``` ### Fragment * **首先开启支持 Fragment 自定义参数的功能** ```java AutoSizeConfig.getInstance().setCustomFragment(true); ``` * **当某个 Fragment 的设计图尺寸与在 AndroidManifest 中填写的全局设计图尺寸不同时,可以实现 CustomAdapt 接口扩展适配参数** ```java public class CustomAdaptFragment extends Fragment implements CustomAdapt { @Override public boolean isBaseOnWidth() { return false; } @Override public float getSizeInDp() { return 667; } } ``` * **当某个 Fragment 想放弃适配,请实现 CancelAdapt 接口** ```java public class CancelAdaptFragment extends Fragment implements CancelAdapt { } ``` ### Subunits (请认真看 demo-subunits,里面有详细介绍) * 可以在 **pt、in、mm** 这三个冷门单位中,选择一个作为副单位,副单位是用于规避修改 **DisplayMetrics#density** 所造成的对于其他使用 **dp** 布局的系统控件或三方库控件的不良影响,使用副单位后可直接填写设计图上的像素尺寸,不需要再将像素转化为 **dp** ```java AutoSizeConfig.getInstance().getUnitsManager() .setSupportDP(false) .setSupportSP(false) .setSupportSubunits(Subunits.MM); ``` ## About Me * **Email**: * **Home**: * **掘金**: * **简书**: ## License ``` Copyright 2018, jessyan 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. ``` ================================================ FILE: README.md ================================================ ![Logo](art/autosize_banner.jpg) ![Official](https://raw.githubusercontent.com/JessYanCoding/MVPArms/master/image/official.jpeg)

Latest Stable Version Build Status Min Sdk Version License Author QQ Group

中文说明

## A low-cost Android screen adaptation solution (今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案). ## Overview ### Pixel 2 XL | 1440 x 2880 | 560dpi:

### Pixel XL | 1440 x 2560 | 560dpi:

### Nexus 5X | 1080 x 1920 | 420dpi:

### Nexus 4 | 768 x 1280 | 320dpi:

### Nexus S | 480 x 800 | 240dpi:

## Notice * [Devices Info](https://material.io/tools/devices/) * [Introduction Of Function](https://juejin.im/post/5bce688e6fb9a05cf715d1c2) * [Framework Analysis](https://juejin.im/post/5b7a29736fb9a019d53e7ee2) * [Common Issues](https://github.com/JessYanCoding/AndroidAutoSize/issues/13) * [AndroidAutoLayout Migration Guide](https://github.com/JessYanCoding/AndroidAutoSize/issues/90) * [Android Advanced Framework](https://github.com/JessYanCoding/MVPArms) ## Download ### Jcenter ([ ⚠️ DEPRECATION WARNING: the JCenter repository will keep serving packages until February 1st, 2022](https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter)): ``` gradle implementation 'me.jessyan:autosize:1.2.1' ``` ### JitPack: Step 1. Add the JitPack repository in your root [build.gradle](https://github.com/JessYanCoding/AndroidAutoSize/blob/master/build.gradle#L20) at the end of repositories: ```gradle allprojects { repositories { ... maven { url "https://jitpack.io" } } } ``` Step 2. Add the dependency ```gradle dependencies { implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1' } ``` ## Usage ### Step 1 (just one steps) * **Initialize in AndroidManifest, if you use a subunits, you can write the pixel size, no need to convert the pixel to dp, please see [demo-subunits](https://github.com/JessYanCoding/AndroidAutoSize/tree/master/demo-subunits)** ```xml ``` ## Preview * Real-time preview during layout is an important part of the development phase, in many cases, the default preview device provided by **Android Studio** does not fully display our design, so we need to create the virtual device ourselves, under the **dp, pt, in, mm** four units of virtual device creation method * If you don't want the status bar and navigation bar to appear in **Preview** during preview, you can select the **panel** theme according to the following image, after using this theme, the vertical resolution just fills the entire preview page ![theme](art/theme_panel.png) * Virtual device creation method ![create step](art/create_step.png) ### DP * If you use **dp** as a unit in the **layout** file for layout (**AndroidAutoSize** supports **dp, sp** for layout by default), you can find the screen size according to the formula **(sqrt(vertical resolution^2 + horizontal resolution^2))/dpi** and create an virtual device (**write screen size and resolution only**) ![dp](art/unit_dp.png) ### PT * If you use **pt** as a unit in the **layout** file for layout (requires **AutoSizeConfig.getInstance().getUnitsManager().setSupportSubunits(Subunits.PT);** to open **pt** support), you can find the screen size according to the formula **(sqrt(vertical resolution^2 + horizontal resolution^2))/72** and create an virtual device (**write screen size and resolution only**) ![pt](art/unit_pt.png) ### IN * If you use **in** as a unit in the **layout** file for layout (requires **AutoSizeConfig.getInstance().getUnitsManager().setSupportSubunits(Subunits.IN);** to open **in** support), you can find the screen size according to the formula **sqrt(vertical resolution^2 + horizontal resolution^2)** and create an virtual device (**write screen size and resolution only**) ![in](art/unit_in.png) ### MM * If you use **mm** as a unit in the **layout** file for layout (requires **AutoSizeConfig.getInstance().getUnitsManager().setSupportSubunits(Subunits.MM);** to open **mm** support), you can find the screen size according to the formula **(sqrt(vertical resolution^2 + horizontal resolution^2))/25.4** and create an virtual device (**write screen size and resolution only**) ![mm](art/unit_mm.png) ## Advance (see demo) ### Activity * **Customize the adaptation parameters of the Activity:** ```java public class CustomAdaptActivity extends AppCompatActivity implements CustomAdapt { @Override public boolean isBaseOnWidth() { return false; } @Override public float getSizeInDp() { return 667; } } ``` * **Cancel the adaptation of the Activity:** ```java public class CancelAdaptActivity extends AppCompatActivity implements CancelAdapt { } ``` ### Fragment * **First enable the ability to support Fragment custom parameters** ```java AutoSizeConfig.getInstance().setCustomFragment(true); ``` * **Customize the adaptation parameters of the Fragment:** ```java public class CustomAdaptFragment extends Fragment implements CustomAdapt { @Override public boolean isBaseOnWidth() { return false; } @Override public float getSizeInDp() { return 667; } } ``` * **Cancel the adaptation of the Fragment:** ```java public class CancelAdaptFragment extends Fragment implements CancelAdapt { } ``` ### Subunits (see demo-subunits) * You can choose one of the three unpopular units of **pt, in, mm** as the subunits, the subunits is used to avoid the adverse effects caused by modifying **DisplayMetrics#density**, after using the subunits, you can write the pixel size on the design, you don't need to convert it to **dp** ```java AutoSizeConfig.getInstance().getUnitsManager() .setSupportDP(false) .setSupportSP(false) .setSupportSubunits(Subunits.MM); ``` ## About Me * **Email**: * **Home**: * **掘金**: * **简书**: ## License ``` Copyright 2018, jessyan 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. ``` ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-modernist ================================================ FILE: autosize/.gitignore ================================================ /build ================================================ FILE: autosize/bintray.gradle ================================================ apply plugin: 'com.novoda.bintray-release' allprojects { repositories { jcenter() } tasks.withType(Javadoc) { options{ encoding "UTF-8" charSet 'UTF-8' links "http://docs.oracle.com/javase/7/docs/api" } options.addStringOption('Xdoclint:none', '-quiet') } } def siteUrl = 'https://github.com/JessYanCoding/AndroidAutoSize' // 项目的主页 publish { userOrg = 'jessyancoding' //bintray注册的用户名 groupId = 'me.jessyan' //compile引用时的第1部分groupId artifactId = 'autosize' //compile引用时的第2部分项目名 publishVersion = rootProject.versionName //compile引用时的第3部分版本号 desc = '一个极低成本的 Android 屏幕适配方案' website = siteUrl } ================================================ FILE: autosize/build.gradle ================================================ apply plugin: 'com.android.library' android { compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion defaultConfig { minSdkVersion rootProject.minSdkVersion targetSdkVersion rootProject.targetSdkVersion versionCode rootProject.versionCode versionName rootProject.versionName consumerProguardFiles 'proguard-rules.pro' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false warning 'InvalidPackage' } } dependencies { compileOnly rootProject.appcompat_v7 compileOnly rootProject.androidx_appcompat } apply from: 'bintray.gradle' ================================================ FILE: autosize/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # 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 -keep class me.jessyan.autosize.** { *; } -keep interface me.jessyan.autosize.** { *; } ================================================ FILE: autosize/src/main/AndroidManifest.xml ================================================ ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/ActivityLifecycleCallbacksImpl.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.app.Activity; import android.app.Application; import android.os.Bundle; import static me.jessyan.autosize.AutoSizeConfig.DEPENDENCY_ANDROIDX; import static me.jessyan.autosize.AutoSizeConfig.DEPENDENCY_SUPPORT; /** * ================================================ * {@link ActivityLifecycleCallbacksImpl} 可用来代替在 BaseActivity 中加入适配代码的传统方式 * {@link ActivityLifecycleCallbacksImpl} 这种方案类似于 AOP, 面向接口, 侵入性低, 方便统一管理, 扩展性强, 并且也支持适配三方库的 {@link Activity} *

* Created by JessYan on 2018/8/8 14:32 * Contact me * Follow me * ================================================ */ public class ActivityLifecycleCallbacksImpl implements Application.ActivityLifecycleCallbacks { /** * 屏幕适配逻辑策略类 */ private AutoAdaptStrategy mAutoAdaptStrategy; /** * 让 Fragment 支持自定义适配参数 */ private FragmentLifecycleCallbacksImpl mFragmentLifecycleCallbacks; private FragmentLifecycleCallbacksImplToAndroidx mFragmentLifecycleCallbacksToAndroidx; public ActivityLifecycleCallbacksImpl(AutoAdaptStrategy autoAdaptStrategy) { if (DEPENDENCY_ANDROIDX) { mFragmentLifecycleCallbacksToAndroidx = new FragmentLifecycleCallbacksImplToAndroidx(autoAdaptStrategy); } else if (DEPENDENCY_SUPPORT){ mFragmentLifecycleCallbacks = new FragmentLifecycleCallbacksImpl(autoAdaptStrategy); } mAutoAdaptStrategy = autoAdaptStrategy; } @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { if (AutoSizeConfig.getInstance().isCustomFragment()) { if (mFragmentLifecycleCallbacksToAndroidx != null && activity instanceof androidx.fragment.app.FragmentActivity) { ((androidx.fragment.app.FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(mFragmentLifecycleCallbacksToAndroidx, true); } else if (mFragmentLifecycleCallbacks != null && activity instanceof android.support.v4.app.FragmentActivity) { ((android.support.v4.app.FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(mFragmentLifecycleCallbacks, true); } } //Activity 中的 setContentView(View) 一定要在 super.onCreate(Bundle); 之后执行 if (mAutoAdaptStrategy != null) { mAutoAdaptStrategy.applyAdapt(activity, activity); } } @Override public void onActivityStarted(Activity activity) { if (mAutoAdaptStrategy != null) { mAutoAdaptStrategy.applyAdapt(activity, activity); } } @Override public void onActivityResumed(Activity activity) { } @Override public void onActivityPaused(Activity activity) { } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { } /** * 设置屏幕适配逻辑策略类 * * @param autoAdaptStrategy {@link AutoAdaptStrategy} */ public void setAutoAdaptStrategy(AutoAdaptStrategy autoAdaptStrategy) { mAutoAdaptStrategy = autoAdaptStrategy; if (mFragmentLifecycleCallbacksToAndroidx != null) { mFragmentLifecycleCallbacksToAndroidx.setAutoAdaptStrategy(autoAdaptStrategy); } else if (mFragmentLifecycleCallbacks != null) { mFragmentLifecycleCallbacks.setAutoAdaptStrategy(autoAdaptStrategy); } } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/AutoAdaptStrategy.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.app.Activity; import android.app.Application; import android.util.DisplayMetrics; /** * ================================================ * 屏幕适配逻辑策略类, 可通过 {@link AutoSizeConfig#init(Application, boolean, AutoAdaptStrategy)} * 和 {@link AutoSizeConfig#setAutoAdaptStrategy(AutoAdaptStrategy)} 切换策略 * * @see DefaultAutoAdaptStrategy * Created by JessYan on 2018/8/9 15:13 * Contact me * Follow me * ================================================ */ public interface AutoAdaptStrategy { /** * 开始执行屏幕适配逻辑 * * @param target 需要屏幕适配的对象 (可能是 {@link Activity} 或者 Fragment) * @param activity 需要拿到当前的 {@link Activity} 才能修改 {@link DisplayMetrics#density} */ void applyAdapt(Object target, Activity activity); } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/AutoSize.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.app.Activity; import android.app.Application; import android.app.Dialog; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.net.Uri; import android.util.DisplayMetrics; import android.util.SparseArray; import android.view.View; import java.util.Locale; import me.jessyan.autosize.external.ExternalAdaptInfo; import me.jessyan.autosize.external.ExternalAdaptManager; import me.jessyan.autosize.internal.CancelAdapt; import me.jessyan.autosize.internal.CustomAdapt; import me.jessyan.autosize.utils.AutoSizeLog; import me.jessyan.autosize.utils.Preconditions; /** * ================================================ * AndroidAutoSize 用于屏幕适配的核心方法都在这里, 核心原理来自于 今日头条官方适配方案 * 此方案只要应用到 {@link Activity} 上, 这个 {@link Activity} 下的所有 Fragment、{@link Dialog}、 * 自定义 {@link View} 都会达到适配的效果, 如果某个页面不想使用适配请让该 {@link Activity} 实现 {@link CancelAdapt} *

* 任何方案都不可能完美, 在成本和收益中做出取舍, 选择出最适合自己的方案即可, 在没有更好的方案出来之前, 只有继续忍耐它的不完美, 或者自己作出改变 * 既然选择, 就不要抱怨, 感谢 今日头条技术团队 和 张鸿洋 等人对 Android 屏幕适配领域的的贡献 *

* Created by JessYan on 2018/8/8 19:20 * Contact me * Follow me * ================================================ */ public final class AutoSize { private static SparseArray mCache = new SparseArray<>(); private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; private static final int MODE_ON_WIDTH = 1 << MODE_SHIFT; private static final int MODE_DEVICE_SIZE = 2 << MODE_SHIFT; private AutoSize() { throw new IllegalStateException("you can't instantiate me!"); } /** * 检查 AndroidAutoSize 是否已经初始化 * * @return {@code false} 表示 AndroidAutoSize 还未初始化, {@code true} 表示 AndroidAutoSize 已经初始化 */ public static boolean checkInit() { return AutoSizeConfig.getInstance().getInitDensity() != -1; } /** * 由于 AndroidAutoSize 会通过 {@link InitProvider} 的实例化而自动完成初始化, 并且 {@link AutoSizeConfig#init(Application)} * 只允许被调用一次, 否则会报错, 所以 {@link AutoSizeConfig#init(Application)} 的调用权限并没有设为 public, 不允许外部使用者调用 * 但由于某些 issues 反应, 可能会在某些特殊情况下出现 {@link InitProvider} 未能正常实例化的情况, 导致 AndroidAutoSize 未能完成初始化 * 所以提供此静态方法用于让外部使用者在异常情况下也可以初始化 AndroidAutoSize, 在 {@link Application#onCreate()} 中调用即可 * * @param application {@link Application} */ public static void checkAndInit(Application application) { if (!checkInit()) { AutoSizeConfig.getInstance() .setLog(true) .init(application) .setUseDeviceSize(false); } } /** * 使用 AndroidAutoSize 初始化时设置的默认适配参数进行适配 (AndroidManifest 的 Meta 属性) * * @param activity {@link Activity} */ public static void autoConvertDensityOfGlobal(Activity activity) { if (AutoSizeConfig.getInstance().isBaseOnWidth()) { autoConvertDensityBaseOnWidth(activity, AutoSizeConfig.getInstance().getDesignWidthInDp()); } else { autoConvertDensityBaseOnHeight(activity, AutoSizeConfig.getInstance().getDesignHeightInDp()); } } /** * 使用 {@link Activity} 或 Fragment 的自定义参数进行适配 * * @param activity {@link Activity} * @param customAdapt {@link Activity} 或 Fragment 需实现 {@link CustomAdapt} */ public static void autoConvertDensityOfCustomAdapt(Activity activity, CustomAdapt customAdapt) { Preconditions.checkNotNull(customAdapt, "customAdapt == null"); float sizeInDp = customAdapt.getSizeInDp(); //如果 CustomAdapt#getSizeInDp() 返回 0, 则使用在 AndroidManifest 上填写的设计图尺寸 if (sizeInDp <= 0) { if (customAdapt.isBaseOnWidth()) { sizeInDp = AutoSizeConfig.getInstance().getDesignWidthInDp(); } else { sizeInDp = AutoSizeConfig.getInstance().getDesignHeightInDp(); } } autoConvertDensity(activity, sizeInDp, customAdapt.isBaseOnWidth()); } /** * 使用外部三方库的 {@link Activity} 或 Fragment 的自定义适配参数进行适配 * * @param activity {@link Activity} * @param externalAdaptInfo 三方库的 {@link Activity} 或 Fragment 提供的适配参数, 需要配合 {@link ExternalAdaptManager#addExternalAdaptInfoOfActivity(Class, ExternalAdaptInfo)} */ public static void autoConvertDensityOfExternalAdaptInfo(Activity activity, ExternalAdaptInfo externalAdaptInfo) { Preconditions.checkNotNull(externalAdaptInfo, "externalAdaptInfo == null"); float sizeInDp = externalAdaptInfo.getSizeInDp(); //如果 ExternalAdaptInfo#getSizeInDp() 返回 0, 则使用在 AndroidManifest 上填写的设计图尺寸 if (sizeInDp <= 0) { if (externalAdaptInfo.isBaseOnWidth()) { sizeInDp = AutoSizeConfig.getInstance().getDesignWidthInDp(); } else { sizeInDp = AutoSizeConfig.getInstance().getDesignHeightInDp(); } } autoConvertDensity(activity, sizeInDp, externalAdaptInfo.isBaseOnWidth()); } /** * 以宽度为基准进行适配 * * @param activity {@link Activity} * @param designWidthInDp 设计图的总宽度 */ public static void autoConvertDensityBaseOnWidth(Activity activity, float designWidthInDp) { autoConvertDensity(activity, designWidthInDp, true); } /** * 以高度为基准进行适配 * * @param activity {@link Activity} * @param designHeightInDp 设计图的总高度 */ public static void autoConvertDensityBaseOnHeight(Activity activity, float designHeightInDp) { autoConvertDensity(activity, designHeightInDp, false); } /** * 这里是今日头条适配方案的核心代码, 核心在于根据当前设备的实际情况做自动计算并转换 {@link DisplayMetrics#density}、 * {@link DisplayMetrics#scaledDensity}、{@link DisplayMetrics#densityDpi} 这三个值, 额外增加 {@link DisplayMetrics#xdpi} * 以支持单位 {@code pt}、{@code in}、{@code mm} * * @param activity {@link Activity} * @param sizeInDp 设计图上的设计尺寸, 单位 dp, 如果 {@param isBaseOnWidth} 设置为 {@code true}, * {@param sizeInDp} 则应该填写设计图的总宽度, 如果 {@param isBaseOnWidth} 设置为 {@code false}, * {@param sizeInDp} 则应该填写设计图的总高度 * @param isBaseOnWidth 是否按照宽度进行等比例适配, {@code true} 为以宽度进行等比例适配, {@code false} 为以高度进行等比例适配 * @see 今日头条官方适配方案 */ public static void autoConvertDensity(Activity activity, float sizeInDp, boolean isBaseOnWidth) { Preconditions.checkNotNull(activity, "activity == null"); Preconditions.checkMainThread(); float subunitsDesignSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getUnitsManager().getDesignWidth() : AutoSizeConfig.getInstance().getUnitsManager().getDesignHeight(); subunitsDesignSize = subunitsDesignSize > 0 ? subunitsDesignSize : sizeInDp; int screenSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getScreenWidth() : AutoSizeConfig.getInstance().getScreenHeight(); int key = Math.round((sizeInDp + subunitsDesignSize + screenSize) * AutoSizeConfig.getInstance().getInitScaledDensity()) & ~MODE_MASK; key = isBaseOnWidth ? (key | MODE_ON_WIDTH) : (key & ~MODE_ON_WIDTH); key = AutoSizeConfig.getInstance().isUseDeviceSize() ? (key | MODE_DEVICE_SIZE) : (key & ~MODE_DEVICE_SIZE); DisplayMetricsInfo displayMetricsInfo = mCache.get(key); float targetDensity = 0; int targetDensityDpi = 0; float targetScaledDensity = 0; float targetXdpi = 0; int targetScreenWidthDp; int targetScreenHeightDp; if (displayMetricsInfo == null) { if (isBaseOnWidth) { targetDensity = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / sizeInDp; } else { targetDensity = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / sizeInDp; } if (AutoSizeConfig.getInstance().getPrivateFontScale() > 0) { targetScaledDensity = targetDensity * AutoSizeConfig.getInstance().getPrivateFontScale(); } else { float systemFontScale = AutoSizeConfig.getInstance().isExcludeFontScale() ? 1 : AutoSizeConfig.getInstance(). getInitScaledDensity() * 1.0f / AutoSizeConfig.getInstance().getInitDensity(); targetScaledDensity = targetDensity * systemFontScale; } targetDensityDpi = (int) (targetDensity * 160); targetScreenWidthDp = (int) (AutoSizeConfig.getInstance().getScreenWidth() / targetDensity); targetScreenHeightDp = (int) (AutoSizeConfig.getInstance().getScreenHeight() / targetDensity); if (isBaseOnWidth) { targetXdpi = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / subunitsDesignSize; } else { targetXdpi = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / subunitsDesignSize; } mCache.put(key, new DisplayMetricsInfo(targetDensity, targetDensityDpi, targetScaledDensity, targetXdpi, targetScreenWidthDp, targetScreenHeightDp)); } else { targetDensity = displayMetricsInfo.getDensity(); targetDensityDpi = displayMetricsInfo.getDensityDpi(); targetScaledDensity = displayMetricsInfo.getScaledDensity(); targetXdpi = displayMetricsInfo.getXdpi(); targetScreenWidthDp = displayMetricsInfo.getScreenWidthDp(); targetScreenHeightDp = displayMetricsInfo.getScreenHeightDp(); } setDensity(activity, targetDensity, targetDensityDpi, targetScaledDensity, targetXdpi); setScreenSizeDp(activity, targetScreenWidthDp, targetScreenHeightDp); AutoSizeLog.d(String.format(Locale.ENGLISH, "The %s has been adapted! \n%s Info: isBaseOnWidth = %s, %s = %f, %s = %f, targetDensity = %f, targetScaledDensity = %f, targetDensityDpi = %d, targetXdpi = %f, targetScreenWidthDp = %d, targetScreenHeightDp = %d" , activity.getClass().getName(), activity.getClass().getSimpleName(), isBaseOnWidth, isBaseOnWidth ? "designWidthInDp" : "designHeightInDp", sizeInDp, isBaseOnWidth ? "designWidthInSubunits" : "designHeightInSubunits", subunitsDesignSize , targetDensity, targetScaledDensity, targetDensityDpi, targetXdpi, targetScreenWidthDp, targetScreenHeightDp)); } /** * 取消适配 * * @param activity {@link Activity} */ public static void cancelAdapt(Activity activity) { Preconditions.checkMainThread(); float initXdpi = AutoSizeConfig.getInstance().getInitXdpi(); switch (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits()) { case PT: initXdpi = initXdpi / 72f; break; case MM: initXdpi = initXdpi / 25.4f; break; default: } setDensity(activity, AutoSizeConfig.getInstance().getInitDensity() , AutoSizeConfig.getInstance().getInitDensityDpi() , AutoSizeConfig.getInstance().getInitScaledDensity() , initXdpi); setScreenSizeDp(activity , AutoSizeConfig.getInstance().getInitScreenWidthDp() , AutoSizeConfig.getInstance().getInitScreenHeightDp()); } /** * 当 App 中出现多进程,并且您需要适配所有的进程,就需要在 App 初始化时调用 {@link #initCompatMultiProcess} * 建议实现自定义 {@link Application} 并在 {@link Application#onCreate()} 中调用 {@link #initCompatMultiProcess} * * @param context {@link Context} */ public static void initCompatMultiProcess(Context context) { context.getContentResolver().query(Uri.parse("content://" + context.getPackageName() + ".autosize-init-provider"), null, null, null, null); } /** * 给几大 {@link DisplayMetrics} 赋值 * * @param activity {@link Activity} * @param density {@link DisplayMetrics#density} * @param densityDpi {@link DisplayMetrics#densityDpi} * @param scaledDensity {@link DisplayMetrics#scaledDensity} * @param xdpi {@link DisplayMetrics#xdpi} */ private static void setDensity(Activity activity, float density, int densityDpi, float scaledDensity, float xdpi) { DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics(); setDensity(activityDisplayMetrics, density, densityDpi, scaledDensity, xdpi); DisplayMetrics appDisplayMetrics = AutoSizeConfig.getInstance().getApplication().getResources().getDisplayMetrics(); setDensity(appDisplayMetrics, density, densityDpi, scaledDensity, xdpi); //兼容 MIUI DisplayMetrics activityDisplayMetricsOnMIUI = getMetricsOnMiui(activity.getResources()); DisplayMetrics appDisplayMetricsOnMIUI = getMetricsOnMiui(AutoSizeConfig.getInstance().getApplication().getResources()); if (activityDisplayMetricsOnMIUI != null) { setDensity(activityDisplayMetricsOnMIUI, density, densityDpi, scaledDensity, xdpi); } if (appDisplayMetricsOnMIUI != null) { setDensity(appDisplayMetricsOnMIUI, density, densityDpi, scaledDensity, xdpi); } } /** * 赋值 * * @param displayMetrics {@link DisplayMetrics} * @param density {@link DisplayMetrics#density} * @param densityDpi {@link DisplayMetrics#densityDpi} * @param scaledDensity {@link DisplayMetrics#scaledDensity} * @param xdpi {@link DisplayMetrics#xdpi} */ private static void setDensity(DisplayMetrics displayMetrics, float density, int densityDpi, float scaledDensity, float xdpi) { if (AutoSizeConfig.getInstance().getUnitsManager().isSupportDP()) { displayMetrics.density = density; displayMetrics.densityDpi = densityDpi; } if (AutoSizeConfig.getInstance().getUnitsManager().isSupportSP()) { displayMetrics.scaledDensity = scaledDensity; } switch (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits()) { case NONE: break; case PT: displayMetrics.xdpi = xdpi * 72f; break; case IN: displayMetrics.xdpi = xdpi; break; case MM: displayMetrics.xdpi = xdpi * 25.4f; break; default: } } /** * 给 {@link Configuration} 赋值 * * @param activity {@link Activity} * @param screenWidthDp {@link Configuration#screenWidthDp} * @param screenHeightDp {@link Configuration#screenHeightDp} */ private static void setScreenSizeDp(Activity activity, int screenWidthDp, int screenHeightDp) { if (AutoSizeConfig.getInstance().getUnitsManager().isSupportDP() && AutoSizeConfig.getInstance().getUnitsManager().isSupportScreenSizeDP()) { Configuration activityConfiguration = activity.getResources().getConfiguration(); setScreenSizeDp(activityConfiguration, screenWidthDp, screenHeightDp); Configuration appConfiguration = AutoSizeConfig.getInstance().getApplication().getResources().getConfiguration(); setScreenSizeDp(appConfiguration, screenWidthDp, screenHeightDp); } } /** * Configuration赋值 * * @param configuration {@link Configuration} * @param screenWidthDp {@link Configuration#screenWidthDp} * @param screenHeightDp {@link Configuration#screenHeightDp} */ private static void setScreenSizeDp(Configuration configuration, int screenWidthDp, int screenHeightDp) { configuration.screenWidthDp = screenWidthDp; configuration.screenHeightDp = screenHeightDp; } /** * 解决 MIUI 更改框架导致的 MIUI7 + Android5.1.1 上出现的失效问题 (以及极少数基于这部分 MIUI 去掉 ART 然后置入 XPosed 的手机) * 来源于: https://github.com/Firedamp/Rudeness/blob/master/rudeness-sdk/src/main/java/com/bulong/rudeness/RudenessScreenHelper.java#L61:5 * * @param resources {@link Resources} * @return {@link DisplayMetrics}, 可能为 {@code null} */ private static DisplayMetrics getMetricsOnMiui(Resources resources) { if (AutoSizeConfig.getInstance().isMiui() && AutoSizeConfig.getInstance().getTmpMetricsField() != null) { try { return (DisplayMetrics) AutoSizeConfig.getInstance().getTmpMetricsField().get(resources); } catch (Exception e) { return null; } } return null; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/AutoSizeCompat.java ================================================ /* * Copyright 2019 JessYan * * 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. */ package me.jessyan.autosize; import android.app.Activity; import android.content.res.Configuration; import android.content.res.Resources; import android.util.DisplayMetrics; import android.util.SparseArray; import me.jessyan.autosize.external.ExternalAdaptInfo; import me.jessyan.autosize.external.ExternalAdaptManager; import me.jessyan.autosize.internal.CustomAdapt; import me.jessyan.autosize.utils.Preconditions; /** * ================================================ * 当遇到本来适配正常的布局突然出现适配失效,适配异常等问题, 重写当前 {@link Activity} 的 {@link Activity#getResources()} 并调用 * {@link AutoSizeCompat} 的对应方法即可解决问题 *

* Created by JessYan on 2018/8/8 19:20 * Contact me * Follow me * ================================================ */ public final class AutoSizeCompat { private static SparseArray mCache = new SparseArray<>(); private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; private static final int MODE_ON_WIDTH = 1 << MODE_SHIFT; private static final int MODE_DEVICE_SIZE = 2 << MODE_SHIFT; private AutoSizeCompat() { throw new IllegalStateException("you can't instantiate me!"); } /** * 使用 AndroidAutoSize 初始化时设置的默认适配参数进行适配 (AndroidManifest 的 Meta 属性) * * @param resources {@link Resources} */ public static void autoConvertDensityOfGlobal(Resources resources) { if (AutoSizeConfig.getInstance().isBaseOnWidth()) { autoConvertDensityBaseOnWidth(resources, AutoSizeConfig.getInstance().getDesignWidthInDp()); } else { autoConvertDensityBaseOnHeight(resources, AutoSizeConfig.getInstance().getDesignHeightInDp()); } } /** * 使用 {@link Activity} 或 Fragment 的自定义参数进行适配 * * @param resources {@link Resources} * @param customAdapt {@link Activity} 或 Fragment 需实现 {@link CustomAdapt} */ public static void autoConvertDensityOfCustomAdapt(Resources resources, CustomAdapt customAdapt) { Preconditions.checkNotNull(customAdapt, "customAdapt == null"); float sizeInDp = customAdapt.getSizeInDp(); //如果 CustomAdapt#getSizeInDp() 返回 0, 则使用在 AndroidManifest 上填写的设计图尺寸 if (sizeInDp <= 0) { if (customAdapt.isBaseOnWidth()) { sizeInDp = AutoSizeConfig.getInstance().getDesignWidthInDp(); } else { sizeInDp = AutoSizeConfig.getInstance().getDesignHeightInDp(); } } autoConvertDensity(resources, sizeInDp, customAdapt.isBaseOnWidth()); } /** * 使用外部三方库的 {@link Activity} 或 Fragment 的自定义适配参数进行适配 * * @param resources {@link Resources} * @param externalAdaptInfo 三方库的 {@link Activity} 或 Fragment 提供的适配参数, 需要配合 {@link ExternalAdaptManager#addExternalAdaptInfoOfActivity(Class, ExternalAdaptInfo)} */ public static void autoConvertDensityOfExternalAdaptInfo(Resources resources, ExternalAdaptInfo externalAdaptInfo) { Preconditions.checkNotNull(externalAdaptInfo, "externalAdaptInfo == null"); float sizeInDp = externalAdaptInfo.getSizeInDp(); //如果 ExternalAdaptInfo#getSizeInDp() 返回 0, 则使用在 AndroidManifest 上填写的设计图尺寸 if (sizeInDp <= 0) { if (externalAdaptInfo.isBaseOnWidth()) { sizeInDp = AutoSizeConfig.getInstance().getDesignWidthInDp(); } else { sizeInDp = AutoSizeConfig.getInstance().getDesignHeightInDp(); } } autoConvertDensity(resources, sizeInDp, externalAdaptInfo.isBaseOnWidth()); } /** * 以宽度为基准进行适配 * * @param resources {@link Resources} * @param designWidthInDp 设计图的总宽度 */ public static void autoConvertDensityBaseOnWidth(Resources resources, float designWidthInDp) { autoConvertDensity(resources, designWidthInDp, true); } /** * 以高度为基准进行适配 * * @param resources {@link Resources} * @param designHeightInDp 设计图的总高度 */ public static void autoConvertDensityBaseOnHeight(Resources resources, float designHeightInDp) { autoConvertDensity(resources, designHeightInDp, false); } /** * 这里是今日头条适配方案的核心代码, 核心在于根据当前设备的实际情况做自动计算并转换 {@link DisplayMetrics#density}、 * {@link DisplayMetrics#scaledDensity}、{@link DisplayMetrics#densityDpi} 这三个值, 额外增加 {@link DisplayMetrics#xdpi} * 以支持单位 {@code pt}、{@code in}、{@code mm} * * @param resources {@link Resources} * @param sizeInDp 设计图上的设计尺寸, 单位 dp, 如果 {@param isBaseOnWidth} 设置为 {@code true}, * {@param sizeInDp} 则应该填写设计图的总宽度, 如果 {@param isBaseOnWidth} 设置为 {@code false}, * {@param sizeInDp} 则应该填写设计图的总高度 * @param isBaseOnWidth 是否按照宽度进行等比例适配, {@code true} 为以宽度进行等比例适配, {@code false} 为以高度进行等比例适配 * @see 今日头条官方适配方案 */ public static void autoConvertDensity(Resources resources, float sizeInDp, boolean isBaseOnWidth) { Preconditions.checkNotNull(resources, "resources == null"); Preconditions.checkMainThread(); float subunitsDesignSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getUnitsManager().getDesignWidth() : AutoSizeConfig.getInstance().getUnitsManager().getDesignHeight(); subunitsDesignSize = subunitsDesignSize > 0 ? subunitsDesignSize : sizeInDp; int screenSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getScreenWidth() : AutoSizeConfig.getInstance().getScreenHeight(); int key = Math.round((sizeInDp + subunitsDesignSize + screenSize) * AutoSizeConfig.getInstance().getInitScaledDensity()) & ~MODE_MASK; key = isBaseOnWidth ? (key | MODE_ON_WIDTH) : (key & ~MODE_ON_WIDTH); key = AutoSizeConfig.getInstance().isUseDeviceSize() ? (key | MODE_DEVICE_SIZE) : (key & ~MODE_DEVICE_SIZE); DisplayMetricsInfo displayMetricsInfo = mCache.get(key); float targetDensity = 0; int targetDensityDpi = 0; float targetScaledDensity = 0; float targetXdpi = 0; int targetScreenWidthDp; int targetScreenHeightDp; if (displayMetricsInfo == null) { if (isBaseOnWidth) { targetDensity = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / sizeInDp; } else { targetDensity = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / sizeInDp; } if (AutoSizeConfig.getInstance().getPrivateFontScale() > 0) { targetScaledDensity = targetDensity * AutoSizeConfig.getInstance().getPrivateFontScale(); } else { float systemFontScale = AutoSizeConfig.getInstance().isExcludeFontScale() ? 1 : AutoSizeConfig.getInstance(). getInitScaledDensity() * 1.0f / AutoSizeConfig.getInstance().getInitDensity(); targetScaledDensity = targetDensity * systemFontScale; } targetDensityDpi = (int) (targetDensity * 160); targetScreenWidthDp = (int) (AutoSizeConfig.getInstance().getScreenWidth() / targetDensity); targetScreenHeightDp = (int) (AutoSizeConfig.getInstance().getScreenHeight() / targetDensity); if (isBaseOnWidth) { targetXdpi = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / subunitsDesignSize; } else { targetXdpi = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / subunitsDesignSize; } mCache.put(key, new DisplayMetricsInfo(targetDensity, targetDensityDpi, targetScaledDensity, targetXdpi, targetScreenWidthDp, targetScreenHeightDp)); } else { targetDensity = displayMetricsInfo.getDensity(); targetDensityDpi = displayMetricsInfo.getDensityDpi(); targetScaledDensity = displayMetricsInfo.getScaledDensity(); targetXdpi = displayMetricsInfo.getXdpi(); targetScreenWidthDp = displayMetricsInfo.getScreenWidthDp(); targetScreenHeightDp = displayMetricsInfo.getScreenHeightDp(); } setDensity(resources, targetDensity, targetDensityDpi, targetScaledDensity, targetXdpi); setScreenSizeDp(resources, targetScreenWidthDp, targetScreenHeightDp); } /** * 取消适配 * * @param resources {@link Resources} */ public static void cancelAdapt(Resources resources) { Preconditions.checkMainThread(); float initXdpi = AutoSizeConfig.getInstance().getInitXdpi(); switch (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits()) { case PT: initXdpi = initXdpi / 72f; break; case MM: initXdpi = initXdpi / 25.4f; break; default: } setDensity(resources, AutoSizeConfig.getInstance().getInitDensity() , AutoSizeConfig.getInstance().getInitDensityDpi() , AutoSizeConfig.getInstance().getInitScaledDensity() , initXdpi); setScreenSizeDp(resources , AutoSizeConfig.getInstance().getInitScreenWidthDp() , AutoSizeConfig.getInstance().getInitScreenHeightDp()); } /** * 给几大 {@link DisplayMetrics} 赋值 * * @param resources {@link Resources} * @param density {@link DisplayMetrics#density} * @param densityDpi {@link DisplayMetrics#densityDpi} * @param scaledDensity {@link DisplayMetrics#scaledDensity} * @param xdpi {@link DisplayMetrics#xdpi} */ private static void setDensity(Resources resources, float density, int densityDpi, float scaledDensity, float xdpi) { DisplayMetrics activityDisplayMetrics = resources.getDisplayMetrics(); setDensity(activityDisplayMetrics, density, densityDpi, scaledDensity, xdpi); DisplayMetrics appDisplayMetrics = AutoSizeConfig.getInstance().getApplication().getResources().getDisplayMetrics(); setDensity(appDisplayMetrics, density, densityDpi, scaledDensity, xdpi); //兼容 MIUI DisplayMetrics activityDisplayMetricsOnMIUI = getMetricsOnMiui(resources); DisplayMetrics appDisplayMetricsOnMIUI = getMetricsOnMiui(AutoSizeConfig.getInstance().getApplication().getResources()); if (activityDisplayMetricsOnMIUI != null) { setDensity(activityDisplayMetricsOnMIUI, density, densityDpi, scaledDensity, xdpi); } if (appDisplayMetricsOnMIUI != null) { setDensity(appDisplayMetricsOnMIUI, density, densityDpi, scaledDensity, xdpi); } } /** * 赋值 * * @param displayMetrics {@link DisplayMetrics} * @param density {@link DisplayMetrics#density} * @param densityDpi {@link DisplayMetrics#densityDpi} * @param scaledDensity {@link DisplayMetrics#scaledDensity} * @param xdpi {@link DisplayMetrics#xdpi} */ private static void setDensity(DisplayMetrics displayMetrics, float density, int densityDpi, float scaledDensity, float xdpi) { if (AutoSizeConfig.getInstance().getUnitsManager().isSupportDP()) { displayMetrics.density = density; displayMetrics.densityDpi = densityDpi; } if (AutoSizeConfig.getInstance().getUnitsManager().isSupportSP()) { displayMetrics.scaledDensity = scaledDensity; } switch (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits()) { case NONE: break; case PT: displayMetrics.xdpi = xdpi * 72f; break; case IN: displayMetrics.xdpi = xdpi; break; case MM: displayMetrics.xdpi = xdpi * 25.4f; break; default: } } /** * 给 {@link Configuration} 赋值 * * @param resources {@link Resources} * @param screenWidthDp {@link Configuration#screenWidthDp} * @param screenHeightDp {@link Configuration#screenHeightDp} */ private static void setScreenSizeDp(Resources resources, int screenWidthDp, int screenHeightDp) { if (AutoSizeConfig.getInstance().getUnitsManager().isSupportDP() && AutoSizeConfig.getInstance().getUnitsManager().isSupportScreenSizeDP()) { Configuration activityConfiguration = resources.getConfiguration(); setScreenSizeDp(activityConfiguration, screenWidthDp, screenHeightDp); Configuration appConfiguration = AutoSizeConfig.getInstance().getApplication().getResources().getConfiguration(); setScreenSizeDp(appConfiguration, screenWidthDp, screenHeightDp); } } /** * Configuration赋值 * * @param configuration {@link Configuration} * @param screenWidthDp {@link Configuration#screenWidthDp} * @param screenHeightDp {@link Configuration#screenHeightDp} */ private static void setScreenSizeDp(Configuration configuration, int screenWidthDp, int screenHeightDp) { configuration.screenWidthDp = screenWidthDp; configuration.screenHeightDp = screenHeightDp; } /** * 解决 MIUI 更改框架导致的 MIUI7 + Android5.1.1 上出现的失效问题 (以及极少数基于这部分 MIUI 去掉 ART 然后置入 XPosed 的手机) * 来源于: https://github.com/Firedamp/Rudeness/blob/master/rudeness-sdk/src/main/java/com/bulong/rudeness/RudenessScreenHelper.java#L61:5 * * @param resources {@link Resources} * @return {@link DisplayMetrics}, 可能为 {@code null} */ private static DisplayMetrics getMetricsOnMiui(Resources resources) { if (AutoSizeConfig.getInstance().isMiui() && AutoSizeConfig.getInstance().getTmpMetricsField() != null) { try { return (DisplayMetrics) AutoSizeConfig.getInstance().getTmpMetricsField().get(resources); } catch (Exception e) { return null; } } return null; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/AutoSizeConfig.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.app.Activity; import android.app.Application; import android.content.ComponentCallbacks; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.util.DisplayMetrics; import java.lang.reflect.Field; import me.jessyan.autosize.external.ExternalAdaptManager; import me.jessyan.autosize.unit.Subunits; import me.jessyan.autosize.unit.UnitsManager; import me.jessyan.autosize.utils.AutoSizeLog; import me.jessyan.autosize.utils.Preconditions; import me.jessyan.autosize.utils.ScreenUtils; /** * ================================================ * AndroidAutoSize 参数配置类, 给 AndroidAutoSize 配置一些必要的自定义参数 *

* Created by JessYan on 2018/8/8 09:58 * Contact me * Follow me * ================================================ */ public final class AutoSizeConfig { private static volatile AutoSizeConfig sInstance; private static final String KEY_DESIGN_WIDTH_IN_DP = "design_width_in_dp"; private static final String KEY_DESIGN_HEIGHT_IN_DP = "design_height_in_dp"; public static final boolean DEPENDENCY_ANDROIDX; public static final boolean DEPENDENCY_SUPPORT; private Application mApplication; /** * 用来管理外部三方库 {@link Activity} 的适配 */ private ExternalAdaptManager mExternalAdaptManager = new ExternalAdaptManager(); /** * 用来管理 AndroidAutoSize 支持的所有单位, AndroidAutoSize 支持五种单位 (dp、sp、pt、in、mm) */ private UnitsManager mUnitsManager = new UnitsManager(); /** * 最初的 {@link DisplayMetrics#density} */ private float mInitDensity = -1; /** * 最初的 {@link DisplayMetrics#densityDpi} */ private int mInitDensityDpi; /** * 最初的 {@link DisplayMetrics#scaledDensity} */ private float mInitScaledDensity; /** * 最初的 {@link DisplayMetrics#xdpi} */ private float mInitXdpi; /** * 最初的 {@link Configuration#screenWidthDp} */ private int mInitScreenWidthDp; /** * 最初的 {@link Configuration#screenHeightDp} */ private int mInitScreenHeightDp; /** * 设计图上的总宽度, 单位 dp */ private int mDesignWidthInDp; /** * 设计图上的总高度, 单位 dp */ private int mDesignHeightInDp; /** * 设备的屏幕总宽度, 单位 px */ private int mScreenWidth; /** * 设备的屏幕总高度, 单位 px, 如果 {@link #isUseDeviceSize} 为 {@code false}, 屏幕总高度会减去状态栏的高度 */ private int mScreenHeight; /** * 状态栏高度, 当 {@link #isUseDeviceSize} 为 {@code false} 时, AndroidAutoSize 会将 {@link #mScreenHeight} 减去状态栏高度 * AndroidAutoSize 默认使用 {@link ScreenUtils#getStatusBarHeight()} 方法获取状态栏高度 * AndroidAutoSize 使用者可使用 {@link #setStatusBarHeight(int)} 自行设置状态栏高度 */ private int mStatusBarHeight; /** * 为了保证在不同高宽比的屏幕上显示效果也能完全一致, 所以本方案适配时是以设计图宽度与设备实际宽度的比例或设计图高度与设备实际高度的比例应用到 * 每个 View 上 (只能在宽度和高度之中选一个作为基准), 从而使每个 View 的高和宽用同样的比例缩放, 避免在与设计图高宽比不一致的设备上出现适配的 View 高或宽变形的问题 * {@link #isBaseOnWidth} 为 {@code true} 时代表以宽度等比例缩放, {@code false} 代表以高度等比例缩放 * {@link #isBaseOnWidth} 为全局配置, 默认为 {@code true}, 每个 {@link Activity} 也可以单独选择使用高或者宽做等比例缩放 */ private boolean isBaseOnWidth = true; /** * 此字段表示是否使用设备的实际尺寸做适配 * {@link #isUseDeviceSize} 为 {@code true} 表示屏幕高度 {@link #mScreenHeight} 包含状态栏的高度 * {@link #isUseDeviceSize} 为 {@code false} 表示 {@link #mScreenHeight} 会减去状态栏的高度, 默认为 {@code true} */ private boolean isUseDeviceSize = true; /** * {@link #mActivityLifecycleCallbacks} 可用来代替在 BaseActivity 中加入适配代码的传统方式 * {@link #mActivityLifecycleCallbacks} 这种方案类似于 AOP, 面向接口, 侵入性低, 方便统一管理, 扩展性强, 并且也支持适配三方库的 {@link Activity} */ private ActivityLifecycleCallbacksImpl mActivityLifecycleCallbacks; /** * 框架具有 热插拔 特性, 支持在项目运行中动态停止和重新启动适配功能 * * @see #stop(Activity) * @see #restart() */ private boolean isStop; /** * 是否让框架支持自定义 Fragment 的适配参数, 由于这个需求是比较少见的, 所以须要使用者手动开启 */ private boolean isCustomFragment; /** * 屏幕方向, {@code true} 为纵向, {@code false} 为横向 */ private boolean isVertical; /** * 是否屏蔽系统字体大小对 AndroidAutoSize 的影响, 如果为 {@code true}, App 内的字体的大小将不会跟随系统设置中字体大小的改变 * 如果为 {@code false}, 则会跟随系统设置中字体大小的改变, 默认为 {@code false} */ private boolean isExcludeFontScale; /** * 区别于系统字体大小的放大比例, AndroidAutoSize 允许 APP 内部可以独立于系统字体大小之外,独自拥有全局调节 APP 字体大小的能力 * 当然, 在 APP 内您必须使用 sp 来作为字体的单位, 否则此功能无效, 将此值设为 0 则取消此功能 */ private float privateFontScale; /** * 是否是 Miui 系统 */ private boolean isMiui; /** * Miui 系统中的 mTmpMetrics 字段 */ private Field mTmpMetricsField; /** * 屏幕适配监听器,用于监听屏幕适配时的一些事件 */ private onAdaptListener mOnAdaptListener; static { DEPENDENCY_ANDROIDX = findClassByClassName("androidx.fragment.app.FragmentActivity"); DEPENDENCY_SUPPORT = findClassByClassName("android.support.v4.app.FragmentActivity"); } private static boolean findClassByClassName(String className) { boolean hasDependency; try { Class.forName(className); hasDependency = true; } catch (ClassNotFoundException e) { hasDependency = false; } return hasDependency; } public static AutoSizeConfig getInstance() { if (sInstance == null) { synchronized (AutoSizeConfig.class) { if (sInstance == null) { sInstance = new AutoSizeConfig(); } } } return sInstance; } private AutoSizeConfig() { } public Application getApplication() { Preconditions.checkNotNull(mApplication, "Please call the AutoSizeConfig#init() first"); return mApplication; } /** * v0.7.0 以后, 框架会在 APP 启动时自动调用此方法进行初始化, 使用者无需手动初始化, 初始化方法只能调用一次, 否则报错 * 此方法默认使用以宽度进行等比例适配, 如想使用以高度进行等比例适配, 请调用 {@link #init(Application, boolean)} * * @param application {@link Application} */ AutoSizeConfig init(Application application) { return init(application, true, null); } /** * v0.7.0 以后, 框架会在 APP 启动时自动调用此方法进行初始化, 使用者无需手动初始化, 初始化方法只能调用一次, 否则报错 * 此方法使用默认的 {@link AutoAdaptStrategy} 策略, 如想使用自定义的 {@link AutoAdaptStrategy} 策略 * 请调用 {@link #init(Application, boolean, AutoAdaptStrategy)} * * @param application {@link Application} * @param isBaseOnWidth 详情请查看 {@link #isBaseOnWidth} 的注释 */ AutoSizeConfig init(Application application, boolean isBaseOnWidth) { return init(application, isBaseOnWidth, null); } /** * v0.7.0 以后, 框架会在 APP 启动时自动调用此方法进行初始化, 使用者无需手动初始化, 初始化方法只能调用一次, 否则报错 * * @param application {@link Application} * @param isBaseOnWidth 详情请查看 {@link #isBaseOnWidth} 的注释 * @param strategy {@link AutoAdaptStrategy}, 传 {@code null} 则使用 {@link DefaultAutoAdaptStrategy} */ AutoSizeConfig init(final Application application, boolean isBaseOnWidth, AutoAdaptStrategy strategy) { Preconditions.checkArgument(mInitDensity == -1, "AutoSizeConfig#init() can only be called once"); Preconditions.checkNotNull(application, "application == null"); this.mApplication = application; this.isBaseOnWidth = isBaseOnWidth; final DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics(); final Configuration configuration = Resources.getSystem().getConfiguration(); //设置一个默认值, 避免在低配设备上因为获取 MetaData 过慢, 导致适配时未能正常获取到设计图尺寸 //建议使用者在低配设备上主动在 Application#onCreate 中调用 setDesignWidthInDp 替代以使用 AndroidManifest 配置设计图尺寸的方式 if (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits() == Subunits.NONE) { mDesignWidthInDp = 360; mDesignHeightInDp = 640; } else { mDesignWidthInDp = 1080; mDesignHeightInDp = 1920; } getMetaData(application); isVertical = application.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; int[] screenSize = ScreenUtils.getScreenSize(application); mScreenWidth = screenSize[0]; mScreenHeight = screenSize[1]; mStatusBarHeight = ScreenUtils.getStatusBarHeight(); AutoSizeLog.d("designWidthInDp = " + mDesignWidthInDp + ", designHeightInDp = " + mDesignHeightInDp + ", screenWidth = " + mScreenWidth + ", screenHeight = " + mScreenHeight); mInitDensity = displayMetrics.density; mInitDensityDpi = displayMetrics.densityDpi; mInitScaledDensity = displayMetrics.scaledDensity; mInitXdpi = displayMetrics.xdpi; mInitScreenWidthDp = configuration.screenWidthDp; mInitScreenHeightDp = configuration.screenHeightDp; application.registerComponentCallbacks(new ComponentCallbacks() { @Override public void onConfigurationChanged(Configuration newConfig) { if (newConfig != null) { if (newConfig.fontScale > 0) { mInitScaledDensity = Resources.getSystem().getDisplayMetrics().scaledDensity; AutoSizeLog.d("initScaledDensity = " + mInitScaledDensity + " on ConfigurationChanged"); } isVertical = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT; int[] screenSize = ScreenUtils.getScreenSize(application); mScreenWidth = screenSize[0]; mScreenHeight = screenSize[1]; } } @Override public void onLowMemory() { } }); AutoSizeLog.d("initDensity = " + mInitDensity + ", initScaledDensity = " + mInitScaledDensity); mActivityLifecycleCallbacks = new ActivityLifecycleCallbacksImpl(new WrapperAutoAdaptStrategy(strategy == null ? new DefaultAutoAdaptStrategy() : strategy)); application.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks); if ("MiuiResources".equals(application.getResources().getClass().getSimpleName()) || "XResources".equals(application.getResources().getClass().getSimpleName())) { isMiui = true; try { mTmpMetricsField = Resources.class.getDeclaredField("mTmpMetrics"); mTmpMetricsField.setAccessible(true); } catch (Exception e) { mTmpMetricsField = null; } } return this; } /** * 重新开始框架的运行 * 框架具有 热插拔 特性, 支持在项目运行中动态停止和重新启动适配功能 */ public void restart() { Preconditions.checkNotNull(mActivityLifecycleCallbacks, "Please call the AutoSizeConfig#init() first"); synchronized (AutoSizeConfig.class) { if (isStop) { mApplication.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks); isStop = false; } } } /** * 停止框架的运行 * 框架具有 热插拔 特性, 支持在项目运行中动态停止和重新启动适配功能 */ public void stop(Activity activity) { Preconditions.checkNotNull(mActivityLifecycleCallbacks, "Please call the AutoSizeConfig#init() first"); synchronized (AutoSizeConfig.class) { if (!isStop) { mApplication.unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks); AutoSize.cancelAdapt(activity); isStop = true; } } } /** * 设置屏幕适配逻辑策略类 * * @param autoAdaptStrategy {@link AutoAdaptStrategy} */ public AutoSizeConfig setAutoAdaptStrategy(AutoAdaptStrategy autoAdaptStrategy) { Preconditions.checkNotNull(autoAdaptStrategy, "autoAdaptStrategy == null"); Preconditions.checkNotNull(mActivityLifecycleCallbacks, "Please call the AutoSizeConfig#init() first"); mActivityLifecycleCallbacks.setAutoAdaptStrategy(new WrapperAutoAdaptStrategy(autoAdaptStrategy)); return this; } /** * 设置屏幕适配监听器 * * @param onAdaptListener {@link onAdaptListener} */ public AutoSizeConfig setOnAdaptListener(onAdaptListener onAdaptListener) { Preconditions.checkNotNull(onAdaptListener, "onAdaptListener == null"); mOnAdaptListener = onAdaptListener; return this; } /** * 是否全局按照宽度进行等比例适配 * * @param baseOnWidth {@code true} 为按照宽度, {@code false} 为按照高度 * @see #isBaseOnWidth 详情请查看这个字段的注释 */ public AutoSizeConfig setBaseOnWidth(boolean baseOnWidth) { isBaseOnWidth = baseOnWidth; return this; } /** * 是否使用设备的实际尺寸做适配 * * @param useDeviceSize {@code true} 为使用设备的实际尺寸 (包含状态栏), {@code false} 为不使用设备的实际尺寸 (不包含状态栏) * @see #isUseDeviceSize 详情请查看这个字段的注释 */ public AutoSizeConfig setUseDeviceSize(boolean useDeviceSize) { isUseDeviceSize = useDeviceSize; return this; } /** * 是否打印 Log * * @param log {@code true} 为打印 */ public AutoSizeConfig setLog(boolean log) { AutoSizeLog.setDebug(log); return this; } /** * 是否让框架支持自定义 Fragment 的适配参数, 由于这个需求是比较少见的, 所以须要使用者手动开启 * * @param customFragment {@code true} 为支持 */ public AutoSizeConfig setCustomFragment(boolean customFragment) { isCustomFragment = customFragment; return this; } /** * 框架是否已经开启支持自定义 Fragment 的适配参数 * * @return {@code true} 为支持 */ public boolean isCustomFragment() { return isCustomFragment; } /** * 框架是否已经停止运行 * * @return {@code false} 框架正在运行, {@code true} 框架已经停止运行 */ public boolean isStop() { return isStop; } /** * {@link ExternalAdaptManager} 用来管理外部三方库 {@link Activity} 的适配 * * @return {@link #mExternalAdaptManager} */ public ExternalAdaptManager getExternalAdaptManager() { return mExternalAdaptManager; } /** * {@link UnitsManager} 用来管理 AndroidAutoSize 支持的所有单位, AndroidAutoSize 支持五种单位 (dp、sp、pt、in、mm) * * @return {@link #mUnitsManager} */ public UnitsManager getUnitsManager() { return mUnitsManager; } /** * 返回 {@link #mOnAdaptListener} * * @return {@link #mOnAdaptListener} */ public onAdaptListener getOnAdaptListener() { return mOnAdaptListener; } /** * 返回 {@link #isBaseOnWidth} * * @return {@link #isBaseOnWidth} */ public boolean isBaseOnWidth() { return isBaseOnWidth; } /** * 返回 {@link #isUseDeviceSize} * * @return {@link #isUseDeviceSize} */ public boolean isUseDeviceSize() { return isUseDeviceSize; } /** * 返回 {@link #mScreenWidth} * * @return {@link #mScreenWidth} */ public int getScreenWidth() { return mScreenWidth; } /** * 返回 {@link #mScreenHeight} * * @return {@link #mScreenHeight} */ public int getScreenHeight() { return isUseDeviceSize() ? mScreenHeight : mScreenHeight - mStatusBarHeight; } /** * 获取 {@link #mDesignWidthInDp} * * @return {@link #mDesignWidthInDp} */ public int getDesignWidthInDp() { Preconditions.checkArgument(mDesignWidthInDp > 0, "you must set " + KEY_DESIGN_WIDTH_IN_DP + " in your AndroidManifest file"); return mDesignWidthInDp; } /** * 获取 {@link #mDesignHeightInDp} * * @return {@link #mDesignHeightInDp} */ public int getDesignHeightInDp() { Preconditions.checkArgument(mDesignHeightInDp > 0, "you must set " + KEY_DESIGN_HEIGHT_IN_DP + " in your AndroidManifest file"); return mDesignHeightInDp; } /** * 获取 {@link #mInitDensity} * * @return {@link #mInitDensity} */ public float getInitDensity() { return mInitDensity; } /** * 获取 {@link #mInitDensityDpi} * * @return {@link #mInitDensityDpi} */ public int getInitDensityDpi() { return mInitDensityDpi; } /** * 获取 {@link #mInitScaledDensity} * * @return {@link #mInitScaledDensity} */ public float getInitScaledDensity() { return mInitScaledDensity; } /** * 获取 {@link #mInitXdpi} * * @return {@link #mInitXdpi} */ public float getInitXdpi() { return mInitXdpi; } /** * 获取 {@link #mInitScreenWidthDp} * * @return {@link #mInitScreenWidthDp} */ public int getInitScreenWidthDp() { return mInitScreenWidthDp; } /** * 获取 {@link #mInitScreenHeightDp} * * @return {@link #mInitScreenHeightDp} */ public int getInitScreenHeightDp() { return mInitScreenHeightDp; } /** * 获取屏幕方向 * * @return {@code true} 为纵向, {@code false} 为横向 */ public boolean isVertical() { return isVertical; } /** * 返回 {@link #isMiui} * * @return {@link #isMiui} */ public boolean isMiui() { return isMiui; } /** * 返回 {@link #mTmpMetricsField} * * @return {@link #mTmpMetricsField} */ public Field getTmpMetricsField() { return mTmpMetricsField; } /** * 设置屏幕方向 * * @param vertical {@code true} 为纵向, {@code false} 为横向 */ public AutoSizeConfig setVertical(boolean vertical) { isVertical = vertical; return this; } /** * 是否屏蔽系统字体大小对 AndroidAutoSize 的影响, 如果为 {@code true}, App 内的字体的大小将不会跟随系统设置中字体大小的改变 * 如果为 {@code false}, 则会跟随系统设置中字体大小的改变, 默认为 {@code false} * * @return {@link #isExcludeFontScale} */ public boolean isExcludeFontScale() { return isExcludeFontScale; } /** * 是否屏蔽系统字体大小对 AndroidAutoSize 的影响, 如果为 {@code true}, App 内的字体的大小将不会跟随系统设置中字体大小的改变 * 如果为 {@code false}, 则会跟随系统设置中字体大小的改变, 默认为 {@code false} * * @param excludeFontScale 是否屏蔽 */ public AutoSizeConfig setExcludeFontScale(boolean excludeFontScale) { isExcludeFontScale = excludeFontScale; return this; } /** * 区别于系统字体大小的放大比例, AndroidAutoSize 允许 APP 内部可以独立于系统字体大小之外,独自拥有全局调节 APP 字体大小的能力 * 当然, 在 APP 内您必须使用 sp 来作为字体的单位, 否则此功能无效 * * @param fontScale 字体大小放大的比例, 设为 0 则取消此功能 */ public AutoSizeConfig setPrivateFontScale(float fontScale) { privateFontScale = fontScale; return this; } /** * 区别于系统字体大小的放大比例, AndroidAutoSize 允许 APP 内部可以独立于系统字体大小之外,独自拥有全局调节 APP 字体大小的能力 * 当然, 在 APP 内您必须使用 sp 来作为字体的单位, 否则此功能无效 * * @return 私有的字体大小放大比例 */ public float getPrivateFontScale() { return privateFontScale; } /** * 设置屏幕宽度 * * @param screenWidth 屏幕宽度 */ public AutoSizeConfig setScreenWidth(int screenWidth) { Preconditions.checkArgument(screenWidth > 0, "screenWidth must be > 0"); mScreenWidth = screenWidth; return this; } /** * 设置屏幕高度 * * @param screenHeight 屏幕高度 (需要包含状态栏) */ public AutoSizeConfig setScreenHeight(int screenHeight) { Preconditions.checkArgument(screenHeight > 0, "screenHeight must be > 0"); mScreenHeight = screenHeight; return this; } /** * 设置全局设计图宽度 * * @param designWidthInDp 设计图宽度 */ public AutoSizeConfig setDesignWidthInDp(int designWidthInDp) { Preconditions.checkArgument(designWidthInDp > 0, "designWidthInDp must be > 0"); mDesignWidthInDp = designWidthInDp; return this; } /** * 设置全局设计图高度 * * @param designHeightInDp 设计图高度 */ public AutoSizeConfig setDesignHeightInDp(int designHeightInDp) { Preconditions.checkArgument(designHeightInDp > 0, "designHeightInDp must be > 0"); mDesignHeightInDp = designHeightInDp; return this; } /** * 设置状态栏高度 * * @param statusBarHeight 状态栏高度 */ public AutoSizeConfig setStatusBarHeight(int statusBarHeight) { Preconditions.checkArgument(statusBarHeight > 0, "statusBarHeight must be > 0"); mStatusBarHeight = statusBarHeight; return this; } /** * 获取使用者在 AndroidManifest 中填写的 Meta 信息 *

* Example usage: *

     * 
     * 
     * 
* * @param context {@link Context} */ private void getMetaData(final Context context) { new Thread(new Runnable() { @Override public void run() { PackageManager packageManager = context.getPackageManager(); ApplicationInfo applicationInfo; try { applicationInfo = packageManager.getApplicationInfo(context .getPackageName(), PackageManager.GET_META_DATA); if (applicationInfo != null && applicationInfo.metaData != null) { if (applicationInfo.metaData.containsKey(KEY_DESIGN_WIDTH_IN_DP)) { mDesignWidthInDp = (int) applicationInfo.metaData.get(KEY_DESIGN_WIDTH_IN_DP); } if (applicationInfo.metaData.containsKey(KEY_DESIGN_HEIGHT_IN_DP)) { mDesignHeightInDp = (int) applicationInfo.metaData.get(KEY_DESIGN_HEIGHT_IN_DP); } } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } }).start(); } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/DefaultAutoAdaptStrategy.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.app.Activity; import android.app.Application; import java.util.Locale; import me.jessyan.autosize.external.ExternalAdaptInfo; import me.jessyan.autosize.internal.CancelAdapt; import me.jessyan.autosize.internal.CustomAdapt; import me.jessyan.autosize.utils.AutoSizeLog; /** * ================================================ * 屏幕适配逻辑策略默认实现类, 可通过 {@link AutoSizeConfig#init(Application, boolean, AutoAdaptStrategy)} * 和 {@link AutoSizeConfig#setAutoAdaptStrategy(AutoAdaptStrategy)} 切换策略 * * @see AutoAdaptStrategy * Created by JessYan on 2018/8/9 15:57 * Contact me * Follow me * ================================================ */ public class DefaultAutoAdaptStrategy implements AutoAdaptStrategy { @Override public void applyAdapt(Object target, Activity activity) { //检查是否开启了外部三方库的适配模式, 只要不主动调用 ExternalAdaptManager 的方法, 下面的代码就不会执行 if (AutoSizeConfig.getInstance().getExternalAdaptManager().isRun()) { if (AutoSizeConfig.getInstance().getExternalAdaptManager().isCancelAdapt(target.getClass())) { AutoSizeLog.w(String.format(Locale.ENGLISH, "%s canceled the adaptation!", target.getClass().getName())); AutoSize.cancelAdapt(activity); return; } else { ExternalAdaptInfo info = AutoSizeConfig.getInstance().getExternalAdaptManager() .getExternalAdaptInfoOfActivity(target.getClass()); if (info != null) { AutoSizeLog.d(String.format(Locale.ENGLISH, "%s used %s for adaptation!", target.getClass().getName(), ExternalAdaptInfo.class.getName())); AutoSize.autoConvertDensityOfExternalAdaptInfo(activity, info); return; } } } //如果 target 实现 CancelAdapt 接口表示放弃适配, 所有的适配效果都将失效 if (target instanceof CancelAdapt) { AutoSizeLog.w(String.format(Locale.ENGLISH, "%s canceled the adaptation!", target.getClass().getName())); AutoSize.cancelAdapt(activity); return; } //如果 target 实现 CustomAdapt 接口表示该 target 想自定义一些用于适配的参数, 从而改变最终的适配效果 if (target instanceof CustomAdapt) { AutoSizeLog.d(String.format(Locale.ENGLISH, "%s implemented by %s!", target.getClass().getName(), CustomAdapt.class.getName())); AutoSize.autoConvertDensityOfCustomAdapt(activity, (CustomAdapt) target); } else { AutoSizeLog.d(String.format(Locale.ENGLISH, "%s used the global configuration.", target.getClass().getName())); AutoSize.autoConvertDensityOfGlobal(activity); } } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/DisplayMetricsInfo.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.os.Parcel; import android.os.Parcelable; import android.util.DisplayMetrics; /** * ================================================ * {@link DisplayMetrics} 封装类 *

* Created by JessYan on 2018/8/11 16:42 * Contact me * Follow me * ================================================ */ public class DisplayMetricsInfo implements Parcelable { private float density; private int densityDpi; private float scaledDensity; private float xdpi; private int screenWidthDp; private int screenHeightDp; public DisplayMetricsInfo(float density, int densityDpi, float scaledDensity, float xdpi) { this.density = density; this.densityDpi = densityDpi; this.scaledDensity = scaledDensity; this.xdpi = xdpi; } public DisplayMetricsInfo(float density, int densityDpi, float scaledDensity, float xdpi, int screenWidthDp, int screenHeightDp) { this.density = density; this.densityDpi = densityDpi; this.scaledDensity = scaledDensity; this.xdpi = xdpi; this.screenWidthDp = screenWidthDp; this.screenHeightDp = screenHeightDp; } public float getDensity() { return density; } public void setDensity(float density) { this.density = density; } public int getDensityDpi() { return densityDpi; } public void setDensityDpi(int densityDpi) { this.densityDpi = densityDpi; } public float getScaledDensity() { return scaledDensity; } public void setScaledDensity(float scaledDensity) { this.scaledDensity = scaledDensity; } public float getXdpi() { return xdpi; } public void setXdpi(float xdpi) { this.xdpi = xdpi; } public int getScreenWidthDp() { return screenWidthDp; } public void setScreenWidthDp(int screenWidthDp) { this.screenWidthDp = screenWidthDp; } public int getScreenHeightDp() { return screenHeightDp; } public void setScreenHeightDp(int screenHeightDp) { this.screenHeightDp = screenHeightDp; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeFloat(this.density); dest.writeInt(this.densityDpi); dest.writeFloat(this.scaledDensity); dest.writeFloat(this.xdpi); dest.writeInt(this.screenWidthDp); dest.writeInt(this.screenHeightDp); } protected DisplayMetricsInfo(Parcel in) { this.density = in.readFloat(); this.densityDpi = in.readInt(); this.scaledDensity = in.readFloat(); this.xdpi = in.readFloat(); this.screenWidthDp = in.readInt(); this.screenHeightDp = in.readInt(); } public static final Creator CREATOR = new Creator() { @Override public DisplayMetricsInfo createFromParcel(Parcel source) { return new DisplayMetricsInfo(source); } @Override public DisplayMetricsInfo[] newArray(int size) { return new DisplayMetricsInfo[size]; } }; @Override public String toString() { return "DisplayMetricsInfo{" + "density=" + density + ", densityDpi=" + densityDpi + ", scaledDensity=" + scaledDensity + ", xdpi=" + xdpi + ", screenWidthDp=" + screenWidthDp + ", screenHeightDp=" + screenHeightDp + '}'; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/FragmentLifecycleCallbacksImpl.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; /** * ================================================ * {@link FragmentLifecycleCallbacksImpl} 可用来代替在 BaseFragment 中加入适配代码的传统方式 * {@link FragmentLifecycleCallbacksImpl} 这种方案类似于 AOP, 面向接口, 侵入性低, 方便统一管理, 扩展性强, 并且也支持适配三方库的 {@link Fragment} *

* Created by JessYan on 2018/8/25 13:52 * Contact me * Follow me * ================================================ */ public class FragmentLifecycleCallbacksImpl extends FragmentManager.FragmentLifecycleCallbacks { /** * 屏幕适配逻辑策略类 */ private AutoAdaptStrategy mAutoAdaptStrategy; public FragmentLifecycleCallbacksImpl(AutoAdaptStrategy autoAdaptStrategy) { mAutoAdaptStrategy = autoAdaptStrategy; } @Override public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) { if (mAutoAdaptStrategy != null) { mAutoAdaptStrategy.applyAdapt(f, f.getActivity()); } } /** * 设置屏幕适配逻辑策略类 * * @param autoAdaptStrategy {@link AutoAdaptStrategy} */ public void setAutoAdaptStrategy(AutoAdaptStrategy autoAdaptStrategy) { mAutoAdaptStrategy = autoAdaptStrategy; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/FragmentLifecycleCallbacksImplToAndroidx.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.os.Bundle; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; /** * ================================================ * {@link FragmentLifecycleCallbacksImplToAndroidx} 可用来代替在 BaseFragment 中加入适配代码的传统方式 * {@link FragmentLifecycleCallbacksImplToAndroidx} 这种方案类似于 AOP, 面向接口, 侵入性低, 方便统一管理, 扩展性强, 并且也支持适配三方库的 {@link Fragment} *

* Created by JessYan on 2018/8/25 13:52 * Contact me * Follow me * ================================================ */ public class FragmentLifecycleCallbacksImplToAndroidx extends FragmentManager.FragmentLifecycleCallbacks { /** * 屏幕适配逻辑策略类 */ private AutoAdaptStrategy mAutoAdaptStrategy; public FragmentLifecycleCallbacksImplToAndroidx(AutoAdaptStrategy autoAdaptStrategy) { mAutoAdaptStrategy = autoAdaptStrategy; } @Override public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) { if (mAutoAdaptStrategy != null) { mAutoAdaptStrategy.applyAdapt(f, f.getActivity()); } } /** * 设置屏幕适配逻辑策略类 * * @param autoAdaptStrategy {@link AutoAdaptStrategy} */ public void setAutoAdaptStrategy(AutoAdaptStrategy autoAdaptStrategy) { mAutoAdaptStrategy = autoAdaptStrategy; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/InitProvider.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.content.Context; import android.app.Application; import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import me.jessyan.autosize.utils.AutoSizeUtils; /** * ================================================ * 通过声明 {@link ContentProvider} 自动完成初始化 * Created by JessYan on 2018/8/19 11:55 * Contact me * Follow me * ================================================ */ public class InitProvider extends ContentProvider { @Override public boolean onCreate() { Context application = getContext().getApplicationContext(); if (application == null) { application = AutoSizeUtils.getApplicationByReflect(); } AutoSizeConfig.getInstance() .setLog(true) .init((Application) application) .setUseDeviceSize(false); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/WrapperAutoAdaptStrategy.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.app.Activity; /** * ================================================ * {@link AutoAdaptStrategy} 的包装者, 用于给 {@link AutoAdaptStrategy} 的实现类增加一些额外的职责 *

* Created by JessYan on 2018/10/30 15:07 * Contact me * Follow me * ================================================ */ public class WrapperAutoAdaptStrategy implements AutoAdaptStrategy { private final AutoAdaptStrategy mAutoAdaptStrategy; public WrapperAutoAdaptStrategy(AutoAdaptStrategy autoAdaptStrategy) { mAutoAdaptStrategy = autoAdaptStrategy; } @Override public void applyAdapt(Object target, Activity activity) { onAdaptListener onAdaptListener = AutoSizeConfig.getInstance().getOnAdaptListener(); if (onAdaptListener != null){ onAdaptListener.onAdaptBefore(target, activity); } if (mAutoAdaptStrategy != null) { mAutoAdaptStrategy.applyAdapt(target, activity); } if (onAdaptListener != null){ onAdaptListener.onAdaptAfter(target, activity); } } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/external/ExternalAdaptInfo.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.external; import android.app.Activity; import android.os.Parcel; import android.os.Parcelable; /** * ================================================ * {@link ExternalAdaptInfo} 用来存储外部三方库的适配参数, 因为 AndroidAutoSize 默认会对项目中的所有模块都进行适配 * 三方库的 {@link Activity} 也不例外, 但三方库的适配参数可能和自己项目中的适配参数不一致, 导致三方库的适配效果和理想的效果差别很大 * 所以需要向 AndroidAutoSize 提供三方库的适配参数, 已完成对三方库的屏幕适配 *

* Created by JessYan on 2018/8/9 18:19 * Contact me * Follow me * ================================================ */ public class ExternalAdaptInfo implements Parcelable { /** * 是否按照宽度进行等比例适配 (为了保证在高宽比不同的屏幕上也能正常适配, 所以只能在宽度和高度之中选一个作为基准进行适配) * {@code true} 为按照宽度适配, {@code false} 为按照高度适配 */ private boolean isBaseOnWidth; /** * 设计图上的设计尺寸, 单位 dp (三方库页面的设计图尺寸可能无法获知, 所以如果想让三方库的适配效果达到最好, 只有靠不断的尝试) * {@link #sizeInDp} 须配合 {@link #isBaseOnWidth} 使用, 规则如下: * 如果 {@link #isBaseOnWidth} 设置为 {@code true}, {@link #sizeInDp} 则应该设置为设计图的总宽度 * 如果 {@link #isBaseOnWidth} 设置为 {@code false}, {@link #sizeInDp} 则应该设置为设计图的总高度 * 如果您不需要自定义设计图上的设计尺寸, 想继续使用在 AndroidManifest 中填写的设计图尺寸, {@link #sizeInDp} 则设置为 {@code 0} */ private float sizeInDp; public ExternalAdaptInfo(boolean isBaseOnWidth) { this.isBaseOnWidth = isBaseOnWidth; } public ExternalAdaptInfo(boolean isBaseOnWidth, float sizeInDp) { this.isBaseOnWidth = isBaseOnWidth; this.sizeInDp = sizeInDp; } public boolean isBaseOnWidth() { return isBaseOnWidth; } public void setBaseOnWidth(boolean baseOnWidth) { isBaseOnWidth = baseOnWidth; } public float getSizeInDp() { return sizeInDp; } public void setSizeInDp(float sizeInDp) { this.sizeInDp = sizeInDp; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeByte(this.isBaseOnWidth ? (byte) 1 : (byte) 0); dest.writeFloat(this.sizeInDp); } protected ExternalAdaptInfo(Parcel in) { this.isBaseOnWidth = in.readByte() != 0; this.sizeInDp = in.readFloat(); } public static final Creator CREATOR = new Creator() { @Override public ExternalAdaptInfo createFromParcel(Parcel source) { return new ExternalAdaptInfo(source); } @Override public ExternalAdaptInfo[] newArray(int size) { return new ExternalAdaptInfo[size]; } }; @Override public String toString() { return "ExternalAdaptInfo{" + "isBaseOnWidth=" + isBaseOnWidth + ", sizeInDp=" + sizeInDp + '}'; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/external/ExternalAdaptManager.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.external; import android.app.Activity; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import me.jessyan.autosize.AutoSizeConfig; import me.jessyan.autosize.utils.Preconditions; /** * ================================================ * 管理三方库的适配信息和状态, 通过 {@link AutoSizeConfig#getExternalAdaptManager()} 获取, 切勿自己 new * AndroidAutoSize 通过实现接口的方式来让每个 {@link Activity} 都具有自定义适配参数的功能, 从而让每个 {@link Activity} 都可以自定义适配效果 * 但通过远程依赖的三方库并不能修改源码, 所以也不能让三方库的 {@link Activity} 实现接口, 实现接口的方式就显得无能为力 * {@link ExternalAdaptManager} 就是专门用来处理这个问题, 项目初始化时把对应的三方库 {@link Activity} 传入 {@link ExternalAdaptManager} 即可 *

* Created by JessYan on 2018/8/10 14:40 * Contact me * Follow me * ================================================ */ public class ExternalAdaptManager { private List mCancelAdaptList; private Map mExternalAdaptInfos; private boolean isRun; /** * 将不需要适配的第三方库 {@link Activity} 添加进来 (但不局限于三方库), 即可让该 {@link Activity} 的适配效果失效 *

* 支持链式调用, 如: * {@link ExternalAdaptManager#addCancelAdaptOfActivity(Class)#addCancelAdaptOfActivity(Class)} * * @param targetClass {@link Activity} class, Fragment class */ public synchronized ExternalAdaptManager addCancelAdaptOfActivity(Class targetClass) { Preconditions.checkNotNull(targetClass, "targetClass == null"); if (!isRun) { isRun = true; } if (mCancelAdaptList == null) { mCancelAdaptList = new ArrayList<>(); } mCancelAdaptList.add(targetClass.getCanonicalName()); return this; } /** * 将需要提供自定义适配参数的三方库 {@link Activity} 添加进来 (但不局限于三方库), 即可让该 {@link Activity} 根据自己提供的适配参数进行适配 * 默认的全局适配参数不能满足您时可以使用此方法 *

* 一般用于三方库的 Activity, 因为三方库的设计图尺寸可能和项目自身的设计图尺寸不一致, 所以要想完美适配三方库的页面 * 就需要提供三方库的设计图尺寸, 以及适配的方向 (以宽为基准还是高为基准?) * 三方库页面的设计图尺寸可能无法获知, 所以如果想让三方库的适配效果达到最好, 只有靠不断的尝试 * 由于 AndroidAutoSize 可以让布局在所有设备上都等比例缩放, 所以只要您在一个设备上测试出了一个最完美的设计图尺寸 * 那这个三方库页面在其他设备上也会呈现出同样的适配效果, 等比例缩放, 所以也就完成了三方库页面的屏幕适配 * 即使在不改三方库源码的情况下也可以完美适配三方库的页面, 这就是 AndroidAutoSize 的优势 * 但前提是三方库页面的布局使用的是 dp 和 sp, 如果布局全部使用的 px, 那 AndroidAutoSize 也将无能为力 *

* 支持链式调用, 如: * {@link ExternalAdaptManager#addExternalAdaptInfoOfActivity(Class, ExternalAdaptInfo)#addExternalAdaptInfoOfActivity(Class, ExternalAdaptInfo)} * * @param targetClass {@link Activity} class, Fragment class * @param info {@link ExternalAdaptInfo} 适配参数 */ public synchronized ExternalAdaptManager addExternalAdaptInfoOfActivity(Class targetClass, ExternalAdaptInfo info) { Preconditions.checkNotNull(targetClass, "targetClass == null"); if (!isRun) { isRun = true; } if (mExternalAdaptInfos == null) { mExternalAdaptInfos = new HashMap<>(16); } mExternalAdaptInfos.put(targetClass.getCanonicalName(), info); return this; } /** * 这个 {@link Activity} 是否存在在取消适配的列表中, 如果在, 则该 {@link Activity} 适配失效 * * @param targetClass {@link Activity} class, Fragment class * @return {@code true} 为存在, {@code false} 为不存在 */ public synchronized boolean isCancelAdapt(Class targetClass) { Preconditions.checkNotNull(targetClass, "targetClass == null"); if (mCancelAdaptList == null) { return false; } return mCancelAdaptList.contains(targetClass.getCanonicalName()); } /** * 这个 {@link Activity} 是否提供有自定义的适配参数, 如果有则使用此适配参数进行适配 * * @param targetClass {@link Activity} class, Fragment class * @return 如果返回 {@code null} 则说明该 {@link Activity} 没有提供自定义的适配参数 */ public synchronized ExternalAdaptInfo getExternalAdaptInfoOfActivity(Class targetClass) { Preconditions.checkNotNull(targetClass, "targetClass == null"); if (mExternalAdaptInfos == null) { return null; } return mExternalAdaptInfos.get(targetClass.getCanonicalName()); } /** * 此管理器是否已经启动 * * @return {@code true} 为已经启动, {@code false} 为没有启动 */ public boolean isRun() { return isRun; } /** * 设置管理器的运行状态 * * @param run {@code true} 为让管理器启动运行, {@code false} 为让管理器停止运行 */ public ExternalAdaptManager setRun(boolean run) { isRun = run; return this; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/internal/CancelAdapt.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.internal; import android.app.Activity; /** * ================================================ * AndroidAutoSize 默认项目中的所有模块都使用适配功能, 三方库的 {@link Activity} 也不例外 * 如果某个页面不想使用适配功能, 请让该页面 {@link Activity} 实现此接口 * 实现此接口表示放弃适配, 所有的适配效果都将失效 *

* Created by JessYan on 2018/8/9 09:54 * Contact me * Follow me * ================================================ */ public interface CancelAdapt { } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/internal/CustomAdapt.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.internal; import android.app.Activity; /** * ================================================ * 如果某些页面不想使用 AndroidAutoSize 初始化时设置的默认适配参数, 请让该页面 {@link Activity} 实现此接口 * 实现此接口即可自定义用于适配的一些参数, 从而影响最终的适配效果 *

* Created by JessYan on 2018/8/9 10:25 * Contact me * Follow me * ================================================ */ public interface CustomAdapt { /** * 是否按照宽度进行等比例适配 (为了保证在高宽比不同的屏幕上也能正常适配, 所以只能在宽度和高度之中选一个作为基准进行适配) * * @return {@code true} 为按照宽度适配, {@code false} 为按照高度适配 */ boolean isBaseOnWidth(); /** * 返回设计图上的设计尺寸, 单位 dp * {@link #getSizeInDp} 须配合 {@link #isBaseOnWidth()} 使用, 规则如下: * 如果 {@link #isBaseOnWidth()} 返回 {@code true}, {@link #getSizeInDp} 则应该返回设计图的总宽度 * 如果 {@link #isBaseOnWidth()} 返回 {@code false}, {@link #getSizeInDp} 则应该返回设计图的总高度 * 如果您不需要自定义设计图上的设计尺寸, 想继续使用在 AndroidManifest 中填写的设计图尺寸, {@link #getSizeInDp} 则返回 {@code 0} * * @return 设计图上的设计尺寸, 单位 dp */ float getSizeInDp(); } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/onAdaptListener.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize; import android.app.Activity; /** * ================================================ * 屏幕适配监听器,用于监听屏幕适配时的一些事件 *

* Created by JessYan on 2018/10/30 16:29 * Contact me * Follow me * ================================================ */ public interface onAdaptListener { /** * 在屏幕适配前调用 * * @param target 需要屏幕适配的对象 (可能是 {@link Activity} 或者 Fragment) * @param activity 当前 {@link Activity} */ void onAdaptBefore(Object target, Activity activity); /** * 在屏幕适配后调用 * * @param target 需要屏幕适配的对象 (可能是 {@link Activity} 或者 Fragment) * @param activity 当前 {@link Activity} */ void onAdaptAfter(Object target, Activity activity); } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/unit/Subunits.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.unit; import android.util.DisplayMetrics; /** * ================================================ * AndroidAutoSize 支持一些在 Android 系统上比较少见的单位作为副单位, 用于规避修改 {@link DisplayMetrics#density} * 所造成的对于其他使用 dp 布局的系统控件或三方库控件的不良影响 *

* Created by JessYan on 2018/8/28 10:27 * Contact me * Follow me * ================================================ */ public enum Subunits { /** * 不使用副单位 */ NONE, /** * 单位 pt * * @see android.util.TypedValue#COMPLEX_UNIT_PT */ PT, /** * 单位 in * * @see android.util.TypedValue#COMPLEX_UNIT_IN */ IN, /** * 单位 mm * * @see android.util.TypedValue#COMPLEX_UNIT_MM */ MM } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/unit/UnitsManager.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.unit; import android.util.DisplayMetrics; import me.jessyan.autosize.utils.Preconditions; /** * ================================================ * 管理 AndroidAutoSize 支持的所有单位, AndroidAutoSize 支持五种单位 (dp、sp、pt、in、mm) * 其中 dp、sp 这两个是比较常见的单位, 作为 AndroidAutoSize 的主单位, 默认被 AndroidAutoSize 支持 * pt、in、mm 这三个是比较少见的单位, 只可以选择其中的一个, 作为 AndroidAutoSize 的副单位, 与 dp、sp 一起被 AndroidAutoSize 支持 * 副单位是用于规避修改 {@link DisplayMetrics#density} 所造成的对于其他使用 dp 布局的系统控件或三方库控件的不良影响 * 您选择什么单位, 就在 layout 文件中用什么单位布局 *

* 两个主单位和一个副单位, 可以随时使用下面的方法关闭和重新开启对它们的支持 * 如果您想完全规避修改 {@link DisplayMetrics#density} 所造成的对于其他使用 dp 布局的系统控件或三方库控件的不良影响 * 那请调用 {@link #setSupportDP}、{@link #setSupportSP} 都设置为 {@code false}, 停止对两个主单位的支持 (如果开启 sp, 对其他三方库控件影响不大, 也可以不关闭对 sp 的支持) * 并调用 {@link #setSupportSubunits} 从三个冷门单位中选择一个作为副单位 (三个单位的效果都是一样的, 按自己的喜好选择, 比如我就喜欢 mm, 翻译为中文是妹妹的意思) * 然后在 layout 文件中只使用这个副单位进行布局, 这样就可以完全规避修改 {@link DisplayMetrics#density} 所造成的问题 * 因为 dp、sp 这两个单位在其他系统控件或三方库控件中都非常常见, 但三个冷门单位却非常少见 *

* Created by JessYan on 2018/8/28 10:21 * Contact me * Follow me * ================================================ */ public class UnitsManager { /** * 设计图上的总宽度, 建议单位为 px, 当使用者想将旧项目从主单位过渡到副单位, 或从副单位过渡到主单位时使用 * 因为在使用主单位时, 建议在 AndroidManifest 中填写设计图的 dp 尺寸, 比如 360 * 640 * 而副单位有一个特性是可以直接在 AndroidManifest 中填写设计图的 px 尺寸, 比如 1080 * 1920 * 但在 AndroidManifest 中却只能填写一套设计图尺寸, 并且已经填写了主单位的设计图尺寸 * 所以当项目中同时存在副单位和主单位, 并且副单位的设计图尺寸与主单位的设计图尺寸不同时, 就需要在 {@link UnitsManager} 中保存副单位的设计图尺寸 */ private float mDesignWidth; /** * 设计图上的总高度, 建议单位为 px, 当使用者想将旧项目从主单位过渡到副单位, 或从副单位过渡到主单位时使用 * 因为在使用主单位时, 建议在 AndroidManifest 中填写设计图的 dp 尺寸, 比如 360 * 640 * 而副单位有一个特性是可以直接在 AndroidManifest 中填写设计图的 px 尺寸, 比如 1080 * 1920 * 但在 AndroidManifest 中却只能填写一套设计图尺寸, 并且已经填写了主单位的设计图尺寸 * 所以当项目中同时存在副单位和主单位, 并且副单位的设计图尺寸与主单位的设计图尺寸不同时, 就需要在 {@link UnitsManager} 中保存副单位的设计图尺寸 */ private float mDesignHeight; /** * 是否支持 dp 单位, 默认支持 */ private boolean isSupportDP = true; /** * 是否支持 sp 单位, 默认支持 */ private boolean isSupportSP = true; /** * 是否支持副单位, 以什么为副单位? 默认不支持 */ private Subunits mSupportSubunits = Subunits.NONE; /** * 是否支持 ScreenSizeDp 修改, 默认不支持 */ private boolean isSupportScreenSizeDP = false; /** * 设置设计图尺寸 * * @param designWidth 设计图上的总宽度, 建议单位为 px * @param designHeight 设计图上的总高度, 建议单位为 px * @return {@link UnitsManager} * @see #mDesignWidth 详情请查看这个字段的注释 * @see #mDesignHeight 详情请查看这个字段的注释 */ public UnitsManager setDesignSize(float designWidth, float designHeight) { setDesignWidth(designWidth); setDesignHeight(designHeight); return this; } /** * 返回 {@link #mDesignWidth} * * @return {@link #mDesignWidth} */ public float getDesignWidth() { return mDesignWidth; } /** * 设置设计图上的总宽度, 建议单位为 px * * @param designWidth 设计图上的总宽度, 建议单位为 px * @return {@link UnitsManager} * @see #mDesignWidth 详情请查看这个字段的注释 */ public UnitsManager setDesignWidth(float designWidth) { Preconditions.checkArgument(designWidth > 0, "designWidth must be > 0"); mDesignWidth = designWidth; return this; } /** * 返回 {@link #mDesignHeight} * * @return {@link #mDesignHeight} */ public float getDesignHeight() { return mDesignHeight; } /** * 设置设计图上的总高度, 建议单位为 px * * @param designHeight 设计图上的总高度, 建议单位为 px * @return {@link UnitsManager} * @see #mDesignHeight 详情请查看这个字段的注释 */ public UnitsManager setDesignHeight(float designHeight) { Preconditions.checkArgument(designHeight > 0, "designHeight must be > 0"); mDesignHeight = designHeight; return this; } /** * 是否支持 dp 单位, 默认支持, 详情请看类文件的注释 {@link UnitsManager} * * @return {@code true} 为支持, {@code false} 为不支持 */ public boolean isSupportDP() { return isSupportDP; } /** * 是否让 AndroidAutoSize 支持 dp 单位, 默认支持, 详情请看类文件的注释 {@link UnitsManager} * * @param supportDP {@code true} 为支持, {@code false} 为不支持 */ public UnitsManager setSupportDP(boolean supportDP) { isSupportDP = supportDP; return this; } /** * 是否支持 sp 单位, 默认支持, 详情请看类文件的注释 {@link UnitsManager} * * @return {@code true} 为支持, {@code false} 为不支持 */ public boolean isSupportSP() { return isSupportSP; } /** * 是否让 AndroidAutoSize 支持 sp 单位, 默认支持, 详情请看类文件的注释 {@link UnitsManager} * * @param supportSP {@code true} 为支持, {@code false} 为不支持 */ public UnitsManager setSupportSP(boolean supportSP) { isSupportSP = supportSP; return this; } /** * AndroidAutoSize 以什么单位为副单位, 默认为 {@link Subunits#NONE}, 即不支持副单位, 详情请看类文件的注释 {@link UnitsManager} * * @return {@link Subunits} */ public Subunits getSupportSubunits() { return mSupportSubunits; } /** * 是否支持 ScreenSizeDp 修改, 默认不支持, 详情请看类文件的注释 {@link UnitsManager} * * @return {@code true} 为支持, {@code false} 为不支持 */ public boolean isSupportScreenSizeDP() { return isSupportScreenSizeDP; } /** * 是否让 AndroidAutoSize 支持 ScreenSizeDp 修改, 默认不支持, 详情请看类文件的注释 {@link UnitsManager} * * @param supportScreenSizeDP {@code true} 为支持, {@code false} 为不支持 */ public UnitsManager setSupportScreenSizeDP(boolean supportScreenSizeDP) { isSupportScreenSizeDP = supportScreenSizeDP; return this; } /** * 让 AndroidAutoSize 以什么单位为副单位, 在 pt、in、mm 这三个冷门单位中选择一个即可, 三个效果都是一样的 * 按自己的喜好选择, 比如我就喜欢 mm, 翻译为中文是妹妹的意思 * 默认为 {@link Subunits#NONE}, 即不支持副单位, 详情请看类文件的注释 {@link UnitsManager} * * @param supportSubunits {@link Subunits} */ public UnitsManager setSupportSubunits(Subunits supportSubunits) { mSupportSubunits = Preconditions.checkNotNull(supportSubunits, "The supportSubunits can not be null, use Subunits.NONE instead"); return this; } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/utils/AutoSizeLog.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.utils; import android.util.Log; /** * ================================================ * Created by JessYan on 2018/8/8 18:48 * Contact me * Follow me * ================================================ */ public class AutoSizeLog { private static final String TAG = "AndroidAutoSize"; private static boolean debug; private AutoSizeLog() { throw new IllegalStateException("you can't instantiate me!"); } public static boolean isDebug() { return debug; } public static void setDebug(boolean debug) { AutoSizeLog.debug = debug; } public static void d(String message) { if (debug) { Log.d(TAG, message); } } public static void w(String message) { if (debug) { Log.w(TAG, message); } } public static void e(String message) { if (debug) { Log.e(TAG, message); } } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/utils/AutoSizeUtils.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.utils; import android.annotation.SuppressLint; import android.content.Context; import android.util.TypedValue; import android.app.Application; import java.lang.reflect.InvocationTargetException; /** * ================================================ * AndroidAutoSize 常用工具类 *

* Created by JessYan on 2018/8/25 15:24 * Contact me * Follow me * ================================================ */ public class AutoSizeUtils { private AutoSizeUtils() { throw new IllegalStateException("you can't instantiate me!"); } public static int dp2px(Context context, float value) { return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, context.getResources().getDisplayMetrics()) + 0.5f); } public static int sp2px(Context context, float value) { return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, value, context.getResources().getDisplayMetrics()) + 0.5f); } public static int pt2px(Context context, float value) { return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, value, context.getResources().getDisplayMetrics()) + 0.5f); } public static int in2px(Context context, float value) { return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_IN, value, context.getResources().getDisplayMetrics()) + 0.5f); } public static int mm2px(Context context, float value) { return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, value, context.getResources().getDisplayMetrics()) + 0.5f); } public static Application getApplicationByReflect() { try { @SuppressLint("PrivateApi") Class activityThread = Class.forName("android.app.ActivityThread"); Object thread = activityThread.getMethod("currentActivityThread").invoke(null); Object app = activityThread.getMethod("getApplication").invoke(thread); if (app == null) { throw new NullPointerException("you should init first"); } return (Application) app; } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } throw new NullPointerException("you should init first"); } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/utils/Preconditions.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.utils; import android.os.Looper; /** * ================================================ * Created by JessYan on 26/09/2016 13:59 * Contact me * Follow me * ================================================ */ public final class Preconditions { private Preconditions() { throw new IllegalStateException("you can't instantiate me!"); } public static void checkArgument(boolean expression) { if (!expression) { throw new IllegalArgumentException(); } } public static void checkArgument(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalArgumentException(String.valueOf(errorMessage)); } } public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); } } public static void checkState(boolean expression) { if (!expression) { throw new IllegalStateException(); } } public static void checkState(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalStateException(String.valueOf(errorMessage)); } } public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); } } public static T checkNotNull(T reference) { if (reference == null) { throw new NullPointerException(); } else { return reference; } } public static T checkNotNull(T reference, Object errorMessage) { if (reference == null) { throw new NullPointerException(String.valueOf(errorMessage)); } else { return reference; } } public static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { if (reference == null) { throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); } else { return reference; } } public static int checkElementIndex(int index, int size) { return checkElementIndex(index, size, "index"); } public static int checkElementIndex(int index, int size, String desc) { if (index >= 0 && index < size) { return index; } else { throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); } } /** * Throws {@link IllegalStateException} if the calling thread is not the application's main * thread. * * @throws IllegalStateException If the calling thread is not the application's main thread. */ public static void checkMainThread() { if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("Not in applications main thread"); } } private static String badElementIndex(int index, int size, String desc) { if (index < 0) { return format("%s (%s) must not be negative", new Object[]{desc, Integer.valueOf(index)}); } else if (size < 0) { throw new IllegalArgumentException((new StringBuilder(26)).append("negative size: ").append(size).toString()); } else { return format("%s (%s) must be less than size (%s)", new Object[]{desc, Integer.valueOf(index), Integer.valueOf(size)}); } } public static int checkPositionIndex(int index, int size) { return checkPositionIndex(index, size, "index"); } public static int checkPositionIndex(int index, int size, String desc) { if (index >= 0 && index <= size) { return index; } else { throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); } } private static String badPositionIndex(int index, int size, String desc) { if (index < 0) { return format("%s (%s) must not be negative", new Object[]{desc, Integer.valueOf(index)}); } else if (size < 0) { throw new IllegalArgumentException((new StringBuilder(26)).append("negative size: ").append(size).toString()); } else { return format("%s (%s) must not be greater than size (%s)", new Object[]{desc, Integer.valueOf(index), Integer.valueOf(size)}); } } public static void checkPositionIndexes(int start, int end, int size) { if (start < 0 || end < start || end > size) { throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); } } private static String badPositionIndexes(int start, int end, int size) { return start >= 0 && start <= size ? (end >= 0 && end <= size ? format("end index (%s) must not be less than start index (%s)", new Object[]{Integer.valueOf(end), Integer.valueOf(start)}) : badPositionIndex(end, size, "end index")) : badPositionIndex(start, size, "start index"); } static String format(String template, Object... args) { template = String.valueOf(template); StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); int templateStart = 0; int i; int placeholderStart; for (i = 0; i < args.length; templateStart = placeholderStart + 2) { placeholderStart = template.indexOf("%s", templateStart); if (placeholderStart == -1) { break; } builder.append(template.substring(templateStart, placeholderStart)); builder.append(args[i++]); } builder.append(template.substring(templateStart)); if (i < args.length) { builder.append(" ["); builder.append(args[i++]); while (i < args.length) { builder.append(", "); builder.append(args[i++]); } builder.append(']'); } return builder.toString(); } } ================================================ FILE: autosize/src/main/java/me/jessyan/autosize/utils/ScreenUtils.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.utils; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.os.Build; import android.provider.Settings; import android.util.DisplayMetrics; import android.view.Display; import android.view.WindowManager; /** * ================================================ * Created by JessYan on 26/09/2016 16:59 * Contact me * Follow me * ================================================ */ public class ScreenUtils { private ScreenUtils() { throw new IllegalStateException("you can't instantiate me!"); } public static int getStatusBarHeight() { int result = 0; try { int resourceId = Resources.getSystem().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { result = Resources.getSystem().getDimensionPixelSize(resourceId); } } catch (Resources.NotFoundException e) { e.printStackTrace(); } return result; } /** * 获取当前的屏幕尺寸 * * @param context {@link Context} * @return 屏幕尺寸 */ public static int[] getScreenSize(Context context) { int[] size = new int[2]; WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display d = w.getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); d.getMetrics(metrics); size[0] = metrics.widthPixels; size[1] = metrics.heightPixels; return size; } /** * 获取原始的屏幕尺寸 * * @param context {@link Context} * @return 屏幕尺寸 */ public static int[] getRawScreenSize(Context context) { int[] size = new int[2]; WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display d = w.getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); d.getMetrics(metrics); // since SDK_INT = 1; int widthPixels = metrics.widthPixels; int heightPixels = metrics.heightPixels; // includes window decorations (statusbar bar/menu bar) if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17) try { widthPixels = (Integer) Display.class.getMethod("getRawWidth").invoke(d); heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(d); } catch (Exception ignored) { } // includes window decorations (statusbar bar/menu bar) if (Build.VERSION.SDK_INT >= 17) try { Point realSize = new Point(); Display.class.getMethod("getRealSize", Point.class).invoke(d, realSize); widthPixels = realSize.x; heightPixels = realSize.y; } catch (Exception ignored) { } size[0] = widthPixels; size[1] = heightPixels; return size; } public static int getHeightOfNavigationBar(Context context) { //如果小米手机开启了全面屏手势隐藏了导航栏则返回 0 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { if (Settings.Global.getInt(context.getContentResolver(), "force_fsg_nav_bar", 0) != 0) { return 0; } } int realHeight = getRawScreenSize(context)[1]; int displayHeight = getScreenSize(context)[1]; return realHeight - displayHeight; } } ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:3.5.3' classpath 'com.novoda:bintray-release:0.9.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} jcenter() google() } } task clean(type: Delete) { delete rootProject.buildDir } ext { minSdkVersion = 14 targetSdkVersion = 29 compileSdkVersion = 29 buildToolsVersion = "29.0.2" versionCode = 41 versionName = "1.2.1" appcompat_v7 = "com.android.support:appcompat-v7:28.0.0" androidx_appcompat = "androidx.appcompat:appcompat:1.1.0" } ================================================ FILE: demo/.gitignore ================================================ /build ================================================ FILE: demo/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion defaultConfig { applicationId "me.jessyan.autosize.demo" minSdkVersion rootProject.minSdkVersion targetSdkVersion rootProject.targetSdkVersion versionCode rootProject.versionCode versionName rootProject.versionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { // implementation 'me.jessyan:autosize:1.2.1' implementation project(':autosize') implementation rootProject.appcompat_v7 implementation 'cat.ereza:customactivityoncrash:2.2.0' testImplementation 'junit:junit:4.12' } ================================================ FILE: demo/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # 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 ================================================ FILE: demo/src/androidTest/java/me/jessyan/autosize/demo/ExampleInstrumentedTest.java ================================================ package me.jessyan.autosize.demo; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("me.jessyan.autosize.demo", appContext.getPackageName()); } } ================================================ FILE: demo/src/main/AndroidManifest.xml ================================================ ================================================ FILE: demo/src/main/java/me/jessyan/autosize/demo/BaseApplication.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.demo; import android.app.Activity; import android.app.Application; import android.util.DisplayMetrics; import java.util.Locale; import cat.ereza.customactivityoncrash.activity.DefaultErrorActivity; import me.jessyan.autosize.AutoSize; import me.jessyan.autosize.AutoSizeConfig; import me.jessyan.autosize.external.ExternalAdaptInfo; import me.jessyan.autosize.external.ExternalAdaptManager; import me.jessyan.autosize.internal.CustomAdapt; import me.jessyan.autosize.onAdaptListener; import me.jessyan.autosize.utils.AutoSizeLog; /** * ================================================ * v0.9.1 发布后新增了副单位,可以在 pt、in、mm 三个冷门单位中选择一个作为副单位,然后在 layout 文件中使用副单位进行布局 * 副单位可以规避修改 {@link DisplayMetrics#density} 所造成的对于其他使用 dp 布局的系统控件或三方库控件的不良影响 * 使用副单位后可直接在 AndroidManifest 中填写设计图上的像素尺寸,不需要再将像素转化为 dp * 点击查看在布局中的实时预览方式 *

* 本框架核心原理来自于 今日头条官方适配方案 *

* 本框架源码的注释都很详细, 欢迎阅读学习 *

* AndroidAutoSize 会在 APP 启动时自动完成初始化, 如果您想设置自定义参数可以在 {@link Application#onCreate()} 中设置 *

* Created by JessYan on 2018/8/9 17:05 * Contact me * Follow me * ================================================ */ public class BaseApplication extends Application { @Override public void onCreate() { super.onCreate(); //当 App 中出现多进程, 并且您需要适配所有的进程, 就需要在 App 初始化时调用 initCompatMultiProcess() //在 Demo 中跳转的三方库中的 DefaultErrorActivity 就是在另外一个进程中, 所以要想适配这个 Activity 就需要调用 initCompatMultiProcess() AutoSize.initCompatMultiProcess(this); //如果在某些特殊情况下出现 InitProvider 未能正常实例化, 导致 AndroidAutoSize 未能完成初始化 //可以主动调用 AutoSize.checkAndInit(this) 方法, 完成 AndroidAutoSize 的初始化后即可正常使用 // AutoSize.checkAndInit(this); // 如何控制 AndroidAutoSize 的初始化,让 AndroidAutoSize 在某些设备上不自动启动?https://github.com/JessYanCoding/AndroidAutoSize/issues/249 /** * 以下是 AndroidAutoSize 可以自定义的参数, {@link AutoSizeConfig} 的每个方法的注释都写的很详细 * 使用前请一定记得跳进源码,查看方法的注释, 下面的注释只是简单描述!!! */ AutoSizeConfig.getInstance() //是否让框架支持自定义 Fragment 的适配参数, 由于这个需求是比较少见的, 所以须要使用者手动开启 //如果没有这个需求建议不开启 .setCustomFragment(true) //是否屏蔽系统字体大小对 AndroidAutoSize 的影响, 如果为 true, App 内的字体的大小将不会跟随系统设置中字体大小的改变 //如果为 false, 则会跟随系统设置中字体大小的改变, 默认为 false // .setExcludeFontScale(true) //区别于系统字体大小的放大比例, AndroidAutoSize 允许 APP 内部可以独立于系统字体大小之外,独自拥有全局调节 APP 字体大小的能力 //当然, 在 APP 内您必须使用 sp 来作为字体的单位, 否则此功能无效, 不设置或将此值设为 0 则取消此功能 // .setPrivateFontScale(0.8f) //屏幕适配监听器 .setOnAdaptListener(new onAdaptListener() { @Override public void onAdaptBefore(Object target, Activity activity) { //使用以下代码, 可以解决横竖屏切换时的屏幕适配问题 //使用以下代码, 可支持 Android 的分屏或缩放模式, 但前提是在分屏或缩放模式下当用户改变您 App 的窗口大小时 //系统会重绘当前的页面, 经测试在某些机型, 某些情况下系统不会主动重绘当前页面, 所以这时您需要自行重绘当前页面 //ScreenUtils.getScreenSize(activity) 的参数一定要不要传 Application!!! // AutoSizeConfig.getInstance().setScreenWidth(ScreenUtils.getScreenSize(activity)[0]); // AutoSizeConfig.getInstance().setScreenHeight(ScreenUtils.getScreenSize(activity)[1]); AutoSizeLog.d(String.format(Locale.ENGLISH, "%s onAdaptBefore!", target.getClass().getName())); } @Override public void onAdaptAfter(Object target, Activity activity) { AutoSizeLog.d(String.format(Locale.ENGLISH, "%s onAdaptAfter!", target.getClass().getName())); } }) //是否打印 AutoSize 的内部日志, 默认为 true, 如果您不想 AutoSize 打印日志, 则请设置为 false // .setLog(false) //是否使用设备的实际尺寸做适配, 默认为 false, 如果设置为 false, 在以屏幕高度为基准进行适配时 //AutoSize 会将屏幕总高度减去状态栏高度来做适配 //设置为 true 则使用设备的实际屏幕高度, 不会减去状态栏高度 //在全面屏或刘海屏幕设备中, 获取到的屏幕高度可能不包含状态栏高度, 所以在全面屏设备中不需要减去状态栏高度,所以可以 setUseDeviceSize(true) // .setUseDeviceSize(true) //是否全局按照宽度进行等比例适配, 默认为 true, 如果设置为 false, AutoSize 会全局按照高度进行适配 // .setBaseOnWidth(false) //设置屏幕适配逻辑策略类, 一般不用设置, 使用框架默认的就好 // .setAutoAdaptStrategy(new AutoAdaptStrategy()) ; customAdaptForExternal(); } /** * 给外部的三方库 {@link Activity} 自定义适配参数, 因为三方库的 {@link Activity} 并不能通过实现 * {@link CustomAdapt} 接口的方式来提供自定义适配参数 (因为远程依赖改不了源码) * 所以使用 {@link ExternalAdaptManager} 来替代实现接口的方式, 来提供自定义适配参数 */ private void customAdaptForExternal() { /** * {@link ExternalAdaptManager} 是一个管理外部三方库的适配信息和状态的管理类, 详细介绍请看 {@link ExternalAdaptManager} 的类注释 */ AutoSizeConfig.getInstance().getExternalAdaptManager() //加入的 Activity 将会放弃屏幕适配, 一般用于三方库的 Activity, 详情请看方法注释 //如果不想放弃三方库页面的适配, 请用 addExternalAdaptInfoOfActivity 方法, 建议对三方库页面进行适配, 让自己的 App 更完美一点 // .addCancelAdaptOfActivity(DefaultErrorActivity.class) //为指定的 Activity 提供自定义适配参数, AndroidAutoSize 将会按照提供的适配参数进行适配, 详情请看方法注释 //一般用于三方库的 Activity, 因为三方库的设计图尺寸可能和项目自身的设计图尺寸不一致, 所以要想完美适配三方库的页面 //就需要提供三方库的设计图尺寸, 以及适配的方向 (以宽为基准还是高为基准?) //三方库页面的设计图尺寸可能无法获知, 所以如果想让三方库的适配效果达到最好, 只有靠不断的尝试 //由于 AndroidAutoSize 可以让布局在所有设备上都等比例缩放, 所以只要您在一个设备上测试出了一个最完美的设计图尺寸 //那这个三方库页面在其他设备上也会呈现出同样的适配效果, 等比例缩放, 所以也就完成了三方库页面的屏幕适配 //即使在不改三方库源码的情况下也可以完美适配三方库的页面, 这就是 AndroidAutoSize 的优势 //但前提是三方库页面的布局使用的是 dp 和 sp, 如果布局全部使用的 px, 那 AndroidAutoSize 也将无能为力 //经过测试 DefaultErrorActivity 的设计图宽度在 380dp - 400dp 显示效果都是比较舒服的 .addExternalAdaptInfoOfActivity(DefaultErrorActivity.class, new ExternalAdaptInfo(true, 400)); } } ================================================ FILE: demo/src/main/java/me/jessyan/autosize/demo/CustomAdaptActivity.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.demo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.view.View; import me.jessyan.autosize.internal.CustomAdapt; /** * ================================================ * 本框架核心原理来自于 今日头条官方适配方案 *

* {@link CustomAdaptActivity} 展示项目内部的 {@link Activity} 自定义适配参数的用法, 需要实现 {@link CustomAdapt} * 现在 AndroidAutoSize 是全局以屏幕宽度为基准进行适配的, 并且全局的设计图尺寸为 360 * 640 * 这里就展示怎么让 {@link CustomAdaptActivity} 单个页面, 有别于全局设置, 以屏幕高度为基准进行适配, 并且更改设计图尺寸为 iPhone 的设计图尺寸 * 如果这个页面的设计图尺寸有别于其他页面, AndroidAutoSize 允许您改变单个页面的设计图尺寸, {@link #getSizeInDp()} *

* Created by JessYan on 2018/8/11 11:31 * Contact me * Follow me * ================================================ */ public class CustomAdaptActivity extends AppCompatActivity implements CustomAdapt { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom_adapt); } /** * 跳转到 {@link FragmentHost}, 展示项目内部的 {@link Fragment} 自定义适配参数的用法 * * @param view {@link View} */ public void goCustomAdaptFragment(View view) { startActivity(new Intent(getApplicationContext(), FragmentHost.class)); } /** * 是否按照宽度进行等比例适配 (为了保证在高宽比不同的屏幕上也能正常适配, 所以只能在宽度和高度之中选择一个作为基准进行适配) * * @return {@code true} 为按照宽度进行适配, {@code false} 为按照高度进行适配 */ @Override public boolean isBaseOnWidth() { return false; } /** * 这里使用 iPhone 的设计图, iPhone 的设计图尺寸为 750px * 1334px, 高换算成 dp 为 667 (1334px / 2 = 667dp) *

* 返回设计图上的设计尺寸, 单位 dp * {@link #getSizeInDp} 须配合 {@link #isBaseOnWidth()} 使用, 规则如下: * 如果 {@link #isBaseOnWidth()} 返回 {@code true}, {@link #getSizeInDp} 则应该返回设计图的总宽度 * 如果 {@link #isBaseOnWidth()} 返回 {@code false}, {@link #getSizeInDp} 则应该返回设计图的总高度 * 如果您不需要自定义设计图上的设计尺寸, 想继续使用在 AndroidManifest 中填写的设计图尺寸, {@link #getSizeInDp} 则返回 {@code 0} * * @return 设计图上的设计尺寸, 单位 dp */ @Override public float getSizeInDp() { return 667; } } ================================================ FILE: demo/src/main/java/me/jessyan/autosize/demo/CustomFragment1.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.demo; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import me.jessyan.autosize.AutoSize; import me.jessyan.autosize.internal.CustomAdapt; import me.jessyan.autosize.utils.AutoSizeUtils; /** * ================================================ * Created by JessYan on 2018/8/25 14:06 * Contact me * Follow me * ================================================ */ public class CustomFragment1 extends Fragment implements CustomAdapt { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //由于某些原因, 屏幕旋转后 Fragment 的重建, 会导致框架对 Fragment 的自定义适配参数失去效果 //所以如果您的 Fragment 允许屏幕旋转, 则请在 onCreateView 手动调用一次 AutoSize.autoConvertDensity() //如果您的 Fragment 不允许屏幕旋转, 则可以将下面调用 AutoSize.autoConvertDensity() 的代码删除掉 AutoSize.autoConvertDensity(getActivity(), 1080, true); return createTextView(inflater, "Fragment-1\nView width = 360dp\nTotal width = 1080dp", 0xffff0000); } @Override public boolean isBaseOnWidth() { return true; } @Override public float getSizeInDp() { return 1080; } public static View createTextView(LayoutInflater inflater, String content, int backgroundColor) { TextView view = new TextView(inflater.getContext()); ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams((AutoSizeUtils.dp2px(inflater.getContext(), 360)), ViewGroup.LayoutParams.MATCH_PARENT); view.setLayoutParams(layoutParams); view.setText(content); view.setTextColor(0xffffffff); view.setGravity(Gravity.CENTER); view.setTextSize(30); view.setBackgroundColor(backgroundColor); return view; } } ================================================ FILE: demo/src/main/java/me/jessyan/autosize/demo/CustomFragment2.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.demo; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import me.jessyan.autosize.AutoSize; import me.jessyan.autosize.internal.CustomAdapt; /** * ================================================ * Created by JessYan on 2018/8/25 14:06 * Contact me * Follow me * ================================================ */ public class CustomFragment2 extends Fragment implements CustomAdapt{ @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //由于某些原因, 屏幕旋转后 Fragment 的重建, 会导致框架对 Fragment 的自定义适配参数失去效果 //所以如果您的 Fragment 允许屏幕旋转, 则请在 onCreateView 手动调用一次 AutoSize.autoConvertDensity() //如果您的 Fragment 不允许屏幕旋转, 则可以将下面调用 AutoSize.autoConvertDensity() 的代码删除掉 AutoSize.autoConvertDensity(getActivity(), 720, true); return CustomFragment1.createTextView(inflater, "Fragment-2\nView width = 360dp\nTotal width = 720dp", 0xff00ff00); } @Override public boolean isBaseOnWidth() { return true; } @Override public float getSizeInDp() { return 720; } } ================================================ FILE: demo/src/main/java/me/jessyan/autosize/demo/CustomFragment3.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.demo; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import me.jessyan.autosize.AutoSize; import me.jessyan.autosize.internal.CustomAdapt; /** * ================================================ * Created by JessYan on 2018/8/25 14:06 * Contact me * Follow me * ================================================ */ public class CustomFragment3 extends Fragment implements CustomAdapt{ @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //由于某些原因, 屏幕旋转后 Fragment 的重建, 会导致框架对 Fragment 的自定义适配参数失去效果 //所以如果您的 Fragment 允许屏幕旋转, 则请在 onCreateView 手动调用一次 AutoSize.autoConvertDensity() //如果您的 Fragment 不允许屏幕旋转, 则可以将下面调用 AutoSize.autoConvertDensity() 的代码删除掉 AutoSize.autoConvertDensity(getActivity(), 360, true); return CustomFragment1.createTextView(inflater, "Fragment-3\nView width = 360dp\nTotal width = 360dp", 0xff0000ff); } @Override public boolean isBaseOnWidth() { return true; } @Override public float getSizeInDp() { return 360; } } ================================================ FILE: demo/src/main/java/me/jessyan/autosize/demo/FragmentHost.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.demo; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import me.jessyan.autosize.internal.CustomAdapt; /** * ================================================ * Created by JessYan on 2018/8/25 14:39 * Contact me * Follow me * ================================================ */ public class FragmentHost extends AppCompatActivity implements CustomAdapt { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_host); if (getSupportFragmentManager().findFragmentById(R.id.container1) == null) { getSupportFragmentManager().beginTransaction().add(R.id.container1, new CustomFragment1()).commit(); } if (getSupportFragmentManager().findFragmentById(R.id.container2) == null) { getSupportFragmentManager().beginTransaction().add(R.id.container2, new CustomFragment2()).commit(); } if (getSupportFragmentManager().findFragmentById(R.id.container3) == null) { getSupportFragmentManager().beginTransaction().add(R.id.container3, new CustomFragment3()).commit(); } } @Override public boolean isBaseOnWidth() { return true; } @Override public float getSizeInDp() { return 720; } } ================================================ FILE: demo/src/main/java/me/jessyan/autosize/demo/MainActivity.java ================================================ /* * Copyright 2018 JessYan * * 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. */ package me.jessyan.autosize.demo; import android.app.Activity; import android.app.Dialog; import android.app.Fragment; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; import cat.ereza.customactivityoncrash.activity.DefaultErrorActivity; import cat.ereza.customactivityoncrash.config.CaocConfig; import me.jessyan.autosize.AutoSizeConfig; import me.jessyan.autosize.internal.CustomAdapt; /** * ================================================ * 本框架核心原理来自于 今日头条官方适配方案 * 此方案不光可以适配 {@link Activity}, 这个 {@link Activity} 下的所有 {@link Fragment}、{@link Dialog}、{@link View} 都会自动适配 *

* {@link MainActivity} 是以屏幕宽度为基准进行适配的, 并且使用的是在 AndroidManifest 中填写的全局设计图尺寸 360 * 640 * 不懂什么叫基准的话, 请看 {@link AutoSizeConfig#isBaseOnWidth}) 的注释, AndroidAutoSize 默认全局以屏幕宽度为基准进行适配 * 如果想更改为全局以屏幕高度为基准进行适配, 请在 {@link BaseApplication} 中按注释中更改, 为什么强调全局? * 因为 AndroidAutoSize 允许每个 {@link Activity} 可以自定义适配参数, 自定义适配参数通过实现 {@link CustomAdapt} * 如果不自定义适配参数就会使用全局的适配参数, 全局适配参数在 {@link BaseApplication} 中按注释设置 *

* Created by JessYan on 2018/8/9 17:05 * Contact me * Follow me * ================================================ */ //实现 CancelAdapt 即可取消当前 Activity 的屏幕适配, 并且这个 Activity 下的所有 Fragment 和 View 都会被取消适配 //public class MainActivity extends AppCompatActivity implements CancelAdapt { public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } /** * 需要注意的是暂停 AndroidAutoSize 后, AndroidAutoSize 只是停止了对后续还没有启动的 {@link Activity} 进行适配的工作 * 但对已经启动且已经适配的 {@link Activity} 不会有任何影响 * * @param view {@link View} */ public void stop(View view) { Toast.makeText(getApplicationContext(), "AndroidAutoSize stops working!", Toast.LENGTH_SHORT).show(); AutoSizeConfig.getInstance().stop(this); } /** * 需要注意的是重新启动 AndroidAutoSize 后, AndroidAutoSize 只是重新开始了对后续还没有启动的 {@link Activity} 进行适配的工作 * 但对已经启动且在 stop 期间未适配的 {@link Activity} 不会有任何影响 * * @param view {@link View} */ public void restart(View view) { Toast.makeText(getApplicationContext(), "AndroidAutoSize continues to work", Toast.LENGTH_SHORT).show(); AutoSizeConfig.getInstance().restart(); } /** * 跳转到 {@link CustomAdaptActivity}, 展示项目内部的 {@link Activity} 自定义适配参数的用法 * * @param view {@link View} */ public void goCustomAdaptActivity(View view) { startActivity(new Intent(getApplicationContext(), CustomAdaptActivity.class)); } /** * 跳转到三方库的 {@link Activity}, 展示项目外部某些三方库的 {@link Activity} 自定义适配参数的用法 * 跳转前要先在 {@link BaseApplication#customAdaptForExternal()} 中给外部的三方库 {@link Activity} 自定义适配参数 * * @param view {@link View} */ public void goThirdLibraryActivity(View view) { //这里就是随便找个三方库的 Activity, 测试下适配三方库页面的功能是否可用 //以下代码就是为了启动这个三方库的 Activity, 不必在意 Intent intent = new Intent(getApplicationContext(), DefaultErrorActivity.class); Bundle extras = new Bundle(); extras.putSerializable("cat.ereza.customactivityoncrash.EXTRA_CONFIG", CaocConfig.Builder.create().get()); intent.putExtras(extras); startActivity(intent); } } ================================================ FILE: demo/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: demo/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: demo/src/main/res/layout/activity_custom_adapt.xml ================================================