Android使用Opengl錄像時添加水印_Android

來源:腳本之家  責任編輯:小易  

必須具備:1.一款支持Android開發的IDE,如果你沒有的話,可以在Android Developer website下載最新版本的Android studio。2.一款運行Android4.0之上Android手機,并且GPU支持OpenGL ES2.03.對OpenGL的基本知識了解設置OpenGL ES環境創建GLSurfaceView為了顯示OpenGL的圖形,你需要使用GLSurfaceView類,就像其他任何的View子類意義,你可以將它添加到你的Activity或Fragment之上,通過在布局xml文件中定義或者在代碼中創建實例。在本次的教程中,我們使用GLSurfaceView作為唯一的View在我們的Activity中,因此,為了簡便,我們在代碼中創建GLSurfaceView的實例并將其傳入setContentView中,這樣它將會填充你的整個手機屏幕。Activity中的onCreate方法如下:protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState);GLSurfaceView view=new GLSurfaceView(this);setContentView(view);}123456因為媒體效果的框架僅僅支持OpenGL ES2.0及以上的版本,所以在setEGLContextClientVersion 方法中傳入2;view.setEGLContextClientVersion(2);1為了確保GLSurfaceView僅僅在必要的時候進行渲染,我們在setRenderMode 方法中進行設置:view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);1創建RendererRenderer負責渲染GLSurfaceView中的內容。創建類實現接口GLSurfaceView.Renderer,在這里我們打算將這個類命名為EffectsRenderer,添加構造函數并覆寫接口中的抽象方法,如下:public class EffectsRenderer implements GLSurfaceView.Renderer {public EffectsRenderer(Context context){ super();}@Overridepublic void onSurfaceCreated(GL10 gl,EGLConfig config){}@Overridepublic void onSurfaceChanged(GL10 gl,int width,int height){}@Overridepublic void onDrawFrame(GL10 gl){}}123456789101112131415161718回到Activity中調用setRenderer方法,讓GLSurfaceView使用我們創建的Renderer:view.setRenderer(new EffectsRenderer(this));1編寫Manifest文件如果你想要發布你的App到谷歌商店,在AndroidManifest.xml文件中添加如下語句:1這會確保你的app只能被安裝在支持OpenGL ES2.0的設備之上。現在OpenGL環境準備完畢。創建一個OpenGL平面定義頂點GLSurfaceView是不能直接顯示一張照片的,照片首先應該被轉化為紋理,應用在OpenGL square之上。在本次教程中,我將創建一個2D平面,并且具有4個頂點。為了簡單,我將使用一個長方形,現在,創建一個新的類Square,用它來代表形狀。public class Square {}123默認的OpenGL系統的坐標系中的原點是在中心,因此4個角的坐標可以表示為:左下角:(-1,-1)右下角:(1,-1)右上角:(1,1)左上角:(-1,1)我們使用OpenGL繪制的所有的物體都應該是由三角形決定的,為了畫一個方形,我們需要兩個具有一條公共邊的三角形,那意味著這些三角形的坐標應該是:triangle 1:(-1,-1),(1,-1),和(-1,1)triangle 2:(1,-1),(-1,1),和(1,1)創建一個float數組來代表這些頂點:private float vertices[]={1f,-1f,1f,-1f,1f,1f,1f,1f,};123456為了在square上定位紋理,需要確定紋理的頂點坐標,創建另一個數組來表示紋理頂點的坐標:private float textureVertices[]={ 0f,1f,1f,1f,0f,0f,1f,0f};123456創建緩沖區這些坐標數組應該被轉變為緩沖字符(byte buffer)在OpenGL可以使用之前,接下來進行定義:private FloatBuffer verticesBuffer;private FloatBuffer textureBuffer;12在initializeBuffers方法中去初始化這些緩沖區:使用ByteBuffer.allocateDirect來創建緩沖區,因為float是4個字節,那么我們需要的byte數組的長度應該為float的4倍。下面使用ByteBuffer.nativeOrder方法來定義在底層的本地平臺上的byte的順序。使用asFloatBuffer方法將ByteBuffer轉化為FloatBuffer,在FloatBuffer被創建后,我們調用put方法來將float數組放入緩沖區,最后,調用position方法來保證我們是由緩沖區的開頭進行讀取。private void initializeBuffers(){ByteBuffer buff=ByteBuffer.allocateDirect(vertices.length*4);buff.order(ByteOrder.nativeOrder());verticesBuffer=buff.asFloatBuffer();verticesBuffer.put(vertices);verticesBuffer.position(0);buff=ByteBuffer.allocateDirect(textureVertices.length*4);buff.order(ByteOrder.nativeOrder());textureBuffer=buff.asFloatBuffer();textureBuffer.put(textureVertices);textureBuffer.position(0);}12345678910111213創建著色器著色器只不過是簡單的運行在GPU中的每個單獨的頂點的C程序,在本次教程中,我們使用兩種著色器:頂點著色器和片段著色器。頂點著色器的代碼:attribute vec4 aPosition;attribute vec2 aTexPosition;varying vec2 vTexPosition;void main(){gl_Position=aPosition;vTexPosition=aTexPosition;};1234567片段著色器的代碼precision mediump float;uniform sampler2D uTexture;varying vec2 vTexPosition;void main(){gl_FragColor=texture2D(uTexture,vTexPosition);};www.13333515.buzz防采集請勿采集本網。

最近需要開發一個類似行車記錄儀的app,其中需要給錄制的視頻添加動態水印。我使用的是OpenGL開發的,剛開始實現的是靜態水印,后面才實現的動態水印。

android下用的是opengl es,針對手機、PDA和游戲主機等嵌入式設備而設計。要調用應該用JNI(Java Native Interface)與C交互,android有NDK,可以研究一下。

先上效果圖,左下角的是靜態水印,中間偏下的是時間水印(動態水印):

OpenGLES是一個讓人崩潰的東西。在Andorid手機上做3D還就得用它。把我記的一些筆記分享在這里吧:Android OpenGL ES簡介 20011-6-3 Android系統使用OpenGL的標準接口來支持3D圖形功能,android

一、靜態水印

如何在Android使用OpenGL ES 2.0繪制點,看上去并不是一個復雜的問題,但是上網一搜,滿眼都是繪制點的代碼。如果你看到類似如下代碼,基本上你已經掉坑里了。c glBeging();glDrawPoint(.);glEnd

實現原理:錄像時是通過OpenGL把圖像渲染到GLSurfaceView上的,通俗的講,就是把圖片畫到一塊畫布上,然后展示出來。添加圖片水印,就是把水印圖片跟錄制的圖像一起畫到畫布上。

對于三維圖形來說,除了需要代表顏色的二維數組(ColorBuffer),還需要深度二維數組(DepthBuffer)或遮罩數組(StencilBuffer),因此在OpenGL中的FrameBuffer為上述ColorBuffer,DepthBuffer,StencilBuffer

這是加載紋理跟陰影的Java類

這個東西 簡單點做的話 就是在GLSurfaceView上面加一層layout,在上面那層layout里邊擺按鈕。麻煩點做就是 每次坐標變換的時候 計算位置和大小,保證按鈕在屏幕上的位置和大小不變,這種方法做

package com.audiovideo.camera.blog;import android.opengl.GLES20;/** * Created by fenghaitao on 2019/9/12. */public class WaterSignSProgram{ private static int programId; private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + "attribute vec4 aPosition;\n" + "attribute vec4 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = uMVPMatrix * aPosition;\n" + " vTextureCoord = aTextureCoord.xy;\n" + "}\n"; private static final String FRAGMENT_SHADER = "precision mediump float;\n" + "varying vec2 vTextureCoord;\n" + "uniform sampler2D sTexture;\n" + "void main() {\n" + " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + "}\n"; public WaterSignSProgram() { programId = loadShader(VERTEX_SHADER, FRAGMENT_SHADER); uMVPMatrixLoc = GLES20.glGetUniformLocation(programId, "uMVPMatrix"); checkLocation(uMVPMatrixLoc, "uMVPMatrix"); aPositionLoc = GLES20.glGetAttribLocation(programId, "aPosition"); checkLocation(aPositionLoc, "aPosition"); aTextureCoordLoc = GLES20.glGetAttribLocation(programId, "aTextureCoord"); checkLocation(aTextureCoordLoc, "aTextureCoord"); sTextureLoc = GLES20.glGetUniformLocation(programId, "sTexture"); checkLocation(sTextureLoc, "sTexture"); } public int uMVPMatrixLoc; public int aPositionLoc; public int aTextureCoordLoc; public int sTextureLoc; public static void checkLocation(int location, String label) { if (location < 0) { throw new RuntimeException("Unable to locate '" + label + "' in program"); } }/** * 加載編譯連接陰影 * @param vss source of vertex shader * @param fss source of fragment shader * @return */public static int loadShader(final String vss, final String fss) { Log.v(TAG, "loadShader:"); int vs = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); GLES20.glShaderSource(vs, vss); GLES20.glCompileShader(vs); final int[] compiled = new int[1]; GLES20.glGetShaderiv(vs, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { Log.e(TAG, "Failed to compile vertex shader:" + GLES20.glGetShaderInfoLog(vs)); GLES20.glDeleteShader(vs); vs = 0; } int fs = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); GLES20.glShaderSource(fs, fss); GLES20.glCompileShader(fs); GLES20.glGetShaderiv(fs, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { Log.w(TAG, "Failed to compile fragment shader:" + GLES20.glGetShaderInfoLog(fs)); GLES20.glDeleteShader(fs); fs = 0; } final int program = GLES20.glCreateProgram(); GLES20.glAttachShader(program, vs); GLES20.glAttachShader(program, fs); GLES20.glLinkProgram(program); return program;} /** * terminatinng, this should be called in GL context */ public static void release() { if (programId >= 0) GLES20.glDeleteProgram(programId); programId = -1; }}

package com.audiovideo.camera.blog;import android.opengl.GLES20;import android.opengl.Matrix;import com.audiovideo.camera.glutils.GLDrawer2D;import com.audiovideo.camera.utils.LogUtil;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;這是畫水印的Java類/** * Created by fenghaitao on 2019/9/12. */public class WaterSignature { private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + "attribute vec4 aPosition;\n" + "attribute vec4 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = uMVPMatrix * aPosition;\n" + " vTextureCoord = aTextureCoord.xy;\n" + "}\n"; private static final String FRAGMENT_SHADER = "precision mediump float;\n" + "varying vec2 vTextureCoord;\n" + "uniform sampler2D sTexture;\n" + "void main() {\n" + " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + "}\n"; public static final int SIZE_OF_FLOAT = 4; /** * 一個“完整”的正方形,從兩維延伸到-1到1。 * 當 模型/視圖/投影矩陣是都為單位矩陣的時候,這將完全覆蓋視口。 * 紋理坐標相對于矩形是y反的。 * (This seems to work out right with external textures from SurfaceTexture.) */ private static final float FULL_RECTANGLE_COORDS[] = { -1.0f, -1.0f, // 0 bottom left 1.0f, -1.0f, // 1 bottom right -1.0f, 1.0f, // 2 top left 1.0f, 1.0f, // 3 top right }; private static final float FULL_RECTANGLE_TEX_COORDS[] = { 0.0f, 1.0f, //0 bottom left //0.0f, 0.0f, // 0 bottom left 1.0f, 1.0f, //1 bottom right //1.0f, 0.0f, // 1 bottom right 0.0f, 0.0f, //2 top left //0.0f, 1.0f, // 2 top left 1.0f, 0.0f, //3 top right //1.0f, 1.0f, // 3 top right }; private FloatBuffer mVertexArray; private FloatBuffer mTexCoordArray; private int mCoordsPerVertex; private int mCoordsPerTexture; private int mVertexCount; private int mVertexStride; private int mTexCoordStride; private int hProgram; public float[] mProjectionMatrix = new float[16];// 投影矩陣 public float[] mViewMatrix = new float[16]; // 攝像機位置朝向9參數矩陣 public float[] mModelMatrix = new float[16];// 模型變換矩陣 public float[] mMVPMatrix = new float[16];// 獲取具體物體的總變換矩陣 private float[] getFinalMatrix() { Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0); Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); return mMVPMatrix; } public WaterSignature() { mVertexArray = createFloatBuffer(FULL_RECTANGLE_COORDS); mTexCoordArray = createFloatBuffer(FULL_RECTANGLE_TEX_COORDS); mCoordsPerVertex = 2; mCoordsPerTexture = 2; mVertexCount = FULL_RECTANGLE_COORDS.length / mCoordsPerVertex; // 4 mTexCoordStride = 2 * SIZE_OF_FLOAT; mVertexStride = 2 * SIZE_OF_FLOAT; Matrix.setIdentityM(mProjectionMatrix, 0); Matrix.setIdentityM(mViewMatrix, 0); Matrix.setIdentityM(mModelMatrix, 0); Matrix.setIdentityM(mMVPMatrix, 0); hProgram = GLDrawer2D.loadShader(VERTEX_SHADER, FRAGMENT_SHADER); GLES20.glUseProgram(hProgram); } private FloatBuffer createFloatBuffer(float[] coords) { ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * SIZE_OF_FLOAT); bb.order(ByteOrder.nativeOrder()); FloatBuffer fb = bb.asFloatBuffer(); fb.put(coords); fb.position(0); return fb; } private WaterSignSProgram mProgram; public void setShaderProgram(WaterSignSProgram mProgram) { this.mProgram = mProgram; } public void drawFrame(int mTextureId) { GLES20.glUseProgram(hProgram); // 設置紋理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); GLES20.glUniform1i(mProgram.sTextureLoc, 0); GlUtil.checkGlError("GL_TEXTURE_2D sTexture"); // 設置 model / view / projection 矩陣 GLES20.glUniformMatrix4fv(mProgram.uMVPMatrixLoc, 1, false, getFinalMatrix(), 0); GlUtil.checkGlError("glUniformMatrix4fv uMVPMatrixLoc"); // 使用簡單的VAO 設置頂點坐標數據 GLES20.glEnableVertexAttribArray(mProgram.aPositionLoc); GLES20.glVertexAttribPointer(mProgram.aPositionLoc, mCoordsPerVertex, GLES20.GL_FLOAT, false, mVertexStride, mVertexArray); GlUtil.checkGlError("VAO aPositionLoc"); // 使用簡單的VAO 設置紋理坐標數據 GLES20.glEnableVertexAttribArray(mProgram.aTextureCoordLoc); GLES20.glVertexAttribPointer(mProgram.aTextureCoordLoc, mCoordsPerTexture, GLES20.GL_FLOAT, false, mTexCoordStride, mTexCoordArray); GlUtil.checkGlError("VAO aTextureCoordLoc"); // GL_TRIANGLE_STRIP三角形帶,這就為啥只需要指出4個坐標點,就能畫出兩個三角形了。 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mVertexCount); // Done -- 解綁~ GLES20.glDisableVertexAttribArray(mProgram.aPositionLoc); GLES20.glDisableVertexAttribArray(mProgram.aTextureCoordLoc); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glUseProgram(0); } /** * terminatinng, this should be called in GL context */ public void release() { if (hProgram >= 0) GLES20.glDeleteProgram(hProgram); hProgram = -1; } /** * 刪除texture */ public static void deleteTex(final int hTex) { LogUtil.v("WaterSignature", "deleteTex:"); final int[] tex = new int[] {hTex}; GLES20.glDeleteTextures(1, tex, 0); }}

沒時間了。先寫到這,后面是調用,遲點再寫。

下面是如何把水印繪制到畫布上:

1、在SurfaceTexture的onSurfaceCreated方法中初始化并設置陰影;

@Override public void onSurfaceCreated(final GL10 unused, final EGLConfig config) { LogUtil.v(TAG, "onSurfaceCreated:"); // This renderer required OES_EGL_image_external extension final String extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS); // API >= 8 // 使用黃色清除界面 GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f); //設置水印 if (mWaterSign == null) { mWaterSign = new WaterSignature(); } //設置陰影 mWaterSign.setShaderProgram(new WaterSignSProgram()); mSignTexId = loadTexture(MyApplication.getContext(), R.mipmap.watermark); }

這里是生成mSignTexId 的方法,把該圖像與紋理id綁定并返回:

public static int loadTexture(Context context, int resourceId) { final int[] textureObjectIds = new int[1]; GLES20.glGenTextures(1, textureObjectIds, 0); if(textureObjectIds[0] == 0){ Log.e(TAG,"Could not generate a new OpenGL texture object!"); return 0; } final BitmapFactory.Options options = new BitmapFactory.Options(); options.inScaled = false; //指定需要的是原始數據,非壓縮數據 final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options); if(bitmap == null){ Log.e(TAG, "Resource ID "+resourceId + "could not be decode"); GLES20.glDeleteTextures(1, textureObjectIds, 0); return 0; } //告訴OpenGL后面紋理調用應該是應用于哪個紋理對象 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureObjectIds[0]); //設置縮小的時候(GL_TEXTURE_MIN_FILTER)使用mipmap三線程過濾 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR); //設置放大的時候(GL_TEXTURE_MAG_FILTER)使用雙線程過濾 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); //Android設備y坐標是反向的,正常圖顯示到設備上是水平顛倒的,解決方案就是設置紋理包裝,紋理T坐標(y)設置鏡面重復 //ball讀取紋理的時候 t范圍坐標取正常值+1 //GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_MIRRORED_REPEAT); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); //快速生成mipmap貼圖 GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); //解除紋理操作的綁定 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); return textureObjectIds[0];}

2、在繪制方法onDrawFrame中繪制畫面的同時把水印繪制進去;

/** * 繪圖到glsurface * 我們將rendermode設置為glsurfaceview.rendermode_when_dirty, * 僅當調用requestrender時調用此方法(=需要更新紋理時) * 如果不在臟時設置rendermode,則此方法的最大調用速度為60fps。 */ @Override public void onDrawFrame(final GL10 unused) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glEnable(GLES20.GL_BLEND); //開啟GL的混合模式,即圖像疊加 GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_src_ALPHA); /** *中間這里是你繪制的預覽畫面 */ //畫水印(非動態) GLES20.glViewport(20, 20, 288, 120); mWaterSign.drawFrame(mSignTexId); }

這里最重要的是要開啟GL的混合模式,即圖像疊加,不然你繪制的水印會覆蓋原先的預覽畫面

//開啟GL的混合模式,即圖像疊加GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_src_ALPHA);

準備 為了開始本次的教程,你必須具備:1.一款支持Android開發的IDE,如果你沒有的話,可以在Android Developer website下載最新版本的Android studio。2.一款運行Android4.0之上Android手機,并且GPU支持OpenGL ES2.0 3.對OpenGL的基本知識了解 設置OpenGL ES環境 創建GLSurfaceView 為了顯示OpenGL的圖形,你需要使用GLSurfaceView類,就像其他任何的View子類意義,你可以將它添加到你的Activity或Fragment之上,通過在布局xml文件中定義或者在代碼中創建實例。在本次的教程中,我們使用GLSurfaceView作為唯一的View在我們的Activity中,因此,為了簡便,我們在代碼中創建 GLSurfaceView的實例并將其傳入setContentView中,這樣它將會填充你的整個手機屏幕。Activity中的onCreate方 法如下:protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState);GLSurfaceView view=new GLSurfaceView(this);setContentView(view);}123456123456 因為媒體效果的框架僅僅支持OpenGL ES2.0及以上的版本,所以在setEGLContextClientVersion 方法中傳入2;view.setEGLContextClientVersion(2);11 為了確保GLSurfaceView僅僅在必要的時候進行渲染,我們在setRenderMode 方法中進行設置:view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);11 創建Renderer Renderer負責渲染GLSurfaceView中的內容。創建類實現接口GLSurfaceView.Renderer,在這里我們打算將這個類命名為EffectsRenderer,添加構造函數并覆寫接口中的抽象方法,如下:public class EffectsRenderer implements GLSurfaceView.Renderer { public EffectsRenderer(Context context){ super();}@Override public void onSurfaceCreated(GL10 gl,EGLConfig config){ }@Override public void onSurfaceChanged(GL10 gl,int width,int height){ }@Override public void onDrawFrame(GL10 gl){ } }123456789101112131415161718123456789101112131415161718 回到Activity中調用setRenderer方法,讓GLSurfaceView使用我們創建的Renderer:view.setRenderer(new EffectsRenderer(this));11 編寫Manifest文件 如果你想要發布你的App到谷歌商店,在AndroidManifest.xml文件中添加如下語句:<uses-feature android:glEsVersion="0x00020000" android:required="true" />11 這會確保你的app只能被安裝在支持OpenGL ES2.0的設備之上。現在OpenGL環境準備完畢。創建一個OpenGL平面 定義頂點 GLSurfaceView是不能直接顯示一張照片的,照片首先應該被轉化為紋理,應用在OpenGL square之上。在本次教程中,我將創建一個2D平面,并且具有4個頂點。為了簡單,我將使用一個長方形,現在,創建一個新的類Square,用它來代表形狀。public class Square { }123123 默認的OpenGL系統的坐標系中的原點是在中心,因此4個角的坐標可以表示為:左下角:(-1,-1)右下角:(1,-1)右上角:(1,1)左上角:(-1,1)我們使用OpenGL繪制的所有的物體都應該是由三角形決定的,為了畫一個方形,我們需要兩個具有一條公共邊的三角形,那意味著這些三角形的坐標應該是:triangle 1:(-1,-1),(1,-1),和(-1,1)triangle 2:(1,-1),(-1,1),和(1,1)創建一個float數組來代表這些頂點:private float vertices[]={-1f,-1f,1f,-1f,-1f,1f,1f,1f,};123456123456 展開內容來自www.13333515.buzz請勿采集。


  • 本文相關:
  • android視頻處理之動態時間水印效果
  • android自定義view仿探探卡片滑動效果
  • android開發之基于dialogfragment創建對話框的方法示例
  • android安裝apk文件并適配android 7.0詳解
  • android編程開發之打開文件的intent及使用方法
  • cocos2d-x的內存管理總結
  • android 五大布局方式詳解
  • 簡單實現android本地音樂播放器
  • android圖片三級緩存開發
  • android忽略文件實例代碼
  • okhttp3.4.1+retrofit2.1.0實現離線緩存的示例
  • 為什么使用 android opengl
  • 如何使用Android中的OpenGL ES媒體效果
  • 如何使用Android中的OpenGL ES媒體效果
  • 如何使用Android中的OpenGL ES媒體效果
  • 請問android下如何用c++來使用opengl?謝謝。
  • 如何在Android上使用OpenGL ES 2.0繪制點
  • 如何在Android上使用OpenGL ES 2.0繪制點
  • 如何在Android上使用OpenGL ES 2.0繪制點
  • Android用opengl繪圖的問題?
  • 如何使用Android中的OpenGL ES媒體效果
  • 網站首頁網頁制作腳本下載服務器操作系統網站運營平面設計媒體動畫電腦基礎硬件教程網絡安全c#教程vbvb.netc 語言java編程delphijavaandroidiosswiftscala易語言匯編語言其它相關首頁androidandroid視頻處理之動態時間水印效果android自定義view仿探探卡片滑動效果android開發之基于dialogfragment創建對話框的方法示例android安裝apk文件并適配android 7.0詳解android編程開發之打開文件的intent及使用方法cocos2d-x的內存管理總結android 五大布局方式詳解簡單實現android本地音樂播放器android圖片三級緩存開發android忽略文件實例代碼okhttp3.4.1+retrofit2.1.0實現離線緩存的示例一看就懂的android app開發入門教微信公眾平臺開發入門教程(圖文詳android基礎之使用fragment控制切六款值得推薦的android(安卓)開android textview設置中文字體加android應用開發sharedpreferencandroid 動畫之translateanimatiandroid壓力測試命令monkey詳解android按鈕單擊事件的四種常用寫android調試工具ddms的使用詳解android webview實現網頁滾動截圖android編程實現啟動另外的app及傳遞參數android仿ios實現側滑返回功能(類似微信android textview多文本折疊展開效果android開發中toast顯示消息的方法小結android studio 3.1.x中導入項目的正確方android地圖控件之多地圖展示android仿ios底部彈出對話框android viewdraghelper使用方法詳解android 6.0 寫入sd卡的權限申請實例講解
    免責聲明 - 關于我們 - 聯系我們 - 廣告聯系 - 友情鏈接 - 幫助中心 - 頻道導航
    Copyright © 2017 www.13333515.buzz All Rights Reserved
    3排列五开奖结果