안드로이드응용프로그램개발 2010.5 호서대학교뉴미디어학과이호석교수
알림 [1] 세미나자료의 1 장부터 11 장에있는안드로이드관련내용과안드로이드소스코드는참고문헌 (1)(2)(3) 에서발췌하여작성하였다. 12 장과 13 장의내용은 developer.android.com 을참고하여작성하였으며소스코드는 (2)(3) 에서발췌하여작성하였다. [2] 안드로이드에대한최신의내용은 developer.android.com 바란다. 을참고하기 [3] 국내의관련웹으로는 www.androidclub.c o.kr 이있다.
목차 1 장. 안드로이드소개 --------------------------- 1 2 장. 안드로이드개발환경과기본프로그래밍방법 ----- 5 3 장. 안드로이드응용프로그램개발 ---------------- 13 4 장. 기본위젯예제프로그램 --------------------- 19 5 장. 레이아웃을이용한사용자인터페이스설계 ------- 24 6 장. 선택기능위젯 ----------------------------- 32 7 장. 리스트고급활용 --------------------------- 41 8 장. 고급위젯과컨테이너 ------------------------ 46 9 장. 메뉴 ------------------------------------ 54 10 장. WebKit --------------------------------- 57 11 장. 화면회전 ------------------------------- 60 12 장. 그래픽과애니메이션 ----------------------- 62 13 장. 위치기반서비스 -------------------------- 96
1 장. 안드로이드소개 - OHA(Open Handset Alliance, www.openhandsetalliance.com). 핸드폰개발과판매에대한관련개발업체들의연합 (2007 년말결성) ( 칩제조사, 단말기제조사, 소프트웨어개발사, 서비스공급업체, 예를들어 Intel, Qualcomm, Samsung, LG, Google, HTC, NVIDIA, Microso ft, Motorola, Texas Instrument, China Mobile 등). 비독점개방형표준플랫폼공동개발 -> 안드로이드 - 안드로이드시스템. 안드로이드시스템설치. J2SE, Eclipse, Android SDK, Android DT+DDMS Plug-in, Pref erence setting. 안드로이드라이브러리. android.util: 코어유틸리티패키지로서컨테이너, 문자열포맷터, XML 파싱등제공. android.os: 메시지전달, 프로세스통신, 시계기능, 디버깅기능제공. android.graphics: Canvas, 색상, drawing primitives 등기본그래픽환경제공. android.text:. android.database:. android.content: 텍스트출력과파싱을위한텍스트처리도구 데이터베이스커서기능제공 콘텐트공급자기능제공. android.app.activity: 모든안드로이드애플리케이션을위한 Activity 클래스를포함.. android.view.view: Activity 와함께가장핵심적인클래스, 모든화면출력은 View 상속. android.widget: 윈도우콤포넌트이다. 버튼, 리스트, 체크박스등등. com.google.android.maps: 지도기능을제공한다. 위치기반서비스, MapView 등,. android.provider: 표준콘텐츠공급자에쉽게접근하는클래스제공.. android.telephony: 전화, 전화상태, SMS 메시지기능을제공한다.. android.webkit:. android.location:. android.media: 안드로이드웹브라우저 위치기반서비스제공 멀티미디어클래스제공. android.opengl: OpenGL ES 그래픽 API 제공. android.hardware: 카메라, 가속도센서, 나침반센서. android.bluetooth: 불루투스통신기능제공. android.net.wifi: WiFi 통신기능제공 - 1 -
www.openhandsetalliance.com - 2 -
. C/C++ 라이브러리. OpenGL ES: OpenGL ES 1.0 API 3D 그래픽스제공 (OpenGL ES v2.0). FreeType: 비트맵, 벡터폰트렌더링. SGL: 2D 그래픽엔진. libc: 리눅스기반임베디드장치에최적화된표준 C 라이브러리. SQLite: 관계형데이터베이스. SSL: Secure Socket Layer 암호프로토콜. 안드로이드소프트웨어구성. Activity. View: 윈도우위젯(Widget), 레이아웃(Layout). 보안네트워킹(SSL). 웹브라우저(WebKit). 네트워킹 API. SQLite 데이터베이스. OpenGL ES 3 차원그래픽. 멀티디어 API: 정지영상, MPEG4, MP3. 전화걸기 & SMS 보내기 API. LBS(Location-Based Service, 위치기반서비스) API:. 센서 API: 온도, 가속도등. 통신 API: WiFi, Bluetooth 지도서비스. 안드로이드애플리케이션아키텍처. Activity: 사용자기본윈도우( 핸드폰화면).. View: 윈도우에설치되는모든 UI 구성요소( 위젯, 레이아웃).. Intent: 원하는기능(action, message, 전화걸기등). Notification: 사용자에게신호를보내는기능, 예를들어전화가걸려왔을때불빛을 깜박이거나, 진동을울리거나, 아이콘표시를하거나, 등등.. Broadcast Receiver: Broadcasting되는 Intent 를처리한다.. Content Provider: 응용을간의데이터공유를위한인터페이스, SharedPreferences.. Service: 백그라운드모드로항상동작하는기본구성요소.. 현재안드로이드버전과 API 레벨. 버전 API 레벨장치 1.0 1 0.0% 1.1 2 0.1% 1.5 3 37.2% 1.6 4 29.4% 2.0 5 0.3% 2.01 6 0.6% 2.1 7 32.4% - 3 -
. 안드로이드개발환경. Eclipse: Java IDE. AVD(Android Virtual Device, 안드로이드가상기계): 안드로이드 Emulator. DDMS(Dalvik Debug Monitoring Service): 안드로이드를실행하는 Dalvik 가상 기계를모니터하고제어한다. tools/. AAPT(Android Asset Packaging Tool): 배포가능한안드로이드패키지파일(*.apk) 생성한다.. ADB(Android Debug Bridge): 실행중인에뮬레이터에연결할수있다. - 그밖의스마트폰개발환경. Mobile OS 현황 OS 개발업체 개요 개방성 주요단말기 비고 Window Mobile 7 Microsoft PC OS 연동 개방 글로벌제조사 Blackberry RIM RIM 독자 OS 폐쇄 RIM Symbian Nokia 노키아 OS 개방 글로벌제조사 iphone Apple 애플독자 OS 폐쇄 애플 Android Google 오픈소스 개방 글로벌제조사 Bada Samsung 삼성독자 OS 폐쇄 삼성 MeeGo Nokia+Intel Linux 기반오픈소스 개방 - Mobile Platform 현황 OS Window Mobile Blackberry iphone Android 개발언어 Visual Studio Java Objective C Java SDK/DEV Visual Studio BB Web Plugin XCode BB JDE Plugin Android SDK Device 관리 디바이스잠금, 디바이스잠금, 보안관련기능디바이스잠금삭제기능제공삭제관리기능완전하지않음 장점 광범위한개발중앙집중관리시스템보호개방성인프라기업용특화개발자보호개발자선호 단점 감소하는점유율 한국에서인지도낮음 폐쇄성 주요소스노출 - 4 -
2 장. 안드로이드개발환경과기본프로그래밍방법 - 개발환경갖추기. Java2 (java.sun.com) 에서 download. jdk 버전 5 이상설치. Eclipse (www.eclipse.org download) 에서 download. GALILEO 버전이상설치 (Europa, Ganymede 가능). Android SDK. http://developer.android.com/sdk 에서다운로드. download -> android-sdk_r05-windows.zip, SDK 2.1 new : 22.3MB. 압축을풀고 SDK Setup.exe 를실행하여 Android 를모두받으면: 1.27GB. https://dl-ssl.google.com/android/repository/repos itory.xml 에서다운로드. ADT(Android Development Tool) Plug-in for Eclipse. menu -> help -> install new software에서설치. Name: Android, Uri: https://dl-ssl.google.com/android/eclipse/. ADT 0.9.6 (2010년 3 월). Eclipse Preference setting. Window XP, 고급환경변수에서 Path에 Android SDK tools 디렉토리표시. 참고: Android NDK(Native Development Kit) revision3: C/C++ 코드포팅 - 안드로이드가상기기 (AVD, Android Virtual Device). Android SDK Setup.exe 를실행시켜 AVD 설정및작동. 일반 PC에서핸드폰을실행시키는안드로이드 Emulator - 5 -
- 안드로이드응용프로그램프레임워크. android.* com.google.android.maps dalvik.* java.* javax.* junit.* org.apache.http.* org.json org.w3c.dom org.xml.sax org.xmlpull.* - DDMS. DDMS(Dalvik Debug Monitor Service). 안드로이드 AVD 에직접접근하여쓰래드, 메모리등을직접볼수있다. - 안드로이드예제프로그램. API Demo. Bluetooth Chat. Business Card. Contact Manager. Home. Snake. LunarLander. JetBoy. Live Wallpaper. NotePad. Wiktionary 등 - 6 -
- 안드로이드디렉토리구조. src: 현재개발한안드로이드소스코드. gen: 안드로이드가자동으로생성한 R.java 소스코드. Android 라이브러리: Android SDK에서제공하는 Java 라이브러리, *.class 파일만있음.. res: 안드로이드가자동으로생성한자원(resource) 폴더. AndroidManifest.xml:. default.properties:. res/drawable: 현재개발한안드로이드응용프로그램에대한모든정보 속성정보 이미지파일. res/anim: 애니메이션 xml 파일. res/layout: XML 파일로서안드로이드화면 layout. res/menu: XML. res/values: 문자열. res/xml: XML 파일 파일로서메뉴정의 - 7 -
AndroidManifest.xml - 8 -
- 안드로이드자원. R.java: 안드로이드소스코드와 res/ 에있는 XML 파일에의하여생성된자원객체들을 연결시키는 Java 소스코드이며자동으로생성된다. Index를사용하여 XML 파일이 생성한자원객체를표시한다.. public final class R { public static final class attr { public static final class color { public static final int pretytextcolor=0x7f055555; public static final class dimen { public static final int textpointsize=0x7f060000; <-- index. 텍스트문자열, 이미지, 아이콘, 오디오, 동영상등. res 폴더. res/drawable: 이미지파일. res/anim: 애니메이션 xml 파일. res/layout: XML 파일로서안드로이드화면 layout. res/menu: XML. res/values:. res/xml: XML 문자열 파일로서메뉴정의 파일 - 코드에서접근하는방법 R.string.hello : string.xml 자원 xml 파일에서 hello tag의문자열값 String mystring = getresources().getstring(r.string.hello); - Eclipse에서 main.xml, string.xml, 기타 XML 파일을열어서수정하고편집할수있음 - setcontentview(r.layout.main); - 9 -
. 책 102page 표 5.1 기본안드로이드자원디렉토리. 책 103page 표 5.2 자원형식과저장위치 - 10 -
- 안드로이드응용프로그램골격구조 package com.android.myapplication; import android.app.activity; import android.os.bundle; import android.view.view; public class MyActivity extends Activity { public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); // Activity protected void onrestoreinstancestate(bundle savedinstancestate) { super.onrestoreinstancestate(savedinstancestate); // savedinstancestate UI public void onrestart( ) { super.onrestart( ); // Activity protected void onstart( ) { super.onstart( ); // Activity. public void onresume( ) { super.onresume( ); // Activity. protected void onsaveinstancestate(bundle savedinstanc estate) { super.onsaveinstancestate(savedinstancestate); // UI savedinstancestate. Activity savedinstancestate Bundle oncreate. public void onpause( ) { super.onpause( ); // Activity. Activity. protected void onstop( ) { super.onstop( ); // Activity. Activity. public void ondestroy( ) { super.ondestroy( ); // Activity. - 11 -
- 안드로이드기본프로그래밍방법. 화면구성은 XML 파일로정의 (GUI widget은 Resource XML 로정의). 실제기능은소스코드로프로그램 (GUI 콘트롤은 Android 소스코드에서정의). Android 응용프로그램은세부분으로분리되어구성된다.. Android 소스코드. R,java 파일 (Android build 시스템이자동생성). R.java 파일에는 Resource XML 파일에의하여생성된 GUI 객체에대한 identifier 가저장되어있다.. Resource XML 파일 (Android build 시스템이자동생성). 일반 Java GUI 프로그램에서는 Java 소스코드안에 GUI component 선언과 GUI 콘트롤기능을함께코딩한다.. Android GUI 프로그램에서는 GUI widget 선언과콘트롤기능을분리하여따로코딩한다.. GUI widget 콘트롤기능은 Android Java 프로그램에서코딩한다.. GUI widget 은 XML 파일에정의한다.. 이두부분의연결을 R.java 파일이담당한다.. GUI 콘트롤 Android 프로그램을고치지않고, GUI widget XML 파일만수정하여 다른 GUI 출력을낼수있다.. 혹은 GUI widget XML 파일에내용을추가하여(add, +) 새로운 Android GUI 프로그램을 쉽게개발할수있다. R,java 파일은안드로이드 build 시스템이자동으로수정한다.. 예: android:id="@+id/button". Activity 화면출력은 setcontentview(r.layout.main) 한줄이다.. 예 (GUI 에서콘트롤과선언부분분리). 마이크로소프트의 XAML(Extensible Application Markup Language). Adobe의 Flex. Mozilla의 XUL(XML User-interface Language) - 12 -
3 장. 안드로이드응용프로그램개발 - Snake 예제게임프로그램실행 1. 메뉴: File -> New -> Project 선택 2. Android -> Android Project 선택 3. Contents to Create project from existing source 4. Samples 5. Snake 폴더검색 버튼선택 ( 주의: AVD API 버전과 Sample 프로그램 API 버전이동일하여야한다.) 폴더선택 6. Finish 버튼을누른다. - 안드로이드가상장치 (AVD, Android Virtual Device) 생성 1. 안드로이드 SDK에서실행 ( 주의: AVD API 버전과 Sample 프로그램 API 버전이동일하여야한다.) - Snake 실행 1a. Snake 폴더에서마우스오른쪽버튼클릭 -> Run 클릭 ( 주의: AVD 에따라서시간이조금많이걸린다. 인내심을가지고기다려야한다.) (AVD 버전 1.5인경우는대략 30 초정도, 버전 2.1인경우는거의 1 분정도) 1b. 메뉴: Run -> Run Configuration 선택 2b. Android Application 더블클릭 3b. Name 상자에 SnakeRunConfiguration 입력 4b. Run As 아이콘클릭 5b. SnakeRunConfiguration 선택 ( 주의: AVD 에따라서시간이조금많이걸린다. 인내심을가지고기다려야한다.) (AVD 버전 1.5인경우는대략 30 초정도, 버전 2.1인경우는거의 1 분정도) - 13 -
- 안드로이드프로그램개발 1. 메뉴: File -> New -> Project 선택 2. Android -> Android Project 선택 3. Contents create new project in workspace 버튼선택, Use default location 4. Build Target 선택: 선택 생성시킨 AVD 버전과안드로이드프로그램개발 Platform 버전일치. 4. Project name 입력: *** Application name 입력: *** Package name 입력: 예를들어 com.example.android.*** Create Activity 입력: 개발하고자하는안드로이드 Activity 이름 Min SDK Version 설정: 교재에는 3. - 14 -
교재 58page 표 3.1 참조 - 15 -
- 생성된폴더구조 AndroidManifest.xml src 폴더: 개발중인안드로이드자바소스코드폴더 gen 폴더: 자동으로생성된폴더로서 R.java 파일이저장 res 폴더: 자원폴더. 참고: 안드로이드에서는소스코드와자원은분리되어따로저장됨. 자원은 xml 파일형태로 저장됨 5. AVD 생성 (AVD 이름은사용자편의대로하면됨.) 6. 개발한안드로이드프로젝트에마우스를위치시킨다. 7. 마우스오른쪽버튼을클릭한다. 8. 메뉴가나오면실행을클릭한다. 9. 기다린다. (30 초~1 분정도) 10. AVD 에안드로이드프로그램이실행된다. - 16 -
- 예제프로그램 package com.example.android.now; import android.app.activity; import android.os.bundle; import android.view.view; import android.widget.button; import java.util.date; public class Now extends Activity implements View.OnClick Listener { Button btn; /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); btn = new Button(this); btn.setonclicklistener(this); updatetime(); setcontentview(btn); public void onclick(view view) { updatetime(); private void updatetime() { btn.settext(new Date().toString()); - 17 -
package com.example.android.now1; import android.app.activity; import android.os.bundle; import android.view.view; import android.widget.button; import java.util.date; public class Now1 extends Activity implements View.OnClic klistener { Button btn; /** Called when the activity is first created. */ public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); btn=(button)findviewbyid(r.id.button); btn.setonclicklistener(this); updatetime(); public void onclick(view view) { updatetime(); private void updatetime() { btn.settext(new Date().toString()); <?xml version="1.0" encoding="utf-8"?> <Button xmlns:android = "http://schemas.android.com/apk/res/android" android:id="@+id/button" android:text="" android:layout_height="wrap_content" /> - 18 -
4 장. 기본위젯예제프로그램 - 안드로이드뷰 (view), 위젯 (widget), 레이아웃 (layout). 화면출력클라스: android.view.view, android.view.viewgroup. 안드로이드위젯: android.widget. 안드로이드레이아웃: android.widget.linearlayout - TextView. android.wdiget.textview <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id=@+id/textview01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="some sample text here" /> <TextView android:id=@+id/textview02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/sample_text" /> <TextView android:id=@+id/textview03" android:layout_width="wrap_content" android:layout_height="wrap_content" android:lines="2" // 높이는 2줄분량 android:ems="12" // 너비는 12em android:text="@string/autolink_test" /> - 19 -
- EditText <?xml version="1.0" encoding="utf-8"?> <EditText xmlns:android="http://schemas.android.com/apk/res/android" android:id=@+id/edittext01" android:layout_height="wrap_content" android:hint="type here" android:lines="4" /> - Image <?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" /> android:id=@+id/icon" android:layout_height="fill_content" android;adjustviewbounds="true" android:src="drawable/molecule" <-- 현재 drawable 에있는 image 파일설정 - Button <Button android:id=@+id/button" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="basic Button" /> - 20 -
- FieldDemo( 입력필드 ) package com.android.example.fielddemo; import android.app.activity; import android.os.bundle; import android.widget.edittext; public class FieldDemo extends Activity { public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main) { EditText fld = (EditText)findViewById(R.id.field); fld.settext("licensed under the Apache License, version 2.0" + "You may not use this file " + "except in compliance with the License "); <?xml version="1.0" encoding="utf-8"?> <EditText xmlns:android="http://schemas.android.com/apk/res/android" android:id=@+id/field" android:layout_height="fill_parent" android:singlelinet="false" /> - 21 -
- CheckBox package com.commonsware.android.basic; import android.app.activity; import android.os.bundle; import android.widget.checkbox; import android.widget.compoundbutton; public class CheckBoxDemo extends Activity implements CompoundButton.OnCheckedChangeListener { CheckBox cb; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); cb=(checkbox)findviewbyid(r.id.check); cb.setoncheckedchangelistener(this); public void oncheckedchanged(compoundbutton buttonview, if (ischecked) { cb.settext( " 체크상태" ); else { boolean ischecked) { cb.settext( " 체크되지않은상태" ); <?xml version="1.0" encoding="utf-8"?> <CheckBox xmlns:android="http://schemas.android.com/apk/res/android" /> android:id="@+id/check" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 체크되지않은상태" - 22 -
- RadioButton package com.commonsware.android.basic; import android.app.activity; import android.os.bundle; public class RadioButtonDemo extends Activity { public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); <?xml version="1.0" encoding="utf-8"?> <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <RadioButton android:id="@+id/radio1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 가위" /> <RadioButton android:id="@+id/radio2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 바위" /> <RadioButton android:id="@+id/radio3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 보" /> </RadioGroup> - 23 -
5 장. 레이아웃을이용한사용자인터페이스설계 - 코드에서레이아웃만들기 (LinearLayout) import android.app.activity; import android.os.bundle; public class LinearLayout extends Activity { public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); TextView text1 = new TextView(this); text1.settext(" 안녕! ); TextView text2 = new TextView(this); text2.settext(" 나는둘째. 줄바꿈이필요함. ); text2.settextsize((float) 60); LinearLayout ll = new LinearLayout(this); ll.setorientation(linearlayout.vertical); ll.addview(text1); ll.addview(text2); setcontentview(ll); - 24 -
- XML 로레이아웃만들기 (LinearLayout) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:id="@+id/textview1" android:layout_height="wrap_content" android:text=" 안녕! /> <TextView android:id="@+id/textview2" android:layout_height="wrap_content" android:textsize="60px" android:text=" 나는둘째, 줄바꿈이필요함. /> </LinearLayout> - 25 -
- LinearLayout 예제 package com.commonsware.android.containers; import android.app.activity; import android.os.bundle; import android.view.gravity; import android.text.textwatcher; import android.widget.linearlayout; import android.widget.radiogroup; import android.widget.edittext; public class LinearLayoutDemo extends Activity implements RadioGroup.OnCheckedChangeListener { RadioGroup orientation; RadioGroup gravity; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); orientation=(radiogroup)findviewbyid(r.id.orientation); orientation.setoncheckedchangelistener(this); gravity=(radiogroup)findviewbyid(r.id.gravity); gravity.setoncheckedchangelistener(this); public void oncheckedchanged(radiogroup group, int checkedid) { if (group==orientation) { if (checkedid==r.id.horizontal) { orientation.setorientation(linearlayout.horizontal); else { orientation.setorientation(linearlayout.vertical); else if (group==gravity) { if (checkedid==r.id.left) { gravity.setgravity(gravity.left); - 26 -
else if (checkedid==r.id.center) { gravity.setgravity(gravity.center_horizontal); else if (checkedid==r.id.right) { gravity.setgravity(gravity.right); <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <RadioGroup android:id="@+id/orientation" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5px"> <RadioButton android:id="@+id/horizontal" android:text=" 가로" /> <RadioButton android:id="@+id/vertical" android:text=" 세로" /> </RadioGroup> <RadioGroup android:id="@+id/gravity" android:orientation="vertical" android:layout_height="wrap_content" android:padding="5px"> <RadioButton android:id="@+id/left" android:text=" 왼쪽" /> <RadioButton android:id="@+id/center" android:text=" 가운데" /> <RadioButton android:id="@+id/right" android:text=" 오른쪽" /> </RadioGroup> </LinearLayout> - 27 -
- RelativeLayout <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:padding="5px"> <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="url:" android:paddingtop="15px" /> <EditText <Button android:id="@+id/entry" android:layout_height="wrap_content" android:layout_torightof="@id/label" android:layout_alignbaseline="@id/label" /> android:id="@+id/ok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/entry" android:layout_alignright="@id/entry" android:text=" 확인" /> <Button android:id="@+id/cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toleftof="@id/ok" android:layout_aligntop="@id/ok" android:text=" 취소" /> </RelativeLayout> - 28 -
- TableLayout package com.commonsware.android.containers; import android.app.activity; import android.os.bundle; public class TableLayoutDemo extends Activity { public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="fill_parent" android:stretchcolumns="1"> <TableRow> <TextView android:text="url:" /> <EditText android:id="@+id/entry" android:layout_span="3" /> </TableRow> <View android:layout_height="2px" android:background="#0000ff" /> <TableRow> <Button android:id="@+id/cancel" android:layout_column="2" android:text=" 취소" /> <Button </TableRow> </TableLayout> android:id="@+id/ok" android:text=" 확인" /> - 29 -
- Scroll <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content"> <TableLayout android:layout_height="fill_parent" android:stretchcolumns="0"> <TableRow> <View android:layout_height="80px" android:background="#000000" /> <TextView android:text="#000000" android:paddingleft="4px" android:layout_gravity="center_vertical" /> </TableRow> <TableRow> <View android:layout_height="80px" android:background="#440000" /> <TextView android:text="#440000" android:paddingleft="4px" android:layout_gravity="center_vertical" /> </TableRow> <TableRow> <View android:layout_height="80px" android:background="#884400" /> <TextView android:text="#884400" android:paddingleft="4px" android:layout_gravity="center_vertical" /> </TableRow> <TableRow> <View android:layout_height="80px" android:background="#aa8844" /> <TextView android:text="#aa8844" android:paddingleft="4px" android:layout_gravity="center_vertical" /> </TableRow> <TableRow> <View android:layout_height="80px" android:background="#ffaa88" /> <TextView android:text="#ffaa88" android:paddingleft="4px" android:layout_gravity="center_vertical" /> </TableRow> <TableRow> <View android:layout_height="80px" android:background="#ffffaa" /> <TextView android:text="#ffffaa" android:paddingleft="4px" android:layout_gravity="center_vertical" /> </TableRow> <TableRow> <View android:layout_height="80px" android:background="#ffffff" /> <TextView android:text="#ffffff" android:paddingleft="4px" android:layout_gravity="center_vertical" /> </TableRow> </TableLayout> </ScrollView> - 30 -
package com.commonsware.android.containers; import android.app.activity; import android.os.bundle; public class ScrollViewDemo extends Activity { public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); - 31 -
6 장. 선택기능위젯 - ArrayAdapter 사용하기 String[] items={"this", "is", "a", "really", "silly", "list"; ArrayAdapter<String> (this, android.r.layout.simple_list_item_1, items); - 기능: 문자열을안드로이드위젯에매핑시키는기능 - ListView 예제 package com.commonsware.android.selection; import android.app.activity; import android.os.bundle; import android.app.listactivity; import android.view.view; import android.widget.adapterview; import android.widget.arrayadapter; import android.widget.listview; import android.widget.textview; public class ListViewDemo extends ListActivity { TextView selection; String[] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); setlistadapter(new ArrayAdapter<String>(this, android.r.layout.simple_list_item_1, items)); selection=(textview)findviewbyid(r.id.selection); - 32 -
public void onlistitemclick(listview parent, View v, int p osition, long id) { selection.settext(items[position]); <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <TextView android:id="@+id/selection" android:layout_height="wrap_content" /> <ListView android:id="@android:id/list" android:layout_height="fill_parent" android:drawselectorontop="false" /> </LinearLayout> - 33 -
- Spin Control public class SpinnerDemo extends Activity implements AdpaterView.OnItemSelectedListener { TextView selection; String[ ] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); selection=(textview)findviewbyid(r.id.selection); Spinner spin=(spinner)findviewbyid(r.id.spinner); spin.setonitemselectedlistener(this); ArrayAdapter<String> aa=new ArrayAdapter<String> (this, android.r.layout.simple_spinner_item, items); aa.setdropdownviewresource (android.r.layout.simple_spinner_dropdown, items); spin.setadapter(aa); public void onitemselected(adapterview<?> parent, View v, int position, long id) { selection.settext(item[position]); public void onnothingselected(adapterview<?> parent) { selection.settext(" "); - 34 -
<?xml version="1.0" encoding="utf-8"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:id="@+id/selection" android:layout_height="wrap_content" /> <Spinner android:id="@+id/spinner" android:layout_height="wrap_content" android:drawselectorontop="true' /> </LinearLayout> - 35 -
-Grid package com.commonsware.android.selection; import android.app.activity; import android.content.context; import android.os.bundle; import android.view.view; import android.view.viewgroup; import android.widget.adapterview; import android.widget.arrayadapter; import android.widget.button; import android.widget.gridview; import android.widget.textview; public class GridDemo extends Activity implements AdapterView.OnItemSelectedListener { TextView selection; String[] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); selection=(textview)findviewbyid(r.id.selection); GridView g=(gridview) findviewbyid(r.id.grid); g.setadapter(new FunnyLookingAdapter(this, android.r.layout.simple_list_item_1, items)); g.setonitemselectedlistener(this); public void onitemselected( AdapterView<?> parent, View v, int position, long id) { selection.settext(items[position]); - 36 -
public void onnothingselected(adapterview<?> parent) { selection.settext(""); private class FunnyLookingAdapter extends ArrayAdapter { Context ctxt; FunnyLookingAdapter(Context ctxt, int resource, String[] items) { super(ctxt, resource, items); this.ctxt=ctxt; public View getview(int position, View convertview, ViewGroup parent) { TextView label=(textview)convertview; if (convertview==null) { convertview=new TextView(ctxt); label=(textview)convertview; label.settext(items[position]); return(convertview); - 37 -
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <TextView android:id="@+id/selection" android:layout_height="wrap_content" /> <GridView android:id="@+id/grid" android:layout_height="fill_parent" android:verticalspacing="35px" android:horizontalspacing="5px" android:numcolumns="auto_fit" android:columnwidth="100px" android:stretchmode="columnwidth" android:gravity="center" /> </LinearLayout> - 38 -
- 자동입력 ( 타이핑줄이기 ) public class AutoCompleteDemo extends Activity implement s TextWatcher { TextView selection; AutoCompleteTextView edit; String[ ] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); selection=(textview)findviewbyid(r.id.selection); edit=(autocompletetextview)findviewbyid(r.id.edit); edit.addtextchangedlistener(this); edit.setadapter(new ArrayAdapter<String> (this, android.r.layout.simple_dropdown_item_line, items)); public void ontextchanged(charsequence s, int start, int before, int count) { selection.settext(edit.gettext()); public void beforetextchanged(charsequence s, int start, int count, int after) { public void aftertextchanged(editable s) { - 39 -
<?xml version="1.0" encoding="utf-8"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:id="@+id/selection" android:layout_height="wrap_content" /> <AutoCompleteTextView android:id="@+id/edit" android:layout_height="wrap_content" android:completionthreshold="3" /> </LinearLayout> - 40 -
7 장. 리스트고급활용 - Static List package com.commonsware.android.fancylists.two; import android.app.activity; import android.os.bundle; import android.app.listactivity; import android.view.view; import android.widget.adapterview; import android.widget.arrayadapter; import android.widget.listview; import android.widget.textview; public class StaticDemo extends ListActivity { TextView selection; String[] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); setlistadapter(new ArrayAdapter<String>(this, R.layout.row, R.id.label, items)); selection=(textview)findviewbyid(r.id.selection); public void onlistitemclick(listview parent, View v, int p osition, long id) { selection.settext(items[position]); - 41 -
- main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <TextView android:id="@+id/selection" android:layout_height="wrap_content" /> <ListView android:id="@android:id/list" android:layout_height="fill_parent" android:drawselectorontop="false" /> </LinearLayout> - row.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/icon" android:layout_width="22px" android:paddingleft="2px" android:paddingright="2px" android:paddingtop="2px" android:layout_height="wrap_content" android:src="@drawable/ok" /> <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="44sp" /> </LinearLayout> - 42 -
- Dynamic List package com.commonsware.android.fancylists.three; import android.app.activity; import android.os.bundle; import android.app.listactivity; import android.view.view; import android.view.viewgroup; import android.view.layoutinflater; import android.widget.adapterview; import android.widget.arrayadapter; import android.widget.imageview; import android.widget.listview; import android.widget.textview; public class DynamicDemo extends ListActivity { TextView selection; String[] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); setlistadapter(new IconicAdapter(this)); selection=(textview)findviewbyid(r.id.selection); public void onlistitemclick(listview parent, View v, int p osition, long id) { selection.settext(items[position]); class IconicAdapter extends ArrayAdapter { Activity context; IconicAdapter(Activity context) { super(context, R.layout.row, items); this.context=context; - 43 -
public View getview(int position, View convertview, ViewGroup parent) { LayoutInflater inflater=context.getlayoutinflater(); View row=inflater.inflate(r.layout.row, null); TextView label=(textview)row.findviewbyid(r.id.label); label.settext(items[position]); if (items[position].length()>4) { ImageView icon=(imageview)row.findviewbyid(r.id.icon); icon.setimageresource(r.drawable.delete); return(row); - 44 -
- main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <TextView android:id="@+id/selection" android:layout_height="wrap_content" /> <ListView android:id="@android:id/list" android:layout_height="fill_parent" android:drawselectorontop="false" /> </LinearLayout> - row.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/icon" android:layout_width="22px" android:paddingleft="2px" android:paddingright="2px" android:paddingtop="2px" android:layout_height="wrap_content" android:src="@drawable/ok" /> <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="40sp" /> </LinearLayout> - 45 -
8 장. 고급위젯과컨테이너 - Chrono package com.commonsware.android.fancy; import android.app.activity; import android.os.bundle; import android.app.datepickerdialog; import android.app.timepickerdialog; import android.view.view; import android.widget.button; import android.widget.datepicker; import android.widget.timepicker; import android.widget.textview; import java.text.dateformat; import java.util.calendar; public class ChronoDemo extends Activity { DateFormat fmtdateandtime=dateformat.getdatetimeinstance(); TextView dateandtimelabel; Calendar dateandtime=calendar.getinstance(); DatePickerDialog.OnDateSetListener d=new DatePickerDialog.OnDateSetListener() { public void ondateset(datepicker view, int year, int month OfYear, int dayofmonth) { dateandtime.set(calendar.year, year); dateandtime.set(calendar.month, monthofyear); dateandtime.set(calendar.day_of_month, dayofmonth); updatelabel(); ; TimePickerDialog.OnTimeSetListener t=new TimePickerDialog.OnTimeSetListener() { public void ontimeset(timepicker view, int hourofday, int minute) { dateandtime.set(calendar.hour_of_day, hourofday); dateandtime.set(calendar.minute, minute); updatelabel(); ; - 46 -
public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); Button btn=(button)findviewbyid(r.id.datebtn); btn.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { new DatePickerDialog(ChronoDemo.this, d, dateandtime.get(calendar.year), dateandtime.get(calendar.month), dateandtime.get(calendar.day_of_month)).show(); ); btn=(button)findviewbyid(r.id.timebtn); btn.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { new TimePickerDialog(ChronoDemo.this, t, dateandtime.get(calendar.hour_of_day), dateandtime.get(calendar.minute), true).show(); ); dateandtimelabel=(textview)findviewbyid(r.id.dateandtime); updatelabel(); private void updatelabel() { dateandtimelabel.settext(fmtdateandtime.format(dateandtime.gettime())); - 47 -
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <TextView <Button <Button android:orientation="vertical" android:layout_height="fill_parent"> android:id="@+id/dateandtime" android:layout_height="wrap_content" /> android:id="@+id/datebtn" android:layout_height="wrap_content" android:text=" 날짜선택" /> android:id="@+id/timebtn" android:layout_height="wrap_content" android:text=" 시간선택" /> </LinearLayout> - 48 -
- 시계 package com.commonsware.android.fancy; import android.app.activity; import android.os.bundle; public class ClocksDemo extends Activity { public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <AnalogClock android:id="@+id/analog" android:layout_height="wrap_content" android:layout_centerhorizontal="true" android:layout_alignparenttop="true" /> <DigitalClock android:id="@+id/digital" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerhorizontal="true" android:layout_below="@id/analog" /> </RelativeLayout> - 49 -
- DatePicker public class HelloDatePicker extends Activity { private TextView mdatedisplay; private int myear; private int mmonth; private int mday; static final int DATE_DIALOG_ID=0; public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); mdatedisplay = (TextView) findviewbyid(r.id.datedisplay); mpickdate = (Button) findviewbyid(r.id.pickdate); mpickdate.setonclicklistener(new View.OnClickListene r() { public void onclick(view v) { showdialog(date_dialog_id); ); final Calendar c = Calender.getInstance(); myear = c.get(calender.year); mmonth = c.get(calender.month); mday = c.get(calender.day_of_month); updatedisplay(); private void updatedisplay() { mdatedisplay.settext ( new StringBuilder().append(mMonth+1).append("-").append(mDay).append("-").append(mYear).append("-")); - 50 -
protected Dialog oncreatedialog(int id) { switch (id) { case DATE_DIALOG_ID: return new DatePickerDialog(this, mdatesetlistener, m Year, mmonth, mday); return null; private DatePickerDialog.OnDateSetListener mdatesetli stener mdatesetlistener= new DatePickerDialog.OnDateSetListener() { public void ondateset(datepicker view, int year, int month OfYear, int dayofmonth) { myear = year; mmonth = monthofyear; mday = dayofmonth; updatediaplay(); ; - 51 -
- Flipper package com.commonsware.android.flipper1; import android.app.activity; import android.os.bundle; import android.view.view; import android.widget.button; import android.widget.viewflipper; public class FlipperDemo extends Activity { ViewFlipper flipper; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); flipper=(viewflipper)findviewbyid(r.id.details); Button btn=(button)findviewbyid(r.id.flip_me); btn.setonclicklistener(new View.OnClickListener() { public void onclick(view view) { flipper.shownext(); ); - 52 -
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <Button android:id="@+id/flip_me" android:layout_height="wrap_content" android:text=" 페이지넘김" /> <ViewFlipper android:id="@+id/details" android:layout_height="fill_parent"> <TextView <TextView <TextView </ViewFlipper> </LinearLayout> android:layout_height="wrap_content" android:textstyle="bold" android:textcolor="#ff00ff00" android:text=" 첫번째페이지" /> android:layout_height="wrap_content" android:textstyle="bold" android:textcolor="#ffff0000" android:text=" 두번째페이지" /> android:layout_height="wrap_content" android:textstyle="bold" android:textcolor="#ffffff00" android:text=" 세번째페이지" /> - 53 -
9 장. 메뉴 - OptionMenu, ContextMenu - MenuDemo package com.commonsware.android.menus; import android.app.activity; import android.os.bundle; import android.app.listactivity; import android.view.contextmenu; import android.view.menu; import android.view.menuitem; import android.view.view; import android.widget.adapterview; import android.widget.arrayadapter; import android.widget.listview; import android.widget.textview; public class MenuDemo extends ListActivity { TextView selection; String[] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"; public static final int EIGHT_ID = Menu.FIRST+1; public static final int SIXTEEN_ID = Menu.FIRST+2; public static final int TWENTY_FOUR_ID = Menu.FIRST+3; public static final int TWO_ID = Menu.FIRST+4; public static final int THIRTY_TWO_ID = Menu.FIRST+5; public static final int FORTY_ID = Menu.FIRST+6; public static final int ONE_ID = Menu.FIRST+7; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); setlistadapter(new ArrayAdapter<String>(this, android.r.layout.simple_list_item_1, items)); selection=(textview)findviewbyid(r.id.selection); registerforcontextmenu(getlistview()); public void onlistitemclick(listview parent, View v, int p osition, long id) { selection.settext(items[position]); public void oncreatecontextmenu(contextmenu menu, View v, ContextMenu.ContextMenuInfo menuinfo) { populatemenu(menu); - 54 -
public boolean oncreateoptionsmenu(menu menu) { populatemenu(menu); return(super.oncreateoptionsmenu(menu)); public boolean onoptionsitemselected(menuitem item) { return(applymenuchoice(item) super.onoptionsitemselected(item)); public boolean oncontextitemselected(menuitem item) { return(applymenuchoice(item) super.oncontextitemselected(item)); private void populatemenu(menu menu) { menu.add(menu.none, ONE_ID, Menu.NONE, "1 픽셀"); menu.add(menu.none, TWO_ID, Menu.NONE, "2 픽셀"); menu.add(menu.none, EIGHT_ID, Menu.NONE, "8 픽셀"); menu.add(menu.none, SIXTEEN_ID, Menu.NONE, "16 픽셀"); menu.add(menu.none, TWENTY_FOUR_ID, Menu.NONE, "24 픽셀"); menu.add(menu.none, THIRTY_TWO_ID, Menu.NONE, "32 픽셀"); menu.add(menu.none, FORTY_ID, Menu.NONE, "40 픽셀"); private boolean applymenuchoice(menuitem item) { switch (item.getitemid()) { case ONE_ID: getlistview().setdividerheight(1); return(true); case EIGHT_ID: getlistview().setdividerheight(8); return(true); case SIXTEEN_ID: getlistview().setdividerheight(16); return(true); case TWENTY_FOUR_ID: getlistview().setdividerheight(24); return(true); case TWO_ID: getlistview().setdividerheight(2); return(true); case THIRTY_TWO_ID: getlistview().setdividerheight(32); return(true); case FORTY_ID: getlistview().setdividerheight(40); return(true); return(false); - 55 -
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <TextView android:id="@+id/selection" android:layout_height="wrap_content" android:background="#eeeeee" /> <ListView android:id="@android:id/list" android:layout_height="fill_parent" android:drawselectorontop="false" /> </LinearLayout - 56 -
10 장. Webkit - Browser1 package com.commonsware.android.webkit; import android.app.activity; import android.os.bundle; import android.webkit.webview; public class BrowserDemo1 extends Activity { WebView browser; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); browser=(webview)findviewbyid(r.id.webkit); browser.loadurl("http://www.daum.co.kr"); <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <WebView android:id="@+id/webkit" android:layout_height="fill_parent" /> </LinearLayout> - 57 -
- Browser2 package com.commonsware.android.webkit; import android.app.activity; import android.os.bundle; import android.webkit.webview; public class BrowserDemo2 extends Activity { WebView browser; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); browser=(webview)findviewbyid(r.id.webkit); browser.loaddata ("<html><body>hello, world! 안녕하세요!</body></html>", "text/html", "UTF-8"); <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <WebView android:id="@+id/webkit" android:layout_height="fill_parent" /> </LinearLayout> - 58 -
- Browser3 package com.commonsware.android.webkit; import android.app.activity; import android.os.bundle; import android.webkit.webview; import android.webkit.webviewclient; import java.util.date; public class BrowserDemo3 extends Activity { WebView browser; public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); browser=(webview)findviewbyid(r.id.webkit); browser.setwebviewclient(new Callback()); loadtime(); void loadtime() { String page="<html><body><a href=\"clock\">" +new Date().toString() +"</a></body></html>"; browser.loaddatawithbaseurl("x-data://base", page, "text/html", "UTF-8", null); private class Callback extends WebViewClient { public boolean shouldoverrideurlloading(webview view, String url) { loadtime(); return(true); <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <WebView android:id="@+id/webkit" android:layout_height="fill_parent" /> </LinearLayout> - 59 -
11 장. 화면회전 * - RotationThree package com.commonsware.android.rotation.three; import android.app.activity; import android.content.intent; import android.content.res.configuration; import android.net.uri; import android.os.bundle; import android.view.view; import android.widget.button; import android.util.log; public class RotationThreeDemo extends Activity { static final int PICK_REQUEST=1337; Button viewbutton=null; Uri contact=null; public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setupviews(); protected void onactivityresult(int requestcode, int resultcode, Intent data) { if (requestcode==pick_request) { if (resultcode==result_ok) { contact=data.getdata(); viewbutton.setenabled(true); public void onconfigurationchanged(configuration newco nfig) { super.onconfigurationchanged(newconfig); setupviews(); private void setupviews() { setcontentview(r.layout.main); Button btn=(button)findviewbyid(r.id.pick); btn.setonclicklistener(new View.OnClickListener() { public void onclick(view view) { Intent i=new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts/people")); startactivityforresult(i, PICK_REQUEST); ); - 60 -
viewbutton=(button)findviewbyid(r.id.view); viewbutton.setonclicklistener(new View.OnClickListen er() { public void onclick(view view) { startactivity(new Intent(Intent.ACTION_VIEW, contact)); ); viewbutton.setenabled(contact!=null); main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <Button android:id="@+id/pick" android:layout_height="fill_parent" android:layout_weight="1" android:text=" 선택" android:enabled="true" /> <Button android:id="@+id/view" android:layout_height="fill_parent" android:layout_weight="1" android:text=" 보기" android:enabled="false" /> </LinearLayout> - 61 -
12 장. 그래픽스와애니메이션 12.0 소개. View class, Canvas class, Paint class - 화면에원가를그리려면. Canvas: 화폭. Paint: 물감( 색상, 안티엘리어싱, 스타일, 그래디언트) import android.app.activity; import android.content.intent; import android.os.bundle; import android.view.view; private static class ViewWithRedDot extends View { public ViewWithRedDot(Context context) { super(context): protected void ondraw(canvas canvas) { canvas.drawcolor(color.black); Paint circlepaint = new Paint( ); circlepaint.setcolor(color.red); canvas.drawcircle( canvas.getwidth()/2, canvas.getheight()/2, canvas.getwidth()/3, circlepaint); public class WithRedDot extends Activity { /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(new ViewWithRedDot(this)); - 62 -
12.1 2 차원그래픽스 (developer.android.com) 안드로이드는커스텀(custom) 안드로이드라이브러리를통한 2차원그래픽스와 OpenGL ES v1.0을통한 3 차원그래픽스를제공한다. 2차원그래픽스 API(Application Programming Interface) 는드로어블(Drawable) 패키지에서제공되고, OpenGL API는 OpenGL ES 패키지와 OpenGL 유틸리티에서제공된다. OpenGL ES는여기서논의하지않 는다. OpenGL ES 전문서적을참고하기바란다. 2차원그래픽스와 3 차원그래픽스는다른부분이많기때문에, 그래픽스응용을개발할 때는응용의요구사항이무엇인지를분명히파악하는것이중요하다. 왜냐하면간단하고고 정적인그래픽스응용은 안드로이드에서 2 차원그래픽스를사용하여충분히구현할수있기때문이다. 2 차원그래픽스를구현하는방법에는다음두가지방법이있다. 그래픽스를구현하는두가지방법 (1) Layout 위에 View 객체를사용하여그래픽스와애니메이션을구현하는방법이있다. 이방법에서는그림과애니메이션은안드로이드시스템 View 계층의드로잉(drawing) 과정 을통하여이루어진다. 개발자는단순히 View 를상속받아개발을수행하면된다. 이방법은 변화가많지않은간단한그래픽스혹은높은성능이필요하지않는간단한게임의경우에 적합하다. 즉, 예를들어, 이미지, 도형, 칼라, 혹은간단한애니메이션을하는경우에는, Layout을사용하여 View에그림을그리거나혹은 ImageView 에그림을그린다. (2) Canvas 객체위에직접그래픽스를그리는방법이있다. 이방법을사용하면적절한 클래스의 draw() 메소드를호출하여그림을그린다. 예를들어, Canvas의 drawpicture() 메소드를호출하여그림을그릴수있다. 이방법은비디오게임과같이 Canvas 위에규칙 적으로그림을다시그려야하는경우에적합하다. 이방법의구현에는두가지방법이있 다. Canvas에대해서는 9.1.2 절에서좀더자세히다룬다. (2.1) 사용자인터페이스(UI) 와동일한쓰래드에서, Layout 위에커스텀 View 컴포넌트를 생성하고, invalidate() 를호출하고 ondraw() 콜백(callback) 함수를사용하여처리 하는방법이있다. (2.2) 사용자인터페이스와다른쓰래드에서, SurfaceView를사용하여쓰래드가허용하는 가장빠른속도로 Canvas 에그림을그리는방법이있다. SurfaceView에대해서는 9.1.5 절에서좀더자세히설명한다. 지금부터는그래픽스와관련된안드로이드클래스들을하나씩설명한다. 들은 developer.android.com 에서찾을수있다. 좀더상세한자료 - 63 -
12.1.1 Drawable 2차원그래픽스라이브러리는 android.graphics.drawable 과 android.view.animation 패 키지에서찾을수있다. 이절에서는 Drawable 객체를사용한간단한그래픽스, Drawable 객체의서브클래스, 그리고프레임애니메이션과트윈애니메이션( 이동, 확장, 회전) 을소개 한다. Drawable 은안드로이드에서제공하는그림그리기에대한최상위객체이다. Drawable 클래스는 AnimationDrawable, BitmapDrawable, ClipDrawable, Color Drawable, ShapeDrawable, PictureDrawable, LayerDrawable 등등의서브클래스를제공한다. 개발 자는자신이개발하고자하는응용을위하여이클래스들을다시확장하여사용할수있다. Drawable을정의하고활용하는데에는 3 가지방법이있다. 즉, 개발자는개발하고자하는 프로젝트자원에저장된이미지, Drawable의속성을규정한 XML 파일, 혹은클래스의생 성자를이용하여 Drawable 을정의하여활용할수있다. 생성자를이용한방법은이미알고 있는방법이기때문에앞의두가지경우만을설명한다. 12.1.1.1 이미지로부터생성 개발하는응용에그래픽스이미지를더하는간단한방법은프로젝트의자원폴더인 res/drawable/ 에이미지를저장하는것이다. 저장가능한이미지파일형태는 *.PNG( 가장 적합), *.JPG( 사용가능), *.BMP( 사용가능), *.GIF( 사용가능, 비적합) 등이다. 이기법은아 이콘, 로고, 혹은게임에서사용하는간단한그래픽이미지에적합하다. 폴더에저장된이미 지는안드로이드소스코드나 XML 파일에서참조할수있다. 참조할때는확장자 PNG, JPG, BMP, GIF 를제외하고파일이름을직접사용하면된다. 예제소스코드 다. 다음은폴더에저장된영상을사용하여 ImageView를생성하고 Layout에더하는코드이 LinearLayout mlinearlayout; protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); mlinearlayout = new LinearLayout(this); ImageView im = new ImageView(this); im.setimageresource(r.drawable.my_image); im.setadjustviewbounds(true); im.setlayoutparams(new Gallery.LayoutParams.WRAP_CONTENT); mlinearlayout.addview(im); setcontentview(mlinearlayout); - 64 -
그밖의경우에서는, 이미지자원을 Drawable 객체로사용할수있다. 다음은코드이다. Resources res = mcontext.getresource(); Drawable myimage = res.getdrawble(r.drawable.my_image); 12.1.1.2 XML 파일로부터생성 Drawable 객체를 XML 파일에정의할수있다. 비록 Drawable 객체의속성이프로그램 수행중에변경되더라도 Drawable 객체를 XML 파일에정의할수있다. Drawable 객체를 정의한 XML 파일은 res/drawable/ 폴더에저장한다. 다음은간단히 Drawable 자원을 XML 파일의 ImageView 에저장하는방법이다. 예제 XML 소스코드 <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:tint="#55ff0000" android:src="@drawable/my_image"/> 안드로이드소스코드에서는 Resources.getDrawable() 메소드를호출하여객체를출력한 다. Inflate() 메소드를지원하는 Drawable 서브클래스는 XML도정의될수있으며프로그 램에서사용될수있다. 다음은 TansitionDrawable 객체를사용한간단한예이다. TransitionDrawable은 LayerDrawable 을확장한것으로서첫번째와두번째층사이에서이미지가서서히사라 지는현상을구현할때사용한다. 예제소스코드 <transition xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="drawable/expand_collapse"> </transition> 폴더 res/drawable/expand_collapse.xml 에저장된이 XML 파일을사용하여, 다음안드 로이드코드는 TransitionDrawable 을 ImageView 에나타낸다. Resources res = mcontext.getresources(); TransitionDrawable transition = (TransitionDrawable) res.getdrawable(r.drawable.expand_collapse); ImageView image = (ImageView) findviewbyid(r,id.toggle_image); image.setimagedrawable(transition); 다음코드를수행하면이 transition이 1 초동안수행된다. - 65 -
transition.starttransition(1000); 다음은 Drawable 클래스의내용이다. - 인터페이스 Animatable Drawable Callback 애니메이션을위하여필요한인터페이스애니메이션을위한 Drawable - 클래스 AnimationDrawable Frame 애니메이션을위한객체 BitmapDrawable Bitmap 을객체화하여기울기, 확장, 배치 ClipDrawable Drawable 을클립하는객체 ColorDrawable 지정된색깔로 Canvas를채우는 Drawable Drawable 그림을그리는대표적인객체 Drawable.ConstantState DrawableContainer DrawableContanier.DrawableContainerState GradientDrawable Color gradient drawable 객체 InsetDrawable 다른 Drawable을삽입하는객체 LayerDrawable 다른 Drawable 배열을관리하는 Drawable LevelListDrawable 다른 Drawable을관리하는자원 NinePatchDrawable 확장가능한 Bitmap PaintDrawable Paint로그림을그리는 Drawable PictureDrawable Picture를객체로만드는 Drawable RotateDrawable 다른 Drawable을회전시키는 Drawable ScaleDrawable 다른 Drawable을확장시키는 Drawable ShapeDrawable 기본 Shape를그리는 Drawable 객체 ShapeDrawable.ShaderFactory Factory 객체를정의하는기본클래스 StateListDrawable 여러개의이미지를 Drawable에부여 TransitionDrawable LayerDrawable의확장 - Enums GradientDrawable.Orientation Gradient의방향을조정 12.1.2 Canvas 그림을그리거나애니메이션을하고자할때, Canvas 객체를사용하는방법도있다. Canvas 는그림이그려지는실제표면처럼작동하여그림을그린다. Canvas 객체는개발자 의모든 draw" 명령을수행한다. Canvas에그림을그릴때에는 4 가지요소가필요하다. 하나는그림의픽셀을저장할 Bitmap 이고, 두번째는 draw 호출을실행할 Canvas 자체이 고, 세번째는 Rect, Path, Text, Bitmap 등의기본그림요소(drawing primitive) 이고, 마 지막으로는색과스타일을지정하는 Paint 객체이다. 즉, Canvas를통하여그리는그림은 - 66 -
실제로는하위 Bitmap 객체위에서실행되며 Bitmap 은윈도우위에구성된다. ondraw() 콜백메소드를이용하여그림을그릴때에, 개발자는단지 Canvas에자신의 그림을그리는코드를위치시키면된다. 다음은간단한예제코드이다. 이코드는 Bitmap 객체 b를선언하고이객체를 Canvas c 에설정하는방법을제시한다. Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); Canvas는정의된 Bitmap 에그림을그린다. 그림을 Canvas에그린후에는 Bitmap을다른 Canvas로 Canvas.drawBitmap(Bitmap,...) 방식을사용하여옮길수도있다. 그림을그릴 때에는 View.onDraw() 혹은 SurfaceHolder.lockCanvas() 방식을사용하는것이좋다. Canvas 클래스는 drawbitmap(...), drawrect(...), drawtext(...) 도가지고있다. 다음은 Canvas 클래스의메소드들이다. 등의자체드로잉메소드 - 내포클래스 enum enum Canvas.EdgeType Canvas.VertexMode - 상수 int ALL_SAVE_FLAG 모든파일저장 int CLIP_SAVE_FLAG 현재클립저장 int CLIP_TO_LAYER_SAVE_FLAG 레이어바운드클립 int FULL_COLOR_LAYER_SAVE_FLAG int HAS_ALPHA_LAYER_SAVE_FLAG int MATRIX_SAVE_FLAG 현재매트릭스저장 - Public constructor Canvas() 빈래스터 canvas를생성 Canvas(Bitmap bitmap) 지정된 bitmap을사용하여 canvas 생성 Canvas(GL gl) 지정된 gl context를사용하여 canvas 생성 - Public Method 다음은 Canvas 의클래스의메소드들이다. Canvas 클래스가가지고있는메소드는매우 많으며그중에서일부만제시한다. void void drawbitmap(int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasalpha, Paint paint) drawbitmap(bitmap bitmap, Rect src, Rect dst, Paint paint) - 67 -
void void void void void void void void void void void void void void void void void void void void...... 등등 drawbitmap(bitmap bitmap, float left, float top, Pain t paint) drawbitmap(int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasalpha, Paint paint) drawbitmap(bitmap bitmap, Rect src, RectF dst, Paint paint) drawbitmapmesh(bitmap bitmap, int meshwidth, int mes hheight, float[] verts, int vertoffset, int[] colors, int coloroffset, Paint paint) drawcircle(float cx, float cy, float radius, Paint paint) drawcolor(int color) drawcolor(int color, PorterDuff.Mode mode) drawline(float startx, float starty, float stopx, float stopy, Paint paint) drawlines(float[] pts, Paint paint) drawlines(float[] pts, int offset, int count, Paint paint) drawoval(rectf oval, Paint paint) drawpaint(paint paint) drawpath(path path, Paint paint) drawpicture(picture picture, RectF dst) drawpicture(picture picture, Rect dst) drawpicture(picture picture) drawpoint(float x, float y, Paint paint) drawpoints(float[] pts, int offset, int count, Paint paint) drawpoints(float[] pts, Paint paint) drawpostext(char[] text, int index, int count, float[] pos, Paint paint) 12.1.3 View View 클래스는사용자인터페이스를위한기본객체이다. android.view.view 에존재한 다. View는스크린의사각형영역으로서드로잉과이벤트- 핸들링을처리한다. View는위 젯(widget) 의기본클래스이다. ViewGroup 클래스는 View 클래스의서브클래스로서 Layout 을담당한다. 윈도우의 View 는트리구조로구성된다. 안드로이드 Java 코드에서 View를선언할수도있고 XML 파일에서 View 를선언할수도있다. View를구성한다음 에는다음을수행하여야한다.. TextView의 text 결정. View 초점결정: requestfocus() 메소드에의한초점결정. 이벤트-핸들링을위한 Listener 구성. Visibility 결정: View 의가시성결정(setVisibility(int)) 커스텀 View를구현하기위해서는다음의메소드를 override 하여야한다. 그러나일반적 으로는 ondraw() 만 override 하여도그림을그리는것은가능하다. - 68 -
분류 메소드 설명 생성자 View 생성자 Creation View와하위객체들이모두 onfinishinflate() 전개되면호출된다. onmeasure(int, int) View와하위객체들의크기를결정하기위하여호출 Layout onlayout(boolean, int, int, int, int) View가하위자식들에게크기와위치와부여할때 onsizechanged(int, int, int, int) View의크기가변경되었을때 Drawing ondraw 그림을그려야할때 onkeydown Key down 이벤트 Event processing onkeyup Key up 이벤트 ontrackballevent Trackball motion 이벤트 ontouchevent Touch screen motion 이벤트 Focus onfocuschangedevent() View focus 조정 onwindowfocuschanged() View window focus 변화 onattachedtowindow() View window에연결 Attaching ondetachedfromwindow() View window로부터분리 onwindowvisibilitychanged() Window visibility 변화 만약안드로이드응용이많은양의그래픽을실행하지않거나혹은낮은프레임처리율 에서실행된다면, 일반적으로커스텀 View 컴포넌트를생성하여 View.onDraw() 에서그림 을그리는것이대부분이다. 시작하는방법은, 우선 View 클래스를확장하고 ondraw() 콜백메소드를정의한다. 이 메소드는안드로이드시스템에의하여호출되어 View 에그림을그리도록한다. 호출이전 에 View 객체는 invalidate() 호출에의하여 invalidate() 된다. 12.1.4 ViewGroup ViewGroup은다른 View를포함할수있는특별한 View이며 Layout과 View 컨테이너 를포함한다. 또한 ViewGroup은 Layout 파라메타를정의한 ViewGroup.LayoutParams 클 래스를포함하고있다. ViewGroup에서 Activity Layout 을정의한다. ViewGroup의직접 서브클래스는 Layout을정의하는 AbsoluteLayout, FrameLayout, LinearLayout, RelativeLayout, SlidingDrawer, AdapterView<T extends Adapter> 이다. 12.1.5 SurfaceView SurfaceView는 View 클래스의특별한하위클래스이다. 이객체의목적은멀티쓰래딩 환경에서두번째쓰래드를생성하여그림을그릴수있도록하는것이다. 즉, 첫번째쓰 래드에서 View 객체가그림을그리는것을완료할때까지기다리지않고, 응용의 SurfaceView 객체가두번째쓰래드를이용하여바로자신의 Canvas에그림을그리도록 하는것이다. 응용을개발하기위해서는 SurfaceView 클래스를확장하고인터페이스 SurfaceHolder.Callback 을구현하여야한다. 이인터페이스는사용할 Surface 의생성, 변 - 69 -
화, 그리고소멸에대하여알려준다. 이중에서 Surface 생성이벤트가가장중요하다. 이 이벤트가있어야언제 SurfaceView 가생성되어그림을그릴수있는지알수있다. 12.1.6 Shape Shape 클래스는 java.lang.object 를상속한다. 직접하위클래스는 PathShape, RectShape 이다. 간접하위클래스는 ArcShape, OvalShape, RoundRectShape 이다. Shape 클래스는그래픽도형을 Canvas 위에 draw() 메소드를사용하여그린다. ShapeDrawable() 을사용하면더욱정교한그래픽조정을할수있다. - Public constructor Shape() - Public Method Shape() clone() this 객체를생성하여반환 abstract void draw(canvas canvas, Paint paint) 현재도형 Canvas에 Paint로그림 final float getheight() 도형높이반환 final float getwidth() 도형넓이반환 boolean hasalpha() 도형불투명성검사 final void resize(float width, float height) 도형크기변경 - Protected Method void onresize(float width, float height) callback method 12.1.7 ShapeDrawable ShapeDrawable 클래스는 java.lang.object 하위의 android.graphics.drawable.drawable 클래스를상속한다. 직접하위클래스는 PaintDrawable 이다. Drawable 클래스는기본도 형을그리는반면 ShapeDrawable 클래스는 Shape 객체를받아서화면에도형을그린다. Shape가주어지지않으면 ShapeDrawable 은디폴트로서 RectShape 를그린다. 2 차원그래픽을동적으로그리기위해서는, ShapeDrawable 객체가적합하다. ShapeDrawable 을사용하면기본도형을구성할수있고도형의속성을변경할수있다. View의배경으로는 setbackgrounddrawable() 을사용할수있다. 다음은 View를확장하 여 ShapeDrawable 에그림을그리는코드이다. 예제안드로이드소스코드 public class CustomDrawableView extends View { private ShapeDrawable mdrawable; - 70 -
public CustomDrawableView(Context context) { super(context); int x = 100; int y = 100; int width = 600; int height = 1000; mdrawable = new ShapeDrawable(new OvalShape()); mdrawable.getpaint().setcolor(0xff74ac23); mdrawable.setbounds(x, y, x+width, y+height); protected void ondraw(canvas canvas) { mdrawable.draw(canvas); 생성자를보면, ShapeDrawable에는 OvalShape 가정의되어있다. 또한도형의 color와도 bound 가정의되어있다. 도형의 color 가정의되어있지않으면디폴트(default) 로서검은색 으로지정되고, bound 가정의되지않으면도형은그려지지않는다. 정의된 CustomDrawableView 는다음과같이프로그램에서사용하여그림을그릴수있다. CustomDrawableView mcustomdrawableview; protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); mcustomdrawableview = new CustomDrawableView(this); setcontentview(mcustomdrawableview); 다음은 ShapeDrawable 클래스의메소드에대한설명이다. - 내포클래스 class ShapeDrawable.ShapeFactory 기반클래스는 shape factory 객체를정의한다. - XML 속성 Attribute Name Related Method Description android:bottom 아래부분 padding android:color Shape의색상정의 android:height Shape의높이정의 android:left 왼쪽 padding android:right 오른쪽 padding android:top 상단부분 padding android:width Shape의폭정의 - Public constructor - 71 -
ShapeDrawable() ShapeDrawable 생성자 ShapeDrawable(Shape s) 지정된 Shape로 ShapeDrawable 생성 - Public Method void draw(canvas canvas) Canvas에도형을그린다 int getchangingconfiguration() Configuration 인자의 mask 반환 Drawable. ConstantState getconstantstate() int getintrinsicheight() 객체높이반환 int getintrinsicwidth() 객체넓이반환 int getopacity() 객체의투명성/ 반투명성반환 boolean getpadding(rect padding) Padding 반환 Paint getpaint() 도형을그리는 Paint 반환 ShapeDrawable. getshaderfactory() ShapeDrawable의 ShaderFactory 반환 ShaderFactory Shape getshape() ShapeDrawable의 Shape 반환 void inflate(resources r, XmlPullParser parser, AttributeSet attrs) Drawable mutate() 현재의 mutable을 drawable로반환 void setalpha(int alpha) 현재 drawable의 alpha 레벨 void setcolorfilter(colorfilter cf) 현재 drawable에 colorfilter 설정 void setdither(boolean dither) Dither 기능활성화 void getintrinsicheight() Shape의높이설정 void getintrinsicwidth() Shape의넓이설정 void setpadding(int left, int top, int right, int bottom) Shape의 padding 설정 void setpadding(rect padding) Rect 객체의 padding 설정, void setshaderfactory(shapedra ShaderFactory 설정 wable.shaderfactory fact) void setshape(shape s) 현재 ShapeDrawable의 Shape 설정 - Protected Method boolean inflatetag(string n, Resources r, XmlPullParser p, AttributeSet a) void onboundschange(rect bounds) void ondraw(shape s, Canvas c, Paint p) 하위클래스는 override하여커스텀 서브엘리먼트를파싱한다서브클래스에서 override하여객체 모양을바꾼다. Drawable의 draw() 로부터호출되어 좌표 (0,0) 에 Shape 를그린다. - 72 -
12.1.8 ImageView ImageView는 android.view.view 의서브클래스이다. Icon과같은이미지를출력하는데 사용된다. ImageView 클래스는자원그리고 content provider에서이미지들을가져올수 있다. Layout 관리자와연계되어사용되며확장이나착색( 틴팅, tinting) 기능도제공한다. 12.1.9 Bitmap java.lang.object 의서브클래스이다. Bitmap 이미지를생성하고관리하는기능을제공한다. Canvas 객체가실제로그림을그리는객체이다. - 73 -
12.2 예제안드로이드프로그램과결과 다음은안드로이드예제프로그램소스코드와실행결과이다. 12.2.1XML 파일로직사각형을그리는프로그램. 초록색직사각형을 import android.app.activity; import android.os.bundle; import android.view.view; import android.widget.imageview; res/drawable/green_rect.xml 로정의하여사용하였다. public class Rectangle extends Activity { /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); ImageView iview=(imageview)findviewbyid(r.id.imageview1); iview.setimageresource(r.drawable.green_rect); res/layout/main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:layout_height="wrap_content" android:text="@string/hello" /> <ImageView android:id="@+id/imageview1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minheight="100px" android:minwidth="60px" android:layout_margin="115px" /> </LinearLayout> res/drawable/green_rect.xml <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#0f0" android:width="5dp" android:height="20dp" /> </shape> - 74 -
( 그림 ) 12.2.2 안드로이드코드에서직사각형을그리는프로그램. ShapeDrawable 클래스를사용하여빨간색직사각형을정의하였다. import android.app.activity; import android.graphics.color; import android.graphics.drawable.shapedrawable; import android.graphics.drawable.shapes.rectshape; import android.os.bundle; import android.widget.imageview; public class Rectangle extends Activity { /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); ShapeDrawable rect=new ShapeDrawable(new RectShape()); rect.getpaint().setcolor(color.magenta); rect.setintrinsicheight(200); rect.setintrinsicwidth(400); ImageView iview=(imageview)findviewbyid(r.id.imageview1); iview.setimagedrawable(rect); res/layout/main.xml - 75 -
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:layout_height="wrap_content" android:text="@string/hello" /> <ImageView android:id="@+id/imageview1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minheight="100px" android:minwidth="60px" android:layout_margin="115px" /> </LinearLayout> ( 그림 ) 12.2.3 둥근모서리사각형그리는프로그램 다음은모서리가둥근사각형을그리는프로그램이다. 각형을나타낸다. 다음코드부분이모서리가둥근사 float[] outerradii=new float[] {6,6,6,6,6,6,6,6; RectF insetrectangle=new RectF(2,2,2,2); float[] innerradii=new float[] {6,6,6,6,6,6,6,6; - 76 -
import android.app.activity; import android.os.bundle; import android.graphics.rectf; import android.graphics.color; import android.graphics.drawable.shapedrawable; import android.graphics.drawable.shapes.roundrectshape; import android.widget.imageview; public class RoundArcRect extends Activity { /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); float[] outerradii=new float[] {6,6,6,6,6,6,6,6; RectF insetrectangle=new RectF(2,2,2,2); float[] innerradii=new float[] {6,6,6,6,6,6,6,6; ShapeDrawable rndrect=new ShapeDrawable (new RoundRectShape( outerradii, insetrectangle, innerradii)); rndrect.setintrinsicheight(50); rndrect.setintrinsicwidth(100); rndrect.getpaint().setcolor(color.white); ImageView iview=(imageview)findviewbyid(r.id.imageview1); iview.setimagedrawable(rndrect); res/layout/main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:layout_height="wrap_content" android:text="@string/hello" /> <ImageView android:id="@+id/imageview1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minheight="100px" android:minwidth="100px" android:layout_margin="115px" /> </LinearLayout> - 77 -
( 그림 ) 12.2.4 텍스트와폰트프로그램 다음은텍스와폰트에대한예제프로그램이다. import android.app.activity; import android.os.bundle; import android.content.*; import android.view.*; import android.graphics.canvas; import android.graphics.color; import android.graphics.paint; import android.graphics.typeface; public class TxtFont extends Activity { /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(new TxFont(this)); class TxFont extends View { /** Called when the activity is first created. */ public TxFont(Context context) { super(context); - 78 -
protected void ondraw(canvas canvas) { canvas.drawcolor(color.white); Paint tpaint=new Paint(Paint.ANTI_ALIAS_FLAG); Typeface ttype1 = Typeface.create(Typeface.SERIF, Typeface.ITALIC); tpaint.settextsize(15); tpaint.settypeface(ttype1); canvas.drawtext("serif Italic Typeface", 20, 20, tpaint); Typeface ttype2 = Typeface.create(Typeface.SANS_SERIF, Tpeface.NORMAL); tpaint.settextsize(20); tpaint.settypeface(ttype2); canvas.drawtext("sans_serif Normal Typeface", 30, 50, tpaint); ( 그림 ) 12.2.5 별을그린경로객체프로그램 import android.app.activity; import android.os.bundle; import android.graphics.color; import android.graphics.path; import android.graphics.drawable.shapedrawable; import android.graphics.drawable.shapes.pathshape; import android.widget.imageview; public class Stella extends Activity { /** Called when the activity is first created. */ - 79 -
public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); Path p = new Path(); p.moveto(50, 0); //1 p.lineto(25, 200); //2 p.lineto(100, 50); //3 p.lineto(0, 50); //4 p.lineto(75, 200); //5 p.lineto(50, 0); //6 ShapeDrawable stella=new ShapeDrawable(new PathShape(p, 100, 100)); stella.setintrinsicheight(50); stella.setintrinsicwidth(100); stella.getpaint().setcolor(color.magenta); ImageView iview=(imageview)findviewbyid(r.id.imageview1); iview.setimagedrawable(stella); res/layout/main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:layout_height="wrap_content" android:text="@string/hello" /> <ImageView android:id="@+id/imageview1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minheight="300px" android:minwidth="400px" android:layout_margin="80px" /> </LinearLayout> - 80 -
( 그림 ) 12.2.6 Bitmap 이미지출력프로그램 다음은 Bitmap 이미지출력프로그램이다. 하나의이미지를확대하여다시나타냈다. import android.app.activity; import android.os.bundle; import android.content.*; import android.view.*; import android.graphics.canvas; import android.graphics.color; import android.graphics.bitmap; import android.graphics.bitmapfactory; public class Bitmp extends Activity { /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(new Btmp(this)); class Btmp extends View { /** Called when the activity is first created. */ public Btmp(Context context) { super(context); protected void ondraw(canvas canvas) { - 81 -
canvas.drawcolor(color.white); Bitmap pic1=bitmapfactory.decoderesource(getresources(), R.drawable.icon); canvas.drawbitmap(pic1, 130, 60, null); Bitmap pic2=bitmap.createscaledbitmap(pic1, 200, 200, false); canvas.drawbitmap(pic2, 60, 150, null); ( 그림 ) 12.2.7 방사형그래디언트프로그램 다음은방사형그래디언트프로그램이다. import android.app.activity; import android.os.bundle; import android.content.*; import android.view.*; import android.graphics.canvas; import android.graphics.color; import android.graphics.radialgradient; import android.graphics.paint; import android.graphics.shader; public class RadGradient extends Activity { /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { - 82 -
super.oncreate(savedinstancestate); // setcontentview(r.layout.main); setcontentview(new RGradient(this)); class RGradient extends View { /** Called when the activity is first created. */ public RGradient(Context context) { super(context); protected void ondraw(canvas canvas) { canvas.drawcolor(color.black); Paint circlepaint=new Paint(Paint.ANTI_ALIAS_FLAG); RadialGradient radgrad = new RadialGradient(canvas.getWidth()/2, canvas.getheight()/3, 50, Color.RED, Color.GREEN, Shader.TileMode.MIRROR); circlepaint.setshader(radgrad); canvas.drawcircle (canvas.getwidth()/2, canvas.getheight()/3, canvas.getwidth()/4, circlepaint); ( 그림 ) 12.2.8 스윕그래디언트프로그램 다음스윕(sweep) 그래디언트프로그램이다. import android.app.activity; - 83 -
import android.os.bundle; import android.content.*; import android.view.*; import android.graphics.canvas; import android.graphics.color; import android.graphics.sweepgradient; import android.graphics.paint; import android.graphics.shader; public class SwpGradient extends Activity { /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(new SwGradient(this)); class SwGradient extends View { /** Called when the activity is first created. */ public SwGradient(Context context) { super(context); protected void ondraw(canvas canvas) { canvas.drawcolor(color.black); Paint circlepaint=new Paint(Paint.ANTI_ALIAS_FLAG); SweepGradient radgrad = new SweepGradient (canvas.getwidth()/2, canvas.getheight()/3, new int[] {Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.MAGENTA, null); circlepaint.setshader(radgrad); canvas.drawcircle(canvas.getwidth()/2, canvas.getheight()/3, canvas.getwidth()/4, circlepaint); ( 그림 ) - 84 -
12.3 애니매이션 12.3.1 프레임 (frame) 애니메이션 프레임애니메이션은전통적인의미의애니메이션으로서약간씩다른몇개의영상을연 속적으로실행하여구현한다. AnimationDrawable 클래스가프레임애니메이션의기본클 래스이다. 프레임애니메이션은 AnimationDrawable API를사용하여안드로이드 Java 코드 에서수행할수도있고, XML 파일에서수행할수도있다. XML 파일은 res/anim/ 디렉토 리에저장된다. XML 파일은 <animation-list> 요소를루트요소로그리고 <item> 요소들 을하위자식노드로구성된다. 다음은프레임애니메이션을위한 XML 파일이다. <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true'> <item android:drawable="@drawable/rocket_thrust1" android:duration="200" /> <item android:drawable="@drawable/rocket_thrust2" android:duration="200" /> <item android:drawable="@drawable/rocket_thrust3" android:duration="200" /> </animation-list> 이애니메이션은세개의프레임을사용하여실행된다. 만약 android:oneshot 속성을 true 로한다면애니메이션은한번실행되고마지막프레임에서멈춘다. 만약 android:oneshot 속성을 false 로하면애니메이션은계속실행된다. 다음은 rocket_thrust.xml을프레임애니 메이션으로구현한안드로이드 Java 소스코드의예를보여준다. AnimationDrawable rocketanimation; public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); ImageView rocketimage = (ImageView) findviewbyid(r.id.rocket_image); rocketimage.setbackgoundresource(r.anim.rocket_thrust); rocketanimation = (AnimationDrawable) rocketimage.getbackground(); public boolean ontouchevent(motionevent event) { if (event.getaction() == MotionEvent.ACTION_DOWN) { rocketanimation.start(); return true; return super.ontouchevent(event); 이코드에서주의할부분은 AnimationDrawable 에서사용된 start() 메소드는 oncreate() 메소드에서는호출될수없다는것이다. 왜냐하면, AnimationDrawable이아직윈도우에완 - 85 -
전히밀착되지못했기때문이다. 애니메이션을바로실행하기위해서는, start() 메소드를 onwindowfocuschanged() 에서호출하여야한다. 12.3.2 트윈 (tween) 애니메이션 트윈애니메이션은 View 객체에대하여간단한변환( 이동, 확장, 회전등) 을계속수행하 는애니메이션을의미한다. 만약 TextView 객체가있으면, 이객체를이동, 회전, 확대, 혹 은축소하는애니메이션을의미한다. 만약에배경이미지가있으면, 배경이미지도텍스트 와함께변환된다. android.view.animation 패키지는트윈애니메이션과관련된모든클래 스를제공한다. 트윈애니메이션은 XML 코드에의하여정의될수도있고안드로이드코드에의하여정 의될수도있다. 일반적으로 Layout을정의할때처럼 XML 파일로트윈애니메이션을정의 하는것을권장한다. 왜냐하면 XML 파일로정의하면, 더읽기쉽고재사용도쉽기때문이 다. 아래의예에서는 XML 을사용한다. 애니메이션 XML 파일은 res/anim/ 디렉토리에저장된다. 애니메이션파일은오직하나 의루트(root) 요소를가지고있어야한다. 이루트요소는 <alpha>, <scale>, <translate>, <rotate>, <interpolate element>, <set> 요소이다. <set> 요소는다른요소를원소로가 지는그룹요소이다. 디폴트로서, 모든애니메이션은명령과동시에실행된다. 명령을순차 적으로실행하려면, startoffset 속성을설정하여야한다. 다음 XML 파일은 ApiDemo 예로서 View 객체를확장하고회전시킨다. <set android:shareinterpolator = "false"> <scale android:interpolator ="@android;anim/accelerate_decelerate_interpolator" android:fromxscale = "1.0" android:toxscale = "1.4" android:fromyscale = "1.0" android:toyscale = "0.6" android:pivotx = "50%" android:pivoty = "50%" android:fillafter = "false" android:duration = "700"/> <set android:interpolator = "@android:anim/decelerate_interpolator"> <scale android:fromxscale = "1.4" android:toxscale = "0.0" android:fromyscale = "0.6" android:toyscale = "0.0" android:pivotx = "50%" android:pivoty = "50%" android:startoffset = "700" android:duration = "400" android:fillbefore ="false" /> <rotate android:fromdegrees = "0" android:todegrees = "-45" android:toyscale = "0.0" android:pivotx = "50%" - 86 -
</set> </set> android:pivoty = "50%" android:startoffset = "700" android:duration = "400" /> pivotx 값은객체자체혹은상위부모객체에대하여상대적으로설정될수있다. 그러나 알맞은포맷을사용하여야한다. 예를들어, 50 은상위부모에대하여 50% 상대적이라는 의미이고, 50% 은객체자체에대하여 50% 상대적이라는의미이다. 2차원객체의변환이시간에따라서어떻게적용되는가는 Interpolator 객체를통하여 조정할수있다. 안드로이드는스피드커브를조정하는몇개의 interpolator 서브클래스를 포함하고있다. 예를들어 AccelerateInterpolator 는변환의속도를처음에는천천히시작 하여점점빠르게조정한다. 예를들어서, 다음 hyperspace_jump.xml XML 파일이 res/anim/ 폴더에있다면, 안드로이드 Java 코드는이파일을참조하여 ImageView 객체에 적용할수있다. ImageView spaceshipimage = (Image) findviewbyid(r.id.spaceshipimage); Animation hyperspacejumpanimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump); spaceshipimage.startanimation(hyperspacejumpanimation); startanimation() 에대한대안으로서, 애니메이션시작시간을 Animation.setStartTime() 으 로설정할수있다. 다음에애니메이션을 View에 View.setAnimation() 으로설정할수있 다. 12.3.3 애니메이션클래스 Animation 클래스는 android.view.animation.animation이며 java.lang.object를상속한 다. 관련된하위클래스는 AlphaAnimation, AnimationSet, RotateAnimation, ScaleAnimation, TranslateAnimation 이다. 애니메이션클래스는 View, Surface, 그리고 다른객체에적용된다. - 내포클래스 인터페이스 Animation.AnimationListener Notification을받는다 클래스 Animation.Description 크기를파싱하는유틸리티클래스 - XML 속성 속성이름 관련된메소드 설명 android:detachwallpaper setdetachwallpaper(boolean) 윈도우애니메이션옵션 android:duration setduration(long) 애니메이션수행시간 android:fillafter setfillafter(boolean) True 이면, 애니메이션완료후애니메이션변환적용 - 87 -
android:fillbefore setfillbefore(boolean) True 이면, 애니메이션완료전애니메이션변환적용 android:fillenabled setfillenabled(boolean) True 이면, fillafter 적용 android:interpolator setinterpolator(interpolator) 애니메이션보간적용 android:repeatcount setrepeatcount(int) 애니메이션반복회수 android:repeatmode setrepeatmode(int) 애니메이션행동정의 android:startoffset setstartoffset(long) 애니메이션수행지연시간정의( 밀리세컨드) android:zadjustment setzadjustment(int) 애니메이션 Z 순서조정 - 상수 int ABSOLUTE 픽셀개수 int INFINITE 애니메이션무한실행 int RELATIVE_TO_PARENT 부동소수점수로서현재객체의부모객체의폭혹은높이와곱해진다. int RELATIVE_TO_SELF 부동소수점수로서현재의애니메이션객체의폭혹은높이와곱해진다. int RESTART 애니메이션이마지막에도달하고 INFINITE_REPEAT 이거나양수면애니메이션은처음부터시작 int REVERSE 애니메이션이마지막에도달하고 INFINITE_REPEAT 이거나양수면애니메이션은역방향으로진행 int START_ON_FIRST_FRAME 애니메이션시작시간 int ZORDER_BOTTOM 애니메이션 Z 순서의영향을 받도록조정 int ZORDER_NORMAL 애니메이션이현재 Z 순서로 유지되도록한다 int ZORDER_TOP 애니메이션이다른내용의 최상위가되도록한다. - Public Constructors Animation( ) Animation(Context context, AttributeSet attrs) 디폴트값을사용하여새로운애니메이션생성 context, attrs 인자로새로운애니메이션생성 - Public Method long boolean long boolean boolean interpolator computedurationhint() getdetachwallpaper() getduration() getfillafter() getfillbefore() getinterpolator() - 88 -
int getrepeatcount() int getrepeatmode() long getstartoffset() long getstarttime() boolean gettransformation(long currenttime, Transformation outtransformation) int getzadjustment() boolean hasended() boolean hasstarted() void initialize(int width, int height, int parentwidth, int parentheight) boolean isfillenabled() boolean isinitialized() void reset() void restrictduration(long durationmillis) void scalecurrentduration(float scale) void setanimationlistener(animation.animationlistene r listener) void setdetachwallpaper(boolean detachwallpaper) void setduration(long durationmillis) void setfillafter(boolean fillafter) void setfillbefore(boolean fillbefore) void setfillenabled(boolean fillenabled) void setinterpolator(context context, int resid) void setinterpolator(interpolator i) void setrepeatcount(int repeatcount) void setrepeatmode(int repeatmode) void setstartoffset(long starttimemillis) void setstarttime(long starttimemillis) void setzadjustment(int zadjustment) void start() void startnow() boolean willchangebounds() boolean willchangetransformation(int type, float value, int size, int parentsize) - Protected method void applytransformation(float interpolationtime, Tran sformation t) Animation clone() void ensureinterpolator() void resolvesize(int type, float value, int size, int parentsize) - 89 -
12.4 예제애니메이션프로그램과결과 12.4.1 프레임애니메이션 다음은프레임애니메이션프로그램과실행결과이다. import android.app.activity; import android.graphics.drawable.animationdrawable; import android.graphics.drawable.bitmapdrawable; import android.os.bundle; import android.view.view; import android.widget.button; import android.widget.imageview; public class FrameAnimation extends Activity { AnimationDrawable frameanimation = null; protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.frame); // Handle 시작버튼 final Button onbutton = (Button) findviewbyid(r.id.buttonstart); onbutton.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { startanimation(); ); // Handle 중지버튼 final Button offbutton = (Button) findviewbyid(r.id.buttonstop); offbutton.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { stopanimation(); ); private void startanimation() { ImageView img = (ImageView)findViewById(R.id.ImageView1); // Three frames for frame animation BitmapDrawable frame1 = (BitmapDrawable)getResources().getDrawable(R.drawable.f1); BitmapDrawable frame2 = (BitmapDrawable)getResources().getDrawable(R.drawable.f2); BitmapDrawable frame3 = (BitmapDrawable)getResources().getDrawable(R.drawable.f3); // Get the background int Duration = 50; frameanimation = new AnimationDrawable(); frameanimation.setoneshot(false); // loop continuously - 90 -
frameanimation.addframe(frame1, Duration); frameanimation.addframe(frame2, Duration); frameanimation.addframe(frame3, Duration); img.setbackgrounddrawable(frameanimation); frameanimation.setvisible(true,true); frameanimation.start(); private void stopanimation() { frameanimation.stop(); frameanimation.setvisible(false,false); res/layout/frame.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <LinearLayout android:id="@+id/buttonrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/buttonstart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 시작" /> <Button android:id="@+id/buttonstop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 끝" /> </LinearLayout> <ImageView android:id="@+id/imageview1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> res/drawable f1.bmp, f2.bmp, f3.bmp ( 그림 ) - 91 -
12.4.2 트윈애니메이션 다음은트윈애니메이션프로그램과결과이다. import android.app.activity; import android.os.bundle; import android.view.view; import android.view.animation.animation; import android.view.animation.animationutils; import android.widget.button; import android.widget.imageview; public class TweenAni extends Activity { protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.tween); // Handle Scale Button final Button growbutton = (Button) findviewbyid(r.id.buttonscale); growbutton.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { performanimation(r.anim.scale); ); // Handle Move Button final Button movebutton = (Button) findviewbyid(r.id.buttontranslate); movebutton.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { performanimation(r.anim.translate); ); // Handle Spin Button final Button spinbutton = (Button) findviewbyid(r.id.buttonrotate); spinbutton.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { performanimation(r.anim.rotate); ); private void performanimation(int animationresourceid) { // We will animate the imageview ImageView reusableimageview = (ImageView)findViewById(R.id.ImageViewForTweening); reusableimageview.setimageresource(r.drawable.green_rect); reusableimageview.setvisibility(view.visible); // Load the appropriate animation Animation an = AnimationUtils.loadAnimation(this, animationresourceid); // Register a listener, so we can disable and re-enable buttons an.setanimationlistener(new MyAnimationListener()); // Start the animation reusableimageview.startanimation(an); - 92 -
private void togglebuttons(boolean clickablestate) { // Handle Scale Button final Button growbutton = (Button) findviewbyid(r.id.buttonscale); growbutton.setclickable(clickablestate); // Handle Move Button final Button movebutton = (Button) findviewbyid(r.id.buttontranslate); movebutton.setclickable(clickablestate); // Handle Spin Button final Button spinbutton = (Button) findviewbyid(r.id.buttonrotate); spinbutton.setclickable(clickablestate); class MyAnimationListener implements Animation.AnimationListener { public void onanimationend(animation animation) { // Hide our ImageView ImageView reusableimageview = (ImageView)findViewById(R.id.ImageViewForTweening); reusableimageview.setvisibility(view.invisible); // Enable all buttons once animation is over togglebuttons(true); public void onanimationrepeat(animation animation) { // what to do when animation loops public void onanimationstart(animation animation) { // Disable all buttons while animation is running togglebuttons(false); res/layout/tween.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent"> <TextView android:layout_height="wrap_content" android:text="@string/tween" /> <LinearLayout android:id="@+id/buttonrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/buttonscale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 성장"></Button> <Button - 93 -
<Button android:id="@+id/buttontranslate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 이동"></Button> android:id="@+id/buttonrotate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 회전"></Button> </LinearLayout> <LinearLayout android:id="@+id/linearlayout01" android:layout_height="fill_parent" android:orientation="horizontal" android:layout_gravity="center_vertical center_horizontal"> <ImageView android:id="@+id/imageviewfortweening" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minheight="90px" android:minwidth="90px" android:layout_margin="115px"></imageview> </LinearLayout> </LinearLayout> res/anim/scale.xml <?xml version="1.0" encoding="utf-8"?> <!-- Over the course of 5 seconds, scale up and back --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareinterpolator="false"> <scale android:pivotx="50%" android:pivoty="50%" android:fromxscale="1.0" android:fromyscale="1.0" android:toxscale="4.0" android:toyscale="4.0" android:duration="3000" /> <scale android:startoffset="2500" android:duration="3000" android:pivotx="50%" android:pivoty="50%" android:fromxscale="1.0" android:fromyscale="1.0" android:toxscale="0.5" android:toyscale="0.5" /> </set> res/anim/rotate.xml <?xml version="1.0" encoding="utf-8"?> <!-- Over the course of 5 seconds, spin 360 --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareinterpolator="false"> <rotate android:fromdegrees="0" android:todegrees="700" - 94 -
</set> android:pivotx="50%" android:pivoty="50%" android:duration="10000" /> res/anim/translate.xml <?xml version="1.0" encoding="utf-8"?> <!-- Over the course of 5 seconds, move 150 up and then 100 back down the Y axis. --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareinterpolator="false"> </set> <translate android:toydelta="-100" android:fillafter="true" android:duration="2500" /> <translate android:toydelta="150" android:fillafter="true" android:duration="2500" android:startoffset="2500"/> ( 그림 ) - 95 -
13 장. 위치기반서비스 (Location-Based Service) 위치기반서비스는모바일사용자들에게필수적인것이다. 위치기반서비스는 android.location 패키지와 Google Maps 외부라이브러리를통하여제공된다. 위치기반 서비스에서가장중요한시스템서비스는 LocationManager 이며위치기반서비스를위한 API 를제공한다. 다른시스템서비스와마찬가지로 LocationManager 를직접객체화하는 것이아니라 getsystemservice(context.location_service) 호출을통하여 LocationManager 객체를얻는다. LocationManager 객체를얻으면다음 3가지기능을할 수있다. - LocationManager 로부터가장최근의위치정보를제공하는 LocationProvider 를구한다. - LocationProvider 로부터현재위치정보의제공을등록(register)/ 해제(unregister) 한다. - 모바일기기가정해진위도와경도범위에들어오면위치정보 Intent를작동시킬수 있도록등록(register)/ 해제(unregister) 한다. 그러나 AVD를통하여위치기반서비스를개발하는경우에는실제로 GPS로부터위도와 경도정보를받지는못한다. 이런경우에는 DDMS(Dalvik Debug Monitoring Service) 에서 위도와경도정보를입력할수있다. 위도와경도정보를입력하는방법에는 3가지방법이 있다. - 키보드에서입력하는방법 - GPX 파일을통하여입력하는방법 - KML 파일을통하여입력하는방법 간단한응용의경우에는키보드를통하여입력하는방법을사용하는것이편리하다. DDMS 에는디폴트값으로이미위도(latitude):37.422006, 경도(longitude):-122.084095 값이입 력되어있다. 13.1 간단한위치기반서비스프로그램 다음은간단한위치기반응용프로그램이다. 첫번째코드는안드로이드 Java 코드이다. 이프로그램은 LocationManager를얻은다음에 List<String> providers = locationmanager.getproviders(true); 에의하여위치정보제공자를얻는다. 위치정보제 공자로부터위도와경도정보를얻어서화면에출력한다. import java.util.list; import android.app.activity; import android.os.bundle; import android.content.context; import android.location.location; import android.location.locationmanager; import android.location.locationlistener; import android.widget.textview; - 96 -
public class BasicLoc extends Activity { /** Called when the activity is first created. */ LocationManager locationmanager; public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); locationmanager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); locproviders(); public void locproviders() { TextView tvw = (TextView)findViewById(R.id.myTextView); StringBuilder sb = new StringBuilder(" 위치정보공급자: "); List<String> providers = locationmanager.getproviders(true); for(string provider : providers) { locationmanager.requestlocationupdates (provider, 1000, 0, new LocationListener() { public void onlocationchanged(location location) { public void onproviderdisabled(string provider) { public void onproviderenabled(string provider) { public void onstatuschanged (String provider, int status, Bundle extras) { ); sb.append(provider).append("\n"); Location location = locationmanager.getlastknownlocation(provider); if(location!= null) { double lat = location.getlatitude(); // 위도 double lng = location.getlongitude(); // 경도 sb.append(" 위치(").append(" 위도:").append(lat).append(", 경도:"). append(lng).append(")"); else { sb.append(" 위치를찾을수없음"); tvw.settext(sb); 다음은 res/layout/ 폴더에있는 main.xml 파일의내용이다. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:id="@+id/mytextview" android:layout_height="wrap_content" android:text="@string/hello" - 97 -
/> </LinearLayout> 다음은 AndroidManifest.xml 파일의내용이다. <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lbs1" android:versioncode="1" android:versionname="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".location1" 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.access_fine_location"/> <uses-sdk android:minsdkversion="7" /> </manifest> 출력결과는다음과같다. ( 그림 ) - 98 -
13.2 요구기준에의한위치찾기예제프로그램 다음은요구기준에의하여위치정보를찾는프로그램이다. 다음코드부분이요구하는 위치정보는넓은(coarse) 간격의위치정보이다. 정밀한간격을요구하는경우에는 (Criteria.ACCURACY_FINE) 이라고해야한다. Criteria criteria = new Criteria(); criteria.setaccuracy(criteria.accuracy_coarse); criteria.setaltituderequired(false); criteria.setbearingrequired(false); criteria.setcostallowed(true); criteria.setpowerrequirement(criteria.power_low); 다음은프로그램소스코드이다. 위치정보갱신이가능하도록되어있으며다음코드 locationmanager.requestlocationupdates(provider, 2000, 10, locationlistener); 에의하여 2 초마다위치정보를갱신한다. import android.app.activity; import android.os.bundle; import android.content.context; import android.location.criteria; import android.location.location; import android.location.locationlistener; import android.location.locationmanager; import android.widget.textview; public class criteria_loc extends Activity { public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); LocationManager locationmanager; locationmanager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setaccuracy(criteria.accuracy_coarse); criteria.setaltituderequired(false); criteria.setbearingrequired(false); criteria.setcostallowed(true); criteria.setpowerrequirement(criteria.power_low); String provider = locationmanager.getbestprovider(criteria, true); Location location = locationmanager.getlastknownlocation(provider); updatewithnewlocation(location); locationmanager.requestlocationupdates(provider, 2000, 10, locationlistener); - 99 -
private final LocationListener locationlistener = new LocationListener() { public void onlocationchanged(location location) { updatewithnewlocation(location); public void onproviderdisabled(string provider){ updatewithnewlocation(null); public void onproviderenabled(string provider){ public void onstatuschanged(string provider, int status, Bundle extras) { ; /** Update UI with a new location */ private void updatewithnewlocation(location location) { TextView MyLocationText = (TextView)findViewById(R.id.MyLocationText); String latlongstring; if (location!= null) { double lat = location.getlatitude(); double lng = location.getlongitude(); latlongstring = " 위도:" + lat + ", 경도:" + lng; else { latlongstring = " 위치찾지못함."; MyLocationText.setText(" 당신의현재위치는:\n" + latlongstring); 다음은 res/layout/ 폴더에있는 main.xml 파일의내용이다. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:layout_height="wrap_content" /> <TextView android:id="@+id/mylocationtext" android:layout_height="fill_parent" android:text="@string/hello" /> </LinearLayout> 다음은 AndroidManifest.xml 파일의내용이다. <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - 100 -
package="com.wai2" android:versioncode="1" android:versionname="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".wai2" 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.access_fine_location"/> <uses-sdk android:minsdkversion="7" /> </manifest> 다음은출력결과이다. ( 그림 ) - 101 -
13.3 지오코더 (Geocoder) 에의한주소생성 다음은위도와경도정보로부터주소를출력하는프로그램이다. 다음코드부분에서 Geocoder gc = new Geocoder(this, Locale.getDefault()); 위도와경도정보로부터주소를반환하는 Geocoder 객체를얻고, 다음코드부분에서 List<Address> addresses = gc.getfromlocation(lat, lng, 1); 실제주소를얻는다. import java.io.ioexception; import java.util.list; import java.util.locale; import android.app.activity; import android.os.bundle; import android.content.context; import android.location.address; import android.location.criteria; import android.location.geocoder; import android.location.location; import android.location.locationlistener; import android.location.locationmanager; import android.widget.textview; public class GeoLoc extends Activity { public void oncreate(bundle icicle) { super.oncreate(icicle); setcontentview(r.layout.main); LocationManager locationmanager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setaccuracy(criteria.accuracy_fine); criteria.setaltituderequired(false); criteria.setbearingrequired(false); criteria.setcostallowed(true); criteria.setpowerrequirement(criteria.power_low); String provider = locationmanager.getbestprovider(criteria, true); Location location = locationmanager.getlastknownlocation(provider); updatewithnewlocation(location); locationmanager.requestlocationupdates(provider, 2000, 10, locationlistener); - 102 -
private final LocationListener locationlistener = new LocationListener() { public void onlocationchanged(location location) { updatewithnewlocation(location); public void onproviderdisabled(string provider){ updatewithnewlocation(null); public void onproviderenabled(string provider) { public void onstatuschanged(string provider, int status, Bundle extras){ ; /** Update UI with a new location */ private void updatewithnewlocation(location location) { TextView MyLocationText = (TextView)findViewById(R.id.MyLocationText); String latlongstring; String addressstring = null; if (location!= null) { double lat = location.getlatitude(); double lng = location.getlongitude(); latlongstring = "( 위도:" + lat + ", 경도:" + lng + ")"; Geocoder gc = new Geocoder(this, Locale.getDefault()); try { List<Address> addresses = gc.getfromlocation(lat, lng, 1); StringBuilder sb = new StringBuilder(); if (addresses.size() > 0) { Address address = addresses.get(0); for (int i = 0; i < address.getmaxaddresslineindex(); i++) sb.append(address.getaddressline(i)).append("\n"); sb.append(address.getlocality()).append("\n"); sb.append(address.getpostalcode()).append("\n"); sb.append(address.getcountryname()); addressstring = sb.tostring(); else addressstring = " 주소찾을수없음."; catch (IOException e) {System.out.println("IO Exception occurred.\n"); else { latlongstring = " 위치발견되지않음."; MyLocationText.setText(" 당신의위치:\n" + latlongstring + "\n\n 당신의주소:\n" + addressstring); AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.geoloc" android:versioncode="1" android:versionname="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> - 103 -
<activity android:name=".geoloc" 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.access_fine_location"/> <uses-sdk android:minsdkversion="7" /> </manifest> res/layout/main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:layout_height="wrap_content" /> <TextView android:id="@+id/mylocationtext" android:layout_height="fill_parent" android:text="@string/hello" /> </LinearLayout> 다음은출력결과이다. ( 그림 ) - 104 -
13.4 MapView 와 MapActivity 에의한지도출력 다음은 MapView와 MapActivity 에의한지도출력프로그램이다. 다음코드는응용을위한 MapView와 mapcontroller 를선언하는부분이다. MapView mymapview = (MapView)findViewById(R.id.myMapView); mapcontroller = mymapview.getcontroller(); 다음코드는지도출력옵션을나타내는코드이다. // Configure the map display options mymapview.setsatellite(true); mymapview.setstreetview(true); mymapview.displayzoomcontrols(false); 다음코드는지도를갱신하는부분이다. // Update the map location. Double geolat = location.getlatitude()*1e6; Double geolng = location.getlongitude()*1e6; GeoPoint point = new GeoPoint(geoLat.intValue(), geolng.intvalue()); mapcontroller.animateto(point); 다음은전체코드이다. import java.io.ioexception; import java.util.list; import java.util.locale; import android.location.address; import android.location.geocoder; import android.os.bundle; import com.google.android.maps.geopoint; import com.google.android.maps.mapactivity; import com.google.android.maps.mapcontroller; import com.google.android.maps.mapview; import android.content.context; import android.location.criteria; import android.location.location; import android.location.locationlistener; import android.location.locationmanager; import android.widget.textview; public class MyMapActivity extends MapActivity { MapController mapcontroller; public void oncreate(bundle icicle) { super.oncreate(icicle); - 105 -
setcontentview(r.layout.main); MapView mymapview = (MapView)findViewById(R.id.myMapView); mapcontroller = mymapview.getcontroller(); // Configure the map display options mymapview.setsatellite(true); mymapview.setstreetview(true); mymapview.displayzoomcontrols(false); // Zoom in mapcontroller.setzoom(17); LocationManager locationmanager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setaccuracy(criteria.accuracy_fine); criteria.setaltituderequired(false); criteria.setbearingrequired(false); criteria.setcostallowed(true); criteria.setpowerrequirement(criteria.power_low); String provider = locationmanager.getbestprovider(criteria, true); Location location = locationmanager.getlastknownlocation(provider); updatewithnewlocation(location); locationmanager.requestlocationupdates(provider, 2000, 10, locationlistener); ; private final LocationListener locationlistener = new LocationListener() { public void onlocationchanged(location location) { updatewithnewlocation(location); public void onproviderdisabled(string provider) { updatewithnewlocation(null); public void onproviderenabled(string provider) { public void onstatuschanged(string provider, int status, Bundle extras) { /** Update the map with a new location */ private void updatewithnewlocation(location location) { // Update the map location. Double geolat = location.getlatitude()*1e6; Double geolng = location.getlongitude()*1e6; GeoPoint point = new GeoPoint(geoLat.intValue(), geolng.intvalue()); mapcontroller.animateto(point); TextView mylocationtext = (TextView)findViewById(R.id.myLocationText); String latlongstring; String addressstring = " 주소발견하지못함"; if (location!= null) { double lat = location.getlatitude(); double lng = location.getlongitude(); latlongstring = " 위도:" + lat + "\n 경도:" + lng; Geocoder gc = new Geocoder(this, Locale.getDefault()); try { - 106 -
List<Address> addresses = gc.getfromlocation(lat, lng, 1); StringBuilder sb = new StringBuilder(); if (addresses.size() > 0) { Address address = addresses.get(0); for (int i = 0; i < address.getmaxaddresslineindex(); i++) sb.append(address.getaddressline(i)).append("\n"); sb.append(address.getlocality()).append("\n"); sb.append(address.getpostalcode()).append("\n"); sb.append(address.getcountryname()); addressstring = sb.tostring(); catch (IOException e) { else { latlongstring = " 위치찾지못했음."; mylocationtext.settext(" 당신의현재위치\n" + latlongstring + "\n\n 당신의주소:\n" + addressstring); protected boolean isroutedisplayed() { return false; 다음은 AndroidManifest.xml 코드이다. Google 지도사용을선언하는다음부분은 <activity>... </activity> 안에반드시존재하여야한다. <uses-library android:name="com.google.android.maps" /> 인터넷사용권한을부여하는다음부분도 AndroidManifest.xml 파일에반드시있어야한다. 다음은 <uses-permission android:name="android.permission.internet" /> <uses-permission android:name="android.permission.access_fine_location" /> XML 파일코드이다. AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mymapactivity" android:versioncode="1" android:versionname="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".mymapactivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.main" /> <category android:name="android.intent.category.launcher" /> </intent-filter> </activity> <uses-library android:name="com.google.android.maps" /> </application> - 107 -
<uses-permission android:name="android.permission.internet" /> <uses-permission android:name="android.permission.access_fine_location" /> <uses-sdk android:minsdkversion="7" /> </manifest> 다음은 main.xml 코드이다. 이코드에서가장중요한부분은 <com.google.android.maps.mapview 에서 android:apikey 부분이다. 지도 Key는 MD 5 지문을생성한다음에 code.google.com/android/maps-api-signup.html에서부여받아야 한다. IP 주소마다다른 Key를부여받아야 google 지도서비스를사용할수있다. res/layout/main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" > <TextView android:id="@+id/mylocationtext" android:layout_height="wrap_content" android:text="@string/hello" /> <com.google.android.maps.mapview android:id="@+id/mymapview" android:layout_height="fill_parent" android:enabled="true" android:clickable="true" android:apikey="0a1nyi... 생략부분... B0Dt-Q" /> </LinearLayout> 다음은출력결과이다. 위도와경도정보가출력되고주소가출력된다. 다음에지도가출력 되었다. 현재다음옵션이선언되어있다. android:enabled="true" android:clickable="true" 그래서지도를움직일수가있으며지도에클릭을할수가있다. 지도를마우스로움직이면 현재 AVD 윈도우에없는지도부분은다시로딩된다. 그리고지도를클릭하면도로에파 란색선이형성된다. 다음첫번째지도그림은원래출력된지도그림이고, 그다음그림 은지도를마우스로클릭한다음에이동시킨그림이다. 도로에파란색선이형성되어있고 민둥산이크게나타나있다. - 108 -
( 그림 ) ( 그림 ) - 109 -
13.5 MD5 인증구하기 참고로다음은 MD5 인증서지문을구하는그림이다. ( 그림 ) MD5 를구하는절차는다음과같이하였다. 이과정이약간까다롭지만 google 지도서비 스를받기위해서는반드시필요한과정이다. 다음과정은본저자가사용한방법이다. 일 반적으로거의비슷하지만약간씩다를수는있다. 이과정을시작하기전에 www.google.com 에계정을먼저만들어두어야한다. (1) 편의상 C:/Program Files/Java/jre1.60_03/bin/ 밑에있는 keytool.exe 파일과 jli.dll 파일을 C:/Document and Settings/user/ 로복사한다. (2) 편의상 C:/Document and Settings/user/.android/ 밑에있는 debug.keyst ore 파일을 C:/ 밑으로복사한다. (3) 다음에 C:/Document and Settings/user/ 에있는 keytool.exe 파일을실행한다. 실행옵션은다음과같다. C:/Document and Settings/user>keytool -list -alias andro iddebugkey -keystore C:/debug.keystore -storepass android -keypass android (4) MD5 지문이출력된다. MD5 지문은두개의문자와 : 로구성된패턴으로서예를들어 7F:8F:7A:04:1E:E7:D3:E9:F7:5A:60:37:FA:1A:7B:BB 형태이다. - 110 -