- 投稿日:2020-09-15T23:05:40+09:00
localhostにスマホから繋げてみた!!(Windows編)
どうも、三町哲平です。
前回は、localhostにスマホから繋げてみた!!(Mac編)で、MacBookで起動したlocalhostにAndroidスマホとiPad、WindowsPCの3点からアクセスする方法を紹介しましたが、Macをやったからには、双璧をなすWindowsに触れない訳にはいきません。
では、Windows編をどうぞ!!
1. Wi-Fi環境を同じにする
まず最初にWindowsPC、Androidスマホ、iPad、MacBookを同じWi-Fi環境に接続してください。
今回は、Air-WiFi_0OXPYNに接続しています。ここはMac編と違いはありませんね。
2. WindowsPCのIPアドレスを確認する。
① デスクトップから「cmd」と検索する。
② コマンドプロンプトを開き、「ipconfig」と入力して、Enterを押します。
③ なんか沢山出てきます。
色々書いていますが、デフォルトゲートウェイが開いている箇所を探します。
今回の場合は、
イーサネット アダプター vEthernet (aaaaaaaaaaaaa1): 接続固有の DNS サフィックス . . . . .: リンクローカル IPv6 アドレス. . . . .: ----::---:----:----:----%-- IPv4 アドレス . . . . . . . . . . . .: 192.168.43.187 サブネット マスク . . . . . . . . . .: ---.---.---.- デフォルト ゲートウェイ . . . . . . .: 192.168.43.1今回は、この aaaaaaaaaaaaa1 が該当します。
IPv4 アドレス 192.168.43.187 をコピーやメモして下さい。
3. localhostを起動する
ターミナルで、
$ docker-compose upを使用して、docker-composeで、localhostを起動します。
ポート番号は、8000番です。
4. WindowsPCでlocalhost:8000にアクセス
無事、ページが表示されました。
5. その他の機器で試す
※WindowsPCからは今回の場合、localhost:3000とブラウザのURLに入力することで、表示されますが、その他機器では、192.168.43.187:8000と入力します。
Androidスマホ
iPad
MacBook
このように、同じWi-Fiに接続することにより、WindowsPCで起動したloclhostにAndroidスマホとiPad、MacBookからもアクセスすることが出来ました。
- 投稿日:2020-09-15T20:53:28+09:00
Android/OpenGL sample1
(1)四角形を表示するだけ。
塗りつぶし色はuniform変数で指定している。(2)三角形
頂点カラーhttps://1drv.ms/u/s!ApO4wsxFJBbVuUr4rbo3RkWkdF_b?e=vmgUss
package com.ore.blackv2; //package com.example.android.opengl; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import android.opengl.GLES20; import android.util.Log; import static android.opengl.GLES20.GL_FALSE; import static android.opengl.GLES20.GL_LINK_STATUS; import static android.opengl.GLES20.glDeleteProgram; import static android.opengl.GLES20.glGetProgramInfoLog; import static android.opengl.GLES20.glGetProgramiv; public class Square { private final String vertexShaderCode = // This matrix member variable provides a hook to manipulate // the coordinates of the objects that use this vertex shader "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() {" + " gl_Position = uMVPMatrix * vPosition;" + "}"; private final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}"; private final FloatBuffer vertexBuffer; private final ShortBuffer drawListBuffer; private final int mProgram; private int mPositionHandle; private int mColorHandle; private int mMVPMatrixHandle; // number of coordinates per vertex in this array static final int COORDS_PER_VERTEX = 3; static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left -0.5f, -0.5f, 0.0f, // bottom left 0.5f, -0.5f, 0.0f, // bottom right 0.5f, 0.5f, 0.0f }; // top right private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex float color[] = { 1.0f, 0.709803922f, 0.898039216f, 1.0f }; /** * Sets up the drawing object data for use in an OpenGL ES context. */ public Square() { // initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) squareCoords.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(squareCoords); vertexBuffer.position(0); // initialize byte buffer for the draw list ByteBuffer dlb = ByteBuffer.allocateDirect( // (# of coordinate values * 2 bytes per short) drawOrder.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(drawOrder); drawListBuffer.position(0); // prepare shaders and OpenGL program // int vertexShader = MyGLRenderer.loadShader( GLES20.GL_VERTEX_SHADER, vertexShaderCode); // int fragmentShader = MyGLRenderer.loadShader( GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); int vertexShader = GLESUtils.createShader( GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = GLESUtils.createShader( GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program GLES20.glLinkProgram(mProgram); // create OpenGL program executables int[] status = new int[1]; glGetProgramiv(mProgram, GL_LINK_STATUS, status, 0); if (status[0] == GL_FALSE) { //Log.e(TAG, glGetProgramInfoLog(mProgram)); glDeleteProgram(mProgram); } } /** * Encapsulates the OpenGL ES instructions for drawing this shape. * * @param mvpMatrix - The Model View Project matrix in which to draw * this shape. */ public void draw(float[] mvpMatrix) { // Add program to OpenGL environment GLES20.glUseProgram(mProgram); // get handle to vertex shader's vPosition member mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // Enable a handle to the triangle vertices GLES20.glEnableVertexAttribArray(mPositionHandle); // Prepare the triangle coordinate data GLES20.glVertexAttribPointer( mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); // get handle to fragment shader's vColor member mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // Set color for drawing the triangle GLES20.glUniform4fv(mColorHandle, 1, color, 0); // get handle to shape's transformation matrix mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); //MyGLRenderer.checkGlError("glGetUniformLocation"); // Apply the projection and view transformation GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); //MyGLRenderer.checkGlError("glUniformMatrix4fv"); // Draw the square GLES20.glDrawElements( GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer); // Disable vertex array GLES20.glDisableVertexAttribArray(mPositionHandle); } }public class Triangle { public final String vertexShaderCode = "attribute vec4 vPosition;" + "attribute vec4 vColor;" + "varying mediump vec4 varColor;"+ "void main() {" + " varColor = vColor;"+ " gl_Position = vPosition;" + "}"; public final String fragmentShaderCode = "precision mediump float;" + "varying mediump vec4 varColor;"+ "void main() {" + " gl_FragColor = varColor;" + "}"; private int loadShader(int type, String shaderCode){ int err = 0; int shader = GLES20.glCreateShader(type); err = EGL14.eglGetError(); if (err != EGL14.EGL_SUCCESS) return 0; GLES20.glShaderSource(shader, shaderCode); if (EGL14.eglGetError() != EGL14.EGL_SUCCESS) return 0; GLES20.glCompileShader(shader); return shader; } private int shaderProgram; public Triangle() { } public int Init(){ int err = 0; int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); shaderProgram = GLES20.glCreateProgram(); err = GLES20.glGetError(); if (err != GLES20.GL_NO_ERROR) return err; GLES20.glAttachShader(shaderProgram, vertexShader); GLES20.glAttachShader(shaderProgram, fragmentShader); GLES20.glLinkProgram(shaderProgram); err = GLES20.glGetError(); if (err != GLES20.GL_NO_ERROR) return err; int[] status = new int[1]; glGetProgramiv(shaderProgram, GL_LINK_STATUS, status, 0); if (status[0] == GL_FALSE) { String msz = glGetProgramInfoLog(shaderProgram); Log.e("sample", glGetProgramInfoLog(shaderProgram)); glDeleteProgram(shaderProgram); } return GLES20.GL_NO_ERROR; } public void draw(){ int err = 0; //if( shaderProgram == 0 ) return; GLES20.glUseProgram(shaderProgram); err = GLES20.glGetError(); if (err != GLES20.GL_NO_ERROR) return ; int positionAttrib = GLES20.glGetAttribLocation(shaderProgram, "vPosition"); GLES20.glEnableVertexAttribArray(positionAttrib); if( true ) { // xyz, rgba, float vertices[] = { 0.0f, 0.4f, 0.0f, 1.0f, -0.3f, -0.3f, 0.0f, 1.0f, 0.5f, -0.3f, 0.0f, 1.0f, }; ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4); bb.order(ByteOrder.nativeOrder()); FloatBuffer vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); int stride = 0; GLES20.glVertexAttribPointer(positionAttrib, 4, GLES20.GL_FLOAT, false, stride, vertexBuffer); } boolean enableColor = true; int colorAttrib = 0; if( enableColor ) { float colors[] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, }; colorAttrib = GLES20.glGetAttribLocation(shaderProgram, "vColor"); GLES20.glEnableVertexAttribArray(colorAttrib); ByteBuffer bb2 = ByteBuffer.allocateDirect(colors.length * 4); bb2.order(ByteOrder.nativeOrder()); FloatBuffer colorBuffer = bb2.asFloatBuffer(); colorBuffer.put(colors); colorBuffer.position(0); int stride = 0; GLES20.glVertexAttribPointer(colorAttrib, 4, GLES20.GL_FLOAT, false, stride, colorBuffer); } GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); GLES20.glDisableVertexAttribArray(positionAttrib); if( enableColor ) { GLES20.glDisableVertexAttribArray(colorAttrib); } }package com.ore.egltest4; import java.nio.*; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.EGL14; import android.opengl.GLES20; import android.opengl.GLUtils; import android.util.Log; import static android.opengl.GLES20.GL_FALSE; import static android.opengl.GLES20.GL_LINK_STATUS; import static android.opengl.GLES20.glDeleteProgram; import static android.opengl.GLES20.glGetProgramInfoLog; import static android.opengl.GLES20.glGetProgramiv; import static android.opengl.GLES20.GL_ARRAY_BUFFER; import static android.opengl.GLES20.GL_ELEMENT_ARRAY_BUFFER; import static android.opengl.GLES20.GL_STATIC_DRAW; import static android.opengl.GLES20.glClearColor; import static android.opengl.GLES20.glGetUniformLocation; import static android.opengl.GLES20.glViewport; import static android.opengl.GLES20.GL_TRIANGLES; import static android.opengl.GLES20.GL_UNSIGNED_INT; import static android.opengl.GLES20.glAttachShader; import static android.opengl.GLES20.glBufferData; import static android.opengl.GLES20.glClearColor; import static android.opengl.GLES20.glCompileShader; import static android.opengl.GLES20.glCreateProgram; import static android.opengl.GLES20.glCreateShader; import static android.opengl.GLES20.glDeleteShader; import static android.opengl.GLES20.glDrawElements; import static android.opengl.GLES20.glGenBuffers; import static android.opengl.GLES20.glGetProgramInfoLog; import static android.opengl.GLES20.glGetProgramiv; import static android.opengl.GLES20.glGetUniformLocation; import static android.opengl.GLES20.glLinkProgram; import static android.opengl.GLES20.glShaderSource; import static android.opengl.GLES20.glViewport; import static android.opengl.GLES20.glGetShaderiv; import static android.opengl.GLES20.glBindAttribLocation; import static android.opengl.GLES20.glGenTextures; import static android.opengl.GLES20.glBindTexture; import static android.opengl.GLES20.glTexImage2D; import static android.opengl.GLES20.glTexParameterf; import static android.opengl.GLES20.glGetIntegerv; import static android.opengl.GLES20.glGenFramebuffers; import static android.opengl.GLES20.glBindFramebuffer; import static android.opengl.GLES20.glFramebufferTexture2D; import static android.opengl.GLES20.glCheckFramebufferStatus; import static android.opengl.GLES20.glGetError; import static android.opengl.GLES20.glBindBuffer; import static android.opengl.GLES20.glUseProgram; import static android.opengl.GLES20.glActiveTexture; import static android.opengl.GLES20.glGetUniformLocation; import static android.opengl.GLES20.glUniformMatrix4fv; import static android.opengl.GLES20.glEnableVertexAttribArray; import static android.opengl.GLES20.glUniform1i; import static android.opengl.GLES20.glDrawArrays; import static android.opengl.GLES20.glDisableVertexAttribArray; import static android.opengl.GLES20.glTexParameteri; import static android.opengl.GLES20.glVertexAttribPointer; import android.opengl.GLES11Ext; import static android.opengl.EGL14.eglGetError; import static android.opengl.GLES20.GL_TEXTURE_2D; import static android.opengl.GLES20.GL_RGBA; import static android.opengl.GLES20.GL_UNSIGNED_BYTE; import static android.opengl.GLES20.GL_LINEAR; import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER; import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER; import static android.opengl.GLES20.GL_FRAMEBUFFER_BINDING; import static android.opengl.GLES20.GL_TEXTURE_WRAP_S; import static android.opengl.GLES20.GL_CLAMP_TO_EDGE; import static android.opengl.GLES20.GL_TEXTURE_WRAP_T; import static android.opengl.GLES20.GL_FRAMEBUFFER; import static android.opengl.GLES20.GL_COLOR_ATTACHMENT0; import static android.opengl.GLES20.GL_DEPTH_ATTACHMENT; import static android.opengl.GLES20.GL_RENDERBUFFER; import static android.opengl.GLES20.GL_FRAMEBUFFER_COMPLETE; import static android.opengl.GLES20.GL_TEXTURE0; import static android.opengl.GLES20.GL_TRIANGLE_STRIP; import static android.opengl.GLES20.GL_COMPILE_STATUS; import static android.opengl.GLES20.GL_ARRAY_BUFFER; import static android.opengl.GLES20.GL_ELEMENT_ARRAY_BUFFER; import static android.opengl.GLES20.GL_FALSE; import static android.opengl.GLES20.GL_FLOAT; import static android.opengl.GLES20.GL_FRAGMENT_SHADER; import static android.opengl.GLES20.GL_LINK_STATUS; import static android.opengl.GLES20.GL_NO_ERROR; import static android.opengl.GLES20.GL_STATIC_DRAW; import static android.opengl.GLES20.GL_VERTEX_SHADER; /* * public final String vertexShaderCode = "attribute vec4 vPosition;" + "attribute vec4 vColor;" + "attribute vec2 vUV;" + "varying mediump vec2 varUV;"+ "varying mediump vec4 varColor;"+ "void main() {" + " varColor = vColor;"+ " gl_Position = vPosition;" + " varUV = vUV.st;"+ "}"; public final String fragmentShaderCode = "precision mediump float;" + "uniform sampler2D sampler2d;"+ "varying mediump vec2 varUV;"+ "varying mediump vec4 varColor;"+ "void main() {" + " gl_FragColor = varColor;" + " gl_FragColor = texture2D(sampler2d,varUV);"+ "}"; * */ public class Triangle { public final String vertexShaderCode = "attribute vec4 vPosition;" + "attribute vec4 vColor;" + "attribute vec2 vUV;" + "varying mediump vec2 varUV;"+ "varying mediump vec4 varColor;"+ "void main() {" + " varColor = vColor;"+ " varUV = vUV.st;"+ " gl_Position = vPosition;" + "}"; public final String fragmentShaderCode = "precision mediump float;" + "uniform sampler2D sampler2d;"+ "varying mediump vec2 varUV;"+ "varying mediump vec4 varColor;"+ "void main() {" + " gl_FragColor = texture2D(sampler2d,varUV);"+ "}"; private int loadShader(int type, String shaderCode){ int err = 0; int shader = GLES20.glCreateShader(type); err = EGL14.eglGetError(); if (err != EGL14.EGL_SUCCESS) return 0; GLES20.glShaderSource(shader, shaderCode); if (EGL14.eglGetError() != EGL14.EGL_SUCCESS) return 0; GLES20.glCompileShader(shader); return shader; } private static String LOGTAG = "ORE"; public static void checkError(String op) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(LOGTAG, op + ": glError 0x" + Integer.toHexString(error)); } } private int shaderProgram; public Triangle() { } int[] m_nIchimatsuTexture = new int[1]; int[] m_nHogeTexture = new int[1]; public void loadGLTexture(Context context){ Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.texaxis); //Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.r1); Bitmap bitmap01 = Bitmap.createScaledBitmap(bitmap, 512, 512, false); try { bitmap = bitmap01; } finally { } glGenTextures(1, m_nHogeTexture, 0); glBindTexture(GL_TEXTURE_2D, m_nHogeTexture[0]); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); } //やり方がわからない。 /* void init_texture() { final int TEX_SIZE = 256; byte[] pTexData = new byte[TEX_SIZE*TEX_SIZE*4]; for (int i = 0; i < TEX_SIZE; i++) { for (int j = 0; j < TEX_SIZE; j++) { // Fills the data with a fancy pattern //int col = (255 << 24) + ((255 - j * 2) << 16) + ((255 - i) << 8) + (255 - i * 2); int col1 = 255; int col2 = (255 - j * 2); int col3 = (255 - i); int col4 = (255 - i * 2); if(( ((i * j) / 8) % 2) != 0){ // col = (int) (255 << 24) + (255 << 16) + (0 << 8) + (255); col1 = 255; col2 = 255; col3 = 0; col4 = 255; } //pTexData[j * TEX_SIZE + i] = col; int index = (i*TEX_SIZE + j)*4; pTexData[index + 3] = (byte)col1; pTexData[index + 2] = (byte)col2; pTexData[index + 1] = (byte)col3; pTexData[index + 0] = (byte)col4; } } //for (int i = 0; i < TEX_SIZE*TEX_SIZE*4; i++) pTexData[i] = (byte)0xff; GLES20.glGenTextures(1, m_nIchimatsuTexture,0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_nIchimatsuTexture[0]); GLES20.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); GLES20.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); checkError("glTexParameterf"); { Bitmap bitmap = BitmapFactory.decodeByteArray(pTexData, 0, pTexData.length); Bitmap bitmap01 = Bitmap.createScaledBitmap(bitmap, TEX_SIZE, TEX_SIZE, false); try { bitmap = bitmap01; } catch (Exception e) { e.printStackTrace(); } ByteBuffer bb = ByteBuffer.allocate(TEX_SIZE * TEX_SIZE); bb.wrap(pTexData); GLES20.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, bb); GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); checkError("glTexImage2D"); } }*/ public int Init(){ int err = 0; int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); shaderProgram = GLES20.glCreateProgram(); err = GLES20.glGetError(); if (err != GLES20.GL_NO_ERROR) return err; GLES20.glAttachShader(shaderProgram, vertexShader); GLES20.glAttachShader(shaderProgram, fragmentShader); GLES20.glLinkProgram(shaderProgram); err = GLES20.glGetError(); if (err != GLES20.GL_NO_ERROR) return err; int[] status = new int[1]; glGetProgramiv(shaderProgram, GL_LINK_STATUS, status, 0); if (status[0] == GL_FALSE) { String msz = glGetProgramInfoLog(shaderProgram); Log.e("sample", glGetProgramInfoLog(shaderProgram)); glDeleteProgram(shaderProgram); } // init_texture(); return GLES20.GL_NO_ERROR; } public void draw(){ int err = 0; //if( shaderProgram == 0 ) return; GLES20.glUseProgram(shaderProgram); err = GLES20.glGetError(); if (err != GLES20.GL_NO_ERROR) return ; glBindTexture(GL_TEXTURE_2D, m_nHogeTexture[0]); glActiveTexture((m_nHogeTexture[0])); glUniform1i(glGetUniformLocation(shaderProgram, "sampler2d"), 0); int positionAttrib = GLES20.glGetAttribLocation(shaderProgram, "vPosition"); GLES20.glEnableVertexAttribArray(positionAttrib); if( true ) { // xyz, rgba, float vertices[] = { -0.6f, -0.4f, 0.0f, 1.0f, 0.6f, -0.4f, 0.0f, 1.0f, -0.6f, 0.4f, 0.0f, 1.0f, 0.6f, 0.4f, 0.0f, 1.0f, }; ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4); bb.order(ByteOrder.nativeOrder()); FloatBuffer vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); int stride = 0; GLES20.glVertexAttribPointer(positionAttrib, 4, GLES20.GL_FLOAT, false, stride, vertexBuffer); } boolean enableColor = true; int colorAttrib = 0; if( enableColor ) { float colors[] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, }; colorAttrib = GLES20.glGetAttribLocation(shaderProgram, "vColor"); GLES20.glEnableVertexAttribArray(colorAttrib); ByteBuffer bb2 = ByteBuffer.allocateDirect(colors.length * 4); bb2.order(ByteOrder.nativeOrder()); FloatBuffer colorBuffer = bb2.asFloatBuffer(); colorBuffer.put(colors); colorBuffer.position(0); int stride = 0; glVertexAttribPointer(colorAttrib, 4, GL_FLOAT, false, stride, colorBuffer); } boolean enableTexture = true; int uvAttrib = 0; if( enableTexture ) { float uvs[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; uvAttrib = GLES20.glGetAttribLocation(shaderProgram, "vUV"); GLES20.glEnableVertexAttribArray(uvAttrib); checkError("glEnableVertexAttribArray"); ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4); bb.order(ByteOrder.nativeOrder()); FloatBuffer uvBuffer = bb.asFloatBuffer(); uvBuffer.put(uvs); uvBuffer.position(0); int stride = 0; glVertexAttribPointer(uvAttrib, 2, GL_FLOAT, false, stride, uvBuffer); checkError("glVertexAttribPointer"); } //GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); checkError("glDrawArrays"); GLES20.glDisableVertexAttribArray(positionAttrib); if( enableColor ) GLES20.glDisableVertexAttribArray(colorAttrib); if( enableTexture ) GLES20.glDisableVertexAttribArray(uvAttrib); checkError("glDisableVertexAttribArray"); } }
- 投稿日:2020-09-15T18:08:37+09:00
再インストールしてもRoomがVersionが違うとクラッシュし続ける場合の対処法
RoomでSchemaを変更したにもかかわらずDatabaseのバージョンを変更していないと以下のログが出てクラッシュします。
java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
おっしゃるとおり、Versionを上げてMigrationを書くのが正攻法なのですが、開発中でまだ定義が定まっていない時に毎回Versionを上げてMigartionを書くのは馬鹿らしいですよね。開発中のデータなんて消えても問題ないのであれば、schemaを変更したときはversionを上げて
fallbackToDestructiveMigration
で消しちゃえばいいです。Room.databaseBuilder( context, RoomDatabase::class.java, "room_database" ) // Version違ってMigrationなかったら消しちゃう .fallbackToDestructiveMigration() .build()とはいっても、毎回versionを変えるのすらめんどくさい。そういうときもあるとおもいます。人間ですからね。そんなときはアプリを再インストールすればマイグレーションもなにも無いだろうと思うかもしれませんが、再インストールしてもMigrationコードが無いとクラッシュする場合があります。
これは、
AndroidManifest.xml
でandroid:allowBackup=true
が設定されているとDB情報がバックアップされてしまうからです。allowBackup
はデフォルトでtrueが設定されているため、明示的にfalseを設定してあげないといけません。アンインストールしてもVersionが違うとクラッシュし続ける場合は確認してみましょう。
- 投稿日:2020-09-15T18:05:38+09:00
AndroidのBasic Android Accessibilityコードラボのメモ
アクセシビリティについて、少し触れる機会があったので、メモを残しておきます。
このコードラボです。
https://codelabs.developers.google.com/codelabs/basic-android-accessibility/#0このコードラボの目的は以下
- 障害のある方がどのようにAndroidのアプリケーションを使うのかを学ぶ。
- TalkBackやAccessibility Scannerを使って、どのようにアクセシビリティのissueを確認、テスト、デバッグするのか
- アクセシビリティに対してどのようなAPIが使われるのか
サンプルコード
https://github.com/googlecodelabs/basic-android-accessibility
のmasterがアクセシビリティができていない状態で、以下のブランチができている状態になっている
https://github.com/googlecodelabs/android-accessibility/tree/accessible障害のある方がどのようにAndroidのアプリケーションを使うのか
TalkBackを使う
TalkBackを使うことで聴覚、触覚、音声によるフィードバックを提供する。目を使わずにアプリを利用できる。
TalkBackの有効化
設定でTalkBackで検索して有効化できる。
有効化するとチュートリアルが始まるって使い方を学べる。TalkBackの無効化
音量の上げるボタンと下げるボタンを同時押しでできるメニューが出てくるみたいなんですが、できなかったので、GoogleアシスタントにTalkBackをオフしてとお願いしました。
他にも以下の2本指操作で普通に設定画面からオフにできます。TalkBack中にスワイプやクリックする
2本指で操作するとTalkBackでないときみたいに通常通りクリックできるので設定画面にたどり着けます。 (目が見えない方にもこの機能は半分ぐらい使われるみたいです。)次の要素や前の要素にアクセスする。
左にスワイプまたは右にスワイプ他にもたくさんのジェスチャーがあるようです。
https://support.google.com/accessibility/android/answer/6151827TalkBackやAccessibility Scannerを使って、どのようにアクセシビリティのissueを確認、テスト、デバッグするのか
TalkBackを使って確認する。例えば
contentDescription="@null"
にしていると "ラベルなし"と言われる。アクセシビリティに対してどのようなAPIが使われるのか
ラベルをつける
基本的にxmlではandroid:contentDescription=""をつける、コードではsetContentDescriptionを使う。 EditTextではhintを使う。
ラベルをつけるためのベストプラクティス
- 内容を簡潔にする (点字の場合に40文字しか読むことができないため)
- コンテンツの説明にタップなどの言葉を使わない。ユーザーによっては画面に触れない場合がある。AccessibilityServiceがそこをサポートする。
- 最も意味的に重要な情報がコンテンツラベルの最初に来るようにする。
コンテンツのグループ化
例えば一つずつ分かれてしまっていると、
「アーティスト」って読み上げられて、ユーザーが右にスワイプして、「ビートルズ」みたいになってしまって、すごく非効率な感じで読み上げが行われてしまいます。
読み上げさせたいスコープでViewGroupで囲って、そのViewGroupにforcusableをつけることで対応できるらしい。
(ただ、レイアウトが深くなるとAndroid的にはパフォーマンスが若干悪くなるので、一旦はラベルを付けることを優先で良いかも。)ライブリージョン
例えば、ラジオボタンがあって、下にあっているか表示されるときに、フォーカスだけでは、あっているみたいな表示がされても分からないですよね?そういうときに使えるのが、
android:accessibilityLiveRegion
です。
android:accessibilityLiveRegion="polite"のように指定し、politeとassertiveがあります。
android:accessibilityLiveRegionがViewに設定されていると、テキストが変わったタイミングで読み上げがされます。
assertiveは今読み上げようとしているキューの最初に割り込んで読み上げがされ、politeはキューに積まれます。
うざくなってしまうので、politeを基本的には使うと良いそうです。CustomViewへの対応
このコードラボでは触れられないが、いくつかのAPIへの紹介がありました。
AccessibilityNodeProvider classを使ってvirtual view hierarchy
を提供できる。
ExploreByTouchHelperでいくつものコレクションがある場合に、アクセシビリティサービスに情報を提供し、フォーカスを指定するのに役立つ。
AccessibilityDelegate classはViewにセットすることができ、互換性を保つことができるそうです。タッチエリアを広くする
指が大きかったり、運動能力の症状があったりするので、大きくしたほうがよい。48dp以上だと良い。
Settings > System > Developer OptionsでShow layout bounds
でデバッグする
https://codelabs.developers.google.com/codelabs/basic-android-accessibility/#9 より単純に大きさをpaddingなどで変えるか、TouchDelegate APIを使う。
コントラストの確保
コントラスト比が低いと一部のユーザーにとってぼやけて表示されてしまう。
Web Content Accessibility Guidelines では4.5:1がテキストに対して最低限必要で、3.0:1で大きい文字や太い文字に対して必要となっている。
比率は以下でみられる。 (luma符号化して、割り算して割合を出すみたい。)
https://webaim.org/resources/contrastchecker/Accessibility Scannerを使っても確認できる。
Accessibility Scanner
このアプリを入れることで、コントラストや文字の大きさなどを確認できる。
https://play.google.com/store/apps/details?id=com.google.android.apps.accessibility.auditor
ただ、これだけだと手動テストの代わりにはならないので、talkbackを使ってちゃんと確認しましょうと書いてありました。
- 投稿日:2020-09-15T18:03:22+09:00
4. 【Android/Kotlin】リストビュー(ListView)
はじめに
DreamHanksのMOONです。
前回は画面を遷移させる方法と遷移させる場合、値も送る方法について説明をしました。
3. 【Android/Kotlin】画面遷移今回は「リストビュー」というViewについて説明をしていきます。
ListViewとは
ListViewは、スクロール可能な項目を表す時に使用されるビューグループです。
リストビューを使うためには下記の項目が必要です。
・データークラス
・xmlにリストビュー
・アイテム作成
・アダプター作成
・アダプター設定ListView追加
xml生成及びListView追加
activity_listview.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical" tools:context=".ListViewActivity" android:gravity="center"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"> </ListView> </LinearLayout>データークラス追加
データークラスを追加(テストするため、ユーザー(名前、メール)のクラス)
User.ktpackage com.example.practiceapplication class User (val name: String, val email: String)アイテムのxml追加
item_user.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout android:id="@+id/firstLin" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="#ffffff" android:padding="10dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/imageIcon" android:layout_width="52dp" android:layout_height="52dp" android:layout_margin="10dp" android:src="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="10dp" android:layout_toRightOf="@+id/imageIcon" android:orientation="vertical"> <TextView android:id="@+id/name_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14sp" android:text="佐藤" /> <TextView android:id="@+id/email_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="12sp" android:text="sato@ggggg.com"/> </LinearLayout> </RelativeLayout> </RelativeLayout> </LinearLayout>Adapter追加
リストビューとデーターのアイテム間の連結するためにアダプター追加
ListAdapter.ktpackage com.example.practiceapplication import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import android.widget.TextView class ListAdapter (val context: Context, val UserList: ArrayList<User>) : BaseAdapter() { override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { val view: View = LayoutInflater.from(context).inflate(R.layout.item_user, null) val Name = view.findViewById<TextView>(R.id.name_tv) val Email = view.findViewById<TextView>(R.id.email_tv) val user = UserList[position] Name.text = user.name Email.text = user.email return view } override fun getItem(position: Int): Any { return UserList[position] } override fun getItemId(position: Int): Long { return 0 } override fun getCount(): Int { return UserList.size } }Activity設定
Activityにリストビューとアダプターを作成及び設定
ListViewActivity.ktpackage com.example.practiceapplication import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.* import kotlinx.android.synthetic.main.activity_listview.* import javax.xml.validation.Validator class ListViewActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_listview) //ユーザーリストでデーターを追加 var UserList = arrayListOf<User>( User("佐藤","sato@ggggg.com"), User("鈴木","suzuki@aaaaa.com"), User("高橋","takahasi@yyyyy.com"), User("伊藤","ito@fffff.com"), User("渡辺","watanabe@bbbbb.com"), User("山本","yamamoto@zzzzz.com"), User("中村","nakamura@ccccc.com"), User("小林","kobayasi@xxxxx.com"), User("加藤","gato@wwwww.com") ) var list_view = findViewById<ListView>(R.id.list_view) //アダプターにユーザーリストを導入 val Adapter = ListAdapter(this, UserList) //リストビューにアダプターを設定 list_view.adapter = Adapter } }・ユーザーリストを作成し、データーをリストに追加します。
・アダプターにそのリストを設定します。
・リストビューに設定されているアダプターを設定します。アプリ起動
終わりに
今回は「リストビュー」というViewについて説明をしました。
次回は「ダイアログ」について説明をしていきます。
5. 【Android/Kotlin】ダイアログ(Dialog)
- 投稿日:2020-09-15T14:42:49+09:00
スマホの時、htmlをfont-size:62.5%;にしないと画面に余白ができる
iPhone(safari)では大丈夫なのに、Androidは余白が!!!
パソコンでの開発では出なかったのですが、
実機チェックした時、
Android端末では、以下のような空白ができました。。。
chromeだけでなく、chatworkやslackのプレビューでもこの空白ができました。
原因
スマホの時、htmlを「font-size: 62.5%;」にしていなかったためです。
これは「ルートのフォントサイズ」で、
あるサイトにはこう書いていました。font-size: 62.5%;は呪文みたいなもの」
[引用][css]html, bodyのfont-sizeはいくつにしてる?~よく見る62.5%ってなに!?~
有無を言わずに入れとけ!ということですね。
対処法
とはいえ、全部のフォントサイズを変えるのは大変なので、
以下のように応急処置しました。
ポイントは、htmlとbody両方にoverflow-x: hidden;を入れること。
※overflow:hidden;にすると、スクロールできなくなります。@media screen and (max-width:768px) { //スマホ時 html { font-size: 90%; //自分が設定したい大きさで overflow-x: hidden; } body{ overflow-x: hidden; } }
- 投稿日:2020-09-15T10:49:33+09:00
Navigation導入するならきっと役に立つ5つのTips
最近、FragmentTransactionからNavigationへ移行しました。
今回の記事では、基本的なNavigationの実装方法などは他にも記事がたくさんあるのでそこは省いています。
基本的な実装というよりは、ここでは設計時に考慮すべきだった事や実装時に気をつけないとクラッシュが起きる所の話などを中心にしています。
Navigationに移行作業中の方や、これからNavigationの導入を検討している方など、誰かの参考になれば嬉しいです。
1. Action と Global Action
デスティネーション間の経路を視覚的に表示する Action についてのTipsです。
まず基本的な話ですが、Navigationでは、xmlで書かれたナビゲーショングラフを使用してアプリのナビゲーション(画面遷移)を管理します。例えば、Fragmentが3つだけあるアプリのナビゲーショングラフはこんな感じ。
main_navigation.xml<?xml version="1.0" encoding="utf-8"?> <navigation 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:id="@+id/main_navigation" app:startDestination="@id/first_fragment"> <fragment android:id="@+id/first_fragment" android:name="com.example.sample.FirstFragment" tools:layout="@layout/fragment_first"> <action android:id="@+id/action_first_fragment_to_second_fragment" app:destination="@id/second_fragment" /> </fragment> <fragment android:id="@+id/second_fragment" android:name="com.example.sample.SecondFragment" tools:layout="@layout/fragment_second"> <action android:id="@+id/action_second_fragment_to_third_fragment" app:destination="@id/third_fragment" /> </fragment> <fragment android:id="@+id/third_fragment" android:name="com.example.sample.ThirdFragment" tools:layout="@layout/fragment_third"> </fragment> </navigation>この例だと、First → Second → Third というシンプルな流れで、この各経路がActionで定義されています。経路が簡潔でわかりやすいですね。
次に、画面を2つ増やして、さらにどの画面からも最初の画面(FirstFragment)に遷移する経路を作るとなったとき、単純にactionを追加するとこんなグラフになります。
ちょっと線が絡み合って見にくくなりましたね。まだFragmentが5個だけで経路も多くないですが、今後Fragmentの数が20, 40, 60,,,と増えると、どことどこが繋がっているのか追いにくくなってしまうのが想像して頂けるかと思います。
で!こんなときは、Global Actionを使ってやればいいです。
設定は、こんな風にナビゲーショングラフ上のGUIから簡単にできます。
FirestFragmentの左側に付いている、→ がGlobalActionです。
線が減ってすっきりしましたー!
このように複数の画面から遷移される画面(たとえばアプリのホーム画面とか)への経路は、Global Action
で定義するとグラフの線が絡み合わずにすっきり管理することができます。xmlの定義も少なく済むし。ただちょっと注意点が、この
Global Action
が便利すぎるところ。便利で何が悪いねんって言われそうですが、
極端な例としてあげると、
全経路をGlobal Action
で定義したグラフがこれです
(もはやグラフになってない)
まあこんなことするケースは中々ないとは思いますが、全部Global Actonで定義しても動作的には問題はないって事を言いたかった。ただし、Fragment間に線が表示されないので視覚的に経路が追えなくなりますね。私は、ラクしたくて気づいたらGlobalActionを結構使っていたので、実装途中で「これでいいのか?」って少し迷ったりしました。
アプリの規模やユースケースによるので正解はないと思いますが、
Global Action
と通常のAction
をどう使い分けるかは、ナビゲーショングラフの設計時に考慮しましょう。2. Navigationに add Fragmentはない!!
Navigationには、add Fragmentが存在しないので、
FragmentTransaction
でadd
を使用している方は注意が必要ですよっていうお話です。Navigationでは、replaceで遷移をすることが前提なんですって。
そのため、addを使っていた所はreplaceされても問題ないようにFragment側の実装を修正しました。
ただしどうしても
add
の挙動にしたい場合は以下のような対応が選択肢としてあります。参考までに。
- データを
ViewModel
に保存してonViewCreated()
でチェックする<Dialog>
を使用して全画面DialogFragmentsを使用する3. safeArgsで意外に渡せなかった引数の型
safeArgsに関するTipsです。
Navigationの便利機能の1つであり、安全に引数の引き渡しが可能になるsafeAegs。このsafeArgsで、意外に引き渡す事ができない型がいくつかありました。そのうちの1つで個人的に意外だったのが
List
。ちなみに、
double
,short
,byte
,char
もビルド時にエラーになって使えない。
List
のように、そのままだと渡せない場合は、@Parcelize
と組み合わせて data classを作成してそれを引き渡すか、bundle
で渡す方法がある。サポートされている引数の型は、デスティネーション間でデータを渡すにまとまっているので、new Instanceで引き渡していた所をSafeArgsにしたいと考えている場合はご一読しとくのがおすすめ。
4.java.lang.RuntimeException: Unable to start activity
各FragmentでNavControllerを使用するときに気をつけたいところです。
結論から言うと、Fragment のonCreate()
でNavController
を取得する実装をしているのがよくなかった。
これがよくない例。FirstFragment.ktoverride fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) navController = findNavController() }このままだと通常起動時は問題ないですが、Activityが再生成されたときに以下のクラッシュが発生します。
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sample/com.example.sample.MainActivity}: android.view.InflateException: Binary XML file line #9 in com.example.sample:layout/activity_main: Binary XML file line #9 in com.example.sample:layout/activity_main: Error inflating class fragment at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)ログは、
まだ親のActivityが生成されていないのにFragment側でNavControllerを取得しようとするからクラッシュが発生してしまってるよ
と言ってます。
対処としては、Kotlinの場合は、lazyで遅延初期化するよう実装すれば解決します。FirstFragment.ktprivate val navController by lazy { findNavController() }Javaの場合、便利なlazyはないので、DaggerやDIする方法がある。
うちではKotlin化を進めているのでこの機会にまだJavaで書かれていたFragmentは全てKotlin化しました。5. java.lang.IllegalArgumentException: navigation destination XXX is unknown to this NavController
各Fragmentでナビゲーションの処理を書くときに気をつけたいところです。
私の場合は、リリース後にクラッシュが発生して少し困ったので、今後Navigationに移行する人には同じ轍を踏んでほしくない。そのクラッシュの例がこれ
(FirstFragmentでnextボタンを連打してSecondFragmentに遷移しようとしたときのクラッシュ)java.lang.IllegalArgumentException: navigation destination com.example.sample:id/action_first_fragment_to_second_fragment is unknown to this NavController at androidx.navigation.NavController.navigate(NavController.java:789) at androidx.navigation.NavController.navigate(NavController.java:730) at androidx.navigation.NavController.navigate(NavController.java:716) at androidx.navigation.NavController.navigate(NavController.java:704) at com.example.sample.FirstFragment$onViewCreated$1.onClick(FirstFragment.kt:27)ログは、
現在のNavControllerは、action_first_fragment_to_second_fragmentなんて知らないですよ。
と言っていますね。発生原因として考えられるもの
- 連打
- 複数のナビゲーションの同時押し
- etc.
つまり、FirstFragmentからSecondFragmentへの遷移で発生したクラッシュの一連の流れはこんな感じ。
1. FirstFragmentからSecondFragmentへ遷移する経路を高速連打 2. SecondFragmentに遷移しているのに、action_first_fragment_to_second_fragmentを実行しようとする 3. SecondFragmentのNavControllerはそんなDestinationは知らないのでエラーを出すこの対処として役に立つのがcurrentDestinationチェックです。
FirstFragment.ktoverride fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) next_button?.setOnClickListener { if (navController.currentDestination?.id == R.id.first_fragment) { navController.navigate(R.id.action_first_fragment_to_second_fragment) } } }今のところ手元で再現できているのが、アニメーション付きで遷移する経路での連打や同時押しですが、アニメーションなしの経路でも同様のクラッシュは発生するようです。
そのため、このcurrentDestinationをチェックする処理はナビゲーションの前に必ず入れた方がよさそうな所感が今のところありますが。もっと良い方法などがあればぜひ教えてください。
おわり
最後まで読んでいただきありがとうございました。
- 投稿日:2020-09-15T02:07:11+09:00
コード補完がちゃんと効く状態で Flutter の Plugin プロジェクトを開く
Android Studio 上で flutter の Plugin プロジェクトを普通に開くと、 flutter SDK 関係のライブラリのコード補完が効きません。
flutter/dart プラグインが Android Studio にインストールされた状態で、プロジェクトの android フォルダを右クリックし、メニューから [Flutter]-[Open Android module in Android Studio] を選択すると、コード補完が効いた状態で Plugin プロジェクトを開くことができます。