Android繪圖機制和處理技巧

前言

  • Android群英傳讀書筆記

目錄

Android繪圖機制與處理技巧.png

Android屏幕相關(guān)知識

  • 了解一些基礎(chǔ)名詞和參數(shù)信息

屏幕尺寸信息

屏幕參數(shù)

  • 屏幕大小:指屏幕對角線的長度,通常使用“寸”來度量,如4.7寸、5.5寸手機等
  • 分辨率:手機屏幕像素點的個數(shù),720×1280就是指寬有720個像素點,高有1280個像素點
  • PPI:每英寸像素,又名DPI,由對角線的像素點數(shù)除以屏幕的大小得到的

系統(tǒng)屏幕密度

密度 密度值 分辨率
ldpi 120 240×320
mdpi 160 320×480
hdpi 240 480×800
xhdpi 320 720×1280
xxhdpi 480 1080×1920

獨立像素密度dp

Android系統(tǒng)使用mdpi即密度值為160的屏幕作為標(biāo)準(zhǔn),在這個屏幕上1px = 1dp,其他屏幕可以進行比例換算,例如同樣是100dp的長度,在mdpi中為100px,而hdpi中為150px。也就是說在mdpi中1dp = 1px,而hdpi中1dp = 1.5px,在xdpi中1dp = 2px,在xxhpi中1dp = 3px

各分辨率換算比率
ldpi :mdpi :hdpi :xhdpi :xxhdpi = 3:4:6:8:12

單位換算

  • 這里的換算封裝成工具類以便以后使用
  • 核心就是從DisplayMetrics顯示度量類中獲取信息
  • 這里我順便添加了獲得屏幕寬度的方法
public class DisplayUtils {

    /**
     * 獲得屏幕寬度(像素)
     *
     * @param activity
     * @return
     */
    public static int getDisplayWidth(Activity activity) {
        DisplayMetrics metric = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(metric);
        int width = metric.widthPixels;     // 屏幕寬度(像素)
        return width;
    }

    /**
     * 獲得屏幕高度(像素)
     *
     * @param activity
     * @return
     */
    public static int getDisplayHeight(Activity activity) {
        DisplayMetrics metric = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(metric);
        int height = metric.heightPixels;     // 屏幕高度(像素)
        return height;
    }


    /**
     * 將px值轉(zhuǎn)換為dip或dp值,保證尺寸大小不變
     *
     * @param context 通過上下文獲得顯示參數(shù)中的屏幕密度
     * @param pxValue 需要轉(zhuǎn)換的px值
     * @return
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }


    /**
     * 將dip或dp值轉(zhuǎn)換為px值,保證尺寸大小不變
     *
     * @param context  通過上下文獲得顯示參數(shù)中的屏幕密度
     * @param dipValue 需要轉(zhuǎn)換的dp值
     * @return
     */
    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }


    /**
     * 將px值轉(zhuǎn)換為sp值,保證文字大小不變
     *
     * @param context 通過上下文獲得顯示參數(shù)中的屏幕密度
     * @param pxValue 需要轉(zhuǎn)換的px值
     * @return
     */
    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }


    /**
     * 將sp值轉(zhuǎn)換為px值,保證文字大小不變
     *
     * @param context 通過上下文獲得顯示參數(shù)中的屏幕密度
     * @param spValue 需要轉(zhuǎn)換的sp值
     * @return
     */
    public static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }
}

Android繪圖技巧

2D繪圖技巧

  • 系統(tǒng)通過Canvas畫布對象來提供繪圖方法,它提供了各種繪圖API
API 繪制(6)
drawPoint()
drawLine()
drawsLine() 多條線
drawRect() 矩形
drawRoundRect() 圓角矩形
drawVertices() 多邊形
drawArc() 弧、扇
drawCircle()
drawOval() 橢圓
drawText() 繪制文本
drawPosText() 在指定位置繪制文本
drawPath() 繪制路徑
drawBitmap() 繪制Bitmap(加載)
API 設(shè)置屬性
setAntiAlias() 設(shè)置畫筆鋸齒效果
setColor() 設(shè)置畫筆顏色
setARGB() 設(shè)置畫筆A,R,G,B值
setAlpha() 設(shè)置畫筆Alpha值
setTextSize() 設(shè)置字體尺寸
setStyle() 設(shè)置畫筆風(fēng)格(空心或?qū)嵭模?/td>
setStrokeWidth() 設(shè)置空心邊框的寬度
reset() 重置

繪制的實例
http://blog.csdn.net/rhljiayou/article/details/7212620

  • 需要注意其中畫弧和扇形,本質(zhì)是畫一個矩形,以矩形的中心為弧或扇的中心點,以矩形的中心為角度的坐標(biāo)原點繪制

Android XML繪圖

Bitmap

  • 將圖片直接轉(zhuǎn)換為Bitmap
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@mipmap/ic_launcher"/>

Shape

  • 繪制各種形狀,省去了canvas和paint的步驟
<?xml version="1.0" encoding="utf-8"?>
<shape    
    xmlns:android="http://schemas.android.com/apk/res/android"    
    android:shape=["rectangle" | "oval" | "line" | "ring"] >    
    <corners        //當(dāng)shape為rectangle時使用
        android:radius="integer"        //半徑值會被后面的單個半徑屬性覆蓋,默認為1dp
        android:topLeftRadius="integer"        
        android:topRightRadius="integer"        
        android:bottomLeftRadius="integer"        
        android:bottomRightRadius="integer" />    
    <gradient       //漸變
        android:angle="integer"        
        android:centerX="integer"        
        android:centerY="integer"        
        android:centerColor="integer"        
        android:endColor="color"        
        android:gradientRadius="integer"        
        android:startColor="color"        
        android:type=["linear" | "radial" | "sweep"]        
        android:useLevel=["true" | "false"] />    
    <padding        //內(nèi)邊距
        android:left="integer"        
        android:top="integer"        
        android:right="integer"        
        android:bottom="integer" />    
    <size           //指定大小,一般用在imageview配合scaleType屬性使用
        android:width="integer"        
        android:height="integer" />    
    <solid          //填充顏色
        android:color="color" />    
    <stroke         //邊框
        android:width="integer"        
        android:color="color"        
        android:dashWidth="integer"        
        android:dashGap="integer" />
</shape>

Layer 層


<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_launcher" />
    <item android:drawable="@drawable/ic_launcher" 
               android:left="10.0dp"
               android:top="10.0dp"
               android:right="10.0dp"
               android:bottom="10.0dp"/>
    ......
</layer-list>

Selector 選擇器

  • Fragment配合RadioGroup實現(xiàn)點擊切換內(nèi)容時RadioGroup的子項就是用選擇器作為View的觸摸反饋,不過我更喜歡FragmentTabHost做為下方選項卡(跑題了。。。)
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 默認時的背景圖片-->
    <item android:drawable="@drawable/X1"/>
    <!-- 沒有焦點時的背景圖片 -->
    <item android:drawable="@drawable/X2" android:state_window_focused="false"/>
    <!-- 非觸摸模式下獲得焦點并單擊時的背景圖片 -->
    <item android:drawable="@drawable/X3" android:state_focused="true" android:state_pressed="true"/>
    <!-- 觸摸模式下單擊時的背景圖片-->
    <item android:drawable="@drawable/X4" android:state_focused="false" android:state_pressed="true"/>
    <!--選中時的圖片背景-->
    <item android:drawable="@drawable/X5" android:state_selected="true"/>
    <!--獲得焦點時的圖片背景-->
    <item android:drawable="@drawable/X6" android:state_focused="true"/>
</selector>

Android繪圖技巧

  • 簡化,優(yōu)化繪圖操作

Canvas

API 作用
Canvas.save() 保存畫布(類似PS中的圖層保存)
Canvas.restore() 合并畫布(類似PS中的合并圖層)
Canvas.translate() 畫布所在坐標(biāo)系平移
Canvas.rotate() 畫布所在坐標(biāo)系旋轉(zhuǎn)

Layer 層

  • 類似于PS中圖層的概念
  • 基于棧的結(jié)構(gòu)進行管理的
API 作用
saveLayer() 將圖層入棧
saveLayerAlpha() 將圖層入棧
restore() 將圖層出棧
restoreCount() 將圖層出棧
  • 入棧時,后面所有的操作都會發(fā)生在這個圖層中
  • 出棧時,則會把圖像繪制到上層Canvas上
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawColor(Color.WHITE);
        p.setColor(Color.BLUE);
        canvas.drawCircle(150, 150, 100, p);

        // 圖一
        canvas.saveLayerAlpha(0, 0, 400, 400, 127, LAYER_TYPE_NONE);
        // 圖二
//        canvas.saveLayerAlpha(0, 0, 400, 400, 255, LAYER_TYPE_NONE);
        p.setColor(Color.RED);
        canvas.drawCircle(200, 200, 100, p);
        canvas.restore();
    }

圖一,透明度為127時,為半透明狀態(tài)
圖二,透明度設(shè)置為255時,完全不透明

Android圖像處理技巧

  • Android對于圖片的處理,最常用使用到的數(shù)據(jù)結(jié)構(gòu)就是位圖--Bitmap,它包含一張圖片的所有數(shù)據(jù)
  • 整個圖片都是有點陣和顏色組成的
  • 點陣就是一個包含像素的矩陣,每個元素對應(yīng)者圖片的一個像素

色彩矩陣分析

  • 在色彩處理中通常用以下三個角度描繪一個圖像
  • 色調(diào)--物體傳播的顏色
  • 飽和度--顏色的純度,從0(灰)到100%(飽和)來進行描述
  • 亮度--顏色的相對明暗程度
  • 在Android中系統(tǒng)使用顏色矩陣ColorMatrix來處理這個色彩效果

// ---- 施工中(8月2日--6日) ----


SurfaceView的使用

  • SurfaceView是為了解決View中執(zhí)行操作邏輯太多出現(xiàn)卡頓的情況

SurfaceView和View的區(qū)別

  • View主要適用于主動更新的情況下,而SurfaceView主要適用于被動更新,例如頻繁的刷新
  • View在主線程中對畫面進行刷新,而SurfaceView通常會通過一個子線程來進行頁面的刷新
  • View在繪圖時沒有使用雙緩沖機制,而SurfaceView在底層實現(xiàn)機制中已經(jīng)實現(xiàn)了雙緩沖機制

SurfaceView的使用

  • 創(chuàng)建SurfaceView
    • 自定義SurfaceView繼承SurfaceView
    • 實現(xiàn)SurfaceHolder.Callback接口,并實現(xiàn)對應(yīng)的三個方法
    • 實現(xiàn)Runnable接口,實現(xiàn)run()方法
 @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {

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

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

    }

    @Override
    public void run() {

    }
  • 初始化SurfaceView
private SurfaceHolder mHolder;
private Canvas mCanvas;
private boolean mIsDrawing;

private void init() {    
     mHolder = getHolder(); 
     mHolder.addCallback(this);
}
  • 使用SurfaceView
API 作用
lockCanvas() 獲得當(dāng)前Canvas對象
drawColor() 清屏
public class SurfaceViewDemo extends SurfaceView implements Runnable, SurfaceHolder.Callback {

    private SurfaceHolder mHolder;
    private Canvas mCanvas;
    private boolean mIsDrawing;         // 子線程標(biāo)志位

    public SurfaceViewDemo(Context context) {
        super(context);
        initView();
    }

    private void initView() {
        mHolder = getHolder();
        mHolder.addCallback(this);

        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
        // mHolder.setFormat(PixelFormat.OPAQUE);
    }


    @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() {
        while (mIsDrawing) {
            draws();
        }
    }

    public void draws() {
        try {
            mCanvas = mHolder.lockCanvas();
        } catch (Exception e) {
            
        } finally {
            if (mCanvas != null) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }
}

SurfaceView實例

1. 正選曲線

@Override
    public void run() {
        while (mIsDrawing) {
            draws();
            x += 1;
            y = (int) (100 * Math.sin(x * 2 * Math.PI/180) + 400);
            mPath.lineTo(x, y);
        }
    }

    private void draws() {
        try {
            mCanvas = mHolder.lockCanvas();
            // Surface背景
            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(mPath, mPaint);
        } catch (Exception e) {

        } finally {
            if (mCanvas != null) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }
繪制正弦曲線

2. 繪圖板

private void draws() {
        try {
            mCanvas = mHolder.lockCanvas();
            // Surface背景
            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(mPath, mPaint);
        } catch (Exception e) {

        } 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:
                mPath.moveTo(x,y);
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(x, y);
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

畫筆粗細沒調(diào)。。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容