美文网首页Android 成长笔记
Android 自定义 View 之使用 SurfaceView

Android 自定义 View 之使用 SurfaceView

作者: 赵者也 | 来源:发表于2017-04-19 21:58 被阅读99次

    本文参考文献:《疯狂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);
        }
    }
    

    该示例的运行效果如下:

    显示效果

    相关文章

      网友评论

        本文标题:Android 自定义 View 之使用 SurfaceView

        本文链接:https://www.haomeiwen.com/subject/esbqzttx.html