Tag Archives: 안드로이드

[Android] ProgressDialog 재사용시에 멈추는 현상 해결하기

제목에 재사용이라고 썼지만 사실 재사용을 안하는 이야기를 하려고 합니다-_-a 대부분의 책들이 ProgressDialog의 경우 재사용 하는 예제를 싣고 있고 물론 작동을 잘 하고 사용에 아무런 문제가 없습니다.

하지만 여기서 겪는 문제가 있습니다. 스타일을 ProgressDialog.STYLE_SPINNER 쓸때 보면 다이얼로그의 재사용시에 스피너가 회전을 하지 않습니다. 이것 참 난감하더군요.

여기에 대해 다양한 방법론이 제시되곤 합니다. 쓰레드등을 사용하여 비동기로 띄운다거나..하지만 잘 생각해 보면 그냥 재사용 안하면 됩니다; 우리 Dalvik VM의 GC를 믿어보는거죠;

[code]@Override
protected Dialog onCreateDialog(int id)
{
  switch(id)
  {
  case DEFAULT_PROGRESS_BAR:
    dlgProgress = new ProgressDialog(this);
    dlgProgress.setMessage(“Loading…”);
    dlgProgress.setIndeterminate(true);
    dlgProgress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    dlgProgress.setCancelable(false);
    return dlgProgress;
  }
 
  return super.onCreateDialog(id);
}[/code]
이제 다이얼로그를 띄울때는 다음과 같이 showDialog를 사용하여 띄우면 되겠죠.

[code]showDialog(DEFAULT_PROGRESS_BAR);[/code]
이제 다이얼로그를 닫을때 dismiss를 사용하실텐데 다음과 같이 removeDialog도 해주시기 바랍니다.
[code]dlgProgress.dismiss();
removeDialog(DEFAULT_PROGRESS_BAR);[/code]
이제 평소에 비해 좀 더 좋아보이시나요?

[Android] ListView 구현시에 뷰홀더(ViewHolder) 사용하기

 

대부분의 안드로이드 관련 책을 보면 ListView를 구현시에 Row를 캐시하는것에 대해 언급이 되어있습니다. 하지만 ViewHolder를 쓰는 방법에 대해서는 언급된 책이 별로 없더군요. 저도 지난번 안드로이드 개발자랩에 가서 이것의 존재를 알게 되었습니다;;

ViewHolder란, 이름 그대로 뷰들을 홀더에 꼽아놓듯이 보관하는 객체를 말합니다. 각각의 Row를 그려낼 때 그 안의 위젯들의 속성을 변경하기 위해 findViewById를 호출하는데 이것의 비용이 큰것을 줄이기 위해 사용합니다.

public class ForStudyAdapter extends BaseAdapter
{
  private Context mContext;
  private LayoutInflater mInflater;
  private ArrayList<Person> mItemList;
  private int mLayout;

  public ForStudyAdapter(Context context, int layout, ArrayList<Person> itemList)
  {
    this.mContext = context;
    this.mLayout = layout;
    this.mItemList = itemList;
    this.mInflater = (LayoutInflater) context.getSystemService
    (Context.LAYOUT_INFLATER_SERVICE);
  }

  @Override
  public int getCount()
  {
    return mItemList.size();
  }

  @Override
  public Object getItem(int position)
  {
    return mItemList.get(position);
  }

  @Override
  public long getItemId(int position)
  {
    return position;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent)
  {
    PersonViewHolder viewHolder;

    // 캐시된 뷰가 없을 경우 새로 생성하고 뷰홀더를 생성한다
    if(convertView == null)
    {
        convertView = mInflater.inflate(mLayout, parent, false);

        viewHolder = new PersonViewHolder();
        viewHolder.icon = (ImageView) convertView.findViewById(R.id.iconImage);
        viewHolder.name = (TextView) convertView.findViewById(R.id.name);
        viewHolder.address = (TextView) convertView.findViewById(R.id.address);
        viewHolder.phone = (TextView) convertView.findViewById(R.id.phone);

        convertView.setTag(viewHolder);
    }
    // 캐시된 뷰가 있을 경우 저장된 뷰홀더를 사용한다
    else
    {
        viewHolder = (PersonViewHolder) convertView.getTag();
    }

    viewHolder.name.setText(mItemList.get(position).getName());
    viewHolder.address.setText(mItemList.get(position).getAddress());
    viewHolder.phone.setText(mItemList.get(position).getPhone());

    return convertView;
  }
}

위에서 ViewHolder를 구현한 부분은 getView하나만 보시면 됩니다. 생성된 viewHolder의 경우 다음과 같이 전체가 public으로 구현된 간단한 클래스 하나면 됩니다.

public class PersonViewHolder
{
  public ImageView icon;
  public TextView name;
  public TextView address;
  public TextView phone;
}

여기서 조금 특이한 점은 대부분이 맴버변수는 private으로 선언한 뒤에 getter/setter를 사용하는 방식을 취하지 않고 맴버변수에 직접적으로 접근을 한다는 점입니다. [이글]을 참고해보시면 메서드내에서 맴버변수(필드)에 접근하는것조차 상대적으로 비용이 크다는 언급이 나옵니다.

결론적으로 실행에 드는 비용을 줄일려고 ViewHolder를 사용하므로 ViewHolder내에서도 메서드 호출의 숫자까지 줄이는것이 중요해 보입니다. 결론적으로 viewHolder에서 Row내의 요소 위젯들을 직접적으로 가지고 있으므로 바로바로 값을 변경할 수 있습니다.

실제로 안드로이드 개발자랩에서 보여준 데모에서는 많은 Row를 가진 ListView라도 매우 빠르게 동작하더군요.