本文参考文献:《疯狂Android讲义 : 第2版 》
虽然前面大量介绍了使用自定义 View 来进行绘图,但 View 的绘图机制存在如下缺陷:
- View 缺乏双缓冲机制;
- 当程序需要更新 View 上的图像时,程序必须重绘 View 上显示的整张图片;
- 新线程无法直接更新 View 组件。
由于 View 存在上述缺陷,所以通过自定义 View 来实现绘图,尤其是游戏中的绘图时性能并不好。Android 提供了一个 SurfaceView 来代替 View,在实现游戏绘图方面,SurfaceView 比 View 更加出色,因此一般推荐使用 SurfaceView。
布局文件的内容如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/container"
>
<SurfaceView
android:id="@+id/show"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
主程序文件的代码如下:
package com.toby.personal.testlistview;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
public class MainActivity extends AppCompatActivity {
final private static String TAG = "Toby_Test";
private SurfaceHolder holder;
private Paint paint;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
paint = new Paint();
SurfaceView surface = (SurfaceView) findViewById(R.id.show);
holder = surface.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas canvas = holder.lockCanvas();
Bitmap back = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.plane);
canvas.drawBitmap(back, 0, 0, null);
holder.unlockCanvasAndPost(canvas);
holder.lockCanvas(new Rect(0, 0, 0, 0));
holder.unlockCanvasAndPost(canvas);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
surface.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int cx = (int) event.getX();
int cy = (int) event.getY();
Canvas canvas = holder.lockCanvas(new Rect(cx - 50, cy - 50, cx + 50, cy + 50));
canvas.save();
canvas.rotate(30, cx, cy);
paint.setColor(Color.RED);
canvas.drawRect(cx - 40, cy - 40, cx, cy, paint);
canvas.restore();
paint.setColor(Color.GREEN);
canvas.drawRect(cx, cy, cx + 40, cy + 40, paint);
holder.unlockCanvasAndPost(canvas);
}
return false;
}
});
}
}
实例:基于 SurfaceView 开发示波器
SurfaceView 与普通 View 还有一个重要的区别:View 的绘图必须在当前 UI 线程中进行;但是 SurfaceView 是由 SurfaceHolder 来完成的。这样就避免了阻塞主线程。
下面是主布局代码的内容:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/container"
android:background="@color/colorGray"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sin"
android:onClick="drawSinLine"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cos"
android:onClick="drawCosLine"
/>
</LinearLayout>
<SurfaceView
android:id="@+id/show"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
主程序文件的代码如下:
package com.toby.personal.testlistview;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends AppCompatActivity {
final private static String TAG = "Toby_Test";
private SurfaceHolder holder;
private Paint paint;
final int HEIGHT = 320;
final int WIDTH = 640;
final int X_OFFSET = 40;
private int cx = X_OFFSET;
int centerY = HEIGHT / 2;
Timer timer = new Timer();
TimerTask task = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SurfaceView surface = (SurfaceView) findViewById(R.id.show);
holder = surface.getHolder();
paint = new Paint();
paint.setStrokeWidth(3);
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
drawBack(holder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
timer.cancel();
}
});
}
private void drawBack(SurfaceHolder holder) {
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.GRAY);
Paint p = new Paint();
p.setColor(Color.BLACK);
p.setStrokeWidth(3);
canvas.drawLine(X_OFFSET, centerY, WIDTH, centerY, p);
canvas.drawLine(X_OFFSET, 40, X_OFFSET, HEIGHT, p);
holder.unlockCanvasAndPost(canvas);
holder.lockCanvas(new Rect(0, 0, 0, 0));
holder.unlockCanvasAndPost(canvas);
}
public void drawSinLine(View view) {
doDraw(view);
}
private void doDraw(final View source) {
drawBack(holder);
cx = X_OFFSET;
if (task != null) {
task.cancel();
}
task = new TimerTask() {
@Override
public void run() {
final double radian = (cx - X_OFFSET) * 2 * Math.PI / 150;
final int cy = source.getId() == R.id.sin
? centerY - (int) (100 * Math.sin(radian))
: centerY - (int) (100 * Math.cos(radian));
paint.setColor(source.getId() == R.id.sin ? Color.GREEN : Color.RED);
Canvas canvas = holder.lockCanvas(new Rect(cx, cy - 2, cx + 2, cy + 2));
canvas.drawPoint(cx, cy, paint);
cx++;
if (cx > WIDTH) {
task.cancel();
task = null;
}
holder.unlockCanvasAndPost(canvas);
}
};
timer.schedule(task, 0, 30);
}
public void drawCosLine(View view) {
doDraw(view);
}
}
该示例的运行效果如下:
显示效果
网友评论