何がしたかった?
ListViewの一部でレイアウトを変更したかった.
極端な例を挙げると,リストには文字列がひとつあり,通常はセンタリングされている.
これを,positionが偶数のリストアイテムだけ,文字列を左寄せにしたいという感じ.
コード例
既にテキストをセンタリング済みのリストアイテム用ViewXML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textSize="18sp" />
</RelativeLayout>
レイアウトファイルをInflateするAdapter
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
// ViewHolder使っての前処理を省略
final ViewHolder holder = convertView.getTag();
if(position % 2 == 0) {
// positionが偶数ならテキストを左寄せ
RelateiveLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.text.getLayoutParams();
params.leftMargin = 10;
params.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
holder.text.setLayoutParams(params);
} else {
// positionが奇数ならテキストをセンタリングに戻す
RelateiveLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.text.getLayoutParams();
params.leftMargin = 0;
params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
holder.text.setLayoutParams(params);
}
return convertView;
}
結果
一見うまくいったように見えたのもつかの間.
めくるとpositionが奇数・偶数に関わらず,どんどん左寄せされて,センタリングされなかった.
どうやらListViewによるconvertViewが使い回され,ALIGH_PARENT_LEFTのルールが残り続けている.
問題点
LayoutParams.addRule の名のとおり,addRuleメソッドはルールを追加するものであり,上書きするものでは無かった.
そのため,positionが奇数時の処理において ALIGN_PANRET_LEFT のルールは残っており,その上でCENTER_HORIZONTALのルールが追加されただけだった.
解決
API Level 17の場合,RelativeLayout.LayoutParams.removeRule(int anchor) メソッドでルールを削除することができる.
params.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
API Level 17未満の場合は,以下の指定で削除を行う
params.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0);
addRuleを呼び出し,削除したいルールを第一引数に,削除するというアンカー0を第二引数に渡す.
元々指定用AnchorでRelativeLayout.TRUEという定数は用意されているが,FALSEは無い.
JavaDocを読むと,FALSEは0を指定しろと書いてあった.
/**
* Adds a layout rule to be interpreted by the RelativeLayout. Use this for
* verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
* value (VISIBLE).
*
* @param verb One of the verbs defined by
* {@link android.widget.RelativeLayout RelativeLayout}, such as
* ALIGN_WITH_PARENT_LEFT.
* @param anchor The id of another view to use as an anchor,
* or a boolean value(represented as {@link RelativeLayout#TRUE})
* for true or 0 for false). For verbs that don't refer to another sibling
* (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
* @see #addRule(int)
*/
基本的に一度inflateしたViewのLayoutParamsをいじることって無いので,なかなか抜け出せず,いやーハマったハマった…