最近剛剛開始接觸安卓,本著輸出是最好的學(xué)習(xí)的想法,把學(xué)到的技術(shù)點(diǎn)總結(jié)一下。
因?yàn)楣镜囊粋€項(xiàng)目在華為P20手機(jī)上遇到了一個詭異的bug,作為一個剛開始接觸安卓的小白就義不容辭的擔(dān)負(fù)了查找bug的工作。bug現(xiàn)象:從強(qiáng)制橫屏且有渲染相機(jī)的Activity跳轉(zhuǎn)到一個強(qiáng)制豎屏的Activity,然后在返回到相機(jī)的Activity時會有較大的概率發(fā)生一次橫豎屏切換的操作。即相機(jī)的Activity會先扭成豎屏然后在扭成橫屏狀態(tài)。
為了復(fù)現(xiàn)這個問題,我通過GLSurface自己手動渲染了相機(jī)的預(yù)覽,現(xiàn)在通過這篇文章總結(jié)下實(shí)現(xiàn)流程。
涉及到的主要技術(shù)點(diǎn):
1. 請求權(quán)限
2. 使用OpenGLES繪制圖像
3. 打開相機(jī)并繪制相機(jī)畫面
一、請求權(quán)限
請求權(quán)限使用的是別人已經(jīng)封好的一套代碼,還未完整拜讀完,這里先不細(xì)說。值得注意的一點(diǎn)是要申請的權(quán)限需要在AndroidManifest.xml中提前定義。
<uses-permission android:name="android.permission.CAMERA"/>
二、使用OpenGLES繪制圖像
首先參考借鑒的文章,感謝前人種樹。
關(guān)于OpenGLES渲染的:https://blog.csdn.net/cassiePython/article/details/51539799
關(guān)于繪制相機(jī)預(yù)覽的:https://zhuanlan.zhihu.com/p/35192609
總體思路:
- 需要一個GLSurfaceView,可以直接new,也可以在布局文件中中寫,我用的方式是在布局文件中寫。關(guān)鍵代碼如下:
<android.opengl.GLSurfaceView
android:id="@+id/gl"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- 為GLSurface函數(shù)設(shè)置渲染版本,添加渲染回調(diào),并設(shè)置渲染方式。主要的渲染工作就在渲染回調(diào)中完成。
glSurfaceView = findViewById(R.id.gl);
//設(shè)置渲染GLES版本
glSurfaceView.setEGLContextClientVersion(2);
//設(shè)置渲染回調(diào)
glSurfaceView.setRenderer(new MyRender(this));
/*渲染方式,RENDERMODE_WHEN_DIRTY表示被動渲染,只有在調(diào)用requestRender或者onResume等方法時才會進(jìn)行渲染。RENDERMODE_CONTINUOUSLY表示持續(xù)渲染*/
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
- 在權(quán)限請求成功后創(chuàng)建打開相機(jī)
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();
}
- 實(shí)現(xiàn)渲染回調(diào)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 只運(yùn)行一次
requestPermission();
//擦除顏色紅色
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
mOESTextureId = createOESTextureObject();
//創(chuàng)建一個渲染圖
mSurfaceTexture = new SurfaceTexture(mOESTextureId);
//new一個控制GLES渲染的類
triangle = new Triangle(context);
try {
mCamera.setPreviewTexture(mSurfaceTexture);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
//添加幀可用監(jiān)聽,通知GLSurface渲染
mSurfaceTexture.setOnFrameAvailableListener(CameraActivity.this);
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
//todo 渲染窗口大小發(fā)生改變的處理
Log.e(TAG, "onSurfaceChanged232323232323232 width:" + width + " height" + height);
triangle.Change(width, height);
}
@Override
public void onDrawFrame(GL10 gl10) {
//todo 執(zhí)行渲染工作
glClear(GL_COLOR_BUFFER_BIT);
mSurfaceTexture.updateTexImage();
triangle.draw();
}
}
- Triangle類的實(shí)現(xiàn)是借用參考鏈接中的實(shí)現(xiàn)做的修改。主要思路是讀取glsl代碼文本編譯并鏈接到程序中,大概流程為Camera->SurfaceTexture->GLES外部紋理->GLSurfaceView
關(guān)鍵綁定代碼:
Camera同SurfaceTexture綁定
mCamera.setPreviewTexture(mSurfaceTexture);
SurfaceTexture同GLES外部紋理綁定
mSurfaceTexture = new SurfaceTexture(mOESTextureId);
//new一個控制GLES渲染的類
triangle = new Triangle(context);
GLES同GLSurfaceView綁定
glSurfaceView.setRenderer(new MyRender(this));
詳細(xì)工程
sample地址:https://github.com/gggab/DrawCamera