Android屏幕適配的幾種方案

開頭先了瞭解一下dp,px,dpi,ppi,縮放因子scaleDensity

這裏就以榮耀V9爲例 物理分辨率爲1440*2560,5.7英寸

dpi: 這個是系統指定的爲640

scaleDensity: scaleDensity=dpi/160由於dpi是系統指定所以scaleDensity=640/160=4

ppi: 每英寸所包含的像素點個數

邏輯分辨率 = (物理分辨率) / 縮放因子=360*640

渲染的分辨率又爲 邏輯分辨率*縮放因子=1440*2560

 

在我們Android裏面1dp = 1px*scaleDensity = 1px*dpi/160

所以榮耀V9手機: 1dp = 1px*640/160=4px;  假如我們在佈局裏面輸出360dp的話那麼渲染到屏幕上就是4*360個像素點=1440屏幕的寬度.

在很多公司的切圖都是以IOS的750作爲切圖標準,而且只切一套圖,這個時候就可以根據修改上面的dpi的 density來達到適配

density = 1440/750=1.92

dpi =density*160=307.2

這樣邏輯分辨率就爲(1440*2560)/1.92=750*1333

那麼當輸入390dp的時候會顯示多少像素呢?

1dp = 1px*scaleDensity這個公式不變

1dp = 1.92px;  390dp = 748.8px這只是邏輯寬度 實際渲染的寬度就等於748.8*1.92=1437.696 相當於一個屏幕寬度

 

1:圖片資源適配:.9圖和SVG圖片進行縮放

2:佈局適配

3:像素適配

4:限定符適配:比如分辨率限定符 xhdpi xxhdpi xxxhdpi 尺寸限定符:layout_small, layout_large...

5:最小屏幕限定符:values-sw360dp,values-sw384dp,... 屏幕方向限定符:layout_land, layout_port

6:百分比適配

7:改變DisplayMetrics參數適配

百分比適配:

PercentLayout extends RelativeLayout 並自定義幾個百分比屬性,比如width,height,margin等

重寫其onMeasure方法,獲取到所有子類,並獲取子類的百分比屬性,根據控件的寬高和百分比係數轉換成真正的寬高最後達到最終適配.

 

像素適配  ScreenLayout extends RelativeLayout

像素適配就是直接輸入Px的適配也叫跟據屏幕比來適配,

原理:我們要跟據UI妹妹給的切圖的尺寸來當做一個標準,比如很多都用IOS的750 ,然後我們通過代碼獲取到Android手機屏幕寬高,這二個數字對比得到一個比例,然後在onMeasure裏面對子View的寬高等參數進行重新計算

 

改變DisplayMetrics參數適配

DisplayMetrics裏面有幾個重要參數

density:屏幕密度->以每一寸有160個像素點爲基礎 density=1;,如果每一寸有320個像素點的話,那麼density=2

scaleDensity:字體縮放比例,默認情況下density = scaleDensity;

densityDpi:屏幕上每一英寸像素點有多不個,就是160或者320... Android最終都是以px來顯示的

因爲Android裏面的大小都是通過px來顯示的,可以從

TypeValue裏面的public static float applyDimension(...)這個方法可以看出
    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;
    }

所以我們改變裏面幾個參數就可以達到完美適配

獲取DisplayMetrics方法,Resources裏面的getDisplayMetrics()

然後獲取

appDensity = displayMetrics.density;
appScaleDensity = displayMetrics.scaledDensity;

我們這可以跟據我們自己設定的一個設置稿的寬高,比如

public static final float WIDTH = 384;

這樣我們就可以自己計算出自己想要的density;  targetDensity =displayMetrics.widthPixels / WIDTH

自己想要的scaleDensity字體縮放比例

targetDensity * (appScaleDensity / appDensity)

計算出自己的dpi: targetDensityDpi = (int) (targetDensity * 160);

最後就是將計算出來的進行替換

//替換系統的值
DisplayMetrics dm = activity.getResources().getDisplayMetrics();
dm.density = targetDensity;
dm.densityDpi = targetDensityDpi;
dm.scaledDensity = targetScaleDensity;

因爲系統字體可放大放小,可以還要監聽字體改變

            //添加字體變換監聽
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    if (newConfig != null && newConfig.fontScale > 0) {
                        //字體發生更改,重新對appScaleDensity賦值
//                        appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });

最終代碼如下:

package com.ancely.desy;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

/*
 *  @項目名:  px
 *  @包名:    com.ancely.desy
 *  @文件名:   PercentLayout
 *  @描述:    百分比佈局的實現
 */
public class PercentLayout extends RelativeLayout {
    public PercentLayout(Context context) {
        super(context);
    }

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

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

    public static class LayoutParams extends RelativeLayout.LayoutParams {
        private float widthPresent;
        private float heightPresent;
        private float leftMarginPresent;
        private float rightMarginPresent;
        private float topMarginPresent;
        private float bottomMarginPresent;

        LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            //解析自定義屬性

            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
            widthPresent = a.getFloat(R.styleable.PercentLayout_Layout_widthPresent, 0);
            heightPresent = a.getFloat(R.styleable.PercentLayout_Layout_heightPresent, 0);
            leftMarginPresent = a.getFloat(R.styleable.PercentLayout_Layout_margintLeftPresent, 0);
            rightMarginPresent = a.getFloat(R.styleable.PercentLayout_Layout_margintRightPresent, 0);
            topMarginPresent = a.getFloat(R.styleable.PercentLayout_Layout_margintTopPresent, 0);
            bottomMarginPresent = a.getFloat(R.styleable.PercentLayout_Layout_margintBottomPresent, 0);
            a.recycle();
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            ViewGroup.LayoutParams params = child.getLayoutParams();
            if (checkLayoutParams(params)) {
                LayoutParams lp = (LayoutParams) params;
                float widthPresent = lp.widthPresent;
                float heightPresent = lp.heightPresent;
                float leftMarginPresent = lp.leftMarginPresent;
                float rightMarginPresent = lp.rightMarginPresent;
                float topMarginPresent = lp.topMarginPresent;
                float bottomMarginPresent = lp.bottomMarginPresent;
                if (widthPresent > 0) {
                    lp.width = (int) (widthSize * widthPresent);
                }
                if (heightPresent > 0) {
                    lp.height = (int) (heightSize * heightPresent);
                }
                if (leftMarginPresent > 0) {
                    lp.leftMargin = (int) (widthSize * leftMarginPresent);
                }
                if (widthPresent > 0) {
                    lp.rightMargin = (int) (widthSize * rightMarginPresent);
                }
                if (widthPresent > 0) {
                    lp.topMargin = (int) (heightSize * topMarginPresent);
                }
                if (widthPresent > 0) {
                    lp.bottomMargin = (int) (heightSize * bottomMarginPresent);
                }
            }
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章