이전 글 필요 한 사람.

2012/02/08 - [Android Study] - Android custom list 만들기 + separate 넣기 1편 

레이아웃 파일 내용.
 
list_content.xml 파일

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>


icon_list_item.xml 파일

<?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:minHeight="?android:attr/listPreferredItemHeight"
    android:paddingTop="1dp"
    android:paddingBottom="1dp"
    android:paddingRight="9dp"
    android:paddingLeft="9dp"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingRight="9dp"/>

    <TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="left|center_vertical"
        android:layout_weight="1"
        android:textAppearance="?android:attr/textAppearanceMedium"/>
</LinearLayout>


여기 까지는 다들 의문점이 없을 것이라고 생각 됨.
후후 code가 기니깐 담편으로~
 




Custom adapter로 대충 만든 icon list 화면 임.
나는 특별 하니깐!! camera 아이콘 과 연필 아이콘 사이에 separate 하는 녀석을 추가 할꺼임.

우선은 icon list 만드는 법 부터 설명 ㄱㄱㅆ

Java Code


public class AndroidListActivity extends Activity {
    private ListView mList;
    private IconAdapter mAdapter;

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

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

        setupListItem();
    }

    private void setupListItem() {
        String[] items = { "Test1", "Test2", "Test3", "Test4", "Test5"};
        int[] icons = { android.R.drawable.ic_menu_add, 
                android.R.drawable.ic_menu_agenda,
                android.R.drawable.ic_menu_camera, android.R.drawable.ic_menu_edit,
                android.R.drawable.ic_menu_manage};

        mAdapter.clear();

        for (int i=0; i mIconArrayList = new ArrayList();

        public void clear() {
            mIconArrayList.clear();
        }

        public void add(Iconinfo item) {
            mIconArrayList.add(item);
        }

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

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

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = getLayoutInflater().inflate(R.layout.icon_list_item, parent, false);
            }

            Iconinfo info = mIconArrayList.get(position);
            ((TextView) convertView.findViewById(R.id.text1)).setText(info.text);
            ((ImageView) convertView.findViewById(R.id.icon)).setImageResource(info.icon);
            return convertView;
        }
    }

    public class Iconinfo {
        private int icon;
        private String text;
    }
} 

드래그 가능 하니깐 복사해서 코드 보기를 하시던 맘대로 하셈. 
레이아웃 파일은 담편에...
 



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

전편 필요한분.
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



Android 에서는 component 들을 widget 이라고 부른다. 이번에 설명 해줄 widget은 ListView에 관한 것 이다.

어떤 data를 List 형태로 보여 주는 동작을 하고 싶을 때 이용 되는 widget 으로 data와 Adapter라는 controller
그리고 Listview가 있어야 한다.

Adapter는 ListView를 control 하기 위해 사용 된다.
어떤 식이냐면 Adapter에는 base method(기본 동작 함수)로 getCount() 라는 함수가 있다. 
이녀석은 ListView에 나타날 총 item 목록의 갯수를 알려 준다. 이녀석은 ListView에서 나름 중요 한 녀석(?) 이다.

ListView는 자신의 UI 크기에서 개별 item의 UI 크기  (위 그림에서 "Android 1" 이라고 써져있는 item 하나의 크기)를 계산해서 한 화면에 item을 몇 개를 뿌릴 수 있는지 계산한다. - 위 그림 대로 계산하면 대략 6이 나온다.

ListView가 그리릴 수 있는 내용은 최 상단의 item 의 index 값(item의 실제 순서 정도로 이해 하면된다) +  한 화면에 6개의 item을 그릴 수 있으니까 index + 6이 될 것이다. 이렇게 수행 하면서 scroll 하면 계속해서 갱신 해간다. 최종 목적지는 getCount 에서 얻어진 숫자 만큼 item을 그리도록 한다.

그럼 만약 getCount에서 인위적으로 1 이라고 처리를 한다면 총 갯수가 1개 이므로 볼것도 없이 item은 1개만 그린다.
이런식으로 인위적으로 ListView를 손질 할 수 있다.

Item의 개별적인 display를 담당 하는 Adapter의 method는 getView(...) 이다.
원래는 getView(...)에서 처리를 하는데 Adapter의 종류에 따라 종종 bindView(...) 라는 녀석도 있다.
무튼 getView(...)에 들어 오는 parameter 값을 보면 View 타입으로 convertView 인가? 하는 녀석이 있다.
이녀석이 item (제시된 image에서 Android 1 에 해당)을 display 하는 녀석이다.

그럼 ConvertView라는 녀석은 어디서 할 당 하냐면 getView를 최초 호출시 이값은 null 이다. 이때 newView(...) 라는 녀석을 통해 새로 생성 되도록 되어 있고 여기에는 Layout 파일을 지정 할 수 가 있다. (Adapter 생성시 넣을 수도 있음)

간혹 CustomItemView를 control 하다가 display가 엉망이 되는 경우가 있다고 한다.
(Checkbox 처리를 하다보면 자주 생김)  
원인 부터 알려 주자면 getView는 item 개별적으로 호출 되는데 convertView 이녀석 값은 돌려 쓰기 때문이다.
즉 getView가 호출되면 parameter로 convertView가 들어온다. 1번 item 일때 checkbox를 check 했다면 2번 item의 getView가 호출 되어 convertView를 확인 하면 내가 check 해주지 않아도 check 되어 있다.

해결 책은 converView를 초기화 하던지 if문과 else 문을 쌍으로 써주는 것이다.

ListView에서 data 부분은 대체로 Cursor(SQL query 결과물), Array인데. 둘다 getView에서 data를 꺼내서 convertView에 붙이는 작업을 해야 한다. (뭐 제공되는 Simple... 이런녀석들도 전부 이런 작업을 해준다.)

'Android' 카테고리의 다른 글

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


스터디 스텝상 Activity는 기본으로 이해하고 넘어 가야 하는데...
각종 설명들이 여러군데 퍼져있으므로 어렵게는 이해 할 수 있음.

쉽게 비유를 들자면 C프로젝트 처음에 만들면 xxx.c 파일 안에 있는 void main(xxx) {}; 뭐 이런거와 비슷??
프로그램(프로젝트)를 시작하는 스타트 파일 이라고 생각하면됨!!

Activity는 작업 공간? 인데 화면 단위로 구분 되기도 함.
무슨 소린고하니 화면에 스텝에 따라서 Activity를 만들고 그 화면을 제어 해야함.

Activity는 dalvik 에서 관리 해주는 스텝에 따라 흘러 가게 되어있는데
예를 들어 내가 android 에서 카카오톡 icon을 누르면

(아이콘 누름 -> Launcher Activity 찾음 -> Activity 실행 시킴 -> onCreate(Bundle x) 실행함 ->
onResume() 실행함 -> 화면 보임)


요래 동작 하도록 되어 있음!!

그니까 이걸 이해 해야함 내가 코드를 어디다 짱박아야 하는지 이해 할 수 있는 거임.!

'Android' 카테고리의 다른 글

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


(구글링 하다가 퍼온 사진임 저작권 문제가 생기면 내리겠음)

지금에 와서야 Android를 이야기 시작 한다는게 많이 늦었다는 생각이 든다.
나는 코딩을 엄청 잘하는 개발자도 아니고, 내 전공이 컴공은 더욱더 아니다. 그래서 하는 말인데...
코드를 좀 쉽게 이야기를 해보고 싶었다는... 비록 말로 끝날지 모르겠지만

비록 Google 에서 Motorola를 인수하면서 좀 많이 걱정 되는 것도 사실이긴 한데 뭐 배워둬서 나쁠껀
없다고 생각이 듬.

각종 블로그나 인터넷 정보에 따르면 Android에 관한 자료들이 무진장? 있을 수도 있고 없을 수도 있고.
무튼 처음 환경 setting 이라던지 기타 내용들은 이미 자세히 나와 있으니까... 여러곳에서 구하시고

기본 준비 내용은 Eclipse, SDK, JDK, 환경변수, Hello Android 정도?

+ Recent posts