20200915のAndroidに関する記事は8件です。

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」と検索する。

1.PNG

② コマンドプロンプトを開き、「ipconfig」と入力して、Enterを押します。

2.PNG

③ なんか沢山出てきます。

3PNG.PNG

色々書いていますが、デフォルトゲートウェイが開いている箇所を探します。

今回の場合は、

イーサネット アダプター 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.PNG

4. WindowsPCでlocalhost:8000にアクセス

スクリーンショット (4).png

無事、ページが表示されました。

5. その他の機器で試す

※WindowsPCからは今回の場合、localhost:3000とブラウザのURLに入力することで、表示されますが、その他機器では、192.168.43.187:8000と入力します。

Androidスマホ

iPad

MacBook

スクリーンショット 2020-09-11 0.30.50.png

このように、同じWi-Fiに接続することにより、WindowsPCで起動したloclhostにAndroidスマホとiPad、MacBookからもアクセスすることが出来ました。

参考記事:パソコンのIPアドレスを確認する方法(Standard TCP/IP)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android/OpenGL sample1

(1)四角形を表示するだけ。
塗りつぶし色はuniform変数で指定している。

(2)三角形
頂点カラー

(3) テクスチャ
image.png

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");

    }
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

再インストールしても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.xmlandroid:allowBackup=true が設定されているとDB情報がバックアップされてしまうからです。 allowBackup はデフォルトでtrueが設定されているため、明示的にfalseを設定してあげないといけません。アンインストールしてもVersionが違うとクラッシュし続ける場合は確認してみましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/6151827

TalkBackやAccessibility Scannerを使って、どのようにアクセシビリティのissueを確認、テスト、デバッグするのか

TalkBackを使って確認する。例えばcontentDescription="@null"にしていると "ラベルなし"と言われる。

アクセシビリティに対してどのようなAPIが使われるのか

ラベルをつける

基本的にxmlではandroid:contentDescription=""をつける、コードではsetContentDescriptionを使う。 EditTextではhintを使う。

ラベルをつけるためのベストプラクティス

  • 内容を簡潔にする (点字の場合に40文字しか読むことができないため)
  • コンテンツの説明にタップなどの言葉を使わない。ユーザーによっては画面に触れない場合がある。AccessibilityServiceがそこをサポートする。
  • 最も意味的に重要な情報がコンテンツラベルの最初に来るようにする。

コンテンツのグループ化

例えば一つずつ分かれてしまっていると、
「アーティスト」って読み上げられて、ユーザーが右にスワイプして、「ビートルズ」みたいになってしまって、すごく非効率な感じで読み上げが行われてしまいます。
読み上げさせたいスコープでViewGroupで囲って、そのViewGroupにforcusableをつけることで対応できるらしい。
(ただ、レイアウトが深くなるとAndroid的にはパフォーマンスが若干悪くなるので、一旦はラベルを付けることを優先で良いかも。)

ライブリージョン

例えば、ラジオボタンがあって、下にあっているか表示されるときに、フォーカスだけでは、あっているみたいな表示がされても分からないですよね?そういうときに使えるのが、 android:accessibilityLiveRegion です。
image.png

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を使ってちゃんと確認しましょうと書いてありました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

4. 【Android/Kotlin】リストビュー(ListView)

はじめに

DreamHanksのMOONです。

前回は画面を遷移させる方法と遷移させる場合、値も送る方法について説明をしました。
3. 【Android/Kotlin】画面遷移

今回は「リストビュー」というViewについて説明をしていきます。

ListViewとは

ListViewは、スクロール可能な項目を表す時に使用されるビューグループです。

リストビューを使うためには下記の項目が必要です。
 ・データークラス
 ・xmlにリストビュー
 ・アイテム作成
 ・アダプター作成
 ・アダプター設定

ListView追加

xml生成及びListView追加

 xmlを生成し、リストビューを追加します。
1.JPG

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> 

データークラス追加

 データークラスを追加(テストするため、ユーザー(名前、メール)のクラス)
2.JPG

User.kt
package com.example.practiceapplication 

class User (val name: String, val email: String)    

アイテムのxml追加

 リストビューに表示されるアイテムのxmlを追加
3.JPG

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.kt
package 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.kt
package 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)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマホの時、htmlをfont-size:62.5%;にしないと画面に余白ができる

iPhone(safari)では大丈夫なのに、Androidは余白が!!!

パソコンでの開発では出なかったのですが、
実機チェックした時、
Android端末では、以下のような空白ができました。。。
chromeだけでなく、chatworkやslackのプレビューでもこの空白ができました。
スクリーンショット-2020-09-15-14.24.58.png

原因

スマホの時、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;
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Navigation導入するならきっと役に立つ5つのTips

最近、FragmentTransactionからNavigationへ移行しました。

今回の記事では、基本的なNavigationの実装方法などは他にも記事がたくさんあるのでそこは省いています。

基本的な実装というよりは、ここでは設計時に考慮すべきだった事や実装時に気をつけないとクラッシュが起きる所の話などを中心にしています。

Navigationに移行作業中の方や、これからNavigationの導入を検討している方など、誰かの参考になれば嬉しいです。:pray:

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で定義されています。経路が簡潔でわかりやすいですね。
スクリーンショット 2020-09-14 12.48.55.png
次に、画面を2つ増やして、さらにどの画面からも最初の画面(FirstFragment)に遷移する経路を作るとなったとき、単純にactionを追加するとこんなグラフになります。
スクリーンショット 2020-09-14 13.04.46.png
ちょっと線が絡み合って見にくくなりましたね。

まだFragmentが5個だけで経路も多くないですが、今後Fragmentの数が20, 40, 60,,,と増えると、どことどこが繋がっているのか追いにくくなってしまうのが想像して頂けるかと思います。:thinking:

で!こんなときは、Global Actionを使ってやればいいです。:point_up:
設定は、こんな風にナビゲーショングラフ上のGUIから簡単にできます。
スクリーンショット 2020-09-14 13.16.13.png
FirestFragmentの左側に付いている、→ がGlobalActionです。
線が減ってすっきりしましたー!:sparkles:
スクリーンショット 2020-09-14 13.19.01.png
このように複数の画面から遷移される画面(たとえばアプリのホーム画面とか)への経路は、Global Actionで定義するとグラフの線が絡み合わずにすっきり管理することができます。xmlの定義も少なく済むし。:relieved:

ただちょっと注意点が、このGlobal Actionが便利すぎるところ。

便利で何が悪いねんって言われそうですが、

極端な例としてあげると、
全経路をGlobal Actionで定義したグラフがこれです:point_down:
(もはやグラフになってない)
スクリーンショット 2020-09-14 13.28.21.png
まあこんなことするケースは中々ないとは思いますが、全部Global Actonで定義しても動作的には問題はないって事を言いたかった。ただし、Fragment間に線が表示されないので視覚的に経路が追えなくなりますね。

私は、ラクしたくて気づいたらGlobalActionを結構使っていたので、実装途中で「これでいいのか?」って少し迷ったりしました。:thinking:

アプリの規模やユースケースによるので正解はないと思いますが、Global Action通常のActionをどう使い分けるかは、ナビゲーショングラフの設計時に考慮しましょう。

2. Navigationに add Fragmentはない!!

Navigationには、add Fragmentが存在しないので、FragmentTransactionaddを使用している方は注意が必要ですよっていうお話です。

Navigationでは、replaceで遷移をすることが前提なんですって。:point_up:

そのため、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にしたいと考えている場合はご一読しとくのがおすすめ。:eyes:

4.java.lang.RuntimeException: Unable to start activity

各FragmentでNavControllerを使用するときに気をつけたいところです。
結論から言うと、Fragment のonCreate()NavControllerを取得する実装をしているのがよくなかった。
これがよくない例。

FirstFragment.kt
override 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.kt
private val navController by lazy { findNavController() }

Javaの場合、便利なlazyはないので、DaggerやDIする方法がある。
うちではKotlin化を進めているのでこの機会にまだJavaで書かれていたFragmentは全てKotlin化しました。:hatching_chick:

5. java.lang.IllegalArgumentException: navigation destination XXX is unknown to this NavController

各Fragmentでナビゲーションの処理を書くときに気をつけたいところです。
私の場合は、リリース後にクラッシュが発生して少し困ったので、今後Navigationに移行する人には同じ轍を踏んでほしくない。

そのクラッシュの例がこれ:point_down:
(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チェックです。:point_up:

FirstFragment.kt
override 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をチェックする処理はナビゲーションの前に必ず入れた方がよさそうな所感が今のところありますが。もっと良い方法などがあればぜひ教えてください。:bow_tone2:

おわり

最後まで読んでいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コード補完がちゃんと効く状態で Flutter の Plugin プロジェクトを開く

Android Studio 上で flutter の Plugin プロジェクトを普通に開くと、 flutter SDK 関係のライブラリのコード補完が効きません。

flutter/dart プラグインが Android Studio にインストールされた状態で、プロジェクトの android フォルダを右クリックし、メニューから [Flutter]-[Open Android module in Android Studio] を選択すると、コード補完が効いた状態で Plugin プロジェクトを開くことができます。

Screen Shot 2020-09-15 at 1.59.40.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む