複数セットのdimensまたは複数セットのlayoutを設定しない場合に、異なるサイズのスクリーンを適切に配置する方法

7917 ワード

前言
Androidの開発の過程で、私たちは異なるスクリーンを適切にする時、一般的にandroidの基準に従って、異なるlayoutを設定したり、異なるvalues-フォルダの下に異なるdimensを設定して需要を満たしたりしますが、ここでは繰り返しの労動があり、効率が特に高くありません.だから、最初はUI設計の標準に従って、ある画面の下の標準のdimensを設定して、それから自動化のスクリプトを書いて、異なる画面のパラメータによって異なるdimensを自動化して生産しますが、面倒です.だからソースコードの角度から見て、何か実行可能な考えがあると思います.
ソース分析
私たちはlayoutを書く時xmlの中でいくつかの属性を設定して、例えばあるコントロールの幅、高さなど、一般的にdip単位で、Viewを例にして、私たちはソースコードを追いかけます:Viewの構造関数の中で、このコードがあります:
public View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        this(context);

        final TypedArray a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
        //      ......
        final int N = a.getIndexCount();
        for (int i = 0; i < N; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case com.android.internal.R.styleable.View_padding:
                    padding = a.getDimensionPixelSize(attr, -1);
                    mUserPaddingLeftInitial = padding;
                    mUserPaddingRightInitial = padding;
                    leftPaddingDefined = true;
                    rightPaddingDefined = true;
                    break;
                 case com.android.internal.R.styleable.View_paddingLeft:
                    leftPadding = a.getDimensionPixelSize(attr, -1);
                    mUserPaddingLeftInitial = leftPadding;
                    leftPaddingDefined = true;
                    break;
                 //      ......
            }
        }
    }

OK、paddingを取得する東があります.TypedArrayのgetDimensionPixelSize()の方法を見てみましょう.
public int getDimensionPixelSize(int index, int defValue) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }

        index *= AssetManager.STYLE_NUM_ENTRIES;
        final int[] data = mData;
        final int type = data[index+AssetManager.STYLE_TYPE];
        if (type == TypedValue.TYPE_NULL) {
            return defValue;
        } else if (type == TypedValue.TYPE_DIMENSION) {
            return TypedValue.complexToDimensionPixelSize(
                data[index+AssetManager.STYLE_DATA], mMetrics);
        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
            throw new RuntimeException("Failed to resolve attribute at index " + index);
        }

        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
                + Integer.toHexString(type));
    }

OK、私たちは知っています.dipは画素に関係なく、比ですか.それはスクリーンのdensityと関係があるに違いありません.ちょうどmMetricsがあります.中でどのように実現するかを見てみましょう.
public static int complexToDimensionPixelSize(int data,
            DisplayMetrics metrics) {
        final float value = complexToFloat(data);
        final float f = applyDimension(
                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
                value,
                metrics);
        final int res = (int)(f+0.5f);
        if (res != 0) return res;
        if (value == 0) return 0;
        if (value > 0) return 1;
        return -1;
    }

次に下を見ます.
public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics) {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }

OK、ここだからmetricsを変えるだけだ.densityの値は、画面全体にわたって空間の大きさが変化しているに違いありません.でもどうやって変えるの?
解決策
コンフィギュレーションというクラスを見てみましょう.
public void updateConfiguration(Configuration config,
            DisplayMetrics metrics, CompatibilityInfo compat) {
        synchronized (mAccessLock) {
            if (false) {
                Slog.i(TAG, "**** Updating config of " + this + ": old config is "
                        + mConfiguration + " old compat is " + mCompatibilityInfo);
                Slog.i(TAG, "**** Updating config of " + this + ": new config is "
                        + config + " new compat is " + compat);
            }
            if (compat != null) {
                mCompatibilityInfo = compat;
            }
            if (metrics != null) {
                mMetrics.setTo(metrics);
            }
            // NOTE: We should re-arrange this code to create a Display
            // with the CompatibilityInfo that is used everywhere we deal
            // with the display in relation to this app, rather than
            // doing the conversion here.  This impl should be okay because
            // we make sure to return a compatible display in the places
            // where there are public APIs to retrieve the display...  but
            // it would be cleaner and more maintainble to just be
            // consistently dealing with a compatible display everywhere in
            // the framework.
            mCompatibilityInfo.applyToDisplayMetrics(mMetrics);

            final int configChanges = calcConfigChanges(config);
            if (mConfiguration.locale == null) {
                mConfiguration.locale = Locale.getDefault();
                mConfiguration.setLayoutDirection(mConfiguration.locale);
            }

           //  *    ,   ,   ,    ,   ,   ...
            if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
                mMetrics.densityDpi = mConfiguration.densityDpi;
                mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
            }
            mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;

            String locale = null;
            if (mConfiguration.locale != null) {
                locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
            }

            final int width, height;
            if (mMetrics.widthPixels >= mMetrics.heightPixels) {
                width = mMetrics.widthPixels;
                height = mMetrics.heightPixels;
            } else {
                //noinspection SuspiciousNameCombination
                width = mMetrics.heightPixels;
                //noinspection SuspiciousNameCombination
                height = mMetrics.widthPixels;
            }
            //      ......
            mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
                    locale, mConfiguration.orientation,
                    mConfiguration.touchscreen,
                    mConfiguration.densityDpi, mConfiguration.keyboard,
                    keyboardHidden, mConfiguration.navigation, width, height,
                    mConfiguration.smallestScreenWidthDp,
                    mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
                    mConfiguration.screenLayout, mConfiguration.uiMode,
                    Build.VERSION.RESOURCES_SDK_INT);
            //      ......
    }

so、順調に章を作ったのは、次の方法があります.
public class ConfigurationDPIUtil {
    public static void initDpi(Context context) {
        DisplayMetrics displayMetrics =  context.getResources().getDisplayMetrics();
        Configuration configuration = context.getResources().getConfiguration();
        configuration.densityDpi = DisplayMetrics.DENSITY_XHIGH ;//densityDpi    ,     dp   pix   
        context.getResources().updateConfiguration(configuration,displayMetrics);
    }

}

まとめ
仕事をするには、考えが必要で、考えがないのは自分の知識の枠組みが構築されていないためで、自分の実践と総括が不十分だ.