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
- 獨(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)系是:
- 密度無(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ǔ)充