Android绘图机制与处理技巧

   屏幕的尺寸信息

    1.屏幕参数

    一块屏幕通常具有以下几个参数

    (1)屏幕大小

    指屏幕对角线的长度,通常使用“寸”来度量。

    (2)分辨率

    分辨率是指手机屏幕的像素点的个数。

     (3)DPI

     每英寸像素。它是由对角线的像素点数除以屏幕大小得到的。

    系统屏幕密度

     每个厂商的Android手机具有不同的大小尺寸和像素密度的屏幕。因此,系统定义了几个标准的DPI值,作为手机的固定DPI

   独立像素密度dp

     正式由于各种屏幕密度的不同,导致同样像素大小的长度,在不同密度的屏幕上显示长度不同。因为相同长度的屏幕,搞密度的屏幕包含更多的像素点。Android系统使用mdpi即密度为160的屏幕密度作为标准,在这个屏幕上1px=1dp。其他屏幕则可以通过此比例进行换算。

   单位转换

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

/**
 * 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就是前面我们所说的换算比例。这里使用的是公式换算的方法进行转换。同时,系统也提供了TypeValue类帮助我们转换。

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

  2D绘图基础

   系统通过提供的Canvas对象来提供绘图方法。它提供了各种绘制图像的API,如drawPoint(点)、drawLine(线)、drawRect(矩形)、drawVertices(多边形)、drawArc(弧)、drawCircle(圆),等等。通过它们的名字,我们基本可以大致了解它们的功能。当然,Paint作为一个非常重要的元素,功能也是很强大的,这里简单列举了一些它的属性和对应的功能。

   1.setAntiAlias()  //设置画笔的锯齿效果

   2.setColor()    //设置画笔的颜色

   3.setARGB()   //设置画笔的A R G B值

   4.setAlpha()    //设置画笔的Alpha值       0.0表示完全透明   1.0表示完全不透明

   5.setTextSize()  //设置字体的尺寸

   6.setStyle() //设置画笔的风格(空心或实心)

   7.setStrokeWidth();//设置空心边框的宽度

 正是由于画笔功能的不一样,再结合各种不同的绘画API。这样任意组合就可以实现不同的绘图效果,比如同样是矩形,设置Paint的Style就可以画出空心或者实心的矩形。

 下面我们看看Canvas家族的成员们。

 1.DrawPoint  绘制点

 

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPoint(x,y,paint);
}

  2.DrawLine 绘制线

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawLine(startX,startY,endX,endY,paint);
    }

3.DrawLines,绘制多条直线

Float[] pts = {startX1,startY1,endX1,endY1,.........};
canvas.drawLines(pts,paint);

 4.drawRect,绘制矩形

canvas.drawRect(left,top,right,botton,paint);

5.DrawRoundRect,绘制圆角矩形

canvas.drawRoundRect(left,top,right,botton,radiusX,radiusY,paint);

6.DrawCircle,绘制圆

canvas,drawCircle(circleX,circleY,radius,paint)

7.DrawArc,绘制弧形、扇形

paint.setStyle(Paint.Style.STROKE);
canvas.drawArc(left,top,right,bottom,startAngle,sweepAngle,useCenter,paint);

     这里需要注意下,绘制弧形与扇形的区分就是倒数第二个参数useCenter的区别 。使用不同的Paint.Style和useCenter属性产生不同的效果。

8.DrawOval,绘制椭圆

 
//通过椭圆的外接矩形来绘制椭圆
        canvas.drawOval(left,top,right,bottom,paint)

9.DrawText,绘制文本

 canvas.drawText(text,startX,startY,paint);

10.DrawPosText,在指定位置绘制文本

canvas.drawPosText(new Float[]{X1,Y1,X2,Y2....},paint);

11.DrawPath,绘制路径

        Path path  = new Path();
        path.moveTo(50,50);
        path.lineTo(100,100);
        path.lineTo(100,300);
        path.lineTo(300,50);
        canvas.drawPath(path,paint);

 

  Android xml绘图

   XML在Android系统中可不仅仅是Java中的一个布局文件、配置列表。在Android开发者的手上,它甚至可以变成一幅画,一张图。Android的开发者给XML提供了几个强大的技能来帮助我们实现这一功能。

   Bitmap

在XML中使用Bitmap非常简单

 

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@mipmap/ic_launcher">

</bitmap>

通过这样引用图片,就可以将图片直接转成了Bitmap让我们在程序中使用

 Shape

通过Shape可以在XML中绘制各种形状,下面展示了Shape所支持的参数

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

    <!--当shape= rectangle的时候使用-->
    <corners
        android:bottomLeftRadius="1dp"
        android:bottomRightRadius="1dp"
        android:radius="1dp"
        android:topLeftRadius="1dp"
        android:topRightRadius="1dp" />
    <!--半径,会被后面的单个半径属性覆盖,默认是1dp-->

    <!--渐变-->
    <gradient
        android:angle="1dp"
        android:centerColor="@color/colorAccent"
        android:centerX="1dp"
        android:centerY="1dp"
        android:gradientRadius="1dp"
        android:startColor="@color/colorAccent"
        android:type="linear"
        android:useLevel="true" />

    <!--内间距-->
    <padding
        android:bottom="1dp"
        android:left="1dp"
        android:right="1dp"
        android:top="1dp" />

    <!--大小,主要用于imageview用于scaletype-->
    <size
        android:width="1dp"
        android:height="1dp" />

    <!--填充颜色-->
    <solid android:color="@color/colorAccent" />

    <!--指定边框-->
    <stroke
        android:width="1dp"
        android:color="@color/colorAccent" />
    <!--虚线宽度-->
    android:dashWidth= "1dp"

    <!--虚线间隔宽度-->
    android:dashGap= "1dp"

</shape>

    Shape可以说是XML绘图的精华所在。Shape功能十分强大,无论是扁平化、拟物化还是渐变,它都能绘制。整个Shape的可用参数,在上面的表中已经列举的很清楚了。

  Layer

Layer是在PhototShop中非常常用的功能。在Android中,同样可以通过Layer来实现类似PhotoShop中图层的概念。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!--图片1-->
    <item android:drawable="@mipmap/ic_launcher"/>

    <!--图片2-->
    <item
        android:bottom="10dp"
        android:top="10dp"
        android:right="10dp"
        android:left="10dp"
        android:drawable="@mipmap/ic_launcher"
        />

</layer-list>

通过layer,layer-list 可以很方便地实现Photoshop中的图层效果,图片会依次叠加

  Selector

Selector的作用在于帮助开发者实现静态绘图中的事件反馈,通过给不同的事件设置不同的图像,从而在程序中根据用户输入,返回不同的效果。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 默认时候的背景-->
    <item android:drawable="@mipmap/ic_launcher" />

    <!-- 没有焦点时候的背景-->
    <item android:drawable="@mipmap/ic_launcher" android:state_window_focused="false" />

    <!-- 非触摸模式下获得焦点并点击时的背景图片-->
    <item android:drawable="@mipmap/ic_launcher" android:state_pressed="true" android:state_window_focused="true" />

    <!-- 触摸模式下获得焦点并点击时的背景图片-->
    <item android:drawable="@mipmap/ic_launcher" android:state_focused="false" android:state_pressed="true" />

    <!--选中时的图片背景-->
    <item android:drawable="@mipmap/ic_launcher" android:state_selected="true" />

    <!--获得焦点时的图片背景-->
    <item android:drawable="@mipmap/ic_launcher" android:state_focused="true" />

</selector>

         这一方法可以帮助开发者快速制作View的触摸反馈。通过配置不同的触发事件,Selector可以自动选择不同的图片。特别是自定义Button的时候,我们就不用再使用原生Button单调的背景,而使用Selector特别定制过的背景。不用再程序中修改点击的逻辑,就能完美实现触摸反馈。

         通常情况下,上面提到的这些方法都可以共同实现

 

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <!--填充颜色-->
            <solid android:color="#33444444" />
            <!--设置按钮的四个角为弧形-->
            <corners android:radius="5dp" />
            <!--间距-->
            <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
        </shape>
    </item>

    <item>
        <shape android:shape="rectangle">
            <!--填充颜色-->
            <solid android:color="#FFFFFF" />
            <!--设置按钮的四个角为弧形-->
            <corners android:radius="5dp" />
            <!--间距-->
            <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
        </shape>
    </item>
</selector>

      Android绘图技巧

    1.Canvas作为绘制图形的直接对象,提供了以下几个非常有用的方法。

    (1)Canvas.save()

     (2)Canvas.restore()

     (3)Canvas.translate()

     (4)Canvas.rotate()

    首先,来看Canvas.save()和Canvas.restore()这俩个方法。  

    Canvas.save()这个方法, 从字面上可以理解为保存画布。它的作用就是将之前的所有已绘制图形保存起来,让后续的操作就好像在一个新的图层上操作一样,这一点雨PhotoShop中的图层理解基本一致。

    而Canvas.restore()这个方法,则可以理解为PhotoShop中的合并图层操作。它的操作是将我们在save()之后绘制的所有图像与save()之前的图像进行合并。

    那么translate()方法与rotate()方法呢?虽然从字面上看,可以将它们理解为是画布平移、画布翻转,但是把它理解为坐标系的平移与翻转则会更加形象。前面说了,默认绘图坐标零点位于屏幕左上角,那么在调用translate(x,y)方法之后,则将原点(0,0)移动到了(x,y).之后的所有绘图操作都将以(x,y)为原点执行。同理,rotate()方法也一样。当然这些方法都是为了简化绘图而创建的。

public class DialView extends View {

    // 宽高
    private int mWidth;
    private int mHeight;

    // 构造方法
    public DialView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 获取屏幕的宽高
        WindowManager wm = (WindowManager) getContext().getSystemService(
                Context.WINDOW_SERVICE);
        mWidth = wm.getDefaultDisplay().getWidth();
        mHeight = wm.getDefaultDisplay().getHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);

        // 画外圆
        Paint paintCircle = new Paint();
        paintCircle.setAntiAlias(true);
        paintCircle.setStyle(Paint.Style.STROKE);
        paintCircle.setStrokeWidth(5);
        canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, paintCircle);

        // 画刻度
        Paint paintDegree = new Paint();
        paintDegree.setStrokeWidth(3);
        for (int i = 0; i < 24; i++) {
            // 区别整点和非整点
            if (i == 0 || i == 6 || i == 12 || i == 18) {
                paintDegree.setStrokeWidth(5);
                paintDegree.setTextSize(30);
                canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,
                        mWidth / 2, mHeight / 2 - mWidth / 2 + 60, paintDegree);
                String degree = String.valueOf(i);
                canvas.drawText(degree,
                        mWidth / 2 - paintDegree.measureText(degree) / 2,
                        mHeight / 2 - mWidth / 2 + 90, paintDegree);
            } else {
                paintDegree.setStrokeWidth(3);
                paintDegree.setTextSize(15);
                canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,
                        mWidth / 2, mHeight / 2 - mWidth / 2 + 30, paintDegree);
                String degree = String.valueOf(i);
                canvas.drawText(degree,
                        mWidth / 2 - paintDegree.measureText(degree) / 2,
                        mHeight / 2 - mWidth / 2 + 60, paintDegree);
            }
            // 通过旋转画布简化坐标运算
            canvas.rotate(15, mWidth / 2, mHeight / 2);
        }

        // 画指针
        Paint paintHour = new Paint();
        paintHour.setStrokeWidth(20);
        Paint paintMinute = new Paint();
        paintMinute.setStrokeWidth(10);
        canvas.save();
        canvas.translate(mWidth / 2, mHeight / 2);
        canvas.drawLine(0, 0, 100, 100, paintHour);
        canvas.drawLine(0, 0, 100, 200, paintMinute);
        canvas.restore();
    }

}

     

  Layer图层

       Android中的绘图API,很大程度上都来自于现实生活中的绘图,特别是借鉴了很多PhotoShop中的概念,比如图层的概念。那么什么是图层呢?相信用过ShotoShop的朋友一定会非常清楚,一张复杂的画可以由很多个图层叠加起来,形成一个复杂的图像。在Android中,使用saveLayer()方法可以由很多图层叠加起来,形成一个复杂的图像。在Android中,使用setLayer()方法来创建一个图层,图层同样是基于栈的结构进行管理的。

     

 Android通过调用saveLayer()方法、saveLayerAlpha()方法将一个图层入栈,使用restore()方法、restoreToCount()方法将一个图层出栈。入栈的时候,后面所有的操作都发生在这个图层上,而出栈的时候,则会把图像绘制到上层Canvas上。

@Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        mPaint.setColor(Color.BLUE);
        canvas.drawCircle(150, 150, 100, mPaint);

        canvas.saveLayerAlpha(0, 0,400,400,127,LAYER_TYPE_NONE);
        mPaint.setColor(Color.RED);
        canvas.drawCircle(200, 200, 100, mPaint);
        canvas.restore();
    }

    在onDraw方法中绘制两个相交的圆,当然这两个圆位于两个图层上。

    接下来将图层后面的透明度设置成0-255不同值

    我们分别演示 127 255 0三个

    

Android 图像处理之色彩特效处理

 Android对于图片的处理,最常使用到的数据结构是位图-------Bitmap,它包含了一张图片所有的数据。整个图片都是由点阵和颜色值组成的,所谓点阵就是一个包含像素的矩阵,每一个元素对应着图片的一个像素。而颜色值-----ARGB,分别对应透明度、红、绿、蓝这四个通道分量,它们共同决定了每个像素点显示的颜色。

    

    色彩矩阵分析

    在色彩处理中,通常使用以下三个角度来描述一个图像。

   1.色调-------物体传播的颜色

   2.饱和度-------颜色的纯度,从0到100%来进行描述

   3.亮度--------颜色的相对明暗程度

   而在Android中,系统使用一个颜色矩阵--------ColorMatrix,来处理图像的这些色彩效果。Android中的颜色矩阵是一个4*5的颜色矩阵,它用来对图片的色彩进行处理。而对于每个像素点,都有一个颜色分量矩阵用来保存颜色的RGBA值。

图中矩阵A就是一个4X5的颜色矩阵,在Android中,他们会以一段一维数组的形式来存储[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t],则C就是一个颜色矩形分量,在处理图像中,使用矩阵乘法运算AC处理颜色分量矩阵如下图

矩阵运算的乘法公式相信大家都在线性代数课程中学过,计算过程如下

从这个公式我们可以发现

这些是这样归类的

 

  • 第一行的abcde用来决定新的颜色值R——红色
  • 第二行的fghij用来决定新的颜色值G——绿色
  • 第三行的kimno用来决定新的颜色值B——蓝色
  • 第四行的pqrst用来决定新的颜色值A——透明

 

 

这样划分好各自的范围之后,这些值就比较慢明确了,不过只这样说可能会比较抽象,我们通过几个小李子不断的去分析

首先,我们来看一下矩阵变换的计算公式,以R分量为例,计算过程是

R1 = a * R + b* G + c*B+d *A + e

如果让a = 1,b,c,d,e都等于0,那么计算的结果为R1 = R,因此我们可以构建一个矩阵

 

如果把这个矩阵公式带入R1 = AC,那么根据矩阵的乘法运算法则,可以得到R1 = R;因此,这个矩阵通常是用来作为初始的颜色矩阵来使用,他不会对原有颜色进行任何改变

那么当我们要变换颜色值的时候通常有两种方法,一个是直接改变颜色的offset,即修改颜色的分量。另一种方法直接改变RGBA值的系数来改变颜色分量的值。

改变偏移量

从前面的分析中,可以知道要修改R1的值,只要将第五列的值进行修改即可,即改变颜色的偏移量,其它值保存初始矩阵的值,如图所示的颜色矩阵实现了这样的效果。

在上面这个矩阵中,我们修改了R,G,所对应的颜色偏移量,那么最后的处理结果,就是图像的红色绿色分别增加了100,而我们知道,红色混合绿色的到了黄色,所以最终的颜色处理结果就是让整个图片的色调偏黄色

 

2.改变颜色系数

如果修改颜色分量中的某个系数值而其他只依然保存初始矩阵的值

在上面这个矩阵中改变了G分量所对应的系数据g,这样在矩形运算后G分量会变成以前的两倍,最终效果就是图像的色调更加偏绿。

 

改变色光属性

图像的色调,饱和度,亮度这三个属性在图像处理中使用的非常多,因此在颜色矩阵中也封装了一些API来快速调用这些参数,而不用每次都去计算矩阵的值。下面通过一个实例来看看如何通过句正改变图像的色调,饱和度和亮度。

在Android中,系统封装了一个类——ColorMatrix,也就是前面所说的颜色矩阵。通过这个类,可以很方便地通过改变矩阵值来处理颜色的效果,创建一个ColorMatrix对象非常简单代码如下。

 

 

ColorMatrix colorMatrix = new ColorMatrix();

下面我们来处理不同的色光属性

色调

安卓安卓系统提供了setRotate()帮助我们设置三个颜色的色调,第一个参数系统分别使用0,1,2来代表red green blue 三个颜色的处理而第二个参数就是需要处理的值了

 

   ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.setRotate(0, 2);
        colorMatrix.setRotate(1, 4);
        colorMatrix.setRotate(2, 3);

 

通过这样的方法,可以为RGB三个颜色的分量重新设置不同的值了

饱和度

Android系统中提供了setSaturation()来设置颜色的饱和度,参数即代表饱和度的值,当饱和度为0的时候是灰色的

 

ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(10);

亮度

当三原色以相同的比例及及混合的时候,就会显示出白色,系统也正在使用这个原理来改变一个图像的亮度,代码如下,当亮度为零时图像会变成全黑。

 

ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setScale(10, 10, 10, 10);

除了单独使用上面的三种方法来经颜色效果的处理之外,Android系统还封装了矩阵的乘法运算,它提供了PostConcat方法来将矩阵的作用效果混合,从而叠加处理效果代码如下

 

  ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.postConcat(colorMatrix);
        colorMatrix.postConcat(colorMatrix);
        colorMatrix.postConcat(colorMatrix);

 

在了解了改变色光属性之后,我们回到现实中的例子,在本例子中,我们通过seekBar来改变不同的数值,并将这些数值作用到对应的矩阵中,最后通过PostConcat方法混合处理

滑动seekbar获得输入值的代码如下

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值