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

FlutterアプリをCodemagicでCDしてみた(App Store Connect)

Google Playストア版の続きになります。

FlutterアプリをCodemagicでCDしてみた(Google Playストア)
https://qiita.com/kasa_le/items/17720a02ea0a4de2b66e

Codemagicへは、すでにtagトリガーなどで動くワークフローがあるものとします。
(こちらの記事などを参照して下さい。)
また、App Store Connectにて、アプリを登録済で、かつTestFlightへ手動でアプリ公開をしたことがあることが前提です。
(こちらの記事などを参照して下さい。)

手順

基本的には以下のページの通りです。
https://docs.codemagic.io/publishing/publishing-to-app-store/

ただ、Apple ID(アプリケーションID)とApple ID(開発者アカウント)、等のように用語で混乱するので、このページでは以下のように統一します。

  • App ID
    • アプリケーション固有のID(10桁の数字)
    • App Store Connectで確認可能
  • 開発者アカウント
    • App Store Connectにログインする時のメールアドレス

1.App IDを取得

App Store Connectにアプリを登録済であれば、以下のような場所に見つかるはずです。

appid.png

2.開発者アカウントのapp-specificパスワードを作成する

app-specificパスワードについては、詳しくはこちらのページにあります。
https://support.apple.com/en-us/HT204397

要するに、サードパーティー製アプリからApp Store Connectに接続できるようにするためのパスワードですね。

(1)二段階認証を有効にする

開発者アカウントを登録する時に必須なので、設定済みなはずですので、割愛します。

(2)app-specificパスワードを作成する

こちらのページよりログインします。ここで二段階認証が必要になるので、iOS/MacOSデバイスが必要です。
https://appleid.apple.com/

  • 「セキュリティ」セクションの「App用パスワード」を探し、[パスワードを生成]をクリック
appspecificpassword.png
  • 任意のラベル名を付ける
    • 例えば、「Codemagic用」とか
app-pass-1.png
  • 表示されたパスワードをコピー
app-pass-2.png

※過去に作成したAppパスワードは、「編集」ボタンから「履歴を表示」で確認できます。

3.ユーザー設定でApple Developerと接続する

  • 左下のプロフィールアイコンから[User Settings]を選ぶ

usersettings.png

  • 開発者アカウントのメールアドレスとパスワードを入力する

connect_appledeveloper.png

  • [Save]をクリック

4.ワークフローに設定する

(1)リリースビルド設定

  • [Build]セクションで、Releaseビルドの設定にする
    • iOSにチェックを入れるのを忘れずに

build_release.png

[Save]を忘れずにクリック。

(2)iOS Code signing設定

  • [Publish]セクションを開く
  • [iOS code signing]を開く
  • Select code signing method: Automatic

    • 個人開発ならAutomaticで良いかと思います。
    • 業務で開発チームのプロビジョニングファイルがある場合はManualにするのかと思います。(こちらは経験無いので、詳しくは分かりません。すみません)
  • Developer portal team (using credentials from user settings): ドロップダウンから選ぶ

    • Apple Developerへの接続が上手くいっていれば、ここでチームを選べます。
    • 基本的には、Xcodeで選択したのと同じものを選べば良いです。
  • Provisioning profile type: TestFlightに上げるならばApp store

  • Bundle identifier (optional): debugビルドとreleaseビルドでBundle Idを変えている場合、ここにリリースビルド用のBundle Idを記入する

    • しておかないとビルド中に「複数のBundle Idが設定にあるよ」的なエラーで失敗します。

ios_code_signing.png

  • [Save]をクリック!

(3)App Store Connect設定

  • [App Store Connect]を開く

    • [Publish]セクションの下の方にあります
  • AppleID: 開発者アカウントのメールアドレス

  • App-specific password: 先ほどコピーしたApp用パスワードを貼り付ける

  • App ID: App IDを貼り付ける

enable_app_store_connect.png

  • Enable App Store Connect publishingにチェックを入れる
  • [Save]をクリック!!

以上で設定は終わりです。
ビルドを流してみましょう!

App Store Connectでの作業

GoogleのPlayストアへのpublishと違い、App Store Connectへのpublishは、ipaファイルというアプリ実行ファイルをアップロードするだけで終わりのようです。
TestFlightでテストを有効にしたり、リリースをAppleに審査申請するのは、App Store Connectのページで手動で行う必要があります。

ipaファイルのビルド・アップロードは、私のマシンだとかなりCPU使って重たくなり、他の作業が出来なくなるので、Codemagicがアップロードまでやってくれればかなり軽減になりますね。

まとめ

Google Playストア版と合わせ、これで「tagがpushされたら自動でリリースビルドしてデプロイ」が両OSで出来るようになりました。

Codemagic、マジでオススメです。Flutterに特化というのが面白いですよね。一応YAML編集してネイティブなAndroidアプリも出来そうではあるんですが、CIまでしかまだ試してなくて(しかもエミュレーターテストはどうやら出来ない)、CDとなるとどうなるか未知数です。

サポートはSlackですがだいたい直ぐに返事が来るので、凄く助かります。もちろん英語のみですが、中学生レベルでなんとかなります。時差に気をつけるくらいかな?

ただ、ちょっと大きめのプロジェクトになると、Mac Miniでのビルドでは時間がかかって無料枠をあっという間に使い切りそうなので、その場合は課金してMac Proコースにするか、増量枠を都度課金して使うといったことが必要そうです。

Mac Proは無料で3回までお試しで使えるので私も試しましたが、圧倒的にビルドが早いので快適でした^-^

FlutterのMeetup(Tokyo)が再開されたら、ちょっと参加してCodemagic推してみようかな?

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

Android Fragmentの中にタブ付きスワイプビューを作る(kotlin)

はじめに

完成イメージはこんな感じ

BottomNavigationViewのボタンから遷移した先の、fragmentのページの中で、さらにfragmentで3つのページを作る」みたいな感じです。(Androidネイティブ開発は初心者なので、文章がおかしいかもしれませんが、、、)

前提

すでに画面下の3つのタブがある状態。このタブに作りかたについては、こちらを参考に!(僕もこの動画を見ながら作りました。。)
3つのタブは以下のようなFragmentを表示する。
・fragment_first.xml
・fragment_second.xml
・fragment_third.xml
スクリーンショット 2020-05-31 16.09.45.jpg

こちらと全く同じ作りにすると、res/navigation/my_nav.xmlはこんな感じ。(secondFragmentの上部については、この記事でやっていきます:relieved:
スクリーンショット 2020-05-31 16.20.09.jpg

真ん中のsecondFragmentの中に、さらにスワイプ可能な3つのFragmentを作る。

スワイプ可能な3つのFragmentを作る

File>New>Fragment>Fragment(Blank)より、3つ新たにFragmentを作成
スクリーンショット 2020-05-31 18.45.11.jpg

作るFragmentは以下の3つ
fragment_completed_.xml
fragment_not_completed_.xml
fragment_shared_.xml

タブ付きスワイプビューのFragmentを作成するとこんな感じ。:point_down:
スクリーンショット 2020-06-01 18.52.21.jpg

secondFragmentの上部に、TabLayoutとViewPagerを使ったタブ付きスワイプビューを作成する

一応公式ドキュメントはこちら(これを読んで実装できる初心者は、初心者ではない!)

スクリーンショット 2020-05-31 18.56.37.jpg

fragment_second.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=".SecondFragment">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/pager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tabLayout"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.tabs.TabItem
            android:id="@+id/Not_Completed"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Not Completed" />

        <com.google.android.material.tabs.TabItem
            android:id="@+id/Completed"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Completed" />

        <com.google.android.material.tabs.TabItem
            android:id="@+id/Shared"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Shared" />
    </com.google.android.material.tabs.TabLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

これで一旦見た目はできた。。

スワイプビューの処理をするクラスを作る

New>Kotlin File/ClassよりFileを作成。(クラスと言いつつFileを作る。。)
スクリーンショット 2020-05-31 19.01.48.jpg

スクリーンショット 2020-05-31 19.01.31.jpg

PageAdapter.kt
package com.example.myapplication

import android.content.Context
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import com.example.myapplication.completed_Fragment
import com.example.myapplication.not_completed_Fragment
import com.example.myapplication.shared_Fragment


class PageAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
    override fun getItem(position: Int): Fragment {
        when (position) {
          // どのFragmentを表示するか
            0 -> {return  not_completed_Fragment()}
            1 -> {return  completed_Fragment()}
            2 -> {return  shared_Fragment()}
            else -> {return  not_completed_Fragment()}
        }
    }

// スワイプビューの数が3つだから
    override fun getCount(): Int {
        return 3;
    }

// スワイプビューのタイトルを決める
    override fun getPageTitle(position: Int): CharSequence? {
        when(position){
            0 -> {return "Not Completed"}
            1 -> {return  "Completed"}
            2 -> {return "Shared"}
            else -> {return null}
        }
    }
}

secondFragmentでタブ付きスワイプビューを動作するようにする

SecondFragment.kt
package com.example.myapplication

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.viewpager.widget.ViewPager
import kotlinx.android.synthetic.main.fragment_second.*


/**
 * A simple [Fragment] subclass.
 */
class SecondFragment : androidx.fragment.app.Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?

    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_second, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // ActionBarを隠す
        (activity as AppCompatActivity).supportActionBar?.hide()

        val fragmentManager = (activity as FragmentActivity).supportFragmentManager

       //ここの「pager」はfragment_second.xmlのViewPagerのidの事!
        pager.adapter = PageAdapter(fragmentManager)
        tabLayout.setupWithViewPager(pager)
    }
}

この時点で恐らくタブ付きスワイプビューは動作するかと。。。。

fragment_firstとfragment_thirdで、タイトルバーが再表示されるようにする

・fragment_first.xml
・fragment_second.xml
・fragment_third.xml
スクリーンショット 2020-05-31 16.09.45.jpg

こちらの3つのFragmentだが、SecondFragment.ktonViewCreatedの関数で、タイトルバーを隠すようしているので、他のFragmentでは再表示させないといけません。

SecondFragment.kt
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // ActionBarを隠す
        (activity as AppCompatActivity).supportActionBar?.hide()

        val fragmentManager = (activity as FragmentActivity).supportFragmentManager

       //ここの「pager」はfragment_second.xmlのViewPagerのidの事!
        pager.adapter = PageAdapter(fragmentManager)
        tabLayout.setupWithViewPager(pager)
    }
}

なので、FirstFragment.ktthirdFragment.ktを以下のようにする

package com.example.myapplication

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity

/**
 * A simple [Fragment] subclass.
 */
class FirstFragment : androidx.fragment.app.Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // ActionBarを出す
        (activity as AppCompatActivity).supportActionBar?.show()
    }
}

最後に

Fragmentの中に、さらにFragmentを作って表示、と言うのがなかなか情報がなく、苦戦しました。。。
onViewCreatedの関数が重要なようです!

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

GitHub ActionsでAndroidアプリのCIを構築する方法

はじめに

GitHub Actionsを使い、Androidアプリのビルドと単体テスト、静的解析を行うCIを構築します。

本記事で書かないこと

設定ファイルの紹介

設定ファイルの内容を上から順に紹介します。

name

ワークフローの名前を設定します。

私は単純に「CI」としています。

ci.yml
name: CI

on

ワークフローを実行するトリガーを設定します。

私はGit-flowモデルで開発しており、以下のように設定しています。

ci.yml
on:
  push:
    branches:
      - master
      - develop
    paths-ignore:
      - docs/**
      - README.md
      - LICENSE
  pull_request:
    branches:
      - develop
    paths-ignore:
      - docs/**
      - README.md
      - LICENSE

他の人からPull request(以下、「PR」と呼ぶ)を頂いたときにもCIが動くように、 develop ブランチへのPRをトリガーにしています。
PRのマージ時にもCIが動くように、 developmaster ブランチへのプッシュもトリガーにしています。
feature/** ブランチへのプッシュをトリガーにしていない理由は、 develop ブランチへPRを出したときにCIが二重に動作するのを防ぐためです。
早めにCIを動かしたい場合、「WIP:」を付けてPRを出します(GitHubではドラフト機能があるので、それを使うのもありです)。

README.mdLICENSE など、Androidアプリに直接関係ないファイルは、単体でコミットしてもCIが動かないようにしています。
README.md のみ更新してプッシュしてもCIが動くような気がします…。私の設定が誤っているのでしょうか?)

jobs

今回はジョブを3つ用意して並行に実行しているので、順に紹介します。

test

ビルドと単体テストを実行するジョブです。

基本的には『Androidテスト全書』を参考にしてGradleタスクを実行しているだけなので、詳細は省略します。

Gradleのキャッシュは、READMEの例をそのままコピペしています。
https://github.com/actions/cache/blob/master/examples.md#java---gradle

ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    # チェックアウト
    - uses: actions/checkout@v2

    # JDKのセットアップ
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8

    # Gradleのキャッシュ復元
    - uses: actions/cache@v2
      with:
        path: ~/.gradle/caches
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
        restore-keys: |
          ${{ runner.os }}-gradle-

    # 依存関係の出力
    - name: Displays the Android dependencies of the project
      run: ./gradlew androidDependencies

    # コンパイル
    - name: Run Compile
      run: ./gradlew assembleDebug

    # テスト
    - name: Test with Gradle
      run: ./gradlew test --stacktrace

lint

Android Lintを使って静的解析するジョブです。

./gradlew lint を実行しているだけなので、詳細は省略します。

ci.yml
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
    # チェックアウト
    - uses: actions/checkout@v2

    # 静的解析
    - name: Run Inspection
      run: ./gradlew lint

detekt

detekt を使って静的解析するジョブです。

./gradlew detekt を実行しているだけなので、詳細は省略します。

ci.yml
jobs:
  detekt:
    runs-on: ubuntu-latest
    steps:
    # チェックアウト
    - uses: actions/checkout@v2

    # 静的解析
    - name: Lint with detekt
      run: ./gradlew detekt

Android Lintにはない観点で静的解析するため、両方とも使うのがオススメです。

設定ファイルの全体図

最後に設定ファイルの全体図を載せます。

ci.yml
name: CI

on:
  push:
    branches:
      - master
      - develop
    paths-ignore:
      - docs/**
      - README.md
      - LICENSE
  pull_request:
    branches:
      - develop
    paths-ignore:
      - docs/**
      - README.md
      - LICENSE

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    # チェックアウト
    - uses: actions/checkout@v2

    # JDKのセットアップ
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8

    # Gradleのキャッシュ復元
    - uses: actions/cache@v2
      with:
        path: ~/.gradle/caches
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
        restore-keys: |
          ${{ runner.os }}-gradle-

    # 依存関係の出力
    - name: Displays the Android dependencies of the project
      run: ./gradlew androidDependencies

    # コンパイル
    - name: Run Compile
      run: ./gradlew assembleDebug

    # テスト
    - name: Test with Gradle
      run: ./gradlew test --stacktrace

  lint:
    runs-on: ubuntu-latest
    steps:
    # チェックアウト
    - uses: actions/checkout@v2

    # 静的解析
    - name: Run Inspection
      run: ./gradlew lint

  detekt:
    runs-on: ubuntu-latest
    steps:
    # チェックアウト
    - uses: actions/checkout@v2

    # 静的解析
    - name: Lint with detekt
      run: ./gradlew detekt

シンプルなYAMLファイルなので、慣れれば読みやすいと思います。

おわりに

GitHub Actionsで基本的なAndroidアプリのCIを回すことができました!

参考リンク

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

Activityへ値を渡し戻り値も得る

はじめに

空のActivityに値を渡すことが出来るまでを書いていきます。
ActivityへはGson形式で受渡を行い。
さらにメインのアクティビティとサブのアクティビティにはFragmentを使って同じ画面を表示します。

準備

strings.xmlを編集

strings.xmlを下記の様に編集します。

strings.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
    <string name="app_name">QiitaActivityFragmentSample</string>
    <string name="btEdit">編集</string>
    <string name="tvStr">String型</string>
    <string name="tvInt">Int型</string>
    <string name="tvDouble">Double型</string>
</resources>

Gsonを扱えるようにする

左のプロジェクトのGradleツリーからbuild.gradle(app)を開きます。

build.gradle
dependencies {
    省略
    implementation 'com.google.code.gson:gson:2.8.6'
}

上記の一文を追加してGsonを使えるようにします。

空のFragmentを作成する

左のプロジェクトで右クリック→新規→フラグメント→空のフラグメントを指定します。
Fragmentの名称を指定するダイアログが表示されますが初期値のBlankFragmentで進めます。

Fragmentをデザインする。

FragmentにString型、Int型、Double型を編集出来るレイアウトを作成しました。

fragment_blank.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".BlankFragment">

    <LinearLayout
        android:id="@+id/llv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/textView"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="right"
                android:text="@string/tvStr" />

            <EditText
                android:id="@+id/etString"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10"
                android:inputType="text" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/textView2"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="right"
                android:text="@string/tvInt" />

            <EditText
                android:id="@+id/etInt"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10"
                android:inputType="number" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/textView3"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:gravity="right"
                android:layout_weight="1"
                android:text="@string/tvDouble" />

            <EditText
                android:id="@+id/etDouble"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10"
                android:inputType="numberDecimal" />

        </LinearLayout>
    </LinearLayout>

</FrameLayout>

MainActivityにFragmentを配置する

レイアウトデザインを行います。
MainActivityにはボタン1つとFragmentを1つ配置する予定なのでLinearLayout(vertical)を配置してボタン1つとFragmentを1つ配置します。

MainActivityにてパレットのcontainersのFragmentを配置するとどのFragmentを配置するのか聞いてきますのでBlankFragmentを指定します。

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

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onButtonClick"
            android:text="@string/btEdit" />

        <fragment
            android:id="@+id/fragment"
            android:name="com.example.qiitaactivityfragmentsample.BlankFragment"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

Fragmentの配置が終わったら問題無いか表示を確認しましょう。

空のActivityを作成する

左のプロジェクトで右クリック→新規→アクティビティ→空のアクティビティを指定します。
Activityの名称を指定するダイアログが表示されますので名称をSubActivityと設定して完了をクリックします。

空のActivityにFragmentを配置する

SubActivityにてパレットのcontainersのFragmentを配置するとどのFragmentを配置するのか聞いてきますのでBlankFragmentを指定します。
SubActivityにはFragmentを1つ配置して終了です。

activity_sub.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=".SubActivity">

    <fragment
        android:id="@+id/fragment2"
        android:name="com.example.qiitaactivityfragmentsample.BlankFragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>

追加したActivityに戻るボタンを追加

SubActivity.kt
class SubActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sub)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }

SubActivity.klには戻るボタンを表示させておきます。

Actionbar.kt
 supportActionBar?.setDisplayHomeAsUpEnabled(true)

こう書いておくとSubActibity表示の時に戻るボタンが表示されます。

追加したActivityを表示する

MainActivity.ktにボタンクリックイベントを作ります。

MainActivity.kt
    fun onButtonClick(view : View){
        val intent = Intent(applicationContext,SubActivity::class.java)
        startActivityForResult(intent,1)
    }

このようにすると追加したイベント内でActivityを表示することができます。
実行してボタンを押すとMainActivityからSubActivityへ表示が変わり、SubActivity内の「←」戻るボタンでMainActivityに戻れば成功です。

Fragmentから値を取得する

FragmentはMainActivityとSubActivityから呼び出されています。以前の失敗編ではこの2つが区別出来ずに自滅しました。
今回はMainActivityとSubActivityに配置したFragmentのidがそれぞれMainがFragment SubがFragment2になっていることをデザインしたXMLファイルで確認しましょう。この差で区別出来るようです。

デバッグ用に値を入れておく

いちいち値を入力するのはデバッグしづらいので生成時に値を入れてしまいます。

MainActivity.kt
class MainActivity : AppCompatActivity() {
    private val data = gsonFile()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val etString = findViewById<EditText>(R.id.etString)
        val etInt = findViewById<EditText>(R.id.etInt)
        val etDouble = findViewById<EditText>(R.id.etDouble)
        etString.setText("test")
        etInt.setText("1234")
        etDouble.setText("987.65")
    }

Fragmentを参照する

デザイン時に作られたFragmentは

MainActivity.kt
    fun onButtonClick(view : View){
        val flg = supportFragmentManager.findFragmentById(R.id.fragment) as BlankFragment
        Log.d("Sample","flg = $flg")
    }

このようにして実行するとfragmentが取得できていることがわかります。
※このやり方が正しいのかどうかわかりません。

Fragmentと値をやりとりする

Fragmentのプログラムに色々追加して簡単にやりとりできるようにします。

メソッド名 内容
toGsonString Fragmentの編集内容をGson形式の文字列で返します
fromGsonString 渡されたGson形式の文字列に従いFragmentの表示に反映します

準備

BlankFragment内で使用するクラスを宣言します。

BlankFragment.kt
class BlankFragment : Fragment() {
    private val data = gsonFile()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

Fragmentの内容からGson文字列の変換

toGsonString.kt
    fun toGsonString() : String{
        val dStr : EditText? = activity?.findViewById<EditText>(R.id.etString)
        val dInt : EditText? = activity?.findViewById<EditText>(R.id.etInt)
        val dDouble : EditText? = activity?.findViewById<EditText>(R.id.etDouble)
        data.dStr = dStr?.text.toString()
        data.dInt = dInt?.text.toString().toInt()
        data.dDouble = dDouble?.text.toString().toDouble()
        return data.gsonString()
    }

Fragment内でレイアウト上のidを取得するためにはこれまでのfindViewByIdの前にactivity?としてactivity経由の参照に変わります。
EditTextのidを参照してtext部分を読み取り、それをGson形式の文字列にして返します。※gsonString()は後で完成したものを参考にして下さい。

Gson文字列からFragmentの内容に変換

fromGsonString.kt
    fun fromGsonString(str : String){
        data.fromData(str)
        val dStr : EditText? = activity?.findViewById<EditText>(R.id.etString)
        val dInt : EditText? = activity?.findViewById<EditText>(R.id.etInt)
        val dDouble : EditText? = activity?.findViewById<EditText>(R.id.etDouble)
        dStr?.setText(data.dStr.toString())
        dInt?.setText(data.dInt.toString())
        dDouble?.setText(data.dDouble.toString())
    }

完成したBlankFragment.kt

BlankFragment.kt
class BlankFragment : Fragment() {
    private val data = gsonFile()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank, container, false)
    }


    fun toGsonString() : String{
        Log.d("Sample","toGsonString = $activity")
        val dStr : EditText? = activity?.findViewById<EditText>(R.id.etString)
        val dInt : EditText? = activity?.findViewById<EditText>(R.id.etInt)
        val dDouble : EditText? = activity?.findViewById<EditText>(R.id.etDouble)
        data.dStr = dStr?.text.toString()
        data.dInt = dInt?.text.toString().toInt()
        data.dDouble = dDouble?.text.toString().toDouble()
        return data.gsonString()
    }

    fun fromGsonString(str : String){
        data.fromData(str)
        val dStr : EditText? = activity?.findViewById<EditText>(R.id.etString)
        val dInt : EditText? = activity?.findViewById<EditText>(R.id.etInt)
        val dDouble : EditText? = activity?.findViewById<EditText>(R.id.etDouble)
        dStr?.setText(data.dStr.toString())
        dInt?.setText(data.dInt.toString())
        dDouble?.setText(data.dDouble.toString())
    }
}

open class gsonData(){
    var dStr : String = ""
    var dInt : Int = 0
    var dDouble : Double = 0.0
    var backColor : Int = android.R.color.background_light

    fun assign(a : gsonData){
        dStr = a.dStr
        dInt = a.dInt
        dDouble = a.dDouble
        backColor = a.backColor

    }
    fun gsonString() : String{
        return Gson().toJson(this)
    }
}


class gsonFile() : gsonData(){
    fun fromData(str : String){
        if (str == "") return
        val data =  Gson().fromJson<gsonData>(str, gsonData::class.java) as gsonData
        super.assign(data)
    }
    fun fileSave(filename : String,str : String){
        File(SingletonContext.applicationContext().filesDir, filename).writer().use {
            it.write(str)
        }

    }
    fun fileLoad(filename : String) : String?{
        val readFile = File(SingletonContext.applicationContext().filesDir, filename)

        if(!readFile.exists()){
            Log.d("debug","No file exists")
            return null
        }
        else{
            return readFile.bufferedReader().use(BufferedReader::readText)
        }
    }
}

private class SingletonContext : Application() {
    init { instance = this }
    companion object {
        private var instance: SingletonContext? = null
        fun applicationContext() : Context {return instance!!.applicationContext}
    }
}

FragmentをGson文字列にしてActivityに渡す

MainActivity.ktのボタンクリックにSubActivityへ渡す処理を追加します。

MainActivity.kt
    fun onButtonClick(view : View){
        val flg = supportFragmentManager.findFragmentById(R.id.fragment) as BlankFragment
        val str = flg.toGsonString()
        val intent = Intent(applicationContext,SubActivity::class.java)
        intent.putExtra("data",str)
        startActivityForResult(intent,1)
    }

Gson文字列にしたものをintent.putExtra("data",str)としてキー「data」の文字列として渡します。

SubActivityから値を返す

SubActivityから値を取得するためにstartActivityではなくstartActivityResultを使っています。これを使って呼び出さないとSubActivity側でいくら頑張ってもMainActivityには値が返ってきません。

SubActivityで値を受け取る

MainActivityから値を渡されたのでSubActivityで値を受け取ります。
SubActivityは自動的に色々作られますが、この例で使用するのはほんのわずかです。

SubActivity.kt
class SubActivity : AppCompatActivity() {
    private val data = gsonFile()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sub)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)

        var str = intent.getStringExtra("data")
        val flg = supportFragmentManager.findFragmentById(R.id.fragment2) as BlankFragment
        flg.fromGsonString(str)
    }
}

たった三行追加しただけで済みました。

1.kt
var str = intent.getStringExtra("data")

MainActivityから渡されたデータを受け取ります

2.kt
val flg = supportFragmentManager.findFragmentById(R.id.fragment2) as BlankFragment

SubActivityが表示しているFragmentを参照します

3.kt
flg.fromGsonString(str)

Fragmentに値を反映させます
ここまで書けたら実行してMainActivityからSubActivityに値が渡されているか確認してみましょう

SubActivityからMainActivityへ値を渡す

SubActivityの「戻る」ボタンをクリックしたときに発生するイベントを受け取ります。

SubActivity.kt
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        if (item.itemId == android.R.id.home) {
            val flg = supportFragmentManager.findFragmentById(R.id.fragment2) as BlankFragment
            val result: Intent = Intent(applicationContext, SubActivity::class.java)
            val str = flg.toGsonString()
            result.putExtra("data", str)
            setResult(Activity.RESULT_OK, result)
            finish()
        }
        return super.onOptionsItemSelected(item)
    }

順番に説明していきます。

home.kt
 if (item.itemId == android.R.id.home) {

押されたのが「戻る」ボタンかを判断しています。

Fragment.kt
val flg = supportFragmentManager.findFragmentById(R.id.fragment2) as BlankFragment

SubActivityのFragment2を参照します。

Intent.kt
val result: Intent = Intent(applicationContext, SubActivity::class.java)

SubActivityから値を返すIntent型を定義します。

putExtra.kt
result.putExtra("data", str)

Intent型に返すべき値を渡します。

setResult.kt
setResult(Activity.RESULT_OK, result)

MainActivityに対してSubActivityはどのような状態で終了したのかを設定します。

finishi.kt
finish()

SubActivityを終了します。

MainActivityにてSubActivityの値を受け取る

Activity.ktにてSubActivityの値を受け取ります。
SubActivityから戻るとonActivityResultイベントが発生しますのでこれを継承します。

onActivityResult.kt
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode === Activity.RESULT_OK && requestCode === 1) {
            val flg = supportFragmentManager.findFragmentById(R.id.fragment) as BlankFragment
            val str = data?.extras?.getString("data").toString()
            flg.fromGsonString(str)
        }
    }

それぞれ説明します。

result.kt
if (resultCode === Activity.RESULT_OK && requestCode === 1) {

resultCodeはSubActivityにてsetResult(Activity.RESULT_OK, result)と返り値を指定された値が返りますのでこれと比較しています。

さらにMainActivityからSubActivityを呼び出す時に** startActivityForResult(intent,1)**とrequestCodeを指定していてこれが返ってきますので比較しています。

Fragment.kt
val flg = supportFragmentManager.findFragmentById(R.id.fragment) as BlankFragment

Fragmentを参照します。

extras.kt
val str = data?.extras?.getString("data").toString()

onActivityRsultの引数としてIntent型の dataが渡されますのでここからSubActivityで設定した文字列を取得します。

Gson.kt
flg.fromGsonString(str)

FragmentにSubActivityから返ってきたGson形式の文字列を渡して反映させます。

最後に

Fragmentにて編集すべきデータクラスと、さらにFragmentのレイアウトに設定しているidに従って値の取得と反映処理を作っておくと、使う側が非常に楽になりました。

またActivity間を移動する場合でもGson型の文字列として渡すことが出来て便利です。

これで1つのレイアウトを複数から参照も出来そうです。

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

Android関連 利用頻度高い製品のバージョン (2020-06-01)

2020-06-01 現在 Android関連 利用頻度高い製品のバージョン

個人的に特定時期のバージョン情報がまとまっていると嬉しいのでまとめたものです。

Android Studio

4.0.0 (2020-05-28)

Download Android Studio and SDK tools  |  Android Developers
https://developer.android.com/studio

Android Studio release notes  |  Android Developers
https://developer.android.com/studio/releases

Gradle

6.4.1 (2020-05-15)

Gradle Build Tool
https://gradle.org/

Gradle | Releases
https://gradle.org/releases/

Android Gradle plugin

4.0.0 (April 2020)

Android Gradle plugin release notes  |  Android Developers
https://developer.android.com/studio/releases/gradle-plugin?hl=en

Kotlin

Kotlin 1.3.70 (2020-03-03)

Kotlin Programming Language
https://kotlinlang.org/

Kotlin 1.3.70 Released | Kotlin Blog
https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-3-70-released/

Kotlin コルーチンでアプリのパフォーマンスを改善する  |  Android デベロッパー  |  Android Developers
https://developer.android.com/kotlin/coroutines?hl=ja

RxKotlin

RxKotlin 3.0.0 (2020-03-21)

ReactiveX/RxKotlin: RxJava bindings for Kotlin
https://github.com/ReactiveX/RxKotlin

Releases · ReactiveX/RxKotlin
https://github.com/ReactiveX/RxKotlin/releases

OkHttp

Version 4.7.2 (2020-05-20)

square/okhttp: Square’s meticulous HTTP client for Java and Kotlin.
https://github.com/square/okhttp/releases

okhttp/CHANGELOG.md at master · square/okhttp
https://github.com/square/okhttp/blob/master/CHANGELOG.md

Retrofit

Version 2.9.0 (2020-05-20)

Retrofit
https://square.github.io/retrofit/

retrofit/CHANGELOG.md at master · square/retrofit
https://github.com/square/retrofit/blob/master/CHANGELOG.md

Moshi

Version 1.9.2 (2019-11-17)

square/moshi: A modern JSON library for Kotlin and Java.
https://github.com/square/moshi

moshi/CHANGELOG.md at master · square/moshi
https://github.com/square/moshi/blob/master/CHANGELOG.md

Realm

7.0.0(2020-05-16)

Realm: Create reactive mobile apps in a fraction of the time
https://realm.io/

realm-java/CHANGELOG.md at master · realm/realm-java
https://github.com/realm/realm-java/blob/master/CHANGELOG.md

Timber

Version 4.7.1 (2018-06-28)

JakeWharton/timber: A logger with a small, extensible API which provides utility on top of Android's normal Log class.
https://github.com/JakeWharton/timber

timber/CHANGELOG.md at master · JakeWharton/timber
https://github.com/JakeWharton/timber/blob/master/CHANGELOG.md

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

Android Studioのオススメ初期セットアップ(Mac)

はじめに

Android Studioのオススメ初期セットアップを紹介します:level_slider:

セットアップ完了前後でこのように変わります。

before
スクリーンショット 2020-06-01 10.02.46.png

after
スクリーンショット 2020-06-01 12.14.53.png

環境

  • OS:macOS Catalina 10.15.3
  • Android Studio:4.0

インストール

公式サイトから.dmgファイルをダウンロードして実行し、.appファイルを「アプリケーション」フォルダにコピーします。
https://developer.android.com/studio/?hl=ja

環境設定

Android Studioを起動して ⌘, で環境設定を開きます。

文字サイズを大きくする

Editor > Font
Size:12→14

スクリーンショット_2020-06-01_9_46_24_skitch.png

私は文字サイズは14がちょうどいいです。

マウスオーバーでドキュメントを表示する

Editor > General > Other
[Show quick documentation on mouse move]チェック:OFF→ON

スクリーンショット_2020-06-01_9_54_39_skitch.png

コード上のクラスなどをマウスオーバーするだけでドキュメントが表示されるようになります。
↓では AppCompatActivity() をマウスオーバーしています。
スクリーンショット 2020-06-01 12.36.48.png

補完時に大文字小文字を区別しない

Editor > General > Code Completion
Match case:ON→OFF

スクリーンショット_2020-06-01_9_56_08_skitch.png

補完時に大文字小文字を区別しないことで、わざわざShiftキーを押下しなくてもクラス名などを補完できるようになります。

インポート文を自動で挿入・削除する

Editor > General > Auto Import > Kotlin (Java)
[Add unambiguous imports on the fly]チェック:OFF→ON
[Optimize imports on the fly (for current project)]チェック:OFF→ON

スクリーンショット_2020-06-01_9_59_44_skitch.png

手動でインポートする手間を省けます。

使っている言語に合わせて設定してください。私はKotlinしか使っていませんが、Javaも設定しています。

インポート文を自動で隠さない

Editor > General > Code Folding > Fold by default: > General
[Imports]チェック:ON→OFF

スクリーンショット_2020-06-01_10_03_49_skitch.png

インポート文が隠れていると実装しづらいので、私は常に表示しています。

Kotlinでワイルドカードインポートを使わない

Editor > Code Style > Kotlin > [imports]タブ
Top-level Symbols:Use import with '*' when at least 5 names used→Use single name import
Java Statics and Enum Members:Use import with '*' when at least 3 names used→Use single name import

スクリーンショット_2020-06-01_10_05_19_skitch.png

detektのWildcardImportルールに反するため、ワイルドカードでのインポートを使わないようにします。

Android Studio 4.0だとデフォルトで設定されていたので、確認だけ行いました。

シェルをzshからbashに変更する

Tools > Terminal > Application settings
Shell path: /bin/zsh/bin/bash

スクリーンショット_2020-06-01_10_11_01_skitch.png

macOSではCatalinaから標準のシェルがbashからzshに変更されました。
私はbashに慣れているので戻します。

Ctrl + Tabで横のタブに移動するようにする

Keymap > Main menu > Window > Editor Tabs
Select Next Tab: ^→^ Tab
Select Previous Tab: ^←^ Shift Tab

スクリーンショット_2020-06-01_10_42_24_skitch.png

私はCtrl + Tabでサッと横のタブに移動したいため、キーマップを変えています。

Keymap > Version Control Systems > Diff & Merge
Select Opposite Diff Pane: ^ Tab → なし
Select Position In Opposite Diff Pane: ^ Shift Tab → なし

スクリーンショット_2020-06-01_10_45_42_skitch.png

それぞれ ^→ , ^← を割り当てたいのですが、入力できなくて諦めました、、

プラグインを追加する

Plugins > [Marketplace]タブ
追加したいプラグインを検索し、[Install]ボタンを押下

スクリーンショット_2020-06-01_13_10_03_skitch.png

私はIdeaVimと自動でインストールされるプラグインしか追加していません。
スクリーンショット_2020-06-01_13_10_13_skitch.png

オススメのプラグインがあれば、教えていただけると嬉しいです:blush:

Visual Studio風のカラースキームを設定する

私はVisual Studioの文字色に慣れているので、それに近い色合いのカラースキームを設定します。
完全に私の好みのため、必要に応じて設定してください。

以下からVisual Studio風のカラースキームをダウンロードします。
http://color-themes.com/?view=theme&id=563a1a6780b4acf11273ae55

Editor > Color Scheme
歯車のアイコンをクリック > Import Scheme…
ダウンロードした.jarファイルを選択

スクリーンショット_2020-06-01_13_30_03_skitch.png

背景色が白でコメントの文字色が緑でないと落ち着きません:no_mouth:

カラースキームでなくデフォルトの文字サイズを使う

Editor > Color Scheme > Color Scheme Font
[Use color scheme font instead of default]チェック:ON→OFF

スクリーンショット_2020-06-01_12_11_18_skitch.png

Visual Studio風のカラースキームには文字サイズも含まれています。
先ほど設定したデフォルトの文字サイズ(14)のままにしたいため、こちらのチェックを外します。

コメントの文字色を変更する

Editor > Color Scheme > Language Defaults
Comments
Block comment:808080(グレー)→008000(緑)
Doc Comment
Markup:000000(黒)→808080(グレー)
Tag:0000FF(青)→808080(グレー)
Tag value:3D3D3D(黒)→808080(グレー)
Text:008000(緑)→808080(グレー)

スクリーンショット_2020-06-01_12_12_57_skitch.png

Visual Studioと同様、通常のコメントを緑、ドキュメンテーションコメントの文字色をグレーにします。

フォントの斜体と太字を変更する

Editor > Color Scheme > Language Defaults
Itaricをすべて外す
TagのみBold、それ以外はBoldを外す

文字色を変更したときと同じ画面なので、スクリーンショットは省略します。

Xcodeでは斜体を一切使っておらず、太字もタグでしか使われていません(違ったらすみません)。
私はXcodeに慣れているため、同じように設定します。

その他の設定

保存時にインポート文の最適化とコード整形を行う

以下の記事を参考に設定します。
https://qiita.com/chocomelon/items/46810763a5be0a433158

私はIdeaVimを使っていて :w で保存しているため設定していませんが、 Command + S で保存している方にはオススメです。
私もIdeaVimを使う前までは設定していました。

上記の記事ではマクロで設定していますが、プラグインを使う方法もあるようです。
https://qiita.com/ikemura23/items/9a33c1b2bdb5a65d29a5

開いているファイルをプロジェクトツリーで選択状態にする

プロジェクトツリーの右上にある歯車のアイコンを押下
[Always Select Opened File]チェック:OFF→ON

スクリーンショット_2020-06-01_15_10_44_skitch.png

開いているファイルを常にプロジェクトツリーで選択状態にします。
スクリーンショットだとわかりづらいかもしれませんが、 MainActivity.kt をタブで開いているので、プロジェクトツリーでも MainActivity が選択されています。

おわりに

これで今日から楽しいAndroidアプリ開発ライフの始まりです:computer::sparkles:

メモらずに変更していて漏れている設定や、他にもオススメの設定がありましたらコメントなどでご連絡いただけると嬉しいです:blush:

参考リンク

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

画面遷移で別のアクティビティを開始するやり方 - Android

今回学ぶこと

・画面遷移の実装方法

目指す成果物

このようにボタンをタップしたら,別のActivityに画面遷移する実装をしてみたいと思います.

gif_画面遷移_qiita.gif

ディレクトリ構成

ポイントは「MainActivity.kt」のようなActivityを2つ作ることと、レイアウトXMLファイルを2つ作ることです.

スクリーンショット 2020-06-01 12.05.09.png

ステップ

実装方法は2パターンあります
ポイントは画面遷移での必須の,「Intent」クラスと「startActivity」メソッドの使い方が方法①と②で異なります

1.  方法① 基本的な実装

MainActivity.kt
package com.example.android_transition_page

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //ここが方法②と異なります
        button_next_activity.setOnClickListener {
            val intent = Intent(this, NextActivity::class.java)
            startActivity(intent)
        }
    }
}
NextActivity.kt
package com.example.android_transition_page

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class NextActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_next)
    }
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MainActivity" />

    <Button
        android:id="@+id/button_next_activity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Move to Next Activity" />

</LinearLayout>
activity_next.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:gravity="center"
    tools:context=".NextActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="NextActivity"/>

</LinearLayout>

2.  方法② コード量を減らして省略した実装

MainActivity.kt
package com.example.android_transition_page

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

     //ここが方法①と異なります
        button_next_activity.setOnClickListener {
            startActivity(Intent(this, NextActivity::class.java))
        }
    }
}
NextActivity.kt
package com.example.android_transition_page

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class NextActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_next)
    }
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MainActivity" />

    <Button
        android:id="@+id/button_next_activity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Move to Next Activity" />

</LinearLayout>
activity_next.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:gravity="center"
    tools:context=".NextActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="NextActivity"/>

</LinearLayout>

まとめ

・画面遷移には「Intent」というクラスを使う

ソースコード

Github:
https://github.com/syuheifujita/android_transition_page

言葉の定義

おわりに:他にもっとわかりやすい方法あれば教えてください

画面遷移の実装方法をまとめてみました.
もしかすると,情報が古かったり間違っていたり,他にもっとわかりやすい方法があるのかもしれません.
皆さんの中で上手に実装方法をまとめているかたがいればぜひ教えてください!

Twitterやコメントなどでお待ちしています.質問でもお気軽に!

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

こんなソースコードはイヤだ-無名関数のパワー

プログラムのソースコードのより良い書き方をまとめていこうと思います。

無名関数のパワー

sample.kt
  var decodeUrl = ""
  try {
    decodeUrl = URLDecoder.decode(this.url, "UTF-8")
  } catch (e: Exception) {
    decodeUrl = this.url
  }
  print(decodeUrl)

どのようにリファクタリングできるのか

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