Android技术知识Android开发Android开发经验谈

第六章 Android 绘图机制与屏幕适配

2018-03-08  本文已影响252人  YoungerDev

Android 群英传笔记
第一章Android体系与系统架构
第二章 Android开发工具及技巧
第三章 Android控件架构与事件拦截机制
第四章 ListView 使用技巧
第五章 Android Scroll 分析
第六章 Android 绘图机制与屏幕适配
第七章 Android 动画机制与使用技巧
第八章 Activity与Activity调用栈分析
第九章 Android 系统信息与安全机制
第十章 Android性能优化
本文出自:
http://www.jianshu.com/u/a1251e598483

屏幕适配的知识

指屏幕对角线的长度,通常使用 "寸"来度量 ,通常的4.7寸,5.5寸手机,现如今各家推出的 带刘海的手机,计算规则相对复杂点

分辨率是指实际屏幕的像素点个数,例如720X1280就是指屏幕的分辨率,宽有720个像素点,高有1280个像素点

每英寸像素又称为DPI,他是由对角线的的像素点数除以屏幕的大小所得,通常有400PPI就已经很6了

系统屏幕密度

每个厂商的安卓手机具有不同的大小尺寸和像素密度的屏幕,安卓系统如果要精确到每种DPI的屏幕,基本上是不可能的,因此系统定义了几个标准的DPI

Android手机分辨率

这是由于各种屏幕密度的不同,导致同样像素大小的长度,在不同密度的屏幕上显示长度不同,因此相同长度的屏幕,高密度的屏幕包含更多的像素点,在安卓系统中使用mdpi密度值为160的屏幕作为标准,在这个屏幕上,1px = 1dp,其他屏幕则可以通过比例进行换算,例如同样是100dp的长度,mdpi中为100px,而在hdpi中为150,我们也可以得出在各个密度值中的换算公式,在mdpi中 1dp = 1px, 在hdpi中, 1dp = 1.5px,在xhdpi中,1dp = 2px,在xxhdpi中1dp = 3px,由此可见,我们换算公式 l:m:h:xh:xxh = 3:4:6:8:12

| 密度类型 | 代表的分辨率(px) | 屏幕像素密度(dpi)|
| ------------- |:-------------:|
| 低密度(ldpi) | 240x320 | 120 |
| 中密度(mdpi) | 320x480 | 160 |
| 高密度(hdpi) | 480x800 | 240|
| 超高密度(xhdpi) | 720x1280 | 320|
| 超超高密度(xxhdpi) | 1080x1920 | 480 |

一部手机的分辨率是宽x高,屏幕大小是以寸为单位,那么三者的关系是:

image

含义:density-independent pixel,叫dp或dip,与终端上的实际物理像素点无关。
单位:dp,可以保证在不同屏幕像素密度的设备上显示相同的效果
Android开发时用dp而不是px单位设置图片大小,是Android特有的单位
场景:假如同样都是画一条长度是屏幕一半的线,如果使用px作为计量单位,那么在480x800分辨率手机上设置应为240px;在320x480的手机上应设置为160px,二者设置就不同了;如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一半的长度。

含义:scale-independent pixel,叫sp或sip
单位:sp

在程序中,我们可以非常方便地对一些单位的换算,下面的代码给出了一种换算的方法我们可以把这些代码作为工具类保存在项目中

package com.younger.testyounger;

import android.content.Context;


/**
 * dp,sp转换成px的工具类
 * Created by lgl on 16/3/23.
 */
public class DisplayUtils {

    /**
     * 将px值转换成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转化成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转化成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转化成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);
    }
}

其实的density就是前面所说的换算比例,这里使用的是公式换算方法进行转换,同时系统也提供了TypedValue帮助我们转换

    /**
     * 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());
    }

关于Android 屏幕适配,我目前在项目中实际用到过的有三种

这个px并不代表1像素,我在内部会进行百分比化处理,也就是说:720px高度的屏幕,你这里填写72px,占据10%;当这个布局文件运行在任何分辨率的手机上,这个72px都代表10%的高度,这就是本库适配的原理。

详细可以参考链接文章.

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

关于屏幕适配的文章 可以参考 https://www.jianshu.com/p/ec5a1a30694b

绘图的内容,可以查看 系列文章 https://www.jianshu.com/p/bd153dfc0095

SurfaceView

Android系统提供了VieW进行绘图处理, vieW可以满足大部分的绘图需求,但在某些时却也有些心有余而力不足,特别是在进行一些开发的时候。 我们知道,VieW通过刷新来视图, Android系统通过发出VSYNC信号来进行屏幕的重绘, 刷新的间隔时间为I6ms。在16ms内View完成了你所需要执行的所有操作,那么用户在视觉上, 就不会产生卡顿的感觉:而如果执行的操作逻辑太多,特别是需要频繁刷新的界面上,例如游戏界面,那么就塞主线程,从而导致画面卡顿,很多时候,在自定义VieW的Log中经常会看见如下示的警告

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

这些警告的产生,很多情况下就是因为在绘制过程中, 处理逻辑太多造成的为了避免这一问题的产生一 Android系统提供了surfacevicW组件来解决这个问题,可以说是VieW的孪生兄弟,但它与View还是有所不同的,它们的区别主要体现

总结起来,如果你的自定义View需要频繁刷新,或者刷新时数据处理量比较大,那么你就可以考虑使用surfaceVicw取代View了

surfaceView的使用

创建自定义的surfaceView,实现它的两个接口

public class SurfaView extends SurfaceView implements SurfaceHolder.Callback,Runnable

通过实现它的两个几口,就会有三个回调方法

代码实现,Android 触摸画图 下面是View 的实现代码

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

好了,关于Android 绘图只知识, 和屏幕适配基本就先写到这,如果以后有新的内容,会继续补充

上一篇下一篇

猜你喜欢

热点阅读