미치겠다 봐도 모르겠다..1818181818..

=====================================

import java.util.*;

import android.app.*;
import android.content.*;
import android.graphics.*;
import android.os.*;
import android.view.*;

public class FreeLine extends Activity {
private MyView vw;
ArrayList<Vertex> arVertex;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        vw = new MyView(this);
        setContentView(vw);

        arVertex = new ArrayList<Vertex>();
    }

    // 정점 하나에 대한 정보를 가지는 클래스
public class Vertex {
Vertex(float ax, float ay, boolean ad) {
x = ax;
y = ay;
Draw = ad;
}
float x;
float y;
boolean Draw;
}
    protected class MyView extends View {
     Paint mPaint;

     public MyView(Context context) {
       super(context);
       
       // Paint 객체 미리 초기화
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
mPaint.setAntiAlias(true);
   }

   public void onDraw(Canvas canvas) {
canvas.drawColor(0xffe0e0e0);
// 정점을 순회하면서 선분으로 잇는다.
for (int i=0;i<arVertex.size();i++) {
if (arVertex.get(i).Draw) {
canvas.drawLine(arVertex.get(i-1).x, arVertex.get(i-1).y, 
arVertex.get(i).x, arVertex.get(i).y, mPaint);
}
}
}

   // 터치 이동시마다 정점들을 추가한다.
   public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
    arVertex.add(new Vertex(event.getX(), event.getY(), false));
    return true;
    }
    if (event.getAction() == MotionEvent.ACTION_MOVE) {
    arVertex.add(new Vertex(event.getX(), event.getY(), true));
    invalidate();
    return true;
    }
    return false;
   }
}
}

안드로이드 코딩을 할 때, 가장 기본적인 메소드 중 하나가 바로 startActivityForResult()이다.

하지만 웹 어디를 뒤져봐도 사용법을 쉽게 설명해 둔 페이지는 찾아보기가 힘들더라. (망할것)

나처럼 완전 삽질을 하는 사람이 생겨나는 것을 막고자 이렇게 포스팅을 해보려 한다.

 

안드로이드의 Activity들은 startActivityForResult()라는 메소드를 통해 sub activity를 만들고 Activity끼리 서로 데이터를 교환할 수 있다.

그 사이에는 'extra'라고 하는 통로가 존재하는데, 이 부분을 잘 알아야 한다.

 

Activity A가 Activity B를 서브 엑티비티로서 부르고 그로부터 결과값인 데이터를 전송받기 위해서는 어떻게 해야 할까?

먼저 삽화를 통해 기본 원리를 파악해보도록 하자.

 

다음과 같은 원리이다.

Activity A가 startActivityForResult를 통해서 Activity B를 호출하면,

B는 종료가 되면서 Result 값을 통해 Extra 꾸러미를 넘긴다.

그러면 Activity는 Extra 꾸러미 안에 있는 데이터들을 꺼내서 사용할 수 있는 것이다.

 

그렇다면 실제 구현 과정에서 어떤 작업들을 해주어야 하는지 살펴보자.

 

[Activity A 사이드]

Activity A에서 구현되어야 할 부분은,

첫째, int값의 requestCode 값을 설정해준다.

둘째, Intent를 만들어 Activity B를 실행시킨다.

셋째, onActivityResult()를 통해 각 requestCode값에 해당하는 결과값을 받아온다.

 

[Activity B 사이드]

Activity B에서 구현되어야 할 부분은,

첫째, Intent를 만들어 데이터 꾸러미를 Intent에 추가시킨다.

둘째, 결과값을 보내면서 Extra 꾸러미를 가지고 있는 Intent를 함께 넘겨준다.

 

간단하지 않은가? 그럼 코드에서는 이와같은 것들이 어떻게 구현이 될까?

 

[Activity A 사이드]

private static final int B_ACTIVITY = 0;

 

public void onCreate(){

...

Intent a_i = new Intent(this, B.class);

startActivityForResult(a_i, B_ACTIVITY);

...}

 

public void onActivityResult(int requestCode, int resultCode, Intent intent){

super.onActivityResult(requestCode, resultCode, intent);

 

switch(requestCode){

case B_ACTIVITY: // requestCode가 B_ACTIVITY인 케이스

if(resultCode == RESULT_OK){ //B_ACTIVITY에서 넘겨진 resultCode가 OK일때만 실행

intent.getExtras.getInt("data"); //등과 같이 사용할 수 있는데, 여기서 getXXX()안에 들어있는 파라메터는 꾸러미 속 데이터의 이름표라고 보면된다.

}

}

}

 

[Activity B 사이드]

 

Bundle extra;

Intent intent;

 

onCreate(){

...

extra = new Bundle();

intent = new Intent(); //초기화 깜빡 했다간 NullPointerException이라는 짜증나는 놈이랑 대면하게 된다.

...

 

extra.putInt("data", 1);

intent.putExtras(extra);

this.setResult(RESULT_OK, intent); // 성공했다는 결과값을 보내면서 데이터 꾸러미를 지고 있는 intent를 함께 전달한다.

this.finish();

}

 

 

이와 같이 하면 B 사이드에서 1이라는 데이터값을 가지고 있는 "data"라는 이름표의 꾸러미는 Activity A로 결과값으로써 전달이 되고, 사용이 될 수 있는 것이다.

나는 Bundle을 이용해서 했는데 번들을 사용하지 않고도 결과값을 보내기 위한 메소드는 있다고 본다. 이거는 API를 보면 쉽게 파악할 수 있다.

또한 int값 말고도 모든 데이터 변수들을 보낼 수 있어, 그것에 대한 걱정은 하지 않아도 된다.

 

실제로 예를 만들어보고 생각을 해보면서 공부해보면 디버깅의 어려움 없이 코드를 구성할 수 있을 것이다.

안드로이드 개발자들이여 디버깅 free의 그날이 올 때까지 쭉~ 노력합시다

안드로이드 실전 개발 - SQLite

1. SQLite 소개

SQLite는 아시다시피 iphone이나 android에서 사용하는 파일 베이스 RDB입니다. 주요 특징으로는 Zero Configuration, Portabiliy, Compactness, Simplicity, Flexibility, Liberal Licensing, Reliability 라고 http://www.sqlite.org/ 사이트에 나와 있습니다. 좋은 말은 다 있는것 같습니다.

저희가 알아야 될 세부 특징은 다음과 같습니다. 중요합니다.

SQLite는 ANSI92의 기능을 대부분 지원하지만, 아래의 사항은 지원하지 않습니다.

1. RIGHT and FULL OUTER JOIN : LEFT OUTER JOIN만 지원합니다.
2. Complete ALTER TABLE Support : RENAME TABLE과 ADD COLUMN만 지원합니다. DROP COLUMN, ALTER COLUMN, ADD CONSTRAINT 등 다른 기능은 지원하지 않습니다.
3. Writing to VIEWs : SQLite에서 View는 read-only입니다.
4, GRANT and REVOKE : Sqlite에서 읽기/쓰기 권한은 OS 파일 시스템 권한을 사용합니다. 별도로 권한 부여 기능이 없습니다.

2. GUI Tools

SQLite 개발을 효과적으로 하기 위해서는 좋은 툴은 필수겠죠?

SQLite를 지원하는 GUI Tools 리스트는 아래 링크에 나와있습니다.
http://sb2.info/commercial-and-freeware-sqlite-tools-list-2/

무지 많습니다. 그 중 몇개를 골라서 사용해 봤는데.. 제가 추천해 드리고 싶은 툴은 다음과 같습니다.

SQLite Expert (http://www.sqliteexpert.com/)


여러 제품 중에서 가장 무난하게 사용할 수 있는 제품인 것 같습니다. 필요한 기능은 다 있고, UI도 그럭저럭 쓸만합니다. Personal Edition과 Professional Edition으로 나누어지며, Personal Edition은 무료입니다.
(향후 SQLite관련 포스트에서 저는 이 제품으로 테스트 하겠습니다.) Pro Edition의 Crack도 그리 어렵지 않게 구할 수 있습니다.

SQLite Maestro (http://www.sqlmaestro.com/products/sqlite/maestro/)


아마도 제가 본 Sqlite 관련 툴 중에서 가장 강력한 제품인 것 같습니다. 다른 기능은 몰라도 Reverse로 ERD를 만들어 주는 기능은 이 제품만이 가능합니다.

여담이지만 제가 Touch Call(터치콜) App 개발할 당시 Android SDK2.0 기준의 전화번호부 DB를 이용해야 하는데, 구글의 document만으로는 이해하기가 상당히 어려웠습니다. 출판되어 있는 책들도 다 이전 데이터베이스를 기준으로 작성된 책들이고.. 그때 이 제품을 평가판으로 설치해서 애뮬레이터에 있는 contact2 데이터베이스를 가지고 ERD를 만들어 보니… 훨씬 빠르게 이해를 할 수 있었습니다. 

다음 그림은 그 당시 사용했던 contact2 db의 ERD입니다.



좋은 제품이긴 하지만 애석하게도 free 버전은 없습니다. 모두 상용버전 밖에 없으며, 한달 Trial 버전만 사용할 수 있습니다. 현재 버전의 crack은 거의 구하기 힘들며, 이전 버전은 구하실 수는 있을 겁니다.

DeZign (http://www.datanamic.com/dezign/index.html)
현재 SQLite를 Forward/Backward Engineering를 완벽하게 지원하는 모델링 툴은 datanamic의 dezign이란 모델링 툴 밖에 없습니다. 근데, 막상 평가판 설치해보니..저희 정서와 맞지 않는 툴이더군요. 저희는 한글로 논리모델을 만들고 영문으로 물리모델을 만드는데, 이 툴은 그런 개념없이 논리/물리가 함께 처리되는 툴이라 바로 지워 버렸습니다. 조금 불편하더라도 DA#이나 ERWin으로 모델링을 하고, Forward Generation한 스크립트를 수정해서 사용하는 게 더 편리할 듯 합니다.

끝으로 SQLite 강좌에서 사용할 demo db에 대해서 소개를 하겠습니다. 위에서 얘기했듯이 저는 SQLlite Expert Personal 버전을 이용해서 테스트를 하도록 하겠습니다.

SQLite Expert Personal를 실행한 후 File > Open Demo Database를 실행합니다.
Dbdemos란 데이터베이스가 스키마브라우저에 나타납니다.


좌측 스키마 브라우저에 많은 테이블 리스트가 나옵니다. 저는 그중에서 아래 ERD에 나오는 몇 개의 테이블을 주로 사용해서 DML 테스트 등을 진행하도록 하겠습니다.


고객, 직원, 주문, 주문내역 등 간단한 ERD 구조입니다.
참고로 이 ERD는 Dbdemos SQLlite 데이터베이스를 SQLite Maestro를 이용하여 일부 테이블만 designer에 띄운 모습입니다.)

3. System Catalog

시스템 카탈로그를 조회할 수 있는 방법은
sqlite_master 테이블을 직접 조회하시면 됩니다. SQLITE_MASTER 테이블은 READ-ONLY 테이블입니다.

SELECT * FROM SQLITE_MASTER;

SQLITE_MASTER 테이블을 조회하시면 테이블, 인덱스, 트리거등 모든 정보와 DDL문까지 알 수가 있습니다.

SQLITE_MASTER 테이블 조회 외에 PRAGMA 명령어를 통해서도 필요한 정보를 알 수 있습니다.

PRAGMA table_info(table-name);
테이블 정보를 조회하는 명령어입니다.

PRAGMA table_info(COUNTRY);

cid name type notnull dflt_value pk
0 Name CHAR(24) 0  1
1 Capital CHAR(24) 0  0
2 Continent CHAR(24) 0  0
3 Area FLOAT 0  0
4 Population FLOAT 0  0


PRAGMA index_list(table-name); 인덱스 리스트를 볼 수 있습니다.
PRAGMA index_info(index-name); 인덱스 정보를 조회할 수 있습니다.
PRAGMA foreign_key_list(table-name); fk 리스틀 볼 수 있습니다.

4. DATATYPE

Sqlite가 지원하는 데이터 타입은 다음과 같습니다.

1. Null
2. Integer ? 부호있는 정수, 실제 값에 따라 1byte에서 8byte까지 가변적으로 저장됨.
3. Real ? 실수
4. Text ? 문자열
5. BLOB ? blob 데이터

실제적으로 저희가 테이블 생성시 DDL상에 VARCHAR(10)이라고 컬럼 사이즈를 정의해도 SQLITE는 TEXT 타입으로 만들어집니다. 그렇기 때문에 10자 이상의 데이터도 삽입이 가능합니다. 그러니, DDL 문 만들 때 구지 다른 데이터 타입을 외울 필요없이 위 타입만 알고 있으면 될 것 같습니다.

재미있는 것은 Data and Time 즉, 날짜 관련 데이터 타입이 따로 없다는 것입니다. DATETIME은 입력되는 값에 따라서 TEXT, REAL, INTEGER 타입으로 저장됩니다. http://www.sqlite.org/datatype3.html 보시면 어떤 데이터 타입이 어떤 식으로 변경되는지 알수가 있습니다.

5. CREATE TABLE


예제1)
CREATE TABLE TEST2 (
     _ID INTEGER NOT NULL,    
     CLASS TEXT NOT NULL,    
     VALUE TEXT,
     CONSTRAINT TEST2_PK PRIMARY KEY (_ID, CLASS)
);

SQLite에서는 ALTER TABLE 문에서 ADD CONSTRAINT 구문이 지원되지 않기 때문에 PRIMARY KEY, UNIQUE, CHECK등의 TABLE LEVEL의 CONSTRAINT는 위 문장처럼 CREATE TABLE 문 제일 하단에 기술해야 합니다.


예제2)
CREATE TABLE TEST1 (
     _ID  INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
     TYPE TEXT NOT NULL DEFAULT 'HIGH',
     NAME TEX);

일련번호 채번은 위와 같이 AUTOINCREMENT 구문을 CRATE TABLE 의 해당 컬럼에 기술하면 자동 증가되는 일련번호를 사용할 수 있습니다.
AUTOINCREMENT 문을 가진 DDL이 최초 실행되면 SQLITE는 내부적으로 SQLITE_SEQUENCE 테이블을 생성합니다. 이 테이블은 NAME, SEQ 컬럼을 가진 테이블로 테이블마다 자동 증가되는 최종값을 가집니다. 재미있게도 SQLITE_SEQUENCE 테이블값을 직접 핸들링도 가능합니다.

6. SELECT

SELECT 는 워낙 잘 들 아시는 부분이라  한 두가지만 테스트 해 보도록 하겠습니다.
먼저, 오라클의 dual 테이블 같이 처리되는 구문은 MS-SQL 처럼 FROM 절 없이 사용하면 될 것 같습니다.

예)
SELECT 'A' a


페이징 처리 (ROWNUM, TOP과 유사한 기능)

페이징 처리시 오라클에서는 ROWNUM을 사용하고 MS-SQL에서는 TOP을 주로 사용들 합니다. 물론 요즘은 ROW_NUMBER() 함수를 더 많이 사용하신다구요?
SQLITE에서는 페이징 처리하기가 더 편리합니다. LIMIT와 OFFSET이 그것입니다.

SELECT CUSTNO, COMPANY, CITY
  FROM CUSTOMER 
 ORDER BY CUSTNO
 LIMIT 5 OFFSET 5;


ORDER BY와 상관없이 사용이 가능합니다. LIMIT에 값을 지정하면 화면에 출력할 레코드 개수를 지정할 수 있습니다. OFFSET은 건너뛸 레코드 수를 나타냅니다. 즉, 위의 쿼리는 CUSTNO로 정렬해서 나온 순서에서 6번째부터 5개의 레코드를 가져오는 SELECT문입니다. OFFSET은 생략도 가능합니다.

페이징은 편리하나 오라클의 ROWNUM 처럼 결과에 수치값을 나타낼 방법이 있다면 여러모로 편리한데 그런 기능은 아쉽습니다. 필요시에는 별도의 NUMBER를 가진 테이블을 하나 만들어서 조인해서 사용해야 할 듯 합니다.

7. UPDATE (JOIN)

UPDATE 구문도 워낙 잘 아시니 여기서는 JOIN UPDATE에 대해서만 확인해 보도록 하겠습니다.
테스트를 위해 CUSTOMER 테이블에 컬럼을 2개 추가했습니다.

/* JOIN UPDATE TEST */
-- ALTER TABLE에서 한번여 여러 컬럼 추가도 안됩니다.

ALTER TABLE CUSTOMER
ADD CAPITAL TEXT;

ALTER TABLE CUSTOMER
ADD COLUMN CONTINENT TEXT;

-- 오라클 스타일 JOIN UPDATE TEST => ERROR
UPDATE CUSTOMER C
   SET (CAPITAL, CONTINENT) = (SELECT CAPTIAL, CONTINENT  
                                WHERE COUNTRY K                               
                                  AND K.[Name] = C.COUNTRY)                                 
 WHERE COUNTRY IN (SELECT NAME FROM COUNTRY);

-- MS-SQL 스타일 JOIN UPDATE => ERROR
UPDATE CUSTOMER
   SET CAPITAL = K.CAPITAL,  
       CONTINENT = K.CONTINENT      
FROM CUSTOMER C JOIN COUNTRY K ON C.Country = K.NAME;


찾아보니 JOIN UPDATE 자체를 지원하지 않는답니다. 에구, 이게 안되는 건 좀 치명적인듯. 업데이트시에 어플에서 처리하는 방식밖에 안될 것 같습니다.

8. DELETE

DELETE시에도 LIMIT와 OFFSET 구문을 함께 사용할 수 있습니다. 이 부분은 편리한 듯~ , 참 UPDATE 구문에도 LIMIT와 OFFSET 구문을 함께 사용할 수 있습니다.

DELETE문도 단순 스타일은 워낙 잘 들 아시니 중복 레코드 제거 쿼리 테스트만 해보도록 하겠습니다.


/* 중복 제거 DELETE 문 테스트 */

-- 임시 테이블 생성.
create table t1 (
id integer,
name varchar(10)
);

-- 테스트 데이터 삽입.
insert into t1 values(1,'a');
insert into t1 values(2,'a');
insert into t1 values(2,'b');
insert into t1 values(1,'b');
insert into t1 values(1,'C');

-- 오라클에서 주로 사용하는 방식으로 테스트.
-- ANY 키워드를 지원하지 않아서 에러가 발생함.
DELETE FROM t1 A
WHERE ROWID > ANY (SELECT ROWID
                                     FROM t1 B
                                    WHERE A.id = B.id);

-- DELETE문에서 서브쿼리로 조인을 지원안해서 이것도 에러가 발생함.
DELETE FROM t1 A
WHERE ROWID > (SELECT MIN(ROWID)
                              FROM    t1 B
                             WHERE A.id = B.id);

-- 조금은 부하가 있지만, GROUP BY절을 이용한 NOT IN 서브쿼리로 중복 제거 가능.
DELETE FROM t1
WHERE ROWID NOT IN (SELECT MIN(ROWID)
                      FROM T1                     
                     GROUP BY ID);

9.Core Function

 coalesce(X,Y,...) coalesce() 함수는 Argument 중에서 첫번째로 Not Null인 Argument값을 리턴하는 함수입니다. 만일 모든 인자가 null이면 null을 리턴합니다.
 ifnull(X,Y) ifnull() 함수는 두 인자중에서 첫번째로 Not Null인 인자값을 리턴합니다. 만일, 둘다 null이면 null을 리턴합니다. Ifnull() 함수는 인자가 두개인 coalesce() 함수와 동일합니다.
 length(X) 길이값을 리턴하는 함수입니다. 만일 X 인자가 null이면 null을 리턴합니다.
 like(X,Y)
 like(X,Y,Z)
Like 함수는 “Y LIKE X [ESCAPE Z]”구문과 동일합니다.
 lower(x) 소문자로 변환 합니다.
 upper(X) 대문자로 변환 합니다.
 ltrimX)
 ltrim(X,Y)
ltrim(X)는 X 값 중 왼쪽편의 공백을 제거하는 함수입니다.
ltrim(X,Y)는 X 문자열중에서 Y에 나타난 값을 제일 좌측부터 제거하는 함수입니다.
 
select ltrim(" ZZZZabcZZ ", " aZ")
=> 좌측문자열에서 부터 공백,a,Z 문자열이 있으면 제거하고, 처음으로 공백,a,Z가 아닌 문자열부터 출력합니다. 즉, “bcZZ”가 출력됩니다.
ltrim(" ZZZZabcZZ ", " ") 는 ltrim(" ZZZZabcZZ ")과 동일합니다.
 rtrim(X)
 rtrim(X,Y)
rtrim(X)는 우측편 공백 제거
rtrim(X,Y)는 ltrim(X,Y)와 동일한 방식이지만 우측편부터 매칭되는 글자를 제거합니다. 예를들어 select rtrim(" ZZZZabcZZ ", " Z") 문장은 우측편부터 공백과 Z를 빼고 처음으로 공백과 Z가 아닌 글자, 즉 c 까지 글자가 나타납니다. 결과값: “ ZZZZabc”
 trim(X)
 trim(X,Y)
trim(X)는 양쪽 공백 제거
trim(X,Y) 는 Y에 해당되는 글자를 양쪽 끝에서 부터 제거하고 나머지 글자만 리턴함. 예) trim(" ZZZZabcZZ ", " Z") => “abc” trim은 ltrim과 rtrim을 각각 적용한것과 동일한 결과가 나타납니다.
 max(X,Y,..) 인자값들 중 최대값을 리턴합니다.

create table t1 (coll integer, col2 integer, col3 integer);

insert into t1 values(1,2,3);
insert into t1 values(5,3,1);

select max(col1, col2, col3) from t1;
 min(X,Y,...) 인자값들 중 최소값을 리턴합니다.
 nullif(X,Y) 두 인자가 서로 같으면 null을 리턴, 서로 다르면 X값을 리턴합니다.
nullif('x','y') => ‘x’ , nullif('x','x') => null 리턴
 quote(X) Quote()함수는 single quotation을 escape 해줍니다. ‘값을 ‘’ 로 변경합니다.
Insert나 update 시에 사용하면 유용할 듯 합니다.
select quote("girl's mouse") => 'girl''s mouse'
 random(*) -9223372036854775808 부터 +9223372036854775807 숫자 사이의 임의의 수를 리턴합니다.
 randomblob(N) N으로 지정된 bytes의 랜덤 바이너리 데이터를 생성합니다.
 hex(X) 바이너리 값을 hex 값으로 변경합니다.
select hex(randomblob(16))
 replace(X,Y,Z) X 문자열 중에서 Y문자열을 Z로 변경합니다.
select replace('1/12/2009','1','x') => "x/x2/2009"
 round(X)
 round(X,Y)
반올림 함수. Y는 소수점 자리. Y가 없으면 0으로 처리합니다.
Round(3.5) => 4, round(2.555, 2) => 2.56
 substr(X,Y)
 substr(X,Y,Z)
substr()함수는 X문자열 중에서 Y번째부터 시작해서 Z개수만큼 문자열을 가져오는 함수입니다. Z가 생략되면 Y번째 문자열부터 문자열 끝까지 리턴합니다. Y의 최소값은 1입니다. 자바에서는 0으로 시작하지만 SQLite에서는 1부터 인덱스가 시작합니다. 만일 Y가 ?(마이너스)값이면 문자열 우측끝부터 카운팅을 시작합니다.
select substr("string", 1, 3) => str
select substr("string", 0, 3) => st
select substr("string", -1, 3) => g
select substr("string", -3, 3) => ing
select substr("string", 2) => tring
 typeof(X)  X 표현식에 대한 데이터 타입을 리턴합니다. 리턴값은 “null”, “integer”, “real”, “text”, “blob” 중의 하나입니다.

10. Aggregation Function (집합 함수)

avg(X) 그룹내의 Not Null값의 평균값을 리턴합니다. X 컬럼값이 문자열이나 BLOB이면 0으로 간주하고 처리합니다.
count(*)
count(X)
count(X)는 X가 Not Null값을 가진 레코드의 개수 리턴합니다.
count(*)는 그룹내의 모든 rows의 수를 리턴합니다.
group_concat(X)
group_concat(X,Y)
X가 not null인 경우 그룹내의 모든 문자열을 콤마(,)를 구분자로 해서 문자열을 합쳐서 리턴합니다. Y가 주어지면 Y값이 구분자로 처리됩니다.
max(X) 그룹내의 값들중 최대값을 리턴합니다.
min(X) 그룹내의 값들중 최소값을 리턴합니다. 모든값이 Null이면 Null을 리턴합니다.
sum(X)
total(X)
Sum과 total은 그룹내의 Not Null값의 합계를 리턴합니다. X의 모든 값이 Null인 경우 Sum()은 Null을 리턴하고 Total()은 0.0을 리턴합니다.

11. DateTime 관련 Keyword

먼저 SQLite에서는 DateTime관련해서 다음과 같은 세가지 키워드가 있습니다.
CURRENT_TIME : 현재 시간 (형식: 03:22:56) 다만, UTC 기준입니다.
CURRENT_DATE: 현재 날짜 (형식: 2010-08-25) UTC 기준.
CURRENT_DATETIME : 현재 날자 및 시간 (형식: 2010-08-25 03:23:37) UTC 기준.

UTC 기준 날짜 및 시간이라서 사용할때는 로컬시간으로 다시 변환작업이 필요할 듯 보입니다. DATETIME 관련 함수들이 있어 크게 유용해 보이지는 않습니다.

12. DateTime 관련 함수.

DateTime 관련 함수는 다음의 5가지가 있습니다.

date(timestring, modifier, modifier, ...)  : 날짜 함수
time(timestring, modifier, modifier, ...)  : 시간 함수
datetime(timestring, modifier, modifier, ...)  :날짜/시간 함수
julianday(timestring, modifier, modifier, ...)  : 율리우스력 함수
strftime(format, timestring, modifier, modifier, ...)  : 날짜 포맷팅 함수


위 다섯개의 함수 인자 중 timestring 파라미터에로 들어갈 수 있는 날짜 형식은 아래와 같습니다.

1. YYYY-MM-DD
2. YYYY-MM-DD HH:MM
3. YYYY-MM-DD HH:MM:SS
4. YYYY-MM-DD HH:MM:SS.SSS
5. YYYY-MM-DDTHH:MM
6. YYYY-MM-DDTHH:MM:SS
7. YYYY-MM-DDTHH:MM:SS.SSS
8. HH:MM
9. HH:MM:SS
10. HH:MM:SS.SSS
11. now
12. DDDDDDDDDD

strftime 함수에 사용되는 format에 사용할 수 있는 값들은 다음과 같습니다.

%d    day of month: 00
%f     fractional seconds: SS.SSS
%H    hour: 00-24
%j     day of year: 001-366
%J     Julian day number
%m    month: 01-12
%M    minute: 00-59
%s     seconds since 1970-01-01
%S     seconds: 00-59
%w     day of week 0-6 with sunday==0
%W     week of year: 00-53
%Y     year: 0000-9999
%%    %

함수의 modifer에 들어갈 수 있는 값들은 다음과 같습니다.

1. NNN days
2. NNN hours
3. NNN minutes
4. NNN.NNNN seconds
5. NNN months
6. NNN years
7. start of month
8. start of year
9. start of day
10. weekday N
11. unixepoch
12. localtime
13. utc


잘 안 와 닿는듯 합니다. 아래 샘플 코드를 보면 이해가 가실 겁니다.

--UTC 기준의 현재 날짜/시간
select datetime('now');
2010-08-25 04:01:46

-- 로컬 기준의 현재 날짜/시간
select datetime('now','localtime');
2010-08-25 13:02:30

--현재 로컬 기준 시간에서 10분 3.5초를 더한 시간.
select datetime('now','localtime','+3.5 seconds','+10 minutes');
2010-08-25 13:14:15

--현재 로컬 시간에 3.5초를 더하고 날짜는 돌아오는 화요일 (weekday == 0 이 일요일입니다.)
select datetime('now','localtime','+3.5 seconds','weekday 2');
2010-08-31 13:05:39

--현재 달의 마지막 날짜
SELECT date('now','start of month','+1 month','-1 day','localtime');
2010-08-31

--2004-01-01 02:34:56초부터 현재까지의 총 초
SELECT strftime('%s','now') - strftime('%s','2004-01-01 02:34:56');
209785028

--현재날짜/시간 기준에서 올해 9번째달의 첫번째 화요일
SELECT date('now','start of year','+9 months','weekday 2');
2010-10-05

-- 날짜 포맷 스타일 변경
select strftime("%Y/%m/%d %H:%M:%S",'now','localtime');
2010/08/27 09:17:22



이것으로서 간략하게 나마 SQLite에 대해서 알아보았습니다.

SQLite User Guide 블로그 포스트

1. SQLite User Guide - 소개. GUI Tools 등
2. SQLite User Guide - PRAGMA, 시스템 카탈로그, DATA TYPE
3. SQLite User Guide - DDL(CREATE, DROP 등)
4. SQLite User Guide - DML (SELECT, INSERT, UPDATE, DELETE 등)
5. SQLite User Guide - Function (내장함수, Aggregation 함수)
6. SQLite User Guide - DateTime 함수, DateTime Formatting
7. SQLite User Guide - Trigger

 



 

 

레이어 닫기

작성하신 글은 아래의 서비스로 보내신 글입니다.
해당 서비스에서도 삭제 반영 됩니다.
(비디오반영은 추후 제공됩니다.)

* 글보내기 정보는 아래와 같습니다.

취소
쓰불..안드로이드 내장DB에서 forign키가 안먹히다니...비극이넹..결국 트리거로 작성함..

SQLite를 사용한 트리거 (Trigger) 의 이해 그리고 사용 방법 | 데이터베이스
권진호 2009.01.16 15:17
파일 및 문자열로 저장하기 위한 간단한 XML 문서 빌더를 만들어 봅시다.
처음에 잘못된 소스 올려 수정했습니다.

------------------------------------------------------------------------------------------
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 간단히 XML 문서 생성하기
 * 간단한 형식의 XML 문서를 생성하기 위한 유틸성 클래스입니다.
 * 사용법은 XML 순서대로 Element와 Attribute를 등록해주면 되고, 순서에 맞지 않는 XML 문서의 정의는 지원하지 않습니다.
 *
 * 
 */
public class SimpleXMLBuilder {
    /** 최상위 문서 객체 */
    private Document doc;

    /** 마지막 추가한 Element의 부모 */
    private Element parent;

    /** 마지막에 추가한 Element */
    private Element element;

    /**
     * 생성자 : root element 받음
     * 
     * @param root
     * @throws ParserConfigurationException
     */
    public SimpleXMLBuilder(String root) throws ParserConfigurationException {
        doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        element = doc.createElement(root);
        parent = element;

        doc.appendChild( element );
    }

    /**
     * 현재 Element에 자식 Element 추가
     *
     * @param name
     */
    public void addSon(String name) {
        Element e = doc.createElement(name);
        element.appendChild(e);
        parent = element;
        element = e;
    }

    /**
     * 현재 부모 Element에 자식 Element 추가
     * 
     * @param name
     */
    public void addBrother(String name) {
        Element e = doc.createElement(name);
        parent.appendChild(e);
        element = e;
    }

    /**
     * 위치 이동
     *
     * @param deep 노드의 깊이
     * @param position 한 부모 노드에서의 형재 노드간의 위치
     * @throws Exception
     */
    public void move(int deep, int position) throws Exception {
        move(doc.getDocumentElement(), deep, position);
    }

    /**
     * 특정 노드로 부터의 위치 이동
     *
     * @param node 기준 노드
     * @param deep 노드의 깊이
     * @param position 한 부모 노드에서의 형재 노드간의 위치
     * @throws Exception
     */
    public void move(Node node, int deep, int position) throws Exception {
        Node n = node;
        for(int i=0; i<deep; i++) {
            if( n.getFirstChild() == null )
                throw new Exception("Can't move to deep " + deep);
            
            n = n.getFirstChild();
        }

        if( position > 0 ) {
            NodeList nodeList = n.getParentNode().getChildNodes();
            n = nodeList.item(position);
            if( n == null )
                throw new Exception("Can't move to position " + position);
        }

        element = (Element) n;
        if( n.getParentNode() != null )
            parent = (Element) n.getParentNode();
    }

    public void move(String expr) throws Exception {
        move(doc.getDocumentElement(), expr);
    }

    public void move(Node node, String expr) throws Exception {
        Node n = XPathAPI.selectSingleNode(node, expr);
        if( n == null )
            throw new Exception("Can't move to xpath expression " + expr);

        element = (Element) n;
        if( n.getParentNode() != null )
            parent = (Element) n.getParentNode();
    }

    /**
     * 마지막 추가한 Element에 속성 추가
     * 
     * @param name
     * @param value
     */
    public void setAttribute(String name, String value) {
        element.setAttribute(name, value);
    }

    /**
     * XML 문서를 문자열로 저장
     * 
     * @return
     * @throws IOException
     */
    public String saveToString() throws IOException {
        OutputFormat format = new OutputFormat();
        format.setIndenting(true);
        
        StringWriter out = new StringWriter();
        XMLSerializer serializer = new XMLSerializer(out, format);
        serializer.asDOMSerializer();
        serializer.serialize(doc.getDocumentElement());

        return out.toString();
    }

    /**
     * XML 문서를 File로 저장
     * 
     * @param filePath
     * @throws IOException
     */
    public void saveToFile(String filePath) throws IOException {
        File file = new File(filePath);
        if( file.exists() )
            throw new IOException("File already exists");

        OutputFormat format = new OutputFormat();
        format.setIndenting(true);

        FileWriter fw = new FileWriter(file);
        
        XMLSerializer serializer = new XMLSerializer(fw, format);
        serializer.asDOMSerializer();
        serializer.serialize(doc.getDocumentElement());

        fw.close();
    }

    public static void main(String[] args) {

        try {
            SimpleXMLBuilder xml = new SimpleXMLBuilder("애완동물");
            xml.addSon("조류");
            xml.addBrother("포유류");
            xml.addBrother("절지류");
            xml.addBrother("파충류");
            xml.addBrother("곤충류");

            // 계층 구조를 깊이와 위치로 이동하기
            xml.move(1, 0);
            xml.addSon("앵무새");
            xml.setAttribute("번호", "1");

            xml.addBrother("참새");
            xml.setAttribute("번호", "2");

            xml.addBrother("딱다구리");
            xml.setAttribute("번호", "3");

            xml.addBrother("부엉이");
            xml.setAttribute("번호", "4");

            // 계층 구조를 깊이와 위치로 이동하기
            xml.move(1, 0);
            xml.move(xml.element, 1, 1);

            xml.addSon("검참새");
            xml.addBrother("황참새");
            xml.addBrother("똥참새");

            // 계층 구조를 깊이와 위치로 이동하기
            xml.move(1, 1);
            xml.addSon("강아지");
            xml.setAttribute("번호", "1");

            xml.addBrother("고양이");
            xml.setAttribute("번호", "2");

            xml.addBrother("돼지");
            xml.setAttribute("번호", "3");

            // 계층 구조를 XPath 표현식으로 이동하기
            xml.move("/애완동물/포유류/고양이");
            xml.addSon("암고양이");
            xml.addBrother("숫고양이");

            System.out.println( xml.saveToString() );

        } catch(Exception e) {
            System.out.println(e);
        }

        
    }

@@안드로이드

Writing a DOM Document to an XML File
     
import java.io.File;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

public class Main {
  public static void main(String[] argvthrows Exception {
    Document doc = null;
    String filename = "name.xml";

    Source source = new DOMSource(doc);

    File file = new File(filename);
    Result result = new StreamResult(file);

    Transformer xformer = TransformerFactory.newInstance().newTransformer();
    xformer.transform(source, result);
  }
}

   
    
    
    
    
  
Related examples in the same category

1.  XML Document Writer
2.  XML Writer
3.  XMLWriter.java - serialize an XML document
4.  Convenience methods for writing XML
5.  Write DOM out
6.  write Xml DOM Node
7.  XMLWriter helper class
8.  DOM writer
9.  Write Xml (Node n, OutputStream os)
10.  Makes writing XML much much easier
11.  Use DOM L3 DOMBuilder, DOMBuilderFilter DOMWriter and other DOM L3 functionality to preparse, revalidate and safe document.

목차

  1. 목차
  2. 인턴트 소개
    1. 인텐트를 이용해 액티비티 띄우기
      1. 명시적으로 새 액티비티 시작시키기
      2. 암시적 인텐트와 늦은 런타임 바인딩
      3. Linkify 소개
        1. 네이티브 링크타입
        2. 커스텀 링크 문자열 만들기
        3. 매치 필터 사용하기
        4. 변형 필터 사용하기
      4. 액티비티 결과

        1. 서브 액티비티 띄우기
        2. 결과 리턴하기
        3. 서브 액티비티 결과 다루기
        4. 네이티브 안드로이드 액션
    2. 인텐트 필터를 이용해 암시적 인텐트 서비스하기
      1. 안드로이드가 인텐트 필터를 해결하는 방법
      2. 인텐트 필터 매치에 반응하기
      3. 책임 떠넘기기
      4. 연락처 선택 예제
    3. 플러그인과 확장성을 위한 인텐트 필터 사용

      1. 애플리케이션에 익명 액션 제공하기
      2. 액티비티 메뉴에 익명 액션 통합하기
    4. 이벤트 방송을 위한 인텐트 사용
      1. 인텐트로 이벤트 방송하기
      2. 브로드캐스트 수신자로 방송에 귀 기울이기
        1. 애플리케이션 매니페스트에 브로드 캐스트 수신자 등록하기
        2. 코드로 브로드캐스트 수신자 등록하기
      3. 네이티브 안드로이드 브로드캐스트 액션
  3. 어댑터 소개
    1. 어댑터를 이용한 데이터 바인딩
      1. 할 일 목록 ArrayAdapter 커스터 마이징
      2. SimpleCursorAdapter 사용하기
  4. 인터넷 리소스 소개
    1. 인터넷 리소스에 접속하기
    2. 인터넷 리소스 이용 극대화
  5. 다이얼로그 소개
    1. Dialog 클래스 소개
      1. AlterDialog 클래스
      2. 전문 입력 다이얼로그
      3. 다이얼로그 사용과 관리
    2. 액티비티를 다이얼로그로 사용하기
  6. 지진 뷰어 만들기
  7. 요약
  8. 보충 - 안드로이드 관련 근황

 

Chapter 05. 인텐트, 브로드캐스트 수신자, 어댑터, 그리고 인터넷

 

작성자 : 김지헌

 

목차#

  • 인턴트 소개
  • 어댑터 소개
  • 인터넷 리소스 소개
  • 다이얼로그 소개
  • 지진 뷰어 만들기
  • 요약

100207_AndroidMindMap_Intent.png

인텐트 소개#

인텐트(intent)는 어떤 액션이 수행되어야 한다는 사용자의 의향을 선언할 수 있도록 해주는 메시지 전달 메커니즘으로 사용된다. 이때 액션은 데이터의 특정 부분을 가지고(혹은 데이터의 특정 부분에 대해) 수행되는 것이 보통이다.

인텐트를 사용해, 어떤 애플리케이션에 속한 것이든 관게 없이 안드로이드 장치에서 사용 가능한 모든 애플리케이션 컴포넌트 간의 상호작용을 지원할 수 있다. 이는 독립적인 컴포넌트들의 컬렉션을 서로 연결된 단일 시스템으로 바꿔준다.

인텐트의 가장 흔한 용도 가운데 하나는 새 액티비티를 명시적으로(읽어 들일 클래스를 지정함으로써)나 암시적으로(데이터의 어느 한 부분에 대해 수행되는 액션을 요청함으로써) 시작시키는 것이다.

인텐트는 또한 시스템 전역에 메시지를 방송하는 데에도 사용될 수 있다. 어떤 애플리케이션이든 브로드캐스트 수신자를 등록해 이들 브로드캐스트 인텐트에 귀 기울이고 반응할 수 있다. -> 이벤트 중심 애플리케이션을 만들 수 있다.

 

인텐트를 이용해 액티비티 띄우기#

애플리케이션에서 다른 애플리케이션 화면(액티비티)를 오픈하려면, 다음의 코드에서 보는 바와 같이 startActivity에 인텐트 하나를 전달해 호출한다.

  1. startActivity(myIntent);

이 Intent는 오픈할 클래스를 명시적으로 지정하거나, 수행되어야 하는 대상인 액션을 포함할 수 있다. 후자의 경우, 런타임은 "인텐트 확인"이라는 과정을 통해 오픈할 액티비티를 고를 것이다.

명시적으로 새 액티비티 시작시키기#

※ 여러 화면(액티비티)는 반드시 애플리케이션 매니페스트 안에 포함되어야 한다.

시작시킬 액티비티 클래스를 명시적으로 선택하려면, 현재 애플리케이션 컨텍스트와 시작시킬 액티비티 클래스를 지정하는 새로운 인텐트 하나를 만든다.

  1. Intent intent = new Intent(MyActivity.this, MyOtherActivity.class);

  2. startActivity(intent);

startActivit를 호출하고 나면 새로운 액티비티가 생성되어화면에 보여지고, 활성화 상태가 되며, 액티비티 스택 맨 위로 이동된다.

이 새로운 액티비티에 finish를 호출하면 액티비티가 종료되어 스택에서 제외될 것이다. 아니면 그렇게 하지 않고 장치의 뒤로 가기 버튼을 사용해 이전 액티비티로 이동할 수 있다.

암시적 인텐트와 늦은 런타임 바인딩#

암시적 인텐트(Implicit Intent)는 익명의 애플리케이션 컴포넌트가 액션 요청을 서비스를 할 수 있도록 하는 매커니즘이다.

Intents.png

암시적...이란 무엇일까? ㅡ_-)? ->> 개발자가 사용하는 기능을 가지고 있는 애플리케이션이 어떤 것인지 정확히 알지 못해도 된다는 것.

액티비티를 시작시키기 위해 이 새로운 암시적 인텐트를 사용하면, 안드로이드는 지정된 데이터 타입에 대한 액션 수행에 가장 적합한 클래스로 그 인텐트를 해결한다.

    if( somethingWeird && itDontLookGood ) {
        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-2368"));
        startActivity(intent);
    }

Linkify 소개#

Linkify는 RegEx 패턴 매칭(Pattern matching)을 통해 TextView 클래스( 그리고 TextView에서 파생된 클래스) 내에 하이퍼링크(HyperLink)를 자동으로 생성하는 도우미 클래스이다.

지정된 RegEx 패턴과 일치하는 텍스트는, 그 일치한 텍스트를 대상 URI로 사용해 암시적으로 startActivity(new Intent(Intent.ACTION_VIEW, uri))를 호출하는 클릭 가능한 하이퍼링크로 변환될 것이다.

네이티브 링크타입#

static 메서드 Linkify.addLinks는 linkify하기 위한 뷰 하나와, Linkify 클래스가 지원하고 제공하는 하나 이상의 기본 켄텐트 타입(Web_URLS, EMAIL_ADDRESS, PHONE_NUMBERS, ALL) 비트 마스크를 받아들인다.

http://developer.android.com/reference/android/text/util/Linkify.html#addLinks(android.widget.TextView, int)

  1. TextView textView = (TextView)findViewById(R.id.myTextView);
    Linkify.addLinks(textView, Linkify.WEBRULS|Linkify.EMAIL_ADDRESS);


  1. <TextView 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="@string/linkify_me"
        android:autoLink="phone|email"
    />

android:autoLink 속성을 사용하면 레이아웃 리소스 안에서 뷰를 링키파이할 수 있다.

 

커스텀 링크 문자열 만들기#

링키파이 문자열을 정의하려면, 하이퍼링크로 표시하고자 하는 텍스트와 일치하도록 새로운 RegEX 패턴을 만든다.

  1. int flags = Pattern.CASE_INSENSITIVE;
    Pattern p = Pattern.compiler("\\bquake[0-9]*\\b", flags);
    Linkify.addLinks(myTextView, p, "content://com.android.earthquake/earthquakes/");

또한 링키파이는 TransforFilter와 MachFilter 인터페이스를 지원한다.

이들은 대상 URI 구조와 매칭 문자열 정의에 대해서 추가적인 제어를 제공하며, 다음의 골격 코드에 보이는 것처럼 사용된다.

  1. Linkify.addLinks(myTextView, pattern, prefixWith, new MyMatchFilter(), new MyTransformFilter());

 

매치 필터 사용하기#

RegEx 패턴 매치에 추가 조건을 더하려면 MatchFilter에 acceptMatch메서드를 구현한다.

  1. public class MyMatchFilter implements MatchFilter {
        @Override
        public boolean acceptMatch(CharSequence s, int start, int end) {
            return (start == 0 || s.charAt(start - 1) != '!';
        }
    }

잠재적인 일치가 발견되면 acceptMatch가 호출되며, 매개변수로 일치 시작 지점과 끝 인덱스가 (탐색중인 전체 텍스트와 함께) 전달된다.

 

변형 필터 사용하기#

변형필터(Transform Fitler)는 여러분으로 하여금 링크 텍스트에 의해 생성된 암시적 URI를 수정하도록 함으로써 텍스트 문자열을 서시고하하는 데 더 많은 자유를 준다. 대장 URI로부터 링크 텍스트를 분리하는 것은 데이터 문자열을 사용자에게 표시하는 방식에 더 많은 자유를 준다. 대상 URI로부터 링크 텍스트를 분리하는 것은 데이터 문자열을 사용자에게 표시하는 방식에 더 많은 자유를 준다.

+ URI : http://ko.wikipedia.org/wiki/URI

+ URL : http://ko.wikipedia.org/wiki/URL

  1. ublic class MyTransformFilter implements TransformFilter {
        @Override
        public String transformUrl(Matcher match, String url) {
            // TODO Auto-generated method stub
            return url.toLowerCase();
        }
    }

 

액티비티 결과#

startActivity로 시작된 액티비티는 자신의 부모와 독립적이며, 종료할 때 어떠한 피드백도 제공하지 않을 것이다. 이에 대한 대안으로, 여러분은 액티비티를 본래부터 그의 부모에 연결된 서브 액티비티로 시작시킬 수 있다. 서브 액티비티는 종료할 떄 자신의 부모 액티비티 안에 있는 이벤트 핸들러를 호출한다. 서브 액티비티는 한 액티비티가 다른 액티비티를 위한 데이터 입력(사용자가 리스트에서 항목 하나를 선택하는 거소가 같은)을 제공하고 있는 상황에 제격이다.

서브 액티비티는 보통 액티비티와 같은 방식으로 만들어지며 또한 애플리케이션 매니페스트에 반드시 등록되어야 한다. 매니페스트에 등록된 모든 액티비티는 서브 액티비티를 오픈될 수 있다.

--> 주소록 보기(ContactPicker)

 

서브 액티비티 띄우기#

startActivityForResult 메서드는 startActivity와 상당 부분 비슷하게 동작하지만 한 가지 중요한 차이점이 있다.

  1. public static final int PICK_CONTACT = 1;
  2. Intent intent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts/"));
    startActivityForResult(intent, PICK_CONTACT);

실행할 액티비티를 결정하는 데 사용되는 인텐트 뿐만 아니라 요청코드도 전달한다. 이 값은 추후 결과를 리턴한 서브 액티비티를 유일하게 식별하는 데 사용된다.

 

결과 리턴하기#

finish를 호출하기 전에 setResult를 호출하여 호출 액티비티에 결과를 리턴한다.

setResult 메서드는 결과 코드와 인텐트로 표현된 결과 페이로드 이렇게 두 개의 매개변수를 취한다. 결과 코드는 실행중인 서브 액티비티의 "결과"다(대개 Activity.RESULT_OK나 Activity.RESULT_CANCELED 중 하나다).

결과로 리턴되는 인텐트는 콘텐트(연락처나 전호번호 혹은 미디어 파일 같은) 하나에 대한 URI와 부가 정보를 리턴하는 데 사용되는 엑스트라 컬렉션을 포함할 수 있다.

  1. public void onItemClick(AdapterView<?>parent, View view, int pos, long id) {
       //커서를 선택된 항목으로 이동한다.
       cursor.moveToPosition(pos);
       //행 id를 얻어온다.
       int rowId = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
       //결과 URI를 생성한다.
       Uri outURI = Uri.parse(data.toString() + rowId );
       Intent outData = new Intent();
       outData.setData(outURI);
       setResult(Activity.RESULT_OK, outData);
       finish();
  2. }

서브 액티비티 결과 다루기#

서브 액티비티가 종료된 그의 부모 액티비티에 있는 onActivityResult 이벤트 핸들러가 호출된다. 서브 액티비티로부터의 결과를 다루려면 이 메서드를 재정의한다. onActivityResult 핸들러는 다음과 같은 매개변수를 전달받는다.

  • 요청코드 : 현재 리턴되고 있는 서브 액티비티를 띄우는 데 사용된 요청 코드

  • 결과코드 : 서브 액티비티가 자신의 결과를 나타내기 위해 설정한 결과 코드 이는 어떠한 정수 값이라도 될 수 있지만, 대개는 Activity.RESULT_OK나 Activity.RESULT_CANCELED가 될 것이다.

  • 데이터 : 리턴된 모든 데이터를 담는 데 사용되는 인텐트. 이는 서브 액티비티에 목적에 따라, 전형적으로 리스트에서 선택된 데이터의 특정 부분을 나타내는 URI를 가진다.

네이티브 안드로이드 액션#

http://developer.android.com/reference/android/content/Intent.html

 

인텐트 필터를 이용해 암시적 인텐트 서비스하기#

http://developer.android.com/reference/android/content/Intent.html

안드로이드가 인텐트 필터를 해결하는 방법#

※ 인텐트 확인(Intent Resolution) : 어떤 액티비티를 시작시킬지 결정하는 과정

네이티브 안드로이드 애플리케이션 컴포넌트는 서드파티 애플리케이션과 정확히 같은 방식으로 인텐트 확인 과정에 참여한다. 이들(네이티브)이라고 보다 높은 우선 순위를 가지지는 않으며, 동일한 액션 요청을 서비스하는 인텐트 필터를 선언하는 새로운 액티비티로 완전히 대체될 수 있다.

인텐트 필터 매치에 반응하기#

애플리케이션 컴포넌트가 암시적 인텐트를 통해 시작되는 경우에는, 그가 수행할 액션과 이를 수행하기 위한 데이터를 얻을 필요가 있다. 컴포넌트 실행에 사용된 인텐트는 다음에 보이는 것처럼(대개 onCreate 메서드 내에서) getIntent 메서드를 호출해 얻는다.

인텐트의 데이터와 액션은 getData와 getAction 메서드로 얻는다.

  1. String action = intent.getAction();

  2. Uri data = intent.getData();

 

책임 떠넘기기#

startNextMatchingActivity 메서드를 이용해, 액션 처리의 책임을 다음 베스트 매칭 애플리케이션 컴포넌트로 넘길 수 있다.

  1. Intent intent = getIntent();
    if ( isAfterMidnight ) {
       startNextMatchingActivity(intnet);
    }

인텐트 필터에 기반을 두고 있는 인텐트 확인 과정의 능력을 뛰어 넘어, 사용 제한이라는 추가 조건을 컴포넌트에 추가할 수 있도록 해준다.

연락처 선택 예제#

이번 예제에서는 연락처 데이터에 대해 PICK_ACTION을 서비스하는 새로운 서브 액티비티를 만든다. 이는 연락처 데이터베이스에 있는 각각의 연락처를 나타내고, 사용자로 하여금 하나를 선택하도록 한 뒤, 그의 URI를 호출 액티비티에 리턴하고 종료한다.

  1. ContactPicker 액티비티를 포함하는 새로운 ContactPicker 프로젝트를 만든다. -> 한마디로 프로젝트를 만들라~~ 
  2. ListView 컨트롤 하나를 포함하도록 main.xml 레이아웃 리소스를 수정한다. 이 컨트롤은 연락처를 표시하는 데 사용될 것이다. 
  3. 텍스트 뷰 하나를 포함하는 새로운 listitemlayout.xml 레이아웃 리소스를 만든다. 이는 각 연락처를 리스트 뷰에 표시하는 데 사용될 것이다. 
  4. ContactPicker 액티비티로 돌아와, onCreate 메서드를 재정의하고 호출 인텐트로부터 데이터 경로를 얻어온다.

    1. 연락처 목록에 저장된 사람들에 대한 리스트 뷰 바인딩
    2. ItemClickListener를 리스트 뷰에 추가한다.
    3. onCreate 메서드를 닫는다.
  5. 애플리케이션 매니페스트를 수정해서, 액티비티의 intent-filter 태그를 연락처 데이터에 대한 선택 액션 지원을 추가하도록 바꾼다.
  6. 이것으로 서브 액티비티를 마무리한다. 이제 이를 테스트하기 위한 새로운 테스트 도구인 ContentPickerTester 액티비트를 만든다.
  7. ContentPickerTester의 onCreate 메서드를 재정의하고 버튼에 클릭 리스너를 추가하여, 버튼이 PICK_ACTION과 연락처 데이터베이스를 지정해 새로운 서브 액티비티를 암시적으로 시작시키도록 만든다.
  8. 서브 액티비티가 리턴하면, 그 결과를 이용해 선택된 지인의 이름으로 텍스트 뷰를 채운다.
  9. 이렇게 완성된 테스트 도구를 애플리케이션 매니페스트에 간단히 추가한다.

플러그인과 확장성을 위한 인텐트 필터 사용#

Menu 클래스로부터 사용 가능한 addIntentOptions 메서드는 이 메뉴에 의해 실행되는 데이터를 기술하는 인텐트를 지정할 수 있게끔 해준다. 안드로이드는 이 인텐트를 확인해 지정된 데이터와 일치하는 인텐트 필터에 지정된 모든 액션을 리턴한다. 새 메뉴 항목은 각각 일치하는 인텐트 필터의 레이블로 채워진 텍스트를 가지고 만들어진다.

애플리케이션에 익명 액션 제공하기#

액션을 다른 액티비티를 위해 사용할 수 있도록 만들려면, intent-filter 태그를 이용해 액션을 해당 액티비티의 매니페스트 노드 내에 알린다. 인텐트 필터는 자신이 수행하는 action과 자신이 수행될 수 있는 data를 기술한다. 후자는 이 액션이 사용가능해져야 하는 때를 결정하기 위한 인텐트 확인 과정 중에 사용될 것이다. category 태그는 반드시 ALTERNATIVE나 SELECTED_ALTERNATIVE 혹은 이둘 모두가 되어야만 한다. 메뉴 항목이 사용하는 텍스트는 android:label 속성에 의해 지어된다.

액티비티 메뉴에 익명 액션 통합하기#

메뉴에 메뉴 옵션을 런타임에 추가하기 위해서는 문제의 그 메뉴 객체에 대해 addIntentOption 메서드를 이용하여, 제공하고자 하는 액션을 위한 데이터를 지정하고 있는 인텐트 하나를 전달한다. 이는 일반적으로 액티비티의 onCreateOptionsMenu나 onCreateContextMenu 핸들러에서 처리될 것이다.

이 인텐트와 더불어 옵션 플래그, 호출 클래스 이름, 사용할 메뉴 그룹, 메뉴 ID 값을 채우고자 하는 메뉴에 대한 addIntentOptions  에 전달한다. 또한 추가 메뉴 항목을 만들기 위해 사용하고자 하는 인텐트의 배열을 지정할 수도 있다.

이벤트 방송을 위한 인텐트 사용#

인텐트는 시스템 수준의 메시지 전달 메커니즘으로서, 프로세스 경계를 가로질러 구조화된 메시지를 전송할 수 있다. 지금까지는 새로운 애플리케이션 컴포넌트를 시작시키기 위한 인텐트 사용에 관해 살펴봤지만, 인텐트는 sendBroadcast 메서드를 이용해 메시지를 컴포넌트 간에 익명으로 방송하는 데에도 사용될 수 있다. 애플리케이션 내에 브로드캐스트 수신자를 구현하여 이들 브로드캐스트 인텐트에 귀 기울이고 반응할 수 있다.

브로드캐스트 인텐트는 시스템이나 애플리케이션 이벤트의 리스너를 통지하는 데 사용되며, 애플리케이션 간의 이벤트 중심 프로그래밍 모델을 확장한다.

인텐트 방송은 애플리케이션을 보다 열려 있도록 만드는 데 도움을 준다. 인텐트를 이용해 이벤트를 방송함으로써, 애플리케이션을 수정하지 않고도 이벤트에 반응할 수 있도록 해준다. 애플리케이션 안에서 네이티브(혹은 서드파티) 애플리케이션을 대체 또는 향상시키거나 시스템 변경사항 및 애플리케이션 이벤트에 반응하기 위해 브로드캐스트 인텐트에 귀 기울일 수 있다(어떻게??). 예컨대, 걸려오는 전화 브로드캐스트에 귀 기울임으로써, 전화건 사람에 따라 벨소리나 벨소리 크기를 바꿀 수 있다.

인텐트로 이벤트 방송하기#

인텐트 방송은 간단하다. 애플리케이션 컴포넌트 안에서 방송하고 싶은 인텐트를 생성하고, sendBroadcast 메서드를 이용해 보내기만 하면 된다.

http://developer.android.com/reference/android/content/Context.html#sendBroadcast(android.content.Intent)

  1. public static final String NEW_LIFEFORM_DETECTED = "com.android.action.NEW_LIFEFORM";

인텐트 내에 데이터를 포함하기 원한다면, 인텐트의 data 프로퍼티를 이용해 URI를 지정할 수 있다. 또한 부가적인 기본 값들을 추가하기 위해엑스트라를 포함할 수도 있다. 이벤트 중심 패러다임의 입장에서 고려해볼 때, 이 엑스트라 번들은 이벤트 핸들러 내에 있는 선택적인 매개변수와 같다.

  1. Intent intent = new Intent(NEW_LIFEFORM_DETECTED);
    intent.putExtra("lifeformName", lifeformType);
    intent.putExtra("logitude", currentlongitude);
    intent.putExtra("latidude", currentLatitude);
           
    sendBroadcast(intent);

브로드캐스트 수신자로 방송에 귀 기울이기#

브로드캐스트 수신자는 브로드캐스트 인텐트에 귀 기울이는 데 사용된다. 브로드캐스트 수신자를 활성화하려면, 코드나 애플리케이션 매니페스트를 통해 이를 등록할 필요가 있다. 브로드캐스트 수신자를 등록할 때는, 인텐트 필터를 이용해 그가 귀 기울이고 있는 인텐트가 무엇인지를 반드시 지정해야 한다.

새로운 브로드캐스트 수신자를 생성하려면, 다음의 코드에서 보이는 것처럼 BroadcastReceiver 클래스를 확장하고 onReceive 이벤트 핸들러를 재정의한다.

  1. import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;

    public class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
        }
    }

onReceive 메서드는 수신자를 등록하는 데 사용된 인텐트 필터와 일치하는 브로드 캐스트 인텐트가 수신될 때 실행될 것이다. onReceive 핸들러는 반드시 5초 이내에 마쳐야 하며, 그렇지 않으면 Application Unreponsive 다이얼로그가 표시될 것이다.

실행할 수신자를 위한 인텐트가 방송될 때, 등록된 브로드캐스트 수신자를 가진 애플리케이션이 꼭 실행되고 있어야 하는 것은 아니다.

브로드캐스트 수신자는 대체로 콘텐트를 업데이트하거나, 서비스를 띄우거나, 액티비티 UI를 업데이트하거나, 알림 관리자를 이용해 사용자에게 통지할 것이다.

애플리케이션 매니페스트에 브로드 캐스트 수신자 등록하기#

  1. <receiver android:name=".LifeformDetectedBroadcastReceiver">
        <intent-filter>
            <action android:name="com.android.action.NEW_LIFEFORM" />
        </intent-filter>
    </receiver>

코드로 브로드캐스트 수신자 등록하기#

코드로 브로드캐스트 수신자 등록을 제어할 수 있다. 이는 전형적으로 수신자가 액티비티에 있는 UI요소를 업데이트하는 데 사용되고 있을 때 이루어진다. 이 경우, 액티비티가 화면에 보이지 않을 때(혹은 활성 상태가 아닐 때) 브로드캐스트 수신자를 등록해제하는 것이 좋은 습관이다.

  1. //브로드캐스트 수신자를 생성해 등록한다.
    IntentFilter filter = new IntentFilter(NEW_LIFEFORM_DETECTED);
    LifeformDetectedBroadcastReceiver r = new LifeformDetectedBroadcastReceiver();
    registerReceiver(r, filter);

 브로드캐스트 수신자를 등록해제하려면 애플리케이션 켄텍스트 상에서 unregisterReceiver 메서드를 이용해 브로드캐스트 수신자 인스턴스를 전달한다.

  1. unregisterReceiver(r);

네이티브 안드로이드 브로드캐스트 액션#

http://developer.android.com/reference/android/content/Intent.html#ACTION_BOOT_COMPLETED

--> Reference에서  위에서처럼 #이후에 클래스에서 사용하는 상수로 액션등을 사용할 수 있다.

 

 

어댑터 소개#

  • 참고사항 :

    • Adapter : http://developer.android.com/reference/android/widget/Adapter.html

      : 어댑터는 데이터를 사용자 인터페이스 뷰와 바인드하는 브리징 클래스다(!?). 어댑터는 각 항목을 표현하는 데 사용되는 자식 뷰 생성과 하부에 놓인 데이터에 대한 접근 제공을 담당한다. 어댑터 바인딩을 지원하는 사용자 인터페이스 컨트롤은 반드시 AdapterView 추상 클래스를 확장해야 한다.

    • ArrayAdapter : http://developer.android.com/reference/android/widget/ArrayAdapter.html

      어댑터 뷰를 객체 배열에 바인드하는 일반적 클래스다. 기본적으로 ArrayAdapter는 각 객체의 toString  값을 레이아웃 내에 정의된 TextView 컨트롤에 바인드한다. => 상속받아 사용하는 자식 클래스들에서 필요에 따라서 getView 메서드를 오버라이딩하여 사용할 수 있다.

    • SimpleCursorAdapter : http://developer.android.com/reference/android/widget/SimpleCursorAdapter.html

      뷰를 콘텐트 공급자 질의을 통해 리턴된 커서에 바인드한다. XML 레이아웃 정의를 지정한 뒤, 결과 셋의 각 열에 있는 값을 이 레이아웃에 있는 뷰에 바인드한다.

어댑터를 이용한 데이터 바인딩#

할 일 목록 ArrayAdapter 커스터 마이징#

  1. 할일 목록 프로젝트 열기, ToDoItem 클래스 생성

  2. ToDoList 클래스 열어 ArrayList와 ArrayAdapter 변수 타입을 문자열 대신 ToDoItem 객체를 저장하도록 수정한다. onCreate  메서드 수정이 필요함
  3. 액티비티를 실행
  4. 각각의 해야 할 일을 표시하기 위한 커스텀 레이아웃 만들기
  5. ToDoItem에 특화된 ArrayAdapter의 변형을 확장하는 새로운 클래스 ToDoItemAdapter를 만든다.
  6. 끝으로 ArrayAdapter 선언을 ToDoItemAdapter로 바꾼다.

SimpleCursorAdapter 사용하기#

SimpleCursorAdapter는 Cursor로부터 열을 커스텀 레이아웃 정의를 사용하는 리스트뷰(ListView)에 바인드할 수 있도록 해준다.

SimpleCursorAdapter는 현재 컨텍스트, 레이아웃 리소스, 커서, 그리고 사용할 열들의 이름을 담은 배열과 그에 대응하는 열의 데이터 값을 표시하기 위해 사용할 뷰의 리소스 ID를 담은(앞의 배열과 동일한 크기의) 배열을 전달함으로써 생성된다.

  1. SimpleCursorAdapter myAdapter;
    myAdapter = new SimpleCursorAdapter(this, R.layout.simplecursorlayout,
  2. myCursor, fromColumns, toLayoutIDs);

 

인터넷 리소스 소개#

띡 클라이언트(Thick-client) :

씬 클라이언트(Thin-client) : http://www.terms.co.kr/thinclient.htm

  • 대역폭 : 네이티브 애플리케이션을 만들어 데이터 업데이트 시에만 대역폭을 사용하도록 제한할 수 있다.
  • 캐싱 : 네이티브 애플리케이션은 살아있는 연결 없이도 가능한 많은 기능을 제공하기 위해 데이터를 캐시할 수 있다.
  • 네이티브 기능 : 온라인 데이터를 장치가 가진 하드웨어 기능과 결합할 수 있다.

인터넷 리소스에 접속하기#

http://developer.android.com/guide/topics/manifest/uses-permission-element.html

http://developer.android.com/reference/android/Manifest.permission.html

 

인터넷 리소스 이용 극대화#

  • WebView 위젯을 이용해 액티비티 안에 WebKit 기반 브라우저 컨트롤 포함 < - > API를 이용해 서버 프로세스와 직접 상호작용할 수 있다.
  • 데이터 추출을 위해 SAX나 javax 와 같은 자바 기반 XML 파서를 이용하여 원격 XML피드를 처리
  • 전송되는 데이터량을 제한해 사용자 경험을 최적화하고, 애플리케이션이 네트워크 정전과 대역폭 제한을 충분히 잘 다룰 수 있도록 견고하게 만들자.

    -> 어떻게!?

다이얼로그 소개#

  • 다이얼로그 계열 클래스 사용

    : 안드로이드는 범용 AlterDialog 클래스와 더불어, Dialog를 확장하는 전문적인 여러 클래스를 포함하고 있다.

  • 다이얼로그 테마가 적용된 액티비티

    : 평범한 액티비티에 다이얼로그 테마를 적용하여 다이얼로그 박스의 겉모습을 입힐 수 있다.

  • 토스트

    : 토스트는 일시적인 비 모달(non-modal) 메시지 박스로서, 사용자에게 이벤트를 통지하기 위하여 브로드캐스트 수신자와 백그라운드 서비스에 의해 종종 사용된다.

Dialog 클래스 소개#

http://developer.android.com/reference/android/app/Dialog.html

AlterDialog 클래스#

http://developer.android.com/reference/android/app/AlertDialog.html

AlertDialog 클래스는 가장 쓰임새가 많은 Dialog 구현 중 하나다. 이 클래스는 다이얼로그 박스가 사용되는 가장 흔한 경우 몇 가지를 위해 화면을 만들수 있도록 다양한 옵션을 제공한다.

  • 사용자에게 여러 개의 버튼 중 하나만 선택할 수 있는 형태로 최대 세 가지 옵션을 제공하는 메시지를 나타냄

  • 체크 버튼이나 라디오 버튼 형태로 옵션 목록을 제공

  • 사용자 입력을 위한 텍스트 입력 박스 제공

 

전문 입력 다이얼로그#

다이얼로그 사용과 관리#

필요할 때마다 매번 다이얼로그의 새로운 인스턴스를 생성하기보다는, 다이얼로그 박스 인스턴스를 지속하고 관리하기 위해 안드로이드는 액티비티 클래스에 onCreateDialog와 onPrepareDialog 이벤트 핸들러를 제공하고 있다.

액티비티를 다이얼로그로 사용하기#

  1. <activity android:name="MyDialogActivity"
             android:theme="@android:style/Theme.Dialog">
    </activity>

 

지진 뷰어 만들기#

==> 요건 패스, 해당하는 API 를 이용하여 정보를 얻을 수가 없다.

http://earthquake.usgs.gov

http://earthquake.usgs.gov/eqcenter/catalogs/1day-M2.5.xml

 

요약#

이번 장의 핵심은 애플리케이션 컴포넌트들을 바인딩하는 것이었다.

  • 인텐트(intent)는 애플리케이션과 다른 애플리케이션들 사이에 액션을 수행하고 이벤트를 신호하기 위한 의향을 전달할 수 있도록 해주는 다재다능한 메시징 시스템을 제공한다. 명시적 인텐트와 암시적 인텐트를 이용해 각각 액티비티를 실행시키는 방법을 배움.
  • 브로드캐스트 인텐트에 대해 살펴보면서, 이들을 이용해 장치 전체에 메시지를 전송하는 방법, 특히 시스템별 이벤트와 애플리케이션별 이벤트에 기반을 둔 이벤트 중심 모델을 지원하는 방법에 대해 알아봤다.
  • 또한 액티비티 간의 데이터 전달을 위해 서브 액티비티를 사용하는 방법과, 정보를 출력하고 사용자 입력을 손쉽게 하기 위한 다이얼로그 사용법을 배웠다.
  • 어댑터에 대해 살펴봤으며, 이를 하부 데이터를 비주얼 컴포넌트에 바인딩해봤다.
  • 특히, 리스트 뷰를 배열 리스트와 커서에 바인딩하기 위해 배열어댑터(ArrayAdapter)와 심플커서어댑터(SimpleCursorAdapter)를 사용하는 방법 살펴봤다.
  • 마지막, 인터넷 연결 이면에 있는 기본사항에 대해 배웠지만, 안되었다!!


다음장에서는 정보를 애플리케이션 내에 지속시키는 방법을 배울 것이다. 안드로이드는 애플리케이션 데이터를 저장하기 위해 파일, 간단한 환경설정, 그리고 모든 특성을 갖춘 관계형 데이터베이스(SQLite) 등을 포함한 여러 메커니즘을 제공한다.

 

보충 - 안드로이드 관련 근황#

 

이 글은 스프링노트에서 작성되었습니다.

Posted by 허니몬

트랙백 주소 :: http://java.ihoney.pe.kr/trackback/107  관련글 쓰기

 

 

 

 

작성자 : 김지헌

작성일 : 2009. 11. 29.

 

Windows 시스템에서 Android SDK 설치하는 과정을 설명하도록 하겠다.

  1. http://developer.android.com/sdk/index.html

    android_sdk_r3-windows.zip 다운로드하기

    android_sdk_01.png

     

  2. android_sdk_r3-windwos.zip 을 특정 위치에서 압축을 푼다.
    android_sdk_02.png

    폴더 내에 보이는 SDK Setup는 안드로이드 SDK와 AVM(안드로이드 가상머신)의 설정을 할 수 있도록 해주는 프로그램이다.

    예 : C:\Program Files 폴더 안에

    C:\Program Files\android-sdk-windows
    폴더로 압축을 해제했다.

     

  3. C:\Program Files\android-sdk-windows 경로를 컴퓨터에서 Path로 설정해주어야 한다.

    C:\Program Files\android-sdk-windows\tools 를 컴퓨터의 Path 마지막에 추가해주어야 한다.

    android_sdk_03.png

    내 컴퓨터 -> 속성 -> 고급 -> 환경변수 -> Path 항목에 C:\Program Files\android-sdk-windows\tools 을 추가한다.

     

  4. http://honeydeveloper.springnote.com/pages/4429375 를 참조하여 Eclipse 에 ADT plug-in을 설치한다.

    android_sdk_04.png

     

  5. Develop tools에 체크를 하고 플러그인을 다운로드 받은 후 이클립스를 다시 시작한다.
  6. 이클립스가 실행되면 다음과 같은 창이 뜰 것이다. 환경설정에서 안드로이드와 관련된 설정을 하라는 내용이다.

    android_sdk_05.png

  7. Preferences -> Android 에서 SDK의 경로를 설정해줘야 한다. 아까 설치했던 경로를 결정한 후 Apply > OK를 누른다.

    android_sdk_06.png

  8. 그후 Windwo > Android SDK and AVM Manger를 선택한다. Available Packages 를 선택한다.

    android_sdk_07.png

    아래에 있는 항목들을 모두 체크하거나, 필요에 따라서 개별적으로 선택을 한다.

  9. Accept All 을 선택하고 Install Accepted를 클릭하여 다운로드를 진행하도록 한다.

    android_sdk_08.png

    android_sdk_09.png

    다운로드가 진행되고 있다.

  10. 다운로드가 완료되면 , Virtual Devices 에서 [New...] 버튼을 클릭한 후에 사용하려고 하는 AVD를 설정해서 생성을 해주면 되겠다.

    android_sdk_11.png

  11. 그런 후에 이클립스에서 Android Project를 생성해주면 안드로이드 애플리케이션을 개발하기 위한 환경을 구축하는 과정은 끝이난다.

이 글은 스프링노트에서 작성되었습니다.

Posted by 허니몬

트랙백 주소 :: http://java.ihoney.pe.kr/trackback/100  관련글 쓰기

 

 

 

 

각 안드로이드 프로젝트는 AndroidManifest.xml 이라는 매니페스트 파일을 가지는데, 이는 해당하는 프로젝트 계측구조의 루트에 저장된다.

매니페스트는 애플리케이션을 구성하는 각각의 컴포넌트(액티비티, 서비스, 콘텐츠 공급자, 브로드캐스트 수신자)에 대한 노드를 포함하고 있으며, 인텐트 필터(Intent Filter)와 권한(Permission)을 사용해 이들이 다른 컴포넌트 및 애플리케이션과 어떻게 상호작용하는지를 결정한다. 또한, 애플리케이션 메타데이터(아이콘이나 테마 같은)를 지정하기 위한 속성을 비롯해, 보안 설정과 단위 테스트에 사용될 수 있는 추가적인 최상위 노드 역시 제공

매니태그는 프로젝트의 패키지 설정을 위한 package 속성을 갖는 manifest 루트 태그로 구성된다.

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.helloworld"
          android:versionCode="1"
          android:versionName="1.0">
      [...매니페스트...]
    </manifest>

 

manifest 태그는 애플리케이션을 구성하는 애플리케이션 컴포넌트, 보안 설정, 테스트 클래스를 정의하는 노드를 가진다.

  • application   매니페스트는 오직 하나의 애플리케이션 노드만을 가질 수 있다. 애플리케이션 노드는 속성을 사용해 개발자의 애플리케이션을 위한 메타데이터(타이틀, 아이콘, 테마 등)을 지정한다. 또한 애플리케이션 노드는 애플리케이션 컴포넌트를 지정하는 데 사용되는 액티비티, 서비스, 콘텐츠 공급자, 브로드 캐스트 수신자 태그를 담는 컨테이너 역할을 한다.

    1. <application android:icon="@drawable/icon" android:label="@string/app_name">
      [...애플리케이션 노드...]
      </application>

 

  • activity   activity 태그는 애플리케이션에 의해 보여지는 모든 액티비티에 필요하며, android:name 속성으로 클래스 이름을 지정한다. 매니페스트에 정의되지 않은 액티비티를 시작하려 하면 런타임 예외가 던져질 것이다. 각 액티비티 노드는 그 액티비티를 띄울 인텐트를 지정하는 intent-filter 자식 태그를 지원한다.
  1. <activity android:name=".Helloworld"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
    </activity>

 

  • service   activity와 마찬가지로, 애플리케이션에서 사용되는 각 서비스 클래스에 대해 새로운 service 태그를 하나씩 생성한다. 서비스 태그 역시 늦은 런타임 바인딩(Late runtime binding)을 지원하기 위한 intent-filter 자식 태그를 지원한다.
  1. <service android:enabled="true" android:name=".MyService"></service>

 

  • provider   provider 태그는 애플리케이션이 가진 각각의 콘텐츠 공급자에 대해 사용된다. 콘텐츠 공급자는 애플리케이션 간의 데이터베이스 접근과 공유를 관리하기 위해 사용된다.
  1. <provider android:permission="com.android.MY_PERMISSION"
            android:name=".MyContentProvider"
            android:enabled="true"
            android:authorities="com.android.myapp.MyContentProvider">
    </provider>

 

  • receiver   receiver 태그를 추가하면 애플리케이션을 띄우지 않고서도 브로드캐스트 수신자를 등록할 수 있다. 브로드캐스트 수신자는 한번 등록되고 나면 애플리케이션에 의해 일치하는 인텐트가 방송될 때마다 실행되는 전역 이벤트 리스너(event listener)와 같다. 브로드캐스트 수신자를 매니페스트에 등록함으로써, 이 처리를 완전히 자립적으로 만들 수 있다.
  1. <receiver android:enabled="true"
            android:label="My Broadcast Receiver"
            android:name=".MyBroadcastReceiver">
    </receiver>

 

  • user-permission   보안 모델의 일부로서, uses-permission 태그는 애플리케이션이 올바르게 동작하는 데 필요하다고 생각되는 권한을 선언한다. 애플리케이션 설치 과정 중 사용자에게 제시되어 권한 부여 혹은 거절이 결정될 것이다. 권한은 많은 네이티브 안드로이드 서비스에 요구되며, 특히 비용이 수반되거나 보안에 관계된 것들(전화 걸기나 SMS 수신 또는 위치기반 서비스(LBS) 사용 같은)에는 필수적이다.
  1. <uses-permission android:name="android.permission.ACCESS_LOCATION">
    </uses-permission>

 

  • permission   어떤 애플리케이션 컴포넌트에 대한 접근을 제한할 수 있으면, 권한을 매니페스트에 정의할 필요가 있다. 이러한 권한 정의를 생성하는 데에는 permission 태그가 사용된다. 이렇게 권한을 정의하고 나면, 애플리케이션 컴포넌트는 adnroid:permission 속성을 추가해 그 권한을 요청할 수 있다. 다른 애플리케이션이 보호된 이들 컴포넌트를 사용할 수 있으려면, 먼저 자신의 매니페스트에 uses-permission 태그를 포함해야 할 것이다(그리고 그 권한을 부여 받아야 할 것이다).
    permission 태그 안에 허용할 권한의 접근수준(normal, dangerous, signature, signatureOrSystem)과 레이블 그리고 그 권한이 주어짐으로써 오는 위험성에 대한 설명을 담은 외부 리소스를 지정할 수 있다.
  1. <permission android:name="com.android.DETONATE_DEVICE"
            android:protectionLevel="dangerous"
            android:label="Self Destruct"
            android:description="@string/detonate_desciption">
    </permission>

 

  • instrumentation   계측(instrumentation) 클래스는 실행 시에 액티비티와 서비스를 테스트하기 위한 프레임워크를 제공한다. 이들 클래스는 애플리케이션과 그가 갖는 시스템 리소스와의 상호작용을 모니터하기 위한 고리를 제공한다. 애플리케이션을 위해 생성한 테스트 클래스 각각에 대해 새로운 노드 하나씩을 생성한다.
  1. <instrumentation android:label="My Test"
            android:name="MyTestClass"
            android:targetPackage="com.android.aPackage">
    </instrumentation>

 

manifest.png ADT(Android Develope Tools)의 새 프로젝트 마법사는 새로운 프로젝트를 만들 때 자동으로 새 매니페스트 파일을 생성한다. 애플리케이션의 각 컴포넌트를 배워나감에 따라 다시 이 매니페스트로 되돌아 올 것이다.

ArrayAdapter의 인자값은 객체 배열이 아닌 Collection 으로 던져주자!

안드로이드에서 Adapter중 가장많이 쓰는 ArrayAdapter는 객체배열이 아닌 Collection이 구현이 된 객체를 인자값으로 던져주는게 훨씬 컨트롤 하기 편합니다. 이게 무슨말이냐 하면 일단 이유를 들기위해 오늘 제가 코딩을 해야할 부분에 대해서 코딩으로 설명을 해보겠습니다. 

상황 : ArrayAdapter에 문자열이 저장이된 String[]배열을 인자값으로 던져주고 동적으로 그 배열값을 수정 및 추가를 해야되는 작업입니다. 

위의 상황을 객체배열과 Collection 구현이된 List 인터페이스가 구현이된 객체로 두가지 코딩을 한번 해보겠습니다. 

1. 객체배열코딩법
1 final String[] items = new String[] { "A" "B" "C" };
2 ArrayAdapter<string> adapter = new ArrayAdapter<string>( this , android.R.layout.simple_list_item_1 , items);
3 adapter.add("D");
4 </string></string>

2. List 코딩법 
1 final List<string> items = new ArrayList<string>();
2 items.add("A");
3 items.add("B");
4 items.add("C");
5 ArrayAdapter<string> adapter = new ArrayAdapter<string>( this , android.R.layout.simple_list_item_1 , items);
6 adapter.add("D");
7 </string></string></string></string>

위의 코딩법이 언뜻보면 똑같은 코딩이라 똑같은 결과가 나올것이라고 생각되는데 막상 컴파일을 해보면 2번코딩법은 정상적으로 동작을 하는데 비해 1번코딩법은 에러가 발생합니다. 도대체 무슨이유지 하는 고민에 빠져서 ArrayAdapter의 소스를 분석해나갔습니다. 

3. 소스 분석 및 결론

소스를 분석도중 ArrayAdapter의 인자값으로 객체배열로 던졋을시는 Arrays.asList를 이용하여 처리를 해주고 있습니다. 하지만 Arrays.asList는 객체배열을 Collection으로 변경시켜주는 메소드인데 안될이유가 없는데 ? 하고 생각을 했지만 여기서 웹서핑중 중요한 사실을 하나 알게됩니다. Arrays.asList로 리턴해주는 객체는 변경이 불가능 하다는 점입니다. 
또 여기서 궁금증이 도지게 됬습니다. 그래서 또 Arrays클래스에 asList메소드의 구현부분을 찾아가게됬는데 일반적으로 ArrayList를 인스턴스화 후 리턴하게 코딩이 되어있습니다. 그런데 자세히 보니 ArrayList가 java.util.ArrayList가 아닌 Arrays객체 안에 있는 static 키워드로 구성이된 내부클래스 였습니다. 그 내부 클래스를 보니 ArrayList객체상태가 변경이 되는 메소드는 모두 구현이 안되어있습니다. 이러한 설계이유는 지금의 저의 레벨에서는 전혀 짐작이 가지 않습니다. 하지만 이것만 게속 분석하면 정확한 답은 아니더라도 분명 어느정도의 예상을 하게되지만 지금의 시기에서는 비효율적인거 같습니다. 지금은 객체의 내부보다는 올바른사용법을 익히는것이 더 중요하다고 생각합니다. (시간을 많이 투자하면 그만큼의 결과가 나오기마련이지만 객체의 갯수는 상상을 초월하고 그 이해력의 수준은 너무 낮다고 생각합니다. 객체를 분석도 좋지만 그것외에 공부해야하는것이 너무나도 많기때문입니다.)
나중에 내공이 많이 쌓이면 다시 한번 봐야되겠습니

안드로이드에서 자주 사용하는 기법인 ListView에 동적으로 아이템을 추가시 새로추가된 아이템으로 스크롤이 이동이 되지 않는 현상에 대해서 간단하게 알아보겠습니다.

일단 간단한 상황을 예를 들어서 풀어보도록 하겠습니다.

상황 : 현재의 Activity는 ListView와 EditText로 구성이 되어있고 EditText에 내용을 입력하고 Enter키를 입력시 ListView에 동적으로 추가된다.

위의 상황을 코드로 표현을 해보겠습니다.

01 public class TranscriptDemo extends ListActivity implements OnKeyListener {
02     private ArrayAdapter<STRING> adapter;
03     private ArrayList<STRING> list = new ArrayList<STRING>();
04     private EditText editText;
05        
06     public void onCreate(Bundle bundle){
07         super.onCreate(bundle);
08         setContentView(R.layout.transcript);
09           
10         editText = (EditText)findViewById(R.id.EditText01);
11         editText.setOnKeyListener(this);
12         adapter = new ArrayAdapter<STRING>(this, android.R.layout.simple_list_item_1, list);
13         setListAdapter(adapter);
14     }
15   
16     @Override
17     public boolean onKey(View v, int keyCode, KeyEvent event) {
18         if (event.getAction() == KeyEvent.ACTION_DOWN) {
19             switch (keyCode) {
20                 case KeyEvent.KEYCODE_ENTER:
21                     setText();
22                     return true;
23             }
24         }
25         return false;
26     }
27       
28     private void setText() { 
29         adapter.add(editText.getText().toString());
30         editText.setText(null);
31     }
32 }
33 </STRING></STRING></STRING></STRING>

코드를 정상적으로 작성을 하였으면 밑에 화면과 같이 정상적으로 동작이 됩니다.



하지만 여기서 문제가 있는게 위와같이 게속 아이템을 등록을 한다면 스크롤바가 생기게 될텐데 그 포커스가 밑에 그림과 같이 처음의 포커스에 맞춰져 잇다는점 입니다.



이 문제를 해결하기 위해서 저는 예전에 새로운 아이템을 추가할 시에 마지막 아이템을 선택 하기 조건을 줬습니다.

1 getListView().setSelection(list.size() - 1);

하지만 안드로이드 내에서 ListView에 제공하는 속성인 android:transcriptMode를 이용하여 더욱더 간단하게 처리를 할수 있습니다.
android:transcriptMode에 대해서 알아보자.

android:transcriptMode ?


Sets the transcript mode for the list. In transcript mode, the list scrolls to the bottom to make new items visible when they are added.

Must be one of the following constant values.


 Constant  Value  Description
 disabled  0  Disables transcript mode. This is the default value.
 normal  1  The list will automatically scroll to the bottom when a data set change notification is received and only if the last item is already visible on screen.
 alwaysScroll  2  The list will automatically scroll to the bottom, no matter what items are currently visible.

위에 자료는 안드로이드 API에 있는 내용이다. 간단하게 설명을하자면
transcriptMode 속성은 3가지의 값을 정의해줄수 있고 각각의 설명은 아래와 같다.  

1. disabled : 스크롤이 되지 않음
2. nomal : 현재의 포커스에서 추가된만큼의 포커스가 이동
3. alwaysScroll : 마지막에 추가된곳으로 포커스 이동

제가 만들프로그램은 마지막에 추가된곳으로 포커스가 이동을 해야하기 때문에 alwaysScroll을 줘야한다.

1 getListView().setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

위와같이 Java단에서 설정을 해줘도 되고 xml에서 설정을 해줘도 된다.
설정이 올바르게 끝났으면 정상적으로 밑에 화면과 같이 작동을 할것이다.

2010/02/22 15:43

복사 http://blog.naver.com/yhr40017/100100499196

첨부파일 (4)

안드로이드 ListView + ArrayAdapter 활용법에 대해 올려봅니다.

 

원래는 TableLayout을 이용하면 화면에 뿌리는거는 HTML이나 FLEX처럼 보여줄 수 있으나,

 

column, row, cell단위로 click하여 event를 처리하기에는 ListView + ArrayAdapter을 사용해야하는데 이에 대한 자세한 내용이 있는 곳이 별로 없어서 올려봅니다.

 

표현하려는 내용은 간단히 ListView에 2개의 TextView와 하나의 Button을 row로 갖는 grid형태의 TableList를 표현해보도록 하겠습니다.

 

1. 먼저 ListView의 row에 들어갈 view를 다음(list_row.xml)과 같이 작성합니다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:padding="4dip">
  <TextView
   android:id="@+id/code"
   android:layout_width="wrap_content"
   android:layout_height="fill_parent"
   android:padding="4dip" />
  <TextView
   android:id="@+id/msg"
   android:layout_width="wrap_content"
   android:layout_height="fill_parent"
   android:padding="4dip" />
  <Button
   android:id="@+id/btn"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:padding="4dip" />
</LinearLayout>

 

2. ListView의 row를 표시한 item을 다음과 같이 정의하겠습니다.

 

public class Item extends TableList {
 private String code;
 private String msg;
 
 public Item(String code, String msg) {
  this.code = code;
  this.msg = msg;
 }
 
 public String getCode() { return this.code; }
 public String getMsg() { return this.msg; }
 public String toString() { return this.code + ", " + this.msg; }
}

 

3. 해당 item 정보를 ListView의 ArrayAdapter로 맵핑하기 위해 ArrayAdapter를 상속받아서 다음과 같이 getView 메쏘드를 작성합니다. ListView를 그리기 위해 현재 Item위치(position), convertView(list_row.xml), parent(ListView) 정보 parameter를 이용하여 해당 row의 정보를 설정하게 되며 다음과 같이 작성된다.

 

public class ItemAdapter extends ArrayAdapter<Item> {
 private ArrayList<Item> itemList;
 private Context context;
 private int rowResourceId;
 
 public ItemAdapter(Context context, int textViewResourceId, ArrayList<Item> itemList) {
        super(context, textViewResourceId, itemList);
        //ListView의 row정보를 저장하는 ArrayList.

        this.itemList = itemList;
        this.context = context;

        // list_row.xml의 id. getView 메쏘드에서 추가될 item의 position에 해당 view를 inflate하기 위해 저장
        this.rowResourceId = textViewResourceId;
 }
 
 @Override
    public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {

                // LayoutInflater를 이용하여 list_row를 view로 설정한다.
                LayoutInflater vi = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(this.rowResourceId, null);
            }

            // 그려지게될 ListView의 현재 row 위치(position) item 정보를 얻어온다.
            Item item = itemList.get(position);
            if (item != null) {

                    // 선택된 row의 데이터를 표시한다. 표시될 view는 list_row.xml의 각 항목을 이용하여 표시한다.
                    TextView codeView = (TextView) v.findViewById(R.id.code);
                    TextView msgView = (TextView) v.findViewById(R.id.msg);
                    Button btn = (Button) v.findViewById(R.id.btn);
                   
                    if (codeView != null)
                     codeView.setText(item.getCode());
                    if (msgView != null)
                     msgView.setText(item.getMsg());
                    if (btn != null)
                     btn.setText("button:"+position);
            }
            return v;
    }
}

 

4. 끝으로 해당 정보를 그려줘야할 Activity를 다음과 같이 작성합니다.

 

public class TableList extends Activity {
 
 //main.xml의 ListView
 private ListView myView;
 //ListView에 데이터를 맵핑해 주게될 ArrayAdapter를 확장 구현한 Adapter
 private ItemAdapter itemAdapter;
 //ItemAdapter(ArrayAdapter)에 데이터를 제공하게될 ArrayList
 private ArrayList <Item> itemList= new ArrayList<Item>();
 
 //UI에 데이터가 변경된 경우 다시 그려주게될 Runnable interface. 데이터를 갱신하고, 이를 화면에
 //반영해야할 경우에는 반드시 Activity의 runOnUiThread(Runnable r) 메쏘드를 이용해서 호출해야
 //실시간으로 화면에 반영됩니다.
 private Runnable updateUI = new Runnable() {
  public void run() {
   TableList.this.itemAdapter.notifyDataSetChanged();
  }
 };
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //main.xml에서 ListView id를 얻어온다.
        myView = (ListView) this.findViewById(R.id.listView);
        //ListView의 데이터 Adapter를 초기화해준다.
        itemAdapter = new ItemAdapter(this, R.layout.list_row, itemList);
        myView.setAdapter(itemAdapter);
    }
   
   
    @Override
 protected void onResume() {
  // TODO Auto-generated method stub
  super.onResume();
  
  //화면이 나타나면 임의의 정보를 ListView에 표시한다.
  String code;
  String msg;
  int itemCount = 5;
  for(int i=0;i<itemCount;i++) {
   code = "item" + i;
   msg = "message" + i;
   Item item = new Item(code, msg);
   this.addItem(item);
  }
 }


 private void addItem(Item item) {
  // ArrayList에 데이터를 추가하고, 화면에 반영하기 위해runOnUiThread()를 호출하여 실시간 갱신한다.
     this.itemList.add(item);
     this.runOnUiThread(updateUI);
    }
}

 

ListIconText.java

 



public class ListIconText extends Activity {
    /** Called when the activity is first created. */
 // 내부 클래스를 담을 ArrayList
 ArrayList<MyItem> arItem;
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  arItem = new ArrayList<MyItem>();
  // 클래스의 객체를 선언하여
  MyItem mi;
  // 객체를 생성하여 arItem에 담음
  mi = new MyItem(R.drawable.icon, "삼성 노트북");arItem.add(mi);
  mi = new MyItem(R.drawable.icon, "LG 세탁기");arItem.add(mi);
  mi = new MyItem(R.drawable.icon, "대우 마티즈");arItem.add(mi);

  // BaseAdpater를 상속받아 선언
  // 리스트 뷰에게 이 항목의 집합을 제공하는 클래스
  // 어댑터를 동작하려면 BaseAdapter로부터 상속받아
  // 기본 기능을 물려받은 후 요구하는 추상 메서드를 재정의 해야함.
  MyListAdapter MyAdapter = new MyListAdapter(this, R.layout.icontext, arItem);

  ListView MyList;
  MyList=(ListView)findViewById(R.id.list);
  MyList.setAdapter(MyAdapter);
 }
}

//리스트 뷰에 출력할 항목
class MyItem {
 MyItem(int aIcon, String aName) {
  Icon = aIcon;
  Name = aName;
 }
 int Icon;
 String Name;
}

//어댑터 클래스
class MyListAdapter extends BaseAdapter {
 Context maincon;
 LayoutInflater Inflater;
 ArrayList<MyItem> arSrc;
 int layout;

 public MyListAdapter(Context context, int alayout, ArrayList<MyItem> aarSrc) {
  maincon = context;
  Inflater = (LayoutInflater)context.getSystemService(
    Context.LAYOUT_INFLATER_SERVICE);
  arSrc = aarSrc;
  layout = alayout;
 }

 public int getCount() {
  return arSrc.size();
 }

 public String getItem(int position) {
  return arSrc.get(position).Name;
 }

 public long getItemId(int position) {
  return position;
 }

 // 항목 하나를 출력하기 위한 뷰
 // 각 항목의 뷰 생성
 // position -> 생성할 항목의 순서값
 // parent -> 생성되는 뷰의 부모
 // convertView -> 이전에 생성된 차일드 뷰
 public View getView(int position, View convertView, ViewGroup parent) {
  final int pos = position;
  if (convertView == null) {
   convertView = Inflater.inflate(layout, parent, false);
  }
  ImageView img = (ImageView)convertView.findViewById(R.id.img);
  img.setImageResource(arSrc.get(position).Icon);

  TextView txt = (TextView)convertView.findViewById(R.id.text);
  txt.setText(arSrc.get(position).Name);

  Button btn = (Button)convertView.findViewById(R.id.btn);
  btn.setOnClickListener(new Button.OnClickListener() {
   public void onClick(View v) {
    String str = arSrc.get(pos).Name + "를 주문합니다.";
    Toast.makeText(maincon, str, Toast.LENGTH_SHORT).show();
   }
  });

  return convertView;
 }
}


1. 인터페이스(aidl) 생성
2. 구현클래스 생성
3. 메니페스트에 적고
4. 엑티비티에서 해당 클래스를가져와서
5. 구현된 함수를 사용.

의 순서로 진행됩니다.

사실 엑티비티 레벨이니 서비스 레벨이니 신경안써도 돼는 작은 어플에서는 샤용할일이 없을듯.

1. aidl
package hell.o;
interface IPlusItService {
int add(int a, int b);
}

2. impl class
package hell.o;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class PlusItService extends Service {

@Override
public IBinder onBind(Intent intent) {
if (IPlusItService.class.getName().equals(intent.getAction())) {
return plusItServiceIf;
}
return null;
}

private IPlusItService.Stub plusItServiceIf = new IPlusItService.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
}
3. manifest
        <service android:name="PlusItService">
<intent-filter>
<action android:name="hell.o.IPlusItService"></action>
</intent-filter>
</service>
4. class load
    private IPlusItService plusItServiceIf
먼저 서비스 인터페이스를 담을 변수를 선언
	Intent intent = new Intent(IPlusItService.class.getName());
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
plusItServiceIf = IPlusItService.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
plusItServiceIf = null;
}
}, BIND_AUTO_CREATE);
온크리에이트에 넣을 내용. (상식이있는 인간이라면 ServiceConnection을 밖으로 빼겠지만 졸려죽겠는데 그런거없다능.)

5. use
	int sum = plusItServiceIf.add(a, b);
요거 한줄하려고 이고생을!

'Android > Tip&Tech' 카테고리의 다른 글

안드로이드(android) ListView + ArrayAdapter ANDROID  (0) 2010.11.04
listview에 버튼넣기  (0) 2010.11.03
안드로이드 서비스 팁  (0) 2010.11.03
Sending SMS Messages  (2) 2010.11.03
ch03애플리케이션과 액티비티 만들기  (0) 2010.11.03

Service = Deamon = Background Program
말그대로 화면없이 뒤에서 실행되는 기능을 말한다.
예를 들어 문자를 보내면서 배경 음악이 나온다면 문자를 보내기 위해 사용자에게 제공되는 Activity 이외에
보이지 않지만 MediaPlayer가 음악을 재생한다. 이것이 android의 service 라고 할 수 있다.

다음은 버튼을 눌렀을때 음악이 재생되는 단순한 예제이다.

res에 raw라는 폴더를 만들고 sorry.mp3를 넣었다. 이에 대한 접근은 다음처럼 R.raw.sorry 로 가능하다.
main.xml에 Button을 만들어놨다. 이전 예제를 수정한거라 android:id = "@+id/btn_message" 이다

RosaActivity.java
package com.rosa.prankapp;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class RosaActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.w("rosa", "PrankAppActivity onCreated()");
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        startService(new Intent("com.rosa.service.test"));
        
        Button btn_message = (Button)findViewById(R.id.btn_message);
        btn_message.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                stopService(new Intent("com.rosa.service.test"));
            }
        });        
    }
}

RosaService.java
package com.rosa.prankapp;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.util.Log;

public class RosaService extends Service {
    
    private MediaPlayer player;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }
    
    @Override
    public void onStart(Intent intent, int startId) {
        Log.i("rosa", "Service onStart()");        
        //반드시 호출해줘야 함
        super.onStart(intent, startId);        
      player = MediaPlayer.create(this, R.raw.sorry);
      player.start();
    }
    
    @Override
    public void onDestroy() {
        Log.i("rosa", "Service onDestory()");        
        super.onDestroy();
        player.stop();
    }
}


AndroidManifest.xml에 다음 부분을 추가
<service android:name="PrankService">
    <intent-filter>
        <action android:name="com.rosa.service.test"/>
      <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</service>

실행화면
버튼 누르면 노래재생.
사용자 삽입 이미지

'Android > Tip&Tech' 카테고리의 다른 글

안드로이드(android) ListView + ArrayAdapter ANDROID  (0) 2010.11.04
listview에 버튼넣기  (0) 2010.11.03
AIDL이 뭘까나?  (0) 2010.11.03
Sending SMS Messages  (2) 2010.11.03
ch03애플리케이션과 액티비티 만들기  (0) 2010.11.03

Mobiforge에서 잘.....온전히 발췌 했습니다.

It would be safe to say that nearly every mobile phone sold in the past decade has SMS messaging capabilities. In fact, SMS messaging is one great killer application for the mobile phone and it has created a steady revenue stream for mobile operators. Understanding how to use SMS messaging in your application can provide you with many ideas to create the next killer application.

In this article, we take a look at how you can programmatically send and receive SMS messages in your Android applications. The good news for Android developers is that you don't need a real device to test out SMS messaging - the free Android emulator provides the capability to do so.

Sending SMS Messages

To get started, first launch Eclipse and create a new Android project. Name the project as shown in Figure 1.

사용자 삽입 이미지

Figure 1 Creating a new Android project using Eclipse

Android uses a permission-based policy where all the permissions needed by an application need to be specified in the AndroidManifest.xml file. By doing so, when the application is installed it will be clear to the user what specific access permissions are required by the application. For example, as sending SMS messages will potentially incur additional cost on the user's end, indicating the SMS permissions in the AndroidManifest.xml file will let the user decide whether to allow the application to install or not.

In the AndroidManifest.xml file, add the two permissions - SEND_SMS and RECEIVE_SMS:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.learn2develop.SMSMessaging"
android:versionCode="1"
android:versionName="1.0.0">

<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".SMS"
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.SEND_SMS">
</uses-permission>
<uses-permission android:name="android.permission.RECEIVE_SMS">
</uses-permission>
</manifest>

In the main.xml file located in the res/layout folder, add the following code so that the user can enter a phone number as well as a message to send:

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

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Enter the phone number of recipient"
/>

<EditText
android:id="@+id/txtPhoneNo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Message"
/>

<EditText
android:id="@+id/txtMessage"
android:layout_width="fill_parent"
android:layout_height="150px"
android:gravity="top"
/>

<Button
android:id="@+id/btnSendSMS"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Send SMS"
/>

</LinearLayout>

The above code creates the UI shown in Figure 2.

사용자 삽입 이미지

Figure 2 Creating the UI for sending SMS messages

Next, in the SMS activity, we wire up the Button view so that when the user clicks on it, we will check to see that the phone number of the recipient and the message is entered before we send the message using the sendSMS() function, which we will define shortly:

package net.learn2develop.SMSMessaging;
 
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.gsm.SmsManager;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
 
public class SMS extends Activity
{
Button btnSendSMS;
EditText txtPhoneNo;
EditText txtMessage;
 
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
 
btnSendSMS = (Button) findViewById(R.id.btnSendSMS);
txtPhoneNo = (EditText) findViewById(R.id.txtPhoneNo);
txtMessage = (EditText) findViewById(R.id.txtMessage);
 
btnSendSMS.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
String phoneNo = txtPhoneNo.getText().toString();
String message = txtMessage.getText().toString();
if (phoneNo.length()>0 && message.length()>0)
sendSMS(phoneNo, message);
else
Toast.makeText(getBaseContext(),
"Please enter both phone number and message.",
Toast.LENGTH_SHORT).show();
}
});
}
}

The sendSMS() function is defined as follows:

public class SMS extends Activity 
{
//...
 
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
//...
}
 
//---sends an SMS message to another device---
private void sendSMS(String phoneNumber, String message)
{
PendingIntent pi = PendingIntent.getActivity(this, 0,
new Intent(this, SMS.class), 0);
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(phoneNumber, null, message, pi, null);
}
}

To send an SMS message, you use the SmsManager class. Unlike other classes, you do not directly instantiate this class; instead you will call the getDefault() static method to obtain an SmsManager object. The sendTextMessage() method sends the SMS message with a PendingIntent. The PendingIntent object is used to identify a target to invoke at a later time. For example, after sending the message, you can use a PendingIntent object to display another activity. In this case, the PendingIntent object (pi) is simply pointing to the same activity (SMS.java), so when the SMS is sent, nothing will happen.

If you need to monitor the status of the SMS message sending process, you can actually use two PendingIntent objects together with two BroadcastReceiver objects, like this:

    //---sends an SMS message to another device---
private void sendSMS(String phoneNumber, String message)
{
String SENT = "SMS_SENT";
String DELIVERED = "SMS_DELIVERED";
 
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
new Intent(SENT), 0);
 
PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,
new Intent(DELIVERED), 0);
 
//---when the SMS has been sent---
registerReceiver(new BroadcastReceiver(){
@Override
public void onReceive(Context arg0, Intent arg1) {
switch (getResultCode())
{
case Activity.RESULT_OK:
Toast.makeText(getBaseContext(), "SMS sent",
Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Toast.makeText(getBaseContext(), "Generic failure",
Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Toast.makeText(getBaseContext(), "No service",
Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
Toast.makeText(getBaseContext(), "Null PDU",
Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Toast.makeText(getBaseContext(), "Radio off",
Toast.LENGTH_SHORT).show();
break;
}
}
}, new IntentFilter(SENT));
 
//---when the SMS has been delivered---
registerReceiver(new BroadcastReceiver(){
@Override
public void onReceive(Context arg0, Intent arg1) {
switch (getResultCode())
{
case Activity.RESULT_OK:
Toast.makeText(getBaseContext(), "SMS delivered",
Toast.LENGTH_SHORT).show();
break;
case Activity.RESULT_CANCELED:
Toast.makeText(getBaseContext(), "SMS not delivered",
Toast.LENGTH_SHORT).show();
break;
}
}
}, new IntentFilter(DELIVERED));
 
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);
}

The above code uses a PendingIntent object (sentPI) to monitor the sending process. When an SMS message is sent, the first BroadcastReceiver's onReceive event will fire. This is where you check the status of the sending process. The second PendingIntent object (deliveredPI) monitors the delivery process. The second BroadcastReceiver's onReceive event will fire when an SMS is successfully delivered.

You can now test the application by pressing F11 in Eclipse. To send an SMS message from one emulator instance to another, simply launch another instance of the Android emulator by going to the Tools folder of the SDK and running Emulator.exe.

사용자 삽입 이미지

Figure 3 Sending an SMS message

Figure 3 shows how you can send an SMS message from one emulator to another; simply use the target emulator's port number (shown in the top left corner of the window) as its phone number. When an SMS is sent successfully, it will display a "SMS sent" message. When it is successfully delivered, it will display a "SMS delivered" message. Note that for testing using the emulator, when an SMS is successfully delivered, the "SMS delivered" message does not appear; this only works for real devices.

Figure 4 shows the SMS message received on the recipient emulator. The message first appeared in the notification bar (top of the screen). Dragging down the notification bar reveals the message received. To view the entire message, click on the message.

사용자 삽입 이미지

Figure 4 The SMS message received by the Android emulator

If you do not want to go through all the trouble of sending the SMS message yourself, you can use an Intent object to help you send an SMS message. The following code shows how you can invoke the built-in SMS application to help you send an SMS message:

        Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.putExtra("sms_body", "Content of the SMS goes here...");
sendIntent.setType("vnd.android-dir/mms-sms");
startActivity(sendIntent);

Figure 5 shows the built-in SMS application invoked to send the SMS message.

사용자 삽입 이미지

Figure 5 Invoking the built-in SMS application

Receiving SMS Messages

Besides programmatically sending SMS messages, you can also intercept incoming SMS messages using a BroadcastReceiver object.

To see how to receive SMS messages from within your Android application, in the AndroidManifest.xml file add the <receiver> element so that incoming SMS messages can be intercepted by the SmsReceiver class:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.learn2develop.SMSMessaging"
android:versionCode="1"
android:versionName="1.0.0">

<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".SMS"
android:label="@string/app_name">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
 
<receiver android:name=".SmsReceiver">
<intent-filter>
<action android:name=
"android.provider.Telephony.SMS_RECEIVED" />

</intent-filter>
</receiver>
 
</application>
<uses-permission android:name="android.permission.SEND_SMS">
</uses-permission>
<uses-permission android:name="android.permission.RECEIVE_SMS">
</uses-permission>
</manifest>

Add a new class file to your project and name it as SmsReceiver.java (see Figure 6).

사용자 삽입 이미지

Figure 6Adding the SmsReceiver.java file to the project

In the SmsReceiver class, extend the BroadcastReceiver class and override the onReceive() method:

package net.learn2develop.SMSMessaging;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
 
public class SmsReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
}
}

When SMS messages are received, the onCreate() method will be invoked. The SMS message is contained and attached to the Intent object (intent- the second parameter in the onReceive() method) via a Bundle object. The messages are stored in an Object array in the PDU format. To extract each message, you use the static createFromPdu() method from the SmsMessage class. The SMS message is then displayed using the Toast class:

package net.learn2develop.SMSMessaging;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.gsm.SmsMessage;
import android.widget.Toast;
 
public class SmsReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
//---get the SMS message passed in---
Bundle bundle = intent.getExtras();
SmsMessage[] msgs = null;
String str = "";
if (bundle != null)
{
//---retrieve the SMS message received---
Object[] pdus = (Object[]) bundle.get("pdus");
msgs = new SmsMessage[pdus.length];
for (int i=0; i<msgs.length; i++){
msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
str += "SMS from " + msgs[i].getOriginatingAddress();
str += " :";
str += msgs[i].getMessageBody().toString();
str += "\n";
}
//---display the new SMS message---
Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
}
}
}

That's it! To test the application, press F11 in Eclipse. Deploy the application to each Android emulator. Figure 7 shows Eclipse showing the emulators currently running. All you need to do is to select each emulator and deploy the application onto each one.

사용자 삽입 이미지

Figure 7 Selecting an emulator/device to deploy the application onto

Figure 8 shows that when you send an SMS message to another emulator instance (port number 5556), the message is received by the target emulator and displayed via the Toast class.

사용자 삽입 이미지

Figure 8 Sending and receiving SMS messages using the Android emulators

Summary

In this article, you have seen how you can send and receive SMS messages programmatically from within your Android application. The capability to send and receive SMS messages is very useful as you can build very compelling applications. As an example, you can build a location tracker application where you can send a secret-coded SMS message to a device and when the device receives the secret SMS message it will reply with another SMS message containing its current geographical location using its built-in GPS receiver. How cool is that?!

'Android > Tip&Tech' 카테고리의 다른 글

안드로이드(android) ListView + ArrayAdapter ANDROID  (0) 2010.11.04
listview에 버튼넣기  (0) 2010.11.03
AIDL이 뭘까나?  (0) 2010.11.03
안드로이드 서비스 팁  (0) 2010.11.03
ch03애플리케이션과 액티비티 만들기  (0) 2010.11.03

ch03애플리케이션과 액티비티 만들기

*이번 장에서는 애플리케이션의 수명 주기와 안드로이드 런타임이 이 수명 주기를 관리하는 방식에 대해 좀더 배운다


- 안드로이드 애플리케이션 구성요소

*액티비티 : 뷰를 사용해 정보를 보여주고 사용자 액션에 반응하는 그래픽 사용자 인터페이스를 형성한다.

*서비스 : 화면에 보이지 않은 채 실행되어,데이터 소스와 화면에 보이는 액티비티들을 업데이트하고 알림을 생성한다.

*콘텐츠 공급자 : 애플리케이션 데이터베이스를 관리하고 공유하는 데 사용된다.

*인텐트 : 간단한 메시지 전달 프레임워크다.메시지를 방송하고 의도를 전달할 수 있다.

*브로드캐스트 수신자 : 수신되는 인텐트에 반응해 자동으로 여러분의 애플리케이션을 시작시킬 것이다.

*알림 : 사용자의 현재 액티비티가 가진 포커스를 빼앗거나 방해함 없이 사용자에게 신호 할 수 있도록 해준다.


-애플리케이션 매니페스트 소개

*매니페스트는 애플리케이션과 애플리케이션이 가진 컴포넌트의 구조 및 메타데이터를 정의하도록 해준다.

* applicatin : 애플리케이션 노드는 속성을 사용해 애플리케이션을 위한 메타데이터(타이틀,아이콘,테마 등)을 지정한다

* activity : activity 태그는android:name 속성으로 클래스 이름을 지정한다.

* service

* provider : 콘텐츠 공급자에 대해 사용된다.

* receiver : receiver 태그를 추가하면 애플리케이션을 띄우지 않고서도 브로드 캐스트 수신자를 등록할 수 있다.

* uses - permission : 애플리케이션이 올바로 동작하는 데 필요하다고 생각되는 권한을 선언한다.

* permission : 권한 정의를 생성하는데 사용된다.

* instrumentation : 액티비티와 서비스를 테스트하기 위한 프레임워크를 제공한다.


- 매니페스트 에디터 사용하기

* Application Attributes (애플리케이션 속성) 패널에서는 아이콘, 레이블, 테마 등을 포함한 애플리케이션의 속성을 지정할 수 있다.


-안드로이드 애플리케이션 수명 주기

*애플리케이션 컴포넌트는 반드시 애플리케이션 상태에 생기는 변화에 귀 기울여 그에 따라 적절히 반응해야 하며, 불시 종료에 대비하도록 특히 신경 써야 한다.

*각 애플리케이션의 메모리와 프로세스 관리는 런타임에 의해 배타적으로 처리된다.


-애플리케이션 우선순위와 프로세스 상태 이해하기

*자원 회수를 위해 종료되는 프로세스의 순서는 주인 애플리케이션의 우선순위에 따라 결정된다.

* 1.활성 프로세스(중요한 우선순위) > 2.화면에 보이는 프로세스 3. 시작된 서비스 프로세스(높은 우선순위) > 4. 백그라운드 프로세스 5. 빈 프로세스(낮은 우선순위)

*활성 프로세스 : 사용자가 현재 상호작용하고 있는 컴포넌트를 가진 주인 애플리케이션이다.

*화면에 보이는 프로세스 : 눈에 보이긴 하지만 포그라운드에 있지 않거나 사용자 이벤트에 반응하지 않는다.

*시작된 서비스 프로세스 : 화면에 보이는 인터페이스 없이 계속 돼야 하는 지속적인 처리를 지원한다.

*백 그라운드 프로세스

*빈 프로세스


-리소스 외부화 하기

 *이미지와 문자열 상수 같은 비 코드 리소스를 코드 외부에 유지하는 것은 항상 좋은 습관이다.

 *기본 레이아웃, 애플리케이션 아이콘,문자열 리소스 정의를 담고 있는 values, drawable, layout 리소스를 위한 하위 폴더를 res폴더 안에 생성할 것이다.

 *지원되는 간단한 값에는 문자열, 색상,치수, 그리고 문자열 배열이나 정수 배열 등이 있다.

 *문자열 : string 태그로 지정된다.

 *색상 : color 태그를 정의한다.

 *치수(dimen) :  테두리와 글꼴 높이 같은 레이아웃 상수를 생성하는 데 유용하다.

 *스타일 : 뷰에 의해 사용되는 속성 값을 지정함으로써 애플리케이션이 일관된 룩앤필을 유지하도록 해준다. 스타일은 style태그의 parent속성을 통해 상속을 지원한다.

 *드로이블(drawable) : 비트맵과 나인패치(늘어날 수 있는 PNG) 이미지가 해당된다.

 *레이아웃: 코드에서 레이아웃을 분리하는 것은 다양한 화면,크기, 방향 또는 키보드 및 터치 스크린의 존재유무와 같이 서로 다른 하드웨어 구성을 위해 최적화된 레이아웃을 만들 수 있게끔 해준다.

 *애니메이션 : 트윈드 애니메이션(alpha, scale, translate, rotate, set태그:duration, startOffset, fillBefore, fillAfter, Interpolator), 프레임 바이 프레임 애니메이션

 *코드에서 리소스 사용하기 : 코드에서는 static클래스 R을 사용해 리소스를 접근한다. 기본 새 프로젝트는 R.stringR.drawable 하위클래스를 포함한다.

 *리소스에서 다른 리소스 참조하기 : @표기법을 사용한다, @[packagename:] resourcetype/resourceidentifier

 *시스템 리소스 사용하기 : android.R을 통해 사용가능한 네이티브 안드로이드 리소스 클래스를 사용한다.

 *현재 테마에서 스타일 참조하기 : ?android: 를 사용한다

 *여러 가지 언어와 하드웨어를 위한 리소스 만들기

 *런타임 구성 변경 : 런타임 구성 변경을 감지하는 액티비티를 가지려면, 매니페시트 노드에 android:configChanges 속성을 추가한 뒤 구성 변경을 지정(orientation, keyboardHidden, fontScale, locale, keyboard, touchscreen or navigagion)


 -안드로이드 액티비티 좀더 자세히 살펴보기

 *애플리케이션을 위한 사용자 인터페이스 화면을 만들기 위해서는, Activity 클래스를 확장하고, 뷰를 사용해 사용자 상호작용을 제공한다

 *액티비티 만들기 : 새로운 액티비티를 생성하려면, Activity 클래스를 확장한 다음 사용자 인터페이스를 정의하고 원하는 기능을 구현한다.

 *액티비티 수명주기 : 활성Active(화면에 보이고 포커스를 가지며 사용자 입력을 받는다, 다른 액티비티가 활성화되면 기존의 활성 액티비티는 일시 중지될 것이다)

일시 중지Paused(투명한 액티비티나 화면 전체를 사용하지 않는 액티비티가 그 앞에 활성화되는 경우 도달된다),

중지Stopped(화면에 보이지 않을 경우 "중지"된다)

비활성Inactive(종료되고 난 이후와 띄워지기 이전 비활성상태로 있는다)

 *상태 변화 모니터링 : 액티비티가 자신의 전체수명full lifetime, 가시수명visible lifetime, 활성수명active lifetime 사이를 전이할 때 호출되는 일련의 이벤트 핸들러들을 제공한다.

* onCrete() : 전체 수명 시장 시 호출

* onRestoreInstanceState() : onCrete가 종료된 후 호출되며, UI 상태 복구에 사용

* onRestart() : 가시 수명으로 이어지기 전, 액티비티 처리를 위해 호출

* onStart() : 가시 수명 시작 시 호출

* onResume() : 활성 수명 시작 시 호출

* onSaveInstanceState() : 활성 수명 끄트머리에서 UI 상태 변화를 저장하기 위해 호출

* onPause() : 활성 수명의 끝에서 호출

* onStop() : 가시 수명의 끝에서 호출

* onDestroy() : 전체 수명의 끝에서 호출

*전체 수명 : onCrete ~onDestroy  

*가시 수명 : onStart ~ onStop

*활성 수명 : onResume ~ onPause

*안드로이드 액티비티 클래스 : MapActivity, ListActivity, ExpandableListActivity, ActivityGroup

ch04 사용자 인터페이스 만들기


 - 기본적인 안드로이드 UI디자인

 *뷰 : 비주얼 인터페이스 요소(컨트롤 또는 위젯으로 보통 알려진)를 위한 기본 사용자 인터페이스 클래스다

 *뷰 그룹 : 여러 개의 자식 뷰를 담을 수 있는 뷰 클래스의 확장이다.

 *액티비티 : 사용자에게 보여지는 윈도우나 화면을 나타낸다.


 - 뷰 소개

 *컨트롤은 보통 상대적으로 간단한 기능을 구현하는 뷰의 확장을 가리키는 반면,위젯은 대개 복잡한 컨트롤과 좀더 복잡한 뷰의 확장 모두를 가리킨다.

 *뷰를 사용해 액티비티 사용자 인터페이스 만들기 : 사용자 인터페이스를 설정하려면 setContentView를 호출하고 나타낼 뷰 인스턴스(보통 레이아웃)을 전달한다. findViewById 메서드를 사용하면 레이아웃 내에 사용된 뷰의 레퍼런스를 얻을 수 있다.

 *안드로이드 위젯 도구 상자(TextView, EditText, ListView, Spinner(복합컨트롤), Button, CheckBox, RadioButton)


 - 레이아웃 소개

 *레이아웃은 화면 위에 있는 자식 컨트롤의 위치를 제어하기 위해 설계된 ViewGroup클래스의 확장이다.

 * FrameLayout(쌓음), LinearLayout(수평 수직),RelativeLayout(상대적으로 정의), TableLayout(행과 열의 격자), AbsoluteLayout(절대좌표)

 *외부 리소스를 사용해 XML로 구현하는 것이 선호되며,레이아웃 XML은 반드시 단일 루트 엘리먼트를 가져야 한다.


- 새로운 뷰 만들기

 *기존 뷰 수정하기 : 기존 컨트롤에 기반을 둔 위젯을 만들려면,원하는 컨트롤을 확장하는 새로운 클래스를 생성한다.

 *할 일 목록 커스터마이즈하기 :

1.TextView를 확장하는 TodoListItemView 클래스를 만든다.

2.res/values폴더에 color.xml 리소스를 만든다.

3.dimens.xml리소스 파일을 만들고 종이의 여백 폭을 위한 새로운 값을 추가한다.

4.종이 배경과 여백을 그리는 데 사용할 Paint 객체들을 저장하기 위한 private인스턴스 변수들을 만든다.

5. onDraw를 재정의한 뒤 Paint 객체로 종이이미지를 그린다.

6. Res/layout 폴더에 todolist_item.xml리소스를 만든다.

7. ToDoList 액티비티 클래스의 onCreate에서 ArrayAdapter에 전달된 매개변수를 R.layout.todolist_item 레이아웃으로 바꾼다.

 *복합 컨트롤 : 서로 연결되어 배치된 여러 자식 컨트롤을 담는 원자적이면서도 재사용 가능한 위젯이다. 자식 컨트롤의 위치를 지정하는 데 가장 적합한 레이아웃 클래스 하나를 골라 이를 확장한다. 레이아웃을 새 복합 컨트롤에 사용하기 위해서는 해당 복합 컨트롤의 생성자를 오버라이드 한 다음, LayoutInflate시스템 서비스의 inflate메서드를 사용해 레이아웃 리소스를 부플린다. Inflate메서드는 레이아웃 리소스를 취하고 부풀려진 뷰를 리턴한다.

 *커스텀 위젯과 컨트롤 만들기 : 빈 캔버스로부터 새 컨트롤을 만들려면 ViewSurface 클래스 중 하나를 확장한다. View 클래스는 Canvas객체 하나를 비롯하여 일련의 그리기 메서드와 Paint 클래스를 제공한다. SurfaceView는 그리기를 지원하고 3D 그래픽스를 위해 OpenGL을 사용하는 캔버스를 제공한다.

*새 비주얼 인터페이스 만들기 : 비주얼 인터페이스를 나타내려면, onMeasureonDraw메서드를 재정의할 필요가 있다.

*컨트롤 그리기 : Canvas 클래스는 원,,사각형, 텍스트, 드로어블(이미지)등 기본 2D 객체를 그리기위한 메서드를 가지고 있다. DrawablePaint클래스를 조립해 사용한다.

*컨트롤 크기 바꾸기 : onMeasure 메서드는 두 개의 매개변수(widthMeasureSpec, heightMeasureSpec)를 전달, 뷰의 높이와 폭을 setMeasuredDimension메서드에 전달한다.

 *사용자 상호작용 이벤트 다루기 : 가상 이벤트 핸들러(onKeyDown, onKeyUp, onTrackballEvent, onTouchEvent)

 *나침반 뷰 예제 만들기 :

1. CompassView 클래스를 만든다. 컨트롤 초기화에 사용될 initCompassView메서드를 추가하고 이를 각 생성자에서 호출한다. 2. onMeasure 메서드를 재정의해 가장 짧은 변의 길이를 계산하고, setMeasuredDimension을 사용해 이 값을 높이와 폭으로 설정한다.

3.나침반을 그리는 데 사용할 색상과 텍스트 문자열을 저장하는 두 개의 새로운 리소스파일을 만든다.

4.CompassView클래스에서 방위표시를 위한 새로운 프로퍼티 하나를 추가하고 이를 위한 get,set메서드를 만든다. 5.initCompassView 메서드에서 각 리소스의 레퍼런스를 얻고,문자열 값은 인스턴스 변수로 저장, 색상 값은 클래스 범위의 새로운 Paint객체들을 만드는 데 사용한다.

6.String및 Paint 객체들을 사용해 나침반 면을 그린다.

7.main.xml레이아웃리소스를 수정해 TextView 레퍼런스를 새로 만든 CompassView로 바꾼다.

 *커스텀 컨트롤 사용하기



- 메뉴 만들고 사용하기

 *안드로이드 메뉴 시스템 소개(아이콘 메뉴 : 메뉴 버튼을 누르면 화면 하단에 나타난다. , 확장 메뉴 : 사용자가 아이콘 메뉴에서 자세히 메뉴 항목을 선택할 때 나타난다. , 하위메뉴: 하위 메뉴를 부동 윈도우에 나타낸다.)

 *액티비티 메뉴 정의하기 : onCreateOptionMenu 메서드를 재정의.메뉴를 채우려면 Menu 객체에 add메서드를 사용한다.

 *메뉴 항목 옵션 : 체크 박스와 라디오 버튼(setGroupCheckable),단축 키(setShortcut), 간결한 제목(setTitleCondensed),아이콘(setIcon), 메뉴 항목 클릭 리스너(setOnMenuItemClickListener),인텐트(setIntent)

 *동적으로 메뉴 항목 업데이트하기 : 액티비티의 onPrepareOptionsMenu메서드를 재정의하면, 메뉴가 표시될 때마다 애플리케이션 상태에 기반해 메뉴를 수정할 수 있다.

 *메뉴 선택 처리하기 : onOtionsItemSelected메서드라는 단일 이벤트 핸들러를 사용해 다룬다.

 *하위메뉴와 컨텍스트 메뉴 : 하위메뉴만들기(addSubMenu메서드)

*컨텍스트 메뉴 사용하기 : 컨텍스트 메뉴는 현재 포커스를 가진 뷰에 의해 맥락화되며, 트랙볼이나 가운데 D-패드 버튼 또는 뷰를 약 3초간 누르면 나타난다.

*컨텍스트 메뉴 만들기 : onCreateContextMenu핸들러를 재정의해, View 클래스에 대한 일반적인 컨텍스트 메뉴를 만든다.

*컨텍스트 메뉴 선택 다루기 : 액티비티에 onContextItemSelected메서드를 재정의하는 기법을 사용할 수 있다.

 *할 일 목록 예제 계속 :

1.메뉴 기능을 지원하기 위해 필요한 패키지들(Menu, MenuItem, ContextMenu, AdapterView)ToDoList액티비티 클래스에 임포트한다.

2.각 메뉴 항목을 위한 유일한 ID를 정의하는 private static final 변수를 추가한다.

3. onCreateOptionsMenu 메서드를 재정의하고, 할 일 추가 메뉴 항목과 할 일 삭제 메뉴 항목을 추가한다. 각 항목에 대해 적절한 텍스트를 설정하고 아이콘 리소스와 단축키를 할당한다.

4.컨텍스트 메뉴를 만든다. onCreate를 수정해 ListView가 컨텍스트 메뉴를 받도록 등록한다. 그런 다음 onCreateContextMenu를 재정의해 메뉴에 삭제 항목을 추가한다.

5. onPrepareOptionsMenu 메서드를 재정의해,애플리케이션 상황에 기반하여 메뉴의 겉모습을 바꾼다.

6. todoItems와 ListView 컨트롤의 범위를 onCreate메서드 이상으로 늘린다.( ArrayAdapterEditText 동일한 작업)

7.메뉴 아이템 클릭 처리, onOptionsItemSelectedonContextItemSelected메서드를 재정의해 새 메뉴 항목을 다루는 스텁을 실행한다.

8.각 스텁을 구현해 새로운 기능을 제공한다.

9.새 항목이 추가된 후 텍스트 입력 박스를 감춘다. 새 항목 추가 후 cancelAdd함수를 호출하도록 onCreate 메서드에 있는 onKeyListener를 수정한다.

10.일관된 UI를 보장하기 위해 main.xml레이아웃을 수정하여 사용자가 새로운 항목 추가를 선택할 때까지 텍스트 입력 박스를 감춘다.




ch05인텐트,브로드캐스트 수신자, 어댑터, 그리고 인터넷


- 인텐트 소개

* 인텐트intents는 어떠한 액션이 수행되어야 한다는 의향을 선언할 수 있도록 해주는 메시지 전달 메커니즘이다. 새 액티비티를 시작(명시적,암시적), 시스템 전역에 메시지 방송, 인터넷 연결 상태나 배터리 충전 수준의 변화 같은 시스템 이벤트 알림 등에 사용된다.

* 인텐트를 이용해 액티비티 띄우기 : startActivity(myIntent); myIntent와 가장 필적한 액티비티 하나를 찾아 시작시킨다.

** 명시적으로 새 액티비티 시작시키기 : 읽어 들일 클래스를 지정 (Intent intent = new Intent(MyActivity.this, MyOtherActivity.class)

** 암시적 인텐트와 늦은 런타임 바인딩 : 익명의 애플리케이션 컴포넌트가 액션 요청을 서비스 할 수 있게 함. 데이터의 어느 한 부분에 대해 수행되는 액션을 요청(Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-2368));

** Linkify 소개 :하이퍼 링크를 자동으로 생성하는 도우미 클래스

*** 네이티브 링크 타입 : 브라우저나 이메일, phone 등을 위한 링크를 제공한다.(Linkify.addLink(textView, Linkfy.WEB_URLS | Linkfy.EMAIL_ADDRESSES); (android:autoLink="phone | email" )

*** 커스텀 링크 문자열 만들기 : 하이퍼링크로 표시하고자 하는 텍스트와 일치하도록 새로운 RegEx 패턴을 만든다.

(Pattern p = Pattern.compile("\\bquake[0-9]*\\b", flags); Linkify.addLinks(myTextView, p , "content://com.paad.earthquake/earthquakes/");

*** 매치 필터 사용하기 : RegEx 패턴 매치에 추가 조건을 더하려면 MatchFilteracceptMatch메서드를 구현한다.

*** 변형 필터 사용하기 : 링크 텍스트에 의해 생성된 암시적 URI를 수정함으로써 텍스트 문자열을 서싲화하는데 더 많은 자유를 준다. transformUrl 메서드를 구현한다.

** 액티비티 결과 리턴하기 : startActivity로 시작된 액티비티는 종료할 때 어떠한 피드백도 제공하지 않는다. 액티비티를 그의 부모에 연결된 서브 액티비티로 시작하면 서브 액티비티가 종료할 때 자신의 부모 액티비티 안에 있는 이벤트 핸들러를 호출하기 때문에 결과를 리턴할 수 있다.

*** 서브 액티비티 띄우기 : startActivityForResult(intent, SHOW_SUBACTIVITY);

*** 결과 리턴하기 : finish를 호출하기 전에 setResult를 호출하여 호출 액티비티에 결과를 리턴한다.

*** 서브 액티비티 결과 다루기 : 서브 액티비티가 종료되면 부모 액티비티에 있는 onActivityResult 이벤트 핸들러가 호출된다. onActivityResult의 매개변수(요청코드,결과코드,데이터)

** 네이티브 안드로이드 액션 : static문자열로서 사용가능한 Intent 클래스에 있는 네이티브 액션들(ACTION_ANSWER, ACTION_CALL, ACTION_DELETE, ACTION_DIAL, ACTION_EDIT, ACTION_INSERT, ACTION_PICK, ACTION_SEARCH, ACTION_SENDTO, ACTION_SEND, ACTION_VIEW, ACTION_WEB_SEARCH )

* 인텐트 필터를 이용해 암시적 인텐트 서비스하기 :인텐트 필터는 액티비티, 서비스, 그리고 브로드캐스트 수신자를 특정한 종류의 데이터에 대한 액션을 수행할 수 있는 존재로 등록하는 데 사용된다. 인텐트 필터 노드에 액션(액션의 이름),범주(상황조건),데이터(실행할 수 있는 데이터에 대한 명세)를 지정할 수 있다.(action(android:name), category(ALTERNATIVE, SELECTED_ALTERNATIVE, BROWSABLE, DEFAULT, GADGET, HOME, LAUNCHER), data(android:host, android:mimetype, android:path, android:path, android:port, android:scheme)

** 안드로이드가 인텐트 필터를 해결하는 방법 :지정된 데이터에 주어진 액션을 실행할 수 있는 액티비티가 여러 개 존재한다면, "가장 좋은 것"이 시작된다.

** 인텐트 필터 매치에 반응하기 : 인텐트의 데이터와 액션은 getDatagetAction 메서드로 얻는다.인텐트의 엑스트라 번들에 저장된 추가 정보는 타입에 안전한 get<type>Extra 메서드로 얻는다.

** 책임 떠넘기기 : startNextMatchingActivity 메서드를 이용해, 액션 처리의 책임을 다음 베스트 매칭 애플리케이션 컴포넌트로 넘길 수 있다.

** 연락처 선택 예제 : 1. ContactPicker액티비티 생성 2.main.xmlListView컨트롤 포함되도록 수정(연락처 표시) 3. listitemlayout.xml레이아웃 리소스 생성(텍스트 뷰 포함) 4. onCreate메서드 재정의, 호출 인텐트로부터 데이터 경로 얻어옴,연락처 사람들에 대한 데이터URI를 만들고 SimpleCursorArrayAdapter를 이용해 리스트 뷰에 바인딩, ItemClickListener를 리스트 뷰에 추가 5.매니페스트에 intent-filter 태그를 연락처 데이터에 대한 선택액션 지원을 추가하도록 수정 6. ContentPickerTester 액티비티 생성, 새로운 레이아웃 리소스(선택된 연락처 표시, 시작버튼) 7.ContentPickerTester의 버튼에 클릭 리스너를 추가하여, 버튼이 PICK_ACTION과 연락처 데이터베이스 URI를 지정해 새로운 서브 액티비티를 암시적으로 시작하도록 만듬 8.서브 액티비티가 리턴하면,그 결과를 이용해 선택된 지인의 이름으로 텍스트 뷰를 채움 9. 테스트 도구를 매니페스트에 추가, uses-permission태그에 READ_CONTACTS 권한을 추가

* 플러그 인과 확장성을 위한 이넨트 필터 사용 :런타임 메뉴 채움은 주어진 타입의 데이터에 대해 액션 수행이 가능한 새로운 컴포넌트를 만드는 경우에 옛 기능을 새 기능으로 바꿀 수 있는 능력을 준다

** 애플리케이션에 익명 액션 제공하기 : 액션을 다른 액티비티를 위해 사용할 수 있도록 만들려면, intent-filter 태그를 이용해 액션을 해당 액티비티의 매니페스트 노드 내에 알린다.

** 액티비티 메뉴에 익명 액션 통합하기 : 액션을 찾는데 사용되는 인텐트와 더불어 옵션 플래그, 호출 클래스 이름, 사용할 메뉴 그룹,메뉴 ID값을 여러분이 채우고자 하는 메뉴에 대한 addIntentOptions에 전달한다.

* 이벤트 방송을 위한 인텐트 사용 : sendBroadcast메서드를 이용해 메시지를 컴포넌트들 간에 익명으로 방송하는 데 사용될 수 있다.

** 인텐트로 이벤트 방송하기 : Intent intent = new Intent(NEW_LIFEFORM_DETECTED); sendBroadcast(intent)

** 브로드캐스트 수신자로 방송에 귀 기울이기 : BroadcastReceiver 클래스를 확장하고 onReceive 이벤트 핸들러를 재정의

*** 애플리케이션 매니페스트에 브로드캐스트 수신자 등록하기 : receiver 태그를 applicaion 노드 내에 추가한다.

*** 코드로 브로드캐스트 수신자 등록하기 : registerReceiver(r, filter);

** 네이티브 안드로이드 브로드캐스트 액션(ACTION_BOOT_COMPLETED, ACTION_CAMERA_BUTTON, ACTION_DATE_CHANGED & ACTION_TIME_CHANGED, ACTION_GTALK_SERVICE_CNNECTED & ACTION_GTALK_SERVICE_DISCONNECTED, ACTION_MEDIA_BUTTON, ACTION_MEDIA_EJECT, ACTION_MEDIA_MOUNTED & ACTION_MEDIA_UNMOUNTED, ACTION_SCREEN_OFF & ACTION_SCREEN_ON, ACTION_TIMEZONE_CHANGED)


- 어댑터 소개

* 어댑터는 데이터를 사용자 인터페이스 뷰와 바인드하는 브리징 클래스이다.

* 안드로이드가 제공하는 어댑터 몇가지(ArrayAdapter, SimpleCursorAdapter)

* 어댑터를 이용한 데이터 바인딩 : 어댑터 인스턴스를 뷰의 setAdapter 메서드에 전달해 호출한다.

** 할 일 목록 ArrayAdapter 커스터마이징 1. ToDoItem 클래스 생성. 항목 데이터의 요약을 리턴하도록 toString메서드 재정의 2. ArrauListArrayAdapter 변수타입을 문자열 대신 ToDoItem의 객체를 저장하도록 수정, onKeyListener핸들러를 업데이트 4. 커스텀 레이아웃을 두 번째 TextView를 포함하도록 수정 5. ArrayAdapter의 변형을 호가장하는 새로운 클래스 ToDoItemAdapter 생성 getView재정의해 ToDoItem 객체에 있는 작업과 날짜 프로퍼티를 레이아웃 내의 뷰에 할당 6. ArrayAdapter 선언을 ToDoItemAdapter 로 바꿈

** SimpleCursorAdapter 사용하기 : SimpleCursorAdapterCursor로부터 열을 커스텀 레이아웃 정의를 사용하는 리스트 뷰에 바인드 할 수 있도록 해준다. SimpleCursorAdapter는 현재 컨텍스트,레이아웃 리소스, 커서, 그리고 사용할 열들의 이름을 담은 배열과 그에 대응하는 열의 데이터 값을 표시하기 위해 사용할 뷰의 리소스 ID를 담은 배열을 전달함으로써 생성된다.


- 인터넷 리소스 사용하기

* 인터넷 네이티브 애플리케이션을 만드는 데의 이점 :대역폭(데이트 업데이트 시에만 대역폭을 사용하도록 제한 가능), 캐싱(살아있는 연결 없이도 가능한 많은 기능을 제공하기 위해 데이터를 캐시 가능), 네이티브 기능(풍부한 사용자 경험 제공),

* 인터넷 연결을 위한 세가지 연결 기법(GPRS, EDGE,그리고 3G), Wi-Fi

* 인터넷 리소스에 접속하기 <uses-permission android:name ="android.permission.INTERNET"/>

* 인터넷 리소스 이용 극대화하기 : 전송되는 데이터량을 제한해 사용자 경험을 최적화하고, 애플리케이션이 네트워크 정전과 대역폭 제한을 충분히 잘 다룰 수 있도록 견고하게 만들자.


- 다이얼로그 소개

* 안드로이드 다이얼로그는 자신을 띄운 액티비티를 부분적으로 가리는 부동 윈도우floating window.

* 다이얼로그 구현방법 : 다이얼로그 계열 클래스 사용, 다이얼로그 테마가 적용된 액티비티, 토스트

* Dialog 클래스 : Dialog d = new Dialog(MyActivity.this); d.setTitle("다이얼로그 제목"); d.setContentView(R.layout.dialog_view); d.show();

** AlertDialog 클래스 :사용자에게 여러 개의 버튼 중 하나만 선택할 수 있는 형태로 보통 OK,Cancel, 그리고 YesNo로 이루어진 버튼

** 전문 입력 다이얼로그 (DatePickerDialog, TimePickerDialog, ProgressDialog) : 사용자 입력을 위한 인터페이스를 제공

** 다이얼로그 사용과 관리 : onCreateDialog, onPrepareDialog 이벤트 핸들러

* 액티비티를 다이얼로그로 사용하기 :  매니페스트에 추가 할 때 android:style/Theme.Dialog 테마를 적용


- 지진 뷰어 만들기

1. Earthquake 액티비티 생성. main. xml레이아웃 리소스를 수정(리스트 뷰 컨트롤 하나 추가) 2.새 클래스 Quake 생성(지진별 세부정보를 저장하는 데 사용), toString 메서드 재정의 해 리스트 뷰 안에 있는 각 지진을 위해 사용될 문자열 제공 3.Earthquake액티비티에서 onCreate 메서드 재정의(Quake 객체들의 ArrayList를 저장, ArrayAdater 이용해 ListView에 바인드) 4.지진 피드 처리 5. 인터넷 접근 권한 부여(매니페스트에 uses-permission) 6.Earthquake 액티비티에 지진 피드에 접속하고 이를 파싱하는 refresh Earthquake메서드 새엉. 각각의 지진 정보를 추출한 뒤 이를 파싱해 날짜,진도,링크,위치를 얻음. 각각의 지진 정보 파싱을 마치고 나면,이를 새로운 addNewQuake 메서드에 전달 7.새로 처리된 각각의 지진 정보를 받아 지진 ArrayList에 추가하도록 addNewQuake메서드를 업데이트, 8. 시작 시 onCreate 메서드가 refreshEarthquakes를 호출하도록 수정 9.프로젝트 실행 10. 사용자가 필요 시 지진 피드를 새롭게 갱신할 수 있도록 하는 새로운 메뉴 항목 만들기 10.1 해당 메뉴 옵션을 위한 새로운 외부 문자열 추가 10.2 onCreateOptionMenuonOptionItemSelected 메서드를 재정의 해 지진 정보 갱신 메뉴 항목을 표시하고 처리 11. 사용자가 리스트에서 지진 정보를 선택할 때,다이얼로그 박스를 열어 그에 대한 세부 정보를 살펴볼 수 있도록 하기 11.1 항목이 클릭될 때 표시할 다이얼로그 박스를 위해 새로운 quake_details.xml레이아웃 리소스를 만듬 11.2지진 항목이 선택될 때마다 다이얼로그 박스를 표시하는 ItemClickListener를 리스트 뷰에 추가하도록 onCreate 메서드를 수정 11.3 onCreateDialogonPrePareDialog메서드를 재정의해, 지진 세부 정보 다이얼로그를 만들고 그 안에 내용을 채움 11.4 USGS에 대한 링크를 하이퍼링크로 만들기 위해 다이얼로그를 링키파이 하기. 다이얼로그 박스의 XML레이아웃 리소스 정의가 autolink 속성을 갖도록 조정



ch06데이터 저장, 검색, 그리고 공유

  • 안드로이드의 데이터 저장 기법
    • 공유 환경설정
    • 파일
    • SQLite 데이터베이스
    • 콘텐트 공급자
  • 공유 환경설정
    • 애플리케이션내 컴포넌트내에서만 공유, 다른 애플리케이션은 사용불가
    • SharedPreference.Editor 클래스 이용, 지원 타입 : boolean, int, long, float, String
    • Bundle매개변수 이용: onSaveInstanceState, onCreate 이벤트 사용 (onSaveInstanceState는 액티비티가 종료 호출에 의해서나 사용자가 뒤로 가기버튼을 누름으로서 종료되고 있는 경우가 아닌, 비활성화되는 경우에만 호출된다)
    • 할일 목록 액티비티 상태 저장 예제
    • 지진 뷰어 환경설정 예제
  • 파일
    • FileOutputStream, FileInputStream
    • 애플리케이션간 파일을 공유하기 위해서는 콘텐트 공급자를 이용 해야함
    • Context.MODE_APPEND   Context.MODE_WORLD_READABLE   Context.MODE_WORLD_WRITEABLE
    • 프로젝트내 res/raw 폴더에 파일리소스를 추가, openRawResource 메소드로 사용
    • 파일관리 메소드 : deleteFile, fileList
  • SQLite
    • 관계형 데이터베이스 시스템(RDBMS), 특징 : 오픈소스/표준준수/경량/단일계층, 저장위치 :  /data/data/<package_name>/databases
    • 컬럼의 데이터 타입이 정의 되지 않고 각 컬럼의 행별로 데이터 타입이 정의
    • 데이터베이스 쿼리는 Cursor 객체로 리턴 된다.
    • Cursor 함수
      • moveToFirst : 첫번째 행으로 이동, 쿼리된 데이터가 읽어들일때 항상 실행, 데이터가 없는 경우 false 반환
      • moveToNext : 커서를 다음 행으로 이동
      • moveToPrevious : 커서를 이전 행으로 이동
      • getCount : 쿼리된 데이터의 수
      • getColumnIndexOrThrow : 지정된 컬럼에 대한 인덱스를 반환
      • getColumnName : 열이름 리턴
      • getColumnNames : 모든 열이름을 String 배열로 리턴
      • moveToPosition : 지정한 행으로 이동
      • getPosition : 현재 커서 위치 반환
    • 액티비티내에서 커서를 관리하는 메소드 : startManagingCursor, stopManagingCursor
    • SQLiteOpenHelper : 데이터베이스를 관리하는 클래스,  생성자 / onCreate / onUpgrade 메소드를 재정의
    •  SQLiteOpenHelper 없이 openOrCreateDatabase 메소드를 사용 가능
    • 데이터베이스 설계시 고려사항
      • 파일은 데이터베이스에 저장하지 않고 경로 저장
      • 테이블에 자동증가키 사용(인덱스키로 사용)
    • 데이터베이스 조회 쿼리
      • query 메소드 사용
      • Cursor android.database.sqlite.SQLiteDatabase.query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
        • boolean distinct : 결과 값에 대한 유일한 값만 가져올지에 대한 여부
        • String table : 쿼리할 테이블 명
        • String[] columns : 쿼리대상 열명
        • String selection : 쿼리의 조건절을 구성
        • String[] selectionArgs : selection에 들어간 ? 와일드문자를 대체할 문자열
        • String groupBy : 데이터를 묶을 그룹 조건 정의
        • String having : groupBy 지정된 경우 그룹 조건 정의
        • String orderBy : 결과의 정렬 조건
        • String limit : 결과 값의 행 개수를 제한을 정의
    • 데이터베이스 입력
      • insert 메소드 사용
      • long android.database.sqlite.SQLiteDatabase.insert(String table, String nullColumnHack, ContentValues values)
        • String table : 입력할 테이블명
        • String nullColumnHack : 널값을 대체할 문자
        • ContentValues values : 입력할 데이터 키/값 조합 - void android.content.ContentValues.put(String key, String value)
    • 데이터베이스 수정
      • update 메소드 사용
      • int android.database.sqlite.SQLiteDatabase.update(String table, ContentValues values, String whereClause, String[] whereArgs)
        • String table : 입력할 테이블명
        • ContentValues values : 수정할 데이터 키/값 조합
        • String whereClause : 조건절 구성
        • String[] whereArgs : 조건절에 들어간 ? 와일드문자를 대체할 문자열
    • 데이터베이스 삭제
      • delete 메소드 사용
      • int android.database.sqlite.SQLiteDatabase.delete(String table, String whereClause, String[] whereArgs)
        • String table : 입력할 테이블명
        • String whereClause : 조건절 구성
        • String[] whereArgs : 조건절에 들어간 ? 와일드문자를 대체할 문자열
    • 데이터베이스 연결 닫기 : 액티비디에서 onDestory 메소드에서 데이터베이스를 닫는다
  • 콘텐트 공급자
    • 애플리케이션 간에 데이터를 공유할 수 있도록 하는 인터페이스
    • URI 모델을 이용해 접근
    • ContentResolver 클래스 사용
    • Cursor android.content.ContentResolver.query(Uri uri, String[] projection, String selection, String[]  selectionArgs, String sortOrder)
      • Uri uri : 질의하고자 하는 콘텐트 공급자의 데이터의 URI
      • String[] projection : 질의에 들어갈 컬럼명
      • String selection : 조건절
      • String[]  selectionArgs : 조건절에 들어간 ? 와일드문자를 대체할 문자열
      • String sortOrder : 결과의 정렬 조건
    • Uri android.content.ContentResolver.insert(Uri url, ContentValues values)
    • int android.content.ContentResolver.bulkInsert(Uri url, ContentValues[] values)
    • int android.content.ContentResolver.delete(Uri url, String where, String[] selectionArgs)
    • int android.content.ContentResolver.update(Uri uri, ContentValues values, String where, String[] selectionArgs)
    • 콘텐트 공급자에서 파일 접근 : openOutputStream, openInputStream
  • 네이티브 안드로이드 콘텐트 공급자
    • Browser : 브라우저 콘텐드 공급자를 이용
    • Callog : 착발신 통화 내역
    • Contacts : 주소록
    • MediaStore : 오디오, 비디오, 이미지 등
    • Settings : 설정공급자
    • 콘텐트 공급자 생성 : ContentProvider 클래스 상속을 받아서 구현
    • public static CONTENT_URI 변수 선언
    • 공급자 URI 형식 : content://com.<회사명>.provider.<앱명>/<데이터경로> [/<행번호>]
    • 공급자 등록 : 매니페스트에 authorities 등록

ch7  맵, 지오코딩, 그리고 위치기반 서비스


- 위치기반 서비스 이용하기

- LocationManager : 위치기반 서비스에 대한 고리를 제공

- LocationProvider : 장치의 현재 위치를 결정하는데 사용되는 서로 다른 각각의 위치검색 기술을 표현


- 테스트 공급자를 이용해 에뮬레이터 설정하기

- 에뮬레이터 위치 공금자의 위치 업데이트 하기

- 이클립스 DDMS Location Control을 사용

- Manual : 특정 위도/경도를 지정

- KML : 지점간 경로를 xml 형태로 기록하여 이동경로를 표현 (참고: http://mygeoposition.com/)

- GPX :


- 위치 공급자 선택하기

- getProvider를 호출

String providerName = LocationManager.GPS_PROVIDER;

LocationProvider gpsProvider;

gpsProvider = locationManager.getProvider(providerName);


- 이용할 수 있는 공급자 찾기

- LocationManager.GPS_PROVIDER

- LocationManager.NETWORK_PROVIDER

장치에 이용할 수 있는 공급자 리스트를 얻기 위해서는getProviders를 호출

List<String> providers = locationManager.getProviders(True);


- 요구 기준에 기반해 공급자 찾기

- Criteria Class : 사용자의 요구사항에 적합한 위치공급자의 기준을 명시

setAccuracy(Criteria.ACCURACY_COARSE / Criteria.ACCURACY_FINE) : 정확도

setPowerRequirement(Criteria.POWER_LOW / ) :전력소비량

setAltitudeRequired(true / false) : 고도데이터 사용 유무

setBearingRequired(true / false) :방위데이터 사용 유무 ***확인

setSpeedRequired(true / false) :속도데이터 사용 유무

setCostAllowed(true / false) :비용 들지 여부


- LocationManager.getBestProvider(criteria, {true|false})

요구사항에 가장 부합하는 위치공급자를 리턴.

boolean 현재 사용 가능한 공급자로 결과를 제한


- LocationManager.getProvider(criteria, {true|false})

요구사항에 부합하는 위치공급자를 리스트형태로 리턴.



- 내 위치찾기

LBS(Location Based Services)에 대한 접근은 Location Manager를 이용.

Location Manager 접근위해getSystemService()를 이용해 LOCATION_SERVICE의 인스턴스를 요청


LocationManager locationManager;

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


use-permission Manifast파일에 추가

<use-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

<use-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>


위치정보는 getLastKnownLocation메서드에 Location Provider를 전달함으로써 찾을 수 있다.

String provider = LocationManager.GPS_PROVIDER;

Location location = locationManager.getLastKnownLocation(provider);

* getLastKnownLocation : location provider에 현재위치를 업데이트하도록 요청하지는 않음.

* 리턴받은 location객체는 위도,경도, 방위, 고도, 속도, 위치 수정이 이뤄진 시간 등을 포함할 수 있고 get메소드를 사용해 얻을 수 있다.


- 움직임 추적

- locationManager.requestLocationUpdates(provider, 밀리초, 거리, LocationListener)

위치가 바뀔 때 마다 업데이트된 위치 정보를 얻음.

-locationManager.removeUpdates(LocationListener)

위치정보 업데이트를 중단함.


- 근접 경보 사용하기

사용자가 특정 위치로 이동하거나 그 위치에서 벗어날 때 어플리케이션이 반응하도록하는 기능.

근접 경보 발생시 intent를 생성.보통은 broadcast intent를 생성.

발생시킬 intent를 지정하려면

Intent intent = new Intent(MY_ACTIVITY);

PendingIntent pendingIntent = PendingIntent.getBroadcast(this, -1, intent, 0);


- 지오코더 사용하기

주소와 경도/위도 맵 좌표간의 정보를 서로 변환.

Geocoder class는 두가지 지오코딩 기능을 제공

Forward Geocoding : 특정 주소의 위도와 경도를 찾는다

Reverse Geocoding : 주어진 위도와 경도에 대한 주소를 찾는다


Geocoder goecoder = new Geocoder(getApplicationContext(), Locale.getDefault());


- 역방향 지오코딩

대상 위도와 경도를 지오코더의getFromLocation메소드에 전달,

일치하는 주소 리스트 리턴하거나 없을경우 null리턴.


- 순방향 지오코딩

getFromLocationName메소드 호출.

List<Address> 형태의 결과 값을 리턴.



- 맵기반 액티비티 만들기

- MapView MapActivity소개

MapView : 실제 맵 뷰

MapActivity :맵 뷰를 포함할 수 있는 새로운 activity를 만들기 위해 상속받아야할 class.

Overlay :맵에 주석을 다는데 사용되는 class

MapController : 맵을 제어. 중심 위치와 확대 단계를 설정.

MyLocationOverlay : 현재 위치와 장치의 방향을 표시하는데 사용

ItemizedOverlay, OverlayItem : 둘이 함께 사용되어drawable 및 그와 연관된 텍스트를 이용해 표시되는 map marker를 만듦


- 맵기반 액티비티 만들기

MapActivity를 상속받은 새로운 액티비티 생성.

액티비티에서 보여줄 레이아웃에는 MapView추가. (apiKey를 포함)

도로정보를 화면 출력 시isRoutDisplayed를 재정의.

Manifast 파일

<use-library android:name="com.google.android.maps"/> 추가

<use-permission android:name="android.permission.INTERNET"/> 추가


- 맵뷰 구성하고 사용하기

mapView.setSatellite(true) :위성 뷰

mapView.setStreetView(true) : street

mapView.setTraffic(true) :예상 교통량


map Zoom Controls 사용을 위해

View zoomControls = mapView.getZoomControls();

mapView.addView(zoomControls, MapView.LayoutParams);

mapView.displayZoomControls(true);


- 맵 컨트롤러 사용하기

MapView를 이동/확대 시.

getController를 이용해 MapView Controller 레퍼런스를 얻는다.

MapViewController mc = MapView.getController();


Location객체에 저장된 위도 경도 값을 이용하려면 GeoPoint에 백만단위로 변환한 뒤 저장

Double lat = 37.123 * 1E6;

Double lng = 123.123 * 1E6;

GeoPoint point = new GeoPoint(lat.intValue(), lng.intValue());


MapView 중심을 다시 잡거나 확대하려면

MapController.setCenter(point); //point위치가 화면 중앙에 오도록 설정.

MapController.setZoom(1); : 1~21


새로운 위치로 이동시MapController.animateTo(point)를 이용해 부드러운 화면 이동 처리


- 오버레이 만들고 사용하기

- 새 오버레이 만들기

Overlay를 상속받는 새로운 클래스 생성.

draw메소드를 재정의해 화면에 보여질 데이터 표기.

onTap을 재정의해 사용자 클릭에 반응한다.


- 프로젝션 소개

Projection 클래스는 위도/경도 좌표와 x/y화면 픽셀 좌표간을 바꿀 수 있도록 해준다.

Projection projection = mapView.getProjection();

GeoPoint Point로 혹은 그 반대로 바꾸려면 fromPixel, toPixel메소드 이용


- 오버레이 추가하고 제거하기

MapView는 현재 표시되는 오버레이들의 리스트를 갖고 있다.

getOverlays를 호출해 이에 대한 레퍼런스를 얻는다

List<Overlay> overlays = mapView.getOverlays();


맵뷰에 오버레이를 추가하려면

overlays.add(새로추가할Overlay);

mapView.postInvalidate(); //맵 위의 변경사항을 업데이트


- MyLocationOverlay소개

현재 위치와 방향을 MapView위에 보여주도록 설계된 오버레이

MyLocationOverlay를 사용하려면

List<Overlay> overlays = mapView.getOverlays();

MyLocationOverlay mylocationoverlay = new MyLocationOverlay(this, mapView);

overlays.add(mylocationoverlay);


나침반과 마커 모두를 활성화 하기위해서는

mylocationoverlay.enableCompass();

mylocationoverlay.enableMyLocation(mapView.getMapController());


- ItemizedOverlay OverlayItem소개

MapView에 간단한 마커기능을 제공

맵에 ItemizedOverlay마커레이어 추가하려면

ItemizedOverlay<OverlayItem>을 상속받는 클래스 만든다.

size 메소드를 재정의해 표시할 마커 수 리턴.

createItem 메소드 재정의해 각 마커의 인덱스에 기반한 새로운 OverlayItem생성.

클래스 생성자에서 populate()를 호출해 각 OverlayItem이 생성되도록 만든다.


맵에 ItemizedOverlay를 추가하려면

List<Overlay> overlays = mapView.getOverlay();

MyItemizedOverlay markers = new MyItemizedOverlay(이미지);

overlays.add(markers);



8장 백그라운드에서 작업하기  - 발표: 2010년 03월 3일(수) - 오후 07시 30분 ~




8장 목차


 

사용자 삽입 이미지

  1. Service소개

    1. 서비스 만들고 제어하기
    2. 서비스에 액티비티Binding하기
  2. 백그라운 작업자Thread 이용하기

    1. 새로운 스레드 만들기
    2. GUI 작업을 위한 스레드 동기화(Handler)
  3. Toast만들기

    1. 토스트 만들기
    2. 토스트 커스터마이징 하기
  4. Notification소개

    1. 알림 관리자 소개
    2. 알림 만들기
    3. 알림 발생시키기
    4. 고급 알림 기법 (소리, 진동, 불빛)
    5. 진행중 알림과 강조 알림
  5. Alarm이용하기


주요 용어: Service, Bind, Thread, Handler, Toast, Notification, 소리, 진동, 불빛, Alarm



~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. 서비스 소개


  - 서비스는 백그라운드에서 실행되어지며, 비활성 액티비티보다 더 높은 우선순위를 갖는다.

  - 사용자 입력에 직접 의존하지 않는 동작을 규칙적이며 연속적으로 수행할 때 사용하면 좋다.

  - 서비스의 시작과 종료는 다른서비스, 엑티비티, 브로드캐스트 수신자를 포함한 다른 애플리케이션으로부터 가능하다.

  - 서비스 활용 사례 (별도의 쓰레드를 통해서 사용 가능하다)

     . 파일 다운로드

     . Media Player

  - 안드로이드 소프트웨어 스택내의 서비스 사용 사례

     . Location Manger

     . Media Controller

     . Alarm Manager 등


  • 서비스와 바인딩 서비스의 생명주기



사용자 삽입 이미지





1.1 서비스 만들고 제어하기

사용자 삽입 이미지



    1) 서비스를 상속 받은 MyService class를 만든다.

    2) MyService에서 onCreate(), onStart() 메소드를 재정의해서 자신의 기능을 구현한다.

    3) 새로 만든 Service는 Manifest에 등록한다.

    4) MyController 클래스에서 startService()를 통해서 MyService를 시작한다.

    * 한번 생성된 서비스를 다시 startService()로 실행할 때는 onCreate()는 타지 않고 onStart() 실행됨(상기그림의 회색선)

       또한 여러번 시작한 서비스라 할지라도 종료는 stopService()로 단 한번만으로 종료 된다.



1.<service android:enabled="true" android:name=".MyServic"></service>




암시적 서비스 실행

명시적 서비스 실행

 startService(new Intent(MyService.MY_ACTION);

startService(new Intent(this, MyService.class);

* MyService 클래스에 MY_ACTION을 포함시키고, 인텐트 필더에 MY_ACTION을 공급자로 등록해야 한다.





서비스명을 통한 서비스 종료

명시적 서비스 종료

 ComponentName service

   = startService(new Intent(this, MyService.class);

 stopService(new Intent(this, service.getClass());

try{

  Class serviceClass

    = Class.forName(service.getClassName());

stopService(new Intent(this, serviceClass);

}catch(ClassNotFoundException e){}








바인더에 대해서

Binder는 리눅서 커널에 있는 Binder를 통해서 관리된다.

사용자 삽입 이미지


사용자 삽입 이미지




1) ServiceManager는 BinderDriver 에 대한 special Node 0 로 동작

2) ServiceProvider (예 : AudioFlinger) 는 자신의 RPC에 대한 Interface를 ServiceManager proxy object에 대한 Interface를 통해 등록한다.

  - 이 과정을 통해 BinderDriver에서는 ServiceProvider로의 path를 생성

3) Service User는 사용하려는 Service에 대한 Interface를 생성하는데, 이를 위해 ServiceManager에게 RPC call을 통해 요청한다.

4) ServiceManager는 Bind되어있는 Object들의 목록에서 해당하는 ServiceProvider에 대한 Node를 요청한 ServiceUser에게 return

5) ServiceUser는 return된 Node정보를 통해 해당 Service Provider로의 RPC를 수행함

* 인용) http://andstudy.springnote.com/pages/4314107 / 수원안드로이드플랫폼스터디



서비스에서 바인더를 사용하는 이유:

* 액티비티가 서비스에 바인딩되면, 그 액티비티는 해당 서비스 인스턴스 자체에 대한 레퍼런스를 유지하고 실행중에 서비스에 대해 메서드를 호출할 수 있도록 해 준다.

* 일반 서비스는 MyController 액티비티가 서비스의 동작을 알지 못하게 은밀하게 동작한다면, 바인딩된 서비스는  액티비티가 상태를 파악하고 제어할 수 있도록 해 준다.


* 서비스가 바인드되고 나면, 서비스가 가지고 있는 모든 public 메서드와 프로퍼티는 onServiceConnected핸들러를 통해서 얻어진 serviceBinder객체를 통해서 이용 가능하다.

* 브로드캐스트 인텐트 또는 서비스 시작시 사용되는 인텐스 익스트라 번들을 이용해서 간단히 다른 프로세스로 실행 중인 서비스와 간단히 통신할 수 있다.

* 좀더 단단히 결합된 연결을 원할 경우에는 AIDL을 이용하면 된다.




1.2 서비스에 액티비티 바인딩하기


    서비스 작성 순서

사용자 삽입 이미지



     서비스 동작

사용자 삽입 이미지



예제) Toast를 사용한 Service Bind 예제:PAAD08_service.zip


일반 서비스와 바인드서비스의 사용예

사용자 삽입 이미지 사용자 삽입 이미지

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

2.백그라운 작업자Thread 이용하기


사용자 삽입 이미지


2-1.백그라운드의 thread의 필요성

 - 모든 콤포넌트는 하나의 Main thread위에서 동작한다.

 - 따라서 시간이 많이 걸리는 작업을 하면 화면에 보이는 Activity뿐만아니라 다른 모든 콤포넌트까지 불록시킨다.

 - 이때 "Application Unresponsive" 메시지가 출력될 것이다.


2-2 백그라운드 thread 활용 추천

 - 입력 이벤트(키 눌림)에 대해서 5초 이내에 반응하지 않을 때

 - onReceive 핸들러를 10초 이내에 완료하지 않는 Broadcast Receiver

 - 이런 현상이 발생되는 주요 작업 사항으로는

   . 파일작업

   . 네트워크 조회

   . 데이터베이스 트랜잭션

   . 복잡한 계산


 2-3 백그라운드 스레드 사용의 결론

  - 느리고 시간이 많이 드는 모든 작업을 Main thread에서 Child thread로 옮기는 것이 좋은 습관이다.


 2-4 새로운 thread와 GUI 작업을 위한 thread 동기화

사용자 삽입 이미지



  - 골격 코드



01.// GUI Handler 처리..
02.private Handler handler =new Handler();
03.

04.// 이 메서드는 메인 GUI 스레드에서 호출된다.

05.private void mainProcessing(){
06.// 이는 시간이 많이 드는 작업을 자식 스레드로 옮긴다.
07.Thread thread =new Thread(null, doBackgroundThreadProcessing,"Background");
08.thread.start();
09.}
10.

11.// 백그라운드 처리 메서드를 실행하는 Runnable

12.private Runnable doBackgroundThreadProcessing =new Runnable(){
13.@Override
14.public void run() {
15.backgroundThreadProcessing();
16.}
17.};
18.

19.// 백그라운드에서 몇 가지 처리를 수행하는 메서드

20.private void backgroundThreadProcessing(){
21.//[필요한 코드]
22.// 처리가 끝나고 결과를 UI로 출력을 해야 할 때 아래 핸들러를 추가해서 사용한다.
23.handler.post(doUpdateGUI);
24.}
25.

26.// GUI 업데이트 메서드를 실행하는 Runnable.

27.private Runnable doUpdateGUI =new Runnable(){
28.@Override
29.public void run() {
30.updateGUI();
31.}
32.};
33.

34.private void updateGUI() {

35.// [[ 필요한 UI 코드 ]]
36.}


   Thread를 만드는 핵심 코드

    private void mainProcessing(){
     Thread thread = new Thread(null, doBackgroundThreadProcessing, "Background");
     thread.start();
    }

   =>> Thread(ThreadGroup group, Runnable runnable, String threadName)


   Thread와 GUI연결을 위한 핵심 코드

    handler.post(doUpdateGUI);  

     ==>> post(Runnable r)


  - Handler를 사용하는 이유

   Child Thread는 GUI를 갖고 있지 않다. 모든 GUI는 Main thread가 갖고 있어서 뷰, Toast등은 모두 Main thread에서 처리해야됨

   따라서 handler를 통해서 child thread의 처리 결과를 Main thread에서 처리할 수 있게 해준다.


 - Handler 클래스의 추가 메소드

   . pastDelayed: 포스팅을 늦추어 처리한다. / handler.postDelayed(doUpdateGUI,5000)

   . pastAtTime : 포스팅을 특정 시간에 실행하도록 한다.


 - UIThreadUtilities 클래스

   이 클래스는 runOnUIThread를 갖고 있어서 View, Activity, Dialog와 같은 thread에서 메소드를 강제적으로 실행해 줌


 - Thread에서 꼭 기억해야 할 사항


" 애플리케이션 컴포넌트에서, 알림과 인텐트는 항상 GUI 스레드에서 수신되고 처리된다.
  다른 모든 경우에 있어, GUI스레드(뷰 같은)에서 생성된 객체와 명시적으로 상호작용하거나 메시지를 표시하는 작업은
  메인 스레드에서 띄워져야만 한다."


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


3.Toast 만들기

 

사용자 삽입 이미지
======>
사용자 삽입 이미지
 



토스트란?

- 형태: 몇 초간만 화면에 보였다가 페이드 아웃되는 일시적 다이얼로그.    

- 특징: 포커스를 훔치지 않으며 비 모달로 활성화 애플리케이션에 방해를 주지 않는다.

- 용도: 앱에 방해주지 않고 정보를 사용자에게 쉽게 제공한다.


원형


1.Toast.makeText(this,"10분 남았습니다",1000).show();
2.public static Toast makeText (Context context, CharSequence text,int
3.duration)

 - makeText: 표준 토스트 표시 윈도우를 생성하는 static 메서드

   > context: 애플리케이션 컨텍스트

   > text : 표시할 텍스트 메시지

   > duration : 다이얼로그가 표시될 시간

 - show() : 이것을 통해서 토스트를 화면에 진정으로 표시하게 된다.


  • 위치변경: mToast.setGravity(Gravity.CENTER, offesetX, offsetY); mtoast.show();

사용자 삽입 이미지


- 커스텀View를 통해 모양변경: mToast.setView(customLayout);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


4.Notification 소개


용도

- 액티비티를 사용하지 않고 사용자에게 알리는 기능

- notification manager를 사용함

- 화면이 보이지 않는 앱 콤포넌트에서 주의 이벤트를 발생하는데 선호하는 방식이다.

기능

  > 새로운 상태바 아이콘 만들기

  > 확장 상태바 윈도우에서 추가 정보표시  (인테트 사용)

  > 불빛/LED 동작

  > 진동 알림

  > 가청 경보 울림


Notification Manager / 시스템서비스

- 레퍼런스는 콘텍스트의 serviceName을 인자로 사용하는 getSystemService()메소드를 통해서 얻는다.

- 용도: 새로운 알림을 만듬, 기존 알림을 수정할 수 있음, 기존 알림을 제거할 수 있음



사용방법

사용자 삽입 이미지


* 상기 내용중 noti.FLAG_SHOW_LIGHTS => Notification.FLAG_SHOW_LIGHTS로 정정함



01.String svcName = Context.NOTIFICATION_SERVICE;
02.NotificationManager notificationManager;
03.notificationManager = (NotificationManager)getSystemService(svcName);
04.

05.// 상태바 구성

06.int icon = R.drawable.icon;
07.String tickerText ="알림"
08.long when = System.currentTimeMillis();
09.Notification noti =new Notification(icon, tickerText, when);
10.

11.// 확장 상태바 윈도우 구성

12.Context context = getApplicationContext();
13.String expandedText ="확장 상태 텍스트";
14.String expandedTitle ="알림 제목";
15.Intent intent =new Intent(this, MyActivity.class);
16.PendingIntent launchIntent = PendingIntent.getActivity(context,0, intent,0);
17.noti.setLatestEventInfo(context, expandedTitle, expandedText, lauchIntent);
18.

19.// 상태바 반복 숫자 표시 구성

20.noti.number++;
21.

22.// 사운드 효과 구성

23.noti.sound = ringURI;
24.

25.// 진동 효과 구성

26.long[] vibrate =new long[] = {1000,1000,1000,1000,1000};
27.noti.vibrate = vibrate;
28.

29.// LED 효과 구성

30.noti.ledARGB = color.RED;
31.noti.ledOffMS =0;
32.noti.ledOnMS =1;
33.noti.flags = noti.flags | Notification.FLAG_SHOW_LIGHTS;
34.

35.// 확장 상태바 윈도우에 진행중 또는 강조 구성

36.noti.flags = noti.flas | Notification.FLAG_ONGOING_EVENT;
37.//noti.flags = noti.flas | Notification.FLAG_INSISTENT;
38.

39.// 상기 구성 내용 알림 발생 레퍼런스ID관리 필요

40.int notificationRef =1;
41.noticationManager.notify(notificationRef, noti);
42.

43.// 해당 레퍼런스 ID의 알림 취소, 상태바, 확장 상태바 윈도우 내용 삭제됨 단, 효과는 진행됨

44.noticationManager.cancel(notificationRef);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5. Alarm 소개



- 알람은 미리 예정된 시간에 인텐트를 발생키는 애플리케이션 독립적인 방법

- 애플리케이션 범위 밖에서 설정 가능BroadCast Receiver와 함께 Alarm이 조합 될 때 더욱 강력한 위력을 발휘한다.

  (BroadCast Receiver의 BOOT_COMPLETED를 설정하면 전원 재부팅시에 알람을 새로 시작시킬 수도 있다.)

- 활용: 애플리케이션을 실행시킴, 액션을 수행하는 알람을 설정할 수 있음
   > 알람 시계

   > 규칙적으로 네트워크 조회

   > 시간, 비용이 많이 드는 작업을 유후때 실행할 수 있도록 예약 할 수 있다.

- 설정의 유지 및 해제

  > 절전모드 일때도 알람은 활성화 되어 있다. (RTC, 백업배터리)

  > 절전모드에서 알람이 실행되어야 할때 WakeUp 여부를 선택할 수 있도록 설정할 수 있다.

  > 모든 알람은 전원 재부팅시 모두 소멸된다.


* 애플리케이션이 실행되어 있을 때는 알람을 사용하는 것보다는 Timer와 Thread를 사용하는 것을 권장한다.

  이것은 시스템 리소스를 더욱 잘 제어할 수 있기 때문이다.


- 핵심코드


1.alarms.set(alamType, timeOrLenghtOfWait, pendingIntent):

 > alarmType: 알람타입

 > timeOrLengthOfWait: 트리거시간

 > pendingIntent: 트기거될 때 발생시킬 대기 인텐트


- AlarmType: 트리거 시간의 종류가 wakeup여부에 따라서 4종류가 있다.

   > RTC

   > RTC_WAKEUP

   > ELAPSED_REALTIME

   > ELAPSED_REALTIME_WAKEUP


사용자 삽입 이미지

01.AlarmManager alarms = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
02.

03.String MY_RTC_ALARM = “MY_RTC_ALARM”;

04.String ALARM_ACTION = “MY_ELAPSED_ALARM”;
05.

06.PendingIntent rtcIntent = PendingIntent.getBroadcast(this,0,new Intent(MY_RTC_ALARM),1);

07.PendingIntent elapsedIntent = PendingIntent.getBroadcast(this,0,new Intent(ALARM_ACTION),1);
08.

09.// Wakeup and fire intent in 5 hours.

10.Date t =new Date();t.setTime(java.lang.System.currentTimeMillis() +60*1000*5);
11.alarms.set(AlarmManager.RTC_WAKEUP, t.getTime(), rtcIntent);
12.

13.// Fire intent in 30 mins if already awake.

14.alarms.set(AlarmManager.ELAPSED_REALTIME,30*60*1000, elapsedIntent);
15.

16.// Cancel the first alarm.

17.alarms.cancel(rtcIntent);

알람을 반복해서 발생시키기

alarms.setRepeating(
AlarmManager.RTC_WAKEUP, //AlarmType,       특정시간에 깨워서라도 실행 시키고자 할때 그냥 하려면 RTC
System.currentTimeMillis(),     //triggerAtTime,   RTC로 하면 시간단이가 달라진다.  
1000,                                 //interval               알람 울림 간격            

pi                                     //PendingIntent            

);


상기의 것을 사용할 경우 너무 자주 깨어나면 아래 것을 사용한다.

 alarms.setInexactRepeating(type, triggerAtTime, interval, operation)
 - 정해진 시간에 실행되고 작업 수행시 종료된다.

 - 정해진 시간은 AlarmManager.INTERVAL_FIFTEEN_MINUTES을 거의 사용한다.






PAAD 11장 고급 안드로이드 개발  - 발표: 2010년 03월 10일(수) - 오후 07시 30분

11장 목차


  1. 편집증 환자, 안드로이드

    1. 리눅스 커널 보안
    2. 권한 소개 (Permission)
    3. 권한 선언과 적용하기
    4. 인텐트 방송에 권한 적용하기
  2. AIDL을 이용해 서비스를 위한 IPC 지원하기

    1. AIDL 인터페이스 구현하기

      1. 커스텀 클래스 객체 전달하기
      2. AIDL 정의 만들기
      3. IPC 인터페이스 구현하고 노출하기
  3. 인터넷 서비스 이용하기
  4. 리치 유저 인터페이스 구축하기

    1. 애니메이션으로 작업하기 (tweendanimation, frame by frame)

      1. 트윈드애니메이션 (소개, 만들기, 적용하기, 리스너)
      2. 레이아웃 뷰 그룹 애니메이션 (만들기, 이용하기)
      3. 프레임 바이 프레임 만들고 이용하기
    2. 애플리케이션에 테마로 스킨 입히기
    3. 고급 캔버스 그리기

      1. 캔버스
      2. 페인트 활용
      3. 투명 이용하기
      4. 셰이더 소개
      5. 그레이디언드 셰이더 정의하기
      6. 셰이더 타일 모드 이용하기
      7. MaskFilter 이용하기
      8. ColorFilter 이용하기
      9. PathEffect 이용하기
      10. Xfermode 바꾸기
      11. 앤티앨리어싱으로 페인트 품질 높이기
      12. 2D 그래픽스를 위한 하드웨어 가속
      13. 캔버스 드로잉 베스트 프랙티스
    4. 오버레이에생명력 불어 넣기
    5. SurfaceView소개

      1. 언제 SurfaceView을 이용해야 하나?
      2. 새로운 SurfaceView 컨트롤 만들기
      3. SurfaceView로 3D 컨트롤 만들기
  5. 인터랙티브한 컨트롤 만들기

    1. 터치 스크린 이용하기

      1. 터치 이벤트 처리
      2. 움직임 추적하기
    2. 장치 키와 버튼 이용하기 (D패드 포함)
    3. 트랙볼 이용하기


주요 용어: Permission, AIDL, tween Animation, layoutview group animation, thema, Canvas, SurfaceView, touch event, overlay
1. 편집증환자 안드로이드 / 권한 - Permission

1) 리눅스 커널 보안

샌드박스내의 리소스

01_sandbox.PNG 

안드로이드의 보안

 - 리눅스 커널에 기반한 보안 수준

 - 패키지 설치시 User ID부여. 이것으로 자기 영역을 부여 받고 보호 받는다.

 - 상호 프로세스, 리소스에 대한 사용 제한이 따르게 된다.


어플리케이션간 통신

 - 안드로이드 샌드박스로 격리된 프로세스간에 통신 기능을 제공한다.

 - BroadCast Intent, Service, Content Provider, AIDL이 이런 역할을 수행한다.


안드로이드 권한(Permission)

 - 통신 기능의 내용을 제어하기 위해서 시작과 끝점에서 Permission

을 점검하는 기능이 부여 되어 있다.


Permission이 점검 당하는 시점

- 시스템 내부로의 호출 시점

- 액티비티 시작 시점

- 브로드캐스트 보내는 시점

- 켄텐츠프로바이더 접근 시점

- 서비스 바인딩 시점 또는 시작 시점


 - 점검결과로 퍼미션에 적합하면 동작되고, 부적합하면 실행되지 않고, 알림도 없다 단지 log로는 남는다.

   부적합시에 일부 Exception이 발생될 수 있다.

2) 권한 - Permissions

2-1) 권한의 소개

사용자 삽입 이미지


데이터를 손상시키는 악의적인 애플리케이션, 민감한 정보에 대한 접근, 외부 통신 채널 또는 하드웨어 리소스 남용을 예방하는데 사용됨

- Native Component는 권한 요구사항을 갖는다.

  > android.manifest.permission 에서 네이티브 권한 문자열을 확인할 수있다. (앱에 별도로 지정된 것은 확인 안됨)

  > 이런 권한이 정의된 네이티브 컨포너트는 <uses-permission>을 사용해서 접근할 수 있다.


사용자 삽입 이미지

- androidmanifest.xml에 기술된 권하은 애플리케이션 설치 시점에 분석 표시 되며 설치 승인 및 거부를 할 수 있다.  

- 설치 시점에 권한 확인을 하고 나면 이후로는 해당 권한은 재평가 되지는 않는다.


사용자 삽입 이미지



2-2) 권한의 선언, 적용

다른 패키지들이 내 패키지내의 콤포넌트를 접근 제어하고자 할때 권한을 선언해서 사용할 수 있다.

권한 선언:

permission 태그를 사용해서 선언

사용자 삽입 이미지

  



   <permission

android:name=”com.paad.DETONATE_DEVICE”

android:protectionLevel=”dangerous”

android:label=”Self Destruct”

android:description=”@string/detonate_description”>

   </permission>


protectionLevel속성: 이 레벨을 설정에 따라서 사용자가 퍼미션을 요구하는 애플리케이션 알수 있거나,

해당 퍼미션이 누가 사용하는지 알수 있게 해 준다.

허가할 권한의 접근 수준: normal, dangerous, signature, signatureOrSystme

permissionGroup속성:

 선택 사항으로 단지 시스템이 사용자에게 퍼미션 표시를 돕기 위해서 사용된다.

 표준 시스템 그룹에서 정의한 사항을 표기하는 것이 좋다.

 직접 정의도 가능하다. 그렇지만 사용자에게 퍼미션UI를 단순화 하기 위해서 표준것을 사용하는 것이

좋다.



Lavel속성, Description속성:

 퍼미션 정의에 필수 사항이다.

 Lavel: 퍼미션 리스트에 나타나고, 퍼미션이 보호하는 기능의 핵심부분을 몇개의 단어로 기술할 것

 Desc..: 퍼미션 상세보기에 나타나고, 두문장으로 기술하는데

          첫번째문장은 퍼미션의 설명,

           두번째는 퍼미션 부여시 발생될 수 있는 나쁜 것이 무엇인지를 사용자에게 경고해 주는 내용을 기술한다.




아래 adb명령을 통해서 시스템의 퍼미션 정보를 볼 수 있다.


adb shell pm list permissions

 이 명령을 통해서 시스템에 현재 정의된 퍼미션들을 볼 수 있다.


adb shell pm list permissions -s

 -s 옵션은 사용자가 보는 방식과 같은 방식으로 볼 수 있다.

 




권한 적용:

권한 적용은 AndroidManifest.xml의 각 컴포넌트 태그내에 android:permission속성을 기술하면된다.

- Activity: 권한을 추가해 액티비티를 띄우는 다른 애플리케이션의 능력을 제어한다.

- BroadCast Receiver: 여러분의 수신자로 어떤 애플리케이션이 브로드캐스트 인텐트를 전송할 수 있는지 제어한다.

  > sendBroadcast(myIntnet, REQUIRED_PERMISSION);

- Contents Provider :  콘텐츠 공급자의 읽기 접근과 쓰기 작업을 제어한다.

   > android:readPermission, android:writePermission

  > URI퍼미션: 액티비티에게 그 인텐트 내의 특정 데이터 URI에 대한 접근 퍼미션을 부여한다.

- Service : 서비스 시작하거나 바인드하는 다른 애플리케이션의 능력을 제어한다.


   <activity

android:name=”.MyActivity”

android:label=”@string/app_name”

android:permission=”com.paad.DETONATE_DEVICE”>  //<== 나의 컴포넌트에 permission 속성과 권한 문자열 지정

   </activity>



그밖에 퍼미션 관련 기능

(해당 퍼미션이 부여되어 있는지를 정수 값으로 리턴해 준다.) - Context.checkCallingPermission()

   서비스를 호출할때 요구되는 퍼미션 문자열을 사용해서 호출한다.

- Context.checkPermission(String, int, int)

   이것은 다른 프로세스에 대해서 PID를 사용해서 그 프로세스의 퍼미션을 점검할 수 있다.
- PackageManager.checkPermission(String, String)
   앱의 패키지명을 알고 있을 때 사용한다.

2. AIDL을 이용해 서비스를 위한 IPC 지원하기

AIDL : Android Interface Definition Language

  •  서비스와 애플리케이션 콤포넌트간에 프로세스 간의 통신(IPC)을 지원하기 위한 안드로이드 인터페이스 정의 언어이다.
  •  이것은 Remote Call Procedure(RPC)로 COM이나 Corba와 비슷하다.
  •  Client와 Server사이의 값을 전달하기 위해 proxy클래스를 사용한다.
  •  두개의 프로세스간 통신 할 수 있도록 코드를 생성할 때 사용되는 IDL언어로 Java에도 IDL이 있고

       JDK의  jdk/bin/idl.exe을 통해서 인터페이스를 해석해 준다.

       이것을 Android에서는 AIDL로 정의하고 AIDL Tool을 제공한다.  

원리:

- IPC를 통해서 객체를 전달하려면 OS 수준의 기본요소(Primitive)로 해체할 필요가 있다.

  이렇게 해야지 하부 운영체제(OS)가 이를 애플리케이션의 경계 너머로 전달 할 수 있다.

- AIDL은 프로세스가 객체를 주고 받을 수 있도록 하는 코드를 단순화하는데 이용된다.

- 이 것은 프로세스간에 객체 매개변수를 받아 들이고 리턴하며,  

   값을 리턴할 수 있는 Public 메소드를 나의 서비스 내에 만들 수 있도록 해 준다.


AIDL 문법

- AIDL이 지원하는 Data Type

  • Primitive Java Data Type(import 불필요)
  • String, List, Map, CharSequence (import 불필요)
  • 다른 AIDL-generated interface (반드시 import 필요)
  • Parcelable protocol 을 구현한 클래스 (반드시 import필요)

- method : 매개변수가 0개 이상 / return 값: void or value

- 매개변수: primitive 값 또는 다른 AIDL로 정의된 parameter

- 매개변수 direction : 매개변수가 값 타입인지, 레퍼런스 타입인지 나타내기 위해 방향 태그가 필요함

                                  in / out / inout (primitive를 제외하고는 반드시 표기 필요 primitive default는 in임)

- 같은 패키지내 인터페이스라 하더라도 반드시 import를 해 줘야 한다.


커스텀 클래스 객체 전달하기

Parcelable인터페이스를 통해서 내 애플리케이션 클래스를 AIDL과 호환되도록 만든다.

- 객체를 프로세스간 경계를 넘어 마셜링될 수 있는 parcel내에 저장되는 기본 타임으로 분해 할 수 있도록 해준다.


작성 방법

 1) Parcelable객체를 상속받는 내 클래서를 만든다.

 2) writeToParcel메소드를 구현한다. : 클래스를 분해를 위해서 필요

 3) Parcelable.Creator객체 생성 : 들어오는 Parcel에 기반한 새로운 객체를 생성할 Public static필드

 4) describeContents()

 5) AIDL 정의 : 내 서비스에 대한 AIDL인터페이스 정의할 때 이용하도록 하기 위함

    (parcelable Quke; )


AIDL 정의 만들기

- aidl파일: 프로세스를 넘나들 수 있는 서비스를 위해서 정의하며, 서비스가 구현할 인터페이스에 포함될 메서드와 필드를 정의한다.

- AIDL문법에 준수해서 내용을 작성한다.

- 주의 사항으로는 매개변수 마샬링에는 비용이 많이들기에 각 매개변수의 방향을 제한해야 한다.


파일: IEarthquakeService.aidl

package com.paad.earthquake;

import com.paad.earthquake.Quake;

interface IEarthquakeService {

List<Quake> getEarthquakes();

void refreshEarthquakes();

}



서비스에서 IPC 인터페이스 구현하고 노출하기

- ADT플러그인은 .aidl파일이 저장되면 "자바 interface파일 코드"가 자동 생성되어진다.
- 이 인터페이스는 내부클래스인 stub을 포함하는데 추상클래스로 우리가 만드는 서비스에서 확장애서 실젱 작업할 내용을 넣어 놓는다.
- 구현된 기능과 인터페이스는 클라이언트에 정보를 노출해야 한다.
   이 방법은 해당 인터페이스의 인스턴스를 리턴하도록 onBind메서드에 재정의함으로 가능하다.

액티비티에서 IPC 서비스 이용하기

- IPC서비스를 바인드한다.
- ServiceConnection 클래스를 포함해서 onServiceConnected 메소드를 재정해서 사용한다.

AIDL 프로젝트의 클래스 구성도


사용자 삽입 이미지



AIDLServiceProject 생성

   (1) IMyService.aidl정의

   (2) AIDL툴에 의해 코드 생성 확인

   (3) IMyService.Stub클래스에서 Remote Service메소드 정의

   (4) MyService생성

        AndroidManifest.xml에 MyService를 등록

        AIDLClientProject 생성

   (5) IMyService.aidl정의

   (6) AIDL툴에 의해서 코드 생성 확인

   (7) ServiceConnection클래스 정의

   (8) Service Client 정의의


실행순서

   1) AIDLService 실행

   2) AIDLClient 실행


3. 인터넷 서비스 이용하기

  • 구글의 g데이터 서비스

  • 야휴! 파이프
  • 구글 앱 엔진
  • 아마존 웹 서비스

4. 리치 인터페이스 구축하기

4.1.1 트윈드에니메이션

소개:

 - 최소의 리소스 비용으로 깊이나 움직임 또는 피드백을 제공하기 위한 간단한 방법을 제공한다.

  (직접 캔버스에 다시 그리는 것에 비해 훨씬 쉽고 다소 리소스가 적게든다.)

 - 단일 리소스에 대해서 복수개의 인스턴스를 취급할때 여러 상태를 가질 수 있다.

  (Drawable을 사용할때는 한쪽의 인스턴스에 리소스의 상태 변형하면 다른쪽 인스턴스로 영향을 받음)


제공 가능한 효과:

 - 일련의 방향

 - 크기의 변화

 - 위치 이동

 - 불투명 변화


사용예:

 - 액티비티 간 전환

 - 액티비티 내에 있는 레이아웃 간 전화

 - 같은 뷰 내에 표시된 서로 다른 콘텐트 간 전환

 - 진행 상태를 표시하기 위한 모래시계 뷰 회전

 - 부정확하거나 유효하지 않은 데이터 입력을 표시하기 위한 입력 박스 흔들기


트윈드 애니메이션 만들기

사용자 삽입 이미지

 - 코드 방식


애니메이션시퀀스 (애니메이션, 애니메이션셋 모두 사용가능)

 - .setRepeatMode(): 값으로 RESTART(반복), REVERSE(앞뒤로 왔다 갔다)

 - .setRepeatCount(): 반복하고자 하는 숫자, 또는 INFINITE

 * 이 메소드를 사용하지 않을 경우 기본값으로 한 번 실행됨


 - 외부 리소스 방식

사용자 삽입 이미지


 * xml 파일 위치: res\anim\

 코드


 코드 방식  외부 리소스 방식
 

   // ---- Tweened animations

    // Create the AnimationSet

    animationSet = new AnimationSet(true);


    // Create a rotate animation.

    RotateAnimationrotate = new RotateAnimation(

           0, 360,                                                      RotateAnimation.RELATIVE_TO_SELF,0.5f,

           RotateAnimation.RELATIVE_TO_SELF,0.5f);

    rotate.setFillAfter(true);

    rotate.setDuration(1000);


    // Create a scale animation

    ScaleAnimationscale = new ScaleAnimation(

            1, 0,                                                         1, 0,                                                         ScaleAnimation.RELATIVE_TO_SELF, 0.5f,                        ScaleAnimation.RELATIVE_TO_SELF, 0.5f);

   scale.setFillAfter(true);

   scale.setDuration(500);

   scale.setStartOffset(500);


    // Create an alpha animation

    AlphaAnimationalpha = new AlphaAnimation(1, 0);

   scale.setFillAfter(true);

   scale.setDuration(500);

    scale.setStartOffset(500);


    // Add each animation to the set

    animationSet.addAnimation(rotate);

   animationSet.addAnimation(scale);

   animationSet.addAnimation(alpha);


    animationSet.setRepeatMode(Animation.RESTART);

    animationSet.setRepeatCount(Animation.INFINITE);

    myView.startAnimation(animationSet);  

 

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

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

  android:shareInterpolator="true">

  <rotate

   android:fromDegrees="0"

   android:toDegrees="360"

   android:pivotX="50%"

   android:pivotY="50%"

   android:startOffset="0"

   android:duration="1000"

  />

  <scale

   android:fromXScale="1.0"

   android:toXScale="0.0"

   android:fromYScale="1.0"

   android:toYScale="0.0"

   android:pivotX="50%"

   android:pivotY="50%"

   android:startOffset="500"

   android:duration="500"

  />

  <alpha

   android:fromAlpha="1.0"

   android:toAlpha="0.0"

   android:startOffset="500"

   android:duration="500"

  />

</set>


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ImageView myView;
= (ImageView) findViewById(R.id.myViewId);
AnimationmyAnimation
= AnimationUtils.loadAnimation(this, R.anim.myXML);
myView.startAnimation(myAnimation);
 

style="line-height: 2em;">



애니메이션 리스너 이용하기 (애니메이션, 애니메이션셋 모두 사용가능)

 - 애니메이션이 시작하거나 끝날 때 호출 되는 이벤트 핸들러를 만들도록 해준다.

 - .setAnimationListener(new AnimationListener(){}

   > onAnimationEnd()    : 완료한 후

   > onAnimationRepeat() : 반복 시작 때

   > onAnimationStart()  : 새로 시작할 때



4.1.2 Layout과 View Animation만들기

소개

LayoutAnimation

 - View Group을 애니메니션하고 미리 결정된 순서에 따라서 각 자식 View 애니메이션(애니메이션셋)을 적용에 이용됨

LayoutAnimationController클래스: View Group에 있는 자식 View에 적용된 애니메이션을 지정한다.

 - LayoutAnimationController: 각 View의 시작 Offset값과 자식 View의 애니메이션 순서을 선택할 수 있다.

   (순서: Animation효과가 적용될 자식View순서 값: forward, reverse, random)

 - GridLayoutAnimationController: 격자 행과 열 레퍼런스를 이용해 자신 뷰의 애니메니션 순서를 지정


LayoutAnimation만들기

- 외부 리소스 방식

사용자 삽입 이미지


- 코드 방식

사용자 삽입 이미지


* .scheduleLayoutAnimation(): 강제적으로 ViewGroup을 다시 실행한다. (단 layoutAnimation이 설정되어 있어야 효과가있다.)

  ?? 이 애니메이션은 ViewGroup이 다음 번 배치될 때 실행 될 것이다. ?? 뭔소리??


LayoutAnimationListener

 - ViewGroup에 객체에 리스너를 부착할 수 있다. (콜백함수는 Animation리스너와 동일하다.)


코드


<!-- popin.xml-->

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

  android:interpolator="@android:anim/accelerate_interpolator">

  <scale

   android:fromXScale="0.0" android:toXScale="1.0"

   android:fromYScale="0.0" android:toYScale="1.0"

   android:pivotX="50%"    

   android:pivotY="50%"

   android:duration="400"

  />

</set>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

<!-- popinlayout.xml-->

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

  android:delay="0.5"

  android:animationOrder="reverse"

  android:animation="@anim/popin"

 />

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

<!-- main.xml-->

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

<LinearLayout

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

  android:id="@+id/layout"

  android:orientation="vertical"

  android:layout_width="fill_parent"

  android:layout_height="fill_parent"

  android:layoutAnimation="@anim/popinlayout"

  >

 ...

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ViewGroup을 사용한 코드 방식

 LayoutAnimationControllerlayoutAni;

 layoutAni = AnimationUtils.loadLayoutAnimation(MyActivity.this, R.anim.popinlayout);

 viewGroup.setLayoutAnimation(layoutAni);




4.1.3 프레임 바이 프레임 애니메이션 만들고 이용하기

소개

- 프레임마다 이미지가 선택된 전통적인 셀기반 만화영화 기법과 가깝다.

- 프레임마다 백그라운드으로 사용되는 일련의 Drawable객체를 지정하고, 이것을 애니메이션 처리한다.


만들기

- XML draw 파일 만들기

  Item 태그를 사용해서 애니메이션될 실제 이미지와 표시될 시간을 정의한다.

  <item android:drawble="@drawable/rocekt1" android:duration="500"/>

  이것을 그룹화 animation-list 태그를 갖이 사용한다.

- .setBackgroudResource()를 사용해서 ImageView객체와 애니메이션(XML)을 배경으로 설정한다.

- .setBackgroudDrawable()을 사용해서 리소스 레퍼런스 대신 드로어블 인스턴스를 사용한다.

- 애니메이션의 start()메소드를 사용해서 애니메이션을 실행한다.



코드


<!-- animated_rocket.xml-->

<animation-list xmlns:android=”http://schemas.android.com/apk/res/android

android:oneshot=”false”>

<item android:drawable=”@drawable/rocket1” android:duration=”500” />

<item android:drawable=”@drawable/rocket2” android:duration=”500” />

<item android:drawable=”@drawable/rocket3” android:duration=”500” />

</animation-list>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ImageView image = (ImageView)findViewById(R.id.my_animation_frame);

image.setBackgroundResource(R.drawable.animated_rocket);

AnimationDrawable animation =
(AnimationDrawable)image.getBackground(); animation.start();


style="line-height: 2em;">

4.2 애플리케이션 테마로 스킨 입히기

소개

테마는 애플리케이션이 일관된 LookAndFeel을 나타내도록 보장하는 방법


적용방법

 - manifest.xml에 application, activity 태트내에 다음 내용을 기록한다.

   android:theme="@android:style/Theme.Light"

 - Theme.Black : 검은 배경에 하얀 전경 컨트롤 및 텍스트 사용한다.

 - Theme.Light : 하얀 배경에 어두운 테두리 및 텍스트를 사용한다.

 - Theme.Translucent : 부분적으로 투명한 폼을 사용한다.

4.3 고급 캔버스 그리기

캔버스?

 비티맵Bitmap이나 서피스Surface 오브젝트에 대한 실질적인 비트bit 컴포지션compositing을 처리하는 그리기를 하는 표면surface.

 canvas는 비트맵을 래핑하고 draw메서드를 노출한다.


그리기 기본 구성요소

 - Canvas: 하부에 있는 비트맵에 그리기 기본요소를 칠하는 그리기 메서드 제공

 - Paint : "브러시"라고도 불리며 Paint는 기본요소가 비트맵 위에 어떻게 그려지는지 지정할 수 있도록 함

 - Bitmap : 그리기가 수행되는 표면

사용자 삽입 이미지

 


안드로이드 그리기 API

 - 비트맵, 원, 선, 사각형, 텍스트

 - 둥근 모서리 사각형

 - 투명도

 - gradient fill

 - anti-aliasing

 * 백터 그래픽스는 지원하지 않음

 * 전통적인 래스터 스타일의 다시 그리기 이동


Canvas와 Paint의 관계


사용자 삽입 이미지






canvas.drawXXX(): 이것을 사용하여서 화면에 실제 그린다.



 drawARGB 캔버스를 한가지 색으로 칠한다
 drawRGB 캔버스를 한가지 색으로 칠한다.
 drawArc

사각형 경계 영역 내에서 두 각 사이의 호를 그린다.

 drawBitmap 캔버스에 비트맵을 그린다. 대상 크기를 지정하거나 변형을 위한 매트릭스를 이용하면 겉모습을 변경할 수 있다.
 drawBitmapMesh 매시를 이용해 비트맵을 그린다. 매시는 매시 안에 있는 점을 이동함으로써 대상의 겉모습을 조작할 수 있다
 drawCircle 주어진 점을 중심으로 지정된 반지름 크기의 원을 그린다.
 drawLine 두 점 사이에 선(혹은 일련의 선)을 그린다.
 drawOval 지정된 사각형을 경계로 하는 타원을 그린다.
 drawPaint 캔버스 전체를 지정된 페인트로 칠한다.
 drawPath 지정된 패스를 그린다. Path객체는 종종 그리기 기본요소 집합을 하나의 객체안에 담는데 이용한다.
 drawPicture 지정된 사각형 안에 Picture객체를 그린다.
 drawPosText 각 문자의 오프셋을 지정하는 텍스트 문자열을 그린다.
 drawRect 사각형을 그린다.
 drawRoundRect 둥근 모서리를 가진 사각형을 그린다.
 drawText 캔버스 위에 텍스트 문자열을 그린다. 텍스트 폰트, 크기, 색상, 랜더링 속성은 모두 Paint객체에서 설정한다.
 drawTextOnPath 지정된 패스를 따라 텍스트를 그린다.
 drawVertices 일련의 정점들로 지정된 여러 삼각 패치를 그린다.
 drawColor 캔버스를 한가지 색으로 칠한다.



Paint 활용하기

paint클래스는 페인트브러쉬와 팔레트를 표현한다.

Paint객체를 수정해서 그릴때 이용되는 색상, 스타일, 폰트, 특수효과(투명,세이더,필터 등)를 제어할 수 있다.


.setColor() : 페인트 색상선택

.setStyle() : 객체의 윤곽, 일부을 채우고 등의 작업

.parseColor(): 해당 색상을 투명하게 처리

.setAlpha() : 불투명 처리

.setShader() : 솔리드 색상으로 칠해준다. 세이더의 가장 흔한 것으로 그레디언트로 채움은 것

 참고: ComposeShader, BitmapShader, GradientShader

.setMaskFilter(): 모서리에 알파채널변화로 효과주기. (BlueMaskFilter, EmbossMaskFilter)

.setColorfilter(): RGB채널 변화로 효과 주기(이것은 알파채널을 무시한다.)

 참고: ColorMatrixColorFilter, LightColorFilter, PorterDuffColorFilter

.setPathEffect(): 그리는 방법을 제어한다. 도형의 모서리 모양을 바꾸거나, 윤곽선을 제어한다.

 참고: CornerPathEffect, DashPathEffect, DiscretePathEffect, PathDashPathEffect, SumPathEffect, ComposePathEffect

.setXfermode(): 캔버스에 이미 그려진 것 위에 새로 칠하는 방법

 참고: AvoidXfermode, PixelXorfermode, PorterDuffXfermode

.setSubpixelText() : 그려지는 사선을 부드럽게 하기 위해 안티앨리어싱을 적용해 준다. (성능이 떨어뜨릴 수 있음)

.setAntiAlias() : 그려지는 사선을 부드럽게 하기 위해 안티앨리어싱을 적용해 준다. (성능이 떨어뜨릴 수 있음)


2D 그래피스를 위한 하드웨어 가속

- HW설정하기 : 2D 그래픽랜더링을 위해서 HW가속기를 사용하자.

  myActivity.requestWindowFeature(Window.FATURE_OPENGL);

- 이 효과에 해당되지 않는 요소 존재한다. 특히 Path가 해당하지 않는다.

- invalidate()는 캔버스 랜더링을 새로 하도록 한다.00


캔버스 드로잉 베스트 프랙티스

- 하드웨어 가속을 고려해라 : 추천한다.

- 크기와 방향을 고려해라 : 뷰와 오버레이 만들때 해상도와 크기에 대한 고려를 해야 한다.

- 객체는 정적으로 한 번만 만들자 : 객체 생성 비용이 매우 비싸다. 페인트, 패스, 세이더는 한번 만들어서 계속 사용

- onDraw는 비싸다는 사실을 기억해라 : 캔버스를 다시 그리는 일을 최소화 한다. 아래 3가지가 팀이다.

- 캔버스 변환을 이용해라 :캔버스내의 엘리먼트를 수정하지 말고 캔버스를 변환하도록 하라.

- 애니메이션을 이용해라 : 뷰를 직접 그리기 보다는 미리 정의된 애니메이션을 이용해라

- 비트맵과 나인패치를 이용해라: 이미지 정적이용시 직접 그리기보다는 이것을 사용하라.


맵 오버레이에 생명력 불어넣기

- Skip합니다.


4.5 SurfaceView 소개

SurfaceView 소개

 - SurfaceView는 뷰 계층 구조 내에서 드로잉 전용 공간을 제공하는 뷰에 대한 특수한 서브클래스 이다.

 - 그 목적은 시스템의 뷰 계층 구조가 그릴 준비가 될 때까지 애플리케이션이 기다리지 않도록 하기 위해

   애플리케이션의 보조 쓰레드에게 이러한 드로잉 공간을 제공하는데 있다.

 - 이 것은 View파생 클래스와 정확히 동일한 방식으로 이동한다. (애니메니션, 레이아웃 등 사용 가능)

 - Canvas 메소드를 이용한 대부분의 지원하며, OpenGL ES 라이브러리를 완벽히 지원한다.


용도:

 - 이것은 리소스가 많이 들거나 빠른 업데이트, 높은 프레임 속도를 요구할 때 특히 유용하다.

 - 단, 메모리의 소비가 발생될 수 있기에 사용시 신중을 기해야 한다.


사용예:

 - 구글 어쓰

 - 인터렉티브 게임에 탑재된 3D 이미지 표시

 - 실시간 카메라 미리보기


SurfaceView 컨트롤 만들기

주요 코드 설명

SurfaceHolder.callback:

 - 상속받은 나의 클래스에는 실제 SurfaceView가 있지 않다 이 SurfaceHolder를 통해서 연결되지고

 - 기반이 되는 서피스의 생성, 변경, 종료에 대한 이벤트를 알려 주는 인터페이스

 - 이것은 서피스의 이벤트를 Callback형태로 제공해서 내가 언제 drawing할지 알게해준다.

getHolder(): surfaceHolder 얻어 온다.

 holder.addCallBack(this) : 내가 만든 객체가 SurfaceHolder 콜백을 수신하고자 한다는 것을 SurfaceHolder에게 알림

 surfaceHolder.lockCanvas(): 서피스를 잠그고 그리기 위한 캔버스를 리턴 받는다. 이제 우린 그릴 캔버스를 확보한 것이며,

 이 캔버스는 이전 상태를 갖고 있기에 drawColor()로 초기색으로 변경하거나, drawBitmap()로 배경을 재설정해야할 것이다.

 sufaceHolder.unlockCanvasAndPost(): 캔버스 락을 풀고 현재 이미지를 랜더링한다.  


CallBack 메소드

 init(): 새로운 SurfaceHolder를 만들고 이 클래스를 SurfaceHolder의 콜백으로 지정한다.

 resume(): 그래픽 업데이트 스레드를 만들기 시작한다.

 pause(): 그래픽스 업데이트 스레드를 종료한다.

 surfaceCreated(): 인너 클래스의 child Thread를 실행한다.

 surfaceDestroyed(): 서피스를 종료을 위해서 pause()를 호출한다.

 surfaceChanged() : 서피스의 크기변경에 대응하는 메소드


MySurfaceViewThread

  - 인너클래스로  실제 작업을 수행하는 Child Thread와 Canvas Draw를 처리하는 메소드를 포함한 클래스

  run(): 오버라이드해서 스레드가 정지될 때까지 드로잉 루프를 반복한다.

  requestExitAndWait() : 스레드를 완료 상태로 표시하고 join()를 통해서 메인 스레드에 결합한다. (실제 화면에 그려진다.)

  onWindowResize() : 서피스 크기 변화를 준다.


사용자 삽입 이미지

 

 * OpenGL ES를 사용한 SurfaceView 3D 컨트롤은 APIDemo의 GLSurfaceView를 참조할 것.


public classMySurfaceViewextendsSurfaceViewimplementsSurfaceHolder.Callback {

  private SurfaceHolder holder;
  private MySurfaceViewThread mySurfaceViewThread;
  private boolean hasSurface;

MySurfaceView(Context context) {
    super(context);
    init();
  }
 
  private void
init() {
    // Create a new SurfaceHolder and assign this class as its callback.
    holder =
getHolder();
    holder.
addCallback(this);
    hasSurface = false;
  }

  public voidresume() {
    // Create and start the graphics update thread.
    if (mySurfaceViewThread == null) {
mySurfaceViewThread= newMySurfaceViewThread();

      if (hasSurface == true)
mySurfaceViewThread.start();
    }
  }

  public voidpause() {
    // Kill the graphics update thread
    if (mySurfaceViewThread != null) {
      mySurfaceViewThread.
requestExitAndWait();
      mySurfaceViewThread = null;
    }
  }

  public voidsurfaceCreated(SurfaceHolder holder) {
    hasSurface = true;
    if (mySurfaceViewThread != null)
mySurfaceViewThread.start();
  }

  public voidsurfaceDestroyed(SurfaceHolder holder) {
    hasSurface = false;
pause();
  }

  public voidsurfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    if (mySurfaceViewThread != null)
      mySurfaceViewThread.
onWindowResize(w, h);
  }

  //--------------------------------------------------------------------------

  classMySurfaceViewThreadextendsThread{
    private boolean done;

MySurfaceViewThread() {
      super();
      done = false;
    }

    @Override
    public void
run() {
      SurfaceHolder surfaceHolder = holder;
     
      // Repeat the drawing loop until the thread is stopped.
while(!done) {
        // Lock the surface and return the canvas to draw onto.
        Canvas
canvas=surfaceHolder.lockCanvas();

        // TODO: Draw on the canvas!

        // Unlock the canvas and render the current image.
surfaceHolder.unlockCanvasAndPost(canvas);
      }
    }

    public voidrequestExitAndWait() {
      // Mark this thread as complete and combine into
      // the main application thread.
done= true;
      try {
join();
      } catch (InterruptedException ex) { }
    }

    public voidonWindowResize(int w, int h) {
      // Deal with a change in the available surface size.
    }
  }
}



 

5. 인터랙티브한 컨트롤 만들기


사용자 삽입 이미지


Touch Key Event

 - onTouchEvent()

 - Start, Move, End

 - RawX, RawY  X, Y

 - getPressure() 터치의 정도의 정보도 확보 가능함 (0:압력 없음, 1:보통압력)

 - getSize() 접촉면의 정규화 크기를 측정할 수 있다. (0: 매우 정밀 1:굵은 터치)

 - getHistory() 움직임을 추적을 도와 준다. 과거의 값과 현재 발생된 모든 이벤트 저장

 - getHistorical*() 위치 인덱스를 주면 각각의 시간, 압력, 면적, 위치를 얻을 수 있다.

 - Sample 예제: FingerPaint

Key Event

 - onKeyup(), onKeyDown()

 - Down, Up, Repeat(?)

 - 포함되는 키: 키보드, D패드, 볼륨, 뒤로가기, 전화걸기, 전화끊기

 - 미포함 키: 홈 (하나의 앱에 묶이지 않게 하기 위해 용도를 예약한 것 때문임)

 - keyCode메개변수: 눌려진 키 값

 - keyEvent매개변수: 특정키의 수행을 돕기 위해서 기능, 시프트, 심벌메타키를 감지하는 메서드 제공

   또한 KeyDown,KeyUp도 감지한다.

TrackBall Event

 - onTrackballEvent()

 - MotionEvent매개변수 : 마지막 트랙볼의 움직임에 대한 상대적인 움직임 값을 x, y로 제공

 - D패드를 사용해도 동일한 움직임을 나타낸다.(??)


리스너 부착해서 이용하기

 - 해당 뷰에 .set*Listener를 사용하면 서브클래싱하지 않아도 된다.


'Android > Tip&Tech' 카테고리의 다른 글

안드로이드(android) ListView + ArrayAdapter ANDROID  (0) 2010.11.04
listview에 버튼넣기  (0) 2010.11.03
AIDL이 뭘까나?  (0) 2010.11.03
안드로이드 서비스 팁  (0) 2010.11.03
Sending SMS Messages  (2) 2010.11.03

+ Recent posts