最近刚刚开始接触安卓,本着输出是最好的学习的想法,把学到的技术点总结一下。
因为公司的一个项目在华为P20手机上遇到了一个诡异的bug,作为一个刚开始接触安卓的小白就义不容辞的担负了查找bug的工作。bug现象:从强制横屏且有渲染相机的Activity跳转到一个强制竖屏的Activity,然后在返回到相机的Activity时会有较大的概率发生一次横竖屏切换的操作。即相机的Activity会先扭成竖屏然后在扭成横屏状态。
为了复现这个问题,我通过GLSurface自己手动渲染了相机的预览,现在通过这篇文章总结下实现流程。
涉及到的主要技术点:
1. 请求权限
2. 使用OpenGLES绘制图像
3. 打开相机并绘制相机画面
一、请求权限
请求权限使用的是别人已经封好的一套代码,还未完整拜读完,这里先不细说。值得注意的一点是要申请的权限需要在AndroidManifest.xml中提前定义。
<uses-permission android:name="android.permission.CAMERA"/>
二、使用OpenGLES绘制图像
首先参考借鉴的文章,感谢前人种树。
关于OpenGLES渲染的:https://blog.csdn.net/cassiePython/article/details/51539799
关于绘制相机预览的:https://zhuanlan.zhihu.com/p/35192609
总体思路:
- 需要一个GLSurfaceView,可以直接new,也可以在布局文件中中写,我用的方式是在布局文件中写。关键代码如下:
<android.opengl.GLSurfaceView
android:id="@+id/gl"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- 为GLSurface函数设置渲染版本,添加渲染回调,并设置渲染方式。主要的渲染工作就在渲染回调中完成。
glSurfaceView = findViewById(R.id.gl);
//设置渲染GLES版本
glSurfaceView.setEGLContextClientVersion(2);
//设置渲染回调
glSurfaceView.setRenderer(new MyRender(this));
/*渲染方式,RENDERMODE_WHEN_DIRTY表示被动渲染,只有在调用requestRender或者onResume等方法时才会进行渲染。RENDERMODE_CONTINUOUSLY表示持续渲染*/
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
- 在权限请求成功后创建打开相机
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
Camera.Parameters parameters=mCamera.getParameters();
parameters.set("orientation", "portrait");
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
parameters.setPreviewSize(1280, 720);
mCamera.setDisplayOrientation(90);
mCamera.setParameters(parameters);
- 在onFrameAvailable中请求渲染
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture){
glSurfaceView.requestRender();
}
- 实现渲染回调onRenderer
public class MyRender implements GLSurfaceView.Renderer{
private Context context;
Triangle triangle = null;
public MyRender(Context context) {
this.context=context;
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
//todo 只运行一次
requestPermission();
//擦除颜色红色
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
mOESTextureId = createOESTextureObject();
//创建一个渲染图
mSurfaceTexture = new SurfaceTexture(mOESTextureId);
//new一个控制GLES渲染的类
triangle = new Triangle(context);
try {
mCamera.setPreviewTexture(mSurfaceTexture);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
//添加帧可用监听,通知GLSurface渲染
mSurfaceTexture.setOnFrameAvailableListener(CameraActivity.this);
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
//todo 渲染窗口大小发生改变的处理
Log.e(TAG, "onSurfaceChanged232323232323232 width:" + width + " height" + height);
triangle.Change(width, height);
}
@Override
public void onDrawFrame(GL10 gl10) {
//todo 执行渲染工作
glClear(GL_COLOR_BUFFER_BIT);
mSurfaceTexture.updateTexImage();
triangle.draw();
}
}
- Triangle类的实现是借用参考链接中的实现做的修改。主要思路是读取glsl代码文本编译并链接到程序中,大概流程为Camera->SurfaceTexture->GLES外部纹理->GLSurfaceView
关键绑定代码:
Camera同SurfaceTexture绑定
mCamera.setPreviewTexture(mSurfaceTexture);
SurfaceTexture同GLES外部纹理绑定
mSurfaceTexture = new SurfaceTexture(mOESTextureId);
//new一个控制GLES渲染的类
triangle = new Triangle(context);
GLES同GLSurfaceView绑定
glSurfaceView.setRenderer(new MyRender(this));
详细工程
sample地址:https://github.com/gggab/DrawCamera