阅读 1758

音视频入门系列之绘制图片三种方式

本文由玉刚说写作平台提供写作赞助,版权归玉刚说微信公众号所有
原作者:苍溟
版权声明:未经玉刚说许可,不得以任何形式转载

在 Android 音视频开发学习思路 里面,我们写到了,想要逐步入门音视频开发,就需要一步步的去学习整理,并积累。本文是音视频开发积累的第一篇。 对应的要学习的内容是:在 Android 平台绘制一张图片,使用3 种不同的 API,ImageView、SurfaceView、自定义 View。

ImageView 绘制图片

这个想必做过Android开发的都知道如何去绘制了。很简单:

//  ImageView 加载几种来源 
//(1) drawable/mipmap 中通过 R.drawabe.xxx 加载图片资源
//(2) assests或者sdcard的路径的资源
// 这里我以 assests 代表来加载资源
ImageView customImageView = findViewById(R.id.img_middle);
customImageView.setImageBitmap(Util.getImageFromAssetsFile(this"prettygirl.png"));
// Util.getImageFromAssetsFile(this, "prettygirl.png")
public static Bitmap getImageFromAssetsFile(Context context, String fileName) {
    Bitmap image = null;
    AssetManager am = context.getResources().getAssets();
    try {
        InputStream is = am.open(fileName);
        image = BitmapFactory.decodeStream(is);
        is.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return image;
}
复制代码

很轻松,在界面上清晰的看到养眼的美女:

avatar
avatar

自定义 View 绘制图片

有些时候我们需要加载图片需要自定义View来加载图片,通过上边加载图片的方式加上我自己自定义View 经验,我们很快能写出下边通过自定义View的代码来加载图片。

/**
 * 自定义View 显示图片
 */

public class CustomImageView extends View {
    private Bitmap mBitmap;
    private Paint mPaint = new Paint();

    ···

    private void init() {
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        //设置抗锯齿
        mPaint.setAntiAlias(true);
    }

    public void setBitmap(Bitmap bitmap) {
        mBitmap = bitmap;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBitmap == null) {
            return;
        }
        canvas.drawBitmap(mBitmap, 00, mPaint);
    }
}
复制代码

SurfaceView 绘制图片

这个比 ImageView 绘制图片稍微复杂一点点,接下来呢,说说自己对它的理解

定义

SurfaceView是View的一个特殊子类,它的目的是另外提供一个线程进行绘制操作。

它的特性是:可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。

实现

首先继承SurfaceView并实现SurfaceHolder.Callback接口,使用接口的原因:因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始(Surface—表面,这个概念在 图形编程中常常被提到。基本上我们可以把它当作显存的一个映射,写入到Surface 的内容。

可以被直接复制到显存从而显示出来,这使得显示速度会非常快),而在Surface 被销毁之前必须结束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。

需要重写的方法如下:

//在surface的大小发生改变时激发、
public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}
//在创建时激发,一般在这里调用画图的线程。
public void surfaceCreated(SurfaceHolder holder){}
//销毁时激发,一般在这里将画图的线程停止、释放。
public void surfaceDestroyed(SurfaceHolder holder) {}
复制代码

使用SurfaceView大概流程:

  1. 继承SurfaceView并实现SurfaceHolder.Callback接口

  2. SurfaceView.getHolder()获得SurfaceHolder对象

  3. SurfaceHolder.addCallback(callback)添加回调函数

  4. SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布

  5. Canvas绘画

  6. SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。

这里用到了一个类SurfaceHolder,可以把它当成surface的控制器,就好比我们常用的MVC 设计模式,用来操纵Surface。处理它的Canvas上画的效果和动画,控制表面、大小、像素等。下面我说几个需要注意的方法:

// 给SurfaceView当前的持有者一个回调对象。
abstract void addCallback(SurfaceHolder.Callback callback);
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
abstract Canvas lockCanvas();
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
abstract Canvas lockCanvas(Rect dirty);
// 结束锁定画图,并提交改变。
abstract void unlockCanvasAndPost(Canvas canvas);
复制代码

注意:每次利用SurfaceHolder获得画布时,前一次的内容将会保留。

我这里贴一下重要部分的代码:

/**
 * 自定义SurfaceView 绘制图片
 */

public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.CallbackRunnable {

   ····

    private void init() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        mPaint = new Paint();
        //获取焦点
        setFocusable(true);
        setFocusableInTouchMode(true);
        //设置常量
        setKeepScreenOn(true);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        isRunning = true;
        mThread = new Thread(this);
        mThread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isRunning = false;
    }

    @Override
    public void run() {
        //循环绘制
        while (isRunning) {
            draw();
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            if (mCanvas != null) {
                mCanvas.drawBitmap(Util.getImageFromAssetsFile(mContext, PICTURE_NAME), 00, mPaint);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mCanvas != null) {
                //释放canvas
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }

    }
}
复制代码

源码地址(image包名下):https://github.com/StudyLifeTime/basicvideotutorial

总结

最后总结一下学习这三种加载方式,自定义View 和 ImageView 加载方式大同小异,就是自定义View注意画笔的抗锯齿操作,然后绘制图片如果很浪费资源的情况下推荐使用 SurfaceView ,毕竟是在工作线程里绘制不会影响到主线程的阻塞问题。

关注下面的标签,发现更多相似文章
评论