2012年10月25日

【Android】ScrollView内にListViewを表示


ScrollViewの中にListViewを入れようとした人いますよね?
僕も入れようとしました。

でも、ScrollViewもListViewもスクロールするものですから、
ScrollView内にListViewは入れてはいけないんですよね。

でも、ListViewのように、コンテンツの数分の行数が欲しくて、
そのコンテンツの数が動的に変化するなんて場合はどうすればいいんだ〜。

そんな人の為のクラスを用意いたしました。

LinearLayoutにAdapterを設定して、
コンテンツを追加するたびに1行増やせるというものです。

public class LinearListView extends LinearLayout {
 public static final int CHOICE_MODE_NONE = 0;
 public static final int CHOICE_MODE_SINGLE = 1;
 public static final int CHOICE_MODE_MULTIPLE = 2;

 private Adapter adapter;
 private Observer observer;
 private int choiceMode;
 private OnItemClickListener clickListener;
 private OnItemLongClickListener longClickListener;
 private OnItemCheckListener checkListener;
 private SparseBooleanArray checkStates;

 {
  observer = new Observer(this);
  checkStates = new SparseBooleanArray();
  choiceMode = CHOICE_MODE_NONE;
 }

 public LinearListView(Context context) {
  super(context);
 }

 public LinearListView(Context context, AttributeSet attrs) {
  super(context, attrs);
 }

 public void setChoiceMode(int choiceMode) {
  this.choiceMode = choiceMode;
 }

 public void setAdapter(Adapter adapter) {
  if (this.adapter != null)
   this.adapter.unregisterDataSetObserver(observer);
  this.adapter = adapter;
  adapter.registerDataSetObserver(observer);
  observer.onChanged();
 }

 public Adapter getAdapter() {
  return this.adapter;
 }

 public interface OnItemClickListener {
  void onItemClick(View view, int position, long id);
 }

 public interface OnItemLongClickListener {
  void onItemLongClick(View view, int position, long id);
 }

 public interface OnItemCheckListener {
  void onItemCheck(View view, int position, long id);
 }

 public void setOnItemClickListener(OnItemClickListener clickListener) {
  this.clickListener = clickListener;
 }

 public void setOnItemLongClickListener(
   OnItemLongClickListener longClickListener) {
  this.longClickListener = longClickListener;
 }

 public void setOnItemCheckListener(OnItemCheckListener checkListener) {
  this.checkListener = checkListener;
 }

 public void onItemClick(View view) {
  if (clickListener != null) {
   int position = indexOfChild(view);
   long id = adapter.getItemId(position);
   clickListener.onItemClick(view, position, id);
  }
 }

 public void onItemLongClick(View view) {
  if (longClickListener != null) {
   int position = indexOfChild(view);
   long id = adapter.getItemId(position);
   longClickListener.onItemLongClick(view, position, id);
  }
 }

 public void onItemCheckedChanged(View view, boolean checked) {
  if (choiceMode == CHOICE_MODE_NONE)
   return;

  int position = indexOfChild(view);

  if (choiceMode == CHOICE_MODE_MULTIPLE) {
   checkStates.put(position, checked);

  } else if (choiceMode == CHOICE_MODE_SINGLE) {
   checkStates.clear();
   if (checked) {
    checkStates.put(position, true);
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
     View v = getChildAt(i);
     if (v != view && v instanceof Checkable)
      ((Checkable) v).setChecked(false);
    }
   }
  }

  if (checkListener != null) {
   long id = adapter.getItemId(position);
   checkListener.onItemCheck(view, position, id);
  }
 }

 public void setItemChecked(int position, boolean checked) {

  if (choiceMode == CHOICE_MODE_NONE)
   return;

  if (choiceMode == CHOICE_MODE_MULTIPLE) {
   checkStates.put(position, checked);
   View view = getChildAt(position);
   if (view instanceof Checkable)
    ((Checkable) view).setChecked(checked);

  } else if (choiceMode == CHOICE_MODE_SINGLE) {
   checkStates.clear();
   if (checked) {
    checkStates.put(position, true);
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
     View view = getChildAt(i);
     if (view instanceof Checkable)
      ((Checkable) view).setChecked(i == position);
    }
   }
  }
 }

 public boolean isAnyItemChecked() {

  if (choiceMode == CHOICE_MODE_NONE)
   return false;

  int count = getChildCount();
  for (int i = 0; i < count; i++)
   if (checkStates.get(i))
    return true;

  return false;
 }

 public SparseBooleanArray getCheckStates() {
  return checkStates;
 }

 public int getCheckedItemPosition() {
  if (choiceMode == CHOICE_MODE_SINGLE && checkStates.size() == 1)
   return checkStates.keyAt(0);
  return -1;
 }

 public long getCheckedItemId() {
  if (choiceMode == CHOICE_MODE_SINGLE) {
   int position = getCheckedItemPosition();
   if (position != -1)
    return adapter.getItemId(position);
  }
  return -1;
 }

 private class Observer extends DataSetObserver {

  private LinearListView context;

  public Observer(LinearListView context) {
   this.context = context;
  }

  @Override
  public void onChanged() {

   int childCount = context.getChildCount();
   List<View> oldViews = new ArrayList<View>(childCount);
   for (int i = 0; i < childCount; i++)
    oldViews.add(context.getChildAt(i));
   Iterator<View> iterator = oldViews.iterator();

   int adapterCount = context.adapter.getCount();
   for (int i = 0; i < adapterCount; i++) {
    View convertView = (iterator.hasNext() ? iterator.next() : null);
    View view = context.adapter.getView(i, convertView, context);
    if (convertView == null) {
     context.addView(view);
     final int position = i;
     view.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
       if(clickListener != null) {
        context.clickListener.onItemClick(v, position, v.getId());
       }
      }
     });
     view.setOnLongClickListener(new OnLongClickListener() {
      @Override
      public boolean onLongClick(View v) {
       if(longClickListener != null) {
        context.longClickListener.onItemLongClick(v, position, v.getId());
       }
       return false;
      }
     });
    }
    if (choiceMode != CHOICE_MODE_NONE && view instanceof Checkable)
     ((Checkable) view).setChecked(checkStates.get(i));
   }

   if (childCount > adapterCount)
    context.removeViews(adapterCount, childCount - adapterCount);
  }

  @Override
  public void onInvalidated() {
   // context.removeAllViews();
  }
 }
}