SurfaceView的简单使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SurfaceView的简单使用相关的知识,希望对你有一定的参考价值。

参考技术A public class MainActivity extends AppCompatActivity implements View.OnClickListener

protected Button mBufferBtn;

protected Button mVideoBtn;

@Override

protected void onCreate(Bundle savedInstanceState)

super.onCreate(savedInstanceState);

super.setContentView(R.layout.activity_main);

initView();



@Override

public void onClick(View view)

Intent intent = new Intent();

if (view.getId() == R.id.button)

intent.setClass(this, BufferActivity.class);

else if (view.getId() == R.id.button)

intent.setClass(this, VideoActivity.class);



startActivity(intent);



private void initView()

mBufferBtn = (Button) findViewById(R.id.button2);

mBufferBtn.setOnClickListener(MainActivity.this);

mVideoBtn = (Button) findViewById(R.id.button2);

mVideoBtn.setOnClickListener(MainActivity.this);





public class BufferActivity extends AppCompatActivity implements SurfaceHolder.Callback

protected SurfaceView mSurfaceView;

private SurfaceHolder mHolder;

@Override

protected void onCreate(Bundle savedInstanceState)

super.onCreate(savedInstanceState);

super.setContentView(R.layout.activity_buffer);

initView();

initSurfaceHolder();



// 初始化Surface的管理者

private void initSurfaceHolder()

mHolder = mSurfaceView.getHolder();

// 添加管理生命周期的接口回调

mHolder.addCallback(this);



private void initView()

mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);



// 缓冲区创建

@Override

public void surfaceCreated(SurfaceHolder holder)

Log.d("1507", "surfaceCreated");

new DrawThread().start();



// 缓冲区内容改变(子线程渲染UI的过程)

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)

Log.d("1507", "surfaceChanged");



// 缓冲区销毁

@Override

public void surfaceDestroyed(SurfaceHolder holder)

Log.d("1507", "surfaceDestroyed");



// 绘制UI的子线程

private class DrawThread extends Thread

@Override

public void run()

super.run();

// 创建画笔

Paint paint = new Paint();

paint.setColor(Color.GREEN);// 画笔颜色

paint.setStrokeWidth(10);// 画笔粗细。注意:Java中设置的尺寸单位都是px

paint.setStyle(Paint.Style.FILL_AND_STROKE);// 设置实心

paint.setAntiAlias(true);// 设置是否抗锯齿

// 获取SurfaceView的盖度

int height = mSurfaceView.getHeight();

Canvas canvas = null;

for (int i = 0; i < height; i+= 5)

// 获取Surface中的画布

canvas = mHolder.lockCanvas();// 锁定画布

// 使用画笔在画布上绘制指定形状

canvas.drawCircle(100, i + 50, 50, paint);// 圆心x坐标,圆心y坐标,半径,画笔

// 缓冲区的画布绘制完毕,需要解锁并提交给窗口展示

mHolder.unlockCanvasAndPost(canvas);

//                try

//                    Thread.sleep(100);

//                catch (InterruptedException e)

//                    e.printStackTrace();

//               









public class VideoActivity extends AppCompatActivity implements View.OnClickListener

protected MyVideoSurfaceView mSurfaceView;

protected Button mPlayBtn;

@Override

protected void onCreate(Bundle savedInstanceState)

super.onCreate(savedInstanceState);

super.setContentView(R.layout.activity_video);

initView();



// 运行、可见

@Override

protected void onStart()

super.onStart();



// 可交互

@Override

protected void onResume()

super.onResume();



private void play()

String videoPath = Environment.getExternalStorageDirectory().getPath() +

"/VID_20171117_144736.3gp";// 外部存储根路径

mSurfaceView.playVideo(videoPath);



private void initView()

mSurfaceView = (MyVideoSurfaceView) findViewById(R.id.surface_view);

mPlayBtn = (Button) findViewById(R.id.play_btn);

mPlayBtn.setOnClickListener(VideoActivity.this);



@Override

public void onClick(View view)

if (view.getId() == R.id.play_btn)

play();







public class MyVideoSurfaceView extends SurfaceView implements SurfaceHolder.Callback

private SurfaceHolder mHolder;

private MediaPlayer mMediaPlayer;

public MyVideoSurfaceView(Context context, AttributeSet attrs)

super(context, attrs);

init();



private void init()

// 获取Surface换朝哪个区的持有者

mHolder = getHolder();

mHolder.addCallback(this);



// 设置播放源

public void playVideo(String path)

if (mMediaPlayer == null)

mMediaPlayer = new MediaPlayer();



try

// 设置播放源

mMediaPlayer.setDataSource(path);

// 设置多媒体的显示部分:使用SurfaceHolder渲染画面

mMediaPlayer.setDisplay(mHolder);

mMediaPlayer.prepare();

mMediaPlayer.start();

catch (IOException e)

e.printStackTrace();





@Override

public void surfaceCreated(SurfaceHolder holder)



@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)



@Override

public void surfaceDestroyed(SurfaceHolder holder)

mMediaPlayer.release();

mMediaPlayer = null;



Android自定义SurfaceView简单实现烟花效果

烟花效果

实现原理

  • SurfaceView + HandlerThread
    为什么使用SurfaceView?
    因为SurfaceView在子线程刷新不会阻塞主线程,适用于界面频繁更新、对帧率要求较高的情况,SurfaceView可以控制刷新频率,比如10ms刷新一次,SurfaceView底层利用双缓存机制,绘图时不会出现闪烁问题。
  • ValueAnimator控制位移、缩放和透明度

总的来说,非常简单就能实现,利用ValueAnimator属性动画控制两张图片的位移、缩放和透明度变化,将这些动画效果组合起来就行了,直接贴代码。

代码实现

public class FireworkShow extends SurfaceView implements SurfaceHolder.Callback 

    private DrawTask mDrawTask; //绘制UI线程
    private DrawView mLauncherView; //烟花爆炸的引信
    private DrawView mFirework; //爆炸的烟花
    private int mWidth; //控件宽度
    private int mHeight; //控件高度
    private Paint mPaint;
    private int top; //引信上升的位移
    private int lx, ly; //引信消失的位置
    private ValueAnimator mLauncherRiseAnimator; //引信上升效果
    private ValueAnimator mLauncherAlphaAnimator; //引信消失效果
    private ValueAnimator mBoomAnimator; //烟花爆炸效果
    private ValueAnimator mBoomEndAnimator; //烟花爆炸消失效果

    private boolean isDisappear = true; //烟花是否消失

    private static final int MSG_DRAW = 1;

    //获取Looper
    private static final HandlerThread mHandlerThread = new HandlerThread(FireworkShow.class.getName());
    static 
        mHandlerThread.start();
    
    //Handler消息处理
    private final Handler.Callback mCallback = new Handler.Callback() 
        @Override
        public boolean handleMessage(@NonNull Message message) 
            if (message.what == MSG_DRAW) 
                mHandler.post(mDrawTask);
                mHandler.sendEmptyMessageDelayed(MSG_DRAW, 10);
            
            return true;
        
    ;
    //用Looper创建弱引用Handler
    private final WeakHandler mHandler = new WeakHandler(mCallback, mHandlerThread.getLooper());
    private static class WeakHandler extends Handler 
        private final WeakReference<Callback> mWeakReference;
        public WeakHandler(Callback callback, Looper looper) 
            super(looper);
            mWeakReference = new WeakReference<>(callback);
        

        @Override
        public void handleMessage(@NonNull Message msg) 
            Callback mCallback = mWeakReference.get();
            if (mCallback != null) 
                mCallback.handleMessage(msg);
            
        
    

    public FireworkShow(Context context) 
        this(context, null);
    

    public FireworkShow(Context context, AttributeSet attributeSet) 
        super(context, attributeSet);
        init();
    

    private void init() 
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        holder.setKeepScreenOn(true);
        holder.setFormat(PixelFormat.TRANSPARENT);

        mPaint = new Paint();
    

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) 
        if (mDrawTask == null) 
            mDrawTask = new DrawTask(surfaceHolder, this);
        
    

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int format, int width, int height) 
        this.mWidth = width;
        this.mHeight = height;
    

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) 

    

    @Override
    public void draw(Canvas canvas) 
        if (canvas == null) return;
        super.draw(canvas);
        //清空界面
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        if (mLauncherView == null || mFirework == null) return;

        mLauncherView.top = mHeight - mLauncherView.bitmap.getHeight() - top;
        mLauncherView.left = (mWidth - mLauncherView.bitmap.getWidth()) / 2;
        mPaint.setAlpha(mLauncherView.alpha);
        canvas.drawBitmap(mLauncherView.bitmap, mLauncherView.left, mLauncherView.top, mPaint);

        canvas.save();
        mPaint.setAlpha(mFirework.alpha);
        mFirework.left = (int) (lx - mFirework.bitmap.getWidth() * mFirework.scale / 2.0f);
        mFirework.top = (int) (ly - mFirework.bitmap.getHeight() * mFirework.scale / 2.0f);
        canvas.scale(mFirework.scale, mFirework.scale, mFirework.left, mFirework.top);
        canvas.drawBitmap(mFirework.bitmap, mFirework.left, mFirework.top, mPaint);
        canvas.restore();
    

    public void startFireworkShow() 
        mHandler.sendEmptyMessage(MSG_DRAW);
        initDrawView();
        //开始动画效果
        mLauncherRiseAnimator.start();
    

    public void stopFireworkShow() 
        mHandler.removeMessages(MSG_DRAW);
        mLauncherRiseAnimator.cancel();
        mLauncherAlphaAnimator.cancel();
        mBoomAnimator.cancel();
        mBoomEndAnimator.cancel();
    


    private void initDrawView() 
        mLauncherView = new DrawView();
        mLauncherView.bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.launcher);

        mFirework = new DrawView();
        mFirework.bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.firework);

        mLauncherRiseAnimator = ValueAnimator.ofInt(0, 1400);
        mLauncherRiseAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        mLauncherRiseAnimator.setDuration(4000);
        mLauncherRiseAnimator.addUpdateListener(valueAnimator -> top = (int) valueAnimator.getAnimatedValue());
        mLauncherRiseAnimator.addListener(new Animator.AnimatorListener() 
            @Override
            public void onAnimationStart(Animator animator) 
                isDisappear = false;
                //烟花不显示
                mFirework.alpha = 0;
                //引信发射
                mLauncherView.alpha = 255;
                mLauncherAlphaAnimator.start();
            

            @Override
            public void onAnimationEnd(Animator animator) 
                //记录引信消失的位置
                lx = mLauncherView.left;
                ly = mLauncherView.top;
            

            @Override
            public void onAnimationCancel(Animator animator) 

            

            @Override
            public void onAnimationRepeat(Animator animator) 

            
        );

        mLauncherAlphaAnimator = ValueAnimator.ofInt(255, 50);
        mLauncherAlphaAnimator.setInterpolator(new LinearInterpolator());
        mLauncherAlphaAnimator.setDuration(4000);
        mLauncherAlphaAnimator.addUpdateListener(valueAnimator -> mLauncherView.alpha = (int) valueAnimator.getAnimatedValue());
        mLauncherAlphaAnimator.addListener(new Animator.AnimatorListener() 
            @Override
            public void onAnimationStart(Animator animator) 

            

            @Override
            public void onAnimationEnd(Animator animator) 
                mLauncherView.alpha = 0; //引信消失
                //烟花显示
                mBoomAnimator.start();
            

            @Override
            public void onAnimationCancel(Animator animator) 

            

            @Override
            public void onAnimationRepeat(Animator animator) 

            
        );

        mBoomAnimator = ValueAnimator.ofFloat(0.0f, 2.0f);
        mBoomAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        mBoomAnimator.setDuration(2000);
        mBoomAnimator.setStartDelay(1000);
        mBoomAnimator.addUpdateListener(valueAnimator -> mFirework.scale = (float) valueAnimator.getAnimatedValue());
        mBoomAnimator.addListener(new Animator.AnimatorListener() 
            @Override
            public void onAnimationStart(Animator animator) 
                //烟花消失效果
                mBoomEndAnimator.start();
            

            @Override
            public void onAnimationEnd(Animator animator) 

            

            @Override
            public void onAnimationCancel(Animator animator) 

            

            @Override
            public void onAnimationRepeat(Animator animator) 

            
        );

        mBoomEndAnimator = ValueAnimator.ofInt(255, 0);
        mBoomAnimator.setInterpolator(new DecelerateInterpolator());
        mBoomEndAnimator.setDuration(7000);
        mBoomEndAnimator.addUpdateListener(valueAnimator -> mFirework.alpha = (int) valueAnimator.getAnimatedValue());
        mBoomEndAnimator.addListener(new Animator.AnimatorListener() 
            @Override
            public void onAnimationStart(Animator animator) 

            

            @Override
            public void onAnimationEnd(Animator animator) 
                isDisappear = true;
            

            @Override
            public void onAnimationCancel(Animator animator) 

            

            @Override
            public void onAnimationRepeat(Animator animator) 

            
        );
    

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) 
        if (isDisappear) mLauncherRiseAnimator.start();
        return true;
    

    //绘制元素
    private static class DrawView 
        int top;
        int left;
        Bitmap bitmap;
        float scale;
        int alpha;
    


    //绘制子线程
    private static class DrawTask implements Runnable 
        private final SurfaceHolder holder;
        private final FireworkShow fireworkShow;

        public DrawTask(SurfaceHolder holder, FireworkShow fireworkShow) 
            this.holder = holder;
            this.fireworkShow = fireworkShow;
        

        @Override
        public void run() 
            Canvas canvas = null;
            try 
                canvas = holder.lockCanvas();
                synchronized (holder) 
                    fireworkShow.draw(canvas);
                
             finally 
                if (canvas != null) 
                    holder.unlockCanvasAndPost(canvas);
                
            

        
    



activity_main.xml引用:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <com.jon.firework.widget.FireworkShow
        android:id="@+id/fs"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity使用:

public class MainActivity extends AppCompatActivity 

    private FireworkShow fs;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fs = findViewById(R.id.fs);
        fs.startFireworkShow();
    

    @Override
    protected void onDestroy() 
        super.onDestroy();
        fs.stopFireworkShow();
    


以上是关于SurfaceView的简单使用的主要内容,如果未能解决你的问题,请参考以下文章

Android自定义SurfaceView简单实现烟花效果

Android自定义SurfaceView简单实现烟花效果

Android自定义SurfaceView简单实现烟花效果

Android自定义SurfaceView简单实现烟花效果

2048游戏回顾一:使用SurfaceView创建游戏启动动画

使用MediaPlayer和SurfaceView播放视频