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();
}
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)); // ②
}
});
}
}
结果Gif
二、绘图
在为我们的应用界面添砖加瓦的过程中,除了可以使用已有的图片外,Android应用还经常需要在运行过程中动态的生成图片,而这样就需要借助于Android的绘图支持了。
(1)绘图基础——Canvas、Paint
Android的绘图与Swing中的绘图思路类似即开发一个自定义类,然后让该类继承JPanel,之后重写其中的paint(Graphics g)方法即可。而我们Android中的绘图应继承View组件,并重写其中的onDraw(Canvas canvas)即可,光说不练假把式,接下来我们用一段代码绘制几个集合图形。
关键代码如下:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
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.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);
// 为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.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);
}
}
结果截图如下:
(2)Path类
Android提供的Path是一个非常有用的类,它可以在View上将几个点连成一条路径,然后可以调用Canvas中的drawPath(path,paint)方法沿着该路径进行绘图,这些所谓的绘图效果说起来大家很难理解,下面我们通过一段代码来让大家更好的理解一下这些效果,这个应用绘制了7条路径:
关键代码如下:
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposePathEffect;
import android.graphics.CornerPathEffect;
import android.graphics.DashPathEffect;
import android.graphics.DiscretePathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathDashPathEffect;
import android.graphics.PathEffect;
import android.graphics.SumPathEffect;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
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++)
{
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);
// 不使用路径效果
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();
}
}
}
结果Gif
三、实例训练
让我们根据今天所学做两个小小的实例练习吧。
(1)手绘画板
我们主要要实现一个画板,当我们在触摸屏上移动时,就可以在屏幕上绘制任意的图案。
表面上看起来我们可以在画板上自由地画画包括直、曲线,实际上我们还是利用Canvas的drawLine()方法画直线线。当我们在画板上移动时,两次拖动时间发生点的距离很小,我们用多条极短的直线将他们连起来就好像我们画的是曲线。主要我们还是借助Android提供的Path类。
关键代码如下:
View代码:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class DrawView extends View
{
// 定义记录前一个拖动事件发生点的坐标
float preX;
float preY;
private Path path;
public Paint paint = null;
// 定义一个内存中的图片,该图片将作为缓冲区
Bitmap cacheBitmap = null;
// 定义cacheBitmap上的Canvas对象
Canvas cacheCanvas = null;
public DrawView(Context context, int width , int height)
{
super(context);
// 创建一个与该View相同大小的缓存区
cacheBitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
cacheCanvas = new Canvas();
path = new Path();
// 设置cacheCanvas将会绘制到内存中的cacheBitmap上
cacheCanvas.setBitmap(cacheBitmap);
// 设置画笔的颜色
paint = new Paint(Paint.DITHER_FLAG);
paint.setColor(Color.RED);
// 设置画笔风格
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
// 反锯齿
paint.setAntiAlias(true);
paint.setDither(true);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
// 获取拖动事件的发生位置
float x = event.getX();
float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
// 从前一个点绘制到当前点之后,把当前点定义成下次绘制的前一个点
path.moveTo(x, y);
preX = x;
preY = y;
break;
case MotionEvent.ACTION_MOVE:
// 从前一个点绘制到当前点之后,把当前点定义成下次绘制的前一个点
path.quadTo(preX, preY, x, y);
preX = x;
preY = y;
break;
case MotionEvent.ACTION_UP:
cacheCanvas.drawPath(path, paint); // ①
path.reset();
break;
}
invalidate();
// 返回true表明处理方法已经处理该事件
return true;
}
@Override
public void onDraw(Canvas canvas)
{
Paint bmpPaint = new Paint();
// 将cacheBitmap绘制到该View组件上
canvas.drawBitmap(cacheBitmap, 0, 0, bmpPaint); // ②
// 沿着path绘制
canvas.drawPath(path, paint);
}
}
颜色、大小代码:
<?xml version="1.0" encoding="utf-8"?><item android:id=“@+id/red”
android:title=“@string/color_red”/>
<item android:id=“@+id/green”
android:title=“@string/color_green”/>
<item android:id=“@+id/blue”
android:title=“@string/color_blue”/>
<item android:id=“@+id/width_1”
android:title=“@string/width_1”/>
<item android:id=“@+id/width_3”
android:title=“@string/width_3”/>
<item android:id=“@+id/width_5”
android:title=“@string/width_5”/>
结果Gif
(2)弹球小游戏
该应用实现了一个简单的弹球游戏,其中小球和球拍分别以圆形和矩形代替,小球开始以随机速度向下运动,遇到边框或球拍时小球反弹。球拍则由用户控制,当用户按下向A、向D键时,球拍将会向左、向右移动。
关键代码如下:
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.Window;
import android.view.WindowManager;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends Activity
{
private int tableWidth;
private int tableHeight;
private int racketY;
private final int RACKET_HEIGHT = 30;
private final int RACKET_WIDTH = 90;
private final int BALL_SIZE = 16;
private int ySpeed = 15;
Random rand = new Random();
private double xyRate = rand.nextDouble() - 0.5;
private int xSpeed = (int) (ySpeed * xyRate * 2);
// ballX和ballY代表小球的坐标
private int ballX = rand.nextInt(200) + 20;
private int ballY = rand.nextInt(10) + 20;
// racketX代表球拍的水平位置
private int racketX = rand.nextInt(200);
// 游戏是否结束的旗标
private boolean isLose = false;
private GameView contentView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 创建GameView组件
final GameView gameView = new GameView(this);
setContentView(gameView);
// 获取窗口管理器
WindowManager windowManager = getWindowManager();
Display display = windowManager.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
// 获得屏幕宽和高
tableWidth = metrics.widthPixels;
tableHeight = metrics.heightPixels;
racketY = tableHeight - 80;
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == 0x123) {
gameView.invalidate();
}
}
};
gameView.setOnKeyListener(new OnKeyListener() // ②
{
@Override
public boolean onKey(View source, int keyCode, KeyEvent event) {
// 获取由哪个键触发的事件
switch (event.getKeyCode()) {
// 控制挡板左移
case KeyEvent.KEYCODE_A:
if (racketX > 0) racketX -= 10;
break;
// 控制挡板右移
case KeyEvent.KEYCODE_D:
if (racketX < tableWidth - RACKET_WIDTH) racketX += 10;
break;
}
// 通知gameView组件重绘
gameView.invalidate();
return true;
}
});