Android绘图机制和处理技巧

前言

  • Android群英传读书笔记

目录

Android绘图机制与处理技巧.png

Android屏幕相关知识

  • 了解一些基础名词和参数信息

屏幕尺寸信息

屏幕参数

  • 屏幕大小:指屏幕对角线的长度,通常使用“寸”来度量,如4.7寸、5.5寸手机等
  • 分辨率:手机屏幕像素点的个数,720×1280就是指宽有720个像素点,高有1280个像素点
  • PPI:每英寸像素,又名DPI,由对角线的像素点数除以屏幕的大小得到的

系统屏幕密度

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

独立像素密度dp

Android系统使用mdpi即密度值为160的屏幕作为标准,在这个屏幕上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值转换为dip或dp值,保证尺寸大小不变
     *
     * @param context 通过上下文获得显示参数中的屏幕密度
     * @param pxValue 需要转换的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值转换为px值,保证尺寸大小不变
     *
     * @param context  通过上下文获得显示参数中的屏幕密度
     * @param dipValue 需要转换的dp值
     * @return
     */
    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }


    /**
     * 将px值转换为sp值,保证文字大小不变
     *
     * @param context 通过上下文获得显示参数中的屏幕密度
     * @param pxValue 需要转换的px值
     * @return
     */
    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }


    /**
     * 将sp值转换为px值,保证文字大小不变
     *
     * @param context 通过上下文获得显示参数中的屏幕密度
     * @param spValue 需要转换的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绘图技巧

  • 系统通过Canvas画布对象来提供绘图方法,它提供了各种绘图API
API 绘制(6)
drawPoint()
drawLine() 线
drawsLine() 多条线
drawRect() 矩形
drawRoundRect() 圆角矩形
drawVertices() 多边形
drawArc() 弧、扇
drawCircle()
drawOval() 椭圆
drawText() 绘制文本
drawPosText() 在指定位置绘制文本
drawPath() 绘制路径
drawBitmap() 绘制Bitmap(加载)
API 设置属性
setAntiAlias() 设置画笔锯齿效果
setColor() 设置画笔颜色
setARGB() 设置画笔A,R,G,B值
setAlpha() 设置画笔Alpha值
setTextSize() 设置字体尺寸
setStyle() 设置画笔风格(空心或实心)
setStrokeWidth() 设置空心边框的宽度
reset() 重置

绘制的实例
http://blog.csdn.net/rhljiayou/article/details/7212620

  • 需要注意其中画弧和扇形,本质是画一个矩形,以矩形的中心为弧或扇的中心点,以矩形的中心为角度的坐标原点绘制

Android XML绘图

Bitmap

  • 将图片直接转换为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        //当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        //内边距
        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实现点击切换内容时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绘图技巧

  • 简化,优化绘图操作

Canvas

API 作用
Canvas.save() 保存画布(类似PS中的图层保存)
Canvas.restore() 合并画布(类似PS中的合并图层)
Canvas.translate() 画布所在坐标系平移
Canvas.rotate() 画布所在坐标系旋转

Layer 层

  • 类似于PS中图层的概念
  • 基于栈的结构进行管理的
API 作用
saveLayer() 将图层入栈
saveLayerAlpha() 将图层入栈
restore() 将图层出栈
restoreCount() 将图层出栈
  • 入栈时,后面所有的操作都会发生在这个图层中
  • 出栈时,则会把图像绘制到上层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时,为半透明状态
图二,透明度设置为255时,完全不透明

Android图像处理技巧

  • Android对于图片的处理,最常用使用到的数据结构就是位图--Bitmap,它包含一张图片的所有数据
  • 整个图片都是有点阵和颜色组成的
  • 点阵就是一个包含像素的矩阵,每个元素对应者图片的一个像素

色彩矩阵分析

  • 在色彩处理中通常用以下三个角度描绘一个图像
  • 色调--物体传播的颜色
  • 饱和度--颜色的纯度,从0(灰)到100%(饱和)来进行描述
  • 亮度--颜色的相对明暗程度
  • 在Android中系统使用颜色矩阵ColorMatrix来处理这个色彩效果

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


SurfaceView的使用

  • SurfaceView是为了解决View中执行操作逻辑太多出现卡顿的情况

SurfaceView和View的区别

  • View主要适用于主动更新的情况下,而SurfaceView主要适用于被动更新,例如频繁的刷新
  • View在主线程中对画面进行刷新,而SurfaceView通常会通过一个子线程来进行页面的刷新
  • View在绘图时没有使用双缓冲机制,而SurfaceView在底层实现机制中已经实现了双缓冲机制

SurfaceView的使用

  • 创建SurfaceView
    • 自定义SurfaceView继承SurfaceView
    • 实现SurfaceHolder.Callback接口,并实现对应的三个方法
    • 实现Runnable接口,实现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() 获得当前Canvas对象
drawColor() 清屏
public class SurfaceViewDemo extends SurfaceView implements Runnable, SurfaceHolder.Callback {

    private SurfaceHolder mHolder;
    private Canvas mCanvas;
    private boolean mIsDrawing;         // 子线程标志位

    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;
    }

画笔粗细没调。。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容