posted by 프띠버리 2013. 11. 15. 11:32

지역 저장소에 대한 훨씬 더 정교하면서 가장 강력한 방법은 SQLite 데이터베이스를 사용하는 것이다. 모든 애플리케이션은 자신의 SQLite

데이버베이스를 장착하고 있어 애플리케이션 내의 어떠한 클래스에든 접근할 수 있지만, 외부 애플리케이션에서는 그렇지 못하다.

복잡한 쿼리나 코드 블록으로 넘어가기에 앞서 SQLite 데이터베이스가 무엇인지 간단히 알아보자.

 

SQL(Structured Query Language)은 관계형 데이터베이스의 데이터를 관리하기 위한 목적으로 설계된 프로그래밍 언어다. 관계형 데이터베이스는

입력과 삭제, 수정, 쿼리를 수행할 수 있게 해주며, 또한 스킴(테이블로 생각하자)을 생성하거나 수정할 수 있게 해준다. SQLite는 단순히

MSSQL과 PostgreSQL 등의 다른 유명한 데이터베이스 시스템의 축소판이라 할 수 있다. SQLite는 완전히 독립적이고 서버 기능을 제공하지 않으며,

트랜잭션을 지원하며 쿼리를 수행할 수 있는 표준 SQL 언어를 사용한다. 독립적이며 실행가능하다는 즉성으로 인해 SQLite는 매우 효율적이고

유연하며 광범위한 플랫폼에 걸쳐 다양한 분야의 프로그래밍 언어에서 접근 가능하다.

 

이제 다음 예제 코드를 통해 새로운 SQLite 데이터베이스 스킴 인스턴스와 간단한 테이블을 생성하는 과정을 가볍게 살펴보자.

package jwei.apps.dataforandroid.ch1;

 

import android.content.Context;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

import android.util.Log;

 

public class SQLiteHelper extends SQLiteOpenHelper {

 

    private static final String DATABASE_NAME = "my_database.db";

 

    // 테이블과 데이터베이스 업데이트를 위해 이 숫자를 토글한다.

    private static final int DATABASE_VERSION = 1;

 

    // 생성하고자 하는 테이블 이름

    public static final String TABLE_NAME = "my_table";

 

    // 일부 예제 필드

    public static final String UID = "_id";

 

    public static final String NAME = "name";

 

    SQLiteHelper(Context context) {

        super(context, DATABASE_NAME, null, DATABASE_VERSION);

    }

 

    @Override

    public void onCreate(SQLiteDatabase db) {

        db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + UID + " INTEGER PRIMARY KEY AUTOINCREMENT," + NAME

                + " VARCHAR(255));");

    }

 

    @Override

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        Log.w("LOG_TAG", "Upgrading database from version " + oldVersion + " to " + newVersion

                + ", which will destroy all old data");

 

        // 업그레이드되면 이전 테이블을 제거한다.

        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);

 

        // 테이블의 새 인스턴스를 생성한다.

        onCreate(db);

    }

}

여기서 주목해야 한 첫 번째는 사용자가 정의 가능한 데이터베이스 스킴을 생성하기 위해 반드시 SQLiteOpenHelper 클래스를 재정의해야 한다는 점이다.

SQLiteOpenHelper를 재정의한 후 onCreate() 메소드를 재정의해 테이블의 구조를 지시할 수 있다. 예제의 경우 두 개의 칼럼, 즉 ID 칼럼과 name 칼럼을

갖는 테이블을 생성한다. 쿼리는 SQL로 다음 명령을 실행하는 것과 동일하다.

CRATE TABLE my_table(_id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255));

또한 ID 칼럼에 PRIMARY_KEY와 AUTOINCREMENT 속성이 지정돼 있음을 확인했을 것이다. 이는 안드로이드에서 생성된 모든 테이블에 대해 권장되며,

앞으로도 이 표준을 지켜나갈 것이다. 마지막으로 name 칼럼이 최대 255 길이의 문자를 갖는 string 타입으로 선언된 것을 확인할 수 있다.

 

onCreate() 메소드를 재정의한 후 onUpgrade() 메소드도 재정의할 수 있다. 이 메소드를 통해 테이블의 구조를 빠르고 간단하게 변경할 수 있다.

필요한 작업은 DATABASE_VERSION 정수형을 증가시키는 것이 전부이며, 다음에 SQLiteHelper 인스턴스가 생성될 때 자동으로 onUpgrage() 메소드가

호출되며, 이때 제일 먼저 구 버전의 데이터베이스를 삭제한 후 새로운 버전의 데이터베이스를 생성한다.

마지막으로 거의 뼈대만 있는 테이블에 아주 기본적인 값을 입력하거나 쿼리하는 방법을 간단히 살펴보자.

package jwei.apps.dataforandroid.ch1;

 

import jwei.apps.dataforandroid.R;

import android.app.Activity;

import android.content.ContentValues;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import android.os.Bundle;

import android.util.Log;

 

public class SQLiteExample extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        // SQLITE HELPER를 초기화한다.

        SQLiteHelper sqh = new SQLiteHelper(this);

 

        // 읽고 쓸 수 있는 데이터베이스를 가져온다.

        SQLiteDatabase sqdb = sqh.getWritableDatabase();

 

        // 방법 #1: CONTENTVALUE CLASS를 사용한 입력

        ContentValues cv = new ContentValues();

        cv.put(SQLiteHelper.NAME, "jason wei");

 

        // INSERT METHOD 호출

        sqdb.insert(SQLiteHelper.TABLE_NAME, SQLiteHelper.NAME, cv);

 

        // 방법 #2: SQL 쿼리를 사용해 입력

        String insertQuery = "INSERT INTO " + SQLiteHelper.TABLE_NAME + " (" + SQLiteHelper.NAME + ") VALUES ('jwei')";

        sqdb.execSQL(insertQuery);

 

        // 방법 #1: 래퍼 메소드를 사용한 쿼리

        Cursor c = sqdb.query(SQLiteHelper.TABLE_NAME, new String[] { SQLiteHelper.UID, SQLiteHelper.NAME }, null,

                null, null, null, null);

 

        while (c.moveToNext()) {

            // 칼럼 인덱스와 해당 칼럼의 값을 얻어온다.

            int id = c.getInt(c.getColumnIndex(SQLiteHelper.UID));

            String name = c.getString(c.getColumnIndex(SQLiteHelper.NAME));

            Log.i("LOG_TAG", "ROW " + id + " HAS NAME " + name);

        }

 

        c.close();

 

        // 방법 #2: SQL SELECT QUERY사용하기

        String query = "SELECT " + SQLiteHelper.UID + "," + SQLiteHelper.NAME + " FROM " + SQLiteHelper.TABLE_NAME;

        Cursor c2 = sqdb.rawQuery(query, null);

 

        while (c2.moveToNext()) {

            int id = c2.getInt(c2.getColumnIndex(SQLiteHelper.UID));

            String name = c2.getString(c2.getColumnIndex(SQLiteHelper.NAME));

            Log.i("LOG_TAG", "ROW " + id + " HAS NAME " + name);

        }

 

        c2.close();

 

        // 데이터베이스 연결 종료

        sqdb.close();

        sqh.close();

    }

}

이 예제에 주의를 기울이기 바란다. 이 예제는 이후 글에서도 사용될 것이다. 이 예제에서는 제일 먼저 SQLiteHelper를 초기화하고 쓰기 가능한

SQLiteDatabase객체를 얻는다. 그런 후 아주 편리한 래퍼 메소드인 ContentValues 클래스를 사용해 테이블에 행을 입력하거나 갱신, 삭제를

빨리 수행할 수 있다. 여기서 ID 칼럼을 AUTOINCREMENT 필도로 생성했기 때문에 행을 입력할 때 수작업으로 ID를 할당하거나 증가시킬 필요가

없다는 점을 기억해두자. 따라서 ID 필드 없이(예제에서는 name 칼럼) ContentValues를 전달하기만 하면 된다.

그런 다음 SQLiteDatabase 객체로 다시 돌아가 insert() 메소드를 호출한다. 첫 번째 인자는 단순한 데이터베이스 이름이며, 세 번째 인자는 방금

생성한 ContentValue다. 두 번째 인자는 좀 까다롭다. 기본적으로 비어있는 CententValue가 전달되는 경우에 SQLite 데이터베이스는 빈 행을

입력할 수 없기 때문에 두 번째 인자로 전달된 칼럼이 무엇이든지 SQLite 데이터베이스는 자동으로 해당 칼럼의 값을 null로 설정한다.

이렇게 함으로써 SQLite의 예외 발생을 회피할 수 있다.

 

추가적으로 execSQL() 메소드를 활용한 두 번째 방법처럼 기본 SQL 쿼리를 전달해 데이터베이스에 행을 입력할 수 있다.

최종적으로 이제 테이블에 두 개의 행을 입력했고, 이제 이 행을 가져와 읽는 연습을 해보자. 여기서도 두 가지 방법을 알아본다.

첫 번째는 SQLiteDatabase 도우미 메소드 query()를 사용하는 것이고, 두 번째는 기본 SQL 쿼리를 실행하는 것이다.

두 경우 모두 Cursor 객체가 반환된다. Cursor는 쿼리에 의해 반환된 하위 테이블의 행을 순차적으로 반복하는 것으로 생각할 수 있다.

        while (c.moveToNext()) {

            // 칼럽 인덱스와 관련 칼럼의 값을 얻어온다.

            int id = c.getInt(c.getColumnIndex(SQLiteHelper.UID));

            String name = c.getString(c.getColumnIndex(SQLiteHelper.NAME));

            Log.i("LOG_TAG", "ROW " + id + " HAS NAME " + name);

        }

원하는 Cursor를 확보했다면 나머지는 직관적이다. 커서는 while 루프로 전달해야할 각 행을 가져올 수 있게 반복자처럼 동작하기 때문에

각 루프내에서 행 단위로 커서를 아래로 이동시켜야 한다. 그 다음 while 루프 내에서 가져오고자 하는 데이터의 칼럼 인덱스를 얻어야 한다.

예제에서는 단순히 두 칼럼 모두 얻어왔지만, 실전에서는 주어진 시간에 특정한 칼럼으로부터 필요한 데이터만 가져오는 편이 좋다.

마지막으로 커서의 get() 메소드로 칼럼의 인덱스를 전달한 후 칼럼의 타입이 정수형인 경우 getInt() 메소드를 호출한다.

문자열이라면 getString() 메소드를 호출한다.

 

posted by 프띠버리 2013. 11. 15. 10:52

한편으로 외부 저장소는 데이터와 파일을 전화기의 외부 SD카드 Secure Digital Card로 저장한다. 내부/외부 저장소에 대한

개념은 비슷하다. 따라서 앞서 살펴본 내용, 즉 SharedPreferences 대비 외부 저장소의 장단점을 살펴보자.

SharedPreference는 오버헤드가 크지 않기 때문에 단순한  Map 객체 읽기/쓰기 작업은 디스크 읽기/쓰기 작업에 비해 훨씬

더 효과적이다. 하지만 SharePreference는 간단한 기본형 값으로 제한되며, 본질적으로 유연함을 효율성을 바꾸고 있는 셈이다.

내부/외부 저장소 매커니즘을 통해 훨씬 더 많은 데이터 청크(즉, 완전한 XML 파일)는 물론이고, 더욱 복잡한 형태의 데이터

(미디어 파일과 이미지 파일 등)를 저장할 수 있다.

 

내부 저장소 대비 외부 저장소는 어떤 차이가 있을까? 이 둘 사이의 장단점은 좀 더 미묘하다. 우선 저장소 공간의 크기(메모리)를

생각해보자. 사용자가 소유한 전화기에 따라 저장 공간은 다양하지만 내부 메모리 전체 크기가 아주 작을 수 있다.

외부 저장소는 어떤 SD카드를 쓰는지에 따라 크기가 달라진다.

 

내부 저장소의 경우 데이터는 자신의 애플리케이션에 의해서만 접근이 가능하며, 따라서 잠재적으로 악의적인 외부 애플리케이션으로부터

매우 안전한다. 장점은 애플리케이션이 삭제되면 내부 메모리 또한 정리된다는 점이다.

외부 저장소의 경우 본질적으로 읽기와 쓰기가 가능하기 때문에 저장된 모든 파일은 외부 애플리케이션과 사용자에게 노출된다.

따라서 파일이 안전하고 깨끗하게 유지될지 보장할 수 없다.

 

다음 예제를 통해 외부 SD 카드에 실제로 어떻게 접근할 수 있는지 살펴보자.

package jwei.apps.dataforandroid.ch1;

 

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileWriter;

import java.io.IOException;

 

import jwei.apps.dataforandroid.R;

import android.app.Activity;

import android.os.Bundle;

import android.os.Environment;

import android.util.Log;

 

public class ExternalStorageExample extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        String fileName = "my_file.txt";

        String msg = "Hello World.";

 

        boolean externalAvailable = false;

        boolean externalWriteable = false;

        String state = Environment.getExternalStorageState();

 

        if (state.equals(Environment.MEDIA_MOUNTED)) {

            // 미디어는 사용 가능하며, 쓰기도 가능하다

            externalAvailable = true;

            externalWriteable = true;

        } else if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {

            // SD 카드는 사용 가능하지만, 쓰기는 불가능하다

            externalAvailable = true;

        } else {

            // 여러 상황에 실패할 수 있다.

            // 특별한 동작은 없다.

        }

 

        if (externalAvailable && externalWriteable) {

            // API 레벨 7 혹은 그 이하 버전에서 SD 카드 디렉토리를 가져온다.

            File root = Environment.getExternalStorageDirectory();

            File f = new File(root, fileName);

            try {

                // 내부 저장소 쓰기 방법과 차이가 있을에 유의한다.

                FileWriter fWriter = new FileWriter(f);

                BufferedWriter out = new BufferedWriter(fWriter);

                out.write(msg);

                out.close();

            } catch (IOException e) {

                e.printStackTrace();

            }

        } else {

            Log.e("LOG_TAG", "SD CARD UNAVAILABLE");

        }

    }

 

}

 

위 코드를 실행하기 위해 매니페스트 파일에 WRITE_EXTERNAL_STORAGE 권한을 추가하는 것을 잊지 말자.

여기서 실제로 마운트되고 쓰기 가능한 외부 SD 카드를 탐지할 수 있게 Environment 클래스의 getExternalStorageState() 메소드를 호출했다.

이런 사전 확인 없이 파일을 읽거나 쓴다면 에러가 발생할 수 있다.

SD 카드가 마운트돼 실제로 쓰기가 가능한 상황이라면 API 레벨 7 혹은 그 이후에 getExternalStorageDirectory() 를 호출해 SD 카드의 루트

파일 경로를 가져올 수 있다. 이 관점에서 단순히 새로운 파일을 생성해 FileWriter와 BufferedWritter를 초기화하고 문자열을 팡리에 쓰고자 한다.

여기서 주목해야 할 부분은 외부 저장소를 처리할 때 디스크에 쓰는 방법이 이전에 살펴본 내부 저장소에 쓰는 방법과 다르다는 점이다.

이는 실제로 기억하고 이해해야 할 중요한 부분이며, 이러한 쓰기 메소드에 대해 특별히 강조했던 이유이기도 하다. 내부 저장소 예제에서는

Context 클래스의 openFileOutput() 메소드(두 번째 인자로 모드를 받는)를 호출해 FileOutputStream 개체를 얻었다.

MODE_PRIVATE를 전달하면 이면에서 FileOutStream으로 파일을 생성하고 쓸 때마다 해당 파일은 애플리케이션의 고유 ID로 암호화되고

서명된다. 따라서 외부 애플리케이션은 해당 파일의 내용에 접근할 수 없다.

하지만 외부 저장소에 파일을 생성하고 쓸 때는 기본적으로 어떠한 보안 적용 없이 파일이 생성되기 때문에 모든 애플리케이션은 이 파일을

읽거나 쓸 수 있따. 이는 내부 저장소가 아니라 외부 SD 카드에 쓸 때에는 표준 자바 메소드를 사용할 수 있는 이유이기도 하다.

마지막으로 유념해야 할 것은 이클립스의 DDMS perspective에서 새로 생성된 파일을 볼 수 있기 때문에 SD 카드가 설정돼 있따면

DDMS에서 새롭게 생성된 텍스트 파일을 쉽게 확인할 수 있으며 애플리케이션을 개발할 때 DDMS perspective를 잘 활요하면 빠르게 파일을

넣거나 뺄 수 있으며, 디스크에 쓰고 있는 파일을 모니터링할 수 있다.

API 레벨 8 이후에 소개된 외부 저장소 쓰기 관련 변경 사항에 대해 짧게 이야기하고자 한다. 실제로 이 변경에 대한 내용은

http://developer.android.com/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)

문서를 참고하기 바란다.

하지만 API 레벨 8이나 이상 버전의 고수준 관점에서는 단순히 다음 두 가지 새로운 기본 메소드만 의미가 있다.

getExternalFilesDir(String type)

getExternalStoragePublicDirectory(String type)

이러한 메소드 각각에 대해 이제 type 매개변수를 전달할 수 있음을 확인할 수 있을 것이다. type 매개변수는 파일의 종류를 지정할 수 있게 하며,

올바른 하위 폴더로 구성되게 만단다. 첫 번째 메소드에서 반환된 외부 파일 디렉토리 루트는 애플리케이션마다 다르기 때문에 애플리케이션이

삭제될 때 관련된 모든 파일 역시 외부 SD로부터 제거된다. 두 번째 메소드에서 반환된 파일 디렉토리 루트는 공개돼 있는 것으로, 이 경로에

저장된 파일은 애플리케이션이 삭제되더라도 계속 남아있게 된다. 저장하고자 하는 파일의 종류에 따라 어떠한 것을 사용할지 결정하면 된다.

예를 들어 파일이 애플리케이션 내에서 재생된 미디어 파일이고, 사용자가 애플리케이션을 삭제하려고 한다면 사용자에게 이 파일은 더 이상

필요가 없을 것이다.

하지만 애플리케이션을 통해 사용자가 자신의 전화기에 배경 화면을 다운로드할 수 있게 허용한다면 이 경우에는 공개된 디렉토리로 이미지

파일을 저장하게 고려해야 한다. 그 결과로 사용자가 애플리케이션을 삭제한다고 해도 이 파일은 여전히 시스템에 의해 접근 될 수 있을 것이다.

지정할 수 있는 type 매개변수는 다음과 같다.

DIRECTORY_ALARMS

DIRECTORY_DCIM

DIRECTORY_DOWMLOADS

DIRECTORY_MOVIES

DIRECTORY_MUSIC

DIRECTORY_NOTIFICATIONS

DIRECTORY_PICTURES

DIRECTORY_PODCASTS

DIRECTORY_RINGTONES

지금까지 내부/외부 저장소 매커니즘에 대한 다소 장황한 글을 마쳤다.

 

 

posted by 프띠버리 2013. 11. 15. 10:10

안드로이드의 내부 저장소 매커니즘부터 시작해보자. 표준 자바 프로그래밍에 경험이 있는 사람이라면 이 절은 꽤나 친숙하게 느껴질 것이다.

안드로이드의 내부저장소는 단순히 각 애플리케이션의 내부 메모리와 관련된 파일을 읽고 쓸 수 있게 해준다. 이 파일은 애플리케이션에 의해

서만 접근 가능하며, 다른 애플리케이션이나 다른 사용자에 의해서는 접근이 불가능하다. 또한 애플리케이션이 삭제되면 저장돼 있던 파일

또한 자동으로 삭제된다.

다음은 애플리케이션의 내부 저장소에 접근하는 방법에 대한 간단한 예다.

package jwei.apps.dataforandroid.ch1;

 

import java.io.FileOutputStream;

import java.io.IOException;

 

import jwei.apps.dataforandroid.R;

import android.app.Activity;

import android.content.Context;

import android.os.Bundle;

 

public class InternalStorageExample extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        // 파일 이름

        String fileName = "my_file.txt";

 

        // 파일에 쓸 문자열

        String msg = "Hello World.";

        try {

            // 파일 생성 후 쓰기

            FileOutputStream fos = openFileOutput(fileName, Context.MODE_PRIVATE);

            fos.write(msg.getBytes());

            fos.close();

        } catch (IOException e) {

            e.printStackTrace();

        }

 

    }

 

}

여기서는 단순히 Context 클래스의 openFileOutput() 메소드를 사용한다.

openFileOutput() 메소드는 첫 번째 인자로 생성될(혹은 덮어 쓸) 파일 이름을 받고, 두 번째 인자로 파일의 가시성을 받는다.

그 다음 원하는 문자열을 byte형태로 변환해 출력 스트림의 write() 메소드로 전달한다. openFileOutput() 으로 지정될 수 있는 부가적인

모드에 대해 살펴보자. 즉, 다음과 같다.

MODE_APPEND 이 모드는 기존 파일을 열어 문자열을 기존 내용에 추가(다름 모드에서는 기존 내용이 삭제된다)할 수 있게 해준다.

여러분이 이클립스로 프로그래밍을 하고 있다면 DDMS로 이동해 애플리케이션의 내부 파일을 살펴볼 수 있다.

 

따라서 방금 생성한 텍스트 파일을 볼 수 있을 것이다. 터미널로 개발하는 사람들은 /data/data/{앱 경로}/files/my_file.txt 경로에서 찾아볼 수 있다.

불행히도 다시 파일을 읽어오는 작업은 좀 더 복잡하다. 이와 관련된 코드는 다음과 비슷하다.

package jwei.apps.dataforandroid.ch1;

 

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStreamReader;

 

import jwei.apps.dataforandroid.R;

import android.app.Activity;

import android.os.Bundle;

import android.util.Log;

 

public class InternalStorageExample2 extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        // 파일 이름

        String fileName = "my_file.txt";

 

  // 이번에는 파일 입력 스트림을 연다.

        try {

            FileInputStream fis = openFileInput(fileName);

            InputStreamReader isr = new InputStreamReader(fis);

 

            // 임의의 길이로 문자열을 읽는다.

            StringBuilder sb = new StringBuilder();

            char[] inputBuffer = new char[2048];

            int l;

            // 버퍼에 데이터를 채운다.

            while ((l = isr.read(inputBuffer)) != -1) {

                sb.append(inputBuffer, 0, l);

            }

 

            // 바이트를 문자열로 변환한다.

            String readString = sb.toString();

            Log.i("LOG_TAG", "Read string: " + readString);

 

            // 파일을 삭제할 수도 있다.

            deleteFile(fileName);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

여기서 파일 입력 스트림을 열어 이를 스트림 리더에 전달했다. 이를 통해 read()메소드를 호출해 데이터를 바이트 형태로 읽어 StringBuyilder에 추가할

수 있었따. 내용 전체를 읽었다면 단순히 StringBuilder로부터 문자열을 반환한다. 그것이 전부다! 완전한 코드를 위해 마지막 부분에서 확인할 수 있듯이

Context 클래스는 내부 저장소에 저장된 파일을 삭제할 수 있는 간단한 메소드를 제공한다.

 

posted by 프띠버리 2013. 11. 15. 09:54

SharedPreferent 객체를 초기화하고 편집하는 방법을 알고, 데이터 저장 타임에 대한 몇 가지 일반적인 사례에 대해 생각해보는

것이 중요하다. 다음은 애플리케이션에서 저장 용도로 자주 사용하는 작은 기본형 키/값 데이터 쌍을 설명하는 몇가지 예제다.

 

사용자 애플리케이션의 첫 방문 체크

다수의 애플리케이션은 사용자가 처음 방문한 경우 지시 사항이나 튜토리얼 또는스플래시 스크린 액티비티를 보여주고 싶을 것이다.

public class SharedPreferencesExample2 extends Activity {

 

    private static final String MY_DB = "my_db";

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        SharedPreferences sp = getSharedPreferences(MY_DB, Context.MODE_PRIVATE);

 

        /**

         * 사용자의 첫 방문인지 체크한다.

         */

        boolean hasVisited = sp.getBoolean("hasVisited", false);

        if (!hasVisited) {

            // ...

            // 스플래시 액티비티와 로그인 액티비티 등을 보여준다.

            // ...

 

            // 변경을 적용하는 것을 잊지 말자!

            Editor e = sp.edit();

            e.putBoolean("hasVisited", true);

            e.commit();

        }

}

}

 

애플리케이션의 마지막 업데이트 시점 확인

많은 애플리케이션은 캐시나 싱크 혹은 정규 업데이트를 필요로 하는 내장 기능을 갖고 있을 것이다. 마지막 업데이트 시간을 저장하기 위해 얼마나 시간이

지났는지 신속하게 확인할 수 있으며, 업데이트나 싱크가 필요한지를 판단할 수 있다.

        /**

         * 마지막 업데이트 시간 확인

         */

        long lastVisitTime = sp.getLong("lastVisitKey", 0L);

        long timeElapsed = System.currentTimeMillis() - lastVisitTime;

 

        // 업데이트 주기

        final long UPDATE_FREQ = 1000 * 60 * 60 * 24;

 

        if (timeElapsed > UPDATE_FREQ) {

            // ...

            // 필요한 업데이트 수행

            // ...

        }

 

        // 최근 업데이트 시간 저장

        Editor e = sp.edit();

        e.putLong("lastVisitKey", System.currentTimeMillis());

        e.commit();

 

 

사용자의 로그인 사용자 이름 저장

많은 애플리케이션은 사용자가 자신의 사용자 이름(추가적으로 PIN과 전화번호 등 기타 로그인 관련 필드)을 기억하게 허용하며, SharedPreference는

단순한 기본형 문자열 ID를 저장하기 위한 강력한 방법이다.

        /**

         * 사용자 이름을 문자열로 캐시한다.

         */

        // 일반적으로 사용자가 자신의 이름을 입력할 수 있는 EDIT TEXT VIEW를 가진다.

        EditText userNameLoginText = (EditText) findViewById(R.id.login_editText);

        String userName = userNameLoginText.getText().toString();

 

        Editor e1 = sp.edit();

        e1.putString("userNameCache", userName);

        e1.commit();

 

 

애플리케이션의 상태 저장

다수의 애플리케이션에 있어 애플리케이션의 기능은 일반적으로 사용자에 의해 설정된 애플리케이션의 상태에 따라 변경된다. 전화 수신 애플리케이션을

생각해보자. 사용자 전화기가 무음 모드일 때는 어떤 기능도 동작되지 않게 해야 한다면 이는 기억해야 할 중요한 상태일 것이다.

        /**

         * 특정 상태 저장

         */

        boolean isSilentMode = sp.getBoolean("isSilentRinger", false);

        if (isSilentMode) {

            // ...

            // 사운드 끄기

            // ...

        }

 

 

사용자 위치 캐시

모든 위치 기반 애플리케이션은 여러가지 이유(아마도 사용자가 GPS를 껏거나 수신 감도가 미약하거나)로 사용자의 마지막 위치를 캐시하고 싶을 것이다.

이는 사용자의 위도와 경로를 float로 변환한 후  SharedPreference 인스턴스에 이 float 값을 저장하는 방법으로 쉽게 할 수 있다.

        /**

         * 위치 캐시

         */

        // LOCATION MANAGER 초기화

        LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

        // ...

        // LOCATION LISTENERS 무시한다.

        // ...

        Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

        float lat = (float) lastKnownLocation.getLatitude();

        float lon = (float) lastKnownLocation.getLongitude();

 

        Editor e2 = sp.edit();

        e2.putFloat("latitudeCache", lat);

        e2.putFloat("longitudeCache", lon);

        e.commit();

안드로이드 API 레벨 11에는 관련 키로 일련의 문자열 객체를 설정하거나 가져올 수 있게 하는 새로운 getStringSet() 메소드가 있다.

getStringSet이 어떻게 동작하는지 살펴보자.

Set<String> values = new HashSet<String>();

values.add("Hellp");

values.add("World");

Editor e = sp.edit();

e.putStringSet("strSetKey", values);

e.commit();

Set<String> ret = sp.getStringSet(values, new HashSet<String>());

 

for(String r : ret){

Log.i("SharedPreferencesExample", "Retrieved vals : " + r);

}

이와 관련된 사용 사례는 다양하지만, 지금은 그냥 넘어가자.

 

 

전체 소스

package jwei.apps.dataforandroid.ch1;

 

import jwei.apps.dataforandroid.R;

import android.app.Activity;

import android.content.Context;

import android.content.SharedPreferences;

import android.content.SharedPreferences.Editor;

import android.location.Location;

import android.location.LocationManager;

import android.os.Bundle;

import android.widget.EditText;

 

/**

 * Examples for use cases as defined in Chapter 1 of Data for Android.

 *

 * @author Jason Wei

 *

 */

public class SharedPreferencesExample2 extends Activity {

 

    private static final String MY_DB = "my_db";

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        SharedPreferences sp = getSharedPreferences(MY_DB, Context.MODE_PRIVATE);

 

        /**

         * CHECK IF THIS IS USER'S FIRST VISIT

         */

        boolean hasVisited = sp.getBoolean("hasVisited", false);

        if (!hasVisited) {

            // ...

            // SHOW SPLASH ACTIVITY, LOGIN ACTIVITY, ETC

            // ...

 

            // DON'T FORGET TO COMMIT THE CHANGE

            Editor e = sp.edit();

            e.putBoolean("hasVisited", true);

            e.commit();

        }

 

        /**

         * CHECK LAST VISIT TIME

         */

        long lastVisitTime = sp.getLong("lastVisitKey", 0L);

        long timeElapsed = System.currentTimeMillis() - lastVisitTime;

 

        // YOUR UPDATE FREQUENCY HERE

        final long UPDATE_FREQ = 1000 * 60 * 60 * 24;

 

        if (timeElapsed > UPDATE_FREQ) {

            // ...

            // PERFORM NECESSARY UPDATES

            // ...

        }

 

        // STORE LATEST UPDATE TIME

        Editor e = sp.edit();

        e.putLong("lastVisitKey", System.currentTimeMillis());

        e.commit();

 

        /**

         * CACHE USER NAME AS STRING

         */

        // TYPICALLY YOU WILL HAVE AN EDIT TEXT VIEW

        // WHERE THE USER ENTERS THEIR USERNAME

        EditText userNameLoginText = (EditText) findViewById(R.id.login_editText);

        String userName = userNameLoginText.getText().toString();

 

        Editor e1 = sp.edit();

        e1.putString("userNameCache", userName);

        e1.commit();

 

        /**

         * REMEBERING A CERTAIN STATE

         */

        boolean isSilentMode = sp.getBoolean("isSilentRinger", false);

        if (isSilentMode) {

            // ...

            // TURN OFF SOUND

            // ...

        }

 

        /**

         * CACHING A LOCATION

         */

        // INSTANTIATE LOCATION MANAGER

        LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

        // ...

        // IGNORE LOCATION LISTENERS

        // ...

        Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

        float lat = (float) lastKnownLocation.getLatitude();

        float lon = (float) lastKnownLocation.getLongitude();

 

        Editor e2 = sp.edit();

        e2.putFloat("latitudeCache", lat);

        e2.putFloat("longitudeCache", lon);

        e.commit();

    }

}

 

 

 

'Programing > Android' 카테고리의 다른 글

[SQL/DB] SQLite 데이터베이스  (0) 2013.11.15
[SQL/DB] 외부 저장소 메소드  (0) 2013.11.15
[SQL/DB] 내부 저장소 메소드  (0) 2013.11.15
[SQL/DB] SharedPreferences의 사용  (0) 2013.11.13
posted by 프띠버리 2013. 11. 13. 16:22

SharedPreferences는 안드로이드 애플리케이션에 지역 데이터를 저장하기 위한 가장 단순하고 빠른 효과적인 방법이다.

SharedPreferences는 애플리케이션을 통해 다양한 키/값 쌍을 저장하고 연관 짓게 해주는 필수 프레임워크이며, 모든

애플리케이션은 자신의 SharedPreferences 클래스와 관련돼 있기 때문에 저장 후 커밋(committed)된 데이터는 모든

사용자 세션을 넘어 유지된다. 하지만 SharedPreferences의 단숨함과 효율적인 특성으로 인해 기본 데이터 타입만 저장

가능하므로 SharedPreferences의 저장 대상에 대해 유념해야 한다.

(기본 데이터 타입 : boolean, float, long, int, string)

<예제>

package jwei.apps.dataforandroid.ch1;

 

import jwei.apps.dataforandroid.R;

import android.app.Activity;

import android.content.Context;

import android.content.SharedPreferences;

import android.content.SharedPreferences.Editor;

import android.os.Bundle;

import android.util.Log;

 

/**

 * Examples for initial case as defined in Chapter 1 of Data for Android.

 *

 * @author Jason Wei

 *

 */

public class SharedPreferencesExample extends Activity {

 

    private static final String MY_DB = "my_db";

 

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        SharedPreferences sp = getSharedPreferences(MY_DB, Context.MODE_PRIVATE);

 

        Editor e = sp.edit();

        e.putString("testStringKey", "Hello World");

        e.putBoolean("testBooleanKey", true);

        e.commit();

 

        String stringValue = sp.getString("testStringKey", "error");

        boolean booleanValue = sp.getBoolean("testBooleanKey", false);

 

        Log.i("SharedPreferencesExample", "Retrieved string value: " + stringValue);

        Log.i("SharedPreferencesExample", "Retrieved boolean value: " + booleanValue);

    }

}

-----------------------------------------------------------------------------------------------------------------------------------

 

getSharedPreferences(String mapName, int mapMode)

MODE 설명 

MODE_WORLD_READABLE : 다른 애플리케이션에서 접근 가능하게 맵의 가시성을 설정한다. 내용은 읽기 전용이다.

MODE_WORLD_WRITABLE : 다른 애플리케이션에서 접근 가능하게 맵의 가시성을 설정한다. 읽기와 쓰기 모두 가능.

MODE_MULTI_PROCESS : API 레벨 11 이후로 사용 가능한 모드. 다중 프로세스에 의해 맵을 수정할 수 있게 허용함.

                                      동일한 SharedPreferences 인스턴스에 쓰기 작업이 일어날 수 있다.

 

posted by 프띠버리 2013. 10. 16. 15:18

DB를 이용한 간단 메모장 앱 완성본 파일


1. 테이블 및 셀 USER INTERFACE로 바꿈

2. 화면전환 효과 적용(메모장을 넘기고 펼치는듯한 느낌)

3.기존의 버튼들 아이콘화



MemoPad.zip



'Programing > IOS' 카테고리의 다른 글

DB를 이용한 간단 메모장 앱  (0) 2013.10.14
간단한 흔들기 제스쳐  (0) 2013.10.07
SQL 을 이용한 DB프로그램 테이블에 뿌리기  (0) 2013.10.07
세그먼트 연습  (0) 2013.10.04
구구단 퀴즈 프로그램 앱  (0) 2013.10.04
posted by 프띠버리 2013. 10. 14. 16:16

  1 import java.util.*;
  2 
  3 public class ArrayTest
  4 {
  5     public static void main(String[] args)
  6     {
  7         int[] arr = new int[5];
  8         int i, j, k, temp, lng, count = 0;
  9         lng = arr.length;
 10         String str;
 11         Scanner scn = new Scanner(System.in);
 12 
 13         for(i=0;i<lng;i++)
 14         {
 15             str = scn.next();
 16             arr[i] = Integer.parseInt(str);
 17         }
 18 
 19         for(j=0;j<lng-1;j++)
 20         {
 21             for(k=j+1;k<lng;k++)
 22             {
 23                 if(arr[j]>arr[k])
 24                 {
 25                     temp = arr[j];
 26                     arr[j] = arr[k];
 27                     arr[k] = temp;
 28                 }
 29             }
 30         }
 31 
 32         System.out.println("");
 33 
 34         for(i=0;i<lng;i++)
 35         {
 36             System.out.printf("%d\t", arr[i]);
 37         }
 38 
 39         System.out.printf("\n\n");
 40 
 41         for(i=0;i<lng;i++)
 42         {
 43             count = 0;
 44             for(j=0;j<lng;j++)
 45             {
 46                 if(i != j)
 47                 {
 48                     if(arr[i] == arr[j])
 49                     {
 50                         count = count + 1;
 51                     }
 52                 }
 53             }
 54             System.out.printf("arr[%d] 와 같은 수 %d개\n",i, count);
 55         }
 56     }
 57 }

'Programing > JAVA' 카테고리의 다른 글

숫자맞추기 게임 랜덤답 주기  (0) 2013.10.10
posted by 프띠버리 2013. 10. 14. 16:12


MemoData.h

#import <Foundation/Foundation.h>


@interface MemoData : NSObject

{

    NSInteger mIndex;

    NSString *mTitle;

    NSString *mContent;

    NSString *mDate;

}


@property(nonatomic, assign) NSInteger mIndex;

@property(nonatomic, retain) NSString *mTitle;

@property(nonatomic, retain) NSString *mContent;

@property(nonatomic, retain) NSString *mDate;


- (id)initWithData:(NSInteger)pIndex Title:(NSString *)pTitle Content:(NSString *)pContent Date:(NSString *)pDate;


@end




MemoData.m

#import "MemoData.h"


@implementation MemoData

@synthesize mIndex;

@synthesize mTitle;

@synthesize mContent;

@synthesize mDate;


- (id)initWithData:(NSInteger)pIndex Title:(NSString *)pTitle Content:(NSString *)pContent Date:(NSString *)pDate

{

    self.mIndex = pIndex;

    self.mTitle = pTitle;

    self.mContent = pContent;

    self.mDate = pDate;

    

    return self;

}


@end




MemoPadAppDelegate.h

#import <UIKit/UIKit.h>

#import <sqlite3.h>


@interface MemoPadAppDelegate : UIResponder <UIApplicationDelegate>

{

    UIWindow *window;

    UINavigationController *navigationController;

    NSString *DBName;

    NSString *DBPath;

    NSMutableArray *DBData;

    BOOL isFirstTimeAccess;

}


@property (retain, nonatomic) IBOutlet UIWindow *window;

@property (retain, nonatomic) IBOutlet UINavigationController *navigationController;

@property (retain, nonatomic) NSString *DBName;

@property (retain, nonatomic) NSString *DBPath;

@property (retain, nonatomic) NSMutableArray *DBData;

@property (assign) BOOL isFirstTimeAccess;


@property (nonatomic, assign) NSInteger currentMemoSQLIndex;

@property (nonatomic, assign) NSInteger currentMemoRowIndex;


- (void)checkAndreateDatabase;

- (void)readMemoFromDatabase;

- (void)writeMemoToDatabaseWithTitle:(NSString *)inputTitle Content:(NSString *)inputContent;

- (void)updateMemoToDatabaseWithTitle:(NSString *)inputTitle Content:(NSString *)inputContent;


- (void)deleteMemoFromDatabase;


@end




MemoPadAppDelegate.m

#import "MemoPadAppDelegate.h"

#import "RootViewController.h"

#import "MemoData.h"



@implementation MemoPadAppDelegate


@synthesize window;

@synthesize navigationController;

@synthesize DBData;

@synthesize DBName;

@synthesize DBPath;

@synthesize isFirstTimeAccess;


@synthesize currentMemoRowIndex;

@synthesize currentMemoSQLIndex;


- (void)dealloc

{

    [window release];

    [super dealloc];

}


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    // 최초 액세스인가?

    self.isFirstTimeAccess = TRUE;

    

    // 앱의 파일 시스템 경로를 구한다.

    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *documentsDir = [documentPaths objectAtIndex:0];

    

    // 데이터베이스 이름과 경로를 저장한다.

    self.DBName = @"MemoPad.db";

    self.DBPath = [documentsDir stringByAppendingPathComponent:self.DBName];

    

    [self checkAndreateDatabase];

    

    [window addSubview:[navigationController view]];

    [window makeKeyAndVisible];

    return YES;

}


- (void)checkAndreateDatabase

{

    NSFileManager *fileManager = [NSFileManager defaultManager];

    

    if([fileManager fileExistsAtPath:self.DBPath])

    {

        return;

    }

    else

    {

        NSString *databasePathFromApp = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:self.DBName];

        [fileManager copyItemAtPath:databasePathFromApp toPath:self.DBPath error:nil];

    }

}


- (void)readMemoFromDatabase

{

    sqlite3 *database;

    

    // 데이터를 담을 오브젝트인 DBData 초기화 한다.

    // 데이터베이스에 처음 엑세스 하는 것이라면 alloc으로 메모리 할당

    if(self.isFirstTimeAccess == TRUE)

    {

        self.DBData = [[NSMutableArray alloc] init];

        self.isFirstTimeAccess = FALSE;

    }

    else // 처음 엑세스하는 것이 아니면 removeAllObject DBData 초기화 한다.

    {

        [self.DBData removeAllObjects];

    }

    

    // 데이터베이스 파일을 open 한다.

    if(sqlite3_open([self.DBPath UTF8String], &database) == SQLITE_OK)

    {

        const char *sqlStatement = "SELECT * FROM tblMemoPad ORDER BY MP_Index DESC";

        sqlite3_stmt *compiledStatement;

        if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)

        {

            while(sqlite3_step(compiledStatement) == SQLITE_ROW)

            {

                NSInteger aIndex = sqlite3_column_int(compiledStatement, 0);

                NSString *aTitle = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];

                NSString *aContent = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];

                NSString *aDate = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];

                

                NSLog(@"%d  / %@  / %@  /  %@",aIndex, aTitle, aContent, aDate);

                

                MemoData *md = [[MemoData alloc] initWithData:aIndex Title:aTitle Content:aContent Date:aDate];

                [self.DBData addObject:md];

            }

        }

        else

        {

            printf("could not prepare statement : %s\n", sqlite3_errmsg(database));

        }

        sqlite3_finalize(compiledStatement);

    }

    sqlite3_close(database);

}


- (void)applicationWillResignActive:(UIApplication *)application

{

    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.

}


- (void)applicationDidEnterBackground:(UIApplication *)application

{

    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 

    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

}


- (void)applicationWillEnterForeground:(UIApplication *)application

{

    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.

}


- (void)applicationDidBecomeActive:(UIApplication *)application

{

    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

}


- (void)applicationWillTerminate:(UIApplication *)application

{

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

}


- (void)writeMemoToDatabaseWithTitle:(NSString *)inputTitle Content:(NSString *)inputContent

{

    sqlite3 *database;

    

    if(sqlite3_open([self.DBPath UTF8String], &database) == SQLITE_OK)

    {

        const char *sqlStatement = "INSERT INTO tblMemoPad(MP_Title, MP_Content, MP_Date) VALUES(?,?,?)";

        sqlite3_stmt *compiledStatement;

        

        if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)

        {

            NSDate *date = [NSDate date];

            NSCalendar *calendar = [NSCalendar currentCalendar];

            

            NSCalendarUnit unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

            NSDateComponents *comp = [calendar components:unitFlags fromDate:date];

            

            NSString *yearOfCommonEra = [NSString stringWithFormat:@"%02d", [comp year]];

            NSString *monthOfYear = [NSString stringWithFormat:@"%02d", [comp month]];

            NSString *dayOfMonth = [NSString stringWithFormat:@"%02d", [comp day]];

            NSString *hourOfDay = [NSString stringWithFormat:@"%02d", [comp hour]];

            NSString *minuteOfHour = [NSString stringWithFormat:@"%02d", [comp minute]];

            NSString *secondOfMinute = [NSString stringWithFormat:@"%02d", [comp second]];

            

            NSString *dateStringConcat = [NSString stringWithFormat:@"%@-%@-%@", yearOfCommonEra, monthOfYear, dayOfMonth];

            NSString *timeStringConcat = [NSString stringWithFormat:@"%@:%@:%@", hourOfDay, minuteOfHour, secondOfMinute];

            

            NSString *dateTimeString = [NSString stringWithFormat:@"%@ %@", dateStringConcat, timeStringConcat];

            

            sqlite3_bind_text(compiledStatement, 1, [inputTitle UTF8String], -1, SQLITE_TRANSIENT);

            sqlite3_bind_text(compiledStatement, 2, [inputContent UTF8String], -1, SQLITE_TRANSIENT);

            sqlite3_bind_text(compiledStatement, 3, [dateTimeString UTF8String], -1, SQLITE_TRANSIENT);

            

            if(SQLITE_DONE != sqlite3_step(compiledStatement))

            {

                NSAssert1(0, @"Error while inserting into tblLocationHistory. '%s'", sqlite3_errmsg(database));

            }

            sqlite3_reset(compiledStatement);

            sqlite3_close(database);

        }

        else

        {

            printf("could not prepare statemont : %s\n", sqlite3_errmsg(database));

        }

    }

    else

    {

        sqlite3_close(database);

        

        NSAssert1(0, @"Error opening database in tblLocationHistory. '%s'", sqlite3_errmsg(database));

    }

}


- (void)updateMemoToDatabaseWithTitle:(NSString *)inputTitle Content:(NSString *)inputContent

{

    sqlite3 *database;

    

    if(sqlite3_open([self.DBPath UTF8String], &database) == SQLITE_OK)

    {

        NSString *sqlStatementNS = [[NSString alloc] initWithString:[NSString stringWithFormat:@"UPDATE tblMemoPad SET MP_Title=?, MP_Content=?, MP_Date=? WHERE MP_Index = '%d'", self.currentMemoSQLIndex]];

        

        NSLog(@"SQL = %@", sqlStatementNS);

        

        const char *sqlStatement = [sqlStatementNS UTF8String];

        

        sqlite3_stmt *compiledStatement;

        

        if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)

        {

            NSDate *date = [NSDate date];

            NSCalendar *calendar = [NSCalendar currentCalendar];

            

            NSCalendarUnit unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

            NSDateComponents *comp = [calendar components:unitFlags fromDate:date];

            

            NSString *yearOfCommonEra = [NSString stringWithFormat:@"%02d", [comp year]];

            NSString *monthOfYear = [NSString stringWithFormat:@"%02d", [comp month]];

            NSString *dayOfMonth = [NSString stringWithFormat:@"%02d", [comp day]];

            NSString *hourOfDay = [NSString stringWithFormat:@"%02d", [comp hour]];

            NSString *minuteOfHour = [NSString stringWithFormat:@"%02d", [comp minute]];

            NSString *secondOfMinute = [NSString stringWithFormat:@"%02d", [comp second]];

            

            NSString *dateStringConcat = [NSString stringWithFormat:@"%@-%@-%@", yearOfCommonEra, monthOfYear, dayOfMonth];

            NSString *timeStringConcat = [NSString stringWithFormat:@"%@:%@:%@", hourOfDay, minuteOfHour, secondOfMinute];

            

            NSString *dateTimeString = [NSString stringWithFormat:@"%@ %@", dateStringConcat, timeStringConcat];

            

            sqlite3_bind_text(compiledStatement, 1, [inputTitle UTF8String], -1, SQLITE_TRANSIENT);

            sqlite3_bind_text(compiledStatement, 2, [inputContent UTF8String], -1, SQLITE_TRANSIENT);

            sqlite3_bind_text(compiledStatement, 3, [dateTimeString UTF8String], -1, SQLITE_TRANSIENT);

            

            if(SQLITE_DONE != sqlite3_step(compiledStatement))

            {

                NSAssert1(0, @"Error while inserting into tblLocationHistory. '%s'", sqlite3_errmsg(database));

            }

            sqlite3_reset(compiledStatement);

            sqlite3_close(database);

        }

        else

        {

            printf("could not prepare statemont : %s\n", sqlite3_errmsg(database));

        }

    }

    else

    {

        sqlite3_close(database);

        

        NSAssert1(0, @"Error opening database in tblLocationHistory. '%s'", sqlite3_errmsg(database));

    }

    

}


- (void)deleteMemoFromDatabase

{

    sqlite3 *database;

    

    if(sqlite3_open([self.DBPath UTF8String], &database) == SQLITE_OK)

    {

        NSString *sqlStatementNS = [[NSString alloc] initWithString:[NSString stringWithFormat:@"DELETE FROM tblMemoPad WHERE MP_Index = '%d'", self.currentMemoSQLIndex]];

        const char *sqlStatement = [sqlStatementNS UTF8String];

        sqlite3_stmt *compiledStatement;

        

        if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)

        {

            if(SQLITE_DONE != sqlite3_step(compiledStatement))

            {

                NSAssert1(0, @"Error while inserting into tblLocationHistory. '%s'", sqlite3_errmsg(database));

            }

            sqlite3_reset(compiledStatement);

        }

        sqlite3_finalize(compiledStatement);

    }

    sqlite3_close(database);

}


@end




RootViewController.h

#import <UIKit/UIKit.h>


@interface RootViewController : UITableViewController

{

    IBOutlet UIBarButtonItem *btnWrite;

}


- (IBAction)btnWriteTouched:(id)sender;


@end




RootViewController.m

#import "RootViewController.h"

#import "MemoPadAppDelegate.h"

#import "MemoData.h"

#import "ContentViewController.h"

#import "MemoWriteViewController.h"


@interface RootViewController ()


@end


@implementation RootViewController


- (id)initWithStyle:(UITableViewStyle)style

{

    self = [super initWithStyle:style];

    if (self) {

        // Custom initialization

    }

    return self;

}


- (void)viewDidLoad

{

    [super viewDidLoad];

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *)[[UIApplication sharedApplication] delegate];

    [appDelegate readMemoFromDatabase];

    

    self.title = @"MemoPad";

}


- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


- (void)viewWillAppear:(BOOL)animated

{

    [super viewWillAppear:animated];

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *) [[UIApplication sharedApplication] delegate];

    [appDelegate readMemoFromDatabase];

    

    NSLog(@"reloadData");

    [self.tableView reloadData];

}


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

{

    // Return the number of sections.

    return 1;

}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *)[[UIApplication sharedApplication] delegate];

    

    int rowCount = [[appDelegate DBData] count];

    

    return rowCount;

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    }

    

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *)[[UIApplication sharedApplication] delegate];

    MemoData *mData = [[appDelegate DBData] objectAtIndex:indexPath.row];

    

    cell.textLabel.text = mData.mTitle;

    

    return cell;

}



- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    ContentViewController *contentViewController = [[ContentViewController alloc] initWithNibName:@"ContentViewController" bundle:nil];

    

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *) [[UIApplication sharedApplication] delegate];

    

    [appDelegate setCurrentMemoRowIndex:indexPath.row];

    

    MemoData *tempData = [[appDelegate DBData] objectAtIndex:indexPath.row];

    [appDelegate setCurrentMemoSQLIndex:[tempData mIndex]];

    

    [self.navigationController pushViewController:contentViewController animated:YES];

}


- (IBAction)btnWriteTouched:(id)sender

{

    MemoWriteViewController *memoWriteViewController = [[MemoWriteViewController alloc] initWithNibName:@"MemoWriteViewController" bundle:nil];

    

    [self.navigationController presentViewController:memoWriteViewController animated:YES completion:nil];

    

}

@end




ContentViewController.h

#import <UIKit/UIKit.h>

#import "MemoData.h"


@interface ContentViewController : UIViewController<UIAlertViewDelegate>

{

    IBOutlet UILabel *labelDate;

    IBOutlet UILabel *labelTitle;

    IBOutlet UITextView *tvContent;

    IBOutlet UIBarButtonItem *btnDelete;

    IBOutlet UIBarButtonItem *btnEdit;

    IBOutlet UISlider *sliderFontSize;

    

    MemoData *mData;

}


- (IBAction)deleteMemo:(id)sender;

- (IBAction)editMemo:(id)sender;

- (IBAction)changeFontSize:(id)sender;


@property (retain, nonatomic) IBOutlet UILabel *labelDate;

@property (retain, nonatomic) IBOutlet UILabel *labelTitle;

@property (retain, nonatomic) IBOutlet UITextView *tvContent;

@property (retain, nonatomic) IBOutlet UIBarButtonItem *btnDelete;

@property (retain, nonatomic) IBOutlet UIBarButtonItem *btnEdit;

@property (retain, nonatomic) IBOutlet UISlider *sliderFontSize;

@property (retain, nonatomic) MemoData *mData;


@end




ContentViewController.m

#import "ContentViewController.h"

#import "MemoPadAppDelegate.h"

#import "MemoEditViewController.h"


@interface ContentViewController ()


@end


@implementation ContentViewController


@synthesize labelDate;

@synthesize labelTitle;

@synthesize tvContent;

@synthesize mData;

@synthesize btnDelete;

@synthesize btnEdit;

@synthesize sliderFontSize;


- (IBAction)changeFontSize:(id)sender

{

    int fontSizeValue = self.sliderFontSize.value;

    

    self.tvContent.font = [UIFont fontWithName:@"Helvetica" size:fontSizeValue];

}


- (IBAction)deleteMemo:(id)sender

{

    UIAlertView *alertView;


    alertView = [[UIAlertView alloc] initWithTitle:@"MemoPad" message:nil delegate:self cancelButtonTitle:@"No" otherButtonTitles:@"Yes", nil];

    [alertView setMessage:@"Do you want to delete the memo?"];

    [alertView show];

    [alertView release];

//    MemoEditViewController *memoEditViewController = [[MemoEditViewController alloc] initWithNibName:@"MemoEditViewController" bundle:nil];

//    [self.navigationController presentViewController:memoEditViewController animated:YES completion:nil];

//    [memoEditViewController release];

}


- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

{

    if(buttonIndex == 1)

    {

        MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *) [[UIApplication sharedApplication] delegate];

        [appDelegate deleteMemoFromDatabase];

        [self.navigationController popViewControllerAnimated:YES];

    }

}


- (IBAction)editMemo:(id)sender

{

    MemoEditViewController *memoEditViewController = [[MemoEditViewController alloc] initWithNibName:@"MemoEditViewController" bundle:nil];

    

    [self.navigationController presentViewController:memoEditViewController animated:YES completion:nil];

    [memoEditViewController release];

}


- (void)viewWillAppear:(BOOL)animated

{

    [super viewWillAppear:animated];

}


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // Custom initialization

    }

    return self;

}


- (void)viewDidLoad

{

    [super viewDidLoad];

    // Do any additional setup after loading the view from its nib.

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *) [[UIApplication sharedApplication] delegate];

    self.mData = [[appDelegate DBData] objectAtIndex:[appDelegate currentMemoRowIndex]];

    

    self.labelDate.text = [self.mData mDate];

    self.labelTitle.text = [self.mData mTitle];

    self.tvContent.text = [self.mData mContent];

    self.tvContent.font = [UIFont fontWithName:@"Helvetica" size:12.0];

}


- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


@end




MemoWriteViewController.h

#import <UIKit/UIKit.h>


@interface MemoWriteViewController : UIViewController<UITextViewDelegate>

{

    IBOutlet UITextField *tfTitle;

    IBOutlet UITextView *tvContent;

    IBOutlet UINavigationBar *navigationBar;

    IBOutlet UIBarButtonItem *btnSave;

    IBOutlet UIBarButtonItem *btnCancel;

}


@property (retain, nonatomic) IBOutlet UITextField *tfTitle;

@property (retain, nonatomic) IBOutlet UITextView *tvContent;

@property (retain, nonatomic) IBOutlet UINavigationBar *navigationBar;

@property (retain, nonatomic) IBOutlet UIBarButtonItem *btnSave;

@property (retain, nonatomic) IBOutlet UIBarButtonItem *btnCancel;


- (IBAction)saveMemo:(id)sender;

- (IBAction)cancelMemo:(id)sender;



@end




MemoWriteViewController.m

#import "MemoWriteViewController.h"

#import "MemoPadAppDelegate.h"


@interface MemoWriteViewController ()


@end


@implementation MemoWriteViewController


@synthesize tfTitle;

@synthesize tvContent;

@synthesize navigationBar;

@synthesize btnCancel;

@synthesize btnSave;


- (IBAction)saveMemo:(id)sender

{

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *)[[UIApplication sharedApplication] delegate];

    [appDelegate writeMemoToDatabaseWithTitle:[self.tfTitle text] Content:[self.tvContent text]];

    [self dismissViewControllerAnimated:YES completion:nil];

}


- (IBAction)cancelMemo:(id)sender

{

    [self dismissViewControllerAnimated:YES completion:nil];

}


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // Custom initialization

    }

    return self;

}


- (void)viewDidLoad

{

    [super viewDidLoad];

    // Do any additional setup after loading the view from its nib.

}


- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


@end




MemoEditViewController.h

#import <UIKit/UIKit.h>

#import "MemoData.h"


@interface MemoEditViewController : UIViewController

{

    IBOutlet UITextField *tfTitle;

    IBOutlet UITextView *tvContent;

    IBOutlet UIBarButtonItem *btnSave;

    IBOutlet UIBarButtonItem *btnCancel;

    

    MemoData *mData;

}


@property(retain, nonatomic) IBOutlet UITextField *tfTitle;

@property(retain, nonatomic) IBOutlet UITextView *tvContent;

@property(retain, nonatomic) MemoData *mData;


- (IBAction)saveMemo:(id)sender;

- (IBAction)cancelMemo:(id)sender;


@end




MemoEditViewController.m

#import "MemoEditViewController.h"

#import "MemoPadAppDelegate.h"


@interface MemoEditViewController ()


@end


@implementation MemoEditViewController


@synthesize tfTitle;

@synthesize tvContent;

@synthesize mData;


- (IBAction)saveMemo:(id)sender

{

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *) [[UIApplication sharedApplication] delegate];

    

    [appDelegate updateMemoToDatabaseWithTitle:[self.tfTitle text] Content:[self.tvContent text]];

    [self dismissViewControllerAnimated:YES completion:nil];

}


- (IBAction)cancelMemo:(id)sender

{

    [self dismissViewControllerAnimated:YES completion:nil];

    

}


- (void)viewDidAppear:(BOOL)animated

{

    [self.tfTitle becomeFirstResponder];

}


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // Custom initialization

    }

    return self;

}


- (void)viewDidLoad

{

    [super viewDidLoad];

    

    MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *) [[UIApplication sharedApplication] delegate];

    self.mData = [[appDelegate DBData] objectAtIndex:[appDelegate currentMemoRowIndex]];

    

    self.tfTitle.text = [self.mData mTitle];

    self.tvContent.text = [self.mData mContent];

    self.tvContent.font = [UIFont fontWithName:@"Helvetica" size:12.0];

}


- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


@end


posted by 프띠버리 2013. 10. 10. 11:45
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Scanner 및 Random 메소드를 쓰기 위해 불러옴
import java.util.*;
 
// class명은 LetterGame
public class LetterGame
{
    // 본문 시작
    public static void main(String args[])
    {
        // Random 속성을 가지는 randomGenerator 변수 선언
        Random randomGenerator = new Random();
 
        // answer 변수는 0부터 100 사이의 숫자를 무작위로 하나 가진다.
        int answer = randomGenerator.nextInt(100);
 
        // 정수를 입력받아서 저장할 guess 변수 선언
        int guess;
 
        // 시도 횟수를 보여주기 위한 tries 변수를 선언하고 초기값은 0으로 함.
        int tries = 0;
 
        // scan 변수는 Scanner의 속성을 가진다.
        Scanner scan = new Scanner(System.in);
 
        // 화면에 ""안의 내용을 보여주고 줄을 바꾼다.
        System.out.println("<< 0 부터 100까지 사이의 정수를 입력하세요 >>");
    
        // 입력한 정수가 answer의 값과 같을때 까지 무한 반복
        do
        {
            // 줄을바꾸지 않은 상태로 ""안의 내용을 출력
            System.out.print("정답을 추측하여 보십시오 : ");
            // 정수를 입력하여 guess변수에 저장
            guess = scan.nextInt();
            // 시도횟수 변수가 1증가
            tries++;
 
            // 만약 입력한 정수가 answer보다 높으면
            if(guess>answer)
            {
                // ""안의 내용을 출력하고 줄바꿈
                System.out.println("제시한 정수가 높습니다.");
            }
            // 만약 입력한 정수가 answer보다 낮으면
            if(guess<answer)
            {
                // ""안의 내용을 출력하고 줄바꿈
                System.out.println("제시한 정수가 낮습니다.");
            }
        }while(guess != answer); // guess 변수와 answer변수가 같지 않으면!!
 
        // ""안의 내용을 출력하고 총 시도횟수를 표시해줌 -
        // printf를 써서 ("축하합니다. 시도횟수 : %d", tries) 로도 사용 가능
        System.out.printf("축하합니다. 시도횟수 : " + tries);
    }
}

'Programing > JAVA' 카테고리의 다른 글

JAVA 테스트 소스  (0) 2013.10.14
posted by 프띠버리 2013. 10. 7. 15:24


ViewController.h


#import <UIKit/UIKit.h>


@interface ViewController : UIViewController


@property(nonatomic, strong) IBOutlet UIImageView *myImageView;


@end






ViewController.m




#import "ViewController.h"


@interface ViewController ()


@end


@implementation ViewController

@synthesize myImageView;


- (BOOL)canBecomeFirstResponder

{

    return YES;

}


- (void)viewDidAppear:(BOOL)animated

{

    [self becomeFirstResponder];

}


// 흔들기 시작할 호출됨

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event

{

    [self.myImageView setImage:[UIImage imageNamed:@"dice.png"]];

}


// 흔들기 마칠 호출함

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event

{

    NSString *str = [NSString stringWithFormat:@"dice%d.png", arc4random()%6+1];

    [self.myImageView setImage:[UIImage imageNamed:str]];

}


- (void)viewDidLoad

{

    random();

    [super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

}


- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


@end