'안드로이드 앱개발'에 해당되는 글 32건

 

1. URI

 

안드로이드의 보안 정책상 응용 프로그램이 만든 데이터는 기본적으로 혼자만 액세스 가능하다. 자신이 만든 데이터를 외부로 공개할 때는 Content Provider를 제공해야한다.

 

콘텐트 프로바이더는 응용 프로그램을 구성하는 컴포넌트 중 하나로서 데이터를 제공하는 역할을 한다.

 

URI(Uniform Resource Identifier)는 웹상의 주소를 나타내는 URL보다 더 상위의 개념이다. 국제 표준에 URI를 작성하는 방식은 다음과 같이 명시되어 있다.

 

    content://authority/path/id

 

content://는 이 문자열이 URI임을 나타내는 접두이며 무조건 붙여야 한다. authority는 정보 제공자의 명칭이되 중복되면 안 되므로 패키지명을 사용할 것을 권장한다.

 

path는 정보의 종류를 지정하는 가상의 경로이다. id는 어떤 정보를 원하는지 지정하되 전체 정보를 다 읽을 때는 생략할 수 있다.

 

CP는 단수와 복수에 대해 두 가지 형태의 URI를 각각 정의해야 한다.

 

id까지만 있으면 단수이고 path 까지만 있으면 복수이다. 다음은 URI의 몇 가지 예로서 stockmarket이라는 가상의 회사에서 제공하는 정보의 예이다.

 

    content://com.stockmarket/stock //주식 정보

    content://com.stockmarket/stock/posco //posco 종목의 주식 정보

 

URI는 항상 content://로 시작하고 이어서 정보 제공자를 밝히는 저작권 정보가 온다. com.stockmarket이라는 자사의 URL을 사용했으므로 웬만해서는 제공자가 중복되지 않을 것이다.

 

정보 제공자와 정보의 종류에 따라 이 형식대로 URI 문자열을 만든 후 다음 정적 메소드로 URI 객체를 생성한다.

 

    static Uri parse(String uriString)

 

이 메소드는 성능상의 문제로 인해 에러 처리는 하지 않는다. 즉 URI를 잘못 작성하면 쓰레기값이 조사된다.

 

다음 메소드는 URI의 path 정보를 문자열 목록으로 조사한다.

 

    List<String> getPathSegments()

 

이 목록의 0번째 요소가 path이며 1번째 요소가 id이되 /를 제외한 문자열만 조사된다.

 

문자열을 비교해서는 속도가 느리므로 UriMatcher라는 유틸리티 클래스를 이용해서 쉽게 문자열을 비교 할 수 있다.

 

    void addURI(String authority, String path, int code)

    int match(Uri uri)

 

addURI메소드로 authority, path의 쌍으로 정수 코드와 대응시켜 맵을 등록한다. path 에서 *는 임의의 문자열과 대응되며 #은 숫자 하나와 대응된다.

match 메소드는 uri를 분석하여 등록된 정수 코드를 리턴한다. 만약 uri에 해당하는 코드가 발견되지 않으면 -1을 리턴한다.

 

CP는 클라이언트의 URI를 일일이 분석할 필요없이 UriMatcher가 분석해 놓은 정수 코드로 요청을 파악하여 정보를 리턴하면 된다.

정수로부터 요청을 구분할 수 있으므로 switch case문으로 분기할 수 있어 편리하고 코드도 깔끔하게 정리된다.

 

2. 자료공유

 

CP를 만들기 위해서 먼저 ContentProvider클래스를 상속받아야 하며 정보를 관리 및 제공하는 메소드를 재정의해야 한다.

 

onCreate는 CP가 로드될 때 호출되는데 여기서 제공할 데이터를 준비한다.

 

예를 들어 데이터베이스에 들어 있는 정보라면 onCreate에서 DB를 열어 두면 된다. 다음 메소드는 제공하는 데이터의 MIME 타입을 조사한다.

 

    String getType(Uri uri)

 

대체로 다음 형식대로 작성한다.

 

    1. 단수 : vnd.회사명.cursor.item/타입

    2. 복수 : vnd.회사명.cursor.dir/타입

 

CP는 원래 기능을 그대로 유지한 채로 고유의 데이터를 외부에 공개하는 컴포넌트를 하나 더 추가하는 것이므로 기존의 액티비티는 전혀 건드릴 필요가 없다.

 

1. CP를 만드는 예제

 

위와 같이 3부분을 수정해야 합니다.

 

AndroidManifest.xml

application 태그 아래, Provider 태그를 추가한다. 시스템은 정보를 요청하는 쪽의 URI와 매니페스트의 URI를 비교해보고 제공자가 일치하는 CP를 호출한다.

 

그리고 반드시 퍼미션을 정의해줘야 한다. 안그러면 Permission denial: opening provider 에러가 발생한다. (다음 링크 참고 : http://stackoverflow.com/questions/14368867/permission-denial-opening-provider)

 

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.ch20_cp_use"

android:versionCode="1"

android:versionName="1.0" >

 

<uses-sdk

android:minSdkVersion="19"

android:targetSdkVersion="19" />

<!-- ContentProvider 구현한 앱에서 정의한 퍼미션을 그대로 써줘야한다. -->

<uses-permission android:name="de.test.READ_DATABASE" />

    <uses-permission android:name="de.test.WRITE_DATABASE" />

 

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name=".CallWordCP"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

 

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

 

</application>

 

 

 

</manifest>

 

 

englishword.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    >

<Button

    android:id="@+id/insert"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Insert"

    />

<Button

    android:id="@+id/delete"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Delete"

    />

<Button

    android:id="@+id/update"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Update"

    />

<Button

    android:id="@+id/select"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Select"

    />

<EditText

    android:id="@+id/edittext"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    />

</LinearLayout>

 

 

EnglishWord.java

 

package com.example.ch20_contentprovider;

 

import android.app.*;

import android.content.*;

import android.database.*;

import android.database.sqlite.*;

import android.os.*;

import android.view.*;

import android.widget.*;

 

public class EnglishWord extends Activity {

    WordDBHelper mHelper;

    EditText mText;

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.englishword);

 

        mHelper = new WordDBHelper(this);

        mText = (EditText)findViewById(R.id.edittext);

    }

    

    public void mOnClick(View v) {

        SQLiteDatabase db;

        ContentValues row;

        switch (v.getId()) {

        case R.id.insert:

            db = mHelper.getWritableDatabase();

            // insert 메서드로 삽입

            row = new ContentValues();

            row.put("eng", "boy");

            row.put("han", "머스마");

            db.insert("dic", null, row);

            // SQL 명령으로 삽입

            db.execSQL("INSERT INTO dic VALUES (null, 'girl', '가시나');");

            mHelper.close();

            mText.setText("Insert Success");

            break;

        case R.id.delete:

            db = mHelper.getWritableDatabase();

            // delete 메서드로 삭제

            db.delete("dic", null, null);

            // SQL 명령으로 삭제

            //db.execSQL("DELETE FROM dic;");

            mHelper.close();

            mText.setText("Delete Success");

            break;

        case R.id.update:

            db = mHelper.getWritableDatabase();

            // update 메서드로 갱신

            row = new ContentValues();

            row.put("han", "소년");

            db.update("dic", row, "eng = 'boy'", null);

            // SQL 명령으로 갱신

            //db.execSQL("UPDATE dic SET han = '소년' WHERE eng = 'boy';");

            mHelper.close();

            mText.setText("Update Success");

            break;

        case R.id.select:

            db = mHelper.getReadableDatabase();

            Cursor cursor;

            // query 메서드로 읽기

            //cursor = db.query("dic", new String[] {"eng", "han"}, null,

            //        null, null, null, null);

            // SQL 명령으로 읽기

            cursor = db.rawQuery("SELECT eng, han FROM dic", null);

        

            String Result = "";

            while (cursor.moveToNext()) {

                String eng = cursor.getString(0);

                String han = cursor.getString(1);

                Result += (eng + " = " + han + "\n");

            }

 

            if (Result.length() == 0) {

                mText.setText("Empyt Set");

            } else {

                mText.setText(Result);

            }

            cursor.close();

            mHelper.close();

            break;

        }

    }

}

 

//여기서 SQLite 데이터 베이스를 만든다.

class WordDBHelper extends SQLiteOpenHelper {

    public WordDBHelper(Context context) {

        super(context, "EngWord.db", null, 1);

    }

 

    public void onCreate(SQLiteDatabase db) {

        db.execSQL("CREATE TABLE dic ( _id INTEGER PRIMARY KEY AUTOINCREMENT, " +

        "eng TEXT, han TEXT);");

    }

 

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

        db.execSQL("DROP TABLE IF EXISTS dic");

        onCreate(db);

    }

}

 

 

EWProvider.java

 

package com.example.ch20_contentprovider;

 

import android.content.ContentProvider;

import android.content.ContentUris;

import android.content.ContentValues;

import android.content.UriMatcher;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import android.net.Uri;

import android.text.TextUtils;

 

public class EWProvider extends ContentProvider{

    static final Uri CONTENT_URI = Uri.parse("content://com.example.ch20_contentprovider/word");

    

    static final int ALLWORD = 1;

    static final int ONEWORD = 2;

    

    static final UriMatcher Matcher;

    static {

        Matcher = new UriMatcher(UriMatcher.NO_MATCH);

        Matcher.addURI("com.example.ch20_contentprovider", "word", ALLWORD);

        Matcher.addURI("com.example.ch20_contentprovider", "word/*", ONEWORD);

    }

    

    SQLiteDatabase mDB;

    

    //콘텐트 프로바이더 객체가 생성되면 호출됨.

    @Override

    public boolean onCreate() {

        // TODO Auto-generated method stub

        //공유할 데이터베이스를 열어둔다.

        WordDBHelper helper = new WordDBHelper(getContext());

        mDB = helper.getWritableDatabase();

        

        return true;

    }

    

    //MIME 타입을 정의해둠

    //클라리언트에서 주어진 uri 보고 ContentProvider 제공하는 데이터의 MIME타입을 리턴해준다.

    //특정한 상황에서만 사용

    @Override

    public String getType(Uri uri) {

        // TODO Auto-generated method stub

        if(Matcher.match(uri) == ALLWORD){

            return "vnd.EnglishWord.andexam.cursor.item/word";

        }

        if(Matcher.match(uri) == ONEWORD){

            return "vnd.EnglishWord.andexam.cursor.dir/word";

        }

        

        return null;

    }

 

    //실제 데이터를 제공하는 부분

    @Override

    public Cursor query(Uri uri, String[] projection, String selection,

            String[] selectionArgs, String sortOrder) {

        // TODO Auto-generated method stub

        String sql;

        

        //전체에 대한 쿼리 명령

        sql = "SELECT eng, han FROM dic";

        

        //단어 선택 where 추가

        //인수로 전달된 uri 분석함

        if(Matcher.match(uri) == ONEWORD){

            sql += " where eng = '" + uri.getPathSegments().get(1) + "'";//get(1)에서 1 id 의미

        }

        

        Cursor cursor = mDB.rawQuery(sql, null);//sql 실행

        

        return cursor;

    }

 

    // 하나를 삽입한다.

    public Uri insert(Uri uri, ContentValues values) {

        long row = mDB.insert("dic", null, values);

        

        //입력에 성공하면 notifyChange 메소드를 호출함.

        if (row > 0) {

            //아래는 추가된 ID 보유한 Uri 객체를 리턴하는 메소드

            //관련 링크 : http://posnopi13.tistory.com/19 참고

            Uri notiuri = ContentUris.withAppendedId(CONTENT_URI, row);

            

            //다른 CP들에게 변화를 알림

            getContext().getContentResolver().notifyChange(notiuri, null);

            return notiuri;

        }

        return null;

    }

 

    //

    public int delete(Uri uri, String selection, String[] selectionArgs) {

        int count = 0;

        

        //단수와 복수에 대한 요청 구분

        switch (Matcher.match(uri)) {

        case ALLWORD://복수 요청의 경우

            count = mDB.delete("dic", selection, selectionArgs);

            break;

        case ONEWORD://단수 요청의 경우

            String where;

            //해당하는 것만 지우기 위해 쿼리문을 만듬

            where = "eng = '" + uri.getPathSegments().get(1) + "'";

            //String null체크를 하기위한 메소드 빈문자열경우true 리턴

            if (TextUtils.isEmpty(selection) == false) {

                where += " AND" + selection;

            }

            count = mDB.delete("dic", where, selectionArgs);

            break;

        }

        

        //변화된것을 알림

        getContext().getContentResolver().notifyChange(uri, null);

        return count;

        //*/

        

        /* 아래와같이 구현해도됨

        String sql;

        

        // 전체에 대한 쿼리 명령

        sql = "DELETE FROM dic";

        

        // 단어 선택 where 추가

        if (Matcher.match(uri) == ONEWORD) {

            sql += " where eng = '" + uri.getPathSegments().get(1) + "'";

        }

        mDB.execSQL(sql);

        return 1;

        //*/

    }

 

    public int update(Uri uri, ContentValues values, String selection,

            String[] selectionArgs) {

        int count = 0;

        

        switch (Matcher.match(uri)) {

        case ALLWORD:

            count = mDB.update("dic", values, selection, selectionArgs);

            break;

        case ONEWORD:

            String where;

            where = "eng = '" + uri.getPathSegments().get(1) + "'";

            //delete쪽과 같음

            if (TextUtils.isEmpty(selection) == false) {

                where += " AND " + selection;

            }

            count = mDB.update("dic", values, where, selectionArgs);

            break;

        }

        

        getContext().getContentResolver().notifyChange(uri, null);

        return count;

    }

}

 

실행 화면

insert 버튼을 누르면 데이터베이스에 추가되고 delete버튼을 누르면 데이터베이스에서 삭제된다.

그리고 select버튼을 누르면 값이 출력된다. update는 머스마를 소년으로 바꾼다.

 

2. CP를 사용하는 예제

 

AndroidManifest.xml

 

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.ch20_cp_use"

android:versionCode="1"

android:versionName="1.0" >

 

<uses-sdk

android:minSdkVersion="19"

android:targetSdkVersion="19" />

<!-- ContentProvider 구현한 앱에서 정의한 퍼미션을 그대로 써줘야한다. -->

<uses-permission android:name="de.test.READ_DATABASE" />

    <uses-permission android:name="de.test.WRITE_DATABASE" />

 

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name=".CallWordCP"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

 

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

 

</application>

 

 

 

</manifest>

 

 

callwordcp.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    >

<Button

    android:id="@+id/readall"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Readl All"

    />

<Button

    android:id="@+id/readone"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Read One"

    />

<Button

    android:id="@+id/insert"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Insert"

    />

<Button

    android:id="@+id/delete"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Delete"

    />

<Button

    android:id="@+id/update"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:onClick="mOnClick"

    android:text="Update"

    />

<EditText

    android:id="@+id/edittext"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    />

</LinearLayout>

 

 

CallWordCP.java

 

package com.example.ch20_cp_use;

 

import android.app.Activity;

import android.content.ContentResolver;

import android.content.ContentValues;

import android.database.Cursor;

import android.net.Uri;

import android.os.Bundle;

import android.view.View;

import android.widget.EditText;

 

public class CallWordCP extends Activity {

    static final String WORDURI = "content://com.example.ch20_contentprovider/word";

    EditText mText;                

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.callwordcp);

        

        mText = (EditText)findViewById(R.id.edittext);

    }

    

    public void mOnClick(View v){

        

        //먼저 ContentResolver 객체를 구한다.

        //리졸버를 쿼리를 통해 cp 간접적으로 통신하는 표준 인터페이스이다.

        ContentResolver cr = getContentResolver();

        

        switch(v.getId()){

        //전부 읽기

        case R.id.readall:

            Cursor cursor = cr.query(Uri.parse(WORDURI), null, null, null, null);

        

            String Result = "";

            //데이터 베이스 값을 가져와서 뿌려줌

            while(cursor.moveToNext()){

                String eng = cursor.getString(0);

                String han = cursor.getString(1);

                Result += (eng + " = " + han + "\n");

            }

            

            if(Result.length() == 0){

                mText.setText("Empty set");

            }else{

                mText.setText(Result);

            }

            

            cursor.close();

            break;

            

            //하나만 읽기

            case R.id.readone:

                // /boy인것만 읽어옴

                Cursor cursor2 = cr.query(Uri.parse(WORDURI + "/boy"),

                        null, null, null, null);

                

                String Result2 = "";

                if(cursor2.moveToFirst()){

                    String eng = cursor2.getString(0);

                    String han = cursor2.getString(1);

                    Result2 += (eng + " = " + han + "\n");

                }

                

                if(Result2.length() == 0){

                    mText.setText("Empty Set");

                }else{

                    mText.setText(Result2);

                }

                cursor2.close();

                break;

                

            //삽입

            case R.id.insert:

                ContentValues row = new ContentValues();

                row.put("eng", "school");

                row.put("han", "학교");

                cr.insert(Uri.parse(WORDURI), row);

                mText.setText("Insert Success");

                break;

                

            //삭제

            case R.id.delete:

                cr.delete(Uri.parse(WORDURI), null, null);

                mText.setText("Delete Success");

                break;

                

            //수정

            case R.id.update:

                ContentValues row2 = new ContentValues();

                row2.put("han", "핵교다");

                cr.update(Uri.parse(WORDURI + "/school"), row2, null, null);

                mText.setText("Update Success");

                break;

        }

        

    }

    

 

}

 

 

실행 화면

CP를 구현한 예제의 데이터베이스 값을 가져오거나 수정할 수 있다.

즉, 첫번째 앱에 있는 데이터베이스 dic를 두번째 앱인 CP_USE앱에서 수정할 수 있다는 의미이다.

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

압축 파일 관리 기능은 자바에 의해 언어 차원에서 제공되므로 안드로이드도 별도의 추가 라이브러리 없이 압축 파일을 만들거나 해제할 수 있다.

 

ZipFile 클래스는 파일 기반의 zip 압축 파일을 다루며 압축 파일 내의 임의 파일을 랜덤으로 액세스한다.

 

다음 두가지 생성자가 있으며 File 객체로부터 생성할 수도 있고 압축 파일의 경로를 주어 열 수도 있다.

    

    ZipFile(File file [, int mode])

    ZipFile(String name)

 

File 객체로 생성할 때 mode에 OPEN_DELETE를 지정하면 사용 후 자동으로 삭제되므로 임시적으로 압축 파일을 만들어 할용할 수 있다.

 

    Enumeration<? extends ZipEntry> entries()

    ZipEntry getEntry(String entryName)

    int size()

    InputStream getInputStream(ZipEntry entry)

 

entries메소드는 압축 파일에 들어간 순서대로 모든 파일과 디렉터리의 목록을 구하며 getEntry 메소드는 지정한 경로의 항목을 구한다.

size는 압축 파일에 포함된 항목의 총 개수를 구한다. getInputStream은 항목을 액세스할 수 있는 스트림을 구하며 이 스트림에서 데이터를 읽음으로써 압축을 푼 데이터를 추출한다.

 

압축파일에 포함된 항목 하나는 ZipEntry클래스로 표현한다. 관련 메소드는 다음과 같다.

 

메소드

설명

String getName()

파일의 이름

long getSize()

압축을 풀었을 때의 원래 크기

long getCompressedSize()

압축된 크기

long getCrc()

체크섬

long getTime()

최후 수정된 시간

boolean isDirectory()

디렉터리인지 조사한다.

 

다음 동그라미 친 부분을 유의하자 assets에 ZipTest.zip 파일 추가해야 예제가 잘 된다.

ZipTest.zip 다운하기

ZipTest.zip

 

 

이번 예제는 SD카드의 정보를 사용하므로 매니페스트 파일에 퍼미션을 추가해야한다.

 

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.ch25_readzip"

android:versionCode="1"

android:versionName="1.0" >

 

<uses-sdk

android:minSdkVersion="19"

android:targetSdkVersion="19" />

 

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name=".ReadZip"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

 

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

 

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

</manifest>

 

readzip.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

>

 

<Button

android:id="@+id/btnlist"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="list"

/>

<Button

android:id="@+id/btna"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="a.txt"

/>

<Button

android:id="@+id/btnb"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="b.txt"

/>

    <TextView

     android:id="@+id/result"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:text="result"

     />

 

</LinearLayout>

 

ReadZip.java

package com.example.ch25_readzip;

 

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.Enumeration;

import java.util.zip.ZipEntry;

import java.util.zip.ZipFile;

import java.util.zip.ZipInputStream;

 

import android.app.Activity;

import android.content.Context;

import android.content.res.AssetManager;

import android.os.Bundle;

import android.os.Environment;

import android.view.View;

import android.widget.TextView;

 

public class ReadZip extends Activity {

    TextView mResult;

    String mPath;

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.readzip);

      

        mPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/ZipTest.zip";

        mResult = (TextView)findViewById(R.id.result);

        

        //에셋의 zip 파일 복사하기

        CopyAsset(this, "ZipTest.zip", "ZipTest.zip");

    }

 

    public void mOnClick(View v){

        switch(v.getId()){

        case R.id.btnlist:

            ShowList();

            break;

        case R.id.btna:

            ShowA();

            break;

        case R.id.btnb:

            ShowB();

            break;

        }

    }

    

    //Zip파일을 읽은 다음 정보를 보여주는 메소드

    void ShowList(){

        try{

            ZipFile zip = new ZipFile(mPath);

            String s = "";

            s = "size = " + zip.size() + "\n";

            ZipEntry e;

            

            Enumeration<? extends ZipEntry> ent = zip.entries();//zip파일의 엔트리를 가져옴

            while(ent.hasMoreElements()){//엔트리가 있으면

                e = (ZipEntry)ent.nextElement();//가져옴

                s = s + "name = " + e.getName() + " , size = " + e.getSize() +

                        " , Compsize = " + e.getCompressedSize() + "\n";

            }

            

            mResult.setText(s);

        }catch(Exception e){

            return;

        }

    }

    

    void ShowA(){

        try{

            ZipFile zip;

            zip = new ZipFile(mPath);

            

            //a.txt 읽기 위해

            InputStream is = zip.getInputStream(zip.getEntry("a.txt"));

            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            byte[] buffer = new byte[1024];

            

            int len;

            //내용을 출력하는 부분

            for(;;){

                len = is.read(buffer);

                if(len <= 0)break;

                baos.write(buffer, 0, len);

            }

            is.close();

            mResult.setText(baos.toString());

        }catch(IOException e){

            

        }

    }

 

    void ShowB(){

        try{

            ZipInputStream zin = new ZipInputStream(new FileInputStream(mPath));

            for(;;){

                ZipEntry ze = zin.getNextEntry();//다음폴더를 들어가기위해

                if(ze == null) break;

                if(ze.getName().equals("subdir/b.txt")){//찾으면?

                    //아래부분은 출력을 하는부분

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();

                    byte[] buffer = new byte[1024];

                    

                    int len;

                    for(;;){

                        len = zin.read(buffer);

                        if(len <= 0) break;

                        baos.write(buffer, 0, len);

                    }

                    mResult.setText(baos.toString());

                    break;

                }

            }

            zin.close();

        }catch(Exception e){

            

        }

    }    

    

    //Zip파일을 sd카드에 복사하는 메소드

    public boolean CopyAsset(Context context, String src, String dest){

        if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){

            return false;

        }

        

        String root = Environment.getExternalStorageDirectory().getAbsolutePath();

        

        String destpath = root + "/" + dest;

        File f = new File(destpath);

        if(f.exists()){

            return true;

        }

        

        AssetManager am = context.getAssets();

        //파일을 실제로 복사하는 부분

        try{

            InputStream is = am.open(src);

            FileOutputStream os = new FileOutputStream(destpath);

            byte buffer[] = new byte[1024];

            for(;;){

                int read = is.read(buffer);

                if(read <= 0) break;//파일 읽음

                os.write(buffer, 0, read);

            }

            is.close();

            os.close();

        

        }catch(IOException e){

            return false;

        }

 

        

        return true;

    }

    

}

 

 

출력 화면

 

 

a.txt 버튼을 눌렀을 때

 

b.txt 버튼을 눌렀을 때

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

TabActivity가 deprecated됨에 따라 이제 탭을 위해서는 다음의 방법을 써야한다.

참고 내용 : http://limts28.tistory.com/458

 

액션탭을 사용하려면 다음 메소드로 네비게이션탭으로 변경해야한다.

 

    void setNavigationMode(int mode)

    int getNavigationMode()

 

모드

설명

NAVIGATION_MODE_STANDARD

로고 아이콘과 액션 항목이 배치된다.

NAVIGATION_MODE_TABS

액션바에 여러 개의 탭을 배치하려 페이지를 전환한다.

NAVIGATION_MODE_LIST

드롭다운 리스트로 페이지를 전환한다.

 

 

액션바의 다음 메소드로 탭을 추가 삭제한다.

    ActionBar.Tab newTab()

    void addTab(ActionBar.Tab tab, [int position, boolean setSelected])

    void removeTab(ActionBar.Tab tab)

    void removeTabAt(int position)

    void removeAllTabs()

 

newTab 메소드로 새로운 빈탭을 생성하고 addTab으로 액션바에 탭을 추가한다.

position 인수로 추가할 위치를 지정하여 중간에 삽입할 수도 있고 setSelected 인수를 true로 하여 추가 후 바로 선택할 수도 있다.

removeTab은 특정 위치의 탭을 제거, removeAllTabs는 전체 탭을 제거하여 비운다.

 

    ActionBar.Tab setText(int resId)

    ActionBar.Tab setText(CharSequence text)

    ActionBar.Tab setIcon(int resId)

    ActionBar.Tab setCustomView(int layoutResId)

    ActionBar.Tab setCustomView(View view)

    ActionBar.Tab setTag(Object obj)

    ActionBar.Tab setTabListener(ActionBar.TabListener listener)

 

탭에 캡션 , 아이콘, 커스텀뷰를 배치하기위해 위의 메소드를 사용한다.

 

    void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft)

    void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)

    void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft)

 

탭이 선택, 선택 해제, 재선택 될 때 위의 메소드가 호출된다.

 

    int getTabCount()

    void selectTab(ActionBar.Tab tab)

    ActionBar.Tab getSelectedTab()

    ActionBar.Tab getTabAt(int index)

    void setSelectedNavigationItem(int position)

    int getSelectedNavigationIndex()

 

위의 메소드는 탭을 위한 관리 메소드이다.

 

다음 예제는 프래그먼트를 이용해서 탭을 만든다.

 

 

 

res/layout/activity_action_tab.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

>

 

<TextView

android:id="@+id/content"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="" />

 

</LinearLayout>

 

res/layout/actiontab.xml

 

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/tabparent"

android:layout_width="match_parent"

android:layout_height="match_parent"

    >

 

</FrameLayout>

 

src/ActionTab.java

 

package com.example.actiontab;

 

import android.app.ActionBar;

import android.app.ActionBar.Tab;

import android.app.Activity;

import android.app.Fragment;

import android.app.FragmentTransaction;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.TextView;

 

public class ActionTab extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.actiontab);

      

        ActionBar ab = getActionBar();

        ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        

        //여기서 탭을 3 만들어준다.

        for(int i=0; i < 3; i++){

            ActionBar.Tab tab = ab.newTab();

            String Cap = "Tab" + (i + 1);

            tab.setText(Cap);

            

            //여기서는 번들에 값이 들어가 있음

            TabFragment frag = TabFragment.newInstance(Cap);

            tab.setTabListener(new TabListener(frag));//리스너 등록

            ab.addTab(tab);

        }

        

        //화면의 방향이 바껴도 탭의 선택위치가 안바뀌게 하기위해서

        if(savedInstanceState != null){

            int seltab = savedInstanceState.getInt("seltab");

            ab.setSelectedNavigationItem(seltab);

        }

 

    }

      

        //화면의 상태 전환시 현재 상태를 저장하기 위한 메소드 오버라이딩

        public void onSaveInstanceState(Bundle outState){

            super.onSaveInstanceState(outState);

            outState.putInt("seltab", getActionBar().getSelectedNavigationIndex());

        }

        

        private class TabListener implements ActionBar.TabListener{

            private Fragment mFragment;

            

            public TabListener(Fragment fragment){

                mFragment = fragment;

            }

            

            @Override

            public void onTabReselected(Tab tab, FragmentTransaction ft) {

                // TODO Auto-generated method stub

                

            }

 

            @Override

            public void onTabSelected(Tab tab, FragmentTransaction ft) {

                // TODO Auto-generated method stub

                //탭이 선택되면 프래그먼트를 추가한다.

                ft.add(R.id.tabparent, mFragment, "tag");

            }

 

            @Override

            public void onTabUnselected(Tab tab, FragmentTransaction ft) {

                // TODO Auto-generated method stub

                ft.remove(mFragment);

            }

            

        }

        

        public static class TabFragment extends Fragment{

            

            public static TabFragment newInstance(String text){

                TabFragment frag = new TabFragment();

                

                //번들에 넣어줌

                //프래그먼트끼리 송수신은 번들을 이용한다.

                Bundle args = new Bundle();

                args.putString("text", text);//text태그에 text라는 값을 넣는다.

                frag.setArguments(args);

                

                return frag;//반환함.

            }

            

            //프래그먼트 뷰를 생성

            public View onCreateView(LayoutInflater inflater, ViewGroup container,

                        Bundle savedInstanceState){

            //여기서 프래그먼트 UI 그릴때 호출한다.

         //그래서 여기에서 XML 가져와서 UI 만들어준다.

                

                String text = "";

                //프래그먼트끼리 데이터 송수신시에는 반드시 번들을 사용해야한다.

                Bundle args = getArguments();

                if(args != null){

                    text = args.getString("text");

                }

                

                //여기서 전개자를 이용해서 UI 만드는 부분

                View linear = inflater.inflate(R.layout.activity_action_tab, container, false);

                TextView textview = (TextView)linear.findViewById(R.id.content);

                textview.setText(text);

                

                return linear;

            }

        }

        

}

 

실행 화면

다음과 같이 탭이 3개 생성된다.

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

액티비티는 자신의 상태를 저장하거나 복원하는 것이 가능합니다. 이번 포스트에서는 액티비티의 상태를 저장 및 복원하는 콜백 메소드인 onSaveInstanceState와 onRestoreInstanceState에 대해 알아 봅니다.


액티비티의 상태를 저장함의 의미
액티비티의 상태를 저장한다는 것은 액티비티의 모습과 자료 등을 기기의 저장 장치에 저장함을 의미합니다. 여기에서 상태의 뜻이 resumed, paused, stopped 등과 같은 값들 중 하나를 갖는 변수가 아님에 유의하세요.  


액티비티의 상태를 복원함의 의미
종료되지는 않았지만 사용되지 않는 액티비티들은  메모리 부족하면 안드로이드가 강제로 종료 시킵니다. 나중에 사용자가 그것들을 다시 실행할 수 있고, 그것들은 원래의 모습으로 다시 복원됩니다.


액티비티의 상태를 왜 저장하는가?
강제 종료된 액티비티를 안드로이드가 나중에 복원해야 하기 때문입니다.

   
액티비티의 상태는 언제 복원되는가? 
강제 종료 된 액티비티를 사용자가 다시 실행할 때, 그것은 복원 됩니다.  


액티비티의 상태를 저장하는 onSaveInstanceState 메소드
액티비티는 자산의 상태가 running에서 paused로 바뀔 때 onSaveInstanceState 메소드가 onPause보다 먼저 실행 됩니다. 만약에 추가로 저장해야 하는 자료가 있다면, 이 메소드를 오버라이드 하셔야 합니다. 이 때 그것이 슈퍼 클래스인 Activity의 onSaveInstanceState 메소드를 호출함을 잊지 마세요.  그렇지 않으면, 액티비티의 상태가 저장되지 않습니다. 

 

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState); // 반드시 호출해 주세요.
 
    // 추가로 자료를 저장하는 코드는 여기에 작성 하세요. 
}

 
Bundle은 문자열로 식별할 수 있는 자료들을 저장할 수 있는 객체를 정의하는 클래스입니다. outState는 액티비티의 상태를 저장하기 위해 사용되는 객체입니다. 만약 추가로 저장해야 하는 자료가 있다면, outState의 put... 메소드들을 호출하세요. 


액티비티의 상태를 복원하는 onRestoreInstanceState 메소드  
강제 종료 된 액티비티가 다시 실행될 때 onRestoreInstanceState 메소드가 실행 됩니다. 만약 onSaveInstanceState 메소드를 오버라이드 하셨다면, onRestoreInstanceState도 오버라이드 하세요. 그래야 전자에서 추가로 저장했던 자료를 복원할 수 있습니다. 전자와 마찬가지로 슈퍼 클래스 Activity의 onRestoreInstanceState 메소드를 반드시 호출해 주시기 바랍니다. 

 

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
 
    // 추가로 자료를 복원하는 코드는 여기에 작성하세요.
}

 
savedInstanceState 파라메터는 복원할 자료들을 갖고 있습니다. 그것의 get... 메소드를 호출하여 자료를 읽을 수 있습니다.
 
 
onSaveInstanceState, onRestoreInstanceState는 언제 호출되는가?
안드로이드 개발자 사이트에서는 이 메소드들이 언제 호출되는지를 보여주는 그림을 보여줍니다.


 

 


 
원본 이미지의 출처 - http://developer.android.com/guide/topics/fundamentals/activities.html

 

 
마치며...
지금까지 액티비티의 상태를 저장 및 복원하는 콜백 메소드 onSaveInstanceState, onRestoreInstanceState에 대해 알아보았습니다.


출처 : http://netrance.blog.me/110137097968

 


블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

액션 프로바이더는 용도상으로 액션 뷰와 유사하지만 더 확장된 버젼이다.

 

ActionProvider클래스를 상속받아 작성하며 필수 메소드 몇가지를 재정의 해야 한다.

 

    View onCreateActionView([MenuItem forItem])

 

위의 메소드를 재정의하여 액션 뷰를 생성하여 리턴한다.

 

다음 메소드는 액션 뷰가 아닌 메뉴에 배치된 상태에서 메뉴를 선택할 때 호출된다.

 

    boolean onPerformDefaultAction()

 

메뉴 선택 시 이 메소드보다는 onOptionsItemSelected가 먼저 호출되는데 여기서 항목을 처리하지 않을 경우

 

onPerformDefaultAction 메소드가 대신 호출된다. 단 서브 메뉴가 있을 경우에는 이 메소드가 호출되지 않음.

 

이번 예제는 다음과 같이 구성합니다.

 

 

 

res/layout/counterprovider.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

    android:orientation="horizontal"

     >

 

<TextView

android:id="@+id/count"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="10"/>

<Button

android:id="@+id/btnincrease"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="+"/>

<Button

android:id="@+id/btndecrease"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="-"/>

 

 

</LinearLayout>

 

ActionProviderTest.java

 

package com.example.ch21_actionprovider2;

 

import com.example.ch21_actionprovider2.R;

 

import android.app.Activity;

import android.content.Context;

import android.os.Bundle;

import android.view.ActionProvider;

import android.view.LayoutInflater;

import android.view.Menu;

import android.view.MenuInflater;

import android.view.MenuItem;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

public class ActionProviderTest extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        

        TextView text = new TextView(this);

        text.setText("액션 프로바이더를 테스트합니다.");

        setContentView(text);

        

    }

 

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        super.onCreateOptionsMenu(menu);

        MenuInflater inflater = getMenuInflater();

        inflater.inflate(R.menu.actionprovidermenu, menu);

        

        return true;

    }

 

    @Override

    public boolean onOptionsItemSelected(MenuItem item) {

        Toast.makeText(this, "Counter Menu Item selected", 0).show();

        

        return true;

    }

 

    //액션 프로바이더를 위한 클래스

    public static class CounterProvider extends ActionProvider{

        Context mContext;

        TextView mCountText;

        

        public CounterProvider(Context context){

            super(context);

            mContext = context;

        }

        

        //

        public View onCreateActionView(){

            LayoutInflater Inflater = LayoutInflater.from(mContext);

            //xml 전개자로 가져와서 뿌려줌

            View linear = Inflater.inflate(R.layout.counterprovider, null);

            mCountText = (TextView)linear.findViewById(R.id.count);

            

            //버튼 리스너 달아줌

            Button btnInc = (Button)linear.findViewById(R.id.btnincrease);

            btnInc.setOnClickListener(new View.OnClickListener() {

                

                @Override

                public void onClick(View v) {

                    // TODO Auto-generated method stub

                    //클릭시 값이 1 증가함

                    int count = Integer.parseInt(mCountText.getText().toString());

                    mCountText.setText(Integer.toString(count + 1));

                }

            });

            

            Button btnDec = (Button)linear.findViewById(R.id.btndecrease);

            btnDec.setOnClickListener(new View.OnClickListener() {

                

                @Override

                public void onClick(View v) {

                    //클릭시 값이 1 감소함

                    // TODO Auto-generated method stub

                    int count = Integer.parseInt(mCountText.getText().toString());

                    mCountText.setText(Integer.toString(count - 1));

                }

            });

            return linear;

        }

        

        public boolean onPerformDefaultAction(){

            Toast.makeText(mContext, "Counter Menu Item selected -", 0).show();

            return true;

        }

    }

 

}

 

res/menu/actionprovidermenu.xml

 

<menu xmlns:android="http://schemas.android.com/apk/res/android"

>

<item android:id="@+id/counter"

android:title="Counter"

android:showAsAction="always"

android:actionProviderClass="com.example.ch21_actionprovider2.ActionProviderTest$CounterProvider"

/>

<item android:id="@+id/countermenu"

android:title="Counter"

android:showAsAction="never"

android:actionProviderClass="com.example.ch21_actionprovider2.ActionProviderTest$CounterProvider"

/>

</menu>

 

 

실행 화면

버튼을 누를 때마다 값이 증가하거나 감소한다.

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

액션바에서 명령 전달보다 더 복잡한 동작을 직접 처리하려면 원하는 위젯이나 뷰그룹을 액션바에 배치하는데  이를 액션 뷰라고 한다.

 

메뉴 항목에 다음 두 속성을 사용하여 액션 뷰를 지정한다.

 

속성

설명

actionLayout

액션바에 배치할 뷰그룹의 레이아웃 리소스를 지정한다.

actionViewClass

액션바에 배치할 위젯의 클래스를 지정한다.

 

액션 뷰 객체를 구하거나 설정할 때는 메뉴 항목의 다음 메서드를 호출한다.

 

    View getActionView()

    MenuItem setActionView(int resId)

    MenuItem setActionView(View view)

 

onCreateOptionsMenu 메소드에서 메뉴를 전개한 후 메뉴 항목을 먼저 찾는다. 그리고 메뉴 항목의 getActionView 메소드로 액션 뷰 객체를 구하고

 

속성을 조정하거나 리스너를 설치한다.

 

액션뷰는 자체적으로 확장 및 축소가 가능하지만 코드에서도 다음 메소드를 호출하여 확장 및 축소할 수 있다.

 

    boolean expandActionView()

    boolean collapseActionView()

 

리스너에는 확장 및 축소될 때 호출되는 두 개의 메소드가 정의되어 있다.

 

    MeuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener)

    boolean onMenuItemActionExpand(MenuItem item)

    boolean onMenuItemActionCollapse(MenuItem item)

 

대표적인 액션바의 사용 예가 에디트이다. 대화상자를 따로 열 필요 없이 액션바에서 바로 정보를 입력할 수 있으므로 편리하다.

 

안드로이드는 이런 용도로 SearchView 위젯을 제공한다. 메뉴 항목의 actionViewClass 속성에 SearchView를 지정하면 쉽게 배치할 수 있다.

    

    void setIconifiedByDefault(boolean iconified)

    void setIconified(boolean iconify)

    boolean isIconfiedByDefault()

    boolean isIconified()

 

iconified 인수가 true이면 축소된 아이콘 형태로 열리며 디폴트가 true이다. 생성 직후에 이 값을 false로 지정하고 showAsAction 속성에

 

collapseActionView 플래그를 제거하면 처음부터 에디트가 확장된 채로 실행된다.

 

setIconified는 실행 중에 확장 및 축소할 때 사용한다.

 

검색뷰의 에디트를 편집할 때 이벤트를 가로채려면 다음 메소드로 리스너를 등록한다.

 

    void setOnQueryTextListener(SearchView.OnQueryTextListener listener)

    boolean onQueryTextChange(String newText)

    boolean onQueryTextSubmit(String query)

 

이 메소드에서 true를 리턴하면 텍스트 변경 이벤트를 처리했다는 뜻이다.

 

다음 예제는 SeachView를 활용해보는 예제이다.

 

 

action_view.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

tools:context="com.example.actionview.ActionView" >

 

    <item

     android:title="search"

     android:id="@+id/search"

     android:icon="@android:drawable/ic_menu_search"

     android:showAsAction="always|collapseActionView"

     android:actionViewClass="android.widget.SearchView"

     />

 

</menu>

 

 

activity_action_view.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

>

 

<TextView

android:id="@+id/txtsearch"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="검색식 : "/>

<TextView

android:id="@+id/txtresult"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="검색결과"/>

<TextView

android:id="@+id/txtstatus"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="현재 상태 : 축소됨"/>

<Button

android:id="@+id/btnexpand"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="확장"

/>

<Button

android:id="@+id/btncollapse"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="축소"/>

 

</LinearLayout>

 

 

ActionView.java

package com.example.actionview;

 

import android.app.Activity;

import android.os.Bundle;

import android.view.Menu;

import android.view.MenuInflater;

import android.view.MenuItem;

import android.view.View;

import android.widget.SearchView;

import android.widget.TextView;

 

public class ActionView extends Activity {

    MenuItem mSearch;

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_action_view);

    }

 

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        super.onCreateOptionsMenu(menu);

        //메뉴에는 서치뷰 항목 하나만 배치함.

        MenuInflater inflater = getMenuInflater();

        inflater.inflate(R.menu.action_view, menu);

        mSearch = menu.findItem(R.id.search);

          

        

        mSearch.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {

            

            @Override

            public boolean onMenuItemActionExpand(MenuItem item) {

                // TODO Auto-generated method stub

                //서치뷰가 확장됬을         

                TextView text = (TextView)findViewById(R.id.txtstatus);

                text.setText("현재 상태 : 확장됨");

                return true;

            }

            

            @Override

            public boolean onMenuItemActionCollapse(MenuItem item) {

                // TODO Auto-generated method stub

                //서치뷰가 축소됬을

                TextView text = (TextView)findViewById(R.id.txtstatus);

                text.setText("현재 상태 : 축소됨");

                return true;

            }

        });

 

        SearchView sv = (SearchView)mSearch.getActionView();

        sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {

            

            @Override

            public boolean onQueryTextSubmit(String query) {

                // TODO Auto-generated method stub

                //검색 버튼이 눌리면

                TextView text = (TextView)findViewById(R.id.txtresult);

                text.setText(query + " 검색합니다.");

                return true;

            }

            

            @Override

            public boolean onQueryTextChange(String newText) {

                // TODO Auto-generated method stub

                //검색창에 글자를 쓰면 여기로

                TextView text = (TextView)findViewById(R.id.txtsearch);

                text.setText("검색식 : " + newText);

                return true;

            }

        });

        

        return true;

    }

 

    //버튼 클릭시.

    public void mOnClick(View v){

        switch(v.getId()){

        case R.id.btnexpand:

            //서치뷰가 확장됨

            mSearch.expandActionView();

            break;

        case R.id.btncollapse:

            //서치뷰가 축소됨

            mSearch.collapseActionView();

            break;

        }

    }

}

 

실행 화면

SeachView가 확장 됬을 때

 

SearchView가 축소됬을 때

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

여러 개의 액티비티로 구성된 프로그램은 실행 중인 액티비티의 목록을 스택에 저장한다.

 

프래그먼트도 액티비티와 마찬가지로 스택에 저장된다.

 

현재 상태를 스택에 저장하려면 다음 메소드를 사용한다.

 

    FragmentTransaction addToBackStack(String name)

 

name은 스택의 상태에 대해 이름을 주는데 사용된다.(필요 없으면 null)

 

스택에 프래그먼트를 저장한 상태에서 사용자가 Back 버튼을 누르면 스택의 최상위에 있는 프래그먼트를 꺼내 원래 상태로 복귀한다.

 

    FragmentTransaction setTransition(int transit)

    FragmentTransaction setCustomAnimations(int enter, int exit [, int popEnter, int popExit]

 

위 메소드를 이용해서 프래그먼트 전환시에 애니메이션을 지정할 수 있다.

 

다음 예제는 addToBackStack 메소드를 이용해서 백(Back)키를 누르면 이전의 프래그먼트를 가져오는 예제이다.

 

이번 예제는 다음과 같이 4개의 파일을 만들어야 한다.

 

activity_back_stack.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

>

 

<Button

android:id="@+id/btnadd"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="Add"

/>

<FrameLayout

android:id="@+id/frame"

android:layout_width="match_parent"

android:layout_height="wrap_content"

/>

 

</LinearLayout>

 

 

counterfragment.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center_horizontal"

android:background="#ffff00"

>

 

<TextView

android:id="@+id/txtcounter"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#ff0000"

android:textSize="40sp"

android:text="0"

/>

<Button

android:id="@+id/btnincrease"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Increase"

/>

 

</LinearLayout>

 

CounterFragment.java

package com.example.ch20_backstack;

 

import android.app.Fragment;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.TextView;

 

public class CounterFragment extends Fragment{

    //인스턴스 만들고 아규먼트에 값을 넣어두는 메소드

    public static CounterFragment newInstance(int start){

        CounterFragment cf = new CounterFragment();

        

        Bundle args = new Bundle();

        args.putInt("start", start);

        cf.setArguments(args);

        return cf;

    }

    

    public View onCreateView(LayoutInflater inflater, ViewGroup container,

            Bundle savedInstanceState){

        View root = inflater.inflate(R.layout.counterfragment, container,

                false);

        

        Button btnIncrease = (Button)root.findViewById(R.id.btnincrease);

        final TextView textCounter = (TextView)root.findViewById(R.id.btnincrease);

        

        int start = 0;

        Bundle args = getArguments();//여기서 아까 저장한 값을 가져옴

        if(args != null){//값이 있다면

            start = args.getInt("start");//start 태그의 값을 취함

        }

        textCounter.setText(Integer.toString(start));//버튼의 텍스트를 바꿈

        

        //버튼클릭 리스너 등록

        btnIncrease.setOnClickListener(new Button.OnClickListener(){

 

            @Override

            public void onClick(View v) {

                int count = Integer.parseInt(textCounter.getText().toString());

                textCounter.setText(Integer.toString(count + 1));

            }

            

        });

        return root;

    }

}

 

BackStack.java

package com.example.ch20_backstack;

 

import android.app.Activity;

import android.app.FragmentManager;

import android.app.FragmentTransaction;

import android.os.Bundle;

import android.view.View;

 

 

public class BackStack extends Activity {

    int mStart = 10;

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_back_stack);

 

        if(savedInstanceState != null){

            mStart = savedInstanceState.getInt("mStart");

        }

        

        //프래그먼트 매니저 사용을 위한 구문

        FragmentManager fm = getFragmentManager();

        FragmentTransaction tr = fm.beginTransaction();

        CounterFragment cf = CounterFragment.newInstance(mStart);

        tr.add(R.id.frame, cf).commit();//cf frame레이아웃에 더함(겹침)

        

    }

    

    //상태를 저장하기 위한 부분

    //장비의 설정 상태 변경, 화면 방향 변경 등의 변화에 의해 프래그먼트가

    //정지될 호출됨.

    public void onSaveInstanceState(Bundle outState){

        super.onSaveInstanceState(outState);

        

        outState.putInt("mStart", mStart);//mStart값을 넣어둠

    }

 

    //Add버튼 클릭시

    public void mOnClick(View v){

        switch(v.getId()){

        case R.id.btnadd:

            mStart += 10;//mStart 증가

            

            //프래그먼트 매니저를 가져옴

            FragmentManager fm = getFragmentManager();

            FragmentTransaction tr = fm.beginTransaction();

            CounterFragment cf = CounterFragment.newInstance(mStart);//mStart 저장

            tr.replace(R.id.frame, cf);//cf 교체함

            

            //백스택에 넣어둠 이것때문에 (Back)키를 누르면 이전 프래그먼트를 가져오게됨.

            tr.addToBackStack(null);

            

            tr.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);//애니메이션 효과

            tr.commit();

            break;

            

        }

    }

    

}

 

실행 화면

Add버튼을 13번 정도 눌러둔 상태입니다. 여기서 back키를 누르면?

 

 

다음과 같이 이전에 스택에 저장해둔 상태로 되돌아가게됩니다.

 

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

프래그먼트도 메소드와 비슷하게 액티비티의 요구에 따라 조금씩 다르게 동작하기 위해 인수를 받아 들인다.

 

이때 프래그먼트의 인수 저장을 위해 아규먼트를 사용하며 다음 두 메소드로 아큐먼트를 설정하거나 얻는다.

 

    void setArguments(Bundle args)

    Bundle getArguments()

 

아규먼트는 프래그먼트 내부에 저장되는 Bundle 객체이며 주로 프래그먼트의 생성에 관련된 정보를 저장한다.

 

그래서 아규먼트는 액티비티에 프래그먼트를 부착하기 전에만 저장할 수 있다.

 

다음은 아규먼트를 사용하여 정수를 저장하는 예제이다.

 

 

activity_fragment_argument.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

>

    <EditText

     android:id="@+id/startnum"

     android:inputType="number"

     android:layout_width="match_parent"

     android:layout_height="wrap_content"

     android:text="5"

     />

    <Button

     android:id = "@+id/btnadd"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:onClick="mOnClick"

     android:text="Add"

     />

    <FrameLayout

     android:id="@+id/frame"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     />

    

</LinearLayout>

 

counterfragment.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center_horizontal"

android:background="#ffff00"

>

 

<TextView

android:id="@+id/txtcounter"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#ff0000"

android:textSize="40sp"

android:text="0"

/>

<Button

android:id="@+id/btnincrease"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Increase"

/>

 

</LinearLayout>

 

 

FragmentArgument.java

package com.example.ch20_fragmentargument;

 

import android.app.Activity;

import android.app.Fragment;

import android.app.FragmentManager;

import android.app.FragmentTransaction;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

 

 

public class FragmentArgument extends Activity {

    

    EditText mStartNum;

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_fragment_argument);

      

        mStartNum = (EditText)findViewById(R.id.startnum);

    }

    

    public void mOnClick(View v){

        switch(v.getId()){

        case R.id.btnadd:

            FragmentManager fm = getFragmentManager();

            //트랜잭션 시작

            FragmentTransaction tr = fm.beginTransaction();

            int start = Integer.parseInt(mStartNum.getText().toString());

            CounterFragment cf = CounterFragment.newInstance(start);//새로운 인스턴스 가져옴

            tr.add(R.id.frame, cf, "counter");

            tr.commit();

            break;

        }

    }

 

    public static class CounterFragment extends Fragment{

        //인스턴스 만들고 아규먼트에 값을 넣어두는 메소드

        public static CounterFragment newInstance(int start){

            CounterFragment cf = new CounterFragment();

            

            Bundle args = new Bundle();

            args.putInt("start", start);

            cf.setArguments(args);

            return cf;

        }

        

        public View onCreateView(LayoutInflater inflater, ViewGroup container,

                Bundle savedInstanceState){

            View root = inflater.inflate(R.layout.counterfragment, container,

                    false);

            

            Button btnIncrease = (Button)root.findViewById(R.id.btnincrease);

            final TextView textCounter = (TextView)root.findViewById(R.id.btnincrease);

            

            int start = 0;

            Bundle args = getArguments();//여기서 아까 저장한 값을 가져옴

            if(args != null){//값이 있다면

                start = args.getInt("start");//start 태그의 값을 취함

            }

            textCounter.setText(Integer.toString(start));//버튼의 텍스트를 바꿈

            

            //버튼클릭 리스너 등록

            btnIncrease.setOnClickListener(new Button.OnClickListener(){

 

                @Override

                public void onClick(View v) {

                    int count = Integer.parseInt(textCounter.getText().toString());

                    textCounter.setText(Integer.toString(count + 1));

                }

                

            });

            return root;

        }

    }

}

 

 

실행 화면

에디트에 적당한 정수를 입력한 후 Add 버튼을 누르면 아래쪽의 빈 프레임에 프래그먼트를 추가하되 에디트에 입력한 시작값을 전달받아 동작한다.

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

프래그먼트를 실행중에 편집하기 위해서는 프래그먼트 관리자를 사용해야한다.

 

    FragmentManager Activity.getFragmentManager()

    FragmentManager Fragment.getFragmentManager()

 

그리고 프래그먼트를 관리하려면 먼저 대상 프래그먼트를 검색해야 한다.

 

    Fragment findFragmentById(int id)

    Fragment findFragmentByTag(String tag)

 

프래그먼트는 다음 3가지 방식으로 지칭한다.

1. id: 프래그먼트를 배치할 때 지정한 android:id 속성의 정수값을 이용한다.

2. 부모의 id : 실행 중에 생성한 프래그먼트는 고유한 id가 없다. 그래서 부모의 id를 통해 프래그먼트를 찾는다. (단 첫 번째 차일드만 검색 가능)

3. 문자열 형태의 태그 : 실행 중에 프래그먼트를 생성할 때 고유한 이름의 태그를 붙일 수 있는데 이 태그로부터 프래그먼트를 검색한다.

 

프래그먼트를 편집하려면 다음 메소드로 트랜잭션을 시작한다.

 

    FragmentTransaction beginTransaction()

 

이 메소드의 리턴되는 FragmentTransaction 객체의 편집 메소드를 호출하여 프래그먼트를 편집한다.

 

프래그먼트를 추가할 때는 다음 메소드를 호출한다.

    

    FragmentTransaction add(int containerViewId, Fragment fragment [, String tag])

    FragmentTransaction add(Fragment fragment, String tag)

 

contatinerViewId는 프래그먼트가 추가될 부모 뷰의 id이고 이 값이 0이면 프래그먼트가 화면에 나타나지 않음.

fragment는 추가할 프래그먼트 객체, tag인수로 이름을 지정

 

프래그먼트를 제거할 때는 다음 메소드를 호출한다.

 

    FragmentTransaction remove(Fragment fragment)

 

이미 추가되어 있는 프래그먼트를 다른 프래그먼트로 대체할 때는 다음 메소드를 호출한다.

 

    FragmentTransaction replace(int containerViewId, Fragment fragment [, String tag])

 

컨테이너의 id와 새로 대체할 프래그먼트를 인수로 전달하며 차후의 작업을 위해 태그명을 지정할 수 있다.

 

프래그먼트를 숨기거나 보일 때는 다음 두 메소드를 호출한다.

 

    FragmentTransaction show(Fragment fragment)

    FragmentTransaction hide(Fragment fragment)

 

모든 편집이 완료된 후에 최종적으로 다음 메소드를 호출하여 편집을 실행한다.

 

    int commit()

 

이 메소드를 트랜잭션을 직접 처리하는 것이 아니라 스레드가 한가할 때 처리하도록 스케쥴링한다.

 

이번에 예제는 프래그먼트를 추가, 삭제, 교체, 숨김을 실습한다.

 

다음과 같이 총 5개의 파일을 만든다.

 

 

activity_fragment_manager_test.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

>

 

<Button

android:id="@+id/btnadd"

android:layout_height="wrap_content"

android:layout_width="wrap_content"

android:onClick="mOnClick"

android:text="Add"

/>

<Button

android:id="@+id/btnremove"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="Remove"

/>

<Button

android:id="@+id/btnreplace"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="Replace"

/>

<Button

android:id="@+id/btnhideshow"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="Hide"

/>

<FrameLayout

android:id="@+id/frame"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

/>

 

</LinearLayout>

 

 

counterfragment.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center_horizontal"

android:background="#ffff00"

>

 

<TextView

android:id="@+id/txtcounter"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#ff0000"

android:textSize="40sp"

android:text="0"

/>

<Button

android:id="@+id/btnincrease"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Increase"

/>

 

</LinearLayout>

    

 

textfragment.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="match_parent"

android:orientation="vertical"

android:background="#00ff00"

>

 

<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="문자열 입력 프래그먼트"

/>

<EditText

android:id="@+id/text"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="#ff0000"

android:text="String"

/>

 

</LinearLayout>

 

 

CouterFragment.java

package com.example.ch20_fragmentmanager;

 

import android.app.Fragment;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.TextView;

 

 

public class CounterFragment extends Fragment{

    

    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container,

            Bundle savedInstanceState) {

        // TODO Auto-generated method stub

        //onCreateView 프래그먼트의 UI 처음 그릴때 호출한다.

        //그래서 여기서 XML 이용해서 프래그먼트 UI 구성해준다.

        //container 전개자를 사용하여 xml 가져온다

        View root = inflater.inflate(R.layout.counterfragment, container, false);

        

        Button btnIncrease = (Button)root.findViewById(R.id.btnincrease);

        final TextView textCounter = (TextView)root.findViewById(R.id.txtcounter);

      

        

        if(savedInstanceState != null){

            textCounter.setText(Integer.toString(savedInstanceState.getInt("counter")));

        }

          

        

        //간단한 버튼 클릭 리스너 달아줌

        btnIncrease.setOnClickListener(new Button.OnClickListener(){

 

            @Override

            public void onClick(View arg0) {

                // TODO Auto-generated method stub

                int count = Integer.parseInt(textCounter.getText().toString());

                textCounter.setText(Integer.toString(count + 1));

            }

            

        });

        

        return root;

    

    }

    

    //장비의 설정 상태 변경, 화면 방향 변경시에 다음 메소드가 호출된다.

    public void onSaveInstanceState(Bundle outState){

        super.onSaveInstanceState(outState);

        

        TextView textCounter = (TextView)getView().findViewById(R.id.txtcounter);

        int a = Integer.parseInt(textCounter.getText().toString());

        outState.putInt("counter", a);//번들에 저장 키와 값으로...

    }

 

}

 

 

FragmentManagerTest.java

package com.example.ch20_fragmentmanager;

 

import android.app.Activity;

import android.app.Fragment;

import android.app.FragmentManager;

import android.app.FragmentTransaction;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.TextView;

import android.widget.Toast;

 

 

public class FragmentManagerTest extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_fragment_manager_test);

    }

 

    public void mOnClick(View v){

        //프래그먼트 관리자를 가져옴

        FragmentManager fm = getFragmentManager();

        //프래그먼트를 찾음

        Fragment fragment = fm.findFragmentById(R.id.frame);

        

        switch(v.getId()){

        case R.id.btnadd://프래그먼트 생성

            if(fragment == null){

                //프래그먼트가 없으면 다음 트랜잭션으로 생성한다.

                FragmentTransaction tr = fm.beginTransaction();

                CounterFragment cf = new CounterFragment();

                tr.add(R.id.frame, cf, "counter");

                tr.commit();

            }else{

                Toast.makeText(this, "이미 추가되어 있습니다.", 0).show();

            }

            break;

        case R.id.btnremove:

            if(fragment == null){

                Toast.makeText(this, "프래그먼트가 없습니다.", 0).show();

            }else{

                //프래그먼트가 있으면 다음 트랜잭션으로 제거한다.

                FragmentTransaction tr = fm.beginTransaction();

                tr.remove(fragment);

                tr.commit();

            }

            break;

        case R.id.btnreplace:

            if(fragment == null){

                Toast.makeText(this, "프래그먼트가 없습니다.", 0).show();

            }else{

                //프래그먼트가 있으면 다음 트랜잭션으로 교체한다.

                FragmentTransaction tr = fm.beginTransaction();

                if(fragment.getTag() == "counter"){//counter태그를 가진 프래그먼트면

                    TextFragment tf = new TextFragment();//이녀석으로 바꿈

                    tr.replace(R.id.frame, tf, "text");

                }else{//아니면 다른녀서긍로 바꿈

                    CounterFragment cf = new CounterFragment();

                    tr.replace(R.id.frame, cf, "counter");

                }

                tr.commit();

            }

            break;

        case R.id.btnhideshow:

            if(fragment == null){

                Toast.makeText(this, "프래그먼트가 없습니다.", 0).show();

            }else{//프래그먼트가 있다면

                FragmentTransaction tr = fm.beginTransaction();

                if(fragment.isHidden()){//여기서 숨겨졌으면 보이게 하고 보이고있으면 숨김으로 바꿈

                    tr.show(fragment);

                }else{

                    tr.hide(fragment);

                }

                tr.commit();

            }

            break;

        }

        

        

    }

    

    public static class TextFragment extends Fragment{

        public View onCreateView(LayoutInflater inflater, ViewGroup container,

                Bundle savedInstanceState){

            View root = inflater.inflate(R.layout.textfragment, container, false);

            TextView text = (TextView)root.findViewById(R.id.text);

            text.setSaveEnabled(true);

            return root;

        }

        

    }

}

 

 

 

 

실행 화면

첫 화면에서 Add를 누르면

_

아래와 같이 프래그먼트가 추가된다.

 

Remove를 누르면 생성된 프래그먼트가 다시 제거된다.

 

 

Replace를 누르면 프래그먼트가 교체된다.

 

Hide를 누르면 프래그먼트가 숨김이 된다.

 

다시 Hide를 누르면 생긴다.

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

안드로이드 메니페스트 파일에 설치 가능한 위치를 지정하는 installLocation 속성을 추가했다.

 

속성

설명

internalOnly

내부 메모리에만 설치 할 수 있고 외부 메모리에는 설치 할 수 없다.

preferExternal

가급적이면 외부 메모리에 설치한다. 외부 메모리가 부족하거나 없으면 내부 메모리에 설치한다.

auto

내부 메모리가 부족하면 외부 메모리에 설치한다.

 

 

다음 예제를 통해 preferExternal 옵션을 줘서 실제로 외부 메모리에 설치가 되었는지 확인해보자

 

MainActivity.java

 

package com.example.ch18_external;

 

import android.app.Activity;

import android.os.Bundle;

import android.view.Menu;

import android.view.MenuItem;

import android.view.MotionEvent;

import android.widget.Toast;

 

public class MainActivity extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }

 

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        // Inflate the menu; this adds items to the action bar if it is present.

        getMenuInflater().inflate(R.menu.main, menu);

        return true;

    }

 

    @Override

    public boolean onOptionsItemSelected(MenuItem item) {

        // Handle action bar item clicks here. The action bar will

        // automatically handle clicks on the Home/Up button, so long

        // as you specify a parent activity in AndroidManifest.xml.

        int id = item.getItemId();

        if (id == R.id.action_settings) {

            return true;

        }

        return super.onOptionsItemSelected(item);

    }

 

    public boolean onTouchEvent(MotionEvent event){

        super.onTouchEvent(event);

        if(event.getAction() == MotionEvent.ACTION_DOWN){

            Toast.makeText(this, "Touch Event Received", Toast.LENGTH_SHORT).show();

            return true;

        }

        return false;

    }

}

 

 

AndroidMenifest.xml 파일

 

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.ch18_external"

android:versionCode="1"

android:versionName="1.0"

android:installLocation="preferExternal"

>

 

<uses-sdk

android:minSdkVersion="19"

android:targetSdkVersion="19" />

 

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name=".MainActivity"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

 

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

 

</manifest>

 

실행 화면

 

 

다음과 같이 MoveToSDCard가 떴다.. 원래 MoveToPhone이 떠야 되는데.

 

 

 

이유는 SD카드 용량이 별로 없어서 그런 것 같다.

아무래도 내 스마트폰에 외장 메모리를 장착하고 다시 테스트 해야겠다.

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

윈도우 관리자는 안드로이드 프레임워크를 구성하는 주요 모듈로 윈도우를 관리한다.

 

다음의 호출문으로 구할 수 있다.

 

getSystemService(Context.WINDOW_SERVICE)

 

윈도우 관리는 대부분 시스템 내부에서 알아서 수행되므로 공개된 기능은 많지 않다.

 

다음 메소드는 윈도우가 실행되는 화면에 대한 정보를 구한다.

 

    Display getDefaultDisplay()

 

Display 클래스는 장비의 화면 폭이나 높이, 방향, 갱신 주기 등의 정보를 제공한다.

 

다음 메소드는 ViewManager 인터페이스로부터 상속받은 것이며 윈도우에 개별 뷰를 추가하거나 삭제한다.

 

    void addView(View view, ViewGroup.LayoutParams params)

    void removeView(View view)

    void updateViewLayout(View view, ViewGroup.LayoutParams params)

 

 

다음은 레이아웃을 벗어난 위치에 임의의 뷰를 배치하는 예제이다.

 

activity_test.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

>

 

<TextView

android:id="@+id/result"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textSize="20sp" />

 

</LinearLayout>

 

WindowManagerTest.java

 

package com.example.ch18_windowmanager;

 

import android.app.Activity;

import android.content.Context;

import android.graphics.PixelFormat;

import android.graphics.Point;

import android.os.Bundle;

import android.view.Display;

import android.view.Gravity;

import android.view.WindowManager;

import android.widget.ImageView;

import android.widget.TextView;

 

public class WindowManagerTest extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_test);

      

        //윈도우 관리자 가져오기

        WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);

        Display dis = wm.getDefaultDisplay();

        TextView result = (TextView)findViewById(R.id.result);

        Point pt = new Point();

        dis.getSize(pt);

        result.setText("width = " + pt.x + "height = " + pt.y +

                "\nrotate = " + dis.getRotation()/*dis.getOrientation()*/);

        

        //이미지뷰 하나 가져오고 파라미터 세팅

        ImageView img = new ImageView(this);

        img.setImageResource(R.drawable.ic_launcher);

        WindowManager.LayoutParams param = new WindowManager.LayoutParams();

        param.gravity = Gravity.LEFT | Gravity.TOP;

        param.x = 100;

        param.y = 20;

        param.width = WindowManager.LayoutParams.WRAP_CONTENT;

        param.height = WindowManager.LayoutParams.WRAP_CONTENT;

        

        //FLAG_LAYOUT_IN_SCREEN 이므로 윈도우의 좌상단을 기준으로 좌표에 배치됨

        param.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN

                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

        //이미지가 아닌 버튼이나 스피너가 투명색을 제대로 표현하게 .

        param.format = PixelFormat.TRANSLUCENT;

        

        //만든 뷰를 레이아웃에 붙인다.

        wm.addView(img, param);

        

    }

 

      

    

}

 

 

출력 화면

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

윈도우는 빈 채로 생성되며 빈 윈도우 안에 레이아웃을 채워 넣어 UI를 구성하는데 이때는 다음 메소드를 호출 한다.

 

void setContentView(int layoutResID)

void setContentView(View view, [ViewGroup.LayoutParams params])

void addContentView(View view, ViewGroup.LayoutParams params)

 

다음 예제는 2개의 xml파일을 겹쳐서 보여주는 예제이다.

 

overlay1.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

 

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="바닥 레이아웃" />

    <Button

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:text="바닥의 버튼"

     />

 

</LinearLayout>

 

overlay2.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

    android:gravity="center"

    android:background="#40ffff00"

>

    <TextView

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:text="이것은 위쪽의 레이아웃 입니다."/>

 

    <Button

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:text="위쪽 버튼"/>

 

</LinearLayout>

 

Overlay.java

 

package com.example.overlay;

 

import android.app.Activity;

import android.content.Context;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.Window;

import android.widget.LinearLayout;

 

public class Overlay extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        Window win = getWindow();

        win.setContentView(R.layout.overlay1);

        

        //전개자로 xml파일을 가져옴

        LayoutInflater inflater = (LayoutInflater)getSystemService(

                Context.LAYOUT_INFLATER_SERVICE);

        LinearLayout linear = (LinearLayout)inflater.inflate(R.layout.overlay2, null);

          

        //파라미터를 세팅해줌

        LinearLayout.LayoutParams paramlinear = new LinearLayout.LayoutParams(

                LinearLayout.LayoutParams.MATCH_PARENT,

                LinearLayout.LayoutParams.MATCH_PARENT

                );

        

        //윈도우에 추가시킴

        win.addContentView(linear, paramlinear);

        

        

    }

 

 

}

 

실행화면

 

아래쪽 레이아웃은 위쪽 레이아웃 때문에 색상이 약간 변한다. 하지만 두 버튼은 독립적으로 동작해서 클릭 리스너를 추가 시킬 수 있다.

 

 

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

Window 클래스는 윈도우를 표현하며 액티비티의 모양과 동작에 대한 여러가지 옵션을 제공한다.

 

윈도우 객체는 getWindow 메소드를 호출하여 언제든지 얻을 수 있으며 이 객체를 통해 여러가지 조작을 한다.

 

boolean Window.requestFeature(int featureId)

boolean Activity.requestWindowFeature(int featureId)

 

위의 메소드를 활용해서 윈도우의 확장 기능을 활성화 할 수 있다.

 

확장기능 플래그는 다음과 같다.

 

기능

설명

DEFAULT_FEATURES

기본 기능

FEATURE_CONTEXT_MENU

컨텍스트 메뉴를 쓸 수 있다. 디폴트로 이 기능이 선택되어있음

FEATURE_CUSTOM_TITLE

커스텀 타이틀 바를 사용한다.

FEATUE_PROGRESS

타이틀 바에 막대 모양 프로그래스를 표시한다.

FEATURE_INDETERMINATE_PROGRESS

타이틀 바에 원형의 프로그래스를 표시한다.

FEATURE_LEFT_ICON

아이콘을 왼쪽에 놓는다.

FEATURE_RIGHT_ICON

아이콘을 오른쪽에 놓는다.

FEATURE_NO_TITLE

타이틀 바를 가지지 않는다.

FEATURE_OPTIONS_PANEL

옵션 패널을 가진다.

 

 

다음은 타이틀바를 없애는 예제입니다.

NoTitle.java

 

package com.example.ch18_notitle;

 

import android.app.Activity;

import android.os.Bundle;

import android.view.Window;

 

public class NoTitle extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

      

        Window win = getWindow();

        win.requestFeature(Window.FEATURE_NO_TITLE);

        

        // 두줄을 아래와 같이 한줄로 해도된다.

        //requestWindowFeature(Window.FEATURE_NO_TITLE);

 

        setContentView(R.layout.activity_no_title);

    }

 

 

}

 

실행 화면

 

 

이번에는 상태바까지 없애보자.

 

상태바까지 없애기 위해서는 다음 메소드를 사용해서 플래그를 조작한다.

 

    void addFlags(int flags)

    void clearFlags(int flags)

 

플래그

설명

FLAG_FULLSCREEN

장식을 모두 제거하고 전체 화면을 쓴다.

FLAG_BLUR_BEHIND

뒤쪽의 화면을 흐릿하게 표시한다.

FLAG_DIM_BEHIND

뒤쪽의 화면을 흐리게 표시한다.

FLAG_KEEP_SCREEN_ON

윈도우가 보이는 동안은 화면을 끄지 않는다.

FLAG_NOT_FOCUSABLE

키 입력 포커스를 받지 않는다.

FLAG_NOT_TOUCHABLE

터치 입력을 받지 않는다.

FLAG_SHOW_WALLPAPER

바탕의 배경 화면을 보인다.

FLAG_SHOW_WHEN_LOCKED

화면이 잠긴 상태에서도 보이도록 한다.

 

다음은 화면의 상태바까지 없앤 풀스크린을 만드는 예제이다.

 

FullScreen.java

 

package com.example.ch18_fullscreen;

 

import android.app.Activity;

import android.os.Bundle;

import android.view.Window;

import android.view.WindowManager;

 

public class FullScreen extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

      

        Window win = getWindow();

        win.requestFeature(Window.FEATURE_NO_TITLE);

        win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

        

        setContentView(R.layout.activity_full_screen);

    

    }

 

      

 

    

}

 

 

실행 화면

다음과 같이 상태바도 없어진 것을 볼 수 있다.

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

프로세스의 아래에는 단 하나의 유일한 Application 객체가 있고 그아래 컴포넌트인 서비스, 브로드캐스트 리시버, 액티비티, 콘텐트 프로바이더로 구성된다.

 

Application 클래스가 모든 컴포넌트보다 우선적으로 생성되고 유일한 객체만 생성되므로 전역 변수를 두기에는 제일 좋은 곳이다.

 

그리고 Application 객체의 멤버는 프로세스의 어디에서나 참조할 수 있다.

 

    void onCreate()

    void onTerminate()

    void onConfigurationChanged(Configuration newConfig)

    void onLowMemory()

 

onCreate는 응용프로그램이 실행된 직후 호출된다. 다른 컴포넌트인 서비스, 브로드캐스트 리시버, 액티비티, 콘텐트 프로바이더보다 먼저 실행되므로

 

이 메소드가 진입점이 된다.

 

onTerminate는 모든 컴포턴트가 없어진 후 호출된다.

 

onConfigurationChanged와 onLowMemory는 환경 변화나 메모리가 부족할 때 호출된다.

 

액티비티나 서비스에서는 다음 메소드로 Application 객체를 구한다.

 

    final Application getApplication()

 

다음 예제는 Application 객체에 전역변수를 만들고 액티비티에서 전역변수를 가져오는 예제이다.

 

 

activity_application_test.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

    android:orientation="vertical"

>

 

<TextView

android:id="@+id/mode"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="현재 모드 : "

android:textSize="20sp"

/>

<Button

android:id="@+id/beginner"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="초보자 모드"

/>

<Button

android:id="@+id/professional"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="숙련자 모드"

/>

 

</LinearLayout>

 

ApplicationTest.java

 

package com.example.ch13_applicationtest;

 

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.widget.TextView;

 

 

public class ApplicationTest extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_application_test);

      

        UpdateNowMode();

    }

 

    void UpdateNowMode(){

        TextView txtMode = (TextView)findViewById(R.id.mode);

        AndTest_Application app = (AndTest_Application)getApplication();

        if(app.getMode() == AndTest_Application.BEGINNER){

            txtMode.setText("현재 모드 : 초보자 모드");

        }else{

            txtMode.setText("현재 모드 : 숙련자 모드");

        }

    }

    

    public void mOnClick(View v){

        AndTest_Application app = (AndTest_Application)getApplication();

        switch(v.getId()){

        case R.id.beginner:

            app.setMode(AndTest_Application.BEGINNER);

            break;

        case R.id.professional:

            app.setMode(AndTest_Application.PREFESSIONAL);

            break;

        }

        UpdateNowMode();

    }

    

}

 

AndTest_Application.java

 

package com.example.ch13_applicationtest;

 

import android.app.Application;

 

public class AndTest_Application extends Application{

    

    private int mMode;

    //전역변수를 전언함

    static final int BEGINNER = 0;

    static final int PREFESSIONAL = 1;

    

    public void onCreate(){

        super.onCreate();

        mMode = BEGINNER;//mMode 초기화

    

    }

    

    public void onTerminate(){

        super.onTerminate();

    }

    

    //mMode 값을 불러오는 메소드

    public int getMode(){

        return mMode;

    }

    

    //mMode 세팅하는 함수

    public void setMode(int aMode){

        mMode = aMode;

    }

}

 

프로그램 시작 직후에 객체를 생성해야 하므로 매니페스트에는 이 객체의 이름을 반드시 명시해야 한다.

application 태그에 name 속성으로 클래스명을 밝혀 놓으면 태스크가 생성될 때 객체가 생성되고 전역 멤버가 초기화된 후 각 액티비티의

라이플 사이클 메소드가 호출 된다.

 

AndroidManifest.xml 파일

 

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme"

android:name="com.example.ch13_applicationtest.AndTest_Application"

>

 

실행 화면

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

어떤 목록을 보여줄 때 팝업으로 보여주고 싶다면 ListPopupWindow 위젯을 사용해야한다.

팝업 메뉴와 유사하지만 목록을 어댑터로 받는 점이 특이하다.

 

void setWidth(int width)

void setHeight(int height)

void setContentWidth(int width)

 

폭과 높이는 픽셀 단위로 지정한다.

 

    void setAdapter(ListAdapter adapter)

    void setAnchorView(View anchor)

 

위의 메소드는 어댑터와 앵커 뷰를 지정한다.

 

    void setModal(boolean modal)

 

위의 메소드는 팝업 목록의 동작 방식을 지정한다. 모달일 때는 선택을 해도 팝업이 자동으로 닫히지 않으며 팝업 바깥을 눌러야 닫힌다.

 

    void setOnItemSelectedListener(AdapterView.OnItemSelectedListener selectedListener)

    void setOnItemClickListener(AdapterView.OnItemClickListener clickListener)

 

위 메소드를 통해서 선택 변경이나 클릭 동작에 대한 이벤트 리스너를 등록한다.

 

activity_list_popup_window_test.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

    android:orientation="vertical"

>

 

<Button

android:id="@+id/btn"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="Button"/>

 

</LinearLayout>

 

 

ListPopupWindowTest.java

package com.example.ch13_lispopupwindow;

 

import android.app.Activity;

import android.graphics.Color;

import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;

import android.widget.ArrayAdapter;

import android.widget.Button;

import android.widget.ListPopupWindow;

 

public class ListPopupWindowTest extends Activity {

    Button mBtn;

    ListPopupWindow mList;

    public String[] Colors = {

        "Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"    

    };

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_list_popup_window_test);

      

        mBtn = (Button)findViewById(R.id.btn);

        mList = new ListPopupWindow(this);

        mList.setWidth(300);

        mList.setHeight(300);

        mList.setAnchorView(mBtn);//버튼이 눌리면 리스트팝업윈도우가 나온다.

        mList.setAdapter(new ArrayAdapter<String>(this,

                android.R.layout.simple_list_item_1, Colors));//어댑터 연결

        mList.setModal(true);//선택을 해도 자동으로 팝업이 닫히지 않게 한다.

        

        mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {

 

            @Override

            public void onItemClick(AdapterView<?> arg0, View arg1, int position,

                    long id) {

                // TODO Auto-generated method stub

                //선택된 값에 따라 버튼의 백그라운드 색을 바꾼다.

                switch(position){

                case 0:

                    mBtn.setBackgroundColor(Color.RED);

                    break;

                case 1:

                    mBtn.setBackgroundColor(Color.GREEN);

                    break;

                case 2:

                    mBtn.setBackgroundColor(Color.BLUE);

                    break;

                case 3:

                    mBtn.setBackgroundColor(Color.YELLOW);

                    break;

                case 4:

                    mBtn.setBackgroundColor(Color.CYAN);

                    break;

                case 5:

                    mBtn.setBackgroundColor(Color.MAGENTA);

                    break;

                }

                

            }

        

        });

    

    }

 

    public void mOnClick(View v){

        if(mList.isShowing()){

            mList.dismiss();

        }else{

            mList.show();

        }

    }

 

}

 

 

실행 화면

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

CalendarView는 스크롤이 가능한 달력을 보여주며 날짜 하나를 선택 받을 수 있다.

 

속성

설명

firstDayOfWeek

제일 왼쪽의 첫 요일을 선택

minDate

달력에 표시할 최소 날짜. mm/dd/yyyy로 지정

maxDate

달력에 표시할 최대 날짜. mm/dd/yyyy로 지정

focusedMonthDateColor

현재 선택된 달의 배경 색상이다.

selectedWeekBackgroundColor

선택된 주의 배경 색상이다.

unfocusedMonthDateColor

선택되지 않은 달의 배경 색상이다.

showWeekNumber

왼쪽에 주차를 보여줄지

weekNumberColor

주차의 색상을 지정

weekSeparatorLineColor

주 사이의 구분선

selectedDateVerticalBar

선택한 날짜의 양쪽에 보일 수직바에 대한 이미지

 

 

생성 직후에 자동으로 오늘 날짜로 설정된다.

 

long getDate() : 날짜 조사하는 메소드

void setDate(long date [, boolean animate, boolean center]) : 현재 날짜 변경 시 애니메이션이나 중앙에 오게 할 수 있다.

 

날짜가 변경될때 이벤트를 받으려면 다음 메소드로 리스너를 등록한다.

 

void setOnDateChangeListener(CalendarView,OnDateChangeListener listener)

void onSelectedDayChange(CalendarView view, int year, int month, int dayOfMonth)

 

 

main.xml 코드

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

    android:orientation="vertical"

    >

<CalendarView

android:id="@+id/calendar"

android:layout_width="match_parent"

android:layout_height="0px"

android:layout_weight="1"

/>

<CalendarView

android:layout_width="match_parent"

android:layout_height="0px"

android:layout_weight="1"

android:minDate="04/01/2013"

android:maxDate="12/31/2013"

android:focusedMonthDateColor="#ff0000"

android:unfocusedMonthDateColor="#0000ff"

android:selectedWeekBackgroundColor="#ffff00"

android:showWeekNumber="false"

android:weekSeparatorLineColor="#00ff00"

/>

 

</LinearLayout>

 

 

자바 파일

CalendarViewTest.java

 

package com.example.ch13_calendarview;

 

import android.app.Activity;

import android.os.Bundle;

import android.widget.CalendarView;

import android.widget.Toast;

 

 

public class CalendarViewTest extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

      

        //CalendarView 인스턴스 만들기

        CalendarView calendar = (CalendarView)findViewById(R.id.calendar);

        

        //리스너 등록

        calendar.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {

            

            @Override

            public void onSelectedDayChange(CalendarView view, int year, int month,

                    int dayOfMonth) {

                // TODO Auto-generated method stub

                Toast.makeText(CalendarViewTest.this, ""+year+"/"+(month+1)+"/"

                        +dayOfMonth, 0).show();

            }

        });

        

    }

 

 

    

}

  

실행 화면

 

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

넘버 피커는 일정한 범위에 있는 숫자 값 중에 하나를 선택해주는 위젯이다.

 

날짜나 시간 선택기와 비슷한 모양을 하고 있고 입력 필드를 롱터치해서 직접 값을 입력할 수도 있다.

 

    void setMinValue(int minValue)

    void setMaxValue(int maxValue)

    void setValue(int value)

 

setMinValue로 범위의 최소값을 지정하고 setMaxValue로 최대값을 지정한다. 그리고 setValue에서 최대값을 넘어가는 것을 지정했다면 자동적으로 최대값이 바뀌게 된다.

 

    void setWrapSelectorWheel(boolean wrapSelectorWheel)

 

위 메소드는 범위가 1에서 10일 때 10을 초과하면 다시 1로 갈껀지 아니면 10에서 멈출건지를 나타내는 메소드이다.

 

    void setOnLongPressUpdateInterval(long intervalMillis)

 

디폴트는 0.3초로 0.3초마다 한번씩 값이 증가한다.

 

    void setDisplayedValues(String[] displayedValues)

 

범위의 각 값에 해당하는 문자열을 배열로 지정한다.

값 변경 시나 스크롤 시에 이벤트를 받고 싶다면 다음 메소드로 이벤트를 등록한다.

 

    void setOnValueChangedListener(NumberPicker.OnValueChangeListener onValueChangedListener)

    void setOnScrollListener(NumberPicker.OnScrollListener onScrollListener)

 

변경 즉시 어떤 처리를 할 필요가 있을 때만 리스너를 등록한다.

 

다음은 예제이다.

 

activity_number_picker_test.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

>

 

<NumberPicker

android:id="@+id/picker1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginRight="2dp"

/>

<NumberPicker

android:id="@+id/picker2"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginRight="2dp"

/>

    <NumberPicker

     android:id="@+id/picker3"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:layout_marginRight="2dp"

     />

    <NumberPicker

     android:id="@+id/picker4"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:layout_marginRight="2dp"

     />

    <NumberPicker

     android:id="@+id/picker5"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:layout_marginRight="2dp"

     />

 

    

</LinearLayout>

 

 

NumberPickerTest.java

 

package com.example.ch13_numberpicker;

 

import android.app.Activity;

import android.os.Bundle;

import android.widget.NumberPicker;

import android.widget.NumberPicker.OnValueChangeListener;

import android.widget.Toast;

 

 

public class NumberPickerTest extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_number_picker_test);

 

        //기본적으로 0~5까지 넘버피커를 만들고

        //5까지 올리면 더이상 올라가지 않게 하기

        NumberPicker picker1 = (NumberPicker)findViewById(R.id.picker1);

        picker1.setMinValue(0);

        picker1.setMaxValue(5);

        picker1.setWrapSelectorWheel(false);

        

        //0~20까지만들고 길게 누르면 빨리 값이 바뀌게 하기

        NumberPicker picker2 = (NumberPicker)findViewById(R.id.picker2);

        picker2.setMinValue(0);

        picker2.setMaxValue(20);

        picker2.setOnLongPressUpdateInterval(100);

        

        //0~6으로 하고 ~일요일로 하기

        NumberPicker picker3 = (NumberPicker)findViewById(R.id.picker3);

        picker3.setMinValue(0);

        picker3.setMaxValue(6);

        picker3.setDisplayedValues(new String[]{

                "일요일", "월요일", "화요일", "수요일", "목요일", "금요일",

                 "토요일"});

 

        //0~3으로 하고 값별로 텍스트로 바꿔 출력하기

        NumberPicker.Formatter mFormatter = new NumberPicker.Formatter() {

            

            @Override

            public String format(int value) {

                // TODO Auto-generated method stub

                switch(value){

                case 0:

                    return "zero";

                case 1:

                    return "one";

                case 2:

                    return "two";

                

                }

                

                return null;

            }

        };

 

        //0~2로하고 텍스트로 바꿔 출력하기

        NumberPicker picker4 = (NumberPicker)findViewById(R.id.picker4);

        picker4.setMinValue(0);

        picker4.setMaxValue(2);

        picker4.setFormatter(mFormatter);

        

        //100~200으로 하고 값이 바뀔때마다 토스트 메시지 띄우기

        NumberPicker picker5 = (NumberPicker)findViewById(R.id.picker5);

        picker5.setMinValue(100);

        picker5.setMaxValue(200);

        picker5.setOnValueChangedListener(new OnValueChangeListener() {

            

            @Override

            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {

                // TODO Auto-generated method stub

                Toast.makeText(NumberPickerTest.this, "Value : " + newVal, Toast.LENGTH_SHORT).show();

            }

        });

        

        

    }

 

 

}

 

출력 화면

 

블로그 이미지

가카리

소프트웨어와 하드웨어 프로그래밍, 취업 및 직장생활 전문 블로그

 

Space 위젯은 말그대로 공간만 차지하는 위젯이다.

 

이러한 단순한 위젯을 활용하면 내가 원하는 곳에 위젯을 배치하는데 편리하게 이용할 수 있다.

 

예제는 매우 간단하다.

 

xml파일

activity_space_test.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

    android:orientation="vertical"

>

 

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Upper Button"

/>

<Space

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginBottom="30dp"/>

<Button