[컴][안드로이드] ListView 에서 getView() 사용하기

Adapter 의 getView()에 대한 설명

getView()

getView()는 Adapter 가 가지고 있는 data 를 어떻게 보여줄 것인가를 정의하는데 쓰인다. ListView 를 예를 들면 하나의 list item 의 모양을 결정하는 역할을 하는 것이다.

getView() 의 설명과 사용법은 [ref. 2] 에 정리가 잘 되어 있고, source code 도 제공한다. 그러므로 영어에 부담이 없다면 ref.2 를 참조하면 좋을 듯 하다. 여기서는 대략적으로 정리만 하려한다.

ref.2 에서 얘기하듯이 ListView 가 각각의 list item 을 그릴때 Adapter에게 어떻게 그려야 할 지 묻는다. 그때 사용하는 함수가 getView() 가 되겠다.

ref. 2 에서는 이 getView() 를 이용해서 view 의 재사용 하는 법에 대한 자세한 이야기를 해준다.

ref.2 의 코드를 조금 간단하게 만들고 아래에 옮겨놨다. 아래의 코드는 ListView 에 getView() 로 listItem 을 만들어서 보여주는 것이다.


package com.muchart;

import java.util.ArrayList;

import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import android.widget.BaseAdapter;
import android.widget.TextView;


//--------------------------------------------------------
public class MainActivity extends ListActivity {

 private CustomAdapter mAdapter;
 
 //--------------------------------------------------------
 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        mAdapter = new CustomAdapter();
        for (int i = 1; i < 50; i++) {
            mAdapter.addItem("item " + i);
            if (i % 4 == 0) {
                //mAdapter.addSeparatorItem("separator " + i);
            }
        }
        setListAdapter(mAdapter);
    }
 
 
 //--------------------------------------------------------
 private class CustomAdapter extends BaseAdapter{
  
  /**
   * layout inflator
   * 
   * Instantiates a layout XML file into its corresponding View objects.
   * @ref : http://developer.android.com/reference/android/view/LayoutInflater.html
   */
  private LayoutInflater mInflater;
  private ArrayList<String> mData = new ArrayList<String>();

  
  //--------------------------------------------------------
  public CustomAdapter() {
   mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  }

  
  //--------------------------------------------------------
  public void addSeparatorItem(String string) {
   // TODO Auto-generated method stub
   
  }
  
  //--------------------------------------------------------
  public void addItem(String string) {
   mData.add(string);
   notifyDataSetChanged();
  }
  
  
  //--------------------------------------------------------
  @Override
  public int getCount() {
   return mData.size();
  }


  //--------------------------------------------------------
  @Override
  public Object getItem(int position) {
   return mData.get(position);
  }


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


  //--------------------------------------------------------
  @Override
  public View getView(int pos, View convertView, ViewGroup parent) 
                {
                    TextView textView = null;
   
                    if(convertView == null){
                       convertView = mInflater.inflate(R.layout.item1, null);
                       textView = (TextView)convertView.findViewById(R.id.text);
                       convertView.setTag(textView);
                     }
                     else
                     {
                       textView = (TextView)convertView.getTag();
                     }
   
                     textView.setText(mData.get(pos));
   
                     return convertView;
  }

  
 }
 
}

참고로, ListActivity 는 기본적으로 화면 가운데에 한개의 full-screen list 로 구성된 layout 을 갖는다. [ref. 1] constructor 에서

super.onCreate(savedInstanceState);

를 호출할 때 설정되는 듯 하다.

그래서 기본적으로 setListAdapter() 만 설정해 주는 것으로 ListView 를 완성할 수 있다.

여기서 핵심은 getView() 이기 때문에 getView()에 대한 이야기만 조금 하자면,


//--------------------------------------------------------
@Override
public View getView(int pos, View convertView, ViewGroup parent) {
 TextView textView = null;
 
 if(convertView == null){
// item1.xml 을 view object 에 넣어준다.
  convertView = mInflater.inflate(R.layout.item1, null);
  
// text 라는 id 를 가져다가 convertView 에 달아준다.
// setTag() 가 tag 의 용도도 있지만, 그냥 특정 data 를 붙여놓는 용도로도 쓰인다.
  textView = (TextView)convertView.findViewById(R.id.text);
  convertView.setTag(textView);
 }
 else
 {
// convetView 가 null 이 아닌 경우에는
// convertView 에 붙여놓았던 textView 를 
// 그냥 가져다 쓰면 된다.
  textView = (TextView)convertView.getTag();
 }
 
 textView.setText(mData.get(pos));
 
 return convertView;
}

convertView 는 처음에 null 이다. 그런데 처음 만들어진 convertView(편의상 cv1 이라 하자.) 가 화면 밖으로 사라지면, 이 cv1 을 Recycler 라는 곳에 넣어뒀다가, 새롭게 보여지는 item 이 getView() 를 호출할 때 convertView 가 cv1 을 가지고 있게 된다.[ref.2]



여러 type 의 item 이 있을 때

이 때 View 의 종류가 여러 개일 수도 있다. 이때를 위해
  • getItemViewType()
  • getItemViewTypeCount()

를 override 하고, getView() code 에서 getItemViewType() 에 따라 convertView 를 할당해준다면, 나머지는 알아서 해결 해 준다.

AbsListView()에서 getView() 와 관련된 내용


// view 가 추가될때
void addScrapView(View scrap, int position) 
  mScrapViews[viewType].add(scrap);
// view count 를 mViewTypeCount 에 할당
RecycleBin mRecycler
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
  mViewTypeCount = mAdapter.getViewTypeCount()
// view 를 얻어 올 때

View obtainView(int position, boolean[] isScrap)
  scrapView = mRecycler.getScrapView(position);
    // --> getScrapView()
    if (mViewTypeCount == 1) {
      return retrieveFromScrap(mCurrentScrap, position);
    } else {
      int whichScrap = mAdapter.getItemViewType(position);
      if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
        return retrieveFromScrap(mScrapViews[whichScrap], position);
      }
    }
    //<--
  if (scrapView != null)
    child = mAdapter.getView(position, scrapView, this);

ref. 2 에서 example code에서 확인할 수 있다.


getView() 를 사용할 때 주의할 점

결론부터 이야기 하면, 화면에 보이지 않는 item 을 사용하면 null 이 return 되니 주의하라는 이야기이다.

코딩을 하다 보니, 아래 그림과 같은 구조의 ListView 를 사용하게 되었다. 그런데 아래와 같은 코드에서 종종 어플리케이션이 죽었다.

LinearLayout ll = (LinearLayout)getChildAt(mCurPage);
TextView titleTextView = (TextView)ll.findViewById(R.id.title);

mToast.setText(titleTextView.getText());

원인은 titleTextView 가 null 이어서 인데, 아래 그림과 같은 경우에 발생했다.

image

그림에서 처럼 맨 위에 TextView 가 있고, 이녀석의 id 가 title 이다. 그런데 scroll 을 하게 되면, 이녀석이 사라지고, 결국 id 가 title 인 녀석도 ListView 내에서 존재하지 않게 되었다. 그렇기 때문에 그상태에서

LinearLayout ll = (LinearLayout)getChildAt(mCurPage);
TextView titleTextView = (TextView)ll.findViewById(R.id.title);

를 하면 결국 R.id.title 을 찾지 못하고 null 을 return 하는 것이었다. 그러므로 ListView 의 item 을 호출해서 사용할 때는 화면에 보이지 않는 경우에 대한 고려를 하고 사용해야 할 듯 하다.


Reference

  1. http://developer.android.com/reference/android/app/ListActivity.html
  2. HowTo: ListView, Adapter, getView and different list items’ layouts in one ListView
  3. http://www.youtube.com/watch?v=wDBM6wVEO70, 5분부터 보면 된다.
  4. http://stackoverflow.com/questions/10160475/when-getview-in-arrayadapter-is-called
  5. http://android-codes-examples.blogspot.kr/2011/03/customized-listview-items-selection.html
  6. State List of Drawables
  7. Color State List

댓글 없음:

댓글 쓰기