2019-06-06
Android图形处理概述
- 静态图形: 一般将这些图形文件作为资源文件添加到项目中即可。然后通过各种Drawable类来处理。
- 动态图形处理:需要用代码不停的绘制。涉及到画布Canvas, 画笔Paint等类。
2D类:
- android.graphics
- android.graphics.drawable
- android.graphics.drawable.shapes
- android.view.animation
3D类: - android.opengl
ImageView的显示图片方法
setImagexxx(),例如:
- setImageResource(int resId)
- setImageDrawable(Drawable drawable)
- setImageBitmap(Bitmap bm)
Bitmap和BitmapFactory:
BitmapFactory创建Bitmap,Bitmap对象会因为没有回收而导致OutOfMemory错误,使用isRecycled()判断是否已回收,recycle()强制一个对象回收自己
案例:
public class MainActivity extends Activity
{
String[] images = null;
AssetManager assets = null;
int currentImg = 0;
ImageView image;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
image = (ImageView) findViewById(R.id.image);
try
{
assets = getAssets();
// 获取/assets/目录下所有文件
images = assets.list("");
}
catch (IOException e)
{
e.printStackTrace();
}
// 获取next按钮
final Button next = (Button) findViewById(R.id.next);
// 为next按钮绑定事件监听器,该监听器将会查看下一张图片
next.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View sources)
{
// 如果发生数组越界
if (currentImg >= images.length)
{
currentImg = 0;
}
// 找到下一个图片文件
while (!images[currentImg].endsWith(".png")
&& !images[currentImg].endsWith(".jpg")
&& !images[currentImg].endsWith(".gif"))
{
currentImg++;
// 如果已发生数组越界
if (currentImg >= images.length)
{
currentImg = 0;
}
}
InputStream assetFile = null;
try
{
// 打开指定资源对应的输入流
assetFile = assets.open(images[currentImg++]);
}
catch (IOException e)
{
e.printStackTrace();
}
BitmapDrawable bitmapDrawable = (BitmapDrawable) image
.getDrawable();
// 如果图片还未回收,先强制回收该图片
if (bitmapDrawable != null
&& !bitmapDrawable.getBitmap().isRecycled()) // ①
{
bitmapDrawable.getBitmap().recycle();
}
// 改变ImageView显示的图片
image.setImageBitmap(BitmapFactory
.decodeStream(assetFile)); // ②
}
});
}
}
绘图:Canvas、Paint
Canvas提供的图形变换方法
- rotate(float degrees,float px,float py):对Canvas执行旋转
- scale(float sx,float sy,float px,float py):对Canvas执行缩放
- translate(float dx,float dy):移动Canvas
Canvas上的画笔:Paint - Paint类主要用于设置绘制风格,包括画笔颜色、画笔笔触粗细、填充风格。
案例,跑一遍:
public class MyView extends View
{
public MyView(Context context, AttributeSet set)
{
super(context, set);
}
@Override
// 重写该方法,进行绘图
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
// 把整张画布绘制成白色
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
// 去锯齿
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4);
int viewWidth = this.getWidth();
// 绘制圆形
canvas.drawCircle(viewWidth / 10 + 10, viewWidth / 10 + 10
, viewWidth / 10, paint);
// 绘制正方形
canvas.drawRect(10 , viewWidth / 5 + 20 , viewWidth / 5 + 10
, viewWidth * 2 / 5 + 20 , paint);
// 绘制矩形
canvas.drawRect(10, viewWidth * 2 / 5 + 30, viewWidth / 5 + 10
, viewWidth / 2 + 30, paint);
RectF re1 = new RectF(10, viewWidth / 2 + 40
, 10 + viewWidth / 5 ,viewWidth * 3 / 5 + 40);
// 绘制圆角矩形
canvas.drawRoundRect(re1, 15, 15, paint);
RectF re11 = new RectF(10, viewWidth * 3 / 5 + 50
, 10 + viewWidth / 5 ,viewWidth * 7 / 10 + 50);
// 绘制椭圆
canvas.drawOval(re11, paint);
// 定义一个Path对象,封闭成一个三角形
Path path1 = new Path();
path1.moveTo(10, viewWidth * 9 / 10 + 60);
path1.lineTo(viewWidth / 5 + 10, viewWidth * 9 / 10 + 60);
path1.lineTo(viewWidth / 10 + 10, viewWidth * 7 / 10 + 60);
path1.close();
// 根据Path进行绘制,绘制三角形
canvas.drawPath(path1, paint);
// 定义一个Path对象,封闭成一个五角形
Path path2 = new Path();
path2.moveTo(10 + viewWidth / 15, viewWidth * 9 / 10 + 70);
path2.lineTo(10 + viewWidth * 2 / 15, viewWidth * 9 / 10 + 70);
path2.lineTo(10 + viewWidth / 5, viewWidth + 70);
path2.lineTo(10 + viewWidth / 10, viewWidth * 11/10 + 70);
path2.lineTo(10 , viewWidth + 70);
path2.close();
// 根据Path进行绘制,绘制五角形
canvas.drawPath(path2, paint);
// ----------设置填充风格后绘制----------
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
// 绘制圆形
canvas.drawCircle(viewWidth * 3 / 10 + 20, viewWidth / 10 + 10
, viewWidth / 10, paint);
// 绘制正方形
canvas.drawRect(viewWidth / 5 + 20 , viewWidth / 5 + 20
, viewWidth * 2 / 5 + 20 , viewWidth * 2 / 5 + 20 , paint);
// 绘制矩形
canvas.drawRect(viewWidth / 5 + 20, viewWidth * 2 / 5 + 30
, viewWidth * 2 / 5 + 20 , viewWidth / 2 + 30, paint);
RectF re2 = new RectF(viewWidth / 5 + 20, viewWidth / 2 + 40
, 20 + viewWidth * 2 / 5 ,viewWidth * 3 / 5 + 40);
// 绘制圆角矩形
canvas.drawRoundRect(re2, 15, 15, paint);
RectF re21 = new RectF(20 + viewWidth / 5, viewWidth * 3 / 5 + 50
, 20 + viewWidth * 2 / 5 ,viewWidth * 7 / 10 + 50);
// 绘制椭圆
canvas.drawOval(re21, paint);
// 定义一个Path对象,封闭成一个三角形
Path path3 = new Path();
path3.moveTo(20 + viewWidth / 5, viewWidth * 9 / 10 + 60);
path3.lineTo(viewWidth * 2 / 5 + 20, viewWidth * 9 / 10 + 60);
path3.lineTo(viewWidth * 3 / 10 + 20, viewWidth * 7 / 10 + 60);
path3.close();
// 根据Path进行绘制,绘制三角形
canvas.drawPath(path3, paint);
// 定义一个Path对象,封闭成一个五角形
Path path4 = new Path();
path4.moveTo(20 + viewWidth *4 / 15, viewWidth * 9 / 10 + 70);
path4.lineTo(20 + viewWidth / 3, viewWidth * 9 / 10 + 70);
path4.lineTo(20 + viewWidth * 2 / 5, viewWidth + 70);
path4.lineTo(20 + viewWidth * 3 / 10, viewWidth * 11/10 + 70);
path4.lineTo(20 + viewWidth / 5 , viewWidth + 70);
path4.close();
// 根据Path进行绘制,绘制五角形
canvas.drawPath(path4, paint);
// ----------设置渐变器后绘制----------
// 为Paint设置渐变器
Shader mShader = new LinearGradient(0, 0, 40, 60
, new int[] {Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW }
, null , Shader.TileMode.REPEAT);
paint.setShader(mShader);
//设置阴影
paint.setShadowLayer(25 , 20 , 20 , Color.GRAY);
// 绘制圆形
canvas.drawCircle(viewWidth / 2 + 30, viewWidth / 10 + 10
, viewWidth / 10, paint);
// 绘制正方形
canvas.drawRect(viewWidth * 2 / 5 + 30 , viewWidth / 5 + 20
, viewWidth * 3 / 5 + 30 , viewWidth * 2 / 5 + 20 , paint);
// 绘制矩形
canvas.drawRect(viewWidth * 2 / 5 + 30, viewWidth * 2 / 5 + 30
, viewWidth * 3 / 5 + 30 , viewWidth / 2 + 30, paint);
RectF re3 = new RectF(viewWidth * 2 / 5 + 30, viewWidth / 2 + 40
, 30 + viewWidth * 3 / 5 ,viewWidth * 3 / 5 + 40);
// 绘制圆角矩形
canvas.drawRoundRect(re3, 15, 15, paint);
RectF re31 = new RectF(30 + viewWidth *2 / 5, viewWidth * 3 / 5 + 50
, 30 + viewWidth * 3 / 5 ,viewWidth * 7 / 10 + 50);
// 绘制椭圆
canvas.drawOval(re31, paint);
// 定义一个Path对象,封闭成一个三角形
Path path5 = new Path();
path5.moveTo(30 + viewWidth * 2/ 5, viewWidth * 9 / 10 + 60);
path5.lineTo(viewWidth * 3 / 5 + 30, viewWidth * 9 / 10 + 60);
path5.lineTo(viewWidth / 2 + 30, viewWidth * 7 / 10 + 60);
path5.close();
// 根据Path进行绘制,绘制三角形
canvas.drawPath(path5, paint);
// 定义一个Path对象,封闭成一个五角形
Path path6 = new Path();
path6.moveTo(30 + viewWidth * 7 / 15, viewWidth * 9 / 10 + 70);
path6.lineTo(30 + viewWidth * 8 / 15, viewWidth * 9 / 10 + 70);
path6.lineTo(30 + viewWidth * 3 / 5, viewWidth + 70);
path6.lineTo(30 + viewWidth / 2, viewWidth * 11/10 + 70);
path6.lineTo(30 + viewWidth * 2 / 5 , viewWidth + 70);
path6.close();
// 根据Path进行绘制,绘制五角形
canvas.drawPath(path6, paint);
// ----------设置字符大小后绘制----------
paint.setTextSize(48);
paint.setShader(null);
// 绘制7个字符串
canvas.drawText(getResources().getString(R.string.circle)
, 60 + viewWidth * 3 / 5, viewWidth / 10 + 10, paint);
canvas.drawText(getResources().getString(R.string.square)
, 60 + viewWidth * 3 / 5, viewWidth * 3 / 10 + 20, paint);
canvas.drawText(getResources().getString(R.string.rect)
, 60 + viewWidth * 3 / 5, viewWidth * 1 / 2 + 20, paint);
canvas.drawText(getResources().getString(R.string.round_rect)
, 60 + viewWidth * 3 / 5, viewWidth * 3 / 5 + 30, paint);
canvas.drawText(getResources().getString(R.string.oval)
, 60 + viewWidth * 3 / 5, viewWidth * 7 / 10 + 30, paint);
canvas.drawText(getResources().getString(R.string.triangle)
, 60 + viewWidth * 3 / 5, viewWidth * 9 / 10 + 30, paint);
canvas.drawText(getResources().getString(R.string.pentagon)
, 60 + viewWidth * 3 / 5, viewWidth * 11 / 10 + 30, paint);
}
}
//布局
<org.crazyit.image.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
Path类
案例测试:
public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
}
class MyView extends View
{
float phase;
PathEffect[] effects = new PathEffect[7];
int[] colors;
private Paint paint;
Path path;
public MyView(Context context)
{
super(context);
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4);
// 创建并初始化Path
path = new Path();
path.moveTo(0, 0);
for (int i = 1; i <= 40; i++)
{
// 生成40个点,随机生成它们的Y坐标,并将它们连成一条Path
path.lineTo(i * 20, (float) Math.random() * 60);
}
// 初始化7个颜色
colors = new int[] { Color.BLACK, Color.BLUE, Color.CYAN,
Color.GREEN, Color.MAGENTA, Color.RED, Color.YELLOW };
}
@Override
protected void onDraw(Canvas canvas)
{
// 将背景填充成白色
canvas.drawColor(Color.WHITE);
// -----------下面开始初始化7种路径效果----------
// 不使用路径效果
effects[0] = null;
// 使用CornerPathEffect路径效果
effects[1] = new CornerPathEffect(10);
// 初始化DiscretePathEffect
effects[2] = new DiscretePathEffect(3.0f, 5.0f);
// 初始化DashPathEffect
effects[3] = new DashPathEffect(new float[] { 20, 10, 5, 10 },
phase);
// 初始化PathDashPathEffect
Path p = new Path();
p.addRect(0, 0, 8, 8, Path.Direction.CCW);
effects[4] = new PathDashPathEffect(p, 12, phase,
PathDashPathEffect.Style.ROTATE);
// 初始化ComposePathEffect
effects[5] = new ComposePathEffect(effects[2], effects[4]);
effects[6] = new SumPathEffect(effects[4], effects[3]);
// 将画布移动到(8、8)处开始绘制
canvas.translate(8, 8);
// 依次使用7种不同路径效果、7种不同的颜色来绘制路径
for (int i = 0; i < effects.length; i++)
{
paint.setPathEffect(effects[i]);
paint.setColor(colors[i]);
canvas.drawPath(path, paint);
canvas.translate(0, 60);
}
// 改变phase值,形成动画效果
phase += 1;
invalidate();
}
}
}
图形特效处理
Matrix是Android提供的一个矩阵工具类,它本身并不能对图像或组件进行变换(平移、旋转、缩放、倾斜),但它可与其他API结合起来控制图形、组件的变换。
使用Matrix控制图像或组件变换的步骤如下:
- 获取Matrix对象,该Matrix对象既可新创建,也可直接获取其他对象内封装的Matrix;
- 调用Matrix的方法进行平移、旋转、缩放、倾斜等;
- 将程序对Matrix所做的变换应用到指定图像或组件。
动画
两种动画实现方式:
逐帧动画--Frame。
按照时间的先后顺序,一次播放排列好的图片。类似于放电影补间动画--Tween。
通过对场景里的对象不断做图像变换(平移、缩放、旋转)产生动画效果
查看案例
public class MainActivity extends Activity
{
// 记录蝴蝶ImageView当前的位置
private float curX = 0;
private float curY = 30;
// 记录蝴蝶ImageView下一个位置的坐标
float nextX = 0;
float nextY = 0;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取显示蝴蝶的ImageView组件
final ImageView imageView = (ImageView)
findViewById(R.id.butterfly);
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg.what == 0x123)
{
// 横向上一直向右飞
if (nextX > 320)
{
curX = nextX = 0;
}
else
{
nextX += 8;
}
// 纵向上可以随机上下
nextY = curY + (float) (Math.random() * 10 - 5);
// 设置显示蝴蝶的ImageView发生位移改变
TranslateAnimation anim = new TranslateAnimation(
curX, nextX, curY, nextY);
curX = nextX;
curY = nextY;
anim.setDuration(200);
// 开始位移动画
imageView.startAnimation(anim); // ①
}
}
};
final AnimationDrawable butterfly = (AnimationDrawable)
imageView.getBackground();
imageView.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
// 开始播放蝴蝶振翅的逐帧动画
butterfly.start(); // ②
// 通过定制器控制每0.2秒运行一次TranslateAnimation动画
new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
handler.sendEmptyMessage(0x123);
}
}, 0, 200);
}
});
}
}