AutoSize是基于今日头条的适配方案,但它有一些缺点,比如代码侵入性较强,在使用第三方的View框架时,可能会出现不兼容的情况。
我目前的sdk项目不能使用这样的框架,于是自己做了一个简单的工具类,也能够满足基本需求。
适配方案
以设计图的960 x 540 dp作为基准,在代码中获取屏幕宽高px,遇到需要适配的view时,调用autoSizeLayout()方法自动调整。
-
autoSizeLayout方法会遍历viewGroup下的所有根节点,获取当前view的内外边距的px
-
px除以屏幕密度得到当前view在布局文件中设置的dp值
-
使用该dp值根据横向或纵向来除以960或540,得出其在设计图中所占比例
-
比例乘以当前屏幕的宽高px,即为应该设置的新的px值
优点:
-
代码中只需封装一次,以后布局需要支持的话,都调用autoSizeLayout()方法进行处理即可
-
xml中的dp值可以完全以设计图为准,具体值会在代码中进行计算
-
集成该框架无需改动现有项目的代码
缺点:
- xml中的shape资源无法适配,需要手动在代码中处理
public class SimpleAutoSize {/*** 屏幕宽高px*/private int screenWidth = 0;private int screenHeight = 0;/*** 设计图基准dp*/private int basisWidthDp = 0;private int basisHeightDp = 0;private float density = 0;private onAutoSizeViewListener onAutoSizeViewListener;/*** 可以直接使用View的宽高px值,乘以该比例,得出相对于960*540屏幕的百分比宽高* <p>* 计算步骤:* 1、布局中的16dp,跑到了1080*1920的屏幕上,成为了24px* 2、先24除以屏幕密度1.5,得到布局文件中的dp值16* 3、计算16占540的比重* 4、结果乘以1080,得到32px,所以16dp在当前屏幕上,按照百分比计算应该是32px*/private float widthRatio = 0;private float heightRatio = 0;private float screenRatio = 0;private static class SingletonHolder {private static final SimpleAutoSize instance = new SimpleAutoSize();}public static SimpleAutoSize getInstance() {return SingletonHolder.instance;}public interface onAutoSizeViewListener {public void onAutoSize(View view, float screenRatio);}public void init(Context context, int basisWidthDp, int basisHeightDp) {this.basisWidthDp = basisWidthDp;this.basisHeightDp = basisHeightDp;calc(context);}private void calc(Context context){WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = windowManager.getDefaultDisplay();Point screenSize = new Point();display.getSize(screenSize);screenWidth = screenSize.x;screenHeight = screenSize.y;this.density = context.getResources().getDisplayMetrics().density;widthRatio = (float) screenWidth / basisWidthDp / density; // 再除以屏幕密度,方便后续一步求出结果heightRatio = (float) screenHeight / basisHeightDp / density;screenRatio = (float) screenWidth / basisWidthDp;}public SimpleAutoSize.onAutoSizeViewListener getOnAutoSizeViewListener() {return onAutoSizeViewListener;}public void setOnAutoSizeViewListener(SimpleAutoSize.onAutoSizeViewListener onAutoSizeViewListener) {this.onAutoSizeViewListener = onAutoSizeViewListener;}public int getScreenWidth() {return screenWidth;}public int getScreenHeight() {return screenHeight;}public int getBasisWidthDp() {return basisWidthDp;}public int getBasisHeightDp() {return basisHeightDp;}public float getDensity() {return density;}public float getWidthRatio() {return widthRatio;}public float getHeightRatio() {return heightRatio;}public boolean hasChange(Context context){WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = windowManager.getDefaultDisplay();Point screenSize = new Point();display.getSize(screenSize);if (screenSize.x != screenWidth || screenSize.y != screenHeight){return true;}if (density != context.getResources().getDisplayMetrics().density){return true;}return false;}public void autoSizeLayout(ViewGroup viewGroup) {autoSizeLayout(viewGroup, null);}/*** 按照比例自动调整布局,使控件在不同密度下显示一致** @param viewGroup*/public void autoSizeLayout(ViewGroup viewGroup, onAutoSizeViewListener onAutoSizeViewListener) {// 判断屏幕分辨率,和density是否发生变化,如果发生变化,则重新计算if (hasChange(viewGroup.getContext())) {calc(viewGroup.getContext());}int childCount = viewGroup.getChildCount();for (int i = 0; i < childCount; i++) {View childAt = viewGroup.getChildAt(i);if (onAutoSizeViewListener != null) {onAutoSizeViewListener.onAutoSize(childAt, screenRatio);}ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) childAt.getLayoutParams();
// Log.i("XmlBaseAdView", "处理前的Margin: " + layoutParams.topMargin + " " + layoutParams.bottomMargin + " " + layoutParams.leftMargin + " " + layoutParams.rightMargin);layoutParams.topMargin = (int) (layoutParams.topMargin * heightRatio);layoutParams.bottomMargin = (int) (layoutParams.bottomMargin * heightRatio);layoutParams.leftMargin = (int) (layoutParams.leftMargin * widthRatio);layoutParams.rightMargin = (int) (layoutParams.rightMargin * widthRatio);
// Log.i("XmlBaseAdView", "处理后的Margin: " + layoutParams.topMargin + " " + layoutParams.bottomMargin + " " + layoutParams.leftMargin + " " + layoutParams.rightMargin);childAt.setLayoutParams(layoutParams);// Log.i("XmlBaseAdView", "处理前的Padding: " + childAt.getPaddingTop() + " " + childAt.getPaddingBottom() + " " + childAt.getPaddingLeft() + " " + childAt.getPaddingRight());float padTop = childAt.getPaddingTop() * heightRatio;float padBottom = childAt.getPaddingBottom() * heightRatio;float padLeft = childAt.getPaddingLeft() * widthRatio;float padRight = childAt.getPaddingRight() * widthRatio;childAt.setPadding((int) padLeft, (int) padTop, (int) padRight, (int) padBottom);
// Log.i("XmlBaseAdView", "处理后的Padding: " + childAt.getPaddingTop() + " " + childAt.getPaddingBottom() + " " + childAt.getPaddingLeft() + " " + childAt.getPaddingRight());if (childAt instanceof TextView) {// 如果是TextView,还需要处理字体大小TextView textView = (TextView) childAt;float textSize = textView.getTextSize();textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize /density * screenRatio);}if (childAt instanceof ViewGroup) {autoSizeLayout((ViewGroup) childAt);}}}
初始化:
非常简单,只需要一行代码
SimpleAutoSize.getInstance().init(context, 960, 540);
使用:
第一个参数传入要适配的view,一般是根view
SimpleAutoSize.getInstance().autoSizeLayout(baseAdView, (view, screenRatio) -> {Resources resources = context.getResources();if (view.getId() == R.id.ll_countdown_tips) {view.setBackground(SimpleAutoSize.createRectangleDrawable(resources.getColor(R.color.textBg), resources.getDimension(R.dimen.dimen_15) /resources.getDisplayMetrics().density * screenRatio));}if (view.getId() == R.id.tv_show_advert_tag) {view.setBackground(SimpleAutoSize.createRectangleDrawable(resources.getColor(R.color.textBg), resources.getDimension(R.dimen.dimen_3) /resources.getDisplayMetrics().density * screenRatio));}});
autoSizeLayout第二个参数可传可不传,如果需要对view做一下特殊处理的话,可以传。
我这里是因为shape资源无法适配,所以在代码中进行处理。
大家如需使用的话可以把以下工具方法也添加进去。
public static GradientDrawable createRectangleDrawable(int color, float radius) {return createRectangleDrawable(color, 0, 0, radius);}/*** 创建背景颜色** @param color 填充色* @param strokeColor 线条颜色* @param strokeWidth 线条宽度 单位px* @param radius 角度 px*/public static GradientDrawable createRectangleDrawable(int color, int strokeColor, int strokeWidth, float radius) {try {GradientDrawable radiusBg = new GradientDrawable();//设置Shape类型radiusBg.setShape(GradientDrawable.RECTANGLE);//设置填充颜色radiusBg.setColor(color);//设置线条粗心和颜色,pxradiusBg.setStroke(strokeWidth, strokeColor);//设置圆角角度,如果每个角度都一样,则使用此方法radiusBg.setCornerRadius(radius);return radiusBg;} catch (Exception e) {return new GradientDrawable();}}/*** 设置Selector。*/public static StateListDrawable newSelector(int focusColor, int normalColor, float radius) {StateListDrawable bg = new StateListDrawable();// View.PRESSED_ENABLED_STATE_SETbg.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, createRectangleDrawable(focusColor, radius));// View.ENABLED_FOCUSED_STATE_SETbg.addState(new int[]{android.R.attr.state_enabled, android.R.attr.state_focused}, createRectangleDrawable(focusColor, radius));// View.ENABLED_STATE_SETbg.addState(new int[]{android.R.attr.state_enabled}, createRectangleDrawable(normalColor, radius));// View.FOCUSED_STATE_SETbg.addState(new int[]{android.R.attr.state_focused}, createRectangleDrawable(focusColor, radius));// View.WINDOW_FOCUSED_STATE_SETbg.addState(new int[]{android.R.attr.state_window_focused}, createRectangleDrawable(focusColor, radius));// View.EMPTY_STATE_SETbg.addState(new int[]{}, createRectangleDrawable(normalColor, radius));return bg;}public static GradientDrawable getBgDrawable() {return DrawableUtils.createRectangleDrawable(Color.argb(255, 67, 81, 115), 20);}