뭐 고작 ListView에 index 넣는 걸로 엄청 오래 끌었는데... 무튼 이제 거의 다왔음요.

전편 필요한 분.
2012/03/07 - [Android Study] - Android Study ListView Indexer 5편 

Layout 파일 내용.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="25dip"
        android:clickable="false"
        android:background="@*android:drawable/dark_header"
        android:gravity="center_vertical"
        android:focusable="false"
        android:visibility="gone">

        <TextView
            android:id="@+id/head"
            android:layout_width="56dip"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            android:textColor="@*android:color/dim_foreground_dark"
            android:textSize="14sp"
            android:gravity="center"/>
    </LinearLayout>

    <TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="?android:attr/listPreferredItemHeight"
        android:gravity="left|center_vertical"
        android:layout_weight="1"
        android:textAppearance="?android:attr/textAppearanceMedium"/>
</LinearLayout>
붉은 색 부분이 index 표현을 위해서 추가된 code임. Layout 설명은 생략 하도록 하겠음.
IconAdapter에 추가된 함수

private void bindSectionHeader(View itemView, int position) {
    final int section = getSectionForPosition(position);
    LinearLayout header = (LinearLayout) itemView.findViewById(R.id.header);
    TextView head = (TextView) itemView.findViewById(R.id.head);
    if (getPositionForSection(section) == position) {
        String title = (String)mIndexer.getSections()[section];
        head.setText(title);
        header.setVisibility(View.VISIBLE);
    } else {
        header.setVisibility(View.GONE);
    }
}

Index 표시하는 부분 임 그럼 이 함수를 어디서 호출 해주는 것이냐 하면.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    mCursor.moveToPosition(position);
    if (convertView == null) {
        convertView = newView(mContext, mCursor, parent);
    }
    bindView(convertView, mContext, mCursor);
    bindSectionHeader(convertView, position);
    return convertView;
}

이렇게 CursorAdapter 에 기본 함수 getView에서 호출 하도록 만들면 땡!
그러면 Adapter는 어떻게 초기화 하냐 하면.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.list_content);

    mAdapter = new IconAdapter(this, null);
    mList = (ListView) findViewById(android.R.id.list);
    mList.setAdapter(mAdapter);
    mList.setOnItemClickListener(this);

    Cursor cursor = getContentResolver().query(PracticeColumns.CONTENT_URI, 
    new String[] {PracticeColumns.ID, PracticeColumns.DATA}, null,
    null, PracticeColumns.DATA + " ASC");
    mAdapter.changeCursor(cursor);
}

Adapter 에서 changeCursor를 호출하면 땡!

이런 DB를 출력하면

이렇게 출력됨.


보통 Code를 통째로 공개하지는 않지만... 흠흠 이번엔 특별히 내가 귀찮으므로... 첨부~

AndroidList.zip


'Android' 카테고리의 다른 글

Android Custom Widget 만들기 2편  (0) 2012.04.12
Android Custom Widget 만들기 1편  (1) 2012.04.12
Android Study ListView Indexer 5편  (0) 2012.03.07
Android Study ListView Indexer 4편  (0) 2012.03.07
Android Study ListView Indexer 3편  (0) 2012.03.07
전편 필요한 분.
2012/03/07 - [Android Study] - Android Study ListView Indexer 3편

PracticeIndexer class 내용 입니당.
public class PracticeIndexer implements SectionIndexer {
    // Index 내용
    private final String[] mSections;
    // Index 위치 
    private final int[] mPositions;
    // 최종 index 위치 
    private final int mCount;

    public HistortSectionIndexer(String[] sections, int[] counts) {
        // 생성자에서 Index 내용, 위치 배열 설정 null 이면 그냥 종료
        if (sections == null || counts == null) {
            throw new NullPointerException();
        }
        // Index 내용, 위치 배열 크기가 다르면 그냥 종료
        if (sections.length != counts.length) {
            throw new IllegalArgumentException(
                    "The sections and counts arrays must have the same length");
        }

        // Index 내용 초기화
        this.mSections = sections;
        // 위치는 같은 index를 사용 하는 것이 몇개 인지 counting 해서 전달 함.
        // 그래서 count를 일일이 더해야 실제 위치가 나옴. 위치 배열 새로 생성
        mPositions = new int[counts.length];
        int position = 0;
        for (int i = 0; i < counts.length; i++) {
            // Index 내용이 null 이면 공백으로 초기화 
            if (mSections[i] == null) {
                mSections[i] = " ";
            } else {
                // Index 내용에 공백 있으면 제거
                mSections[i] = mSections[i].trim();
            }
            // Index 위치 초기화
            mPositions[i] = position;
            // counting 된 값 더하기. 
            position += counts[i];
        }
        // Index의 최종 위치 지정 
        mCount = position;
    }

    // Index 내용 반환
    @Override
    public Object[] getSections() {
        return mSections;
    }

    // Index 위치 반환
    @Override
    public int getPositionForSection(int section) {
        if (section < 0 || section >= mSections.length) {
            return -1;
        }

        return mPositions[section];
    }

    // Index 위치 내용을 binarySearch 해서 배열 index 반환
    @Override
    public int getSectionForPosition(int position) {
        if (position < 0 || position >= mCount) {
            return -1;
        }

        int index = Arrays.binarySearch(mPositions, position);
        /*
         * Consider this example: section positions are 0, 3, 5; the supplied
         * position is 4. The section corresponding to position 4 starts at
         * position 3, so the expected return value is 1. Binary search will not
         * find 4 in the array and thus will return -insertPosition-1, i.e. -3.
         * To get from that number to the expected value of 1 we need to negate
         * and subtract 2.
         */
         // 위 코멘트는 Google 꺼... 내용인 즉슨 예를 들어 인덱스 위치가 0,3,5 이면
         // 현재 위치가 4면 가까운 시작 위치가 3이니깐 1이 반환 되야함
         // 근데 Binary search에 의해서 4가 나옴
         // 그러니깐 -3이 반환되게 해서 4 + (-3) 하면 1이 나오게 됨.
        return index >= 0 ? index : -index - 2;
    } 


이 코드역시 google base code를 따온것 이구요.
힘드니께 담 이야기는 담편으로~
 

'Android' 카테고리의 다른 글

Android Study ListView Indexer 6편  (3) 2012.03.07
Android Study ListView Indexer 5편  (0) 2012.03.07
Android Study ListView Indexer 3편  (0) 2012.03.07
Android Study ListView Indexer 2편  (1) 2012.03.07
Android Study ListView Indexer 1편  (0) 2012.03.07

전편에 쓰다가 못 쓴거 마저 쓰기!!

전편 필요한분.
2012/02/08 - [Android Study] - Android List에 index를 표시해보자 3편

protected Locale getLocale() {
    return Locale.getDefault();
}

static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";

private static final class AddressBookIndexQuery {
    public static final String LETTER = "letter";
    public static final String TITLE = "title";
    public static final String COUNT = "count";

    public static final String[] COLUMNS = new String[] {
            LETTER, TITLE, COUNT
    };

    public static final int COLUMN_LETTER = 0;
    public static final int COLUMN_TITLE = 1;
    public static final int COLUMN_COUNT = 2;

    public static final String ORDER_BY = LETTER + " COLLATE " +           
                                                         PHONEBOOK_COLLATOR_NAME;
}


남은 부분은 이게 단데... 

솔직히 여기서부터는 설명할 방법이... 찾기도 귀찮고...
게다가 구글에서 개발 해놓은 거라... 겐히 저작권 문제에 휘말리지 않을까? 싶기도하고...

여튼 Provider에서 처리해야 할 내용은 이게 다임...으힛...
담에는 이걸 Adapter 에서 사용 하는 꼴? 을 보여주겠음 움케케 (to be continue after 1 year



전편에 이어서 이제부턴 code 부연 설명 및 추가 code !!

전편 못 본분 여기!!
2012/02/08 - [Android Study] - Android List에 index를 표시해보자 2편



private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
        SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
    String sortKey;
    String sortOrderSuffix = "";

    // Sortkey 설정 부분 - 전낸 중요함 왜 냐면 index 뽑을 때 소트키로 뽑음 그러므로 NULL 안댐
    if (sortOrder != null) {
        int spaceIndex = sortOrder.indexOf(' ');
        if (spaceIndex != -1) {
            sortKey = sortOrder.substring(0, spaceIndex);
            sortOrderSuffix = sortOrder.substring(spaceIndex);
        } else {
            sortKey = sortOrder;
        }
    }

    // 실제 sortkey로 사용 될 부분 위에서 지정한 sortkey가 null 이면 안되는 이유가 여기임!!    
    String subSortKey = "SUBSTR(" + sortKey + ",1,1)";

    // 국가 세팅 얻어오기 (국가별로 sort 내용이 다르니까!!)
    String locale = getLocale().toString();

    // Projection list 만들기 (이거 좀 신기함, 내가 Android SQL은 안까봐서 모르겠는데...
    // 일케도 돌아가나 봄. (아니면 특별히 만든 내용일 확률이 99.9%이긴 한데...)
    HashMap projectionMap = new HashMap();

    // 빨간색 글귀 변경되면 안됨. SQL에서 따로 만들어 놓은 거라서 변경되면 안됨.
    // 변경되면 뭐랄까? 인식을 못 한다고나 할까? Error 날꺼임.. 
    // 추가로 google애들이 적어 놓은 주석을 알려주자면
    
    /**
     * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
     * to map the first letter of the sort key to a character that is traditionally
     * used in phonebooks to represent that letter.  For example, in Korean it will
     * be the first consonant in the letter; for Japanese it will be Hiragana rather
     * than Katakana.
     */
    
     projectionMap.put(AddressBookIndexQuery.LETTER,
             subSortKey + " AS " + AddressBookIndexQuery.LETTER);
 
     projectionMap.put(AddressBookIndexQuery.TITLE,
            "GET_PHONEBOOK_INDEX(" + subSortKey + ",'" + locale + "')"
                    + " AS " + AddressBookIndexQuery.TITLE);

      projectionMap.put(AddressBookIndexQuery.COUNT,
            "COUNT(" + sortKey + ") AS " + AddressBookIndexQuery.COUNT);
 
     // Projection setting 해주기
      qb.setProjectionMap(projectionMap);

     // Query 돌리기!!
      Cursor indexCursor = qb.query(db, null, selection, selectionArgs,
            AddressBookIndexQuery.ORDER_BY, null /* having */,
            AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);

      try {
          int groupCount = indexCursor.getCount();
          String titles[] = new String[groupCount];
          int counts[] = new int[groupCount];
          int indexCount = 0;
          String currentTitle = null;

          int count = 0;

         // 중복된 내용들은 정리하고 배열에 정리해 넣기
           for (int i = 0; i < groupCount; i++) {
              indexCursor.moveToNext();
              int latter = indexCursor.getInt(AddressBookIndexQuery.COLUMN_LETTER);
              String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);

              count = latter;
              if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
               // titles 이게 index 글자들 배열
                  titles[indexCount] = currentTitle = title;
               // index의 position 배열 (2편에서 query 문에 의해 나온
               // bundleLetterCountExtras 여기 들어 오기전 cursor에서 position)
                  counts[indexCount] = count;
                  indexCount++;
              } else {
                counts[indexCount - 1] += count;
            }
        }

       // 배열 크기 조정
        if (indexCount < groupCount) {
            String[] newTitles = new String[indexCount];
            System.arraycopy(titles, 0, newTitles, 0, indexCount);
            titles = newTitles;

            int[] newCounts = new int[indexCount];
            System.arraycopy(counts, 0, newCounts, 0, indexCount);
            counts = newCounts;
        }

       // Bundle 형태로 담아서 cursor에 몰래 꼽아둠 ㅋㅋ
        final Bundle bundle = new Bundle();
       // 빨간색 부분은 Bundle 에서 Extra key 값임 알아서 처리 하셈
        bundle.putStringArray("님덜 맘대로", titles);
        bundle.putIntArray("님덜 맘대로", counts);
        return new CursorWrapper(cursor) {

            @Override
            public Bundle getExtras() {
                return bundle;
            }
        };
    } finally {
        indexCursor.close();
    }
}
으하하하 너무 길어서 담편에 부족한 code 추가 해야겠네...?
데헷~ 너무 좋다.
 


 
저번 글에 뻘짓만 하고 code 안알려줘서 빡친 분들 더러 더러 있을 꺼 같아서
인좌부터 code로 까 놓고 직접 설명 해줄께요.

(님들아 돌 내려놔여...)

기본 가정으로 ListView, Adapter, Provider 구성은 할 줄 안다고 가정 하고 시작 합니다.

기본 코드 출처는 ContactsProvider에 있었음.
(다년 간? 다뤄본 경험에 비추어 가장 좋은 방법? 이라고 판단됨 - 지극히 주관적 ㅋㅋ)

우선 Provider 에서 query 부분을 수정 해야함.
대부분 보통 상태라면 cursor를 return 해야 되기 땜시롱.
 

 case DATA :
 Cursor cursor = database.query(테이블명, 프로젝션, 조건문, 조건문인자, 그룹바이, 해빙, 소트)
 break;
 

요딴식으로 되어 있을 꺼임. 이걸 수정을 가해서 index를 같이 전달 해주도록 고칠 예정임


case DATA :
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
Cursor cursor = database.query(테이블명, 프로젝션, 조건문, 조건문인자, 그룹바이, 해빙, 소트)
qb.setTables(테이블명);
// 요고는 안써도 되는듯 
// qb.setStrictProjectionMap(true);
cursor = bundleLetterCountExtras(cursor,  database, qb, selection, selectionArgs, sortOrder);

일케 고치고 나면.

bundleLetterCountExtras(cursor,  database, qb, selection, selectionArgs, sortOrder);

이 함수 부분이 매우 궁금 할 꺼임? 글치 않음 님덜?



private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
        SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
    String sortKey;
    String sortOrderSuffix = "";
    if (sortOrder != null) {
        int spaceIndex = sortOrder.indexOf(' ');
        if (spaceIndex != -1) {
            sortKey = sortOrder.substring(0, spaceIndex);
            sortOrderSuffix = sortOrder.substring(spaceIndex);
        } else {
            sortKey = sortOrder;
        }
    }
    
    String subSortKey = "SUBSTR(" + sortKey + ",1,1)";

    String locale = getLocale().toString();
    HashMap projectionMap = new HashMap();
    projectionMap.put(AddressBookIndexQuery.LETTER,
            subSortKey + " AS " + AddressBookIndexQuery.LETTER);

    projectionMap.put(AddressBookIndexQuery.TITLE,
            "GET_PHONEBOOK_INDEX(" + subSortKey + ",'" + locale + "')"
                    + " AS " + AddressBookIndexQuery.TITLE);

    projectionMap.put(AddressBookIndexQuery.COUNT,
            "COUNT(" + sortKey + ") AS " + AddressBookIndexQuery.COUNT);

    qb.setProjectionMap(projectionMap);

    Cursor indexCursor = qb.query(db, null, selection, selectionArgs,
            AddressBookIndexQuery.ORDER_BY, null /* having */,
            AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);

    try {
        int groupCount = indexCursor.getCount();
        String titles[] = new String[groupCount];
        int counts[] = new int[groupCount];
        int indexCount = 0;
        String currentTitle = null;

        int count = 0;

        for (int i = 0; i < groupCount; i++) {
            indexCursor.moveToNext();
            int latter = indexCursor.getInt(AddressBookIndexQuery.COLUMN_LETTER);
            String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);

            count = latter;
            if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
                titles[indexCount] = currentTitle = title;
                counts[indexCount] = count;
                indexCount++;
            } else {
                counts[indexCount - 1] += count;
            }
        }

        if (indexCount < groupCount) {
            String[] newTitles = new String[indexCount];
            System.arraycopy(titles, 0, newTitles, 0, indexCount);
            titles = newTitles;

            int[] newCounts = new int[indexCount];
            System.arraycopy(counts, 0, newCounts, 0, indexCount);
            counts = newCounts;
        }

        final Bundle bundle = new Bundle();
        bundle.putStringArray("님덜 맘대로", titles);
        bundle.putIntArray("님덜 맘대로", counts);
        return new CursorWrapper(cursor) {

            @Override
            public Bundle getExtras() {
                return bundle;
            }
        };
    } finally {
        indexCursor.close();
    }
 }
뭔가 내가 쓰긴 했지만 전낸 머리 아픔... 주석도 없고...
글구 몇가지 또 빼먹은 것도 있고 움케케케... 하지만 나머지 설명은 3탄에 할 계획이란거!!
난 이걸로 12편까지 만들고 싶...  (응? 거기 퍼갈땐 댓글!!)




아이폰을 사용 하다 보면 어떤 어플에서는 list에 index를 표시 해주는 것을 볼 수 있다.
그럼 안드로이드에는 list에 index를 표기 하는 법이 없을까?(쓸대없는 호기심 발동 ㅋㅋ)


근데 그냥 안된다. 망할 Android... OTL


I : "Hey android, do you have this component?"


 


A : "Sure. yes i do."

위 영어 표기가 정상인지는 몰겠지만.. 무튼 나도 저거 해보고 싶었다.
그래서 여기저기 뒤져봤다.
뒤져보니 여간 깐깐한게 아니다.
그래서 감히 말하건데... Listview, Adapter, Provider 모르는 사람은 그냥 포기해라.
(다른 방법도 있지만 그건 안알려 줄테다. ^^)

글구 퍼가는거 좋은데 출처 분명히 해줬으면 함. (퍼가긴 할껀지 몰겠네.)

'Android' 카테고리의 다른 글

Android List에 index를 표시해보자 3편  (0) 2012.02.08
Android List에 index를 표시해보자 2편  (0) 2012.02.08
Android base component - ListView  (0) 2011.10.20
Android - Activity  (0) 2011.10.02
Android 시작  (0) 2011.09.20

+ Recent posts