でもタイトルだけ表示しても面白くないので、ListViewをカスタマイズする
ListViewを制する者は、AndroidのView表示を制す!
というだけあっていろいろなテクニックが必要だった
ListViewの作り
ListViewに文字を表示させるには表示させたいデータをAdapterクラスに入れて、Apapterに設定してあるレイアウトを基にViewを組立て、ListViewに表示させる
既存のAdapterでリストをつくるとこうなる
// Viewの生成に必要 Context context = this.getApplicationContext(); // Adapterの設定 Adapterのコンストラクタ(Context,LayoutのID,リストID, リストデータ) String[] list = { "りんご", "ゴリラ", "ラッパ", "パイナップル" }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, android.R.layout.simple_expandable_list_item_1, list); // AdapterをListViewにセット ListView listView = new ListView(context); listView.setAdapter(adapter);「android.R.layout.simple_expandable_list_item_1」は既存のセル用レイアウトでTextViewが1個あるだけ
ArrayAdapterのコードを見てみると、レイアウト内のTextViewに文字列を入れる仕組みになってた
オリジナルのListViewを作るにはAdapterのレイアウトを組み立てる仕組みを作ってやればいい
Layoutの定義
表示するセルのレイアウトを作ってくプロジェクトフォルダ/res/layout/
定義は右や上のフォルダの位置にxmlファイルつくって描く ソースコード上でも描けるけど、この方が見やすいからねw 名前は何でもいいので、「list_aitem.xml」としておく。この名前はレイアウトのIDとして後々使用するので適当すぎると困る |
こんな感じで設定
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:id="@+id/week_view" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_gravity="center" android:layout_margin="10dip" android:textColor="#000000" android:textSize="50sp" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="10dip" android:background="#0FFFF0" android:orientation="horizontal" > <TextView android:id="@+id/time_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20sp" /> <TextView android:id="@+id/tv_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:textColor="#000000" android:textSize="20sp" /> </LinearLayout> <TextView android:id="@+id/title_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:singleLine="true" android:ellipsize="end" android:textColor="#000000" android:textSize="30sp" /> </LinearLayout> </LinearLayout>アニメタイトルのTextViewが問題
TextViewは列をはみ出そうとすると自動で改行を入れてくれる。しかし、ListViewでこれをやられると各セルの高さがばらばらになる
個人的にセルの高さは統一したいので、TextViewは一行で表示、はみだした分を…となるように設定する
色つけてる箇所がポイントで、
singLineを"true"で一行表記設定
ellipsize="end"で…の位置を設定
ちなみにellipsize属性に設定できる他のフラグはこんな感じ
フラグ | 効果 | 例 |
---|---|---|
end | 文末が… | アニメタイ… |
start | 文頭が… | …メタイトル |
middle | 文の途中が… | アニメ…トル |
none | …をしない | アニメタイト |
Adapter作り
上記コードでは既存のAdapterを使っているが、今回は自作したレイアウトにデータを詰め込む実装がいるのでBaseAdapterを継承してオリジナルAdapterを作っていくデータクラスとしてAnime.javaを作っとく。
とりあえずAnimeAPIから得られる情報一式をメンバ変数にする
class Anime { public String title, url, time, station, state, next, episode, cable, today, week; }BaseAdapterは抽象クラスで実装しないといけないクラスがいくつかある
この辺はだいたいテンプレ。特殊なことしないならこれでOK
/** * アニメ表示用Adapter. * */ public class AnimeAdapter extends BaseAdapter { private static final int LAYOUT_ID = R.layout.list_aitem; // 上記で定義したレイアウトのID private Anime[] list; // データリスト private Context mContext; // レイアウトIDからLayoutインスタンス取得に必要 /** コンストラクタ. */ public AnimeAdapter(final Context context, Anime[] animes) { this.mContext = context; this.list = animes; } @Override /** リストの数を取得 */ public int getCount() { return list.length; } @Override /** リストから特定のAnimeを取得.戻り値Anime型にしとくのがミソ */ public Anime getItem(int position) { return list[position]; } @Override /** リストのID取得(まぁリストの順番でOK) */ public long getItemId(int position) { return position; } @Override /** レイアウトにデータを設定する(一番したい処理) */ public View getView(int position, View convertView, ViewGroup parent) { //TODO 色々テクニックいるので下で書く return null; } }次に一番やりたかった処理。レイアウトにデータを詰めて表示する処理
- セル用レイアウトにコンバート セル用レイアウトにデータを突っ込む処理
- 高速処理 上記の方法でも処理できるが、毎回findViewById()をするとViewの呼び出し時間がかかってしまう難点がある
ListViewは生成されたとき全てのセルを作ってるわけではない。セルのViewは画面に出たときに生成されてる
そして、スクロールして新しいセルが表示されたとき、このメソッド(# getView())は呼ばれViewを生成する
引数は
・position | ⇒ | 表示すべきリストのポジション |
・convertView | ⇒ | ListViewで表示するセル用レイアウトのインスタンス |
・parent | ⇒ | よくわからん(今回使わんので無視) |
そこで必要なのがレイアウトのインスタンスを取得するLayoutInflaterクラス。システムからLayoutInflaterのインスタンスを呼び出し、# inflateメソッドでレイアウトを指定してレイアウトのViewのインスタンスを取得する
LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.list_aitem, null);そのレイアウトから各Viewのインスタンスを取得するには#findViewByIdメソッドをつかう
XMLで定義したIDを指定してViewのインスタンスを得て設定する
TextView textView = (TextView) convertView.findViewById(R.id.title_view);以上を行い、レイアウトを設定して戻り値に修正後のレイアウトを指定すればOK
@Override /** レイアウトにデータを設定する(一番したい処理) */ public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = (LayoutInflater) this.mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.list_aitem, null); } Anime anime = getItem(position); ((TextView) convertView.findViewById(R.id.title_view)).setText(anime.title); ((TextView) convertView.findViewById(R.id.time_view)).setText(anime.time); ((TextView) convertView.findViewById(R.id.week_view)).setText(anime.week.substring(0, 1)); ((TextView) convertView.findViewById(R.id.tv_view)).setText(anime.station); return convertView; }
それを回避するためにViewHolderという考え方を使う。レイアウトを使いまわす手法
セル内のそれぞれのViewの参照値あらかじめ持たせておき、そこのインスタンスにデータを突っ込む
@Override /** レイアウトにデータを設定する(一番したい処理) */ public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; // レイアウト ViewHolder holder; // ホルダー if (v == null) { LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = inflater.inflate(R.layout.list_aitem, null); // ホルダーを設定する holder = new ViewHolder(); holder.titleView = (TextView)v.findViewById(R.id.title_view); holder.timeView = (TextView)v.findViewById(R.id.time_view); holder.weekView = (TextView)v.findViewById(R.id.week_view); holder.tvView = (TextView)v.findViewById(R.id.tv_view); v.setTag(holder); }else{ // レイアウトからViewフォルダーを取得 holder = (ViewHolder)v.getTag(); } Anime anime = getItem(position); // Viewフォルダに情報詰め込む holder.titleView.setText(anime.title); holder.timeView.setText(anime.time); holder.weekView.setText(anime.week.substring(0,1)); holder.weekView.setBackgroundColor(color.get(anime.week)); holder.tvView.setText(anime.station); return v; } // ホルダークラス class ViewHolder { TextView titleView, timeView, weekView, tvView; }これでOK
0 件のコメント:
コメントを投稿