Android屏幕適配

  1. px適配名斟;
  2. 百分比適配池凄;
  3. 修改dp適配;

屏幕適配

  • 布局適配
    • 使用wrap_content阳藻,match_parent晰奖;
    • LinearLayout xxx:layout_weight="1"
    • RelativeLayout xxx:layout_centerParent="true"
    • ContraintLayout xxx:layout_constraintLeft_toLeftOf="parent"
    • Percent-support-lib xxx:layout_widthParcent="30%"
  • 圖片資源適配
    • .9圖或者SVG圖實(shí)現(xiàn)縮放
    • 備用位圖匹配不同的分辨率
  • 用戶流程適配
    • 根據(jù)業(yè)務(wù)邏輯執(zhí)行不同的邏輯跳轉(zhuǎn)
    • 根據(jù)別名展示不同的頁面
  • 限定符適配
    • 分辨率限定符 drawable-hdpi,drawable-xhdpi,...
    • 尺寸限定符 layout-small,layout-large,...
    • 最小寬度限定符 values-sw360dp,values-sw84dp,...
    • 屏幕方向限定符 layout-land,layout-port
  • 劉海屏適配
    • Android 9.0 官方適配
    • 華為,Oppo腥泥,Vivo

自定義view中像素適配

  • 以一個特定寬度尺寸的設(shè)備為參考匾南,在view的加載過程,根據(jù)當(dāng)前設(shè)備的實(shí)際像素?fù)Q算出目標(biāo)像素蛔外,再作用在控件上蛆楞。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    if(!flag){
        float scaleX = Utils.getInstance(getContext()).getHorizontalScale();//獲取橫向的縮放比例
        float scaleY = Utils.getInstance(getContext()).getVerticalScale();//獲取豎向的縮放比例
        for(int i = 0; i < getChildCount(); i++){
            View child = getChildAt(i);
            LayoutParams lp = (LayoutParams)child.getLayoutParams();
            lp.width = (int) (lp.width * scaleX);//換算寬度目標(biāo)值
            lp.height = (int)(lp.height * scaleY);//換算高度的目標(biāo)值
            lp.topMargin = (int)(lp.topMargin * scaleY);//換算四周間距的目標(biāo)值
            ......
          }
        flag = true;
    }
    super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}

縮放比例 是當(dāng)前設(shè)備的像素值和參考值比值的結(jié)果;布局中是px來寫布局夹厌;

百分比布局適配

  • 以父容器尺寸作為參考臊岸,在view的加載過程,根據(jù)當(dāng)前父容器實(shí)際尺寸換算出目標(biāo)尺寸尊流,再作用在view上帅戒。
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    //獲取父容器的寬高
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    ...
    for(int i = 0; i < getChildCount(); i++){
        View child = getChildAt(i);//重寫設(shè)置子view的布局屬性,再進(jìn)行view的測量
        LayoutParams lp = (LayoutParams)child.getLayoutParams();
        float widthPercent = ((LayoutParams).lp).widthPercent;//自定義百分比屬性
        if(widthPercent > 0){
            lp.width = (int) width * widthPercent;//設(shè)置當(dāng)前view在父容器中的尺寸占比
        }
        ...
    }
    super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}

注意:需要參考RelativeLayout中的靜態(tài)類LayoutParams實(shí)現(xiàn)自定義容器布局崖技;
view的加載過程逻住,view是通過容器view來進(jìn)行加載;

  1. 創(chuàng)建自定義屬性
  2. 繼承 static LayoutParams迎献,解析自定義屬性瞎访;
  3. 必須重寫generateLayoutParams(AttributeSet attrs)方法;
  4. 重寫onMeasure吁恍,計(jì)算扒秸;

像素密度適配

  • 修改density,scaleDensity冀瓦,densityDpi值,直接更改系統(tǒng)內(nèi)部對于目標(biāo)尺寸而言的像素密度伴奥。

px : 其實(shí)就是像素單位,比如我們通常說的手機(jī)分辨列表800*400都是px的單位
sp : 同dp相似翼闽,還會根據(jù)用戶的字體大小偏好來縮放
dp : 虛擬像素拾徙,在不同的像素密度的設(shè)備上會自動適配
dip: 同dp
要理解dp,首先要先引入dpi這個概念感局,dpi全稱是dots per inch尼啡,對角線每英寸的像素點(diǎn)的個數(shù)暂衡,
而dp也叫dip,是device independent pixels崖瞭。設(shè)備不依賴像素的一個單位狂巢。在不同的像素密度的設(shè)備上會自動適配,比如:
在320x480分辨率书聚,像素密度為160,1dp=1px
在480x800分辨率唧领,像素密度為240,1dp=1.5px
計(jì)算公式:px = dp * (dpi/160)

private static final float WIDTH = 360;//參考像素密度
protected void setDensity(final Application application,Activity activity){
    DisplayMetics appDisplayMetrics = application.getResources().getDisplayMetrics();
    //獲取目標(biāo)density值
    ...
    float targetDensity = appDisplayMetrics.widthPixels / WIDTH;
    float targetScaleDensity = targetDensity * (appScaledDensity / appDensity);
    int targetDensityDpi = (int)(targetDensity * 160);
    //替換Activity的density,scaleDensity等值
    DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
    displayMetrics.density = targetDensity;
    displayMetrics.scaledDensity = targetScaleDensity;
    displayMetrics.densityDpi = targetDensityDpi;
}
//可運(yùn)行代碼
 private static final float WIDTH = 360;//參考屏幕的寬寺惫,單位是dp  設(shè)計(jì)稿的大小
 private static float appDensity;//表示屏幕的密度
 private static float appScaledDensity;//表示字體縮放比例 默認(rèn)是density
public static void setDensity(final Application application, Activity activity){
        final DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
        if (appDensity == 0){
            appDensity = displayMetrics.density;
            appScaledDensity = displayMetrics.scaledDensity;
            //監(jiān)聽系統(tǒng) 字體變化
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    if (newConfig != null && newConfig.fontScale > 0){
                        appScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }
                @Override
                public void onLowMemory() {
                }
            });
        }
        //目標(biāo)的值
        float targetDensity = displayMetrics.widthPixels / WIDTH;// 1080 / 360 = 3.0  --- 類比 160 /160 = 1
        float targetScaledDensity = targetDensity * (appScaledDensity / appDensity);
        int targetDensityDpi = (int) (targetDensity * 160);
        //需要修改的 值
        DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
        metrics.density = targetDensity;
        metrics.scaledDensity = targetScaledDensity;
        metrics.densityDpi = targetDensityDpi;
    }

劉海屏適配

  • Android官方9.0劉海屏適配策略
    • 如果非全屏模式(有狀態(tài)欄)疹吃,則app不受劉海屏的影響蹦疑,劉海屏的高度就是狀態(tài)欄的高西雀;
    • 如果全屏模式,app未適配劉海屏歉摧,則系統(tǒng)會對界面做特殊處理艇肴,豎屏向下移動,橫屏向右移動
  • 全屏模式下劉海區(qū)黑邊(內(nèi)容區(qū)域下挫)問題叁温。

    //1.全屏設(shè)置
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    window.setFlags(LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParamsWindowManager.FLAG_FULLSCREEN);
    //2.讓內(nèi)容區(qū)域延伸至劉海區(qū)再悼,需要先判斷是否有劉海
    WindowManager.LayoutParams params = window.getAttributes();
    params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
    window.setAttributes();
    //3.沉浸式
    int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
    int visibility = view.getDecorView().getSystemUiVisibility();
    visibility |= flags;
    window.getDecorView().setSystemUiVisibility(visibility);

  • 避開劉海區(qū)域

//獲取劉海高度,通常情況劉海的高度不會超過狀態(tài)欄高度
int height = getStatusBarHeight();
//設(shè)置控件的margin
//設(shè)置父容器的padding

  • 其他廠商的適配

部分代碼參考

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //1 設(shè)置全屏
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        Window window = getWindow();
        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        if (hasCutout(window)) {
            //2 內(nèi)容可以延伸進(jìn)劉海
            WindowManager.LayoutParams attributes = window.getAttributes();
            /*
             * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 默認(rèn)
             * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES  可以擴(kuò)展
             * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 不允許
             */
            attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
            //3.沉浸式設(shè)置
            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;//虛擬導(dǎo)航欄
            int visibility = window.getDecorView().getSystemUiVisibility();//系統(tǒng)的顯示類型
            visibility |= flags;
            window.getDecorView().setSystemUiVisibility(visibility);
        }
        setContentView(R.layout.activity_cutout);
        //1.判斷手機(jī)廠商  2.判斷是否有劉海  3.是否內(nèi)容延伸進(jìn)劉海  4.是否內(nèi)容 避開劉海  5膝但、獲取劉海高
    }

    @TargetApi(Build.VERSION_CODES.P)
    boolean hasCutout(Window window) {
        View decorView = window.getDecorView();
        WindowInsets rootWindowInsets = decorView.getRootWindowInsets();
        DisplayCutout displayCutout = rootWindowInsets.getDisplayCutout();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && displayCutout != null) {
            if (displayCutout.getBoundingRects() != null && displayCutout.getBoundingRects().size() > 0 && displayCutout.getSafeInsetTop() > 0) {
                return true;
            }
        }
        return false;
    }

private int getStatusBarHeight(Context context){
        int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resId > 0){
            return context.getResources().getDimensionPixelSize(resId);
        }
        return 0;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末冲九,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子跟束,更是在濱河造成了極大的恐慌莺奸,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冀宴,死亡現(xiàn)場離奇詭異灭贷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)略贮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門甚疟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人逃延,你說我怎么就攤上這事览妖。” “怎么了揽祥?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵黄痪,是天一觀的道長。 經(jīng)常有香客問我盔然,道長桅打,這世上最難降的妖魔是什么是嗜? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮挺尾,結(jié)果婚禮上鹅搪,老公的妹妹穿的比我還像新娘。我一直安慰自己遭铺,他們只是感情好丽柿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著魂挂,像睡著了一般甫题。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涂召,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天坠非,我揣著相機(jī)與錄音,去河邊找鬼果正。 笑死炎码,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秋泳。 我是一名探鬼主播潦闲,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼迫皱!你這毒婦竟也來了歉闰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤卓起,失蹤者是張志新(化名)和其女友劉穎和敬,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體既绩,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡概龄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饲握。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片私杜。...
    茶點(diǎn)故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖救欧,靈堂內(nèi)的尸體忽然破棺而出衰粹,到底是詐尸還是另有隱情,我是刑警寧澤笆怠,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布铝耻,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瓢捉。R本人自食惡果不足惜频丘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泡态。 院中可真熱鬧搂漠,春花似錦、人聲如沸某弦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靶壮。三九已至怔毛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腾降,已是汗流浹背拣度。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜂莉,地道東北人蜡娶。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓捏膨,卻偏偏與公主長得像秉宿,于是被迫代替她去往敵國和親淌山。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容