2014年11月14日金曜日

Android ListViewとViewPager

ListViewとViewPager


●2つの特殊なView


□ListView

縦にスクロールするView。ListView自身には、一覧の中身を管理する機能が無い。
Adapterを用いて、データソースの管理とデータのViewへのBindを行い、スクロール位置に合わせたデータをAdapterから取得する仕組み。この仕組みは、UI作成の際の汎用的な手法である。

リストデータは、Listインターふぇおーすを実装したデータソースや、DBへ問い合わせ結果のデータソースも可能。

□ListAdapter

データをbindして表示する際に、ListAdapterインターフェースを実装したAdapterを使用する。
Adapterにはいくつかの種類が提供されている。


  • BaseAdapter:共通基底クラス。ListViewとSpinnerで使用可能。ListViewでは、ListAdapter用のinterfaceを実装する。
  • ArrayAdapter:配列やリストデータソースをBindする。
  • CursorAdapter:データベースへの問い合わせ結果をBindする。
  • SimpleCursorAdapter:データベースへ問い合わせた結果をViewにBindするための簡易Adapter



●ListViewの表示

以下の2つだけで、ListViewへの表示ができる。


  • ListViewをレイアウトxmlに配置
  • ListViewにAdapterをセットする


レイアウトのex)

<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="match_parent"
    tools:context=".MainActivity" >
    <!-- ListViewを配置 -->
    <ListView ←★これ
        android:id="@+id/ListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" /
</RelativeLayout>

□レイアウトxmlを使うActivity側では、以下の様にAdapterを用意してBindする。


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main); ←★ListViewを定義したレイアウトxml
    mActivity = this;
    // ListViewに表示するデータを作成する ←★ListViewに表示するデータ
    ArrayList<String> list = new ArrayList<String>();
    for (int i = 0; i < 20; i++) {
        list.add("hoge" + i);
    }
    ListView listView = (ListView) findViewById(R.id.ListView);
    // android.R.layout.simple_list_item_1はAndroidで既に定義されているリストアイテムのレイアウトです
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(mActivity,
            android.R.layout.simple_list_item_1, list); ←★Adapterの生成
    listView.setAdapter(adapter); ←★Bind
   
   
    // タップした時の動作を定義する
    listView.setOnItemClickListener(new OnItemClickListener() { ←★タップのリスナーを設定
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            // Adapterからタップした位置のデータを取得する
            String str = (String) parent.getItemAtPosition(position); ←★タップしたPositionなどの情報を取得可能
            Toast.makeText(mActivity, str, Toast.LENGTH_SHORT).show();
        }
    });
}


●カスタマイズしたリストアイテムの表示


以下が必要な作業。


  • リストアイテムのレイアウトxmlに配置
  • 独自アダプタの作成


□リストアイテムのレイアウトxml


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" ←★ここではRelative(横)レイアウトを使う
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="4dp" >
    <ImageView ←★各行に表示する画像
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/ic_launcher" />
    <TextView ←★各行に表示するテキスト
        android:id="@+id/TitleText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/imageView1" ←★imageViw1の右に配置
        android:textSize="18sp" />
    <TextView ←★
        android:id="@+id/SubTitleText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/TitleText"←★TitleTextと左端をあわせる
        android:layout_alignParentRight="true"
        android:layout_below="@+id/TitleText" /> ←★TitleTextの下に配置
</RelativeLayout>

□独自Adapterの作成


public class CustomListItemAdapter extends ArrayAdapter<String> { ←★ArrayAdapterを継承
    private LayoutInflater mLayoutInflater;
    public CustomListItemAdapter(Context context, List<String> objects) {
        // 第2引数はtextViewResourceIdとされていますが、カスタムリストアイテムを使用する場合は特に意識する必要のない引数です
        super(context, 0, objects); ←★表示するデータ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) {
            // レイアウトファイルからViewを生成する
            view = mLayoutInflater.inflate(R.layout.custom_list_item, parent, false);
        } else {
            // レイアウトが存在する場合は再利用する
            view = convertView;
        }
        // リストアイテムに対応するデータを取得する
        String item = getItem(position);
        // 各Viewに表示する情報を設定
        TextView text1 = (TextView) view.findViewById(R.id.TitleText);
        text1.setText("Title:" + item);
        TextView text2 = (TextView) view.findViewById(R.id.SubTitleText);
        text2.setText("SubTitle:" + item);
        return view;
    }
}


●パフォーマンス view holder デザインパターン


findViewByIdメソッドを頻繁に呼び出すとパフォーマンスが低下する。その場合、view holderデザインパターンを利用する。

□Adapter パターン

【参考】少し古い資料ですが、丁寧でわかりやすいのでおすすめ。
http://www.slideshare.net/yanzm/adapter-listview-expandalbelistview
Adapterもデザインパターンの1つである。
メリット
・既存のクラスに修正不要
・インターフェースを変更可能
・ListViewのadapterは委譲のパターン

□view holder パターン

【参考】使い方などの丁寧な説明。
http://y-anz-m.blogspot.jp/2010_08_01_archive.html

findViewByIdでviewを探す処理を削減することで、パフォーマンスを改善することができる。


●ViewPager

横にフリックしてViewを切り替えるためのView

複数のFragmentを切り替える用途にも使用可能
SupportPackageで提供される機能

□ViewPagerの管理

Adapterを使用する。

以下が必要な作業。


  • ①ViewPagerをレイアウトxmlに配置
  • ②独自PagerAdapterを実装
  • ③ViewPagerにAdapterをセットする


①レイアウトxml

レイアウト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="match_parent"
    tools:context=".MainActivity" >
    <android.support.v4.view.ViewPager ←★SupportPackageで提供されるViewPager
        android:id="@+id/Pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

②PaerAdapterの実装

PagerAdapterにimplementsが必要なメソッドは以下のとおり。

Object instatiateItem(ViewGroup container, int position); ← ★ページの生成
void destroyItem(ViewGroup container, int position, Object object); ← ★ページの削除
int getCount(); ← ★ページ数を返却
boolean isViewFromObject(View view, Object object); ← ★instantiateItemで返却されたキーObjectとページが関連づいているかどうかを確認。


ex)
class SamplePagerAdapter extends PagerAdapter {
private static final int PAGE_COUNT = 5; ← ページ数
private Context mContext;
public SamplePagerAdapter(Context context) {
    super();
    mContext = context;
}

@Override
public Object instantiateItem(ViewGroup container, int position) { ←★ページの生成
    // TextViewを生成
    TextView textView = new TextView(mContext);
    textView.setText("Position:" + position);
    //コンテナに追加
    container.addView(textView);
    // ここではTextView自体をキーとして返しています←★Adapter内のuniqueな識別子として利用
    return textView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) { ←★ページ削除
    // viewの削除
    // objectはinstantiateItemで返却したオブジェクトです
    container.removeView((View) object);
}
@Override
public int getCount() { ← ★ページ数
    // ページ数を返します。今回は固定値としています。
    return PAGE_COUNT;
}
@Override
public boolean isViewFromObject(View view, Object object) { ←★instantiateItemのレスうポンスObjectとページが等しいかどうか?
    // キーが正しいかを判定
    return view == (TextView) object;
}

③ViewPagerにAdapterを設定する


MainActivityでViewPagerを生成し、Adapterを設定する。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager pager = (ViewPager) findViewById(R.id.Pager);
    pager.setAdapter(new SamplePagerAdapter(this));
}


●FragmentPagerAdapter

Fragmentの切り替えで使うケースが多い。全てのページをメモリで保持する。

メモリで保持しきれない数のページがある場合、FragmentStatePagerAdapter を使用する。

以下が必要な作業

  • ①ViewPagerをレイアウトxmlに配置
  • ②Fragmentを作成
  • ③独自FratmentPagerAdapterを実装
  • ④ViewPagerにAdapterをセットする



①Fratmentのレイアウト(fragment_main.xml)


ex)

<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"
    android:background="@android:color/black" >
    <TextView
        android:id="@+id/TextView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="4dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:textSize="90sp"
        android:textStyle="bold" />
</FrameLayout>


②SampleFragment(fragmentクラス)


ex)

public class SampleFragment extends Fragment {
    public static SampleFragment newInstance(int position) { ←★ページのpositionをいれておく
        SampleFragment sampleFragment = new SampleFragment();
        Bundle bundle = new Bundle();
        bundle.putInt("position", position);
        sampleFragment.setArguments(bundle);
        return sampleFragment;
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Bundle bundle = getArguments();
        int position = 0;
        if (bundle != null) {
            position = bundle.getInt("position");
        }
        View view = inflater.inflate(R.layout.fragment_main, container, false); ←★Viewにfragmentのレイアウトを設定
        TextView text = (TextView) view.findViewById(R.id.TextView1);
        text.setText(String.valueOf(position));
        return view;
    }
}


③独自FragmentPagerAdapter SampleFragmentPagerAdapter


implementsが必要なデータ

  • Fragment getItem(int position); ←★Fragmentの生成
  • int getCount(); ←★ページ数を返す


ex)

public class SampleFragmentPagerAdapter extends FragmentPagerAdapter {
    private static final int PAGE_COUNT = 5; ←★ページ数を返す
    public SampleFragmentPagerAdapter(FragmentManager fm) {←★ FragmentPagerAdapterを受け取る
        super(fm);
    }
    @Override
    public Fragment getItem(int position) { ←★Fragmentを新しく生成する
        return SampleFragment.newInstance(position);
    }
    @Override
    public int getCount() { ←★ページ数を返す
        return PAGE_COUNT;
    }
}


④ViewPagerにAdapterをセットする


FragmentManagerを独自のFragmentPagerAdapterに渡す。
FragmentPagerAdapterをViewPagerに設定する。


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager pager = (ViewPager) findViewById(R.id.Pager);
    FragmentManager fm = getSupportFragmentManager();
    SampleFragmentPagerAdapter sampleFragmentPagerAdapter = new SampleFragmentPagerAdapter(fm);
    pager.setAdapter(sampleFragmentPagerAdapter);
}

0 件のコメント:

コメントを投稿