Android 自定义 View 之使用 SurfaceView 实现动画

本文参考文献:《疯狂Android讲义 : 第2版

虽然前面大量介绍了使用自定义 View 来进行绘图,但 View 的绘图机制存在如下缺陷:

  1. View 缺乏双缓冲机制;
  2. 当程序需要更新 View 上的图像时,程序必须重绘 View 上显示的整张图片;
  3. 新线程无法直接更新 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);
    }
}

该示例的运行效果如下:

显示效果

转载于:https://my.oschina.net/imtoby/blog/883066

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值