阅读原文
图形图像处理&动画
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();
}
}