屏幕适配之百分比方案详解

RatioLayout一个屏幕适配三方库源码分析 RatioLayout一个屏幕适配三方库源码分析使用dp进行UI设置:可以看出,在不同分辨率或者密度的屏幕,相同dp值设置的效果是不同的。使用ratio进行UI设置(按比例设置):可以看出,在不同分辨率或者密度的屏幕,ratio按比例设置的效果是比较出色的。简单分析RatioLayout:1,初始化一些参数,设置数据,为了在xml中设置属性到自定义布局中使用 2,在系统测量之前手动测量一次,获取到 阅读详情

屏幕适配之百分比方案详解

Android设备碎片化十分严重,在开发过程中的适配工作也非常很繁琐,有关屏幕适配的介绍请看之前的文章屏幕适配。

最近看到DrawerLayout,support v4中提供的类,想到对google提供的这些支持库,自己一点都不熟悉,想着看看Google提供的支持库都有什么内容。结果看着看着在最后忽然看到了Percent Support Library。寻思怎么还百分比呢?仔细一看介绍,我擦,真是太有用了。

Percent Support Library

The Percent package provides APIs to support adding and managing percentage based dimensions in your app.

The Percent Support library adds support for the PercentLayoutHelper.PercentLayoutParams interface and various classes, such as PercentFrameLayout and PercentRelativeLayout.

After you download the Android Support Libraries, this library is located in the /extras/android/support/percent directory. For more information on how to set up your project, follow the instructions in Adding libraries with resources.

The Gradle build script dependency identifier for this library is as follows:
com.android.support:percent:23.3.0

看到了吗? 说提供了PercentFrameLayout和PercentRelativeLayout来支持百分比了。这样不就完美的解决了适配的问题嘛。啥也不说了,立马配置cradle来瞧瞧。

Subclass of FrameLayout that supports percentage based dimensions and margins. You can specify dimension or a margin of child by using attributes with “Percent” suffix.

上代码:

<android.support.percent.PercentFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:src="@mipmap/ic_launcher"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        app:layout_marginTopPercent="25%"
        app:layout_marginLeftPercent="25%"/>
</android.support.percent.PercentFrameLayout>

效果如下:
image
完美.

支持的属性:

  • layout_widthPercent
  • layout_heightPercent
  • layout_marginPercent
  • layout_marginLeftPercent
  • layout_marginTopPercent
  • layout_marginRightPercent
  • layout_marginBottomPercent
  • layout_marginStartPercent
  • layout_marginEndPercent
  • layout_aspectRatio

It is not necessary to specify layout_width/height if you specify layout_widthPercent. However, if you want the view to be able to take up more space than what percentage value permits, you can add layout_width/height=”wrap_content”. In that case if the percentage size is too small for the View’s content, it will be resized using wrap_content rule.

如果指定了layout_widthPercent就不用指定layout_width/height属性了。(Studio可能会提示错误,设置忽略就好)。然而,如果你想要该View能够占用比设置的百分比值更大的空间时,你可以指定layout_widht/height=“wrap_content”。在这种情况下,如果设置的百分比值在显示内容时太小时,将会使用wrap_content的值重新计算。

就是这个意思:如果指定的百分比太小怕显示不开的话,也可以给它指定wrap_content属性,这样当显示不开的时候就会使用wrap_content的值。
如下:

<android.support.percent.PercentFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        android:text="顶顶顶顶顶大大大"
        android:textSize="30dp"
        android:singleLine="true"
        app:layout_widthPercent="30%"
        app:layout_heightPercent="5%"
        app:layout_marginTopPercent="25%"
        app:layout_marginLeftPercent="25%"/>
</android.support.percent.PercentFrameLayout>

效果:
image

加入wrap_content后一点效果也没有啊,还是显示不全啊,还没加wrap_content一样,- -!

你也可以通过只设置width或者height和layout_aspectRatio这种比例值的方式来让第另一个值自动计算。例如,如果你想要使用16:9的比例,你可以使用:

android:layout_width="300dp"
app:layout_aspectRatio="178%"

这样将会在宽固定为300dp的基础上以16:9(1.78:1)来计算高度。

PercentRelativeLayout的使用都是一样的,这里就不贴了。

那它是怎么实现的呢?其实就是内部给通过属性换算,把本布局的宽高和百分比去计算每个view的大小和位置。但是还有为什么上面设置的wrap_content无效呢?是我理解错了吗?

带着这两个疑问我们来看下源码:

public class PercentFrameLayout extends FrameLayout {
    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);

    public PercentFrameLayout(Context context) {
        super(context);
    }

    public PercentFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public PercentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 从名字上就能看出来下面就是处理如果指定百分比过小不足显示内容时,就去使用`wrap_content`的逻辑
        if (mHelper.handleMeasuredStateTooSmall()) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHelper.restoreOriginalParams();
    }

    public static class LayoutParams extends FrameLayout.LayoutParams
            implements PercentLayoutHelper.PercentLayoutParams {

        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            // PercentLayoutInfo中去解析自定义属性的值
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(int width, int height, int gravity) {
            super(width, height, gravity);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }

        public LayoutParams(FrameLayout.LayoutParams source) {
            super((MarginLayoutParams) source);
            gravity = source.gravity;
        }

        public LayoutParams(LayoutParams source) {
            this((FrameLayout.LayoutParams) source);
            mPercentLayoutInfo = source.mPercentLayoutInfo;
        }

        @Override
        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
            if (mPercentLayoutInfo == null) {
                mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo();
            }

            return mPercentLayoutInfo;
        }

        @Override
        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
        }
    }
}

源码不长,主要是三个部分:

  • 重写onMeasure,根据百分比转换成对应的尺寸
  • 重写onLayout
  • 自定义LayoutParams,在FrameLayout.LayoutParams的基础上多实现了一个接口。包含了PercentLayoutHelper.PercentLayoutInfo属性,而文档中对PercentLayoutInfo的介绍是Container for information about percentage dimensions and margins. It acts as an extension for LayoutParams.

我们也先一步步的来分析,首先看下mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);:

/**
     * Iterates over children and changes their width and height to one calculated from percentage
     * values.
     * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
     * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
     */
public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: "
                    + View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: "
                    + View.MeasureSpec.toString(heightMeasureSpec));
        }

        int widthHint = View.MeasureSpec.getSize(widthMeasureSpec);
        int heightHint = View.MeasureSpec.getSize(heightMeasureSpec);
        // mHost是由构造函数传递过来的,就是PercentFrameLayout自身。
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "should adjust " + view + " " + params);
            }
            if (params instanceof PercentLayoutParams) {
                // 通过PercentLayoutParams. getPercentLayoutInfo()方法得到PercentLayoutInfo,至于PercentLayoutInfo类在上面也说过了。
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "using " + info);
                }
                if (info != null) {
                    if (params instanceof ViewGroup.MarginLayoutParams) {
                        // PercentFrameLayout.LayoutParams继承自FrameLayout.LayoutParams,而FrameLayout.LayoutParams又继承自ViewGroup.MarginLayoutParams
                        info.fillMarginLayoutParams(view, (ViewGroup.MarginLayoutParams) params,
                                widthHint, heightHint);
                    } else {
                        info.fillLayoutParams(params, widthHint, heightHint);
                    }
                }
            }
        }
    }

所以上面代码的意思就是通过对每个子View调用getLayoutParams方法,然后从该LayoutParams中获取到它的PercentLayoutInfo属性,然后再调用PercentLayoutInfo.fillMarginLayoutParams()方法。

那我们继续看一下PercentLayoutInfo.fillMarginLayoutParams()方法的实现,该方法是根据百分比去设置尺寸和margin:

/**
         * Fills {@code ViewGroup.MarginLayoutParams} dimensions and margins based on percentage
         * values.
         */
        public void fillMarginLayoutParams(View view, ViewGroup.MarginLayoutParams params,
                int widthHint, int heightHint) {
            // 根据百分比值设置View的尺寸
            fillLayoutParams(params, widthHint, heightHint);

            // 从注释中就能知道,fillLayoutParams是计算尺寸,那下面这部分就是处理margin了
            // mPreservedParams来记录原始的margin值
            // Preserve the original margins, so we can restore them after the measure step.
            mPreservedParams.leftMargin = params.leftMargin;
            mPreservedParams.topMargin = params.topMargin;
            mPreservedParams.rightMargin = params.rightMargin;
            mPreservedParams.bottomMargin = params.bottomMargin;
            MarginLayoutParamsCompat.setMarginStart(mPreservedParams,
                    MarginLayoutParamsCompat.getMarginStart(params));
            MarginLayoutParamsCompat.setMarginEnd(mPreservedParams,
                    MarginLayoutParamsCompat.getMarginEnd(params));

            if (leftMarginPercent >= 0) {
                params.leftMargin = (int) (widthHint * leftMarginPercent);
            }
            if (topMarginPercent >= 0) {
                params.topMargin = (int) (heightHint * topMarginPercent);
            }
            if (rightMarginPercent >= 0) {
                params.rightMargin = (int) (widthHint * rightMarginPercent);
            }
            if (bottomMarginPercent >= 0) {
                params.bottomMargin = (int) (heightHint * bottomMarginPercent);
            }
            boolean shouldResolveLayoutDirection = false;
            if (startMarginPercent >= 0) {
                MarginLayoutParamsCompat.setMarginStart(params,
                        (int) (widthHint * startMarginPercent));
                shouldResolveLayoutDirection = true;
            }
            if (endMarginPercent >= 0) {
                MarginLayoutParamsCompat.setMarginEnd(params,
                        (int) (widthHint * endMarginPercent));
                shouldResolveLayoutDirection = true;
            }
            if (shouldResolveLayoutDirection && (view != null)) {
                // Force the resolve pass so that start / end margins are propagated to the
                // matching left / right fields
                MarginLayoutParamsCompat.resolveLayoutDirection(params,
                        ViewCompat.getLayoutDirection(view));
            }
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height
                        + ")");
            }
        }

再看一下PercentLayoutInfo.fillLayoutParams()的实现,该方法是通过百分比设置View的尺寸:

        /**
         * Fills {@code ViewGroup.LayoutParams} dimensions based on percentage values.
         */
        public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,
                int heightHint) {
            // mPreservedParams来记录原始的宽高值
            // Preserve the original layout params, so we can restore them after the measure step.
            mPreservedParams.width = params.width;
            mPreservedParams.height = params.height;

            // We assume that width/height set to 0 means that value was unset. This might not
            // necessarily be true, as the user might explicitly set it to 0. However, we use this
            // information only for the aspect ratio. If the user set the aspect ratio attribute,
            // it means they accept or soon discover that it will be disregarded.
            final boolean widthNotSet =
                    (mPreservedParams.mIsWidthComputedFromAspectRatio
                            || mPreservedParams.width == 0) && (widthPercent < 0);
            final boolean heightNotSet =
                    (mPreservedParams.mIsHeightComputedFromAspectRatio
                            || mPreservedParams.height == 0) && (heightPercent < 0);

            // 如果指定了百分比属性,就用宽高值和百分比去算出具体的宽高
            if (widthPercent >= 0) {
                params.width = (int) (widthHint * widthPercent);
            }

            if (heightPercent >= 0) {
                params.height = (int) (heightHint * heightPercent);
            }
            // 如果指定了宽高的比例值,就用比例值去算出宽高,从这里能看出`aspectRatio`的优先级比百分比要高。他可以覆盖百分比之前设置的值。
            if (aspectRatio >= 0) {
                if (widthNotSet) {
                    params.width = (int) (params.height * aspectRatio);
                    // Keep track that we've filled the width based on the height and aspect ratio.
                    mPreservedParams.mIsWidthComputedFromAspectRatio = true;
                }
                if (heightNotSet) {
                    params.height = (int) (params.width / aspectRatio);
                    // Keep track that we've filled the height based on the width and aspect ratio.
                    mPreservedParams.mIsHeightComputedFromAspectRatio = true;
                }
            }

            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
            }
        }

到这里onMeasure中通过百分比值设置宽高以及margin的部分就看完了,那我们接着看一下关于百分比值过小时对wrap_content的处理:

if (mHelper.handleMeasuredStateTooSmall()) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

看一下PercentLayoutHelper.handleMeasuredStateTooSmall()方法的实现:

/**
     * Iterates over children and checks if any of them would like to get more space than it
     * received through the percentage dimension.
     *
     * If you are building a layout that supports percentage dimensions you are encouraged to take
     * advantage of this method. The developer should be able to specify that a child should be
     * remeasured by adding normal dimension attribute with {@code wrap_content} value. For example
     * he might specify child's attributes as {@code app:layout_widthPercent="60%p"} and
     * {@code android:layout_width="wrap_content"}. In this case if the child receives too little
     * space, it will be remeasured with width set to {@code WRAP_CONTENT}.
     *
     * @return True if the measure phase needs to be rerun because one of the children would like
     * to receive more space.
     */
    public boolean handleMeasuredStateTooSmall() {
        boolean needsSecondMeasure = false;
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "should handle measured state too small " + view + " " + params);
            }
            if (params instanceof PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (info != null) {
                    // shouldHandleMeasuredWidthTooSmall方法来进行判断
                    if (shouldHandleMeasuredWidthTooSmall(view, info)) {
                        needsSecondMeasure = true;
                        params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
                    }
                    if (shouldHandleMeasuredHeightTooSmall(view, info)) {
                        needsSecondMeasure = true;
                        params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
                    }
                }
            }
        }
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
        }
        return needsSecondMeasure;
    }

那继续看一下shouldHandleMeasuredWidthTooSmall()方法:

private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
        int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;
        return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 &&
                info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
    }

继续看ViewCompat.getMeasuredWidthAndState()的源码:

public static int getMeasuredWidthAndState(View view) {
        return IMPL.getMeasuredWidthAndState(view);
    }

继续往下看,这个IMPL是什么呢?

static final ViewCompatImpl IMPL;
    static {
        final int version = android.os.Build.VERSION.SDK_INT;
        if (version >= 23) {
            IMPL = new MarshmallowViewCompatImpl();
        } else if (version >= 21) {
            IMPL = new LollipopViewCompatImpl();
        } else if (version >= 19) {
            IMPL = new KitKatViewCompatImpl();
        } else if (version >= 17) {
            IMPL = new JbMr1ViewCompatImpl();
        } else if (version >= 16) {
            IMPL = new JBViewCompatImpl();
        } else if (version >= 15) {
            IMPL = new ICSMr1ViewCompatImpl();
        } else if (version >= 14) {
            IMPL = new ICSViewCompatImpl();
        } else if (version >= 11) {
            IMPL = new HCViewCompatImpl();
        } else if (version >= 9) {
            IMPL = new GBViewCompatImpl();
        } else if (version >= 7) {
            IMPL = new EclairMr1ViewCompatImpl();
        } else {
            IMPL = new BaseViewCompatImpl();
        }
    }

继续看,IMPL.getMeasuredWidthAndState(),方法其实最终就是使用的BaseViewCompatImpl.getMeasuredWidthAndState()方法,接着看它的的源码:

 @Override
        public int getMeasuredWidthAndState(View view) {
            return view.getMeasuredWidth();
        }

最后就是调用了getMeasuredWidth()方法。 没毛病- -!

onMeasure的到这里就分析完了。
那接着来看一下onLayout方法,onLayout方法直接调用了PercentLayoutHelper.restoreOriginalParams,:

/**
     * Iterates over children and restores their original dimensions that were changed for
     * percentage values. Calling this method only makes sense if you previously called
     * {@link PercentLayoutHelper#adjustChildren(int, int)}.
     */
    public void restoreOriginalParams() {
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "should restore " + view + " " + params);
            }
            if (params instanceof PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "using " + info);
                }
                if (info != null) {
                    if (params instanceof ViewGroup.MarginLayoutParams) {
                        info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
                    } else {
                        info.restoreLayoutParams(params);
                    }
                }
            }
        }
    }

调用了PercentLayoutInfo.restoreMarginLayoutParams()方法,我们看下它的源码:

/**
         * Restores original dimensions and margins after they were changed for percentage based
         * values. Calling this method only makes sense if you previously called
         * {@link PercentLayoutHelper.PercentLayoutInfo#fillMarginLayoutParams}.
         */
        public void restoreMarginLayoutParams(ViewGroup.MarginLayoutParams params) {
            restoreLayoutParams(params);
            // mPreservedParams的值在之前ad
            params.leftMargin = mPreservedParams.leftMargin;
            params.topMargin = mPreservedParams.topMargin;
            params.rightMargin = mPreservedParams.rightMargin;
            params.bottomMargin = mPreservedParams.bottomMargin;
            MarginLayoutParamsCompat.setMarginStart(params,
                    MarginLayoutParamsCompat.getMarginStart(mPreservedParams));
            MarginLayoutParamsCompat.setMarginEnd(params,
                    MarginLayoutParamsCompat.getMarginEnd(mPreservedParams));
        }

意思是说,再他们被以百分比为基础的数据更改之后恢复成原始的尺寸和margin。
然后继续看一下restoreLayoutParams():

/**
         * Restores original dimensions after they were changed for percentage based values. Calling
         * this method only makes sense if you previously called
         * {@link PercentLayoutHelper.PercentLayoutInfo#fillLayoutParams}.
         */
        public void restoreLayoutParams(ViewGroup.LayoutParams params) {
            if (!mPreservedParams.mIsWidthComputedFromAspectRatio) {
                // Only restore the width if we didn't compute it based on the height and
                // aspect ratio in the fill pass.
                params.width = mPreservedParams.width;
            }
            if (!mPreservedParams.mIsHeightComputedFromAspectRatio) {
                // Only restore the height if we didn't compute it based on the width and
                // aspect ratio in the fill pass.
                params.height = mPreservedParams.height;
            }

            // Reset the tracking flags.
            mPreservedParams.mIsWidthComputedFromAspectRatio = false;
            mPreservedParams.mIsHeightComputedFromAspectRatio = false;
        }

好了到这里也分析完了onLayout的方法,大意就是在onMeasure之前先将原始的宽高和margin都备份一下,然后在onMeasure中根据百分比去设置对应的宽高和margin,等到设置完之后在onLayout方法中再去将这些值恢复到之前备份的起始数据。说实话我没看明白为什么要这样做?

通过上面的代码分析我们知道对布局影响力的优先顺序: app:layout_aspectRatio > app:layout_heightPercent > android:layout_height如果我们同时都设置这三个参数值的话,最终会用app:layout_aspectRatio的值。

遗留问题:

  • 为什么在onLayout方法中去恢复数据,这有什么作用?
  • 看代码在处理如果指定的百分比过小但又指定wrap_content时,会重新根据wrap_content去重新计算的逻辑没有错,但是为什么我在上面测试的时候确没效果?

有知道上面这两个问题的告诉我一下。


更多内容请关注Github

Android 增强版百分比布局库 为了适配而扩展 在这里小编整理了一份Android大厂常见面试题,和一些Android架构视频解析,都已整理成文档,全部都已打包好了,希望能够对大家有所帮助,在面试中能顺利通过。喜欢本文的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取![外链图片转存中…(img-x4FBzTm8-1712153988809)] 阅读详情

相关推荐

Android屏幕适配全解析:从dp原理到实战方案

屏幕适配是移动开发中的基础技术挑战,其核心在于解决不同设备屏幕尺寸和像素密度的差异。Android系统通过密度无关像素(dp)实现物理尺寸一致性,计算公式px = dp * (dpi / 160)确保元素在不同设备上显示相近的实际大小。现代开发中,ConstraintLayout百分比布局和Jetpack Compose响应式设计进一步提升了适配效率。针对可折叠设备和多窗口模式等新型交互场景,开发者需要监听屏幕变化并优化布局结构。通过合理使用资源限定符系统、减少Overdraw以及自动化测试,可以构建出在从

weixin_34411563的博客 367

轻量级的安卓百分比屏幕适配方案SimpleAutoSize

AutoSize是基于今日头条的适配方案,但它有一些缺点,比如代码侵入性较强,在使用第三方的View框架时,可能会出现不兼容的情况。我目前的sdk项目不能使用这样的框架,于是自己做了一个简单的工具类,也能够满足基本需求。

王凯旋的博客 1100

Android 屏幕适配方案(百分比)

博客源址:Android 屏幕适配方案 1、概述 大家在Android开发时,肯定会觉得屏幕适配是个尤其痛苦的事,各种屏幕尺寸适配起来蛋疼无比。如果我们换个角度我们看下这个问题,不知道大家有没有了解过web前端开发,或者说大家对于网页都不陌生吧,其实适配的问题在web页面的设计中理论上也存在,为什么这么说呢?电脑的显示器的分辨率、包括手机分辨率,我敢说分辨率的种类远超过Android设

Android移动开发者的专栏 1万+

Android 适配18:9 和 16:9 屏幕尺寸比例的问题

1.问题:    在华为Mate 9 中,1080X1920分辨率,480dpi,XXhdpi, 1dp = 3px;   按照设计的尺寸去做,最后出来的跟UI 的图,竖向上差距很大,因为我的竖向上同样70dp,显示到手机上很大   最后,问同事才明白,手机是18:9的屏幕,而我们平常用的都是16:9的屏幕;所以根据这个比例 我的竖向上同样的像素占用空间会大; 2.解决:   在

yangaiyu的博客 1万+

简单高效的Android屏幕适配最佳方案--DP转百分比适配

    现在的Android市场,手机碎片化真的是不敢恭维,屏幕分辨率,尺寸,各式各样,虽然满足了广大消费者,但是却也搞得广大android开发者头疼,如何才能对这些手机进行完美的适配呢。    官方给出了一个建议,dp适配,据说以dp为单位能够在不同的手机上显示相似的结果,感觉可以一劳永逸了,但是却也不尽然,首先需要分析一下dp的适配方案:    其实手机才不认识dp这个单位呢,它只认识px,所...

有点不一样 3020

Android屏幕适配方案: AutoDensity&smallest-width

目录前言一、屏幕适配的重要概念1.1 屏幕尺寸、屏幕分辨率、屏幕像素密度1.2 px、dp、dip、dpi1.3 mdpi、hdpi、xdpi、xxdpi1.4 values-sw[xyz]dp二、smallest-width适配方案三、AutoDensity适配方案四、最佳做法五、参考 前言 一个Android开发工程师,在其入门后遇到的第一个考验估计就是屏幕适配。按照谷歌的适配规则,使用wrap_content、match_parent、dp等,当UI工程师换一个设备验收时,提出各种问题。这时候,估计很

half_bottle的博客 1672

ConstraintLayout利用百分比属性来解决一些简单的适配问题(四)

接着上一篇文章讲 ConstraintLayout属性之Group,GuideLine(三) 先前的几篇文章只是讲了一下ConstraintLayout的基本属性,都很简单,感觉没有眼前一亮的感觉,接下来要讲的东西也很简单,但是会很实用,尤其是在屏幕适配方面,如果运用的灵活,简直美滋滋。 在平时写布局的时候我们通常都是以dp为单位的,这也是google推崇的一种方式,但是dp并不能完全适配所有的手...

wzlyd1的专栏 2万+

Android百分比屏幕适配实战指南

在移动应用开发中,屏幕适配是开发者必须面对的关键问题之一。适配的定义是确保应用界面在不同尺寸和分辨率的屏幕上均能保持良好的布局和视觉效果。适配的目标是实现UI元素的自适应和灵活性,以便在多样化的设备上提供一致的用户体验。实现这一目标需要考虑到各种屏幕尺寸、分辨率、屏幕密度及方向等因素。开发者需要设计出能够适应各种屏幕变化的灵活布局,并处理好不同屏幕比例下内容的展示,以避免布局混乱或元素被裁剪。

weixin_29903713的博客 573

Flutter for OpenHarmony Web开发助手App实战:屏幕适配方案

flutter_screenutil是一个专门用于屏幕适配的库。它的核心思路很简单:基于设计稿尺寸,按比例缩放所有元素。比如设计稿是375x812的,实际设备是414x896的,那所有尺寸都按比例放大。这样就能保证视觉效果一致。

2601_94985743的博客 678

工程师必看:如何用功率谱密度搞定随机振动测试(附Grms计算实例)

本文为工程师详解如何通过功率谱密度(PSD)曲线计算Grms(总均方根加速度),从而量化随机振动测试的强度。文章拆解了平直谱、斜谱的计算公式,提供了完整的Grms计算实例,并指出了单位换算、对数坐标等常见工程陷阱,帮助工程师精准完成测试准备与设备推力评估。

3c4x5z6v7b的博客 630

AG32VF407直流电流采集仪设计:从1uA到10A宽范围采样与FPGA加速方案

本文详细介绍了基于AG32VF407芯片的直流电流采集仪设计方案,实现了从1uA到10A的宽范围采样。方案采用低噪声电源、精密运放调理、高速比较器自动档位切换及高精度ADC同步采样,并重点阐述了利用AG32VF407片内FPGA实现硬件加速以提升性能的架构思路,为精密电流测量提供了完整的硬件与FPGA加速解决方案。

weixin_33245447的博客 312

简单介绍屏幕适配(三种常用方案)

简单介绍屏幕适配(三种常用方案)- 1、像素(px): 含义:通常所说的像素,就是CCD/CMOS上光电感应元件的数量,一个感光元件经过感光,光电信号转换,A/D转换等步骤以后,在输出的照片上就形成一个点,我们如果把影像放大数倍,会发现这些连续色调其实是由许多色彩相近的小方点所组成,这些小方点就是构成影像的最小单位“像素”(Pixel)。简而言之,像素就是手机屏幕的最小构成单元。 单位:px(pi...

qq_29722947的博客 5604

ESP8266红外控制美的空调:从协议解析到Home Assistant集成

红外遥控技术作为一种经典的非接触式控制方式,其原理是通过红外发光二极管发射经过特定频率调制的光信号来传递指令。在物联网和智能家居领域,利用微控制器如ESP8266模拟红外信号,成为将传统家电接入智能生态系统的关键技术。这项技术通过精准解析和生成符合设备厂商私有协议的红外编码,实现了对空调、电视等设备的远程与自动化控制,具有很高的实用价值。本文聚焦于美的空调的红外协议,深入探讨了其脉冲宽度调制(PWM)和全量式编码的特点,并提供了基于ESP8266的完整解决方案,包括硬件连接、库函数API详解以及如何将其无缝

weixin_30718391的博客 402

Android 屏幕适配方案

转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/45460089; 本文出自:【张鸿洋的博客】 1、概述大家在Android开发时,肯定会觉得屏幕适配是个尤其痛苦的事,各种屏幕尺寸适配起来蛋疼无比。如果我们换个角度我们看下这个问题,不知道大家有没有了解过web前端开发,或者说大家对于网页都不陌生吧,其实适配的问题在we

Hongyang 41万+

深度学习100例-卷积神经网络(CNN)彩色图片分类 | 第2天

文章目录前期工作设置GPU导入数据标准化可视化构建CNN网络编译训练模型预测模型评估 前期工作 设置GPU 如果使用的是CPU可以忽略这步 import tensorflow as tf gpus = tf.config.list_physical_devices("GPU") if gpus: gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量

“365天深度学习训练营”报名进行中~ 7856

MATLAB - 使用 RRT 进行挖掘机运动规划

本例演示了如何使用运动规划器在包含障碍物的环境中为挖掘机规划路径。在此示例中,您将以运动树的简化形式为挖掘机建模,然后使用基于采样的运动规划器确定挖掘机在存在障碍物的两个姿势之间的可行路径。在 Simscape™ 多体™ 模型中模拟结果。

机器人控制与仿真应用 1852

【Android 屏幕适配】屏幕适配通用解决方案 ⑦ ( PercentRelativeLayout 百分比布局方案 | 该布局已废弃本方案仅做参考 )

一、PercentRelativeLayout 百分比布局方案、 二、将输出结果设置到组件标签中

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 1381

2023年高教社杯全国大学生数学建模竞赛 E 题:《黄河水沙监测数据分析》真题解析与 MATLAB 解决方案

🏆 本文已收录于专栏:《滚雪球学数学建模(含历年真题)》,持续更新最新及历年数学建模竞赛资料,每篇内容力求做到“问题讲透、模型讲清、代码可用、论文可参考”,帮助你从零开始逐步建立完整的数学建模知识体系。

**My Coding Family** 904

实用Android 屏幕适配方案分享

如果我们换个角度看下这个问题,不知道大家有没有了解过web前端开发,或者说对于网页都不陌生,其实适配的问题在web页面的设计中理论上也存在,为什么这么说呢?电脑的显示器的分辨率、包括手机分辨率,我敢说分辨率的种类远超过Android设备的分辨率,那么有一个很奇怪的现象:为什么Web页面设计人员从来没有说过,尼玛适配好麻烦?那么,到底是什么原因,让网页的设计可以在千差万别的分辨率的分辨率中依旧能给用户一个优质的体验呢?其实就是一个原因,网页提供了百分比计算大小。

2401_84912201的博客 751

【动态导通电阻】p-GaN HEMTs正向和反向导通下的动态导通电阻

2024年,浙江大学的研究团队通过多组双脉冲测试方法,深入研究了两种商用p-GaN栅极HEMTs在正向和反向导通模式下的动态导通电阻(RON)特性。研究发现,肖特基型p-GaN栅极HEMTs在反向导通时动态RON比正向导通高3%-5%,而混合漏极嵌入栅注入晶体管在反向导通时动态RON降级较轻,降低约10%。此外,随着漏极电流(IDS)的增加,两种器件在反向导通时动态RON先减小后增加,形成“谷底”现象。

荷塘阅色 827

AI安全护栏评测:TrustedExecBench如何量化评估大模型防护能力

在人工智能和机器学习领域,模型安全对齐是确保AI系统可靠、可控的核心技术。其原理是通过设计安全机制(即“护栏”)来约束模型行为,防止生成有害、偏见或越界内容。这项技术的核心价值在于平衡AI能力与风险管控,是可信AI部署的基石。应用场景广泛覆盖内容审核、对话系统、代码生成等需要人机交互的领域。本文聚焦的TrustedExecBench项目,正是针对“护栏”性能量化评估这一痛点,提供了一个标准化、自动化的开源评测框架。该工具通过构建多维度的恶意测试套件,并引入基于模型的动态评判机制,能够系统性地检验大语言模型在

weixin_42525428的博客 379

Android屏幕适配方案详解

Android手机的百花齐放的态势下,种类繁多的手机屏幕,也是给开发者带来了很大的工作量,屏幕适配是众多开发者老生长谈的话题,我曾经也是被屏幕适配工作弄的头大,所以今天把自己在工作中的使用的一些方法做个小小的总结; 什么是屏幕是适配?说白了就是屏幕分辨率的适配,其核心就在于两个字“缩放” 有哪些地方需要做适配呢? 1、布局适配 比如使用 wrap_co...

EugeneYoung的博客 1291

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Android屏幕适配 - 百分比(二)

接着上一篇:Android屏幕适配 - 百分比(一)既然我们能通过更改pt这个系统单位做到,拿为什么我们不能直接修改dp呢?方案二:直接替换系统单位dp上一篇我们有具体分析,只要修改这个方法的pt转化就可以做到全局适配,同样修改这个方法里面的dp也能做的。我们看到value * metrics.density,所以如果直接修改dp我们需要hook掉的其实是density。这个理论上看是ok的,dp...

Zhouxiaomeng0209的博客 921

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

上一篇: Android开发不申请权限来使用对应功能
下一篇: Instant Run详解
CharonChui
博客等级 码龄12年 66粉丝 134原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值