안드로이드 교육을 받고 프로젝트와 함께 사용소스를 블로그에 올릴려고 했는데 늘 그렇듯이 생활하다보니 흐지부지 되어서 그 중 객체바인딩을 상속하여 재수정한 소스를 올린다.

ListView 컨트롤에 객체를 바인딩하는데 사용되는 데이터소스는 객체 리스트와 데이터베이스가 있다.
객체 리스트 바인딩은 ArrayAdapter 객체를 이용하고 데이터베이스 바인딩은 SimpleCursorAdapter 를 사용한다.

이들을 사용하게 되면 다음과 같은 문재점이 있어 이를 재사용할 방법이 있나 싶어 구현해 보아 사용해 보았다.

  • 일일히 각각의 뷰에 어댑터를 구현을 해 놓아야 해서 다양한 리스트 뷰페이지 많을 경우, 그에 따라 정의되는 어댑터의 수가 많아져서 불필요한 코드가 생산된다.
  • 객체를 바인딩할 경우 ListView 아이템의 여러 뷰에 구현하기 위해서 별도의 어댑터를 구현해야 한다.

먼저 객체목록 소스로 바인딩하는 클래스로 ArrayAdapter 를 재사용하기 위해 모든 객체를 java의 Generics를 사용하여 구현하기로 결정했다.
그리고 객체를 각각의 뷰컨트롤에 바인딩을 하기 위해 리스트를 정의하고 선언하여 각 페이지 뷰에 데이터를 구현하게 했다.

먼저 소스를 보면,

public class MultiBindingAdapter<T> extends ArrayAdapter<T> {

 private int _resourceId;
 private OnDataItemBindListener<T> _dataItemBindListener;
 
 
 public MultiBindingAdapter(Context context, int resourceId, List<T> array){  
  super(context, resourceId, array);
  
  this._resourceId = resourceId;
 }
 
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {

  View v = convertView;   
  if( v ==null ){
   LayoutInflater vi = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   v = vi.inflate(this._resourceId, null);
  }
  
  if(_dataItemBindListener != null)
   _dataItemBindListener.onItemBind(v, this.getItem(position));
  
  return v;
 }
 
 public void setOnDataItemBindListener(OnDataItemBindListener<T> listener){
  _dataItemBindListener = listener;
 }
}

여기서 바인딩하는 핵심 함수는 getView() 인데 여기에서 ListView의 각 아이템 항목을 바인딩한다.

이를 사용하는 샘플소스는      

List<Post> list = PostFactory.getInstance().loadList(PostListView.this, mCategoryId, reload);
     
MultiBindingAdapter<Post> postBindingAdapter =
   new MultiBindingAdapter<Post>(this, R.layout.listitemlayout, list);

postBindingAdapter.setOnDataItemBindListener(new OnDataItemBindListener<Post>(){
      @Override
      public void onItemBind(View v, Post data) {
       super.onItemBind(v, data);
       
       TextView tvTitle = (TextView)v.findViewById(R.id.tvListTitle);
       if(tvTitle != null)
        tvTitle.setText(data.getTitle());
       
       TextView tvSummary = (TextView)v.findViewById(R.id.tvListSummary);
       if(tvSummary != null)
        tvSummary.setText(data.getSummary());       
      }
});
     
setListAdapter(postBindingAdapter);

글 목록을 바인딩하는 샘플인데 List<Post> 객체를 ListView의 각 아이템을 바인딩하여 사용하였다.


다음으로 데이터베이스를 소스로 바인딩하는 클래스로 SimpleCursorAdapter 를 상속하여 구현하였다.

public class MultiBindingCursorAdapter extends SimpleCursorAdapter {
 
 private int _resourceId;
 private OnDataItemBindListener<Cursor> _dataItemBindListener;

 public MultiBindingCursorAdapter(Context context, int layout, Cursor c,
   String[] from, int[] to) {
  super(context, layout, c, from, to);

  this._resourceId = layout;
 }
 
 @Override
 public View newView(Context context, Cursor cursor, ViewGroup parent) {
  
  if( _dataItemBindListener != null){
   LayoutInflater inflater = LayoutInflater.from(context);
   View view = inflater.inflate(this._resourceId, parent, false);
   this._dataItemBindListener.onItemBind(view, cursor);   
     
   return view;
  }
  
  return super.newView(context, cursor, parent);
 }
 
 @Override
 public void bindView(View view, Context context, Cursor cursor) {
  super.bindView(view, context, cursor);
 }
 
 public void setOnDataItemBindListener(OnDataItemBindListener<Cursor> listener){
  this._dataItemBindListener = listener;
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) { 
  return super.getView(position, convertView, parent);
 }
}

여기에서 핵심 메소드는 newView()인데 구현방법은 기존에 구현했던 방법과 유사하다.

이를 사용하는 샘플소스는

Cursor cursor = this.mAdapter.getDocuments(this.mCategoryId);
this.startManagingCursor(cursor);

MultiBindingCursorAdapter ad =
    new MultiBindingCursorAdapter(this, R.layout.documentitemlayout, cursor, new String[] {}, new int[] {});

ad.setOnDataItemBindListener(new OnDataItemBindListener<Cursor>(){
    @Override
    public void onItemBind(View v, Cursor cursor){
     TextView tv = (TextView)v.findViewById(R.id.itemTitle);
     if(tv != null)
      tv.setText(Util.getStringByCursor(cursor, DocumentDbAdapter.DOCUMENTS_TITLE));
     
     RatingView rating = (RatingView)v.findViewById(R.id.itemRating);
     if(rating != null){
      rating.setRating(Util.getIntByCursor(cursor, DocumentDbAdapter.DOCUMENTS_IMPORTANT));
      rating.setReadable(true);
     }

     TextView regDate = (TextView)v.findViewById(R.id.itemRegDate);
     if(regDate != null)
      regDate.setText(Util.getDateByCursor(cursor, DocumentDbAdapter.DOCUMENTS_REGDATE).toLocaleString());
    
    }
});
   
this.setListAdapter(ad);

이 샘플도 MultiBindingAdapter<T>와 유사해서 특별한 설명을 줄인다.
(퇴근해야 해서...)

아 그리고 마지막으로 리스너를 정의부분이 남았다. 물론 이 리스너도 Generics 를 사용했다.
간단해서 굳이 쓸 필요는 없지만.. 그래도 남긴다.

public class OnDataItemBindListener<T> {
 public void onItemBind(View v, T data){
  }
}


 

Posted by hgjung

댓글을 달아 주세요

  1. 정승현 2011/01/21 11:33  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 안드로이드에 대해서 검색을 통해 공부하던 중 여기까지 왔네요
    유용한 정보 감사드립니다. 죄송하지만 OnDataItemBindListener란 클래스에 대해서 좀 알수 있을까요?
    부탁드립니다

  2. Favicon of http://blog.hgjung.pe.kr hgjung 2011/02/07 01:07  댓글주소  수정/삭제  댓글쓰기

    제가 댓글 확인을 이제야 했네요.
    제가 자바쪽을 자주 하지 않아서 이상한 점 있으면 많이 알려주세요.
    ^^