woshidan's blog

あいとゆうきとITと、とっておきの話。

Mixi Android Training 第5回分の参考資料の演習についてメモ1

Mixi Android Training 第五回分の参考資料の演習をやっています。

参考資料の演習が一通り追いついたら、授業見ながらアプリ作っていくことに決めましたというか、 演習がそれなりに重いので、勉強会いけていたとしてもついていけていたのかな感が若干あります。

すごい授業ですね。

まとめ

この範囲の演習で大事そうだと思ったことを先にまとめ。

  • 地名等のコレクションのデータの一覧表示、みたいなことをやりたいときはListViewを使う
  • ListViewのxmlは一覧の中の一行分について作る
    • 一覧の一番上の部分、一番下の部分、Dividerみたいな部分についてはListViewのクラスが持っているっぽい
  • ListViewにデータをセットするにはArrayAdapter(TはAdapterで扱いたい任意のクラス)を使う
    • ListView#setAdapter(ArrayAdapter adapter)メソッドでListViewにAdapterを登録
    • Adapterクラスは、ListViewのxmlの情報を持っている
      • Adapterクラスには、データが与えられたとき、要素ごとにそのデータを埋め込んだxmlを返す
      • データの追加、削除はAdapterクラスの仕事

長くなったので、ListViewの5からは次に。

ListView

1. (実習)

カスタマイズしたリストアイテムを表示するで説明したプロジェクトを写経して実行してください。余裕がある場合はViewの再利用についてについても試してみて下さい。

この実習やってて、スクロールしたときに、Adapterに登録されているXMlを元に スクロールされたタイミングで動的にViewを追加しているのかな、ということを 思いました(コード追ってないんですが)。

2. (実習)

実習1の画面にボタンを配置し、ボタンをタップしたらListViewの先頭を表示するようにして下さい。

        Button button = (Button) findViewById(R.id.button);
        // ButtonのClickListenerにはClickListenerのインスタンスを渡す必要があった...忘
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                listView.smoothScrollToPosition(0);
            }
        });
// onCreate()の中でClickListenerを登録しているので、onDestroy()イベントの中で
// イベントリスナを外す必要があるのだけれど、
// これは、button.setOnClickListener(null);
// でよいのだっけ...

3. (課題)

以下のListViewを表示するように実装してください。 http://mixi-inc.github.io/AndroidTraining/assets/02-06/assignment/assignment_1_sample.png

ちょっと面倒くさかったので、Bookクラスは以下のように適当に作りました(結局、課題5では詰みまして、元のファイルを使用させていただく流れとなりました...。)

IDEいつまで経っても慣れて来なくてよくない感じですね。

public class Book {
    private String title;
    private String publisher;
    private Integer price;
    public Book(String title, String publisher, Integer price) {
        this.title = title;
        this.publisher = publisher;
        this.price = price;
    }
    public String getTitle() {
        return this.title;
    }
    public String getPublisher() {
        return this.publisher;
    }
    public Integer getPrice() {
        return this.price;
    }
}

この課題でやることは、

  • ListViewのxmlを書く
  • BookクラスのListAdapterを実装する
  • ListAdapterをMainActivityから呼び出してListViewにセットする

っぽいので、それぞれ書きます。

ListViewのXML

book_list_item.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:padding="10dp"
    tools:context=".MainActivity" >

    <ImageView
        android:id="@+id/Icon"
        android:src="@drawable/ic_launcher"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        />
    <TextView
        android:id="@+id/TitleText"
        android:layout_toRightOf="@id/Icon"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:textSize="20dp"
        />
    <TextView
        android:id="@+id/PriceText"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:textSize="18dp"
        />
    <TextView
        android:id="@+id/PublisherText"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_above="@id/PriceText"
        />
</RelativeLayout>
BookクラスのListAdapterを実装する

Adapterでやることは、

なので、以下のような感じで書きました。

BookListItemAdapter.java

public class BookListItemAdapter extends ArrayAdapter<Book> {

    @SuppressWarnings("unused")
    private static final String TAG = BookListItemAdapter.class.getSimpleName();

    private LayoutInflater mLayoutInflater;

    public BookListItemAdapter(Context context, List<Book> objects) {
        // 第2引数はtextViewResourceIdとされていますが、カスタムリストアイテムを使用する場合は、
        // 特に意識する必要のない引数です
        super(context, 0, objects);
        // レイアウト生成に使用するインフレーター
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View view = null;

        // ListViewに表示する分のレイアウトが生成されていない場合レイアウトを作成する
        if (convertView == null) {
            // レイアウトファイルからViewwを生成する
            view = mLayoutInflater.inflate(R.layout.book_list_item, parent, false);
        } else {
            // レイアウトが存在する場合は使い回す
            view = convertView;
        }

        // リストアイテムに対応するデータを取得する
        Book book = getItem(position);

        // 各Viewに表示する情報を設定
        TextView text1 = (TextView) view.findViewById(R.id.TitleText);
        text1.setText(book.getTitle());
        TextView text2 = (TextView) view.findViewById(R.id.PublisherText);
        text2.setText(book.getPublisher());
        TextView text3 = (TextView) view.findViewById(R.id.PriceText);
        text3.setText(book.getPrice() + "円");

        return view;
    }
}
ListAdapterをMainActivityから呼び出してListViewにセットする

おりゃー。

        // ListViewに表示するデータを作成する
        ArrayList<Book> list = new ArrayList<Book>();
        for (int i = 0; i < 20; i++) {
            list.add(new Book("タイトル" + i,"出版社" + i, i * 10));
        }

        final ListView listView = (ListView) findViewById(R.id.ListView);
        BookListItemAdapter adapter = new BookListItemAdapter(mActivity, list);
        listView.setAdapter(adapter);

4. (課題)

課題3で作成したListViewをタップしたら、次画面に遷移してタップしたアイテムの情報を表示するようにしてください。

  • 次の画面用のActivityを作る
  • クリックイベントをToastからIntentの送信に変える

みたいな感じで進めます。

前回分の復習で、Intentで次の画面に送信する時は、今のActivityと起動させたいActivityのクラスを指定して、 intentを作成し、そのintentをstartさせてやれば良い話でした。

Intent intent = new Intent(IntentActivity1.this, NewActivity.class);

今回は、自作のクラスのオブジェクトをExtraにつけて送りたいので、一工夫する必要がありました。 (なお、次の回分の資料を読んで、プロセス間通信にはSerializableよりPercelableのほうがいいことが判明し...)

// Book.java
import java.io.Serializable;
public class Book implements Serializable {

クリックイベントのリスナを書き換えます。

// クリックリスナ内
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    // Adapterからタップした位置のデータを取得する
    Book book = (Book) parent.getItemAtPosition(position);
    Intent intent = new Intent(MainActivity.this, BookItemActivity.class);
    intent.putExtra("book", book);
    startActivity(intent);
}

遷移先画面のonCreate()イベントで。

// BookItemActivity#onCreate()
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_book_item);
    Book book = (Book) getIntent().getSerializableExtra("book");
    TextView title = (TextView) findViewById(R.id.title);
    title.setText(book.getTitle());
}