实现原理:
以一个特定的宽度尺寸的设备为参考, 在 View 加载的过程中, 根据当前设备的实际像素换算出目标像素, 再作用到控件上.
通常 UI 给我们的设计稿只有一种像素的标准, 例如 720 * 1280. 例如在设计稿上有一个控件宽度为屏幕尺寸的一半, 即 360px, 假设我们真机的屏幕尺寸为 1080 * 1920 像素, 我们再布局中设置控件尺寸也为 360px, 则显示为屏幕的三分之一. 但是我们想要效果仍然是显示屏幕的一半. 即宽度应该设置为 540px, 这里的 540px 是如何得到的呢, 这就是通过真实屏幕尺寸与参考设计稿的尺寸比例来得到的, 即:(1080/720)*360 = 540.
下面通过代码来实现这一功能:
- public class Utils {
- private static Utils utils;
- // 这是设计稿参考的宽高
- private static final float STANDARD_WIDTH = 720;
- private static final float STANDARD_HEIGHT = 1080;
- // 这里是屏幕的显示宽高
- private int mDisPlayWidth;
- private int mDisPlayHeight;
- public static Utils getInstance(Context context) {
- if (utils == null) {
- utils = new Utils(context.getApplicationContext()); // 获取 application 的 context 是防止内存泄漏
- }
- return utils;
- }
- private Utils(Context context) {
- // 获取屏幕的宽高
- if (mDisPlayHeight == 0 || mDisPlayWidth == 0) { // 宽高还未赋值
- WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- if (manager != null) {
- DisplayMetrics displayMetrics = new DisplayMetrics();
- manager.getDefaultDisplay().getMetrics(displayMetrics); // 此时 displayMetrics 就可以获取到屏幕宽高信息了
- if (displayMetrics.widthPixels> displayMetrics.heightPixels) {
- // 横屏
- mDisPlayWidth = displayMetrics.heightPixels;
- mDisPlayHeight = displayMetrics.widthPixels - getStatusBarHeight(context);
- } else {
- // 竖屏
- mDisPlayWidth = displayMetrics.widthPixels;
- mDisPlayHeight = displayMetrics.heightPixels - getStatusBarHeight(context);
- }
- }
- }
- }
- // 获取状态栏高度
- public int getStatusBarHeight(Context context) {
- int resID = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
- if (resID> 0) { // 可以获取到状态栏高度
- return context.getResources().getDimensionPixelSize(resID);
- }
- return 0;
- }
- // 获取水平方向的缩放比例
- public float getHorizontalScale() {
- return mDisPlayWidth / STANDARD_WIDTH;
- }
- // 获取垂直方向的缩放比例
- public float getVerticalScale() {
- return mDisPlayHeight / STANDARD_HEIGHT;
- }
- }
Utils 工具类用来计算设计稿屏幕分辨率和真实屏幕分辨率的比例.
下面以 RelativeLayout 为例:
- public class ScreenAdapterLayout extends RelativeLayout {
- private boolean flag;
- public ScreenAdapterLayout(Context context) {
- super(context);
- }
- public ScreenAdapterLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- public ScreenAdapterLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // 计算宽高之前获取缩放比例的值
- if (!flag) { // 防止比例的计算多次调用
- float scaleX = Utils.getInstance(getContext()).getHorizontalScale();
- float scaleY = Utils.getInstance(getContext()).getVerticalScale();
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- LayoutParams params = (LayoutParams) child.getLayoutParams();
- params.width = (int) (params.width * scaleX);
- params.height = (int) (params.height * scaleY);
- params.leftMargin = (int) (params.leftMargin * scaleX);
- params.rightMargin = (int) (params.rightMargin * scaleX);
- params.topMargin = (int) (params.leftMargin * scaleY);
- params.bottomMargin = (int) (params.leftMargin * scaleY);
- }
- flag = true;
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
下面在资源布局中使用 ScreenAdapterLayout.
image.PNG
这里我设置的宽度为 360px, 我手机的像素为 1080*2248, 但是显示的 view 的宽度仍然为屏幕的一半. 这是因为根据 Utils 把 view 的宽高做了换算. 我们不需要关注真实屏幕的分辨率. 只需要根据设计稿的分辨率来设置 view 的宽高.
image.PNG
[附] 相关架构及资料
image
资料领取
点赞 + 加群免费获取 Android IoC 架构设计
加群领取获取往期 Android 高级架构资料, 源码, 笔记, 视频. 高级 UI, 性能优化, 架构师课程, NDK, 混合式开发 (ReactNative+Weex) 微信小程序, Flutter 全方面的 Android 进阶实践技术, 群内还有技术大牛一起讨论交流解决问题.
来源: http://www.jianshu.com/p/67d7cce76b20