Chapter 10. 图形图像处理&动画

阅读原文

图形图像处理&动画

10.1 图形图像处理

在Android中,绘制图形时最常用就是Bitmap类、BitmapFactory类、Paint类和Canvas类。其中Bitmap类代表位图,BitmapFactory类顾名思义就是位图工厂,它就是一个工具类,Paint类代表画笔,Canvas代表画布。

10.2 Bitmap类

Bitmap类代表位图,是Android中一个非常重要的图像处理类。

10.2.1 Bitmap类的常用方法

常用方法名称 功能描述
static Bitmap createBitmap(int width,int height,Config config) 创建位图,width代表要创建的图片的宽度,height代表高度,config代表图片的配置信息
static Bitmap createBitmap(int colors[],int offset,int stride,int width,int height,Config config) 使用颜色数组创建一个指定宽高的位图,颜色数组的个数为width*height
static Bitmap createBitmap(Bitmap src) 使用源位图创建一个新的Bitmap
static Bitmap createBitmap(Bitmap source,int x,int y,int width ,int height) 从源位图的指定坐标开始"挖取"指定宽高的一块图像来创建新的Bitmap对象
static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height,Matrix m,boolean filter) 从源位图的指定坐标开始"挖取"指定宽高的一块图像来创建新的Bitmap对象,并按照Matrix规则被回收
final boolean isRecycled() 判断Bitmap对象是否被回收
void recycle() 回收Bitmap对象

Bitmap 的创建,如下所示:

Config config=Config.ARGB_4444;
Bitmap bitmap2=Bitmap.createBitmap(width,height,config);

需要注意的是,Config是Bitmap的内部类,它用来指定Bitmap的一些配置信息,这里的Config.ARGB_4444的意思为Bitmap的每个像素点占用内存2个字节。

10.2.2BitmapFactory 类

BitmapFactory类是一个工具类,主要用于从不同的数据源来解析、创建Bitmap对象。

BitmapFactory常用方法

常用方法名称 功能描述
static Bitmap decodeFile(String pathName) 从指定文件中解析、创建Bitmap对象
static Bitmap decodeStream(InputStream is) 从指定输入流中解析、创建Bitmap对象
static Bitmap decodeResource(Resources res,int id) 根据给定的资源id,从指定资源中解析、创建Bitmap对象。

通过上述几个方法可以解析内存中的图片文件并创立对应的Bitmap对象,具体代码如下:

Bitmap bitmap=BitmapFactory.decodeFile("/sdcard/meinv.jpg");

同样,也可以解析Drawable文件夹中的图片文件并创建相应的Bitmap对象,具体代码如下:

10.3 Piant 类

Paint类代表画笔,用来描述图形的颜色和风格,如线宽、颜色、透明度和填充效果等信息。使用Paint类时,首先要创建它的实例对象,然后通过该类提供的方法来更改Paint对象的默认设置。Paint类提供的常用方法如下所示

常用方法名称 功能描述
Piant() 创建一个Paint对象,并使用默认属性
sPaint(int flags) 创建一个Paint对象,并使用指定属性
void setARGB(int a,int r,int g,int b) 设置颜色,各参数值均为0-255之间的整数,几个参数分别用于表示透明度、红色、绿色和蓝色的值
native void setColor(int color) 设置颜色
native void setAlpha(int a) 设置透明度
native void setAntiAlias(boolean aa) 指定是否使用抗锯齿功能,如果使用会使绘图速度变慢
void setShadowLayer(float radius,float dx,float dy,int color) 设置阴影,参数radius为阴影的角度;dx和dy为阴影在x轴和y轴上的距离;color为阴影的颜色
void setTextAlign(Align align) 设置绘制文本的文字对齐方式、参数为Align.CENTER、Align.LEFT或Align.RIGHT
native void setTextSize(float textSize) 设置绘制文本时文字的大小
native void setFakeBoldText(boolean fakeBoldText) 设置绘制文字时是否为粗体文字
void setXfermode(Xfermode xfermode ) 设置图形重叠时的处理方式,如合并、变焦或并集,经常用来制作橡皮的擦除效果
native void setDeither(boolean dither ) 指定是否使用图像抖动处理,如果使用会使图像颜色更加平滑,饱满,清晰

定义一个画笔,并指定该画笔的颜色为红色,带一个灰色的阴影,具体代码如下:

Paint paint=new Paint();
paint.setColor(Color.RED);
paint.setShadowLayer(2,3,3,Color.GRAY);

10.4 Canvas 类

在Android中绘图,需要创建一个继承自View的视图,并在该类中重写器onDraw(Canvas canvas)方法,然后在Activity中添加视图。Canvas类代表画布,通过该类提供的方法,可以绘制各种图形(如矩形、圆形、线条),Canvas提供的常用绘图方法如表所示:

常用方法名称 功能描述
void drawRect(Rect r,Paint paint) 使用画笔画出指定矩形
void drawOval(RectF oval,Paint paint) 使用画笔画出指定椭圆
void drawCircle(flaot cx,float cy,flaot radius,Paint paint) 使用画笔在指定位置画出指定半径的圆
void drawLine(flaot startX,float startY,float stopX,float stopY,Paint paint) 使用画笔在指定位置画线
void drawRounRece(RectF r,float rx,float ry,Paint paint) 使用画笔绘制指定圆角矩形,其中rx表示X轴圆角半径,ry表示Y轴圆角半径

使用Canvas示例

public class MyPicture extends View
{
    public MyPicture(Context context,AttributeSet atts)
    {
        super(context, attrs);
    }

    @override
    protected void onDraw(Canvass canvas)
    {
        super.onDraw(canvas);
        //创建画笔
        Paint paint=new Paint();
        paint.setColor(Color.RED);
        paint.setShadowLayer(2,3,3,Color.GRAY);

        //构建矩形对象并为其指定位置、宽高
        Rect r=new Rect(40,40,200,100);

        //调用Canvas中绘制矩形的方法
        canvas.drawRect(r,paint);
    }
}

上述代码自定义了一个控件,并在控件上绘制了一个矩形。在布局文件将该控件的全路径名作为使用该控件的标签即可使用,其他使用方法与普通控件一样,具体代码如下:



10.5 Matrix类

对图片添加特效需要使用Matrix类,例如,对图片进行旋转、缩放、倾斜等,Matrix类提供了一系列方法用于对图片进行操作

常用方法名称 功能描述
public Matrix() 创建一个唯一的Matrix对象
public void setRotate(float degrees) 将Matrix对象围绕(0,0)旋转degrees度
public void setRotate(flaot degrees,float px,flaot py) 将Matrix对象围绕指定位置(px,py)旋转degrees度
public void setScale(flaot sx,float sy) 对Matrix对象进行缩放,参数sx代表X轴上的缩放比例,sy代表Y轴上的缩放比例
public void setScale(flaot sx,float sy,flaot px,float py) 让Matrix对象以(px,py)为轴心,在X轴上缩放sx,在Y轴上缩放sy
public void setSkew(flaot kx,float ky) 让Matrix对象倾斜,在X轴上倾斜kx,在Y轴上倾斜ky
public void setSkew(flaot kx,float ky,flaot px,float py) 让Matrix对象以(px,py)为轴心,在X轴上倾斜kx,在Y轴上倾斜ky
public void setTranslate(flaot dx,float dy) 平移Matrix对象,(dx,dy)为Matrix平移后的坐标

通过matrix类的setRotate()方法为图片添加旋转特效示例代码

ImageView mImageView=(ImageView)findViewById(R.id.imgv);

//从资源文件中解析一张Bitmap
Bitmap bitmap=BitmapFactory.decoderResource(getResource(),R.drawable.ic_launcher);

//创建一张与Bitmap宽高、配置信息一样的图片
Bitmap alterbitmap=Bitmap.createBitmap(bitmap.getWidth()*2,bitmap.getHeight()*2,bitmap.getConfig());

//创建一个Canvas对象
Canvas canvas=new Paint();

//创建画笔对象
Panit paint=new Paint();

//为画笔设置颜色
paint.setColor(Color.BLACK);

//创建Matrix对象
Matrix matrix=new Matrix();

//设置Matrix旋转30度
matrix.setRotate(30);

//在alterBitmap上画图
canvas.drawBitmap(bitmap,matrix,paint);

//设置ImageView的背景
mImageView.setImageBitmap(alterbitmap);

上述代码中,首先获取到需要处理的图片对象,然后创建Bitmap对象,用于获取图片信息,接着创建Canvas对象和Paint对象,最后创建Matrix对象,通过该对象设置图片进行旋转30度。

其实,对图片添加特效的过程都是类似的,只不过调用的方法不同而已。例如,对图片进行缩放操作可以使用matrix.setScale(2.0f,0.5f),对图片进行倾斜操作可以使用matrix.setSkew(0.5f,1.0f),对图片进行平移操作可以使用matrix.setTranslate(6.,20);

需要注意的是,从资源文件中解析生成Bitmap后,不能再使用Canvas canvas=new Canvas(alterbitmap);方式创建画布,因为从资源文件中解析生成的bitmap是不可变的,它的内容就是资源文件。因此,需要创建一张空白的图片alterbitmap,并使用alterbitmap创建画布。

案例----------刮刮卡

布局



    

    



用户交互逻辑

public class MainActivity extends Activity
{

    private ImageView mImageView;
    private Bitmap alterbitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = (ImageView) findViewById(R.id.imgv);
        // 从资源文件中解析一张bitmap
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.guagua);
        alterbitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
        // 创建一个canvas对象
        Canvas canvas = new Canvas(alterbitmap);
        // 创建画笔对象
        Paint paint = new Paint();
        // 为画笔设置颜色
        paint.setColor(Color.BLACK);
        paint.setAntiAlias(true);
        // 创建Matrix对象
        Matrix matrix = new Matrix();
        // 在alterBitmap上画图
        canvas.drawBitmap(bitmap, matrix, paint);
        // 设置ImageView的背景
        mImageView.setImageBitmap(alterbitmap);
        mImageView.setOnTouchListener(new OnTouchListener()
        {
            @Override
            public boolean onTouch(View v, MotionEvent event)
            {
                try
                {
                    switch (event.getAction())
                    {
                    case MotionEvent.ACTION_DOWN:
                        Toast.makeText(MainActivity.this, "手指触下", 0).show();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        Toast.makeText(MainActivity.this, "手指移动(" + event.getX() + "," + event.getY() + ")", 0).show();
                        int x = (int) event.getX();
                        int y = (int) event.getY();
                        for (int i = -10; i < 10; i++)
                        {
                            for (int j = -10; j < 10; j++)
                            {
                                if (Math.sqrt((i * i) + (j * j)) <= 10)
                                {
                                    alterbitmap.setPixel(x + i, y + j, Color.TRANSPARENT);
                                }
                            }
                        }
                        mImageView.setImageBitmap(alterbitmap);
                        break;
                    case MotionEvent.ACTION_UP:
                        Toast.makeText(MainActivity.this, "手指松开", 0).show();
                        break;
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
                return true;// 消费掉该触摸事件
            }
        });
    }
}

上述代码用到了ImageView的触摸事件OnTouchListener,如果View设置了触摸监听,那么当手指触碰到该View时就会触发监听中的onTouch()方法。开发者可以在该方法中处理不同的触摸事件(例如,手指按下、松开、移动等)如果需要处理该事件,则在onTouch()方法中返回true表示该事件被销毁了,否则返回false代表该事件没有被销毁。


10.6 动画

Android中的动画通常可以分为逐帧动画和补间动画两种

View动画是对View的影像做操作,他并不能真正改变View的位置参数,包括宽和高,并且如果希望动画后的状态得以保留还必须将fillAfter属性设置为true,否则动画完成后其动画结果会消失。

扩展知识-动画的分类

Android包含三种动画:View Animation(补间)、 Drawable Animation(帧动画)、Property Animation(Android3.0新引入)。

View Animation(视图动画/补间动画):

基于View的渐变动画,她只改变了View的绘制效果,而实际属性值未变。比如动画移动一个按钮位置,但按钮点击的实际位置仍未改变。在代码中定义动画,可以参考AnimationSet类和Animation的子类;而如果使用XML,可以在res/anim/文件夹中定义XML文件。

Drawable Animation(帧动画):

加载一系列Drawable资源来创建动画,这种传统动画某种程度上就是创建不同图片序列,顺序播放,就像电影胶片。在代码中定义动画帧,使用AnimationDrawable类;XML文件能更简单的组成动画帧,在res/drawable文件夹,使用采用来定义不同的帧。感觉只能设置的属性是动画间隔时间。

Property Animation(属性动画):

动画的对象除了传统的View对象,还可以是Object对象,动画之后,Object对象的属性值被实实在在的改变了。Property animation能够通过改变View对象的实际属性来实现View动画。任何时候View属性的改变,View能自动调用invalidate()来试试刷新。


10.6.1 补间动画(Tween Animation)

补间动画是通过对View中的内容进行一系列的图形变换来实现动画效果,其中图形变化包括平移、缩放、旋转、改变透明度等。补间动画的效果可以通过xml文件定义也可以通过编码方式来实现,通常情况下以xml形式定义的动画都会放置在程序的res/anim(自定义的)文件下。

在Android中,提供了4种补间动画,分别是透明度渐变动画(AlpahAnimation),旋转动画(RotateAnimation)、缩放动画(ScaleAnimation)、平移动画(TranslateAnimation)。

透明度渐变动画(AlphaAnimation)

透明度渐变动画是指通过改变View组件透明度来实现的渐变效果。它主要通过为动画指定开始的透明度、结束时的透明度以及动画持续时间来创建动画。在xml文件中定义透明度渐变动画的基本语法格式如下:




        


上述代码定义了一个让View从完全透明到不透明、持续时间为1秒的动画。透明度渐变动画常用的熟悉如下所示:

  • android:interpolator:用于控制动画的变化速度,一般值为@android:anim/linear_interpolator(匀速变化)、@android:anim/accelerate_interpolator(开始慢,后来加速)等

  • android:repeatMode:用于设置动画重复的方式,可选值为reverse(反向)、restart(重新开始)。

  • android:repeatCount:用于设置动画重复次数,属性值可以为正整数,也可以为infinite(无限循环)。

  • android:duration:用于指定动画播放时长

  • android:fromAlpha:用于指定动画开始时的透明度,0.0为完全透明,1.0为不透明

  • android:toAlpha:用于指定动画结束时透明度,0.0为完全透明,1.0为不透明


旋转动画(RotateAnimation)

旋转动画就是通过为动画指定开始时的旋转角度,结束时的旋转角度以及动画的播放时长来创建动画。在xml文件中定义旋转动画的基本语法格式:


    
    
            
    
    

上述代码定义了一个让View从0度旋转到180度持续时间为2秒的旋转动画。旋转动画常用属性如下:

  • android:fromDegrees:指定动画开始时的角度

  • android:toDegrees:指定动画结束时的角度

  • android:pivotX指定轴心的X坐标

  • android:pivotY指定轴心的Y坐标


缩放动画(ScaleAnimation)

缩放动画就是通过为动画指定开始时的缩放系数,结束时的缩放系数以及动画持续时长来创建动画的。在xml文件中定义缩放动画的基本语法格式如下:


    
    
            
    
    

上述代码定义了一个让View在X轴上放大两倍,Y轴上缩小1/2的缩放动画。缩放动画的常用属性如下:

  • android:fromXScale:指定动画开始时X轴上的缩放系数,值为1.0表示不变化
  • android:fromYScale:指定动画开始时Y轴上的缩放系数,值为1.0表示不变化
  • android:toXScale:指定动画结束时X轴上的缩放系数,值为1.0表示不变化
  • android:toYScale:指定动画结束时Y轴上的缩放系数,值为1.0表示不变化

平移动画

平移动画就是通过为动画指定开始时位置,结束位置以及动画持续时长来创建动画的。在xml文件中定义缩放动画的基本语法格式如下:


    
    
            
    
    

上述代码定义了一个让View在从(50,50)平移到(200,200)持续时间为2秒。需要注意的是这里的坐标并不是屏幕像素的坐标,而是相对于View的所在位置的坐标。如果开始位置为(0,0)即表示在View最开始的地方平移(即布局文件定义View所在的位置)

平移动画的常用属性如下:

  • android:fromXDelta:指定动画开始时View的X轴坐标
  • android:fromYDelta:指定动画开始时View的Y轴坐标
  • android:toXDelta:指定动画结束时View的X轴坐标
  • android:toYDelta:指定动画结束时View的Y轴坐标

10.6.2补间动画案例

public class MainActivity extends Activity implements OnClickListener
{

    private ImageView img01;
    private ImageView img02;
    private ImageView img03;
    private ImageView img04;

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        img01 = (ImageView) findViewById(R.id.img01);
        img02 = (ImageView) findViewById(R.id.img02);
        img03 = (ImageView) findViewById(R.id.img03);
        img04 = (ImageView) findViewById(R.id.img04);
        img01.setOnClickListener(this);
        img02.setOnClickListener(this);
        img03.setOnClickListener(this);
        img04.setOnClickListener(this);
    }

    public void onClick(View v)
    {
        switch (v.getId())
        {
        case R.id.img01:
            Animation ani1 = AnimationUtils.loadAnimation(this, R.anim.alpha_animation);
            img01.startAnimation(ani1);
            break;

        case R.id.img02:
            Animation ani2 = AnimationUtils.loadAnimation(this, R.anim.scale_animation);
            img02.startAnimation(ani2);
            break;

        case R.id.img03:
            Animation ani3 = AnimationUtils.loadAnimation(this, R.anim.translate_animation);
            img03.startAnimation(ani3);
            break;

        case R.id.img04:
            Animation ani4 = AnimationUtils.loadAnimation(this, R.anim.rotate_animation);
            img04.startAnimation(ani4);
            break;

        }
    }

}

从上述代码可以看出,XML中定义的动画是通过AnimationUtils.loadAnimation(this,R.anim.alpha):加载的,最后通过startAnimation(loadAnimation):将动画设置到ImageView中


10.6.3 通过代码创建

Tween动画也可以在代码中定义,在代码中定义4种Tweed动画时需要用到AlphaAnimation、ScaleAnimation、TranslateAnimation、RotateAnimation类。具体代码如下所示:

public class MainActivity extends Activity
{
    private ImageView imageview;

    @override
    onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView=(ImageView)findViewById(R.id.img01);

        //创建一个渐变透明度的动画、透明度为0.0f~1.0f(完全不透明)
        AlaphAnimation alaphAnimation=new AlphaAnimation(0.0f,1.0f);

        //设置动画时长
        alaphAnimation.setDuration(5000);

        //设置重复方式
        alaphAnimation.setRepeatMode(AlphaAnimation.REVERSE);

        //动画重复次数
        alaphAnimation.setRepeatCount(AlphaAnimation。INFINITE);

        imageVive.startAnimation(alphaAnimation);
    }

}

上述代码定义了一个透明度渐变动画,并让View播放了该动画,其他三种Tween动画也可以用这种方式定义。


10.6.4 逐帧动画(Frame Animation)

逐帧动画时按顺序播事先准备好的静态图像,利用人眼的视觉暂留原理,该用户造成动画的错觉。放胶片
电影的原理与逐帧动画的原理是一样的,它们都是一张一张地播放事先准备好的静态图像。

定义逐帧动画的步骤如下:

  • 将准备好的图片放入程序的res/drawable目录下

  • 在res/drawable目录下定义动画文件,文件名称可以自定义,例如frame.xml

  • 为指定控件绑定动画效果,并调用AnimationDrawable类的start()方法开启动画

布局


    
    

创建Frame动画资源



    
    
    
    
    
    

逻辑代码

public class MainActivity extends Activity implements OnClickListener
{
    private ImageView iv_flower;
    private Button btn_start;
    private AnimationDrawable animation;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_flower = (ImageView) findViewById(R.id.iv_flower);
        btn_start = (Button) findViewById(R.id.btn_play);
        btn_start.setOnClickListener(this);
        // 拿到AnimationDrawable对象
        animation = (AnimationDrawable) iv_flower.getBackground();
    }

    public void onClick(View v)
    {
        // 播放动画
        if (!animation.isRunning())
        {
            animation.start();
            btn_start.setBackgroundResource(android.R.drawable.ic_media_pause);
        }
        else
        {
            animation.stop();
            btn_start.setBackgroundResource(android.R.drawable.ic_media_play);
        }
    }
}

10.6.5 通过代码创建帧动画

public class MainActivity extends Activity implements OnClickListener
    {
        private ImageView imageView;
        private Button btn_start;
        private AnimationDrawable animation;
    
        @SupperesLlint("NeWApi")
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main); 
            
            imageView=(ImageView)findViewById(R.id.img01);

            //拿到AnimationDrawable对象
            AnimationDrawable ad=new AnimationDrawable();
            imageView.setBackground(ad);
            //在、AnimationDrawable中添加一帧,并为其指定图片和播放时长
            //addFrame()方法添加图片时,需要将图片资源转换成Drawable对象
            ad.addFrame(getResources().getDrawable(R.drawable.girl_1),200);
            ad.addFrame(getResources().getDrawable(R.drawable.girl_2),200);
            ad.addFrame(getResources().getDrawable(R.drawable.girl_3),200);
            //循环播放
            ad.setOneShot(false);
            //播放Frame动画
            ad.start();
        }

    }

10.7 操作图片

Chapter 10. 图形图像处理&动画_第1张图片
image

Chapter 10. 图形图像处理&动画_第2张图片
image

Chapter 10. 图形图像处理&动画_第3张图片
image

10.8 制作.9.png图片

Chapter 10. 图形图像处理&动画_第4张图片
image

Chapter 10. 图形图像处理&动画_第5张图片
image

Chapter 10. 图形图像处理&动画_第6张图片
image

Chapter 10. 图形图像处理&动画_第7张图片
image

Chapter 10. 图形图像处理&动画_第8张图片
image

你可能感兴趣的:(Chapter 10. 图形图像处理&动画)