第六章 Android 繪圖機(jī)制與屏幕適配

Android 群英傳筆記
第一章Android體系與系統(tǒng)架構(gòu)
第二章 Android開(kāi)發(fā)工具及技巧
第三章 Android控件架構(gòu)與事件攔截機(jī)制
第四章 ListView 使用技巧
第五章 Android Scroll 分析
第六章 Android 繪圖機(jī)制與屏幕適配
第七章 Android 動(dòng)畫(huà)機(jī)制與使用技巧
第八章 Activity與Activity調(diào)用棧分析
第九章 Android 系統(tǒng)信息與安全機(jī)制
第十章 Android性能優(yōu)化
本文出自:
http://www.reibang.com/u/a1251e598483

屏幕適配的知識(shí)
  • 屏幕大小

指屏幕對(duì)角線的長(zhǎng)度,通常使用 "寸"來(lái)度量 ,通常的4.7寸,5.5寸手機(jī),現(xiàn)如今各家推出的 帶劉海的手機(jī),計(jì)算規(guī)則相對(duì)復(fù)雜點(diǎn)

  • 分辨率

分辨率是指實(shí)際屏幕的像素點(diǎn)個(gè)數(shù)峻凫,例如720X1280就是指屏幕的分辨率陨晶,寬有720個(gè)像素點(diǎn)泌绣,高有1280個(gè)像素點(diǎn)

  • PPI

每英寸像素又稱為DPI柜候,他是由對(duì)角線的的像素點(diǎn)數(shù)除以屏幕的大小所得柿祈,通常有400PPI就已經(jīng)很6了

系統(tǒng)屏幕密度

每個(gè)廠商的安卓手機(jī)具有不同的大小尺寸和像素密度的屏幕仗岖,安卓系統(tǒng)如果要精確到每種DPI的屏幕慰丛,基本上是不可能的,因此系統(tǒng)定義了幾個(gè)標(biāo)準(zhǔn)的DPI

Android手機(jī)分辨率
  • 獨(dú)立像素密度dp

這是由于各種屏幕密度的不同,導(dǎo)致同樣像素大小的長(zhǎng)度,在不同密度的屏幕上顯示長(zhǎng)度不同站蝠,因此相同長(zhǎng)度的屏幕汰具,高密度的屏幕包含更多的像素點(diǎn),在安卓系統(tǒng)中使用mdpi密度值為160的屏幕作為標(biāo)準(zhǔn)沉衣,在這個(gè)屏幕上,1px = 1dp减牺,其他屏幕則可以通過(guò)比例進(jìn)行換算豌习,例如同樣是100dp的長(zhǎng)度,mdpi中為100px拔疚,而在hdpi中為150肥隆,我們也可以得出在各個(gè)密度值中的換算公式,在mdpi中 1dp = 1px, 在hdpi中稚失, 1dp = 1.5px栋艳,在xhdpi中,1dp = 2px,在xxhdpi中1dp = 3px,由此可見(jiàn)句各,我們換算公式 l:m:h:xh:xxh = 3:4:6:8:12

  • 安卓手機(jī)對(duì)于每類手機(jī)屏幕大小都有一個(gè)相應(yīng)的屏幕像素密度:
| 密度類型 | 代表的分辨率(px) | 屏幕像素密度(dpi)|
| ------------- |:-------------:|
| 低密度(ldpi) | 240x320 | 120 |
| 中密度(mdpi) | 320x480 | 160 |
| 高密度(hdpi) | 480x800 | 240|
| 超高密度(xhdpi) | 720x1280 | 320|
| 超超高密度(xxhdpi) | 1080x1920 | 480 |

  • 屏幕尺寸吸占、分辨率、像素密度三者關(guān)系

一部手機(jī)的分辨率是寬x高凿宾,屏幕大小是以寸為單位矾屯,那么三者的關(guān)系是:

image
  • 密度無(wú)關(guān)像素

含義:density-independent pixel,叫dp或dip初厚,與終端上的實(shí)際物理像素點(diǎn)無(wú)關(guān)件蚕。
單位:dp,可以保證在不同屏幕像素密度的設(shè)備上顯示相同的效果
Android開(kāi)發(fā)時(shí)用dp而不是px單位設(shè)置圖片大小产禾,是Android特有的單位
場(chǎng)景:假如同樣都是畫(huà)一條長(zhǎng)度是屏幕一半的線排作,如果使用px作為計(jì)量單位,那么在480x800分辨率手機(jī)上設(shè)置應(yīng)為240px亚情;在320x480的手機(jī)上應(yīng)設(shè)置為160px妄痪,二者設(shè)置就不同了;如果使用dp為單位楞件,在這兩種分辨率下拌夏,160dp都顯示為屏幕一半的長(zhǎng)度。

  • 獨(dú)立比例像素

含義:scale-independent pixel履因,叫sp或sip
單位:sp

  • 單位換算

在程序中障簿,我們可以非常方便地對(duì)一些單位的換算,下面的代碼給出了一種換算的方法我們可以把這些代碼作為工具類保存在項(xiàng)目中

package com.younger.testyounger;

import android.content.Context;


/**
 * dp栅迄,sp轉(zhuǎn)換成px的工具類
 * Created by lgl on 16/3/23.
 */
public class DisplayUtils {

    /**
     * 將px值轉(zhuǎn)換成dpi或者dp值站故,保持尺寸不變
     *
     * @param content
     * @param pxValus
     * @return
     */
    public static int px2dip(Context content, float pxValus) {
        final float scale = content.getResources().getDisplayMetrics().density;
        return (int) (pxValus / scale + 0.5f);
    }

    /**
     * 將dip和dp轉(zhuǎn)化成px,保證尺寸大小不變。
     *
     * @param content
     * @param pxValus
     * @return
     */
    public static int dip2px(Context content, float pxValus) {
        final float scale = content.getResources().getDisplayMetrics().density;
        return (int) (pxValus / scale + 0.5f);
    }

    /**
     * 將px轉(zhuǎn)化成sp,保證文字大小不變。
     *
     * @param content
     * @param pxValus
     * @return
     */
    public static int px2sp(Context content, float pxValus) {
        final float fontScale = content.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValus / fontScale + 0.5f);
    }

    /**
     * 將sp轉(zhuǎn)化成px,保證文字大小不變西篓。
     *
     * @param content
     * @param pxValus
     * @return
     */
    public static int sp2px(Context content, float pxValus) {
        final float fontScale = content.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValus / fontScale + 0.5f);
    }
}

其實(shí)的density就是前面所說(shuō)的換算比例愈腾,這里使用的是公式換算方法進(jìn)行轉(zhuǎn)換,同時(shí)系統(tǒng)也提供了TypedValue幫助我們轉(zhuǎn)換

    /**
     * dp2px
     * @param dp
     * @return
     */
    protected int dp2px(int dp){
        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,getResources().getDisplayMetrics());
    }

    /**
     * sp2px
     * @param dp
     * @return
     */
    protected int sp2px(int sp){
        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,sp,getResources().getDisplayMetrics());
    }

關(guān)于Android 屏幕適配,我目前在項(xiàng)目中實(shí)際用到過(guò)的有三種

  • 1,在項(xiàng)目res 目錄下 建 dimens-ldpi,dimens-hdpi,dimens-xhdpi,dimens-xxhdpi 不同的文件夾 ,在各自里面都定義 dimens 文件, 然后再 根據(jù) l:m:h:xh:xxh = 3:4:6:8:12 這個(gè)比例換算 ,比如 以hdpi 應(yīng)該是12dp的話為參照的話, mdpi 要是為 8dp,xh為16dp,xxh 為24dp , 也就是在布局文件中起一個(gè)dimens的名稱,在各個(gè)不同分辨率的文件中,創(chuàng)建相同的文件名的dimens ,但是他們的實(shí)際的值 就是上面換算的值,上面說(shuō)的是布局的寬高大小,不是字體,其實(shí)字體也可以按照這種方式適配,字體寫(xiě)成dp 不寫(xiě)sp 可以避免用戶主動(dòng)調(diào)整系統(tǒng)字體,造成App 顯示出來(lái)特別丑的可以這樣做,我看微信好像就可以,你跳大字體,微信的字體也不變,但是你可以在微信的設(shè)置里設(shè)置微信的字體大小..這樣寫(xiě)雖可以適配,但是有時(shí)候還是有誤差, UI 在對(duì)還原度的時(shí)候再適當(dāng)調(diào)整就好了,但就是計(jì)算太累的,麻煩,我記得好像有大神做了一個(gè)插件,可以一鍵生成.這個(gè)還好;
  • 2,中間一度使用UI 給的Hdpi 的數(shù)值,直接在布局文件中寫(xiě)上 多少dp 好像寬度,高度之類的也沒(méi)有太大的問(wèn)題,讓我一度認(rèn)為 第一種的適配的方式還有沒(méi)有必要存在,
    -3, 目前使用的是 鴻洋大神的 Autolayout 號(hào)稱屏幕適配的終結(jié)者,目前已不維護(hù),但是使用方便,目前在實(shí)際使用中,并沒(méi)有發(fā)現(xiàn)問(wèn)題,推薦使用;
    使用的就是百分比 適配,根據(jù)不同的屏幕 動(dòng)態(tài)設(shè)置 大小,開(kāi)發(fā)者在布局文件中直接使用 px 就行了 ,簡(jiǎn)直不要太方便

這個(gè)px并不代表1像素岂津,我在內(nèi)部會(huì)進(jìn)行百分比化處理虱黄,也就是說(shuō):720px高度的屏幕,你這里填寫(xiě)72px吮成,占據(jù)10%橱乱;當(dāng)這個(gè)布局文件運(yùn)行在任何分辨率的手機(jī)上,這個(gè)72px都代表10%的高度粱甫,這就是本庫(kù)適配的原理泳叠。

詳細(xì)可以參考鏈接文章.

鴻洋大神的 AutoLayout 可以看 http://blog.csdn.net/lmj623565791/article/details/49990941

關(guān)于屏幕適配的文章 可以參考 http://www.reibang.com/p/ec5a1a30694b

繪圖的內(nèi)容,可以查看 系列文章 http://www.reibang.com/p/bd153dfc0095

SurfaceView

  • SurfaceView和View的區(qū)別

Android系統(tǒng)提供了VieW進(jìn)行繪圖處理, vieW可以滿足大部分的繪圖需求,但在某些時(shí)卻也有些心有余而力不足,特別是在進(jìn)行一些開(kāi)發(fā)的時(shí)候茶宵。 我們知道,VieW通過(guò)刷新來(lái)視圖, Android系統(tǒng)通過(guò)發(fā)出VSYNC信號(hào)來(lái)進(jìn)行屏幕的重繪, 刷新的間隔時(shí)間為I6ms危纫。在16ms內(nèi)View完成了你所需要執(zhí)行的所有操作,那么用戶在視覺(jué)上, 就不會(huì)產(chǎn)生卡頓的感覺(jué):而如果執(zhí)行的操作邏輯太多,特別是需要頻繁刷新的界面上,例如游戲界面,那么就塞主線程乌庶,從而導(dǎo)致畫(huà)面卡頓种蝶,很多時(shí)候,在自定義VieW的Log中經(jīng)常會(huì)看見(jiàn)如下示的警告

Skipped 47 frames! The application may be doing too much work on its main thread

這些警告的產(chǎn)生,很多情況下就是因?yàn)樵诶L制過(guò)程中, 處理邏輯太多造成的為了避免這一問(wèn)題的產(chǎn)生一 Android系統(tǒng)提供了surfacevicW組件來(lái)解決這個(gè)問(wèn)題,可以說(shuō)是VieW的孿生兄弟,但它與View還是有所不同的,它們的區(qū)別主要體現(xiàn)

  • 1.View主要用于自動(dòng)更新的情況下,而surfaceVicw主要適用于被動(dòng)更新,例如頻繁刷新
  • 2.View在主線程中刷新瞒大,而surfaceView通常會(huì)通過(guò)一 個(gè)子線程來(lái)進(jìn)行頁(yè)面刷新蛤吓。
  • 3.View在繪制的時(shí)候沒(méi)有雙緩沖機(jī)制,而surfaceVicw在底層實(shí)現(xiàn)機(jī)制中就已經(jīng)實(shí)現(xiàn)了雙緩沖機(jī)制;

總結(jié)起來(lái)糠赦,如果你的自定義View需要頻繁刷新,或者刷新時(shí)數(shù)據(jù)處理量比較大会傲,那么你就可以考慮使用surfaceVicw取代View了

surfaceView的使用

  • 創(chuàng)建surfaceView

創(chuàng)建自定義的surfaceView,實(shí)現(xiàn)它的兩個(gè)接口

public class SurfaView extends SurfaceView implements SurfaceHolder.Callback,Runnable

通過(guò)實(shí)現(xiàn)它的兩個(gè)幾口拙泽,就會(huì)有三個(gè)回調(diào)方法

  • 初始化SurfaceView
  • 使用SurfaceView

代碼實(shí)現(xiàn),Android 觸摸畫(huà)圖 下面是View 的實(shí)現(xiàn)代碼

package com.example.younger.youngertest;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by Younger on 2018/3/7.
 */
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder mHolder;

    private Canvas mCanvas;

    private boolean mIsDrawing;

    private int x=100;
    private int y=0;

    private Path path;
    private Paint paint;

    public MySurfaceView(Context context) {
        super(context);
        init();
    }

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

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

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }


    private void init() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
        paint = new Paint();
        path = new Path();
        paint.setColor(Color.parseColor("#112211"));
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(40);
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {

        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        mIsDrawing = false;
    }

    @Override
    public void run() {

       long start = System.currentTimeMillis();

        while (mIsDrawing) {
            draw();
        }
        long end = System.currentTimeMillis();

        if (end-start<100){

            try {
                Thread.sleep(100-(end-start));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();

            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(path,paint);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mCanvas != null) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                path.moveTo(x, y);
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(x,y);
                break;
            case MotionEvent.ACTION_UP:
                break;

        }
        return true;
    }
}

好了,關(guān)于Android 繪圖只知識(shí), 和屏幕適配基本就先寫(xiě)到這,如果以后有新的內(nèi)容,會(huì)繼續(xù)補(bǔ)充

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末淌山,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子顾瞻,更是在濱河造成了極大的恐慌泼疑,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荷荤,死亡現(xiàn)場(chǎng)離奇詭異退渗,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蕴纳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門会油,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人古毛,你說(shuō)我怎么就攤上這事翻翩《夹恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵嫂冻,是天一觀的道長(zhǎng)胶征。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上读整,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布急波。 她就那樣靜靜地躺著从铲,像睡著了一般瘪校。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上名段,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天阱扬,我揣著相機(jī)與錄音,去河邊找鬼伸辟。 笑死麻惶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的信夫。 我是一名探鬼主播窃蹋,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼静稻!你這毒婦竟也來(lái)了警没?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤振湾,失蹤者是張志新(化名)和其女友劉穎杀迹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體押搪,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡树酪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了大州。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片续语。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厦画,靈堂內(nèi)的尸體忽然破棺而出绵载,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布娃豹,位于F島的核電站焚虱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏懂版。R本人自食惡果不足惜鹃栽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望躯畴。 院中可真熱鬧民鼓,春花似錦、人聲如沸蓬抄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嚷缭。三九已至饮亏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阅爽,已是汗流浹背路幸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留付翁,地道東北人简肴。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像百侧,于是被迫代替她去往敵國(guó)和親砰识。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,118評(píng)論 25 707
  • 本文參考自: Google的官方權(quán)威適配文檔 郭霖:Android官方提供的支持不同屏幕大小的全部方法 Storm...
    M悇芐冋憶閱讀 12,822評(píng)論 5 56
  • 屏幕適配 屏幕適配的概念 碎片化既是 Android 的優(yōu)勢(shì)和弱點(diǎn)佣渴,也是開(kāi)發(fā)者們頭疼的問(wèn)題辫狼,同時(shí)也為 Androi...
    s酸菜閱讀 9,775評(píng)論 9 58
  • 本文記錄一些適配問(wèn)題的研究,基礎(chǔ)概念不做過(guò)多介紹观话。 Android在做屏幕適配的時(shí)候一般考慮兩個(gè)因素:分辨率和dp...
    developerzjy閱讀 7,301評(píng)論 1 24
  • 最近我一直在看一些關(guān)于靈魂的故事和視頻予借。不知道別人怎么看待靈魂,轉(zhuǎn)世频蛔,女巫灵迫,靈媒等等,也許和我一樣有著濃厚的興趣...
    豆沙包max閱讀 202評(píng)論 0 0