CursorAdapter 사용

CursorAdapter?

  • cursor를 사용하여 AdapterView에 데이터 표시
  • cursor를 Adapter에 직접 전달
  • 테이블에 반드시 _id컬럼이 만들어져 있어야 함

 

SimpleCurosrAdapter 사용

  • 기본 제공 Adapter
  • 안드로이드 제공 기본 레이아웃을 사용하여 cursor 데이터 출력
SimpleCursorAdpater adapter = new SimpleCursorAdapter(
    /* context, MainActivity안에 있다면 this로 MainActivity 자신을 넣어주면 됨
    MainActivity가 context를 상속받은 클래스이기 때문에 context 역할을 함 */
    this, 
    android.R.layout.simple_list_item_1, 
    // DB 검색 결과
    cursor, 
    // 표시할 컬럼
    new String[] { ContactDBHelper.COL_NAME },
    // 기본 레이아웃에 포함되어 있는 뷰 아이디
    new int[] { android.R.id.text1 }, 
    CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

 

SimpleCurosrAdapter 사용 예시

ListView listview = null;
ContactDBHelper helper;
Cursor cursor; // 멤버로 선언! cursor가 adapter에 직접 연결되기 때문에 cursor가 사라지면 어댑터 화면 못 만듦
SimpleCursorAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_all_contacts);
	lvContacts = (ListView)findViewById(R.id.lvContacts);

	helper = new ContactDBHelper(this);
    
        adapter = new SimpleCursorAdapter (this, android.R.layout.simple_list_item_1, 
        /* onCreate이므로 Activity가 만들어질 때 전체화면 읽어내고 그 이후 실행 x
        cursor를 일단 비워두고 내용을 채우는 작업은 onResume에서 */
        null,
        new String[] { ContactDBHelper.COL_NAME }, new int[] { android.R.id.text1 },
        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        
	lvContacts.setAdapter(adapter);
    } // 이 시점까지 화면에 보여지는 것 x
  
@Override
protected void onResume() { // 화면에 보이기 직전에 호출
    super.onResume();
    // DB에서 데이터를 읽어와 Adapter에 설정
    SQLiteDatabase db = helper.getReadableDatabase();
    cursor = db.rawQuery("select * from " + ContactDBHelper.TABLE_NAME, null);

    adapter.changeCursor(cursor); // null로 되어 있는 cursor항목을 지금 얻은 cursor로 채우기
    helper.close();
    // cursor를 close시키지 않는 이유는 cursor가 닫히면 화면에 있는 연결 정보가 날아가기 때문
}

@Override
protected void onDestroy() { // Activity가 종료(제거)될 때 호출
    super.onDestroy();
    // cursor 사용 종료
    if (cursor != null) cursor.close();
}

📍 swapCurosr

// 기존 cursor를 close하지 않음 - 검색 등 일시적으로 커서를 바꾸고자 할 때 사용
oldCursor = Adapter.swapCursor(new Cursor)
❗ cursor 값을 초기에는 null로 설정 - 화면이 열릴 때 데이터를 가져오기 위해
CurosrAdapter.changeCursor(Cursor cursor) - 현재 Adapter에 설정되어 있는 cursor를 close한 후 매개변수 cursor로 Adapter 재설정
사용 중에는 cursor close하면 안됨 - cursor를 close하면 데이터 사용 불가

 

ListView 데이터 변경 시 화면 갱신

  • 선택한 항목 확인은 id 사용 - 테이블의 _id와 일치
    • onItemLongClick, onItemClick의 마지막 매개변수
      onItemLongClick(AdapterView<?> adapterView, View view, int position, long id)
      onItemClick(AdapterView<?> adapterView, View view, int position, long id)
  • 갱신 - DB 변경 후 DB를 새로 읽어온 cursor로 Adpater의 cursor 변경

 

커스텀 CursorAdapter 사용

CursorAdapter를 사용하여 Custom View를 표시하는 경우

  • ListView 각 항목을 표시하는 layout(xml) 정의
  • CursorAdapter 상속 클래스 작성
    • 생성자
    • newView() - 기존 커스텀 Adapter의 getView()에서 custom view를 생성하는 역할 부분 -> LayoutInflater를 사용하여 ListView에 표시할 layout 객체 생성
    • bindView() - newView()에서 생성한 layout의 내부 view에 cursor의 데이터를 결합하여 실제 view의 내용을 채움
  • ListView에 커스텀 cursor adapter 설정

 

커스텀 CursorAdapter 사용 예시

public class MyCursorAdapter extends CursorAdapter { // CursorAdapter 상속
    LayoutInflater inflater;
    int layout;

    public MyCursorAdapter(Context context, int layout, Cursor c) {
        super(context, c, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.layout = layout;
    }

    // newView는 view에 데이터 결합할 때마다 호출
    // newView는 화면에 보이는 view개수 만큼만 호출 (스크롤을 내리면 가려지는 뷰를 재사용)
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) { 
        View view = inflater.inflate(layout, parent, false);

        ViewHolder holder = new ViewHolder();
        view.setTag(holder);

        return view;
    }

    // bindView는 view를 만들 때마다 호출
    @Override
    public void bindView(View view, Context context, Cursor cursor) { 
        // 한 번이라도 view가 사용이 되었다면 view의 holder안에 findViewById로 찾아놓은 요소들이 있음
        ViewHolder holder = (ViewHolder) view.getTag();

        if (holder.tvContactName == null) {
            holder.tvContactName = view.findViewById(R.id.tvContactName);
            holder.tvContactPhone = view.findViewById(R.id.tvContactPhone);
        }

        holder.tvContactName.setText(cursor.getString(cursor.getColumnIndex(ContactDBHelper.COL_NAME)));
        holder.tvContactPhone.setText(cursor.getString(cursor.getColumnIndex(ContactDBHelper.COL_PHONE)));
    }

    // 정적멤버변수는 한 번 값을 정해두면 그 값을 유지되어서 지속적으로 사용 가능
    static class ViewHolder {
        TextView tvContactName;
        TextView tvContactPhone;

        // 기본적으로 static 클래스 멤버는 null로 초기화되지만 명시적으로
        public ViewHolder() {
            tvContactName = null;
            tvContactPhone = null;
        }
    }
}
❗ 생성자에서 매개변수로 전달받은 커서 정보를 부모 클래스에 넣으면 newView()나 bindView()의 매개변수로 자동으로 들어감 (따로 멤버로 보관할 필요가 x) (context도 마찬가지)

 

  1. ListView에서 view가 필요할 때마다 newView로 view 생성
  2. 이 view에 데이터를 결합할 때 bindView가 호출이 되면서 생성된 view가 매개변수로 들어옴

 

'프로그래밍 > Android' 카테고리의 다른 글

[Android] 안드로이드 SQlite 사용  (0) 2021.09.11
복사했습니다!