전편 필요한분.
2012/02/10 - [Android Study] - Android database 만들기 2편 

전편 코드가 좀 긴 관계로 부분 부분 나눠서 설명을 해야 겠음.

  @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
   
// Activity 나 혹은 Service 같은 곳에서 DB 삭제 요청하면 들어 오는 곳.
    // 예시 ) getContentResolver().delete(URI, null, null); 
        return 0;
    }

    @Override
    public String getType(Uri uri) {
    // URI를 질의 해서 해당 URI 의 Data Type이 뭔지 찾는 녀석 (이거 Intent 할때 사용됨)
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
    // Activity 나 혹은 Service 같은 곳에서 DB 추가 요청하면 들어 오는 곳.
    // 예시 ) getContentResolver().insert(URI, values); 
  
        return null;
    }

    @Override
    public boolean onCreate() {
     // 처음 생성 되는 곳 여기서 Database를 만들어야 함. 
        mPracticeDB = new PracticeDatabase(getContext(), DATABASE_NAME, 
                                   null, DATABASE_VERSION).getWritableDatabase();
     // DB 객체 상태에 따라서 false, true를 반환 하는데 true면 해당 class가 사용 가능 상태,       // false 면 해당 class가  사용 중지
        return (mPracticeDB == null)? false : true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
    // Activity 나 혹은 Service 같은 곳에서 DB 질의 요청하면 들어 오는 곳.
    // 예시 ) getContentResolver().query(URI, proiection, null, null, null); 
    
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
    // Activity 나 혹은 Service 같은 곳에서 DB 갱신 요청하면 들어 오는 곳.
    // 예시 ) getContentResolver().update(URI, values, null, null; 
  
        return 0;
    } 

위에 함수들은 ContentProvider를 상속받으면 기본적으로 적어야 하는 함수임.
붸리 임포턴트!! 

'Android' 카테고리의 다른 글

Android database 만들기 5편  (0) 2012.02.10
Android database 만들기 4편  (0) 2012.02.10
Android database 만들기 2편  (0) 2012.02.10
Android database 만들기 1편  (0) 2012.02.10
Android custom list 만들기 + separate 넣기 4편  (0) 2012.02.08
전편 필요한 분...
2012/02/10 - [Android Study] - Android database 만들기 1편 

요즘 내가 폭풍 글싸놓기를 하는 이유는??
- 급 한가해져서 ㅋㅋ 

전편에 프로젝트 상태를 봤을테니 이제는 실전 코드... 근대 좀 많이 큼..

일단 아무것도 안하고 Database 파일만 생성 하는 코드!!.


 public class AndroidProvider extends ContentProvider {
    private static final String DATABASE_NAME = "practice.db";
    private static final int DATABASE_VERSION = 1;
    private static final String PRACTICE_TABLE = "Practice";

    private SQLiteDatabase mPracticeDB;

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public boolean onCreate() {
        mPracticeDB = new PracticeDatabase(getContext(), DATABASE_NAME,
                                   null, DATABASE_VERSION).getWritableDatabase();

        return (mPracticeDB == null)? false : true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        return 0;
    }

    private class PracticeDatabase extends SQLiteOpenHelper {
        public PracticeDatabase(Context context, String name,
                CursorFactory factory, int version) {
            super(context, name, factory, version);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("create table " + PRACTICE_TABLE
                    + " (" + PracticeColumns.ID + " integer primary key autoincrement, "
                    + PracticeColumns.DATA + " text);");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS " + PRACTICE_TABLE);
            onCreate(db);
        }
    }

    public interface PracticeColumns {
        public static final Uri CONTENT_URI = Uri.parse("content://com.practice.database/data");
        public static final String ID = "_id";
        public static final String DATA = "data";
    }
}


코드가 너무 크니깐.. 담편에 설명을 쩜쩜쩜 - 절대 편수 늘릴려는 꼼수가 아니라능... -
 
뭔가 이전 글 때문에 굳이 database 만들기를 말하려는 것 인거 같지만...
(흠 심증은 있으나 물증이 없는?...)

Android는 sqlite 라는 database system (?)를 제공 한다. 다들 알겠지만.
사용 방법도 다양하고 쉽게 쓸려면 마냥 쉽고 어렵게 쓸려면 또 어려운게 사실이다.

예전에 누군가를 도와주려고 그 사람 code를 본적이 있는데 그 사람은 Activity 에 
database 관련 내용을 다 넣어서 컨트롤 하던 상황이었다. (대체 어떻게 우겨 넣은거지??)
그래 놓고 다른 activity에서 사용 하고 싶다고 (나더러 어쩌라고?) 방법을 문의 하던 기억이...

이것 저것 다양하고 많은 방법이 있지만, 나는 가장 무난한 방법을 설명 할꺼임.


테이블 명은 Practice, 컬럼은 _id, data 를 가지는 database를 생성 할 것임.

손을 봐야 할 곳은 Database를 직접 컨트롤 하는 Provider class 와 AndroidManifest 두 개임.

현재 내 프로젝트 상태.



AndroidListAcitivity.java는 무시하고 눈여겨 봐야 할 것은 AndroidProvider.java 요녀석임.
Provider는 Activity들과 다르므로 다른 package(디렉토리)에 넣어 둘꺼임!


담편에 계속...
이전글 필요한 분.

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

이래 저래 다 완성 하고 나서 click event나 focus event 처리가 문제라면...
아래와 같이 처리하면 됨.

IconAdapter class 내부에 다음과 같은 메소드를 override 해주면됨

         @Override
        public boolean isEnabled(int position) {
            if (position == SEPARATE_POSITION) {
                return false;
            } else {
                return super.isEnabled(position);
            }
        }

후후~ 수고 하셨고 퍼갈때 댓글이나 좀 남겨 주이소...
이전글 필요 한 사람.
2012/02/08 - [Android Study] - Android custom list 만들기 + separate 넣기 1편
2012/02/08 - [Android Study] - Android custom list 만들기 + separate 넣기 2편 

 
자 이제 요롷게 구분선을 하나 넣을 계획 임. (1, 2편 모르면 말짱 황임!! 황!!)
이힛~ 
이제 부터 1편에 있던 java source를 수정 합니다.
수정은  IconAdapter class 만 합니다.
 


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

 수정 code
        @Override
        public int getCount() {
            return mIconArrayList.size() + 1;
        } 


기존 code
 @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;
        }

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

            if (position == SEPARATE_POSITION) {
                ImageView iamge = new ImageView(AndroidListActivity.this);
                iamge.setScaleType(ScaleType.FIT_XY);
                iamge.setImageResource(android.R.drawable.title_bar);
                convertView = iamge;
            } else {
                convertView = getLayoutInflater().inflate(R.layout.icon_list_item, parent, false);
                Iconinfo info = mIconArrayList.get(getRealPosition(position));
                ((TextView) convertView.findViewById(R.id.text1)).setText(info.text);
                ((ImageView) convertView.findViewById(R.id.icon)).setImageResource(info.icon);
            }
            
            return convertView;
        }
 

추가 code
        private static final int SEPARATE_POSITION = 3;
        private int getRealPosition(int position) {
            if (position < SEPARATE_POSITION) {
                return position;
            } else {
                return position-1;
            }
        }

정상적으로 잘 되었다면 저~어기 위에 그림처럼 될꺼임~
근데 여기 까지하고 click event 물리면 대략 난감한 상황이 발생 할꺼임 그건 담편에!! 
이전 글 필요 한 사람.

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편까지 만들고 싶...  (응? 거기 퍼갈땐 댓글!!)



+ Recent posts