Java OpenGL ES を学習する~Androidの開発環境を作る~

OpenGLES 開発環境構築

表題の通りにJava言語でのOpenGL-ESの開発環境を構築したいと思います。参考にする本は以下のものです。

参考書籍

Javaでの実装には役に立たなかったが、OpenGLの処理内容を理解するのには、役立った。

Android版のセットアップ

書籍のほうでは、EclipseにADT(Android開発プラグイン)を入れて実装していました。(参照したのが古い本でした)なので、Android Studioをインストールして実行します。

Windowsでのセットアップですので、インストーラーをダブルクリックしておしまいです。

セットアップはあまり重要でなく、プログラムの実装とOpenGLの仕組みを理解することがメインになります。ですので開発環境が多少違っても
OpenGLのテクノロジーを使用することには変わりないので影響しません。そしてOpenGLはクロスプラットフォームなのでどのOSにも対応しています。
まぁC++でできていればどのOSでもしようできますね。※Java言語も負けてないです。

<インストーラーを起動したときの画像>

Android Studio

上記のインストーラーを早速起動します。そしたら、下の用が画面が出たので、「Send usage statics to Google」をクリックします。

そして、インストールタイプ(Install Type)はスタンダードを指定して、各ライセンス規定に同意する必要があります。
その後、インストールが始まります。IntelliJ IDEAがベースになっているので、AndroidStudioはIntelliJ IDEAと操作方法が似ているはずです。

インストール完了後は下のように表示されした。

ここから先はAndroid仕様で、作成するアクティビティを選択してから実行していきます。
今回は、EmptyComposedActivityを選択してプロジェクトを作成しました。本家のチュートリアルを参照して作成しました。すごーくわかりやすいです。日本語で読めました。

OpenGLのセットアップ

本家のチュートリアルを参考に行いました。

子のチュートリアルは下のような項目を行っていました。

  1. OpenGL ES 環境の構築
    • Android アプリケーションをセットアップして OpenGL グラフィックスを描画できるようにする方法について説明します。
  2. 図形の定義
    • 図形を定義する方法と、面とワインディングについて認識しておく必要がある理由について説明します。
  3. 図形の描画
    • アプリケーションで OpenGL 図形を描画する方法について説明します。
  4. 投影とカメラビューの適用
    • 投影とカメラビューを使用して、描画されたオブジェクトの新しい視点を取得する方法について説明します。
  5. モーションの追加
    • OpenGL を使って描画されたオブジェクトの基本的な動きとアニメーション化を行う方法について説明します。
  6. タップイベントへの応答

このうち
1から3まで実行したところです。
結果として、三角形と四角形の描画があるのですが、とりあえず三角形の描画を行いました。実機にデプロイできるようにエミュレータを使用しました。

補足、Activityは、初めの状態だと「AppCompatActivity」が継承(extends)されているのでそれを「Activity」に変更する必要がありました。

そして、実機でのテストをするのには、やはり本家のサイトを参考にしました。

  1. 自分のAndroid端末とPCのWifiを同じSSIDに接続する
  2. Android StudioでPair Device using Wifiを選択
  3. Android端末でデバックモードをONにする
  4. 表示されたQRコードをAndroid端末で読み込む
  5. AndroidStudioでテストを実行する

<実行結果>

サンプルコードの実装

作成したファイルは以下のファイルです。

  • MainActivity: これはプロジェクトを作成したときに生成されたものです。中身を修正しました。
  • MyGLRender: チュートリアルにあったクラスです。
  • MyGLSurfaceView: チュートリアルにあったクラスです。
  • Square: チュートリアルにあったクラスです。
  • Triangle: チュートリアルにあったクラスです。

それぞれ以下のように作成しました。※ほぼコピペ

MainActivity


import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

public class MainActivity extends Activity /* AppCompatActivity */ {

    private GLSurfaceView gLView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
//        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
        super.onCreate(savedInstanceState);

        gLView = new MyGLSurfaceView(this);
        setContentView(gLView);
    }
}

MyGLRender

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;

public class MyGLRender implements GLSurfaceView.Renderer {

    private Triangle mTriangle;
    private Square   mSquare;

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        // initialize a triangle
        mTriangle = new Triangle();
        // initialize a square
        mSquare = new Square();    }

    public void onDrawFrame(GL10 gl) {
        // Redraw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        mTriangle.draw();
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }
}

MyGLSurfaceView

import android.content.Context;
import android.opengl.GLSurfaceView;

public class MyGLSurfaceView extends GLSurfaceView {
    private final MyGLRender render;

    public MyGLSurfaceView(Context context) {
        super(context);
        // Create an OpenGL ES 2.0 context
        setEGLContextClientVersion(2);

        render = new MyGLRender();

        // Set the Renderer for drawing on the GLSurfaceView
        setRenderer(render);
        // Render the view only when there is a change in the drawing data
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

    }
}

Square

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

public class Square {

    private FloatBuffer vertexBuffer;
    private ShortBuffer drawListBuffer;

    // 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 short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

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

Triangle

import android.opengl.GLES20;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class Triangle {

    private FloatBuffer vertexBuffer;
    private final int mProgram;

    private int positionHandle;
    private int colorHandle;

    private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    static float triangleCoords[] = {   // in counterclockwise order:
            0.0f,  0.622008459f, 0.0f, // top
            -0.5f, -0.311004243f, 0.0f, // bottom left
            0.5f, -0.311004243f, 0.0f  // bottom right
    };

    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "void main() {" +
                    "  gl_Position = vPosition;" +
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

    // Set color with red, green, blue and alpha (opacity) values
    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

    public Triangle() {
        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
                // (number of coordinate values * 4 bytes per float)
                triangleCoords.length * 4);
        // use the device hardware's native byte order
        bb.order(ByteOrder.nativeOrder());

        // create a floating point buffer from the ByteBuffer
        vertexBuffer = bb.asFloatBuffer();
        // add the coordinates to the FloatBuffer
        vertexBuffer.put(triangleCoords);
        // set the buffer to read the first coordinate
        vertexBuffer.position(0);

        int vertexShader = MyGLRender.loadShader(GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = MyGLRender.loadShader(GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        // create empty OpenGL ES Program
        mProgram = GLES20.glCreateProgram();

        // add the vertex shader to program
        GLES20.glAttachShader(mProgram, vertexShader);

        // add the fragment shader to program
        GLES20.glAttachShader(mProgram, fragmentShader);

        // creates OpenGL ES program executables
        GLES20.glLinkProgram(mProgram);
    }

    public void draw() {
        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(positionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        // get handle to fragment shader's vColor member
        colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(colorHandle, 1, color, 0);

        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(positionHandle);
    }
}

環境構築のまとめ

チュートリアルをそのまま実行すればすぐに環境構築ができました。KotlinでもJavaでも実装ができるので良いと思いました。
次は、2次元描画を行いたいところです。そして、この2次元描画を少し丹念に学習したいと思っております。

コード解析

下記の5クラスを実装しました。

  1. MainActivity
  2. MyGLRender
  3. MyGLSurfaceView
  4. Square
  5. Triangle

これらの関係性を表すのに、UMLを書きました。

こんな感じです。

処理概要

下のような感じです。

  • MainActivityでAndroidの画面を作成します。
  • MyGLSurfaceViewでOpenGLの描画を行うパレットのようなものを作成します。
  • MyGLRenderで実際に描画する三角形とか四角形を描画

処理内容: MainActivity

これはAndroidの画面を作成するクラスです。アンドロイドのAPIで定義されているActivittyクラスを継承して実装します。

  • 「extends AppCompatActivity」になっていたところを「Activity」に変更しています。
  • 「onCreate」メソッドをオーバーライドしています。
    • 親クラスで呼び出しているonCreateを上書きするので、こちらのメソッドが動きます。
  • フィールド変数にOpenGLのGLSurfaceViewを定義しています。これに、自作のMyGLSurfaceViewをセットします。
public class MainActivity extends Activity /* AppCompatActivity */ {

    private GLSurfaceView gLView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
//        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
        super.onCreate(savedInstanceState);

        gLView = new MyGLSurfaceView(this);
        setContentView(gLView);
    }
}

処理内容: MyGLRender

このクラスは、「MyGLRender」クラスをGLSurfaceView#setRendererにセットしています。
その他、細かい設定をしています。

public class MyGLSurfaceView extends GLSurfaceView {
    private final MyGLRender render;

    public MyGLSurfaceView(Context context) {
        super(context);
        // Create an OpenGL ES 2.0 context
        setEGLContextClientVersion(2);

        render = new MyGLRender();

        // Set the Renderer for drawing on the GLSurfaceView
        setRenderer(render);
        // Render the view only when there is a change in the drawing data
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

    }
}

処理内容:

「GLSurfaceView.Renderer」をimeplementsすることで、描画処理を行います。
それぞれ「\@Override」アノテーションがついているメソッドがインタフェースでオーバーライドを強制するメソッドになります。

コメントに何をしているか書いています。

  • 「Set the background frame color」=背景枠の色を設定する
  • 「initialize a triangle」=三角形初期化
  • 「initialize a square」=四角形初期化
  • 「Redraw background color」=背景の再描画
public class MyGLRender implements GLSurfaceView.Renderer {

    private Triangle mTriangle;
    private Square   mSquare;

    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        // initialize a triangle
        mTriangle = new Triangle();
        // initialize a square
        mSquare = new Square();
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // Redraw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        mTriangle.draw();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }
}

処理内容:Triangle

これは、長くなってしまうので、文言のみにしますが、

  1. コンストラクタ(Triangle())で描画の準備
  2. draw()で描画処理

上記を行っています。

処理内容:Square

こちらのクラスの描画処理は見当たらなかったので、改めて実装しました。
結論から言うと、三角形の描画メソッドとほぼ同じでした。違うのは、描画のためのデータ、以下のものです。

  • squareCoords: 四角形の各頂点定義
  • 描画モードの設定
    • GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // 三角形の描画
    • GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, vertexCount); // 四角形の描画
package jp.zenryoku.firstapp;

import android.opengl.GLES20;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

public class Square {

    private FloatBuffer vertexBuffer;
    private ShortBuffer drawListBuffer;
    private final int mProgram;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;

    private int positionHandle;

    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 short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

    private int colorHandle;

    private final int vertexCount = squareCoords.length / COORDS_PER_VERTEX;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
    // Set color with red, green, blue and alpha (opacity) values
    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "void main() {" +
                    "  gl_Position = vPosition;" +
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

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

        // create empty OpenGL ES Program
        mProgram = GLES20.glCreateProgram();

        int vertexShader = MyGLRender.loadShader(GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = MyGLRender.loadShader(GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);
        // add the vertex shader to program
        GLES20.glAttachShader(mProgram, vertexShader);

        // add the fragment shader to program
        GLES20.glAttachShader(mProgram, fragmentShader);

        // creates OpenGL ES program executables
        GLES20.glLinkProgram(mProgram);
    }

    public void draw() {
        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(positionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        // get handle to fragment shader's vColor member
        colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(colorHandle, 1, color, 0);

        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, vertexCount);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(positionHandle);
    }
}

Android App 〜画面作成を行う〜

画面作成

作成したのは、以下の機能です。

  1. 多言語化
  2. 画面に文字列を表示する

stringc.xmlを使う

下のように作成した*strings.xmlの中にある「android\:id="@+id\/top_title"」をアプリに反映するというところです。
次のファイルに以下の1行を追加しました。

android:id="@+id/top_title"

全体を記述すると下のようになります。

<activity_main.xml>

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/top_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_name"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.534"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.05" />

</androidx.constraintlayout.widget.ConstraintLayout>

<strings.xml>

<resources>
    <string name="app_name">目標達成アプリ</string>
</resources>

これを実行した結果、下のように表示されました。

テキストのサイズ変更

したのようなプロパティ(属性)を設定することで、テキストのサイズを変更できました。

        android:layout_width="match_parent"
        android:layout_height="100dp"

そして、自動サイズ調整はしたのようにやるみたいです。

        android:autoSizeTextType="uniform"
        android:autoSizeMinTextSize="12sp"
        android:autoSizeMaxTextSize="100sp"

これを記述するとAndroid APIのレベル別のactivity_main.xmlが作成されるようです。
※Android Studioで「Override XXX」と表示されるのでそれをクリックしました。

最終的に、下のようなファイルが作成されました。

イメージの追加

説明を文言でするよりも、動画の方がよいと思いました。

サポートライブラリ追加

ここで、エラーが発生しています。これは、AppCompatというサポートライブラリが無いために起きているエラーです。
これをインストールするためには、Java11が必要になります。

JDK11インストール

WidnowsでのAndroid Studioが起動できなかったので、現在はMacで作業をしていますので、home brewを使用してインストールしました。参考サイトはこちらです。

下のコマンドでインストールできました。

brew install java11

しかし、次のようなエラーが出ました。

Invalid Gradle JDK configuration found

これは、プロジェクト構成(Project Structure) -> SDKLocation -> JDKの設定でJava11tを設定し、改めてビルドしたらなおりました。

Android Studioアップグレード

そして、初めのイメージを設定する部分ですが、次のようなエラーが出ました

Sets a drawable as the content of this ImageView. Allows the use of vector drawable when running on older versions of the platform.

これは、Android Studioをアップデートしてください。というものでした。。。

こちらのサイトを参考にAndroid Studioは下のようにConfigure -> Check for Updateを選択します。

しかし、選択肢が「Download」しかなかったので、結局新しいものをダウンロード、インストールすることになりました。
最終的に置き換えるという形で、アップグレードしました。

まだ、エラーが解消されません。。。

This view is not constrained. It only has designtime positions, so it will jump to (0,0) at runtime unless you add the constraints

ここのサイトを参考にすると設定を追加すると治るということなので、次のようにプロパティを追加しました。

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/ic_add_mokuhyo"
        tools:layout_editor_absoluteX="50dp"
        tools:layout_editor_absoluteY="222dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="VectorDrawableCompat" />

追加したのは、次の部分です。

        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"

そして、英語、日本語と多言語化した場合は、下の3つのファイルに文字列を追加する必要がありました。

  • strings.xml(default)
  • strings.xml(ja)
  • strings.xml(en)

イメージのレイアウト

結構手こずりました。下のサイトを参考に学習しました。

  1. Image Asset Studioを使用する

色々と試したけど結局実行するデバイスを>色々と試したけど結局実行するデバイスを 新しくして試すことにしました。。。

どうやらしているする属性が違うようでした。こちらのサイトでありました。

 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <ImageView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:src="@drawable/my_image"
         android:contentDescription="@string/my_image_description"
         />
 </LinearLayout>
 android:src="@drawable/my_image"

この部分が自動生成したものと値が違う。。。

そして、レイアウトの設定も問題がありそうなので、本家のサイトを参考にレイアウトも学習します。

何かしら触っていると、わかってくるような感じで説明がうまくできないのですが、 各値を設定してやると、見た目も変更されるのでそれで、自分の思った通りに修正するのが、早いと思います。

画像が表示されいない問題

これの原因がわかりました。SDKのバージョン別にactivity_main.xmlが存在していました。

これが原因で、一向に画像が表示されなかった。。。というわけでした。。。

最終的に作成したactivity_main.xmlは下のようなものです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TableRow
            android:layout_width="178dp"
            android:layout_height="192dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <ImageView
                    android:id="@+id/imageView3"
                    android:layout_width="80dp"
                    android:layout_height="80dp"
                    android:layout_marginStart="40dp"
                    android:layout_marginTop="10dp"
                    android:contentDescription="目標追加・一覧"
                    android:src="@drawable/ic_add_mokuhyo"
                    android:visibility="visible"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
                    tools:srcCompat="@drawable/ic_add_mokuhyo" />

                <TextView
                    android:id="@+id/textAddMokuhyo"
                    android:layout_width="180dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="10dp"
                    android:layout_marginTop="10dp"
                    android:autoSizeMaxTextSize="40dp"
                    android:autoSizeMinTextSize="18dp"
                    android:text="目標追加・一覧"
                    android:textSize="20dp"
                    android:visibility="visible"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/imageView3" />
            </LinearLayout>

        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"></LinearLayout>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"></LinearLayout>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"></LinearLayout>
        </TableRow>

    </TableLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

LinerLayoutを学ぶ

よく使用されるであろうレイアウトの1つとして「LinerLayout」があります。ドキュメントにも載っているので基本的なレイアウトなのであろうと思いこれを学ぶことにしました。

LinearLayout は、すべての子を垂直または水平の一方向に揃えるビューグループです。android\:orientation 属性でレイアウトの方向を指定できます。

とりあえずは、コンポーネント(ボタンなどの部品)を並べてみます。そして、プロパティ(android\:XXXX)の値を変えてどのような配置になるかみてみたいと思います。

ここで、着目するプロパティを以下に示します。

プロパティ名 内容 
android\:orientation レイアウトの方向を指定
android\:layout_weight 画面上で占めるスペースという観点で「重要度」を表す値をビューに指定します。この weight 値が大きいほど、親ビューの残りのスペースを埋めるように拡大されます。子ビューに weight 値を指定することで、ビューグループの残りのスペースを、宣言された weight の割合に応じて割り当てることができます。デフォルトの weight 値は 0 です。
android\:layout_height
android
:layout_width
android\:layout_weight
それぞれの値を0dp、0dp、1に設定することで均等配分することができます。

ちょっと試してみましたが、他のプリパティと組み合わせる必要があるので、色々やってみないと理解できません。

次は、合わせて出てきた、TextViewに関してもプロパティを見てみます。

感想

  1. レイアウトの使い方、センタリングなど、やり方を調べると色々出てくるので手が止まることは少なくなりそうだ。
  2. レイアウトマネージャー(動画にある画面)での操作はわかりやすいが、プロパティの場所を探すのが大変だった。

でわでわ。。。

Android App 作成 ~アプリの基礎を学ぶ~

Androidアプリの基礎

下のページにあるチュートリアルを行っていきます。
参考サイト:Android Developperのサイトです。

そして、Androidアプリを作成するうえで理解しておきたいのが、下のようなライフサイクルと呼ばれるものです。
LifeCycle

具体的には、「アクティビティのライフサイクル」ということです。

「初めてのアプリを作成する」という項目は飛ばします。以前やったので。。。

概要をつかむ

Androidアプリを作成するための材料(コンポーネント)として以下のものがあります。

  • アクティビティ
  • サービス
  • ブロードキャスト レシーバ
  • コンテンツ プロバイダ

つまりは、上のような「グループに分けられたクラスがありますよ」ということです。

これらのクラスがそれぞれ、次のように説明されています。

アクティビティ
アクティビティは、ユーザーとやり取りするためのエントリ ポイントです。これは、1 つのユーザー インターフェースを持つ 1 つの画面で表されます。たとえば、メールアプリには、新着メールの一覧を表示するアクティビティ、メールを作成するアクティビティ、そしてメールを閲覧するアクティビティがあります。メールアプリでは、これらの複数のアクティビティが一体となって 1 つのユーザー エクスペリエンスを形成しますが、それぞれのアクティビティは他のものから独立しています。したがって、これらのアクティビティのいずれかを、別のアプリから開始することができます(メールアプリが許可している場合)。たとえば、カメラアプリからメールアプリの新規メールを作成するアクティビティを開始できます。そのようにして、ユーザーが写真をメールで共有できるようにします。アクティビティは、システムとアプリ間における次の重要なインタラクションを行えるようにします。
アクティビティをホストしているプロセスを継続的に実行するために、ユーザーの現在の操作内容(画面の表示)を追跡。
以前に使用されたプロセス(停止されたアクティビティ)のうち、ユーザーが再度アクセスする可能性があるものを検知し、それらの優先順位を上げてプロセスを維持。
アプリがプロセスを強制終了した場合に、ユーザーが以前の状態を復元したアクティビティに戻れるように支援。
アプリ間でのユーザーフローをアプリが実装する手段と、システムがそれらのフローを連携させるための手段を提供(ここでは最も一般的な例を説明します)。
アクティビティは Activity クラスのサブクラスとして実装します。Activity クラスの詳細については、デベロッパー ガイドのアクティビティをご覧ください。

サービス
サービスは、さまざまな理由によりアプリをバックグラウンドで実行し続けるための汎用エントリ ポイントです。長期間の操作やリモート プロセスを処理するためにバックグラウンドで実行されるコンポーネントです。サービスにはユーザー インターフェースがありません。たとえば、サービスはユーザーが別のアプリを使用している間にバックグラウンドで音楽を再生したり、ユーザーが別のアクティビティを操作している間にそれを妨げることなくネットワークからデータを取得したりします。アクティビティなどの他のコンポーネントが、サービスを開始して実行したり、サービスとやり取りするためにサービスにバインドしたりすることができます。アプリの管理方法についてサービスがシステムに通知するセマンティクスは 2 つあり、それぞれ異なる意味を持っています。開始されたサービスが、作業が終了するまで自身の実行を維持するようシステムに指示します。これにより、ユーザーがアプリから離れても、バックグラウンドでデータを同期したり、音楽を再生したりできます。バックグラウンドでのデータの同期や音楽の再生は、開始されたサービスとしてはそれぞれ異なるタイプと認識され、システムがそれらのサービスに対して行う処理もそれぞれ異なります。
音楽の再生はユーザーが直接意識しているものなので、アプリはフォアグラウンドになりたいことをユーザーに通知で知らせることで、システムにそれを指示します。この場合、システムはそのサービス プロセスの実行を維持するよう最善を尽くします。このプロセスが終了するとユーザーが不満を覚えるからです。
通常のバックグラウンド サービスは、その実行をユーザーが直接意識していません。したがって、システムはより柔軟にそのプロセスを管理できます。ユーザーにとってより緊急な課題を処理するために RAM が必要となった場合には、プロセスを強制終了し、後から再開できます。
他のアプリ(またはシステム)がサービスを利用することを明示しているなら、バインドされたサービスが実行されます。これは基本的に、別のプロセスに API を提供するサービスです。これにより、これらのプロセス間に依存関係があることをシステムは認識します。プロセス A がプロセス B のサービスにバインドされている場合に、システムはプロセス B とそのサービスの実行を A のために維持する必要があることを認識します。さらに、プロセス A がユーザーにとって優先度が高い場合は、プロセス B もユーザーにとって重要であるとみなされます。サービスは良くも悪くもその柔軟性から、さまざまな上位レベルのシステム コンセプトにおいて、非常に有用な構成要素となってきました。ライブ壁紙、通知リスナー、スクリーン セーバー、入力方法、ユーザー補助機能サービス、その他多くの主要なシステム機能はすべて、アプリが実行するサービスとしてビルドされ、その実行時にシステムによりバインドされます。
サービスは Service のサブクラスとして実装されます。Service クラスの詳細については、デベロッパー ガイドのサービスをご覧ください。

ブロードキャスト レシーバ
ブロードキャスト レシーバは、通常のユーザーフローを外れて、システムがアプリにイベントを配信できるようにするコンポーネントです。これにより、アプリはシステム全体のブロードキャスト アナウンスに応答できます。ブロードキャスト レシーバは明確に定義されたアプリへのエントリであるため、システムは実行中でないアプリに対してもブロードキャストを配信できます。したがってアプリが、たとえば、近づいているイベントについてユーザーに知らせる通知を投稿するためのアラームをスケジューリングすることができます。アプリの BroadcastReceiver にアラームを配信することにより、そのアラームが作動するまでアプリの実行を維持する必要がなくなります。多くのブロードキャストの発信源はシステムです — たとえば、画面がオフになったことを通知するブロードキャスト、電池が残り少ないことを通知するブロードキャスト、画像がキャプチャされたことを通知するブロードキャストなどです。アプリから発信されるブロードキャストもあります — たとえば、端末にデータがダウンロードされ使用できることを他のアプリに知らせる場合などです。ブロードキャスト レシーバがユーザー インターフェースを表示することはありませんが、ステータスバー通知を作成して、ブロードキャスト イベントの発生時にユーザーにアラートできます。一般的には、ブロードキャスト レシーバは他のコンポーネントへの単なるゲートウェイであり、最小限の作業を行うことが前提となっています。たとえば、JobScheduler を使用してイベントに基づいた何らかの作業を実行する JobService をスケジュール設定する場合などです。
ブロードキャスト レシーバは BroadcastReceiver のサブクラスとして実装され、各ブロードキャストは Intent オブジェクトとして配信されます。詳細については、BroadcastReceiver クラスをご覧ください。

コンテンツプロバイダ
コンテンツ プロバイダは、ファイル システム、SQLite データベース、ウェブ、またはアプリからアクセス可能な他の永続的なストレージの場所に保存できるアプリデータの共有されている部分を管理します。コンテンツ プロバイダを介して、他のアプリがデータをクエリしたり、修正したりできます(コンテンツ プロバイダが許可している場合)。たとえば、Android システムはユーザーの連絡先情報を管理するコンテンツ プロバイダを提供しています。したがって、適切なパーミッションさえあれば、アプリからコンテンツ プロバイダに ContactsContract.Data などをクエリして、特定の人物に関する情報を読み取ったり書き込んだりできます。このような一般的なケースのために多くの API やサポートが組み込まれているため、コンテンツ プロバイダをデータベースの抽象化として考えたくなるかもしれません。しかし、システム設計の観点では、コンテンツ プロバイダには別の目的があります。システムから見て、コンテンツ プロバイダは、URI スキームにより識別される名前付きデータ項目を公開するための、アプリへのエントリ ポイントです。したがって、アプリは自身が保有するデータを URI 名前空間にどのようにマッピングし、その URI を他のエンティティに渡してデータにアクセスできるようにするかを決めることができます。アプリの管理に関して、システムでは以下が実行できます。
URI の割り当てはアプリが実行中であるかどうかには影響されないため、その URI を所有しているアプリが終了していても URI は維持されます。システムが確認する必要があるのは、該当 URI からアプリのデータを取得する必要があるときに、所有しているアプリが実行中であることだけです。
URI は重要で詳細なセキュリティ モデルも提供します。たとえば、アプリは画像の URI をクリップボードに配置することができますが、他のアプリが自由にアクセスできないようにコンテンツ プロバイダをロックしたままにできます。別のアプリがクリップボードのその URI にアクセスしようとした場合には、システムが一時的な URI パーミッションを付与して、その URI にあるデータのみにアクセスすることを許可します。その他のデータにはアクセスできません。
コンテンツ プロバイダは、アプリだけに公開されている、他で共有されていないデータを閲覧したり書き込んだりする場合にも役立ちます。
コンテンツ プロバイダは ContentProvider のサブクラスとして実装され、他のアプリがトランザクションを実行できるようにする API の標準セットを実装する必要があります。詳細については、デベロッパー ガイドのコンテンツ プロバイダをご覧ください。

これらを使って作る

上記のコンポーネント(部品)をしようしてAndroidアプリを作成します。この基礎になる部分をちゃんと理解すればほぼ極めたといってよいと思います。※勝手な想像です。なぜなら自分もこれから学習するので(笑)

なので、この基礎部分をしっかり学習したいと思います。

「概要をつかむ」のまとめ

Androidアプリを作るための基本的な材料(クラス)として次の4つがある。

  • アクティビティ
  • サービス
  • ブロードキャスト レシーバ
  • コンテンツ プロバイダ

そして、これらの材料を組み合わせてAndroidアプリを作成するということを理解。

次は、順番に「アクティビティ」を理解します。

アクティビティについて

アクティビティについて学習します。まずは、概要にある内容を抜粋を読みます。

main() メソッドで起動するアプリをプログラミングする際の枠組みとは異なり、Android システムでは、ライフサイクルのそれぞれの段階に対応するコールバック メソッドを呼び出すことにより、Activity インスタンス内のコードが開始されます。

つまるところは、以下の文言がキーポイントになります。

一般に、1 つのアクティビティがアプリ内の 1 つの画面を実装します。

例として、次のような説明があります。

アプリのアクティビティの 1 つが「設定」画面を実装し、他のアクティビティが「写真を選択」画面を実装します。

画面=アクティビティであり、このアクティビティの操作により様々な操作(処理)を実装するというわけです。

マニフェストで画面の設定

アプリでアクティビティを使用できるようにするには、マニフェストでアクティビティとその属性を宣言する必要があります。

    <manifest ... >
      <application ... >
          <activity android:name=".ExampleActivity" />
          ...
      </application ... >
      ...
    </manifest >

必須の属性は、アクティビティのクラス名を指定する android\:name だけです。

簡単ですね。つまりわかりやすい=使いやすいということだと思います。
続けて、そのほかの設定に関しては、次の部分にあります。

ラベル、アイコン、UI テーマなどのアクティビティの特性を定義する属性を追加することもできます。

こちらのページにアクティビティの詳細がありました。

構文は以下のようになっています。

<activity android:allowEmbedded=["true" | "false"]
          android:allowTaskReparenting=["true" | "false"]
          android:alwaysRetainTaskState=["true" | "false"]
          android:autoRemoveFromRecents=["true" | "false"]
          android:banner="drawable resource"
          android:clearTaskOnLaunch=["true" | "false"]
          android:colorMode=[ "hdr" | "wideColorGamut"]
          android:configChanges=["mcc", "mnc", "locale",
                                 "touchscreen", "keyboard", "keyboardHidden",
                                 "navigation", "screenLayout", "fontScale",
                                 "uiMode", "orientation", "density",
                                 "screenSize", "smallestScreenSize"]
          android:directBootAware=["true" | "false"]
          android:documentLaunchMode=["intoExisting" | "always" |
                                  "none" | "never"]
          android:enabled=["true" | "false"]
          android:excludeFromRecents=["true" | "false"]
          android:exported=["true" | "false"]
          android:finishOnTaskLaunch=["true" | "false"]
          android:hardwareAccelerated=["true" | "false"]
          android:icon="drawable resource"
          android:immersive=["true" | "false"]
          android:label="string resource"
          android:launchMode=["standard" | "singleTop" |
                              "singleTask" | "singleInstance"]
          android:lockTaskMode=["normal" | "never" |
                              "if_whitelisted" | "always"]
          android:maxRecents="integer"
          android:maxAspectRatio="float"
          android:multiprocess=["true" | "false"]
          android:name="string"
          android:noHistory=["true" | "false"]  
          android:parentActivityName="string" 
          android:persistableMode=["persistRootOnly" | 
                                   "persistAcrossReboots" | "persistNever"]
          android:permission="string"
          android:process="string"
          android:relinquishTaskIdentity=["true" | "false"]
          android:resizeableActivity=["true" | "false"]
          android:screenOrientation=["unspecified" | "behind" |
                                     "landscape" | "portrait" |
                                     "reverseLandscape" | "reversePortrait" |
                                     "sensorLandscape" | "sensorPortrait" |
                                     "userLandscape" | "userPortrait" |
                                     "sensor" | "fullSensor" | "nosensor" |
                                     "user" | "fullUser" | "locked"]
          android:showForAllUsers=["true" | "false"]
          android:stateNotNeeded=["true" | "false"]
          android:supportsPictureInPicture=["true" | "false"]
          android:taskAffinity="string"
          android:theme="resource or theme"
          android:uiOptions=["none" | "splitActionBarWhenNarrow"]
          android:windowSoftInputMode=["stateUnspecified",
                                       "stateUnchanged", "stateHidden",
                                       "stateAlwaysHidden", "stateVisible",
                                       "stateAlwaysVisible", "adjustUnspecified",
                                       "adjustResize", "adjustPan"] >
    . . .
</activity>
属性 説明
android\:allowEmbedded アクティビティを別のアクティビティの子として埋め込み、起動できることを示します。これは特に、別のアクティビティが所有するディスプレイなどのコンテナに子が存在する場合です。たとえば、Wear のカスタム通知に使用されるアクティビティは、Wear が別のプロセスに存在するコンテキスト ストリーム内でこのアクティビティを表示できるようにするため、この宣言が必要です。この属性のデフォルト値は false です。
android\:allowTaskReparenting タスクが次に前面に移動したとき、アクティビティを開始したタスクからアフィニティを持つタスクにアクティビティを移動できるかどうか。移動できる場合は "true"、アクティビティを開始したタスクに留まる必要がある場合は "false" を指定します。この属性が設定されていない場合、対応する 要素の allowTaskReparenting 属性によって設定された値がアクティビティに適用されます。デフォルト値は "false" です。通常、アクティビティを開始すると、アクティビティを開始したタスクにアクティビティが関連付けられ、アクティビティの生存期間中はそこに留まります。この属性を使用すると、現在のタスクが表示されなくなったときに、アフィニティを持つタスクをアクティビティの親として再割り当てすることができます。一般に、アプリに関連付けられたメインタスクにアプリのアクティビティを移動する場合に使用します。たとえば、メール メッセージにウェブページへのリンクが含まれている場合、リンクをクリックすると、そのウェブページを表示できるアクティビティが起動します。そのアクティビティはブラウザアプリで定義されていますが、メールタスクの一部として起動されます。ブラウザタスクをアクティビティの親として再割り当てすると、ブラウザが次に前面に移動したときにアクティビティが表示され、メールタスクが再び前面に移動したときにはそのアクティビティが表示されなくなります。アクティビティのアフィニティは、taskAffinity 属性で定義されます。タスクのアフィニティは、タスクのルート アクティビティのアフィニティを読み取って決定されます。したがって、定義上は、ルート アクティビティは常に、同じアフィニティを持つタスク内に存在します。起動モードに "singleTask" または "singleInstance" が設定されたアクティビティはタスクのルートにのみ存在できるため、親の再割り当ては "standard" モードと "singleTop" モードに限定されます。(launchMode 属性もご覧ください。)
android\:alwaysRetainTaskState アクティビティが割り当てられているタスクの状態を常にシステムで維持するかどうか。維持する場合は "true"、特定の状況でシステムがタスクを初期状態にリセットできるようにするには "false" を指定します。デフォルト値は "false" です。この属性が重要なのは、タスクのルート アクティビティのみです。その他すべてのアクティビティについては無視されます。通常は、ユーザーがホーム画面からタスクを選択し直したような状況で、システムはタスクをクリアします(ルート アクティビティ上のスタックからすべてのアクティビティを削除します)。一般に、ユーザーが特定の時間(たとえば 30 分間)、タスクにアクセスしなかった場合にこの処理が行われます。ただし、この属性が "true" の場合、ユーザーがどのような方法でタスクにアクセスしても、常に前回のタスクの状態に戻ります。これは、たとえば、ウェブブラウザのようなアプリで便利です。これにより、ユーザーは多くの状態(開いている複数のタブなど)をそのまま維持することができます。
android\:autoRemoveFromRecents この属性を指定したアクティビティによって開始されたタスクについて、タスク内の最後のアクティビティが完了するまでオーバービュー画面に表示し続けるかどうか。true の場合、タスクが自動的にオーバービュー画面から削除されます。これは、呼び出し元による FLAG_ACTIVITY_RETAIN_IN_RECENTS の使用よりも優先されます。"true" または "false" のブール値を指定する必要があります。
android\:banner 関連するアイテムに拡張されたグラフィック バナーを提供するドローアブル リソース。 タグで使用すると、特定のアクティビティにデフォルトのバナーを配置します。 タグで使用すると、アプリのすべてのアクティビティにバナーを配置します。Android TV のホーム画面では、システムがこのバナーを使用してアプリを表示します。バナーはホーム画面のみに表示されるため、CATEGORY_LEANBACK_LAUNCHER インテントを処理するアクティビティを持つアプリでのみ指定する必要があります。この属性は、画像を含むドローアブル リソースへの参照(たとえば "@drawable/banner")として設定する必要があります。デフォルトのバナーはありません。詳細については、TV アプリのビルドを開始するページのホーム スクリーンにバナーを配置するをご覧ください
android\:clearTaskOnLaunch タスクがホーム画面から再起動されたときに、ルート アクティビティを除くすべてのアクティビティをタスクから削除するかどうか。常にタスクのルート アクティビティ以外をクリアする場合は "true"、そうでない場合は "false" を指定します。デフォルト値は "false" です。この属性は、新しいタスクを開始するアクティビティ(ルート アクティビティ)にのみ影響します。タスクのその他すべてのアクティビティでは、この属性は無視されます。値が "true" の場合は、タスクの直前の操作や、タスクから離れるときに [戻る] ボタンまたは [ホーム] ボタンを押したかどうかにかかわらず、ユーザーが再びタスクを開始するたびにルート アクティビティから開始します。値が "false" の場合、状況によってはタスクのアクティビティがクリアされることがありますが、常にクリアされるわけではありません(alwaysRetainTaskState 属性をご覧ください)。たとえば、ホーム画面からアクティビティ P を起動し、そこからアクティビティ Q に移ったとします。ユーザーが [ホーム] を押して、再びアクティビティ P に戻りました。通常、ユーザーにはアクティビティ Q が表示されます。前回、P のタスクで Q を実行していたからです。ただし、P でこのフラグが "true" に設定されていると、P をベースにするすべてのアクティビティ(この場合は Q)は、ユーザーが [ホーム] を押したときに削除され、タスクはバックグラウンドに移動します。そのため、ユーザーがタスクに戻ると、P のみが表示されます。この属性と allowTaskReparenting の両方が "true" の場合、親の再割り当てが可能なアクティビティはすべて、アフィニティを共有するタスクに移動され、残りのアクティビティは上記のとおり削除されます。
android\:colorMode 対応端末でアクティビティを広色域モードで表示するようにリクエストします。広色域モードでは、SRGB よりも広い色域でウィンドウをレンダリングして、より鮮やかな色を表示できます。端末で広色域レンダリングがサポートされていない場合、この属性を指定しても効果はありません。ワイドカラー モードでのレンダリングの詳細については、ワイドカラー コンテンツによるグラフィックの拡張をご覧ください。
android\:configChanges アクティビティで処理する設定変更のリストを指定します。実行時に設定の変更が発生すると、デフォルトではアクティビティはシャットダウンおよび再起動されますが、この属性で設定を宣言しておくと、アクティビティの再起動を防止できます。代わりに、アクティビティは実行中のままになり、アクティビティの onConfigurationChanged() メソッドが呼び出されます。

ほかにもありますが、詳細は本家のページを参照ください。

このマニフェストで、各アクティビティの設定を行います。

次回は、画面(アクティビティ)から画面(アクティビティ)を呼び出す方法を学習します。>次回は、画面(アクティビティ)から画面(アクティビティ)を呼び出す方法を学習します。

アクティビティの作成

AndroidStudioでプロジェクトを作成すると、「MainActivity」クラスが出来上がっています。
このクラスが上記のアクティビティになります。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

このアクティビティ(画面)を作成しているのは、
setContentView(R.layout.activity_main);の部分になります。
R.layout.activity_mainの部分は、下の図のように、「activity_main.xml」に定義(描かれて)されています。

「activity_main.xml」を開くと右上のほうにのようなボタンがあるので、「Design」を選択すると画面のイメージが表示されます。

ここで、テキスト「Hello World」が表示されています。この部品が「TextView」という部品です。
この部品を移動してタイトルとして使用しようと思っていますが、その前に。。。

パッケージの修正

デフォルトで作成されたパッケージは「com.example.プロジェクト名」になっていると思います。
これを自作のパッケージ名に修正したいと思いますが、ただ単に修正するとエラーになります。

これは、AndroidManifest.xmlの修正も必要なためです。
使用するパッケージ名を上のXMLに記述します。今回作成するパッケージ名は「jp.zenryoku.mokuhyotasseiap」としてます。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.zenryoku.mokuhyotasseiap">

これで、エラーが解消され、Javaファイルも下のようにエラーが消えました。

多言語対応

作成するアプリは、多言語対応で作成したいと考えております。なので、こちらのページを参考に、多言語化を行います。

まとめると、下の図のように各言語に対応するXMLファイルを作成するというところです。

resを右クリックして、フォルダ名「values」の後ろに対応する言語を付けます。

  • 日本語(values-ja)
  • 英語(values-en)

そして、それぞれのstring.xmlに各言語に対応する文字列を記述します。

XMLの参照方法

多言語の文字列を設定することができたら、次は画面のコンポーネント(部品)にその値を設定します。
必要な処理ととしては、次のものになります。

  1. コンポーネントの取得
  2. 文字列の取得
  3. 値の設定
// コンポーネントの取得
TextView titleView = findViewById(R.id.appTitle);
// 値の設定
titleView.setText(R.string.app_name);

上のような実装で行けそうです。まだ起動確認していません。。。

バーチャルデバイスのインストール

実機がない場合は仮想デバイス(バーチャルデバイス)をインストールする必要があります。
動かしたときのイメージを見るためです。
これは、画面操作で簡単に行けます。

しかしエラーが!

Execution failed for task ':app:compressDebugAssets'.

上記のようなエラーが出ました。
こちらのサイトを参考にすると、Gradleのバージョンを下げてやればいけるというところで。。。

それぞれのバージョンを以下のように設定して再実行しました。
Android Gradle Plugin: 4.2.1
Gradle: 6.7.1

次のエラー

Caused by: org.gradle.internal.metaobject.AbstractDynamicObject$CustomMessageMissingMethodException: Could not find method dependencyResolutionManagement() for arguments

これもGradleのバージョンを次のように変えてやればよい感じでした。
Android Gradle Plugin: 7.0
Gradle: 7.0

しかし、使用しているPCの容量不足のため、AndroidStudioが落ちました。。。
Android開発は、十分にHDの要領があるPCを使用しないと駄目なようです。。。

でわでわ。。。

Android Studio App 作成1 〜プロジェクト作成〜

Android Studioの使い

Androidアプリを作成したいと思う人は多いと思います。自分もその一人です。
しかし、どこから着手してよいかわからないということが往々にしてありますので、まとめてみました。

  1. 開発ツールをインストール(Android Studioのインストール)

  2. プロジェクトの作成: Android Studioに作成するアプリのフォルダを作成する

  3. 仕様の作成: どんな操作で、どんな機能を持っているアプリなのか明確にする、資料を作る

  4. 画面イメージの作成(モック): 作成するアプリで使用する画面を機能の作成をしない状態で作成する

  5. 機能のテストケース作成: 各画面の操作を行ったときにどのような動きをするのが正しいか、リストアップする
    ※「〇〇〇と操作したときはXXXのように表示」のように「このように動く」ということをリストアップする

  6. 機能実装用テストケース作成: 5で作成したテストケースを確認するためのプログラムを作成します。これを作成しておくと、部分的に修正したいときに、ほかに影響がないことを確認するということが、自動で出来るようになります。

  7. 各種部品の実装とテスト: 6で作成したテストケース(プログラム)が全てOKになるようにプログラムを作成します。テストケースを網羅するので、実装とテストが一緒にできます。

プロジェクトの作成

Android Studioを使用して、Androidアプリを作成しようと思います。

上記と重複してしまいますが、全体的に下のような手順で作成していきます。

  1. プロジェクトの作成
  2. 仕様の作成
  3. 画面イメージの作成(モック)
  4. 機能のテストケース作成
  5. 機能実装用テストケース作成
  6. 各種部品の実装とテスト

上記のような形で実装していきます。

(余談)目標達成AP~タスク管理~

Gitに作成した仕様が本来のものですが、これを作成するのにはかなりの時間がかかるので、分解して実装していきます。
最終的には、人工知能処理も必要になるので、この部分は最後の方に回したいと思っています。

なので、Version1.0として、目標達成AP〜タスク管理〜バージョンとしてアプリの作成に着手しようと言う考えです。

ちなみに、この記事を書いてから大分時間が経っていますが、ようやく着手できるところまで来ました。

画面の作成に着手

初めに、Android Studioを起動すると下のような画面が見れます。

次へ(next)を押下すると、インストールタイプの選択がありますが、これはStandard(標準)でよいと思います。
※使い慣れていないんで。。。

そして画面のタイプを選択します。白系か黒系か。。。自分は白系を選択しました。

最後は、そのままFinish(完了)です。

すると、必要なライブラリなどをインストールし始めます。

結構長いです。。。ネットワークスピードが遅い環境なもので。。。

Android Stdioでプロジェクトの作成

Android Stdioを起動します。

そして、create new projectを選択します。

Emptyプロジェクトを選択します。


最後に、プロジェクト名としようする言語などを入力してFinishをクリックします。

そして、下のようにワークスペースが開きます。

アプリケーションを動かす

とりあえずは、動かしてみるのも良いでしょう。自動生成されたアプリを起動して動きを見て、どのような構造になっているかプログラムを眺めてみるのも1つです。こちらに詳細を記載しました。

今後

自分は、これからアプリケーションの実装に着手したいと考えております、が、Android Studioの使い方など学習することが多く時間がかかりそうです。。。

やはり、設計レベルである程度、形を作っておくと実装も楽になります。

そして、AndroidはGoogle製のOSなので当然TensorFlow(人工知能ライブラリ)などを併用することが可能です。ほかにも、いろいろなデバイスにインストールすることができるので作成したアプリをいろんなデバイスで実行することが可能になります。

Activityが表示できない

下の画像のように、文字が赤くなっている部分があるときは、エラーになっている状態です。

これを解決するのに、足りないものがあります。これらを追加するのに「Gradle sync failed:」というエラーメッセージがあります。これを解決するのにこちらのサイトを参考にしました。

エラーが出たとき

下のようなエラーが出ました。

Caused by: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:

File → Project Structure(プロジェクトの構造)からProject(プロジェクト)を開き
解決できるかと思って下のようにAndroidGradle PluginとGradleのバージョンをそそろえてみたがダメでした。
しかし、Gradleのバージョン < AndroidGradle Pluginの関係を保ち、変更してやるとGradleがちゃんと走りました。
結局は、AndroidGradle Pluginのバージョンをそのままにして、Gradleのバージョンを下げていったらうまくいきました。。。が必要なパッケージをダウンロードしただけでした。。。

こちらのサイトを参考にするとJDKのバージョンとGradleのバージョンがあっていないということで、下のように修正しました。

またエラーが!

下のようなエラーが出ました。

The cache entry for initialization script 'C:\Users\XXX\AppData\Local\Temp\sync.studio.tooling278.gradle' appears to be corrupted.

  • Try:
    Run with --scan to get full insights

これは、chacheする場所を新たに指定したらなおりました。

The cache entry for initialization script 'C:\Users\XXX\AppData\Local\Temp\sync.studio.tooling472.gradle' appears to be corrupted.

これは、AppDataの場所を変更していたので、出たエラーと思われます。
こちらのサイトを参考に修正しました。
Files -> Settingsから

ほかにも

The cache entry for initialization script 'C:\Users\XXXX\AppData\Local\Temp\sync.studio.tooling336.gradle' appears to be corrupted.

というようなエラーがでました。

とりあえず不足していたのが、環境変数の設定でした。こちらのページに詳細があります。

これは、普通にWindowsの設定を開き環境変数を設定してやればOKです。
ただし、再起動が必要らしいです。

そして、gradleのディレクトリがCドライブになっていたのでそれをDドライブに変更します。
※筆者は、DドライブにAndroidStudioをインストールしました。。。

Gradle

File > Settings > Build Execution, Deployment > Gradle > Service directory path

の順でクリックります。しかし、Gradleがあったのは別の場所で、gradleホームをすでにDドライブに設定していました。

仕方ないので、コマンドでビルドを実行することにしました。画面の下のほうにある「Terminal」をクリックすると、コマンドラインの画面が見れますので、ここに以下のコマンドを入力します。

gradlew --info build

エラーログを見ると。。。

Initialized native services in: C:\Users\XXX.gradle\native

のようなメッセージがあり、つまりはワーキングディレクトリがCドライブになっているのが原因と判断しました。
だがしかし、結局解決ならず。。。

ここのページを参考にするとGradleキャッシュをクリアするとよいとあったので、キャッシュフォルダを削除して、今日はおしまいにしようと思います。。。
[ー2021-10-18]

Cドライブのキャッシュを削除してみたものの、エラーログに変化なし。。。

やはり、WindowsでAndroidStudioを使用するならば、デフォルトの場所にインストールするのが良いという判断になりました。

もともとインストールしていたAndroid Studioはアンインストールして、再度やり直すことにしました。
このパソコンは、Android開発用の端末となりました。※CドライブのHD容量が30GBしかないのです。。。

再インストール後

キャッシュが残っていました。
下のようなエラーが出ています。

Minimum supported Gradle version is 7.0.2. Current version is 6.9. If using the gradle wrapper, try editing the distributionUrl in

初めに、プロジェクトのディレクトリにあるbuid.gradleに追記した。

baseDir=D:/workspace

という文字を削除しました。これで、もとのCドライブを参照するようになります。

エラーメッセージ解決

こちらのGradleのサイトでエラーメッセージを検索すると解決策が見つけられそうです。※英語ですが。。。

結局、GradleとAndroid Gradle Pluginのバージョンを合わせて下げてやりました。
Android Gradle Plugin = 4.2.2
Gradle Version = 6.8
上記の設定で何とかビルド完了できました。

でわでわ。。。

Android Studio Tesseractの設定とエラー compileSdkVersion is not specified.

調査するときのポイント

実際に自分が調べていくときに「使えるな!」と思った方法です。

  1. エラーメッセージをコピって検索する
  2. エラーメッセージを翻訳して日本語にする

色々なサイト、ブログ記事を追いかけるように、みて、試して問題解決に向けて調べていきます。

しかし、途中で「根本的な仕組みを理解したほうが早い」場合もあるので、頭の片隅にそれを置いておくと良いかもしれません。

compileSdkVersion is not specified.

上記のようなエラーメッセージが出て、ビルドができない状態になりました。

調べてみるとこちらのページに下のような記述がありました。

プロジェクト下の build.gradle に以下を記載していますか?

allprojects {
repositories {
mavenLocal()
jcenter()
maven {
url "$rootDir/../node_modules/react-native/android"
}
google()
}
}

これは、プロジェクトのbuild.gradleを修正すると言うことを意味しているので下の図の「AndroidOpenCV」と書かれているものを修正しました。

最終的に下のようになりました。

allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url 'https://maven.google.com/'
            name 'Google'
        }
    }
}

Could not find method maven() for arguments

次に出力されたエラーメッセージは上のものです。

mavenのホームディレクトリ(インストールした場所)を指定しなくてはいけないけど、全く違う場所を指定しているので出力されたエラーです。

Mavenプラグインを使用

参考サイトはこちらのAndroidドキュメントです。

しかし、これは必要ないと判断しました。

maven {
    url 'https://maven.google.com/'
    name 'Google'
}

そして、初めのエラー対応を考えます。

ふと気がつく

「compileSdkVersion is not specified.」このメッセージは日本語にすると「compileSdkVersionが指定されていません。」になります。

ここを中心に調べていくとGradleのページに着きました。
しかし、有益な情報は見つからず。。。

プロジェクトの設定を見てみる

ヘッダーメニューからFile -> Project Structureを選択

Gradleのバージョンを6.5に設定しました。

Could not find com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2.

次に上のエラーが出ました。
こちらのページには次のような追記をしてください。とあったのでそのようにしました。

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5"
  }
}

apply plugin: "com.jfrog.bintray"

Could not find org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32.

さらに次のエラーが出ました。

ERROR: Could not find org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32.
Searched in the following locations:
  - https://dl.google.com/dl/android/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.4.32/kotlin-stdlib-jdk8-1.4.32.pom
If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
Required by:
    project : > com.android.tools.build:gradle:7.0.0-alpha14

下の部分に注目

adjust the 'metadataSources { ... }' of the repository declaration.

次のように、プロジェクトのbuild.gradleを修正

buildscript {
    repositories {
        google()
        jcenter()
    }
    ・
    ・
    ・

下のようなエラーが出たので、「Fix Gradle wrapper and re-import project」をクリック

ダウンロードが始まり、「Could not find 〜」と言うようなエラーが出ます、対象になる場所を削除・コメントアウトしました。
<プロジェクトのbuild.gradle>

//apply plugin: 'com.github.dcendents.android-maven'
//apply plugin: 'com.jfrog.bintray'

そして、次の部分もコメントアウトしました。プル下フォルダのbuild.gradleです。

tess/tess-two/build.gradle

//install {
//    repositories.mavenInstaller {
//        pom.project {
//            name = 'tess-two'
//
//            packaging = 'aar'
//            groupId = 'com.rmtheis'
//            artifactId = 'tess-two'
//
//            developers {
//                developer {
//                    id = 'rmtheis'
//                    name = 'Robert Theis'
//                    email = 'robert.m.theis@gmail.com'
//                }
//            }
//            licenses {
//                license {
//                    name = 'The Apache Software License, Version 2.0'
//                    url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
//                    distribution = 'repo'
//                }
//            }
//            scm {
//                url 'https://github.com/rmtheis/tess-two'
//            }
//        }
//    }
//}
//
//bintray {
//    user = properties.getProperty("bintray.user")
//    key = properties.getProperty("bintray.apikey")
//    configurations = ['archives']
//    pkg {
//        repo = 'maven'
//        name = 'tess-two'
//        userOrg = user
//        publish = true
//    }
//}

Gradle DSL method not found: 'implementation()'

次のエラーです。Android Studioに下のようなエラーがあったので、「Open Gradle wrapper file」をクリックします。

すると、gradle.propertiesファイルが開くので、新しいものを設定します。gradleの新しいバージョンです。

distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-rc-1-all.zip

ここまできたらNDKの設定

ヘッダーメニューのTools -> SDK Managerをクリックします。
そして、「SDK Tools」タブを選択します。

そして、[NDK (Side by side)] と [CMake] のチェックボックスをオン
参考ページの内容は以下の通りです。

  1. プロジェクトを開いて、[Tools] > [SDK Manager] をクリックします。
  2. [SDK Tools] タブをクリックします。
  3. [NDK (Side by side)] と [CMake] のチェックボックスをオンにします。

Unable to find method 'org.gradle.api.artifacts.result.ComponentSelectionReason.getDescription()

これは、再起動したら通常通りにビルドが走り、同じエラーが出ました。

そして、Tesseract

こちらのページにある手順を実行します。

export ANDROID_HOME=/path/to/your/android-sdk
git clone git://github.com/rmtheis/tess-two tess
cd tess
./gradlew assemble

まとめると、TesseractをGitからプルしてコンパイルするということです。

このコマンドを実行するのに手間取ったのが、「path/to/your」の部分です。あらかじめNDKのインストール先を理解している必要があります。
NDKのインストール先

android-sdkのインストール先/ndk/ビルド番号?/

実際に自分の場合は下のようにコマンドを叩きました。

~/Android/sdk/ndk/22.1.7171670/ndk-build

結構な時間がかかります。

コンパイル終了後

アプリケーションのbuild.gradleにGradleのデーモン(裏側で動くプロセス)を終了するためにAndroid Studioを再起動します。

あと、Android Studioのアップグレードも行いました。

次のエラー

2つあります。それは、下のような形で出力されたエラーですので、メッセージも複数あります。

This version of the Android Support plugin for IntelliJ IDEA (or Android Studio) cannot open this project, please retry with version 2020.3.1 or newer.
Please remove usages of `jcenter()` Maven repository from your build scripts and migrate your build to other Maven repositories.
This repository is deprecated and it will be shut down in the future.
See http://developer.android.com/r/tools/jcenter-end-of-service for more information.
Currently detected usages in: root project 'AndroidOpenCV', project ':app', project ':eyes-two', ...

下の方のエラーを先に対応しました。プロジェクトのbuild.gradleを修正、しかしこれは意味がありませんでした。

    repositories {
        google()
        //jcenter()
    }

エラーが変わりました。

tried to access method sun.security.util.ECUtil.getECParameters(Ljava/security/Provider;)Ljava/security/AlgorithmParameters; from class sun.security.ec.ECKeyPairGenerator

これには、gradle.propertiesに以下のものを追加することで解決できそうです。がダメでした。。。

org.gradle.java.home=JDKへのパス

次の方法を試します。参考にしたサイトはこちらです。

そしたら次のエラーになりました。

Could not get unknown property 'Os' for task

tess-twoのbuild.gradleの以下の行です

def ndkBuildExt = Os.isFamily(Os.FAMILY_WINDOWS) ? ".cmd" : ""

結局のところ

GUIでは原因がわからなくなってきたので、コマンド実行に切り替えることにしいました。

実行するコマンドは「./gradlew clean」です。

しかし。。。

解決には至りませんでした。。。

諦める

考え方を変えることにしました。もともとOpenCVを動かしていたプロジェクトにTesseractを追加する形での設定を行なっていましたが、諦めて、TesseractプロジェクトにOpenCVを追加する形に変更します

つまりは、追加した部分を削除します。そして、新たにTeserractプロジェクトを作成する方向でやり直すことにします。

ビルドを動くようにする

build.gradle(プロジェクト用)を修正して下のようにします。Tesseractを依存関係から外します。
さらに、buildバージョンも修正して4.1.3にします。

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.3'
//        classpath 'com.android.tools.build:gradle:7.0.0-alpha14'
//        classpath 'com.android.tools.build:gradle:1.1.2'

        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
        //classpath 'com.github.dcendents:android-maven-plugin:1.2'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        maven {
            url 'https://maven.google.com/'
            name 'Google'
        }
    }
}

ext {
    // The following are only a few examples of the types of properties you can define.
    compileSdkVersion = 30
    // You can also create properties to specify versions for dependencies.
    // Having consistent versions between modules can avoid conflicts with behavior.
    supportLibVersion = "30.0.2"
}

ここまできて、依存関係が解決しないエラーが起きました。なのでclasspathを追加して、repositoryも追加しました。

buildscript {
    repositories {
        google()
        maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.3'
        classpath 'com.android.tools.build:aaptcompiler:4.1.3'
        classpath 'com.android.tools.lint:lint-gradle-api:27.1.3'
        classpath 'com.android.tools.build:gradle-api:4.1.3'
        classpath 'com.android.tools:common:27.1.3'
        classpath 'com.android.tools.analytics-library:tracker:27.1.3'
        classpath 'com.android.tools.analytics-library:shared:27.1.3'
        classpath 'com.android.tools:repository:27.1.3'
        classpath 'com.android.tools.build.jetifier:jetifier-core:1.0.0-beta09'

        classpath 'com.android.tools.build:builder:4.1.3'
        classpath 'com.android.tools.build:bundletool:0.14.0'
        classpath 'androidx.databinding:databinding-compiler-common:4.1.3'
        classpath 'com.android.tools.analytics-library:crash:27.1.3'
        classpath 'com.android.tools:sdk-common:27.1.3'
        classpath 'com.android.tools.build:apkzlib:4.1.3'
        classpath 'com.android.tools:sdklib:27.1.3'
        classpath 'com.android.tools.layoutlib:layoutlib-api:27.1.3'
        classpath 'com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta09'
        classpath 'org.ow2.asm:asm-commons:7.0'
        classpath 'org.jdom:jdom2:2.0.6'
        classpath 'com.android.tools:repository:27.1.3'
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
        //classpath 'com.github.dcendents:android-maven-plugin:1.2'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

compileSdkVersion is not specified. Please add it to build.gradle

こんなエラーも出ました。
そして、調べるとandroid{}を追加すればOKと言うところにたどり着き以下のようなbuild.gradleになりました。

// Top-level build file where you can add configuration options common to all sub-projects/modules.

apply plugin: 'com.android.application'

buildscript {
    repositories {
        google()
        maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.3'
        classpath 'com.android.tools.build:aaptcompiler:4.1.3'
        classpath 'com.android.tools.lint:lint-gradle-api:27.1.3'
        classpath 'com.android.tools.build:gradle-api:4.1.3'
        classpath 'com.android.tools:common:27.1.3'
        classpath 'com.android.tools.analytics-library:tracker:27.1.3'
        classpath 'com.android.tools.analytics-library:shared:27.1.3'
        classpath 'com.android.tools:repository:27.1.3'
        classpath 'com.android.tools.build.jetifier:jetifier-core:1.0.0-beta09'

        classpath 'com.android.tools.build:builder:4.1.3'
        classpath 'com.android.tools.build:bundletool:0.14.0'
        classpath 'androidx.databinding:databinding-compiler-common:4.1.3'
        classpath 'com.android.tools.analytics-library:crash:27.1.3'
        classpath 'com.android.tools:sdk-common:27.1.3'
        classpath 'com.android.tools.build:apkzlib:4.1.3'
        classpath 'com.android.tools:sdklib:27.1.3'
        classpath 'com.android.tools.layoutlib:layoutlib-api:27.1.3'
        classpath 'com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta09'
        classpath 'org.ow2.asm:asm-commons:7.0'
        classpath 'org.jdom:jdom2:2.0.6'
        classpath 'com.android.tools:repository:27.1.3'
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
        //classpath 'com.github.dcendents:android-maven-plugin:1.2'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
//        jcenter()
//        maven {
//            url 'https://maven.google.com/'
//            name 'Google'
//        }
    }

}

ext {
    // The following are only a few examples of the types of properties you can define.
    compileSdkVersion = 30
    // You can also create properties to specify versions for dependencies.
    // Having consistent versions between modules can avoid conflicts with behavior.
    supportLibVersion = "30.0.2"
}
//task clean(type: Delete) {
//    delete rootProject.buildDir
//}

android {
    compileSdkVersion 30
    buildToolsVersion '30.0.2'

    defaultConfig {
        applicationId ""
        minSdkVersion 15
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

ビルドも通りました。

でわでわ。。。