'Android > 추천사이트모음' 카테고리의 다른 글
[안드로이드]서비스와 알람 설명 괜찮은 곳 (0) | 2010.11.25 |
---|---|
구글의 인터넷주소 축약서비스 (0) | 2010.11.25 |
기본 안드로이드 게임 따라하기 추천 사이트 (0) | 2010.11.24 |
apk 디컴파일 관련 참고 주소 (0) | 2010.11.23 |
http://blog.naver.com/dbrwhdqja (0) | 2010.11.23 |
[안드로이드]서비스와 알람 설명 괜찮은 곳 (0) | 2010.11.25 |
---|---|
구글의 인터넷주소 축약서비스 (0) | 2010.11.25 |
기본 안드로이드 게임 따라하기 추천 사이트 (0) | 2010.11.24 |
apk 디컴파일 관련 참고 주소 (0) | 2010.11.23 |
http://blog.naver.com/dbrwhdqja (0) | 2010.11.23 |
[출처] :
http://blackzaket.blog.me/80113159648
Android에서는 기본적으로 AlarmManager를 제공하는데..
특정 작업을 Manager에 등록해 두면 알아서 등록한 intent를 깨우준다.
AlarmManager am = (AlarmManager)con.getSystemService(Context.ALARM_SERVICE);
//RealTime으로 예약하는 경우
//반복의 경우
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 60 * 24, pi);
//한번의 경우
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
//부팅경과 시간으로 계산하는 경우
ELAPSED_WAKEUP
주의 사항
간단하게 2가지의 주의 사항이 있는데
1. AlarmManager에 등록되어진 Alarm의 경우 단말이 재부팅을 하게 되면 깨끗하게 최기화 되어진다.
2. calendar 또는 date로 시간을 Set할때 현재 시간에 기준하여 알람 시간이 내일인지 오늘인지를 잘 계산해야 한다. (뭔지 모르고 엄청난 삽질을;;;;)
1번의 해결 방법으로는 Intent-filter를 이용하는 방법이 있는데...
알람을 초기화 하는 Activity에 다음과 같은 intent-filter를 달아주는것이다.
<action android:name="android.intent.action.BOOT_COMPLETED" />
intent에서는 getAction과 같은 값으로 Intent를 구분할 수 있다.
[출처] [android] AlarmManager 사용하기 |작성자 파란검정
안드로이드 백그라운드 설명서 pdf (0) | 2010.11.25 |
---|---|
wifi를 이용한 심플싱크 (0) | 2010.11.25 |
[펌][안드로이드]사운드 재생(sound pool) (0) | 2010.11.25 |
안드로이드 2.2 변경사항(안드로이드프로그래밍정복 책참고) (0) | 2010.11.25 |
[펌]안드로이드 Intent 그리고 PendingIntent 와 Intent Sender (0) | 2010.11.24 |
출처 : http://www.cyworld.com/kenur/3732812
SoundPool(int maxStreams, int streamType, int srcQuality)
첫번째 = 동시에 재생가능한 최대 스트림수
두번째 = 오디오 스트림 STREAM_MUSIC 고정
세번째 = 샘플링 품질
객체를 생성후에 사운드를 아래와같이로드함
int load(Context context, int resld, int priority)
int load(String path, int priority)
리로스나 파일로부터 사운드를 로드 한다
재생메소드
int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
첫번째 = 재생할사운드
두번째 = 좌 볼륨
세번째 = 우 볼륨
네번째 = 재생우선순위
다섯 = 반복설정
여섯 = 재생속도
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/play1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="보통재생" /> <Button android:id="@+id/play2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="볼륨 절반 재생" /> <Button android:id="@+id/play3" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="3회 재생" /> <Button android:id="@+id/play4" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="0.5배속 재생" /> <Button android:id="@+id/play5" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="2배속 재생" /> <Button android:id="@+id/play6" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="3배속 재생" /> </LinearLayout>
package test.Layout; import android.app.*; import android.content.*; import android.media.*; import android.os.*; import android.view.*; import android.widget.*; public class Layout extends Activity { SoundPool pool; int ddok; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); pool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0); ddok = pool.load(this, R.raw.ddok, 1); findViewById(R.id.play1).setOnClickListener(mClickListener); findViewById(R.id.play2).setOnClickListener(mClickListener); findViewById(R.id.play3).setOnClickListener(mClickListener); findViewById(R.id.play4).setOnClickListener(mClickListener); findViewById(R.id.play5).setOnClickListener(mClickListener); findViewById(R.id.play6).setOnClickListener(mClickListener); } Button.OnClickListener mClickListener = new Button.OnClickListener() { public void onClick(View v) { MediaPlayer player; switch (v.getId()) { case R.id.play1: pool.play(ddok, 1, 1, 0, 0, 1); break; case R.id.play2: pool.play(ddok, 0.5f, 0.5f, 0, 0, 1); break; case R.id.play3: pool.play(ddok, 1, 1, 0, 2, 1); break; case R.id.play4: pool.play(ddok, 1, 1, 0, 0, 0.5f); break; case R.id.play5: pool.play(ddok, 1, 1, 0, 0, 2); break; case R.id.play6: pool.play(ddok, 1, 1, 0, 0, 3); break; } } }; }
wifi를 이용한 심플싱크 (0) | 2010.11.25 |
---|---|
[펌]AlarmManager 사용하기 기본팁 (0) | 2010.11.25 |
안드로이드 2.2 변경사항(안드로이드프로그래밍정복 책참고) (0) | 2010.11.25 |
[펌]안드로이드 Intent 그리고 PendingIntent 와 Intent Sender (0) | 2010.11.24 |
[안드로이드]사용자 알람 간단한 예제 (0) | 2010.11.24 |
[펌]AlarmManager 사용하기 기본팁 (0) | 2010.11.25 |
---|---|
[펌][안드로이드]사운드 재생(sound pool) (0) | 2010.11.25 |
[펌]안드로이드 Intent 그리고 PendingIntent 와 Intent Sender (0) | 2010.11.24 |
[안드로이드]사용자 알람 간단한 예제 (0) | 2010.11.24 |
[펌]안드로이드2.2에서 마켓 설치하기 (0) | 2010.11.23 |
자바 취업준비생입니다.
다름이 아니라 이번에 처음으로 만든 어플이 있어서 올려봅니다.
올리는 이유는 펍가족님들에게 조언 좀 얻을려구영..^^;
용기있는 왕초보의 프로젝트입니다..이름은 허접한 그림메모 어플..^^;;;;;; ==;
어플 기능 소개
1.기본적인 리스트
2.미리보기 기능 구현
3.프리드로우 기능 구현(자바 그대로 구현)
4.옵션 메뉴 기능 구현
학습된 내용
1.커스텀 CursorAdapter 공부
2.인텐트 기본 공부
3.Bitmap 기본 변환 공부
4.기본 입출력 공부
5.기본 직렬화 IO 공부
6.DB 기본 컨트롤 공부 등등
단 2장짜리 어플이지만 상당히 공부된 걸로 판단됩니다..^^;;
최적화는 못했습니다..아직 실력이..T.T
사인안된 .apk하나랑 (unsigned랑 sign이랑 있는데 뭔지 잘 모르겠음..ㅋ)
풀소스 올려봅니다. 아주 초보분들은 조금이라도 도움될듯..고수분들에게 보이기엔 상당히 부끄럽습니다..^^;
이제 몇개월만 더 하면 과정 끝나서 안드로이드 취업해야 하는데.... 더 분발해야 할듯..
혹시 여기 올리면 안되면 바로 지우겠습니다..
1.PendingIntent
int | FLAG_CANCEL_CURRENT | 이전에 생성한 PendingIntent 는 취소하고, 새롭게 하나를 만듭니다. (친구에게 예전에 빌려준 카드를 정지 시키고 새롭게 하나 신청합니다.) |
int | FLAG_NO_CREATE | 현재 생성된 PendingIntent 를 반환 합니다. (친구 보고 내가 빌려준 카드를 당장 가져와 보라고 요청합니다. 이 후에, 해당 카드를 회수 할 수도 있습니다.) |
int | FLAG_ONE_SHOT | 이 플래그를 이용해 생성된 PendingIntent 는 단 한번 밖에 사용될 수 없습니다. (일회용 카드) |
int | FLAG_UPDATE_CURRENT | 만일 이미 생성된 PendingIntent 가 존재 한다면, 해당 Intent 의 내용을 변경합니다. (친구에게 전화해서, 인출할 돈의 액수를 다시 알려줍니다.) |
[펌][안드로이드]사운드 재생(sound pool) (0) | 2010.11.25 |
---|---|
안드로이드 2.2 변경사항(안드로이드프로그래밍정복 책참고) (0) | 2010.11.25 |
[안드로이드]사용자 알람 간단한 예제 (0) | 2010.11.24 |
[펌]안드로이드2.2에서 마켓 설치하기 (0) | 2010.11.23 |
[펌]android : View 에 있는 것을 Bitmap으로 저장 / Bitmap으로 된 것을 읽기 (0) | 2010.11.23 |
package org.exam;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class HandlerExampleAct extends Activity {
/** Called when the activity is first created. */
private static final int NOTIFY_ME_ID = 1337;
private Timer timer = new Timer();
private int count = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button)findViewById(R.id.btn1);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
TimerTask task = new TimerTask(){
@Override
public void run(){
notifyMe();
}
};
timer.schedule(task, 5000);
}
});
btn = (Button)findViewById(R.id.btn2);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
NotificationManager mgr =
(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
mgr.cancel(NOTIFY_ME_ID);
}
});
}
private void notifyMe() {
// TODO Auto-generated method stub
final NotificationManager mgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification note =new Notification(R.drawable.icon,"알림메세지",System.currentTimeMillis());
PendingIntent i =PendingIntent.getActivity(this, 0, new Intent(this,NotifyMessage.class), 0);
note.setLatestEventInfo(this, "알림제목", "알림메세지 본문입니다", i);
note.number = ++count;
mgr.notify(NOTIFY_ME_ID,note);
}
=============================================================================================
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class NotifyMessage extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView txt=new TextView(this);
txt.setText("알림 메시지!");
setContentView(txt);
}
}
안드로이드 2.2 변경사항(안드로이드프로그래밍정복 책참고) (0) | 2010.11.25 |
---|---|
[펌]안드로이드 Intent 그리고 PendingIntent 와 Intent Sender (0) | 2010.11.24 |
[펌]안드로이드2.2에서 마켓 설치하기 (0) | 2010.11.23 |
[펌]android : View 에 있는 것을 Bitmap으로 저장 / Bitmap으로 된 것을 읽기 (0) | 2010.11.23 |
[펌]안드로이드 (Android) Bitmap 구현, 관리 하기 (0) | 2010.11.23 |
[안드로이드]서비스와 알람 설명 괜찮은 곳 (0) | 2010.11.25 |
---|---|
구글의 인터넷주소 축약서비스 (0) | 2010.11.25 |
안드로이드 기본 어플 소스모음(Alarm Clock 도 포함) (5) | 2010.11.25 |
apk 디컴파일 관련 참고 주소 (0) | 2010.11.23 |
http://blog.naver.com/dbrwhdqja (0) | 2010.11.23 |
[안드로이드]서비스와 알람 설명 괜찮은 곳 (0) | 2010.11.25 |
---|---|
구글의 인터넷주소 축약서비스 (0) | 2010.11.25 |
안드로이드 기본 어플 소스모음(Alarm Clock 도 포함) (5) | 2010.11.25 |
기본 안드로이드 게임 따라하기 추천 사이트 (0) | 2010.11.24 |
http://blog.naver.com/dbrwhdqja (0) | 2010.11.23 |
안드로이드 에뮬레이터가 필요해 몇 가지를 다운로드 받았지만 마켓을 이용하려면 1.5나 1.6버전을 사용해야 했기 때문에, 2.1용을 찾아 헤매다 구하지 못하고 Android 2.2 (Froyo)의 시스템 이미지 파일을 구했다. 개인적인 용도로 만든 포스트지만, 비슷한 경로로 에뮬레이터에서 마켓을 이용하는 방법을 찾는 사람들을 위해 링크를 남겨둔다.
system.img (84.19MB) Download
먼저 위의 파일을 에뮬레이터에서 이용하기 위한 구성은 Android SDK r06 + API 8 그리고 Andriod SDK는 JDK 등이 필요하다. 혹시라도 안드로이드 앱을 제작하기 위한 기본환경 구축이 되어있지 않다면, 아래의 링크를 참조하여 그냥 순서대로 모두 설치한다.
안드로이드 어플리케이션 개발환경 구축 http://blog.naver.com/baljern/140112647750
먼저 '사용자의 경로:\android-sdk-windows\platforms\andriod-8\images' 폴더에 있는 system.img파일의 이름을
system.bak로 바꿔주고, 위에서 다운로드 받은 system.img 파일을 복사해 넣는다.
SDK Setup.exe를 실행한다.
Refresh Sources가 나오면 Close..
Packages to Install은 Cancle을 클릭한다.
'Virtual Devices'를 클릭하고 새로 만들어줄 가상장치의 이름을 입력하고, Target을 'Andriod 2.2 - API Level 8'로
설정한 후, SD Card 항목에서는 '9'(나중에 적당한 값으로 바꾸면 된다), Skin은 'WVGA800'을 추천한다. (예제에서는
캡쳐 편의상 400을 골랐다)
하드웨어 항목에 대해서 잘 모르겠다면 더 이상 추가할 장치가 나오지 않을 때까지 'New'를 클릭해준다.
센서들이나 GPS는 별 소용 없고 추가 앱의 설치 테스트를 위해서 장치의 내장 메모리의 용량 정도만 설정해주면 된다.
OK 클릭.
만들어진 가상장치를 클릭하고 Start...를 클릭.
모니터 화면에 보여줄 해상도를 설정하는 부분인데, 그냥 Launch를 클릭.
부팅에는 실제 폰과 비슷한 시간이 소요되고.. 스킨도 나중에 config.ini파일을 편집해서 바꿀 수 있다.
부팅이 되면 어플리케이션 버튼을 클릭하자.
마켓을 클릭.
마켓에 접속하려면 실제 폰과 같이 간단한 등록 과정을 거쳐야 한다.
유료 앱은 결제도 되지만 실제로 신용카드로 결제가 되기 때문에 돈이 나간다는 것을 명심하자.
안드로이드 마켓에 접속되었다.
짬이 날 때 마다 검색으로 마켓이 가능한 에뮬레이터를 찾다보니 아예 안드로이드 앱 개발키트를 모두 설치하게 되었는데, Android-SDK는 허접의 극을 달린다. 특히 느린 반응 속도는 이게 과연 개발툴이 맞는지 의심스러울 정도고, 마켓의 사용도 일부만 가능하다. eclipse를 사용하지 않고서도 이런 툴로 앱을 개발할 수 있기는 한 것일까? ^^;
[펌]안드로이드 Intent 그리고 PendingIntent 와 Intent Sender (0) | 2010.11.24 |
---|---|
[안드로이드]사용자 알람 간단한 예제 (0) | 2010.11.24 |
[펌]android : View 에 있는 것을 Bitmap으로 저장 / Bitmap으로 된 것을 읽기 (0) | 2010.11.23 |
[펌]안드로이드 (Android) Bitmap 구현, 관리 하기 (0) | 2010.11.23 |
canvas 화면을 Bitmap으로 저장 완결판.. (0) | 2010.11.23 |
[안드로이드]서비스와 알람 설명 괜찮은 곳 (0) | 2010.11.25 |
---|---|
구글의 인터넷주소 축약서비스 (0) | 2010.11.25 |
안드로이드 기본 어플 소스모음(Alarm Clock 도 포함) (5) | 2010.11.25 |
기본 안드로이드 게임 따라하기 추천 사이트 (0) | 2010.11.24 |
apk 디컴파일 관련 참고 주소 (0) | 2010.11.23 |
현재 View 클래스에 보여지는 화면을 파일로 저장하는 클래스다. View를 상속 받아 만든 클래스 ImageView, WebView 등 클래스를 이용할 수 있다.
사용 예제 레이아웃 main.xml
구현 부
|
[안드로이드]사용자 알람 간단한 예제 (0) | 2010.11.24 |
---|---|
[펌]안드로이드2.2에서 마켓 설치하기 (0) | 2010.11.23 |
[펌]안드로이드 (Android) Bitmap 구현, 관리 하기 (0) | 2010.11.23 |
canvas 화면을 Bitmap으로 저장 완결판.. (0) | 2010.11.23 |
이벤트 핸들러 - 여러 가지 이벤트 (0) | 2010.11.23 |
출처 :
http://blog.naver.com/sancholok?Redirect=Log&logNo=30091202062
개발환경 : JDK 1.5, eclipse-galileo, android googleAPI 7, window XP
이미지 관리와 표현을 위해서는 비트맵을 익히는게 가장 기본이다. 그 비트맵 관련
내용들을 소개한다.
안드로이드에서 비트맵 관련 클래스는 android.graphics.Bitmap 이다. 그래픽 관련
클래스들은 android.graphics 패키지에 있으며 여기에 포함된 것이다.
그리고 객체 Factory 관리를 위한 BitmapFactory 클래스가 있다. BitmapFactory 는
여러가지 이미지 포맷을 decode 해서 bitmap 으로 변환하는 함수들로 되어있는데
그 이름들은 decodeXXX 로 되어있어서 쉽게 원하는 기능의 함수를 찾을수 있을
것이다.
(1) BitmapFactory 에서 주로 사용하고 있는 함수와 옵션에 대한 설명 |
데이터를 가지고 Bitmap 으로 만들어 줄 때 많이 사용한다.
Camera.PictureCallback 에서 들어오는 데이터가 byte[] 배열로 들어오기 때문이다.
BitmapFactory.decodeFile() : 로컬에 존재하는 파일을 그대로 읽어올 때 쓴다. 파일경로를
파라미터로 넘겨주면 FileInputStream 을 만들어서 decodeStream 을 한다.
1 |
Bitmap orgImage = BitmapFactory.decodeFile(“/sdcard/test.jpg”); |
BitmapFactory.decodeResource() : Resource 폴더에 저장된 그림파일을 Bitmap 으로
만들어 리턴해준다
1 |
Bitmap orgImage = |
2 |
BitmapFactory.decodeResource(getResources(), R.drawable.test02); |
BitmapFactory.decodeStream() : InputStream 으로부터 Bitmap 을 만들어 준다.
BitmapFactory.Options : BitmapFactory 가 사용하는 옵션클래스이다. Options 객체를 생성하고
설정하고자 하는 옵션을 넣은후 BitmapFactory 의 함수 실행시 파라미터로 넘기면된다.
inSampleSize : decode 시 얼마나 줄일지 설정하는 옵션인데 1보다 작을때는 1이 된다.
1보다 큰값일 때 1/N 만큼 이미지를 줄여서 decoding 하게 된다. 보통 2의 배수로 설정한다.
1 |
BitmapFactory.Options options = new BitmapFactory.Options(); |
2 |
options.inSampleSize = 4; |
3 |
Bitmap orgImage = BitmapFactory.decodeFile(“/sdcard/test.jpg”, options); |
(2) Bitmap 과 BitmapFactory 을 사용한 여러가지 예제 |
할수 있다. 하지만 예를 들어 크기를 일정하게 2, 4 배등으로 줄일거면 굳이createScaledBitmap
을 사용하지 않고 위에서 설명한 BitmapFactory.Options 의 inSampleSize 를 사용하면 된다.
아래는 SD 카드에서 이미지를 불러와 Bitmap 을 원하는 크기 만큼 줄인 예제이다.
1 |
Bitmap orgImage = BitmapFactory.decodeFile(“/sdcard/test.jpg”); |
2 |
Bitmap resize = Bitmap.createScaledBitmap(orgImage, 300, 400, true ); |
다음은 BitmapFactory.Options 사용해서 이미지를 4배로 줄인것인데 createScaledBitmap 을
사용해서 용량을 줄인 이미지에 다시 입력한 크기만큼 가로, 세로 크기를 줄인 것이 된다
1 |
BitmapFactory.Options options = new BitmapFactory.Options(); |
2 |
options.inSampleSize = 4; |
3 |
Bitmap orgImage = BitmapFactory.decodeFile(“/sdcard/test.jpg”, options); |
4 |
Bitmap resize = Bitmap.createScaledBitmap(orgImage, 300, 400, true ); |
google_protectAndRun("ads_core.google_render_ad", google_handleError, google_render_ad);
[OutOfMemoryError??]
보통 이미지 파일을 읽어서 Resizing을 해야 할 때가 있는데,
그럴때는 BitmapFactory로 읽어서 Bitmap.createScaledBitmap() 메소드를 사용하여 줄이면
간단하게 처리 할 수 있습니다.
그런데 BitmapFactory를 사용할 때 주의해야 할 점이 있습니다.
아래의 예를 한번 보시죠.
Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg");
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);
이미지 파일로부터 Bitmap을 만든 다음에
다시 dstWidth, dstHeight 만큼 줄여서 resized 라는 Bitmap을 만들어 냈습니다.
보통이라면 저렇게 하는게 맞습니다.
읽어서, 줄인다.
그런데 만약 이미지 파일의 크기가 아주 크다면 어떻게 될까요?
지금 Dev Phone에서 카메라로 촬영하면
기본적으로 2048 x 1536 크기의 Jpeg 이미지가 촬영된 데이터로 넘어옵니다.
이것을 decode 하려면 3MB 정도의 메모리가 필요 할 텐데,
과연 어떤 모바일 디바이스에서 얼마나 처리 할 수 있을까요?
실제로 촬영된 Jpeg 이미지를 여러번 decoding 하다보면
아래와 같은 황당한 메세지를 발견 할 수 있습니다.
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
네... OutOfMemory 입니다.
더 이상 무슨 말이 필요 하겠습니까...
메모리가 딸려서 처리를 제대로 못합니다.
이것이 실제로 decoding 후 메모리 해제가 제대로 되지 않아서 그런 것인지,
하더라도 어디서 Leak이 발생 하는지에 대한 정확한 원인은 알 수 없습니다.
이것은 엔지니어들이 해결해야 할 문제 겠죠...
하지만 메모리 에러를 피할 수 있는 방법이 있습니다.
[BitmapFactory.Options.inSampleSize]
BitmapFactory.decodeXXX 시리즈는 똑같은 메소드가 두 개씩 오버로딩 되어 있습니다.
같은 이름이지만 Signature가 다른 메소드의 차이점은
BitmapFactory.Options를 파라메터로 받느냐 안받느냐의 차이죠.
BitmapFactory.Options를 사용하게 되면 decode 할 때 여러가지 옵션을 줄 수 있습니다.
여러가지 많지만 저희가 지금 사용할 것은 inSampleSize 옵션 입니다.
inSampleSize 옵션은,
애초에 decode를 할 때 얼마만큼 줄여서 decoding을 할 지 정하는 옵션 입니다.
inSampleSize 옵션은 1보다 작은 값일때는 무조건 1로 세팅이 되며,
1보다 큰 값, N일때는 1/N 만큼 이미지를 줄여서 decoding 하게 됩니다.
즉 inSampleSize가 4라면 1/4 만큼 이미지를 줄여서 decoding 해서 Bitmap으로 만들게 되는 것이죠.
2의 지수만큼 비례할 때 가장 빠르다고 합니다.
2, 4, 8, 16... 정도 되겠죠?
그래서 만약 내가 줄이고자 하는 이미지가 1/4보다는 작고 1/8보다는 클 때,
inSampleSize 옵션에 4를 주어서 decoding 한 다음에,
Bitmap.createScaledBitmap() 메소드를 사용하여 한번 더 줄이면 됩니다.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg", options);
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);
당연한 이야기 이겠지만,
내가 원하고자 하는 사이즈가 딱 1/4 크기라면
Bitmap.createScaledBitmap() 메소드를 쓸 필요가 없지요.
inSampleSize 옵션을 잘 활용하면 메모리 부족 현상을 대략적으로 해소 할 수 있습니다.
참고로 제가 저 옵션을 사용한 뒤로는 메모리 에러를 본적이 한~번도 없답니다.
[Appendix]
inSampleSize 옵션을 사용하면
SkScaledBitmapSampler Object (Library Level) 를 생성 하게 되는데,
Object를 만들때 정해진 SampleSize 만큼 축소하여 width와 height를 정한 뒤에 만들게 됩니다.
그러니까 애초에 축소된 사이즈로 이미지를 decoding 하는 것이죠.
[펌]안드로이드2.2에서 마켓 설치하기 (0) | 2010.11.23 |
---|---|
[펌]android : View 에 있는 것을 Bitmap으로 저장 / Bitmap으로 된 것을 읽기 (0) | 2010.11.23 |
canvas 화면을 Bitmap으로 저장 완결판.. (0) | 2010.11.23 |
이벤트 핸들러 - 여러 가지 이벤트 (0) | 2010.11.23 |
안드로이드 이미지 썸네일 참고 (0) | 2010.11.23 |
//참고로 setWallPaper는 권한으로 인해 예외가 남..주석처리 하세영..
//100프로 실행됨..최고의 소스...2틀동안 고생하다 발견한 주옥같은 소스..T.T 난 바보양..
/ 소스는 밑에 굵게 표시된 부분임..
< How to save a canvas to disk Options >
Hi,
I am doing a painting program (KIds Paint - you can find in Android
Market) and I have a lot of requests to save the content on disk or to
wallpaper. I have been searching around but cannot find solution.
My guess is that I probably wanted to get the bitmap from the canvas,
but I can't find ways to get it. Then I try to set an empty bitmap
into the canvas and draw on the canvas, and save the bitmap... but I
got an empty bitmap.
Please help! Thanks. Here's my codes:
public class KidsPaintView extends View {
Bitmap bitmap = null;
...
protected void onDraw(Canvas canvas) {
if (bitmap == null) {
bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
canvas.setBitmap(bitmap);
}
... // do painting on canvas
}
}
Then in my main code I try to retrieve the bitmap and save it as
wallpaper:
Bitmap bitmap = view.bitmap;
try { setWallpaper(bitmap); }
catch (IOException e) { e.printStackTrace(); }
But all I got is a black wallpaper.
Reply to author Forward Report spam Rate this post:
gjs
View profile
More options Sep 23 2009, 3:23 pm
Hi,
Have a look at -
http://developer.android.com/reference/android/view/View.html#draw(an...)
- create a new bitmap and a new canvas, associate the bitmap with this
canvas and call view.draw( your_new_canvas ) - using your
KidsPaintView view instance - then save the bitmap or use it to set
the wallpaper.
Regards
On Sep 23, 1:03 pm, limtc <thyech...@gmail.com> wrote:
- Show quoted text -
Reply to author Forward Report spam Rate this post:
limtc
View profile
More options Sep 23 2009, 5:12 pm
Mmm... I don't quite understand. The link you sent is the
documentation for View?
If I have a new canvas, what happened to the canvas in onDraw? And
when should I call view.draw(canvas)?
Do you have any sample codes? Appreciated.
Reply to author Forward Report spam Rate this post:
gjs
View profile
More options Sep 24 2009, 9:12 pm
Hi,
I made up the following to demonstrate what I meant.
In this example when you press the trackball/dpad center button the
view is written to a png file and also set as the wallpaper.
You can change it to save as jpeg file etc.
Hope that helps.
Regards
/////////////////////////////////////////////
package com.testSaveView;
import android.app.Activity;
import android.os.Bundle;
import java.io.FileOutputStream;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
public class testSaveView extends Activity
{
SomeView sv = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
sv = new SomeView(this);
setContentView( sv );
}
@Override
public boolean onKeyDown( int keyCode, KeyEvent event)
{
switch( event.getKeyCode() )
{
case KeyEvent.KEYCODE_DPAD_CENTER:
if ( sv != null )
{
saveView( sv );
return true;
}
default:
}
return super.onKeyDown( keyCode, event );
}
private void saveView( View view )
{
Bitmap b = Bitmap.createBitmap( view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas( b );
view.draw( c );
FileOutputStream fos = null;
try {
fos = new FileOutputStream( "/sdcard/some_view_image_" + System.currentTimeMillis() + ".png" );
if ( fos != null )
{
b.compress(Bitmap.CompressFormat.PNG, 100, fos );
fos.close();
}
setWallpaper( b );
} catch( Exception e )
{
Log.e("testSaveView", "Exception: " + e.toString() );
}
}
class SomeView extends View
{
public SomeView( Context context )
{
super( context );
}
public void onDraw( Canvas canvas )
{
canvas.drawARGB(0x80, 0xff, 0, 0 );
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize( 48 );
canvas.drawText("...Some view...", 10, canvas.getHeight() / 2,
paint);
}
}
}
//////////////////////////
& the manifest -
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.testSaveView"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/
app_name">
<activity android:name=".testSaveView"
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-sdk android:minSdkVersion="3" />
<uses-permission android:name="android.permission.SET_WALLPAPER"></
uses-permission>
</manifest>
On Sep 23, 7:12 pm, limtc <thyech...@gmail.com> wrote:
- Show quoted text -
Reply to author Forward Report spam Rate this post:
limtc
View profile
More options Sep 24 2009, 10:45 pm
Thanks!
I think the part that I am confused is that I never used view.draw
(canvas) code in my program so I don't know when to call it... at this
moment, says I wanted to clear a screen, this is my code in the view:
public void clearScreen() {
gradient = false;
dots.clear();
invalidate(); // this will call the onDraw and pass in the
canvas
}
so I replace invalidate() by draw(canvas)?
Reply to author Forward Report spam Rate this post:
gjs
View profile
More options Sep 25 2009, 3:39 pm
Hi,
That is why I gave you a complete working example ( maybe try it
yourself ). But not knowing your code I could only guess about when
you would want to call the saveView() method I provided.
I guess that you would call the saveView() method when the user
requests that your KidsPaintView view should save their 'drawing'
either to a file or to set as the wallpaper.
I imagine that you would provide a menu option button, or some
touchscreen or keyboard button to allow the user to trigger that
action - when they had finished making their 'drawing'. I just used
the trackball/dpad center button as an example to demonstrate
triggering and saving the view.
In my example I used 'SomeView' to represent your KidsPaintView and
put the saveView() method in the Activity, but you could also put the
saveView() method or some equivalent code inside your KidsPaintView
view if you wish.
You should also realize that you CAN just call draw( canvas ) on your
KidsPaintView as your class extends View so it (automatically)
inherits the View.draw( canvas ) method... You just need to ensure
that you create and use a new canvas and a new bitmap when calling the
draw( canvas) method, as I said originally and demonstrated with the
code posted.
Good luck !
Regards
On Sep 25, 12:45 am, limtc <thyech...@gmail.com> wrote:
- Show quoted text -
Reply to author Forward Report spam Rate this post:
limtc
View profile
More options Sep 29 2009, 4:03 pm
Thanks!
After experiementing, seems to work. I think all I need to do is to do
the view.draw(canvas) prior to have the canvas filled with what's
drawn on screen. I am still exactly sure what's happening though, as
basically I just do everything as it is, and create a new bitmap and a
new canvas associate with it, and prior to saving, pass the new canvas
to the view.
About view.draw(canvas):
http://developer.android.com/reference/android/view/View.html#draw(an...)
Anyway, I am happy!
[펌]android : View 에 있는 것을 Bitmap으로 저장 / Bitmap으로 된 것을 읽기 (0) | 2010.11.23 |
---|---|
[펌]안드로이드 (Android) Bitmap 구현, 관리 하기 (0) | 2010.11.23 |
이벤트 핸들러 - 여러 가지 이벤트 (0) | 2010.11.23 |
안드로이드 이미지 썸네일 참고 (0) | 2010.11.23 |
안드로이드 카메라 제어 참고부분 (0) | 2010.11.23 |
출처 :
http://blog.naver.com/crowdark7?Redirect=Log&logNo=108184807
이벤트 핸들러 - 여러 가지 이벤트
요약
- 터치입력 이벤트: 콜백 메서드 처리
- 키보드입력 이벤트: 콜백 메서드 처리, keyCode속성, KeyEvent속성
- 위젯 이벤트: 리스너 구현 (버튼 별 구현 -> 뷰에 구현해 통합 -> 리스너 객체 선언)
- 포커스 관리: 모드 별 포커스 설정, 일반 모드에서 포커스 이동, 강제 포커스 요청, 포커스 이벤트
터치 입력
터치 입력이란 손가락이나 스타일러스 펜으로 화면을 누르는 터치 동작을 말한다.
터치 입력에 대한 콜벡 메서드는 해당 뷰에서 재정의 하므로 이벤트 정보만을 가지지만, 리스너는 여러 대상에 대해 등록이 가능하기 때문에 이벤트 대상인 v를 전달 받는다.
만약 뷰의 onTouchEvent 콜백 메서드가 처리하지 않았을 경우 액티비티의 콜백이 호출된다.
두 방법의 차이점은 뷰는 뷰에서의 좌표로 인수가 전달되지만 액티비티는 액티비티의 좌상단을 기준으로 인수가 전달되어 오차가 생길 가능성이 크다. (타이틀 바가 있기 때문에)
콜백 메서드 이용
boolean onTouchEvent(MotionEvent event)
인터페이스 이용
boolean onTouch(View v, MotionEvent event)
위의 MotionEvent 객체는 두 경우 모두 동일한 역할을 하는데 MotionEvent.getAction 메서드는 사용자가 화면에 대고 어떤 것을 했는지에 대한 정보를 전달한다.
MotionEvent의 속성 | |
ACTION_DOWN | 화면을 누름 |
ACTION_MOVE | 누른채로 움직였다. |
ACTION_UP | 화면에서 손가락을 뗐다. |
아래의 코드는 터치하여 누른 채로 움직였을 경우 그 점들을 선으로 이어주는 코드 중 하나이다. onTouchEvent를 재정의 한 것으로 arVertex라는 ArrayList에 이벤트가 일어난 곳의 좌표를 저장하는 과정이다.
public boolean onTouchEvent(MotionEvent event) {
// getAction을 이용해서 현재 어떤 상태인지 알아내고 그 상태가 화면을 누른 것이라면 // 누른 곳의 좌표를 받아서 ArrayList에 저장한다. if (event.getAction() == MotionEvent.ACTION_DOWN) { arVertex.add(new Vertex(event.getX(), event.getY(), false)); return true; }
// 누른 채로 움직였다면 좌표를 추가하고, 화면에 표시해주기 위해서 invalidate를 이용한다. 이는 무효화를 통해서 다시 onDraw를 호출하는 기능을 한다. if (event.getAction() == MotionEvent.ACTION_MOVE) { arVertex.add(new Vertex(event.getX(), event.getY(), true)); invalidate(); return true; } return false; } |
키보드 입력
모바일 장비에는 문자 입력을 위한 키보드가 필요한데 쿼티 자판을 가진 장비도 있고 간단한 단추만을 가진 장비도 있다. 이런 키보드를 누를 때의 이벤트는 아래와 같은 메서드가 처리한다.
만약 뷰에서 키 입력을 처리 하지 않으면 액티비티의 콜백 메서드가 처리하게 되어있다. 키 입력의 경우 뒤로 버튼까지 액티비티의 핸들러가 받아버리므로 특별히 처리해야 하는 곤란함이 있어서 가능하면 뷰에서 처리하는 것이 좋다.
콜백 메서드 이용
boolean onKeyDown(int keyCode, keyEvent event)
인터페이스 이용
boolean onKey (View v, int keyCode, keyEvent event)
keyCode의 속성 | |
KEYCODE_DPAD_LEFT | 좌측 이동 |
KEYCODE_DPAD_RIGHT | 우측 이동 |
KEYCODE_DPAD_UP | 위 이동 |
KEYCODE_DPAD_DOWN | 아래 이동 |
KEYCODE_DPAD_CENTER | 중앙버튼 |
KEYCODE_A | 알파벳 키(A to Z) |
KEYCODE_0 | 숫자 키(0~9) |
KEYCODE_CALL | 통화 |
KEYCODE_ENDCALL | 통화종료 |
KEYCODE_HOME | 홈 |
KEYCODE_BACK | 뒤로 |
KEYCODE_VOLUME_UP | 볼륨 증가 |
KEYCODE_VOLUME_DOWN | 볼륨 감소 |
KeyEvent의 속성 | |
ACTION_DOWN | 키를 눌렀음 |
ACTION_UP | 키를 뗐음 |
ACTION_MULTIPLE | 같은 키를 여러 번 눌름 |
아래의 경우는 콜벡 메서드를 재정의하는 경우이다. 방향 패드를 누를 때마다 mX 속성을 그에 맞게 바꾸게 된다.
public boolean onKeyDown(int KeyCode, KeyEvent event) { super.onKeyDown(KeyCode, event); if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (KeyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: mX-=5; invalidate(); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: mX+=5; invalidate(); return true; case KeyEvent.KEYCODE_DPAD_UP: mY-=5; invalidate(); return true; case KeyEvent.KEYCODE_DPAD_DOWN: mY+=5; invalidate(); return true; case KeyEvent.KEYCODE_DPAD_CENTER: if (mColor == Color.BLUE) { mColor = Color.RED; } else { mColor = Color.BLUE; } invalidate(); return true; } } return false; } |
위젯의 이벤트 처리
위젯을 등록할 때는 해당 위젯의 클래스를 사용하는 것이 보통이다. 정해진 위젯의 클래스를 이용하기 때문에 상속을 받지 않고 이벤트를 처리할 수 있어야 하므로 인터페이스를 이용해 리스너로 이벤트를 받아야 한다.
위젯의 경우 터치와 다르게 어느 좌표에서 터치가 이루어 졌는지 등에 대한 정보가 필요 없으므로 클릭된 뷰를 전달하는 것 외에는 별도의 인수가 없다.
아래는 버튼을 클릭하였을 때 이벤트 처리를 구현한 코드 예제이다. 각 버튼에 대해서 클릭 이벤트를 정의하였다. 그런데 버튼이 여러 개일 경우 하는 일은 비슷한데 모두 클릭이벤트를 정해야 한다면 상당히 불편할 것이다.
버튼 별 인터페이스 구현
public class Fruit extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.input_fruit); Button btnApple=(Button)findViewById(R.id.apple); btnApple.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { TextView textFruit=(TextView)findViewById(R.id.fruit); textFruit.setText("Apple"); } }); Button btnOrange=(Button)findViewById(R.id.orange); btnOrange.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { TextView textFruit=(TextView)findViewById(R.id.fruit); textFruit.setText("Orange"); } }); } } |
뷰에 통합 + 리스너 객체 선언
비슷한 코드가 반복 되기 때문에 통합한 코드가 바로 아래의 코드이다. 안드로이드에서는 하나의 리스너를 여러 뷰에 등록하는 것을 허락하기 때문에 통합이 가능하다. 또한 액티비티는 그대로 두고 리스너 객체를 멤버로 선언한 후에 이것을 리스너로 사용하는 것이 좋다.
(this --> mClickListener)
public class Fruit extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.input_fruit); findViewById(R.id.apple).setOnClickListener(mClickListener); findViewById(R.id.orange).setOnClickListener(mClickListener); } Button.OnClickListener mClickListener = new View.OnClickListener() { public void onClick(View v) { TextView textFruit=(TextView)findViewById(R.id.fruit); switch (v.getId()) { case R.id.apple: textFruit.setText("Apple"); break; case R.id.orange: textFruit.setText("Orange"); break; } } }; } |
포커스 관리
키보드 이벤트는 포커스를 가지고 있는 뷰에게만 전달이 된다. 그리고 특정 시점에서 입력을 받을 수 있는 뷰는 하나밖에 없다. 그래서 포커스는 입력을 받을 뷰가 어떤 것인지 가리키며 다른 뷰와 다르게 표시한다.
안드로이드에선 일반적으로 버튼은 주황색으로 표현하며 에디트는 경계선이 굵은 주황색으로 표시한다.
안드로이드는 포커스를 표시할 때 모드 별로 차이가 있다.
터치 모드에서는 포커스를 표시하지 않고
일반 모드일 때는 포커스를 표시한다. 일반 모드는 키를 이용해서 이동하게 되는 경우이다.
현재 터치모드인지를 확인하는 메서드는 IsInTouchMode가 있다. 이 메서드로 현재의 상황을 조사하고 위젯에 포커스가 가능한지 아닌지를 정할 수 있다.
모드 | XML속성 | 포커스 설정 | 포커스 조사 |
일반 | focusable | setFocusable | isFocusable |
터치 | focusableInTouchMode | setFocusableInTouchMode | isFocusableInTouchMode |
만약 위의 setFocusable과 setFocusableInTouchMode를 생략하면 해당 뷰가 포커스를 받지 못하므로 키 입력이벤트는 전달되지 않는다. setFocusableInTouchMode는 없어도 된다고 생각할 수도 있으나 일반 모드에서 터치 모드로 변경될 때를 위해서 필요하다.
일반 모드에서 포커스 이동방식
디폴트
키보드를 이용해서 이동하게 되면 가장 가까운 위젯으로 포커스를 옮기게 되어 있다.
최초 실행 시에 터치모드라면 아무도 포커스를 가지지 않고
일반 모드라면 첫 번째 위젯이 포커스를 가진다.
특정 이동 지정
속성 | 메서드 | 설명 |
nextFocusLeft | setNextFocusLeftId | 왼쪽 이동 시의 위젯 |
nextFocusRight | setNextFocusRightId | 오른쪽 이동 시의 위젯 |
nextFocusUp | setNextFocusUpId | 위쪽 이동 시의 위젯 |
nextFocusDown | setNextFocusDownId | 아래쪽 이동 시의 위젯 |
특정 뷰로 강제 포커스 이동
이럴 경우는 원하는 뷰의 requestFocus 메서드를 호출하면 된다.
포커스 변경 시에는 onFocusChanged이벤트가 발생하는데 포커스가 변경하게 될 경우에 수행하게 되는 동작을 처리한다. 즉, 포커스 이동을 하게 되면 별표로 강조를 하는 경우, 이 이벤트 핸들러를 사용해서 변경시키는 것이다.
*본 포스트는 김상형 저, <안드로이드 프로그래밍 정복>, 한빛미디어를 참고하였습니다.
책에서는 더 많은 내용을 보실 수 있습니다. 안적은 것들이 많아요 ㅠㅠ
[펌]안드로이드 (Android) Bitmap 구현, 관리 하기 (0) | 2010.11.23 |
---|---|
canvas 화면을 Bitmap으로 저장 완결판.. (0) | 2010.11.23 |
안드로이드 이미지 썸네일 참고 (0) | 2010.11.23 |
안드로이드 카메라 제어 참고부분 (0) | 2010.11.23 |
Android에서 View를 bitmap으로 정장하는법 (0) | 2010.11.22 |
would like to write an android app that basically layers an overlay on image on another image and then I would like to save the picture with the overlay as a jpg or png. Basically this will be the whole view that I would like to save. Sample code would be very helpful. EDIT: I tried out your suggestions and am getting a null pointer at the Starred Line.
| |||||||||||
|
up vote 1 down vote accepted
+150 |
You can take advantage of a View's drawing cache.
Where view is your View. The 95 is the quality of the JPG compression. And the file output stream is just that. | ||||||||||||
|
up vote 0 down vote |
Use fos reference as a 3rd parameter of b.compress() method in Moncader's answer. The image will be stored as image.jpg in root directory of your sd card. |
canvas 화면을 Bitmap으로 저장 완결판.. (0) | 2010.11.23 |
---|---|
이벤트 핸들러 - 여러 가지 이벤트 (0) | 2010.11.23 |
안드로이드 카메라 제어 참고부분 (0) | 2010.11.23 |
Android에서 View를 bitmap으로 정장하는법 (0) | 2010.11.22 |
Android View.getDrawingCache() 분석 (0) | 2010.11.22 |
이벤트 핸들러 - 여러 가지 이벤트 (0) | 2010.11.23 |
---|---|
안드로이드 이미지 썸네일 참고 (0) | 2010.11.23 |
Android에서 View를 bitmap으로 정장하는법 (0) | 2010.11.22 |
Android View.getDrawingCache() 분석 (0) | 2010.11.22 |
안드로이드 sqlite 대략 정보 #### (0) | 2010.11.22 |
안드로이드 이미지 썸네일 참고 (0) | 2010.11.23 |
---|---|
안드로이드 카메라 제어 참고부분 (0) | 2010.11.23 |
Android View.getDrawingCache() 분석 (0) | 2010.11.22 |
안드로이드 sqlite 대략 정보 #### (0) | 2010.11.22 |
안드로이드 테이블 존재여부 확인하기 - Sqlite mater table query (0) | 2010.11.22 |
webView.setDrawingCacheEnabled(true);webView.setDrawingCacheBackgroundColor(Color.WHITE);webView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_AUTO);webView.getDrawingCache(true)
bitmap = Bitmap.createBitmap(width, height, quality);
mDrawingCache = new SoftReference<Bitmap>(bitmap);
public static Bitmap createBitmap(int width, int height, Config config) {Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);bm.eraseColor(0); // start with black/transparent pixelsreturn bm;}
static JNINativeMethod gBitmapMethods[] = {^M{ "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;",^M(void*)Bitmap_creator },^M
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,^Mint offset, int stride, int width, int height,^MSkBitmap::Config config, jboolean isMutable) {^M//위에서 jColors는 null, offset은 0으로 넘어왔다는것을 기억하자if (width <= 0 || height <= 0) {^MdoThrowIAE(env, "width and height must be > 0");^Mreturn NULL;^M}^M^Mif (NULL != jColors) {^Msize_t n = env->GetArrayLength(jColors);^Mif (n < SkAbs32(stride) * (size_t)height) {^MdoThrowAIOOBE(env);^Mreturn NULL;^M}^M}^M^MSkBitmap bitmap;^M^Mbitmap.setConfig(config, width, height);^M//가장 중요한 부분. bitmap이 actual pixel memory에 access할 수 있게 셋팅하는 부분이다. 부차적으로 bitmap에 메모리 alloc하고 pixel환경(?)을 잡아주는것 같다. VM이 그 메모리를 관리(?)할수 있도록 셋팅해주는것 같은데...if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) {^Mreturn NULL;^M}^M^M//pixel color(?)를 잡아주는것 같은데 우리는 null이 넘어왔으므로 passif (jColors != NULL) {^MGraphicsJNI::SetPixels(env, jColors, offset, stride,^M0, 0, width, height, bitmap);^M}^M^M//결국 return에서 일을 하는 것이군.. SkBitmap는 메모리만 다시 잡아주는듯...return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable,^MNULL);^M}^M
bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,SkColorTable* ctable, bool reportSizeToVM) {Sk64 size64 = bitmap->getSize64();if (size64.isNeg() || !size64.is32()) {doThrow(env, "java/lang/IllegalArgumentException","bitmap size exceeds 32bits");return false;}
size_t size = size64.get32();jlong jsize = size; // the VM wants longs for the sizeif (reportSizeToVM) {// SkDebugf("-------------- inform VM we've allocated %d bytes\n", size);//VM에 메모리 관련 뭔가를 하는것 같은데 이번글과 긴밀하게 관련 없는것 같으니 패스bool r = env->CallBooleanMethod(gVMRuntime_singleton,gVMRuntime_trackExternalAllocationMethodID,jsize);if (GraphicsJNI::hasException(env)) {return false;}if (!r) {LOGE("VM won't let us allocate %zd bytes\n", size);doThrowOOME(env, "bitmap size exceeds VM budget");return false;}}// call the version of malloc that returns null on failure//actual pixel memory에서 bitmap을 복사할 storage 공간을 잡는다.void* addr = sk_malloc_flags(size, 0);if (NULL == addr) {if (reportSizeToVM) {// SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size);// we didn't actually allocate it, so inform the VMenv->CallVoidMethod(gVMRuntime_singleton,gVMRuntime_trackExternalFreeMethodID,jsize);if (!GraphicsJNI::hasException(env)) {doThrowOOME(env, "bitmap size too large for malloc");}}return false;}//SkMallocPixel로 이미지를 가져올 storage인 addr을 감싼다. 내부적으로 어떻게 하는지는 모르겠지만 화면에 그려질 pixel memory를 addr에 복사하는것 같다. reportSizeToVM이 true이므로 AndroidPixelRef실행. 하지만 AndroidPixelRef의 구현은 VM이 정상인지(?) 체크만 하고 다시 SKMallocPixelRef를 콜한다. SkMallocPixelRef:SkPixelRef 관계이고 void* fStorage를 추가적으로 관리한다. 아무래도 내부적으로 어떻게인지는 모르겠지만 화면에 그려질 pixel memory를 fStorage에 카피하는것 같다.SkPixelRef* pr = reportSizeToVM ?new AndroidPixelRef(env, addr, size, ctable) :new SkMallocPixelRef(addr, size, ctable);//SkPixelRef 을 최종적으로 그림을 담을 bitmap의 fPixelRef에 pr을 설정하고 fPixels에는 pr.pixels()를 통해 PixelRef의 void* fPixels를 지정한다.bitmap->setPixelRef(pr)->unref();// since we're already allocated, we lockPixels right away// HeapAllocator behaves this way too//하일라이트다. actual pixel memory를 카피한다(어떻게인지는 모르겠음). SkBitmap.lockPixel() -> SkPixelRef.lockPixel() -> SkPixelRef.onLockPixels -> SkMallocPixelRef.onLockPixels 를 연쇄적으로 콜한다. SkMallocPixelRef.fStorage가 SkPixelRef.fPixels에 카피되고 그것이 다시 SkBitmap.fPixels에 카피된다.bitmap->lockPixels();return true;}
This class is the smart container for pixel memory, and is used with SkBitmap. A pixelref is installed into a bitmap, and then the bitmap can access the actual pixel memory by calling lockPixels/unlockPixels.
This class can be shared/accessed between multiple threads.
SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,SkColorTable* ctable) {SkASSERT(storage);fStorage = storage;fSize = size;fCTable = ctable;ctable->safeRef();}
void SkBitmap::lockPixels() const {if (NULL != fPixelRef && 1 == ++fPixelLockCount) {//SkPixelRef의 lockPixel을 콜한다.fPixelRef->lockPixels();this->updatePixelsFromRef();}SkDEBUGCODE(this->validate();)}
void SkPixelRef::lockPixels() {SkAutoMutexAcquire ac(*fMutex);if (1 == ++fLockCount) {fPixels = this->onLockPixels(&fColorTable);}}
void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {*ct = fCTable;return fStorage;}
jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, jbyteArray ninepatch, int density){SkASSERT(bitmap != NULL);SkASSERT(NULL != bitmap->pixelRef());
jobject obj = env->AllocObject(gBitmap_class);if (obj) {env->CallVoidMethod(obj, gBitmap_constructorMethodID,(jint)bitmap, isMutable, ninepatch, density);if (hasException(env)) {obj = NULL;}}return obj;}
private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk,int density) {if (nativeBitmap == 0) {throw new RuntimeException("internal error: native bitmap is 0");}// we delete this in our finalizermNativeBitmap = nativeBitmap;mIsMutable = isMutable;mNinePatchChunk = ninePatchChunk;if (density >= 0) {mDensity = density;}}
안드로이드 카메라 제어 참고부분 (0) | 2010.11.23 |
---|---|
Android에서 View를 bitmap으로 정장하는법 (0) | 2010.11.22 |
안드로이드 sqlite 대략 정보 #### (0) | 2010.11.22 |
안드로이드 테이블 존재여부 확인하기 - Sqlite mater table query (0) | 2010.11.22 |
cursoradapter를 커스텀해서 listview에 뿌려주자...^^ (1) | 2010.11.19 |
출처:안드로이드에서 DataBase를 다뤄보자! (1)
SQLite 1
-> SQLite는 다른 프로그램에 임베팅하기 좋으면서도 깔끔한 SQL 인터페이스를 제공
-> 메모리도 적게 사용하면서 속도도 빠르다.
-> 실행파일과 소스 코드가 무료이고 공개되어 있기 때문에 많이 사용된다.
-> 안드로이드는 SQLite를 내장하고 있으며, 모든 안드로이드 애플리케이션은 간단하게 SQLite 데이터베이스를 생성해 활용할수 있다.
-> SQLite는 표준 SQL 인터페이스를 사용한다.
-> SQLite가 JDBC를 기본 API로 제공하지 않고, 휴대폰과 같은 환경에서 JDBC와 같은 규모 있는 프레임웍은 여러모로 무리가 된다.
-> 액티비티는 일반적으로 컨텐트 프로바이더나 서비스 등을 통해 데이터베이스에 접근한다.
SQLite 2
-> SQL 문법에 맞는 명령을 통해 데이터를 가져오거나(SELECT) 데이터를 변경하고(INSERT 등) 데이터 구조를 정의하는(CREATE TABLE 등) 작업을 처리한다.
-> 실제 데이터를 추가할 때는 컬럼마다 데이터 타입에 상관없이 어떤 데이터라도 마음대로 넣을수 있다. 예를 들어 INTEGER로 정의된 컬럼에 문자열 값도 아무런 문제없이 넣을수 있다.
-> 위와 같은 기능을 매니페스트 타입이라고 표현한다.
-> 매니페스트 타입 입장에서 보면 데이터 타입은 컬럼 자체가 아닌 개별값에 연결되는 속성이다. 따라서 SQLite는 애초에 해당 컬럼에 지정된 데이터 타입과 상관없이 어떤 데이터 타입의 어떤 값이라도 아무 컬럼에나 집어넣을수 있다.
-> 표준 SQL 구문에 정의된 기능 가운데 지원하지 않는 기능 : FOREIGN KEY, 중첩 트랜잭션, RIGHT OUTER JOIN, FULL OUTER JOIN, ALTER TABLE 등
기초
-> 데이터베이스를 생성하고 오픈하려면 SQLiteOpenHelper 객체를 사용한다.
-> SQLiteOpenHelper 클래스는 애플리케이션에서 요구하는 내용에 따라 데이터베이스를 생성하거나 업그레이드하는 기능을 제공한다.
-> SQLiteOpenHelper 클래스를 상속받아 구현하려면 다음과 같은 세가지 기능을 준비해야한다
-> 1. 생성 메소드 : 상위 클래스의 생성 메소드를 호출, Activity 등의 Context 인스턴스와 데이터베이스의 이름, 커서 팩토리(보통 Null 지정) 등을 지정하고, 데이터베이스 스키마 버전을 알려주는 숫자값을 넘겨 준다.
-> 2. onCreate() 메소드 : SQLiteDatabase를 넘겨 받으며, 데이블을 생성하고 초기 데이터를 추가하기에 적당한 위치이다.
-> 3. onUpgrade() 메소드 : SQLiteDatabase 인스턴스를 넘겨 받으며, 현재 스키마 버전과 최신 스키마 버전 번호도 받는다.
-> SQLiteOpenHelper를 상속받은 클래스를 사용하려면 먼저 인스턴스를 하나 생성한 다음, 하려는 작업이 읽기 전용인지 여부에 따라 getReadableDatabase()나 getWritealbleDatabese() 메소드를 호출 한다.
db = (new DatabaseHelper(getContext())).getWritableDatabase();
return (db == null) ? false : ture;
-> 결과적으로 db 변수에 SQLiteDatabase 인스턴스를 받아오게 되는데, SQLiteDatabase 인스턴스를 사용해 데이터를 호출하거나 내용을 변경 할수 있다.
-> 액티비티가 종료되는 등 데이터베이스를 모두 사용하고 나면 SQLiteDatabase인스턴스의 close() 메소드를 호출해 연결을 해제한다.
테이블 준비
-> 테이블과 색인 등을 생성하려면 원하는 DDL 구문을 준비해서 SQLiteDatabase 인스턴스의 execSQL() 메소드를 호출해야 한다.
db.execSQL("CREATE TABLE constants (_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, value REAL);");
-> constants라는 테이블이 생성되고, constants 테이블 기본키로 _id 컬럼이 사용되며 _id 컬럼은 정수형의 숫자값이 자동으로 증가되는 컬럼이다.
-> 실제 데이터는 title이라는 문자열 컬럼과 value라는 실수형 컬럼에 들어간다.
-> 키본키에 해당하는 컬럼에 대해서는 자동으로 색인을 생성한다.
-> 다른 컬럼에도 색인이 필요하다면 CREATE INDEX 구문으로 색인을 건다.
-> 테이블이나 색인을 제거해야 하는 상황이라면 DROP INDEX나 DROP TABLE 구문을 execSQL()로 실행하면 된다.
데이터 추가
-> 데이터를 추가하는 방법은 2가지가 있다.
-> 1. execSQL() 메소드는 값을 가져오는 문장이 아닌 INSERT, UPDATE, DELETE등의 모든 SQL 구문을 처리할 수 있다.
db.execSQL("INSERT INTO widgets (name, inventory)" + "VALUES ('Sprocket', 5)");
-> 2. SQLiteDatabase 클래스에서 제공하는 insert(), update(), delete() 등의 메소드를 사용하는 방법이 있다. 이와같은 개별 메소드는 인자로 입력받은 값을 조합해 최종적으로 SQL 문장을 동일하게 실핼하도록 만들어져 있다.
-> 개별 메소드는 Map과 비슷한 구조로 만들어져 있으면서 SQLite의 데이터 타입에 맞춰 동작하도록 구성된 ContentValues 객체를 사용해 동작한다.
-> 지정한 키에 해당하는 값을 찾아올때 단순하게 get() 메소드를 사용하는 대신, getAsInteger(), getAsString() 등의 메소드를 호출한다.
-> insert() 메소드는 대상이 되는 테이블 이름, null 처리 컬럼명, ContentValues 객체에 컬럼별 값을 넣어 인자로 넘겨 준다.
-> SQLite는 값이 하나도 없는 행은 허용하지 않는다. 따라서 ContentValues 인스턴스 값이 하나도 없는 경우 행이 생성되지 않기 때문에 이런경우 null 처리 컬럼 이름으로 지정된 컬럼값으로 NULL을 지정해 행이 생성되게 한다.
contentValues cv = new ContentValues();
cv.put(ContentValues.TITLE, "Gravity, Death Star I");
cv.put(ContentValues.VALUE, SensorManager.GRAVITY_DEATH_STAR_I);
db.insert("constants", getNullColumnHack(), cv);
-> update() 메소드는 대상 테이블 이름과 변경할 값이 들어 있는 ContentValues 객체를 넘겨준다.
-> 값을 변경할 대상을 한정지으려면 WHERE 구문과 함께 WHERE 조건에 해당하는 값 역시 넘겨줌
-> WHERE 구문에 물음표로 지정된 위치에 각자의 값이 배치돼 처리된다.
-> update() 메소드는 다른 정보를 사용해 계산된 값이 아닌 고정된 값을 갖는 컬럼만 변경할 수 있으므로, 필요한 경우에는 execSQL() 메소드를 사용해야 할 수도 있다.
-> WHERE 구문에 표시된 물음표와 각 조건값을 지정하는 방법은 다른 SQL API에서 많이 사용하던 방법과 별반 다르지 않다.
// replacements는 ContentValues 인스턴스
String[] parms = new String[] {"snicklefritz"};
db.update("widgets", replacements, "name=?", parms);
-> delete() 메소드 역시 테이블 이름과 WHERE 구문을 사용한다.
-> 변경할 값을 지정하지 않는다는 점만 제외하면 update()와 동일한 방법으로 동작한다.
데이터 불러오기
-> INSERT, UPDATE, DELETE와 비슷하게 SELECT 구문으로 데이터를 가져올때도 2가지 방법을 사용할 수 있다.
-> 1. rawQuery() 메소드를 사용해 SELECT 구문을 직접 실행
-> 2. query() 메소드를 인자로 각 부분의 값을 넘겨 실행
-> SQLiteQueryBuilder 클래스와 관련된 부분과 커서와 커서 팩토리에 관한 부분이 가장 복잡하다.
SQL문 직접 작성
-> API 호출 방법만 놓고 보면 rawQuery() 메소드를 사용하는 방법이 가장 간단하다.
-> rawQuery() 메소드에 SELECT 구문을 넘겨 주기만 하면 된다.
-> SELECT 구문 역시 위치에 맞는 인자 배열을 함께 넘겨 줄수 있다.
Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='constants'", null);
-> 위의 코드는 SQLite의 기본 테이블 가운데 하나인 sqlite_master 테이블 내용을 가져오는데, 내용으로 보면 constants라는 이름의 테이블이 만들어져 있는지 확인하는 구문이다.
-> 결과로 받아오는 값은 Cursor 인스턴스인데, Cursor를 사용하면 여러 건의 결과를 하나씩 받아오면서 처리할수 있다.
-> SELECT 문장이 동적으로 변경되지 않고 아예 프로그램 내부에 고정시켜버릴 예정이라면 위 방법이 가장 간단하다.
-> SELECT 구문 가운데 이부분이 동적으로 변경되거나, 위치로 인자값을 지정하는 방법으로 한계가 있는 수준이라면 굉장히 복잡해진다.
-> 예를 들어 값을 가져와야 할 컬럼 개수가 개발 당시에 정해지지 않고 동적으로 변경된다면 처리하기 쉽지 않다. 그렇다고 해서 컬럼 이름을 필요할 때마다 쉼표로 연결해 사용하는건 물론 좋은 방법이 아니다. 이런 경우에는 query() 메소드를 사용하는게 훨씬 간편하다.
일정한 형식의 쿼리
-> query() 메소드는 SELECT 구문의 각 부분을 쪼개 각 인자로 넘겨받고, 최종적으로는 SELECT문을 생성해 실행한다.
-> query() 가 받아서 처리하는 인자의 순서
-> 1. 대상 테이블 이름
-> 2. 값을 가져올 컬럼 이름의 배열
-> 3. WHERE 구문. 물음표를 사용해 인자의 위치를 지정할 수 있다.
-> 4. WHERE 구문에 들어가는 인자값
-> 5. GROUP BY 구문
-> 6. ORDER BY 구문
-> 7. HAVING 구문
-> 테이블 이름을 제외한 각 값이 필요없는 경우라면 null을 지정한다.
String[] columns={"ID", "inventory"};
Steing[] parms={"snicklefritz"};
Cursor result=db.query("widgets", columns, "name=?", parms, null, null, null);
쿼리 구문 생성
-> SQLiteQueryBuilder 클래스를 활용하면 훨씬 다양한 방법으로 UNION이나 하위 쿼리 등을 포함하는 복잡한 구문을 생성할 수 있다.
-> SQLiteQueryBuilder 클래스가 ContentProvider 인터페이스와 완벽하게 맞아 떨어진다.
-> 컨텐트 프로바이더의 query() 메소드를 구현하는 가장 일반적인 방법은 SQLiteQueryBuilder 인스턴스를 생성하고 기본값을 일부 채워넣은 다음 전체 쿼리를 생성하고 실행할수 있게 구성한다.
-> SQLiteQueryBuilder 클래스를 사용해 요청을 처리하는 컨텐츠 프로바이더의 코드
@Override
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort){
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(getTableName());
if (isCollectionUri(url)){
qb.setProjectionMap(getDefaultProjection());
}else{
qb.appendWhere(getIdColumnName()+"="+url.getPathSegments().get(1));
}
String orderBy;
if (TextUtils.isEmpty(sort)){
orderBy=getDefaultSortOrder();
}else{
orderBy=sort;
}
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), url);
return c;
}
-> 1. SQLiteQueryBuilder 인스턴스를 생성
-> 2. 쿼리에 사용할 테이블 이름을 설정 (setTables(getTableName())).
-> 3. 값을 가져올 기본 컬럼 이름의 목록을 지정하거나 (setProjectionMap()), 또는 Uri값에 들어 있는 ID값으로 테이블 항목 가운데 특정한 값을 가져올 수 있도록 WHERE 구문을 추가했다(appendWhere()).
-> 4. 마지막으로 기본값과 요청이 들어온 값을 조합해 생성된 쿼리 구문을 실행한다.
(qb.query(db, projection, selection, selectionArgs, null, null, orderBy))
-> SQLiteQueryBuilder에서 쿼리를 직접 실행하는 대신 buildQuery() 메소드를 호출해 최종 생성된 SELECT 구문만을 리턴하게 할 수도 있다. 그러면 넘겨 받은 SELECT문을 필요할때 실행할수 있다.
커서 활용
-> SELECT 쿼리를 어떻게 실행하건 간에 그 결과로는 Cursor 인스턴스를 받는다.
-> 커서 개념을 안드로이드와 SQLite에서 구현한 클래스가 바로 Cursor다.
-> getCount() 메소드 : 전체 결과 건수가 몇개인지 확인할 수 있다.
-> moveToFirst(), moveToNext(), isAfterLast() 등의 메소드 : 결과건을 모두 확인할수있다.
-> getColumnNames() 메소드 : 결과에 포함된 전체 컬럼 이름을 알수 있다.
-> requery() 메소드 : 쿼리를 재실행 할수 있다.
-> close() 메소드 : 커서가 확보한 자원을 모두 해제한다.
Cursor result = db.rawQuery("SELECT ID, namem inventory FROM widgets");
result.moveToFirst();
while (!result.isAfterLast()){
int id = result.getInt(0);
String name = result.getString(1);
int inventory = result.getInt(2);
//실제 필요한 작업 처리
result.moveToNext();
}
result.close();
-> widgets 테이블에 있는 항목을 모두 가져와 모든 결과값을 뽑아내는 반복문 예제
커서 구현
-> 기본적으로 제공하는 Cursor 인스턴스 대신 Cursor를 상속받아 새로운 커서를 구현해야 할 피요가 있을 수도 있다.
-> queryWithFactory() 메소드나 rawQueryWithFactory() 메소드에 SQLiteDatabase.CursorFactory 인스턴스를 인자로 넘겨 사용한다.
-> CursorFactory 클래스는 newCursor() 메소드가 구현된 내용에 따라 새로운 Cursor를 생성한다.
-> 일반적인 안드로이드 애플리케이션을 개발하고 있다면 커서를 새로 구현해야할 일이 많지 않다.
데이터 직접 다루기
-> 에뮬레이터에는 sqlite3 프로그램이 포함되어 있고, adb shell 명령을 통해 실행해 사용할수있다.
-> 에뮬레이터의 셸에 접속한 다음 sqlite3명령을 실행하면서 데이터베이스 파일이 위치한 경로를 함게 지정해 주면 된다.
-> 데이터베이스 파일의 일반적인 위치 : /data/data/your.app.package/database/your-db-name
-> your.app.package 부분은 애플리케이션이 들어 있는 자바 패키지 명을 의미한다.
-> your-db-name 부분은 createDatabase() 명령을 실행할때 지정했던 데이터베이스 이름을 넣는다.
-> sqlite3 프로그램은 충분한 기능을 갖추고 있으며, 콘솔 화면에서 데이터베이스를 다루는게 익숙하다면 괜찮은 방법이다.
-> 콘솔 인터페이스보다 좀더 GUI를 갖춘 화면이 필요하다면, 위의 특정 경로에 보관되어 있는 데이터베이스 파일을 복사한 다음, 데스크탑에서 SQLite 데이터베이스 파일을 인식하는 다양한 프로그램을 활용해 데이터를 조회하고 다룰 수 있다.
-> 바깥으로 불러낸 복사본에 변경 작업을 진행했다면 변경된 데이터베이스 파일을 다시 기기에 업로드해야 반영된다.
-> 데이터베이스 파일을 기기에서 뽑아내려면 adb pull 메소드를 사용하여, 원본 경로와 대상 디렉토리 등을 적어주면 파일을 복사할수 있다.
-> 변경된 데이터베이스 파일을 기기에 업로드 하려면 adb push 명령을 사용한다.
-> adb push도 adb pull과 마찬가지로 원본 파일 경로와 대상 디렉토리 등을 알려줘야 한다.
-> 일반적으로 가장 많이 사용되는 SQLite 클라이언트 프로그램은 파이어폭스 브라우저의 확장 기능으로 구현돼있는 SQLite Manager이다. 파이어 폭스 확장 기능이기 때문에 운영체제 플랫폼에 상관없이 어디서든 사용할 수 있다.
Android에서 View를 bitmap으로 정장하는법 (0) | 2010.11.22 |
---|---|
Android View.getDrawingCache() 분석 (0) | 2010.11.22 |
안드로이드 테이블 존재여부 확인하기 - Sqlite mater table query (0) | 2010.11.22 |
cursoradapter를 커스텀해서 listview에 뿌려주자...^^ (1) | 2010.11.19 |
안드로이드에서 Base64 파일 못찾아서 헤롱거릴때.. (0) | 2010.11.19 |
출처 : http://blog.naver.com/oh4zzang
안드로이드 테이블 존재여부 확인하기 - Sqlite mater table query
안드로이드 sqlite에서 다음과 같이, master table 에 접근해 해당 table 존재 여부를 알 수 있다.
다음은 SQLiteDatabase를 이용한 간단한 로그 찍어 테이블 이름을 확인하는 쿼리다~~
Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);
if(c.moveToFirst()) {
for(;;) {
Log.e(TAG, "table name : " + c.getString(0));
if(!c.moveToNext())
break;
}
}
특정 테이블에 대해서 존재하는지 알기 위해서는 SELECT 문을 아래와 같이 하면 된다~
"SELECT name FROM sqlite_master WHERE type='table' AND name ='테이블명'
테이블 생성 시 존재여부를 따로 쿼리해보고 테이블 생성하는 것이 귀찮게 느껴진다면,
아래처럼 테이블 생성시 CREATE문에 "CREATE TABLE IF NOT EXISTS" 이렇게 해주면~
테이블이 이미 존재한다면 테이블을 생성하지 않는다. >0<
String sql = "CREATE TABLE IF NOT EXISTS " + "테이블 명"+ " (_id integer primary key autoincrement, " + "column 명..)";
db.execSQL(sql);
logcat으로 찍어본 결과는 아래와 같다~~
밑에 2개의 테이블은 현재 프로젝트와 연관있는 이름들이라~ 모자이크 처리 ^^
존재하는 테이블 이름들을 잘 받아오는것을 볼수 있다ㅋ
Android View.getDrawingCache() 분석 (0) | 2010.11.22 |
---|---|
안드로이드 sqlite 대략 정보 #### (0) | 2010.11.22 |
cursoradapter를 커스텀해서 listview에 뿌려주자...^^ (1) | 2010.11.19 |
안드로이드에서 Base64 파일 못찾아서 헤롱거릴때.. (0) | 2010.11.19 |
안드로이드로 사진찍어서 썸네일 이미지 가지고 오기..(중급) (0) | 2010.11.19 |
안드로이드 sqlite 대략 정보 #### (0) | 2010.11.22 |
---|---|
안드로이드 테이블 존재여부 확인하기 - Sqlite mater table query (0) | 2010.11.22 |
안드로이드에서 Base64 파일 못찾아서 헤롱거릴때.. (0) | 2010.11.19 |
안드로이드로 사진찍어서 썸네일 이미지 가지고 오기..(중급) (0) | 2010.11.19 |
안드로이드 개발 참고 사이트 모음입니다. (0) | 2010.11.19 |
안드로이드 테이블 존재여부 확인하기 - Sqlite mater table query (0) | 2010.11.22 |
---|---|
cursoradapter를 커스텀해서 listview에 뿌려주자...^^ (1) | 2010.11.19 |
안드로이드로 사진찍어서 썸네일 이미지 가지고 오기..(중급) (0) | 2010.11.19 |
안드로이드 개발 참고 사이트 모음입니다. (0) | 2010.11.19 |
안드로이드:그림메모 소스.. (0) | 2010.11.14 |
1.
private
OnClickListener photoPickClick =
new
OnClickListener() {
2.
public
void
onClick(View v) {
3.
// TODO Auto-generated method stub
4.
Intent cameraIntent =
new
Intent(MediaStore.ACTION_IMAGE_CAPTURE);
5.
startActivityForResult(cameraIntent,
1
);
6.
}
7.
};
01.
protected
void
onActivityResult(
int
requestCode,
int
resultCode, Intent data) {
02.
// TODO Auto-generated method stub
03.
super
.onActivityResult(requestCode, resultCode, data);
04.
if
(resultCode!=
0
){
05.
if
(requestCode==
1
&&!data.equals(
null
)){
06.
try
{
07.
profileBitmap = (Bitmap)data.getExtras().get(
"data"
);
08.
profileView.setImageBitmap(profileBitmap);
09.
profileView.setScaleType(ImageView.ScaleType.FIT_XY);
10.
}
catch
(Exception e){
11.
return
;
12.
}
13.
}
14.
}
15.
}
1.
ByteArrayOutputStream stream =
new
ByteArrayOutputStream();
2.
profileBM.compress(CompressFormat.PNG,
100
, stream);
3.
byte
[] image = stream.toByteArray();
4.
String profileImageBase64 = Base64.encodeBytes(image);
01.
private
OnClickListener photoPickClick =
new
OnClickListener() {
02.
public
void
onClick(View v) {
03.
// TODO Auto-generated method stub
04.
Intent cameraIntent =
new
Intent(MediaStore.ACTION_IMAGE_CAPTURE);
05.
File file =
new
File(Environment.getExternalStorageDirectory(),
"picture.jpg"
06.
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
07.
startActivityForResult(cameraIntent,
1
);
08.
}
09.
};
cursoradapter를 커스텀해서 listview에 뿌려주자...^^ (1) | 2010.11.19 |
---|---|
안드로이드에서 Base64 파일 못찾아서 헤롱거릴때.. (0) | 2010.11.19 |
안드로이드 개발 참고 사이트 모음입니다. (0) | 2010.11.19 |
안드로이드:그림메모 소스.. (0) | 2010.11.14 |
[Android] startActivityForResult(), onActivityResult() 사용하기 [출처] [Android] startActivityForResult(), onActivityResult() 사용하기|작성자 소녕 (0) | 2010.11.13 |
안드로이드에서 Base64 파일 못찾아서 헤롱거릴때.. (0) | 2010.11.19 |
---|---|
안드로이드로 사진찍어서 썸네일 이미지 가지고 오기..(중급) (0) | 2010.11.19 |
안드로이드:그림메모 소스.. (0) | 2010.11.14 |
[Android] startActivityForResult(), onActivityResult() 사용하기 [출처] [Android] startActivityForResult(), onActivityResult() 사용하기|작성자 소녕 (0) | 2010.11.13 |
안드로이드 DB인 SQLITE 정보 (0) | 2010.11.12 |
ListDataEvent.CONTENTS_CHANGED | |
| |
|
색상표 (0) | 2010.12.02 |
---|---|
[펌]압축 관련 스트림 팁 (0) | 2010.12.01 |
파일이름 중복 검사하는 policy 파일 만들기 (0) | 2010.11.17 |
Introduction to Object Serialization(객체직렬화를 통한 파일 저장 및 DB저장 튜터리얼) (1) | 2010.11.16 |
자바 프리드로우 버전이다. (0) | 2010.11.15 |
public class FileRenamePolicy {
public File rename(File f) { //File f는 원본 파일
if (createNewFile(f)) return f; //생성된 f가
//확장자가 없는 파일 일때 처리
String name = f.getName();
String body = null;
String ext = null;
int dot = name.lastIndexOf(".");
if (dot != -1) { //확장자가 없을때
body = name.substring(0, dot);
ext = name.substring(dot);
} else { //확장자가 있을때
body = name;
ext = "";
}
int count = 0;
//중복된 파일이 있을때
while (!createNewFile(f) && count < 9999) {
count++;
String newName = body + count + ext;
f = new File(f.getParent(), newName);
}
return f;
}
private boolean createNewFile(File f) {
try {
return f.createNewFile(); //존재하는 파일이 아니면
}catch (IOException ignored) {
return false;
}
}
}
[펌]압축 관련 스트림 팁 (0) | 2010.12.01 |
---|---|
javax.swing.event 에서의 ListDataEvent 에 관한 설명 (1) | 2010.11.17 |
Introduction to Object Serialization(객체직렬화를 통한 파일 저장 및 DB저장 튜터리얼) (1) | 2010.11.16 |
자바 프리드로우 버전이다. (0) | 2010.11.15 |
업캐스팅 관련 설명 (0) | 2010.11.15 |
Java object serialization is used to persist Java objects to a file, database, network, process or any other system. Serialization flattens objects into an ordered, or serialized stream of bytes. The ordered stream of bytes can then be read at a later time, or in another environment, to recreate the original objects.
Java serialization does not cannot occur for transient or static fields. Marking the field transient prevents the state from being written to the stream and from being restored during deserialization. Java provides classes to support writing objects to streams and restoring objects from streams. Only objects that support the java.io.Serializable interface or the java.io.Externalizable interface can be written to streams.
public interface Serializable
The transient keyword is a modifier applied to instance variables in a class. It specifies that the variable is not part of the persistent state of the object and thus never saved during serialization.
You can use the transient keyword to describe temporary variables, or variables that contain local information,
ObjectOutputStream is the primary output stream class that implements the ObjectOutput interface for serializing objects. ObjectInputStream is the primary input stream class that implements the ObjectInput interface for deserializing objects.
These high-level streams are each chained to a low-level stream, such as FileInputStream or FileOutputStream.
The low-level streams handle the bytes of data. The writeObject method saves the state of the class by writingthe individual fields to the ObjectOutputStream. The readObject method is used to deserialize the object from
the object input stream.
Case 1: Below is an example that demonstrates object Serialization into a File
PersonDetails is the bean class that implements the Serializable interface
import java.io.Serializable; public class PersonDetails implements Serializable { private String name; private int age; private String sex; public PersonDetails(String name, int age, String sex) { this.name = name; this.age = age; this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } } |
GetPersonDetails is the class that is used to Deserialize object from the File (person.txt).
import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.List; public class GetPersonDetails { public static void main(String[] args) { String filename = "person.txt"; List pDetails = null; FileInputStream fis = null; ObjectInputStream in = null; try { fis = new FileInputStream(filename); in = new ObjectInputStream(fis); pDetails = (ArrayList) in.readObject(); in.close(); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } // print out the size System.out.println("Person Details Size: " + pDetails.size()); System.out.println(); } } |
PersonPersist is the class that is used to serialize object into the File (person.txt).
public class PersonPersist { public static void main(String[] args) { String filename = "person.txt"; PersonDetails person1 = new PersonDetails("hemanth", 10, "Male"); PersonDetails person2 = new PersonDetails("bob", 12, "Male"); PersonDetails person3 = new PersonDetails("Richa", 10, "Female"); List list = new ArrayList(); list.add(person1); list.add(person2); list.add(person3); FileOutputStream fos = null; ObjectOutputStream out = null; try { fos = new FileOutputStream(filename); out = new ObjectOutputStream(fos); out.writeObject(list); out.close(); System.out.println("Object Persisted"); } catch (IOException ex) { ex.printStackTrace(); } } } |
——————————————————————————–
Case 2: Below is an example that demonstrates object Serialization into the database
PersonDetails remains the same as shown above
GetPersonDetails remains the same as shown above
Create SerialTest Table
create table SerialTest(
name BLOB,
viewname VARCHAR2(30)
);
PersonPersist is the class that is used to serialize object into the into the Database Table SerialTest.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement; public class PersonPersist {
static String userid = "scott", password = "tiger";
static String url = "jdbc:odbc:bob";
static int count = 0;
static Connection con = null;
public static void main(String[] args) {
Connection con = getOracleJDBCConnection();
PersonDetails person1 = new PersonDetails("hemanth", 10, "Male");
PersonDetails person2 = new PersonDetails("bob", 12, "Male");
PersonDetails person3 = new PersonDetails("Richa", 10, "Female");
PreparedStatement ps;
try {
ps = con
.prepareStatement("INSERT INTO SerialTest VALUES (?, ?)");
write(person1, ps);
ps.execute();
write(person2, ps);
ps.execute();
write(person3, ps);
ps.execute();
ps.close();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM SerialTest");
while (rs.next()) {
Object obj = read(rs, "Name");
PersonDetails p = (PersonDetails) obj;
System.out.println(p.getName() + "\t" + p.getAge() + "\t"
+ p.getSex());
}
rs.close();
st.close();
} catch (Exception e) {
}
}
public static void write(Object obj, PreparedStatement ps)
throws SQLException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(baos);
oout.writeObject(obj);
oout.close();
ps.setBytes(1, baos.toByteArray());
ps.setInt(2, ++count);
}
public static Object read(ResultSet rs, String column)
throws SQLException, IOException, ClassNotFoundException {
byte[] buf = rs.getBytes(column);
if (buf != null) {
ObjectInputStream objectIn = new ObjectInputStream(
new ByteArrayInputStream(buf));
return objectIn.readObject();
}
return null;
}
public static Connection getOracleJDBCConnection() {
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
} catch (java.lang.ClassNotFoundException e) {
System.err.print("ClassNotFoundException: ");
System.err.println(e.getMessage());
}
try {
con = DriverManager.getConnection(url, userid, password);
} catch (SQLException ex) {
System.err.println("SQLException: " + ex.getMessage());
}
return con;
}
} |
——————————————————————————–
Case 3: Below is an example that demonstrates object Serialization into the database using Base 64 Encoder
PersonDetails remains the same as shown above
GetPersonDetails remains the same as shown above
Create SerialTest Table
create table SerialTest(
name BLOB,
viewname VARCHAR2(30)
);
PersonPersist is the class that is used to serialize object into the Database Table SerialTest
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class PersonPersist { static String userid = "scott", password = "tiger"; static String url = "jdbc:odbc:bob"; static int count = 0; static Connection con = null; static String s; public static void main(String[] args) { Connection con = getOracleJDBCConnection(); PersonDetails person1 = new PersonDetails("hemanth", 10, "Male"); PersonDetails person2 = new PersonDetails("bob", 12, "Male"); PersonDetails person3 = new PersonDetails("Richa", 10, "Female"); PreparedStatement ps; try { ps = con .prepareStatement("INSERT INTO SerialTest VALUES (?, ?)"); write(person1, ps); ps.execute(); write(person2, ps); ps.execute(); write(person3, ps); ps.execute(); ps.close(); Statement st = con.createStatement(); ResultSet rs = st.executeQuery("SELECT * FROM SerialTest"); while (rs.next()) { Object obj = read(rs, "Name"); PersonDetails p = (PersonDetails) obj; System.out.println(p.getName() + "\t" + p.getAge() + "\t" + p.getSex()); } rs.close(); st.close(); } catch (Exception e) { } } public static void write(Object obj, PreparedStatement ps) throws SQLException, IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oout = new ObjectOutputStream(baos); oout.writeObject(obj); oout.close(); byte[] buf = baos.toByteArray(); s = new sun.misc.BASE64Encoder().encode(buf); ps.setString(1, s); // ps.setBytes(1, Base64.byteArrayToBase64(baos.toByteArray())); ps.setBytes(1, baos.toByteArray()); ps.setInt(2, ++count); } public static Object read(ResultSet rs, String column) throws SQLException, IOException, ClassNotFoundException { byte[] buf = new sun.misc.BASE64Decoder().decodeBuffer(s); // byte[] buf = Base64.base64ToByteArray(new // String(rs.getBytes(column))); if (buf != null) { ObjectInputStream objectIn = new ObjectInputStream( new ByteArrayInputStream(buf)); Object obj = objectIn.readObject(); // Contains the object PersonDetails p = (PersonDetails) obj; System.out.println(p.getName() + "\t" + p.getAge() + "\t" + p.getSex()); } return null; } public static Connection getOracleJDBCConnection() { try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch (java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: "); System.err.println(e.getMessage()); } try { con = DriverManager.getConnection(url, userid, password); } catch (SQLException ex) { System.err.println("SQLException: " + ex.getMessage()); } return con; } } |
PersonDetails is the bean class that implements the Serializable interface
import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; public class ObjectSerializationExample { public static void main(String args[]) { try { Object object = new javax.swing.JButton("Submit"); // Serialize to a file namely "filename.dat" ObjectOutput out = new ObjectOutputStream( new FileOutputStream("filename.dat")); out.writeObject(object); out.close(); // Serialize to a byte array ByteArrayOutputStream bos = new ByteArrayOutputStream(); out = new ObjectOutputStream(bos); out.writeObject(object); out.close(); // Get the bytes of the serialized object byte[] buf = bos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } } } |
javax.swing.event 에서의 ListDataEvent 에 관한 설명 (1) | 2010.11.17 |
---|---|
파일이름 중복 검사하는 policy 파일 만들기 (0) | 2010.11.17 |
자바 프리드로우 버전이다. (0) | 2010.11.15 |
업캐스팅 관련 설명 (0) | 2010.11.15 |
내부클래스 설명 (1) | 2010.11.10 |
요즘 며칠동안 공부중인 캔버스 프리드로우 버전입니다영..^^
1단계 : 그냥 캔버스에 간단한 마우스 이벤트를 통한 프리드로우입니다.
2단계 : 1단계를 해보니 할 때마다 다시 페인트를 해서 번쩍번쩍 거림..쓰레드 구현 필요(우리 수업도 빨리 쓰레드,Db,IO가 나가야 할텐데..벌써 한달이 지났엉..==;)
3단계 : 이걸 DB든 파일이든 직렬화 저장해서 정말 스마트폰에 있는 그림메모처럼 만드는 게 목표.
최종단계 : 이걸 그대로 안드로이드폰에 옮겨봄...ㅋ
암튼 현재 1단계 성공..
소스첨부하니 참고하실 분 보세요.
import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import javax.swing.JFrame;
public class DrawTest extends JFrame {
MyCanvas canvas;
ArrayList<Vortex> list;
public DrawTest(){
super("DrawTest");
System.out.println("DrawTest 시작");
list = new ArrayList<Vortex>();
canvas = new MyCanvas();
canvas.setBackground(Color.WHITE);
canvas.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent me){
list.add(new Vortex(me.getX(),me.getY(),false));
}
});
canvas.addMouseMotionListener(new MouseMotionAdapter(){
public void mouseDragged(MouseEvent me){
list.add(new Vortex(me.getX(),me.getY(),true));
canvas.repaint();
}
});
this.add(canvas);
this.setSize(300,300);
}
class Vortex{
float x;
float y;
boolean isDraw;
public Vortex(float x,float y,boolean isDraw){
this.x = x;
this.y = y;
this.isDraw = isDraw;
}
}
class MyCanvas extends Canvas{
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke(2)); // 선 굵기 설정
int size = list.size();
for(int i=0;i<size;i++){
Vortex tex = (Vortex)list.get(i);
if(tex.isDraw){
g.drawLine((int)list.get(i-1).x, (int)list.get(i-1).y,
(int)list.get(i).x, (int)list.get(i).y);
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new DrawTest().setVisible(true);
}
}
파일이름 중복 검사하는 policy 파일 만들기 (0) | 2010.11.17 |
---|---|
Introduction to Object Serialization(객체직렬화를 통한 파일 저장 및 DB저장 튜터리얼) (1) | 2010.11.16 |
업캐스팅 관련 설명 (0) | 2010.11.15 |
내부클래스 설명 (1) | 2010.11.10 |
XML DOM 자바로 이해하기 (0) | 2010.11.07 |
음..
업캐스팅이 처음 들어 보는 단어라고 그러셨는데..
상속을 알고 있다면 그리고 인터페이스 와 추상 클래스, 매서드 오버라이딩의 개념을
알고 있다면 이미 업캐스팅을 사용하는 겁니다.
아니 이미 업캐스팅은 문장에 만들어져 있습니다
모든 자바는 Objdec 클래스에서 상속받습니다.
명시적으
class A {
} 라는게 있다면
이미 묵시적으로
class A extends Object 가 형성되는 거죠.
자 그럼 업태스팅은 무엇인지 알아보죠.
대표적인 구문을 하나 보여드리겠습니다.
class A{
public void draw(){
System.out.println(" A번입니다");
}
}
class B extends A{
public void draw(){
System.out.println("B클래스입니다.");
}
}
class C extends B{
public void draw(){
System.out.println("C클래스입니다");
}
public void set(){
System.out.pirnln("set매서드입니다");
}
실행부
main(String[] args){
A a=new A();
a.draw();
// 이경우는 A클래스의 A 클래스입니다. 출력
B b=new B();
b.draw();
// B클래스입니다. 출력
C c=new C();
c.draw();
c.set();
//C클래스의 C클래스입니다. set 매서드입니다. 출력
A ab=new C();
c.draw();
c.set();
이경우는 C클래스입니다. 출력 c.set()는 에러.
자 마지막을 보죠.
A ab=new C();
라는 것을 만들었죠.
A클래스는 C클래스로 상속이 되 있습니다.
그런데 이 클래스를 풀이해보면
A클래스 형을 가지는 ab는 C클래스의 메모리를 사용한다. 라는 뜻이됩니다.
여기서 매서드 오버라이드 개념이 사용됐죠. draw()는 오버라이드 됐습니다.
오버라이드가 되면 재정의를 통해 아들의 매서드를 사용합니다. 이미 알고 계시니 오버라이드는
생략하죠.
이상하게 c.set()은 에러가 나왔습니다. c클래스이 메모리를 사용하면서 왜 C 클래스의 매서들르
사용 못할까요?
수행순서를 생각해 보죠.
A ab=new C(); 가 만들어지면
상속 받은 클래스 A 에 대해 재정의 된 매서드가 있는지 검사합니다.
draw()는 재정의가 됐으니 당연히 사용가능합니다.
하지만 set() 은 재정의가 이루어지지 않았죠. 당연히 사용 불능입니다.
하지만 이것이 무얼 의미하는지 진정한 의미를 파악하는게 중요합니다.
인터페이스와 추상클래스 개념에서 업캐스팅은 무하한 위력을 발휘죠.
또한 우리가 쉽게 사용하는 구문들 중에서 캐스팅 관련에서 업캐스팅은 많이 일어납니다.
무심코 사용하는 구문들 대부분이 업 캐스팅의 개념을 포함하고 있습니다.
실용적인 예로 인터페이스의 리스너의 들면
마우스 리스러는 마우스의 조작을 처리합니다.
우리는 리스터를 통해 마우스의 여러가지 특성을 부여하면
자바 vm은 알아서 마우스를 제어하는 컴퓨터의 드라이버를 찾아 그 실행에 맞게
마우스를 조작하는 역활을 해주죠. 우선 이 의미를 파악하고 리스너를 사용하십시오
모든 리스너는 다 이런 식입니다.
마우스가 컴퓨터의 윈도우 환경에서 동작하려면 드라이버를 깔아야죠? ps/2나 usb드라이버를
찾지 않습니까? 하지만 자바는 vm기반 언어기에 일일이 드라이버에 대한 정의를 해쥐 않아도 됩니다.
리스너라는 편리한 도구가 있고, 그 리스너에서 제공하는 메서드를 사용해 마우스의 조작을
해주는 겁니다.
키보드 리스너의 경우도 마찬가지입니다. 이것 오에 리스너는 상당 종류가 있습니다면
그 원리를 이해한다면 이미 반은 이해한 겁니다.
그런데 아시다시피 인터페이스도 클래스의 한 종류 입니다.
하지만 인터페이스는 껍데기입니다. 메모리를 가지지 않습니다. 메모리가 없기에 구현을 통해
사용하는 것이죠. (메모리가 없다는 것은 그것을 동작한 자료를 넣을 공간이 없다는 겁니다.)
마우스 리스너의 형을 가지고
A 라는 클래스를 만들면 (A 클래스는 마우스 리스너를 구현했고, 메모리도 가지고 있게 되겠죠.)
우리는 A 라는 클래스에서 리스너의 메서드를 모두 적어줘야 합니다. (그걸 방지하는게 아답터죠.) 여기서 일단 매서드 오버라이딩이 일어나고 (오버라이딩이 일어나지 않으면 구현받은 인터페이스에 접근하는게 불가능하니까요.)
우리는 A클래스를 가지고 인터페이스 마우스 리스너의 제어담당 매서드에 접근해 그것을
사용하게 해주는 겁니다.
왜 그럴까요?
인터페이스는 클래스이지만 메모리가 없습니다.
그래소 implemets를 통해 구현하죠.
느낌이 오십니까? (이런것이 여러가지 제작툴에서 사용되는 업캐스팅의 예입니다. 사실... 업캐스팅이 진짜 의미는 이정도를 넘어섭니다. 엄청난 아주 대단한 위력이 있죠. 남의 소스를 고치거나
클래스를 고치고 할때도 업캐스팅은 강력합니다.)
제 설명이 부족하다면 책을 찾아 보시길 바랍니다.
아주 자세히 설명된 책도 많습니다.
추상클래스의 경우도 마찬가지입니다. 추상 클래스는 클래스이지만 껍데기 입니다.
상속 받아 클래스 안에 메모리를 만들어 줘야 사용가능하죠.
업캐스팅을 처음 들어보셨다면... 책을 좀 더 보야 될것 같습니다.
아주중요한 개념이고 이 개념을 이해해야 뒤에가서도 막히지 않습니다.
상속 과 캐스팅이 자바의 전부라 해도 과헌이 아닙니다.
자바는 기초가 아주 중요합니다. 지금도 계속 책을 찾아 보고 있죠.
사실 그림 맞추기 게임에서 진짜 알야될 부분은 이런 기초적인 것에 대한 확실한 이해를 다지는 겁니다.
이것 외에 배열에 대해서도 상당히 많으 부분을 알야 될겁니다.
애플릿은 단지 툴입니다. 솔직히 제 생각에 에플릿은 그리 대단치 않습니다. 차라리 플래쉬가
백배 낫죠. 하지만 애플릿은 그 자체로도 상당히 복잡합니다.
버튼 하나를 넣기 위해서 여러가지 틀을 생각해서 순서에 맞게 구현해야 되지 않나요?
그걸 외우는건 나중일입니다. 일단 기초적인 공부가 받쳐주면 왜 그런 순서로 작성해야 되는지가
자연스럽게 들어오고 해당 클래스와 매서드들이 자연스럽게 머리에 들어오게 됩니다.
한가지 더 예를 든다면
무심코 사용하는 super에 대한 것을 들죠
슈퍼는 아버지 클래스의 생성자를 참조하는 방법입니다.
하지만 이게 어떤 의미인지 왜 그런지에 대해서는 자세히 알지 못하면 사용하는 의미가 없습니다.
부모 클래스를 상속 받은 아들 클래스는 부모 클래스의 모든 기능을 가집니다.
오버라이딩을 통해서, 업캐스팅을 통해서 모든 기능을 다 구현할 수 있죠.
그런데 여기서 생성자를 생각해 봅시다.
부모도 생성자가 있겠고, 아들도 생성자가 있겠죠. 표현하지 않더라도 디폴트 생성자라는 것을 무조건적으로 가지니까요.
상속시 매소드처럼 오버라이드되어 부모 클래스의 생성자가 호출되지 않는다면 어떨까요?
그렇다면 부모 클래스의 초기화 작업을 아들 클래스에서 모두 해줘야겠죠.
이런 불편함이 있어서 super가 존재합니다.
하지만 super의 명확한 의미는 이게 다가 아닙니다.
상속시 생성자의 순서를 살펴보죠.
일단 아버지 의 디폴트(매개변수가 없는 생성자)가 생성되고
그다음 아들의 생성자가 생성됩니다.
아버의 생성자는 무조건적으로 생성되지만, 오직 디폴트 생성자만 가능합니다.
만약 매개 변수를 가지고 아버지 생성자에 접근하려 해도, 디폴트 생성자 밖에
나오지 않게 됩니다.
이것은 c++ 의 개념과 똑 같습니다. 하지만 자바는 이 문제를 super를 통해 해결합니다.
인자가 붙은 부모 클래스의 생성자에 나는 접근하고 싶다.라고 할때
super();를 사용하는겁니다.
즉 인자가 없는 부모 클래스의 생성자에 접근할 때는 super를 사용할 필요가 없습니다
오직 인자(매개변수)가 있는 부모 클래스에 접근할 때 super를 사용하는 것이죠
이것이 주는 것은 상상합니다.
방금 생성자 생성 순서를 알아봤죠?
생성자는 초기화의 기능을 합니다.
그리고 무조건 적으로 아버지 클래스 부터 생성하고 그다음 아들 클래스를 생성합니다.
왜 super()를 가장 처음에 쓰는지 이것으로 이해됐겠죠?
일단 아버지 클래스가 초기화 되야, 상속받을 수 있고, 그래야 아들 클래스도 구현할 수 있죠.
그렇다면 우리는 상속 받은 상태에서 아버지 클래스의 초기값을 마음대로 바꾸어 프로그램
전반을 제어하는 기능을 가질 수 있는 겁니다.
이 의미까지 이해했다면 상속에 대해 거의 다 이해한 겁니다.
초기값을 바꾼다는 것은 프로그램 자체를 변화시키를 수 있는 거죠.
그것이 바로 자바가 가지는 가장 큰 장점입니다 상속을 통해 아버지 클래스의 내용을
변화 시킬 수 있다는 거죠.
사실 이것 외에도 여러가지가 더 있습니다. 하지만 이정도만 알아도 어지간한 개념은 다 이해되어
넘어갈 것같습니다. 기회가 되면 다음에는 자료구조와 디자인 패턴에 대한 이야기를 더 해보죠.
그럼 공부 열심히 하세요.
Introduction to Object Serialization(객체직렬화를 통한 파일 저장 및 DB저장 튜터리얼) (1) | 2010.11.16 |
---|---|
자바 프리드로우 버전이다. (0) | 2010.11.15 |
내부클래스 설명 (1) | 2010.11.10 |
XML DOM 자바로 이해하기 (0) | 2010.11.07 |
객체직렬화 설명 (0) | 2010.10.29 |