はじめてAndroidアプリを作ったときのメモ


COBOLer→VBer→Rubyist→PHPerを経て来たプログラマが、はじめてAndroidでアプリを作ることになったので、勉強がてらに紙芝居的なUIを持ったアプリを作る際に調べたアレコレ。勉強不足で突っ込みどころ満載だと思いますが、ご指摘はお手柔らかにお願いします。

# ひとまず用語集的なものから

## AndroidStudio

Android開発者向けに作られたIDE。VisualStudioみたいなもの。
ホントすげぇ便利。今はこんなに便利なモノがあるんですねぇ。

## Activity

アプリの画面そのもの。.NETでいうところの「フォーム」だと思っておけば良い。

1アプリはだいたい複数のActivityで構成されている。たとえば、メールアプリの場合、メールの一覧を表示するActivity、メールを閲覧するActivity、メールを作成するActivityといった具合。

凄いのは、各Activityが独立していて、外部のアプリからでも各Activityを利用できるところ（例の場合、メールアプリ自体がそれを許可していればの話）。たとえば、カメラアプリで撮影した写真をメールで送るためにメールアプリ内のメールを作成するActivityを起動する、といった芸当が可能。

どのレイアウトファイル（UI）を使うか、どんな処理を行うかなども書かれてる。MVCでいうCみたいなものか。

## View

Activityに配置するボタンやテキストフィールドといった各種パーツ。
.NETでいうところの「コントロール」だと思っておけば良い。

## Layout

Activityに各種Viewを配置するときの配置方法が定義されている。たとえば、上から順番に並べるとか、左から右へ並べる、あるViewを起点に相対位置に配置するとか。それとUIの配置情報が書かれたXMLファイル自体のことも指す。MVCでいうVみたいなもの。

AndroidStudioで空のActivityを作成すると、ConstraintLayoutなるものが選択されている。
これは読んで字のごとく、「制約によって配置を行う」レイアウト方法。Activityの左端から幾つ離すとか、特定のViewの下に何ピクセル離して配置するとか。

## Intent

他のアプリコンポーネントの処理を開始したい場合に相手へメッセージを送るためのもの。
別なActivityを利用したい場合（アプリ内で画面遷移させた場合など）は、Intentに遷移先Activityを指定し、putExtra()を使って送りたいメッセージを指定、startActivity()にIntentを渡すことでActivityが実行される。

Activityの実行結果を受け取りたい場合は、startActivityForResult()を使って起動すればOK。
相手のActivityからの応答は、onActivityResult()コールバック内で受け取ることが可能。

## Fragment

Activityに配置する各種Viewを再利用が可能なパーツとしてまとめてしまうもの。
複数ページにまたがって設定を行った上で処理を行うような、ウィザード形式のアプリなどの場合、各ページをActivityにしてしまうとデータの受け渡しが煩雑になってしまうので、各ページの情報はFragmentにまとめておいてActivity内でFragmentを切り替えることでウィザードっぽく見せる、といったことが可能になる。

今回、紙芝居風アプリを作るときに取り回しに一番苦戦した箇所でもある。精進あるのみ。

## ViewPager

1つのActivity内で複数のFragmentを切り替えて表示する際にActivity内に設置して利用する。
PagerAdapterとセットで利用する。

# よく使うもの

## リソースを参照する

Rというオブジェクト（？）に色々詰まってる

### Viewを取る場合

R.id.ほにゃらら。オブジェクトを取りたいならfindViewById()に渡す

```java
// APIレベル19 で作ってるからか「(Button) のキャストが冗長ですよ」って怒られるけどスルーしてても動く
Button button = (Button) findViewById(R.id.button);
```

### strings.xmlに書かれてる文字列

R.string.ほにゃらら。

```java
String app_name = R.string.app_name;
```

strings.xmlの置き場所：app/src/main/res/values

置き場所をvalues-jaというディレクトリにすると日本語環境の場合に表示される内容に置き換わる。これだけでローカライズも出来ちゃうんだから凄い。Railsにもあったような気がする。

## ボタンのクリックイベント

```java:Button_onClick
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // ここにやりたいことを書く
    }
});
```

Activity内に処理だけ書いて、layout.xmlに書く方法もある。
Android Studioのレイアウト編集画面で処理を紐付けるとこっちのやり方になる。

```java:MainActivity.java
public void button_onClick(View view) {
    // ここにやりたいことを書く
}
```

```xml:activity_main.xml
<Button
    android:id="@+id/button"
    android:onClick="button_onClick"
    ...
/>
```

（余談）
Fragment内に配置したボタンのイベントを記述したい場合は上のOnClickListenerを使った書き方を取ればFragment内に記述することが可能。通常はActivityに記述することになるらしいのだが、何だか気持ち悪い。Fragment内の持ち物なのでFragment内に記述するのがスマートな気がするので……

## Yes/Noを聞くダイアログ
```java:Confirm_Dialog
AlertDialog.Builder alert = new AlertDialog.Builder(this); // thisはActivityのインスタンス
alert.setTitle(R.string.app_name); // ダイアログにアプリ名を表示
alert.setMessage(R.string.confirm_message); // strings.xmlに書かれてるメッセージを取ってくる
// OKボタンが押されたときの処理
alert.setPositiveButton(R.string.ok_button_caption, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialogInterface, int i) {
        // OKボタンが押されたときにやりたいこと
    }
});
// NGボタンが押されたときの処理
alert.setNegativeButton(R.string.cancel_button_caption, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialogInterface, int i) {
        // NGボタンが押されたときにやりたいこと
    }
});
alert.show();
```

## Toastを表示する

ユーザからの返答が不要なメッセージを表示する。
「トースト」といっても焼いた食パンのことではなく、「乾杯！」とか「Cheers!」といった短い挨拶的なもののことを指すらしい。Messageでは駄目だった理由が知りたい。

```java:Toast
// MainActivityが持ってるメソッドの場合、例：onStart
@Override
protected void onStart() {
    super.onStart();
    // makeText()の第3引数はToastの表示時間
    Toast.makeText(this, "起動しましたよ", Toast.LENGTH_LONG).show();
}

// OnClickListenerの中からとか使いたい場合
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Toast.makeText(MainActivity.this, "ボタン押した", Toast.LENGTH_LONG).show();
    }
});
```

インスタンスを生成して、あれこれオプションを設定することも可能

```java:Toast2
// インスタンス生成にはnewではなくmakeText()を使う
Toast toast = Toast.makeText(...);
// 表示時間（makeText()の第3引数と同じ意味）
toast.setDuration(int duration)
// 文字列設定
toast.setText(CharSequence charSequence)
// 文字列設定#2 リソース（R）から取得して表示するものらしい
toast.setText(int resourceId)
// 表示位置
toast.setGravity(int gravity, int xOffset, int yOffset)
// 画面縦中央左寄せ
toast.setGravity(Gravity.CENTER | Gravity.LEFT, 0, 0);
```

## FragmentをActivity内でクルクル切り替える

MainActivity内にpagerというidでViewPagerを設置、そこにFragment1、Fragment2を表示させる。

Fragmentを容易に追加できるようにFragmentPagerAdapterをちょっと拡張

```java:CustomFragmentPagerAdapter.java
public class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragments = new ArrayList<Fragment>();
    
    public CustomFragmentPagerAdapter(FragmentManager manager) {
        super(manager);
    }
    
    public void addFragment(Fragment fragment) {
        mFragments.add(fragment);
        notifyDataSetChanged();
    }
    
    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }
    
    @Override
    public int getCount() {
        return mFragments.size();
    }
}
```

MainActivity内で各FragmentをViewPagerへ紐付ける

```java:MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.id.activity_main);
    
    // Fragmentの紐付け
    // getFragmentManager() と getSupport～ の2種類のバージョンがある
    // 前者だと上手く動かなった。getSupportだと何故上手く行くのかまで勉強が追いついてない(´・ω・｀)
    FragmentManager manager = getSupportFragmentManager();
    final CustomFragmentPagerAdapter adapter = new CustomFragmentPagerAdapter(manager, this);
    adapter.addFragment(new Fragment1());
    adapter.addFragment(new Fragment2());
    ViewPager pager = (ViewPager) findViewById(R.id.pager);
    pager.setAdapter(adapter);
}
```

これで、1つのActivityの中で2つの画面が表示可能、かつ左右へのスワイプでページ送りまで搭載される。

ひとまずここまで。