Android绘图机制与处理技巧

1 单位转换

由于各种屏幕密度不同,导致同样像素大小的长度,在不同密度的屏幕上显示的长度不同,如下是各个密度值中的换算公式,
在mdpi 中 1dp = 1px,
在hdpi 中 1dp = 1.5px,
在xhdpi 中 1dp = 2px,
在xxhdpi 中 1dp = 3px,
其直接的换算公式是: ldpi:mdpi:hdpi:xhdpi:xxhpi = 3:4:6:8:12
转换代码如下:

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    /**
     *  使用系统提供的TypedValue 进行转换
     *  */
    public static int dp2px(Context context,float dp){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,context.getResources().getDisplayMetrics());
    }

    public static int px2dp(Context context,float px){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,px,context.getResources().getDisplayMetrics());
    }

2 2D的绘图基础

系统通过提供Canvas对象来提供绘图的方法,比如

       // 画笔的属性以及对应功能 
        mPaint.setAlpha(8); //设置画笔的Alpha
        mPaint.setStrokeWidth(5); //设置空心边框的宽度
        mPaint.setShader(new LinearGradient(0, 0, 400, 0, new int[]{Color.BLUE, 0x123321, Color.BLUE}, null, Shader.TileMode.CLAMP))
        mPaint.setTextSize(20);
        mPaint.setARGB(10,8,5,4); //设置颜色值,分别对应 透明度,红,绿,蓝的四个通道分量来决定像素点的颜色
        mPaint.setAntiAlias(true);//设置画笔的锯齿效果
        mPaint.setColor(Color.YELLOW);//设置画笔的颜色
        mPaint.setStyle(Paint.Style.STROKE);//设置画笔的风格:空心STROKE 或者实心FILL
        Path path = new Path(); //定义一条路径 
        path.moveTo(40,50);//移动到坐标 40 50
        path.lineTo(100,100);
        path.lineTo(100,50);
        path.lineTo(50,200);
       // 绘图的功能以及属性
        canvas.drawPath(path,mPaint);
        canvas.drawPosText("Hello",new float[]{
                100,100,//第一个字母的坐标 
                100,200,//第二个字母的坐标 ......
                100,300,100,400,100,500},mPaint);//在指定的位置绘制文本
        canvas.drawOval(100,200,400,300,mPaint);//绘制椭圆
        RectF rect = new RectF(50, 50, 200, 200);//定义一个矩形区域
        canvas.drawRoundRect(rect,
                30, //x轴的半径
                30, //y轴的半径
                mPaint);
        canvas.drawPoint(400,500,mPaint);//绘制点
        canvas.drawLine(200,300,500,600,mPaint);//绘制线
        canvas.drawArc(rect1, //弧线所使用的矩形区域大小
                0,  //开始角度
                90, //扫过的角度
                false, //是否使用中心
                mPaint);
        canvas.drawText("android",200,410,mPaint);//绘制文本
        canvas.drawRect(100,200,500,460,mPaint);//绘制矩形
        canvas.drawCircle(100,370,40,mPaint);//绘制圆形

3 XML绘图

3.1 Bitmap
在xml中的drawable/目录下可以创建Bitmap资源,举例,在drawable目录下创建bit_picture.xml并实现如下代码

<?xml version="1.0" encoding="utf-8"?>
<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="drawable/drawable_resource"
    android:antialias=["true" | "false"]
    android:dither=["true" | "false"]
    android:filter=["true" | "false"]
    android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                      "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                      "center" | "fill" | "clip_vertical" | "clip_horizontal"]
    android:mipMap=["true" | "false"]
    android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"] />

通过以上代码,可以直接将图片转换成Bitmap 直接使用

    //xml调用
    <ImageView
        android:layout_width="100dp"
        android:layout_weight="1"
        android:layout_height="100dp"
        android:src="@drawable/bit_picture"/>
    //java中调用
    getResources().getDrawable(R.drawable.bit_picture);

3.2 Shape
通过shape可以在xml中绘制各种形状,举例 在res/drawable文件夹下,新建一个文件,命名为:shape_demo.xml 代码如下

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle"> // |oval|line|ring

    <!-- shape = rectangle时使用,表示边角的圆弧半径 半径默认是1dp-->
    <corners android:radius="9dp"
        android:topLeftRadius="5dp"
        android:topRightRadius="2dp"
        android:bottomLeftRadius="3dp"
        android:bottomRightRadius="4dp"/>
    <!-- 实心填充 -->
    <solid android:color="#00000000" />
    <!-- 描边:一般大小都是1dp -->
    <stroke
        android:width="1dp"
        android:color="#ff000000"
        android:dashGap="15dp"
        android:dashWidth="40dp"/>
    <!-- 四周留出来的空白,和xml文件中的pad效果一样,对内起作用 -->
    <padding
        android:bottom="30dp"
        android:left="20dp"
        android:right="30dp"
        android:top="20dp" />
    <!-- 指定大小 -->
    <size android:height="400dp"
        android:width="400dp"/>
    <!-- 背景颜色渐变 -->
    <gradient
        android:angle="45"
        android:endColor="#ff00ff00"
        android:startColor="#ff0000ff"
        android:type="linear"
        android:useLevel="true"
        android:centerColor="#611111"
        />
</shape>
调用方法同3.1

3.3 Layer
Layer主要是用来实现图层效果的,举例 在res/drawable文件夹下,新建一个文件,命名为:layer_demo.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/arrow_right"/>
    <item android:left="10.0dip"
        android:top="10.0dip"
        android:right="20.0dip"
        android:bottom="30.0dip"
        android:drawable="@drawable/arrow_right"
        />
</layer-list>
调用方法同3.1

3.4 Selector
主要是实现静态绘图中的事件反馈,通过不同的事件设置不同的图像,在res/drawable文件夹下,新建一个文件,命名为:selector_demo.xml
举例1

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 默认时背景图片-->
    <item android:drawable="@drawable/red"/>
    <!-- 没有焦点时背景图片-->
    <item android:state_window_focused="false"
        android:drawable="@drawable/dark_yellow"/>
    <!-- 非触摸模式下获得焦点并单击时背景图片-->
    <item android:state_focused="true"
        android:state_pressed="true"
        android:drawable="@drawable/bule"/>
    <!-- 触摸模式下获得焦点并单击时背景图片-->
    <item android:state_focused="false"
        android:state_pressed="true"
        android:drawable="@drawable/black"/>
    <!-- 选中时背景图片-->
    <item android:state_selected="true"
        android:drawable="@drawable/dark_bule"/>
    <!-- 获取焦点时背景图片-->
    <item android:state_focused="true"
        android:drawable="@drawable/deep_yellow"/>
</selector>
// 调用方法同3.1

举例2 在Selector中使用shape来作为它的item

    <item android:state_pressed="true" >
        <shape android:shape="rectangle">
            <solid android:color="#224444"></solid>
            <corners android:radius="5dp"/>
            <padding android:right="10dp"
                android:top="10dp"
                android:left="10dp"
                android:bottom="10dp"/>
        </shape>
    </item>
    <item >
        <shape android:shape="rectangle">
            <solid android:color="#FFFFFF"/>
            <corners android:radius="5dp"/>
            <padding android:bottom="10dp"
                android:top="10dp"
                android:left="10dp"
                android:right="10dp"/>
        </shape>
    </item>

4 绘图技巧

4.1 Canvas
作为绘图的直接对象, 以下几个方法非常好用

  • Canvas.save() // 将之前的所有已绘制的图像保存起来,后续操作就像在另一个图层上一样
  • Canvas.restore();//将save之后的绘图与sava之前的绘图合并起来
  • Canvas.translate();//平移坐标系
  • Canvas.rotate();//旋转坐标系
    举例, 画一个仪表盘
    public CanvasView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(10);
        mWidth = 600;
        mHeight = 800;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawCircle(mWidth/2,mHeight/2,mWidth/2,mPaint);// 以mWidth/2,mHeight/2为圆心,以mWidth/2为半径,用mPaint画一个圆
        Log.i("niuniu ", " init  drawCircle ");
        mPaint.setStrokeWidth(8);
        for(int i = 0;i<24;i++){
            String value = String.valueOf(i);
            if(i==0 || i==6 || i == 12 ||i==18){
                mPaint.setStrokeWidth(6);
                mPaint.setTextSize(35);
                canvas.drawLine(mWidth/2,mHeight/2-mWidth/2,mWidth/2,mHeight/2-mWidth/2+60,mPaint);
                canvas.drawText(value,mWidth/2-mPaint.measureText(value)/2,mHeight/2-mWidth/2+110,mPaint);
            }else {
                mPaint.setStrokeWidth(4);
                mPaint.setTextSize(20);
                canvas.drawLine(mWidth/2,mHeight/2-mWidth/2,mWidth/2,mHeight/2-mWidth/2+30,mPaint);
                canvas.drawText(value,mWidth/2-mPaint.measureText(value)/2,mHeight/2-mWidth/2+60,mPaint);
            }
            canvas.rotate(15,mWidth/2,mHeight/2); //以mWidth/2,mHeight/2为圆心,将坐标系旋转15°
        }

        Paint paint1 = new Paint();
        paint1.setStrokeWidth(10);
        Paint paint2 = new Paint();
        paint2.setStrokeWidth(8);
        canvas.save(); // 保存画笔以及上面的圆环
        canvas.translate(mWidth/2,mHeight/2);//平移到圆心 已圆心为坐标起点 0,0
        canvas.drawLine(0,0,100,80,paint1);
        canvas.drawLine(0,0,100,120,paint2);
        canvas.restore(); // 将所画的的指针与sava之前的圆环合并起来
        super.onDraw(canvas);
    }

4.2 Layer图层
图层是基于栈的结构进行管理的.android通过saveLayer(),saveLayerAlpha()将一个图层入栈,利用restore(),restoreToCount()将图层出栈,入栈时,后面的操作都会发生在这个图层上,出栈时,会把图像绘制在上层的cavas上.
举例

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLUE);
        canvas.drawColor(Color.WHITE);
        canvas.drawCircle(150,150,100,mPaint);
        canvas.saveLayerAlpha(0,0,400,400,127,Canvas.ALL_SAVE_FLAG);//入栈,将后面所画的红色圆都发生在该图层上
        mPaint.setColor(Color.RED);
        canvas.drawCircle(200,200,100,mPaint);
        canvas.restore();//出栈,将前面所有的图像都绘制在该canvas上

猜你喜欢

转载自blog.csdn.net/dakaniu/article/details/78846972