うさがにっき

読書感想文とプログラムのこと書いてきます

ListViewでListごとに違うレイアウトを適用する方法

概要

ListViewのadapterでgone操作などにより、リストごとのレイアウトを変更しようとするとスクロール時にレイアウトが崩れることがある
その際にはレイアウトごとのViewHolderを用意して、異なるレイアウトごとに再利用する

詳細

ListViewのリストごとに違うレイアウトを使おうと以下のようなソースを書くことがある

if(convertView == null) {
	LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	convertView = inflater.inflate(R.layout.fragment_login_navigation_drawer_normal_cell, null);
	
	holderNormal = new ViewHolderNormal();
	holderNormal.title = (TextView)convertView.findViewById(R.id.navigation_drawer_title);
	holderNormal.icon = (ImageView)convertView.findViewById(R.id.navigation_drawer_image);
	
	convertView.setTag(holderNormal);
}else {
	holderNormal = (ViewHolderNormal)convertView.getTag();
}

if(position == 2) {
	holderNormal.icon.setVisibility(View.GONE);
}else {
	holderNormal.icon.setVisibility(View.VISIBLE);
}

これで困りどころなのが、うまくいくことがあれば、あまり複雑だとスクロール時にレイアウトが崩れてしまうことがあること
レイアウトが崩れてしまう場合、レイアウトごとにViewHolderを作成するとうまくいく

まずgetItemViewType、getViewTypeCountをOverride

	/*
	 * positionごとのViewのタイプを返す
	 * (non-Javadoc)
	 * @see android.widget.BaseAdapter#getItemViewType(int)
	 */
	@Override
	public int getItemViewType(int position) {
		// positionごとに適切な値を取得
		int resource = imageResource.get(position);
		
		switch (resource) {
		case SEPARATOR:
			return SEPARATOR;
		case RECOMMEND:
			return RECOMMEND;
		default:
			return NORMAL;
		}
	}

	/*
	 * adapterが対応するViewのタイプ数を返す
	 * (non-Javadoc)
	 * @see android.widget.BaseAdapter#getViewTypeCount()
	 */
	@Override
	public int getViewTypeCount() {
		return 3;
	}

各ViewHolder作成

	/**
	 * 通常ViewHolder 
	 */
	static class ViewHolderNormal {
		TextView title;
		ImageView icon;
	}
	
	/**
	 * separator用ViewHolder 
	 */	
	static class ViewHolderSeparator {
		LinearLayout separator;
	}

	/**
	 * recommend用ViewHolder 
	 */	
	static class ViewHolderRecommend {
		TextView recommendTitle;
		LinearLayout recommendSeparator;
	}

あとはgetView時に各positionの際ごとにViewHolderを使い分ける

	ViewHolderNormal holderNormal;
	ViewHolderSeparator holderSeparator;
	ViewHolderRecommend holderRecommend;

	
	switch (getItemViewType(position)) {
	case SEPARATOR:
		if(convertView == null) {
			LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			convertView = inflater.inflate(R.layout.fragment_login_navigation_drawer_separator_cell, null);
			
			holderSeparator = new ViewHolderSeparator();
			holderSeparator.separator = (LinearLayout)convertView.findViewById(R.id.navigation_drawer_separator);
			
			convertView.setTag(holderSeparator);
		}else {
			holderSeparator = (ViewHolderSeparator)convertView.getTag();
		}
		
		break;

	case RECOMMEND:
		if(convertView == null) {
			LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			convertView = inflater.inflate(R.layout.fragment_login_navigation_drawer_recommend_cell, null);
			
			holderRecommend = new ViewHolderRecommend();
			holderRecommend.recommendTitle = (TextView)convertView.findViewById(R.id.navigation_drawer_recommend_title);
			holderRecommend.recommendSeparator = (LinearLayout)convertView.findViewById(R.id.navigation_drawer_recommend_separator);
			
			convertView.setTag(holderRecommend);
		}else {
			holderRecommend = (ViewHolderRecommend)convertView.getTag();
		}
		
		holderRecommend.recommendTitle.setText("お気に入り作品");
		
		break;

	default:
		if(convertView == null) {
			LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			convertView = inflater.inflate(R.layout.fragment_login_navigation_drawer_normal_cell, null);
			
			holderNormal = new ViewHolderNormal();
			holderNormal.title = (TextView)convertView.findViewById(R.id.navigation_drawer_title);
			holderNormal.icon = (ImageView)convertView.findViewById(R.id.navigation_drawer_image);
			
			convertView.setTag(holderNormal);
		}else {
			holderNormal = (ViewHolderNormal)convertView.getTag();
		}
		
		holderNormal.icon.setImageResource(imageResource.get(position));
		holderNormal.title.setText(mContext.getResources().getString(titleResource.get(position)));
		
		break;
	}

	return convertView;

これで別々レイアウトのときもviewの再利用が出来るようになる