2010/05/21

Android 2.2 Froyo SDK 설치하기

Android Froyo 와 SDK 공개 소식을 듣고 업데이트가 가능한지 SDK 설치 폴더의 setup 을 실행시켜 보았더니 업데이트 항목에 2.2 버전에 대한 업데이트 항목이 추가 되었다.





업그레이드 도중 tools 설치가 실패하는 경우가 있는데 이는 eclipse 를 실행한 상태에서 업데이트를 실행하여서 그런다. 시스템 부팅후 java및 android 개발 관련 툴을 실행하지 않고 업데이트를 수행하면 잘 된다.


SDK 업데이트를 마치고 eclipse 를 실행하여 업데이트 항목이 있나 확인해 보니 개발 툴 관련 업데이트가 있어 이 또한 설치를 해 주었다.



이제 Froyo 개발 준비가 된 것이다.<

Original Post : http://neodreamer-dev.tistory.com/444

Android NDK, Revision 4 공개

Android Froyo 공개와 발 맞추어 NDK 도 업그레이드 된 버전이 공개가 되었다. Froyo API 를 지원 한다.



Android NDK, Revision 4 (May 2010)


  • Provides a simplified build system through the new ndk-build build command.

  • Adds support for easy native debugging of generated machine code on production devices through the new ndk-gdb command.

  • Adds a new Android-specific ABI for ARM-based CPU architectures, armeabi-v7a. The new ABI extends the existing armeabi ABI to include these CPU instruction set extensions:


    • Thumb-2 instructions

    • VFP hardware FPU instructions (VFPv3-D16)

    • Optional support for ARM Advanced SIMD (NEON) GCC intrinsics and VFPv3-D32. Supported by devices such as Verizon Droid by Motorola, Google Nexus One, and others.




  • Adds a new cpufeatures static library (with sources) that lets your app detect the host device's CPU features at runtime. Specifically, applications can check for ARMv7-A support, as well as VFPv3-D32 and NEON support, then provide separate code paths as needed.

  • Adds a sample application, hello-neon, that illustrates how to use the cpufeatures library to check CPU features and then provide an optimized code path using NEON instrinsics, if supported by the CPU.

  • Lets you generate machine code for either or both of the instruction sets supported by the NDK. For example, you can build for both ARMv5 and ARMv7-A architectures at the same time and have everything stored to your application's final .apk.

  • To ensure that your applications are available to users only if their devices are capable of running them, Android Market now filters applications based on the instruction set information included in your application — no action is needed on your part to enable the filtering. Additionally, the Android system itself also checks your application at install time and allows the installation to continue only if the application provides a library that is compiled for the device's CPU architecture.

  • Adds support for Android 2.2, including a new stable API for accessing the pixel buffers of Bitmap objects from native code.



<

Original Post : http://neodreamer-dev.tistory.com/443

Android 2.2 Froyo SDK 공개!!





드디어 Android Froyo 가 공개가 되었다. Froyo 공개 후 SDK 도 공개가 되었다.



Froyo(2.2) 가 기존 2.1 버전에 비해 성능이 20~30배 정도 좋아졌다는데 성능 이외에도 많은 것이 기대 되는 버전이다.



Android 2.2 Platform

Android 2.2 Platform Highlights<

Original Post : http://neodreamer-dev.tistory.com/442

2010/05/18

SHBrowseForFolder 폴더 선택시 대화상자에 선택된 경로 보여주기

SHBrowseForFolder 이용한 폴더 선택시 대화상자에서 사용자가 폴더를 선택할 때마다 선택된 경로를 대화 상자에 뿌려 주기 위해서는 SHBrowseForFolder 함수를 호출 할 때 넘겨주는 BrowseInfo 객체의 Flag 에 BIF_STATUSTEXT 를 추가하고 Callback 함수를 지정하여 Callback 함수에서 BFFM_SELCHANGED 메세지를 받아서 처리하면 된다.



static int CALLBACK BrowseCallbackProc(
HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData)
{
switch (uMsg)
{
// 폴더 선택 대화 상자 초기화 할 때, 초기 경로 설정
case BFFM_INITIALIZED:
{
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)lpData );
}
break;

// 사용자가 폴더를 선택할 경우 대화상자에 선택된 경로 표시
case BFFM_SELCHANGED:
{
TCHAR szText[MAX_PATH] = {0};
SHGetPathFromIDList( (LPCITEMIDLIST)lParam, szText );
SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)szText );
}
break;
}

return 0; // Always return 0.
}


void CDlgCopy::OnBnClicked()
{
LPITEMIDLIST pidlBrowse;
WCHAR szFolder[MAX_PATH];

CString strInit = "초기 경로";
BROWSEINFO brInfo;

::ZeroMemory(&brInfo, sizeof(BROWSEINFO));
brInfo.hwndOwner = m_hWnd;
brInfo.pidlRoot = NULL;
brInfo.pszDisplayName = szFolder;
brInfo.lpszTitle = _T("Copy Folder");
brInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_VALIDATE;
brInfo.lpfn = BrowseCallbackProc;
brInfo.lParam = (LPARAM)strInit.GetBuffer( strInit.GetLength() );

::ZeroMemory(szFolder, MAX_PATH);

pidlBrowse = ::SHBrowseForFolder(&brInfo);
strInit.ReleaseBuffer();

if ( pidlBrowse != NULL )
{
::SHGetPathFromIDList(pidlBrowse, szFolder);
}
else
{
return;
}

m_strSaveFolder = szFolder;
}




<

Original Post : http://neodreamer-dev.tistory.com/441

SHBrowseForFolder 초기 경로 설정하기

SHBrowseForFolder 이용하여 폴더 선택 대화상자를 사용할 때, 초기 기본 경로를 설정하는 방법은 Callback 함수를 지정하여 대화상자가 초기화 메세지를 받아 기본 경로를 설정하는 메세지를 보내면 된다.


SHBrowseForFolder 함수를 호출 할 때, BrowseInfo 객체의 lpfn 에 Callback 함수를Flag 에 BIF_VALIDATE 를 지정해야 하고 lParam 에 기본 경로를 설정한다. lParam 에 설정하지 않고 직접 메세지 전달시 지정할 수도 있다.



static int CALLBACK BrowseCallbackProc(
HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData)
{
switch (uMsg)
{
case BFFM_INITIALIZED:
{
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)lpData );
}
break;
}

return 0; // Always return 0.
}


void CDlgCopy::OnBnClicked()
{
LPITEMIDLIST pidlBrowse;
WCHAR szFolder[MAX_PATH];

CString strPath = "초기 경로";
BROWSEINFO brInfo;

::ZeroMemory(&brInfo, sizeof(BROWSEINFO));
brInfo.hwndOwner = m_hWnd;
brInfo.pidlRoot = NULL;
brInfo.pszDisplayName = szFolder;
brInfo.lpszTitle = _T("Copy Folder");
brInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_VALIDATE;
brInfo.lpfn = BrowseCallbackProc;
brInfo.lParam = (LPARAM)strPath.GetBuffer( strPath.GetLength() );

::ZeroMemory(szFolder, MAX_PATH);

pidlBrowse = ::SHBrowseForFolder(&brInfo);
strPath.ReleaseBuffer();

if ( pidlBrowse != NULL )
{
::SHGetPathFromIDList(pidlBrowse, szFolder);
}
else
{
return;
}

m_strSaveFolder = szFolder;
}

<

Original Post : http://neodreamer-dev.tistory.com/440

SQLite 에서 Transaction 사용하기

간단한 데이터 파일을 SQLite 를 이용하여 저장하도록 프로그램을 작성하였는데 엄청난 성능을 보여 주었다. 간단한 저장을 수초에 걸려 처리를 하는 것이였다.



인터넷으로 관련 자료를 찾아보니 Insert 처리 속도가 Transaction 의 사용 여부에 따라 엄청난 결과를 가져왔다.

참고 : Database Speed Comparison



SQLite 의 Transaction Begin stmt 와 Commit stmt 그리고 Rollback stmt 로 구성되는데 구문은 아래와 같다.



begin-stmt:





commit-stmt:





rollback-stmt:





참고 : SQL As Understood By SQLite



C/C++ API 를 이용하는 코드에서는 아래와 같은 명령을 실행 하면 된다.

sqlite3_exec( db, "BEGIN", NULL, NULL, NULL );
:
:
sqlite3_exec( db, "COMMIT", NULL, NULL, NULL );





<

Original Post : http://neodreamer-dev.tistory.com/439

2010/05/13

[Android Dev.] Tab 선택 이벤트 잡기

TabActivity 를 이용한 Tab UI 구현할 때, 사용자가 Tab 을 바꿀 경우 TabHost 의 setOnTabChangedListener 함수를 이용하여 Tab 이 바뀌는 순간을 알아 차릴 수 있다.













package com.neodreamer.MyTab;

import android.app.TabActivity;
import android.os.Bundle;
import android.widget.TabHost;
import android.widget.Toast;
import android.widget.TabHost.OnTabChangeListener;

public class MyTab extends TabActivity implements OnTabChangeListener
{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TabHost tabHost = getTabHost();

TabHost.TabSpec spec;

// 첫 번째 탭
spec = tabHost.newTabSpec( "Tab 01" );
spec.setIndicator( "Tab 01",
getResources().getDrawable( R.drawable.icon ) );
spec.setContent( R.id.TabView1 );
tabHost.addTab( spec );

// 두 번째 탭
spec = tabHost.newTabSpec( "Tab 02" );
spec.setIndicator( "Tab 02" );
spec.setContent( R.id.TabView2 );
tabHost.addTab( spec );

// 세 번째 탭
spec = tabHost.newTabSpec( "Tab 03" );
spec.setIndicator( "Tab 03" );
spec.setContent( R.id.TabView3 );
tabHost.addTab( spec );

tabHost.setCurrentTab( 0 );

// Tab Change 이벤트 리스너 등록
tabHost.setOnTabChangedListener( this );
}

@Override
public void onTabChanged(String tabId)
{
String strMsg;
strMsg = "onTabChanged : " + tabId;
Toast.makeText( this, strMsg, Toast.LENGTH_SHORT ).show();
}
}

<

Original Post : http://neodreamer-dev.tistory.com/438

2010/05/12

[Android Dev.] SQLite Database 사용하기

Android 에서 SQLite Database 를 사용하려면 SQLiteOpenHelper, SQLiteDatabase 클래스를 상속받아 구현을 해야 히지만 복잡하고 귀찮아서 그냥 Activity 안에 구현해 보았다.





Activity 의 Layout 은 버튼하나와 수행 결과를 보여 줄 TextView 만 배치하였다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/BtnTestSQLite"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="Test SQLite"
/>
<TextView
android:id="@+id/TxtResult"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>



버튼이 눌려지만 Database 를 테스트하는 명령이 수행되고 그 결과를 TextView에 출력 하였다.

@Override
public void onClick(View v)
{
// 데이터베이스 열기 (생성)
m_db = openOrCreateDatabase( "test.db", Context.MODE_PRIVATE, null );

if ( !m_db.isOpen() )
{
Log.e( "SQLite", "openOrCreateDatabase ... Fail" );
return;
}

Log.i( "SQLite", "openOrCreateDatabase ... OK" );

String strSQL;

try
{
// member 테이블이 존재하면 삭제
strSQL = "DROP TABLE IF EXISTS member;";
m_db.execSQL( strSQL );

// member 테이블이 존재하지 않으면 생성
strSQL =
"CREATE TABLE IF NOT EXISTS member " +
"(" +
" c_name TEXT" +
" , c_alias TEXT" +
" , c_age INTEGER" +
");";
m_db.execSQL( strSQL );
Log.i( "SQLite", "Create Table ... OK" );

// Insert 구문을 이용한 Row 삽입
for ( int i = 0; i < 3; ++i )
{
strSQL =
"INSERT INTO member ( c_name, c_alias, c_age )" +
" VALUES ( 'code_" + Integer.toString( i ) + "'," +
" 'test', " + Integer.toString( i + 2 ) +
" );";
m_db.execSQL( strSQL );

Log.i( "SQLite", "Insert data " + i + " ... OK" );
}

// ContentValues 를 이용한 데이터 삽입
ContentValues cvInsert = new ContentValues();
cvInsert.put( "c_name", "neo" );
cvInsert.put( "c_alias", "dreamer" );
cvInsert.put( "c_age", "20" );
m_db.insert( "member", null, cvInsert );

cvInsert.put( "c_name", "neo2" );
cvInsert.put( "c_alias", "dreamer2" );
cvInsert.put( "c_age", "40" );
m_db.insert( "member", null, cvInsert );

// rawQuery 함수를 이용한 데이터 질의
m_cursor = m_db.rawQuery( "SELECT * FROM member", null );
if ( m_cursor != null )
{
if ( m_cursor.moveToFirst() )
{
String strRow = "--------------------------------\n";
for(int i = 0 ; i < m_cursor.getColumnCount() ; i++ )
{
strRow += m_cursor.getColumnName(i) + " | ";
}
strRow += "\n";
txtResult.setText( strRow );

do
{
strRow = "";
for(int i = 0 ; i < m_cursor.getColumnCount() ; i++ )
{
strRow += m_cursor.getString(i) + " | ";
}
strRow += "\n";
txtResult.setText( txtResult.getText() + strRow );
} while ( m_cursor.moveToNext() );
}
}
m_cursor.close(); // 커서 닫기

// rawQuery 함수에 parameter 를 이용한 데이터 질의
String strParam[] = { "neo" };
m_cursor = m_db.rawQuery( "SELECT * FROM member WHERE c_name = ?", strParam );
if ( m_cursor != null )
{
if ( m_cursor.moveToFirst() )
{
String strRow = "--------------------------------\n";
for(int i = 0 ; i < m_cursor.getColumnCount() ; i++ )
{
strRow += m_cursor.getColumnName(i) + " | ";
}
strRow += "\n";
txtResult.setText( txtResult.getText() + strRow );

do
{
strRow = "";
for(int i = 0 ; i < m_cursor.getColumnCount() ; i++ )
{
strRow += m_cursor.getString(i) + " | ";
}
strRow += "\n";
txtResult.setText( txtResult.getText() + strRow );
} while ( m_cursor.moveToNext() );
}
}
m_cursor.close(); // 커서 닫기

// query 함수를 이용할 데이터 질의
String strColumn[] = { "c_name", "c_age" };
String strSelection = "c_name like 'neo%'";
m_cursor = m_db.query( "member", strColumn, strSelection,
null, null, null, null );
if ( m_cursor != null )
{
if ( m_cursor.moveToFirst() )
{
String strRow = "--------------------------------\n";
for(int i = 0 ; i < m_cursor.getColumnCount() ; i++ )
{
strRow += m_cursor.getColumnName(i) + " | ";
}
strRow += "\n";
txtResult.setText( txtResult.getText() + strRow );

do
{
strRow = "";
for(int i = 0 ; i < m_cursor.getColumnCount() ; i++ )
{
strRow += m_cursor.getString(i) + " | ";
}
strRow += "\n";
txtResult.setText( txtResult.getText() + strRow );
} while ( m_cursor.moveToNext() );
}
}
m_cursor.close(); // 커서 닫기
}
catch ( SQLException e )
{
// SQL 예외 처리
Log.e( "SQLException", e.getMessage() );
}
finally
{
// Database 닫기
m_db.close();
Log.i( "SQLite", "Database Close ... OK" );
}
}


Log를 통해 진행 상황을 모니터링 한 내용이다.

05-12 23:26:20.110: INFO/SQLite(213): openOrCreateDatabase ... OK
05-12 23:26:20.190: INFO/SQLite(213): Create Table ... OK
05-12 23:26:20.200: INFO/SQLite(213): Insert data 0 ... OK
05-12 23:26:20.210: INFO/SQLite(213): Insert data 1 ... OK
05-12 23:26:20.240: INFO/SQLite(213): Insert data 2 ... OK
05-12 23:26:20.400: INFO/SQLite(213): Database Close ... OK

<

Original Post : http://neodreamer-dev.tistory.com/437

2010/05/11

[Android Dev.] 편리한 Eclipse 의 Intellisense(?)

이전 글 에서는 Import 되지 않는 Widget 을 쉽게 Import 해 주는 Elipse 의 기능에 대해 다루었는데 다른 기능이 있는지 확인해 보았다.



일단 Eclpise 에서 구문 오류가 발생하면 오류 발생 위치에 빨간 물결 무늬의 밑줄을 출력해 주는데 이 오류 부분에 마우스 커서를 올려 놓으면 이 문제를 해결 하기 위한 팝업 윈도우를 보여준다.



아래는 Activity 의 속한 버튼에 대하여 setOnClickListener 함수를 호출하였을때 발생하는 오류에 대한 팝업 윈도우 이다.





팝업 윈도우에서 맨 마지막에 있는 Let '...' implements 'OnClickListener' 메뉴를 선택하게 되면 해당 class 선언문에 implements OnClickListener 구문이 추가 된다. class 에 implements 구문이 추가되면 abstract method(onClick) 개 구현되지 않았아서 클래스 이름에 에러 표시가 되는데 클래스 이름에 마우서 커서를 올려 놓으면 팝업 윈도우가 활성화 되고 method 를 추가 할 수 있는 메뉴가 나온다.





이 윈도우에서 "Add unimplemented methods" 메뉴를 선택하면 아래와 같은 코드가 class 에 추가 된다.

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
<

Original Post : http://neodreamer-dev.tistory.com/436

[Android Dev.] Import 를 편하게 해주는 Eclipse 기능

안드로이드를 공부하면서 코딩을 하다보면 위젯을 많이 사용하게 된다. 위젯을 처음 사용할때마다 import 를 시켜주어야 하는데 처음에는 일일이 입력을 했었는데 Eclipse 에서 Import 를 아주 편하게 해 주는 기능을 찾았다.


Import 되지 않은 위젯을 사용하게 되면 아래 이미지에서 처럼 ...cannot be resolved to a type 이라는 에러가 발생한다.



이 때, Import 되지 않아 에러가 발생한 위젯에 마우스 커서를 올려 놓으면 잠시후에 아래 이미지와 같은 팝업 윈도우가 나타난다.



이 팝업 윈도우에서 맨 위에 있는 Import... 메뉴를 선택하면 해당 위젯이 Import 가 되고 에러가 사라진다.<

Original Post : http://neodreamer-dev.tistory.com/435

2010/05/08

Hello bada


호기심을 참지 못하고 삼성의 bada 개발툴을 설치해 보았다.



bada 의 개발 환경은 잘 알려진 Eclipse IDE 기반이다.



설치 과정이 복잡하지는 않지만 Eclipse 기반이면 기존의 Eclipse 와 통합을 할 수 있었으면 좋았을텐데 하는 아쉬움이 있다.


일단 설치를 했으니 Hello bada 라는 프로젝트를 생성해 보았다. 여러단계를 거쳐서 프로젝트를 생성해 보았다.













생성 된 프로젝트를 실행하여 보니 에뮬레이터 설정단계를 거쳐 실행이 되었다.













간단한게 살펴 보았는데 코드는 C++ 이지만 많이 생소해 보였다. 레퍼런스를 봐야 좀 알겠지만 자료는 많아 보이지 않았다. UI 편집은 일단 Android 보다는 편하고 쉬워 보였다.




<

Original Post : http://neodreamer-dev.tistory.com/434

드디어 삼성의 bada SDK가 일반 공개가 되었다.

삼성의 bada [출처 : 바다 개발지 페이지]

삼성의 bada [출처 : 바다 개발지 페이지]






말도 많고 탈도 많았던 삼성의 mobile 플랫폼인 bada 의 SDK가 공개가 되었다.

이제 일반에게 공개할 만한 수준이 되었다고 판단한 것인지 더 이상 버틸 수 없어서인지는 모르겠다.


일반 공개 소식을 듣고 일단 다운로드를 받아 보았다. 전체 SDK 크기가 600MB 정도 된다. Android 와 크게 차이가 없어 본다. Android 도 eclipse 와 SDK를 합치면 1GB에 달하며 NDK와 cygwin 을 더하면 1.5GB에 달한다.

하지만 그래도 설치가 꺼려진다.


한번을 실체를 보기 위해 설치를 해 보겠지만 bada 플랫폼에서의 개발은 솔직히 꺼려진다. 이번에 공개된 SDK도 S/W 라고 볼 수 있는데 Samsung 의 S/W 는 정말 꽝이라는 말을 많이 들어와서... 이번에 이렇게 공개를 끝나는 것이 아닐지 걱정스럽다. 얼마전 삼성의 갤럭시 S 개발자라는 사람의 블로그를 보았는데 삼성의 수준을 가늠할 수 있어서 더욱 걱정스럽다.


삼성을 그리 좋아라 하지 않지만 모바일 플랫폼에서 우리 고유(?)의 플랫폼이 힘을 내 주었으면 하는 마음이 있다. (그럴려면 삼성의 S/W에 대한 마인드가 바뀌어야 할텐데... 쉽지 않아 보이는데...)


bada 개발자 페이지

bada Release Note<

Original Post : http://neodreamer-dev.tistory.com/433

2010/05/06

[Android Dev.] Canvas.drawPosText 의 불편한 진실

이전 Android 에서의 2D Drawing 에 관련한 글을 올렸었는데 그 글에서 drawPosText 함수의 에러에 대한 글을 썼었다.



2010/05/06 - [Dev Story/Android] - [Android Dev.] 2D Drawing



다른 텍스트 출력은 문제가 없는데 drawPosText 함수만 호출하면 문제가 발생하여 그에 대한 원인을 찾느라 시간을 좀 허비했다. API 문서를 좀더 자세히 읽어 봤더라면 시간을 절약할 수 있었을 것이다.



이 함수는 아래와 같은 형태를 갖고 있다.



public void  drawPosText(String  text, float[] pos, Paint  paint)




Parameters

text     The text to be drawn

pos     Array of [x,y] positions, used to position each character

paint     The paint used for the text (e.g. color, size, style)




함수만 보고 이 함수의 기능이 주어진 문자를 주어진 좌표 배열에 출력을 해주는 것이라고 생각하고 아래와 같은 코드로 테스트를 해 보았다.

myPaint.setARGB( 0, 255, 0, 255 );
float[] pos = new float[] {
60, 50,
60, 420
};
canvas.drawPosText( "Android drawPosText", pos, myPaint );





이 코드가 60,50 과 60,420 좌표에 "Android drawPosText"를 그려 줄 것이라고 예상을 했지만 예상을 깨고 에러 화면만을 보여 주었다.

잘못 된 drawPosText 함수 사용에 따른 에러화면

잘못 된 drawPosText 함수 사용에 따른 에러화면



어렵게 예제 자료를 찾아서 확인해 보았더니 drawPosText 함수의 기능을 주어진 문자열의 각 문자를 좌표 배열의 각각의 위치에 출력하는 기능이였다. API 문서에도 아래와 같이 설명이 되어 있었다.


Draw the text in the array, with each character's origin specified by the pos array.




그래서 아래와 같은 코드로 테스트를 해 보았더니 에러 없이 문자를 출력해 주었다. (보라색 비뚤비뚤 Android 글자)

myPaint.setARGB( 128, 255, 0, 255 );
float[] pos = new float[] {
160, 20,
170, 25,
180, 20,
190, 25,
200, 20,
210, 25,
220, 20
};
canvas.drawPosText( "Android", pos, myPaint );



Canvas의 drawPosText 를 이용한 문자 출력

Canvas의 drawPosText 를 이용한 문자 출력





아래 코드를 수행하여 보면 보다 명확하게 알 수 있다.

Paint paint = new Paint();
paint.setColor( Color.GREEN );
float[] pos = new float[] { 60, 50, 60, 420 };
String strMsg = "AB";
canvas.drawPosText( strMsg.toCharArray(), 0, strMsg.length(), pos, paint );



Canvas의 drawPosText 를 이용한 문자 출력

Canvas의 drawPosText 를 이용한 문자 출력



<

Original Post : http://neodreamer-dev.tistory.com/432

[Android Dev.] 2D Drawing

Android 의 2D 드로잉은 Canvas 객체를 이용하여 하는데 Canvas 의 사용에 있어 중요한 객체가 Paint 객체인데 이 Paint 객체에는 드로잉에 필요한 각종 설정을 담고 있다.



위의 이미지를 출력하기위한 코드이다.

public class My2D extends Activity 
{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE); // 타이틀바 제거
setContentView( new My2DView( this ) );
//setContentView(R.layout.main);
}

class My2DView extends View
{
public My2DView(Context context)
{
super(context);
}

@Override
public void onDraw(Canvas canvas)
{
// Canvas 배경 설정
canvas.drawColor(Color.parseColor("#030303"));

// Bitmap 을 Canvas 에 뿌리기
Bitmap bm = BitmapFactory.decodeResource(
getResources(), R.drawable.icon);
canvas.drawBitmap(bm, 100, 100, null);

// 드로잉을 위핸 Paint 객체 생성
Paint myPaint = new Paint();
myPaint.setAntiAlias( true ); // AntiAliasing 설정

// 선 그리기
myPaint.setColor( Color.parseColor("#FF0000") ); // 선 색상 설정
canvas.drawLine( 10, 10, 100, 100, myPaint );

// 도형 그리기
myPaint.setARGB( 128, 0, 255, 255 ); // 색상 설정
canvas.drawCircle( 200, 300, 100, myPaint ); // 원 그리기
myPaint.setARGB( 128, 255, 0, 255 ); // 색상 설정
canvas.drawCircle( 100, 250, 100, myPaint ); // 원 그리기

myPaint.setARGB( 128, 255, 0, 0 ); // 색상 설정
// 10x10 크기의 모서리가 둥근 사각형 그리기
canvas.drawRoundRect(
new RectF(150.0f, 60.0f, 300.0f, 160.0f),
10.0f, 10.0f, myPaint );

Path myPath = new Path(); // 패스 생성
// 패스에 시계방향 원형 추가
myPath.addCircle( 200, 300, 100, Direction.CW );
myPaint.setARGB( 128, 0, 255, 255 ); // 색상 설정
myPaint.setTextSize( 15 ); // 폰트 크기 설정
// 패스를 따라 5pixel 떨어진 글자 출력
canvas.drawTextOnPath(
"Hello Android 2D",
myPath, 0.0f, -5.0f, myPaint );

// 문자 출력
myPaint.setColor( Color.CYAN ); // 색상 설정
canvas.drawText( "Android drawText", 20, 20, myPaint );

myPaint.setARGB( 0, 255, 0, 255 );
float[] pos = new float[] { 60, 50, 60, 420 };
//canvas.drawPosText( "Android drawPosText", pos, myPaint );
}
}
}





다른 것은 어렵지 않게 되는데 맨 마지막 시도한 drawPosText 는 잘 되지 않는데 이 함수만 실행하면 프로그램이 다운되어 버린다. 관련 자료를 찾고 있는데 쉽지가 않다.<

Original Post : http://neodreamer-dev.tistory.com/431

2010/05/02

[Android Dev.] Thread 사용하기

Android 의 Thread 를 사용해 보았다. 이미 올린 글에서 Progress Dialog 를 제어 하면서도 사용을 해 보았지만 개념이 쉽게 와 닿지 않아 다시 공부해 보았다.



Android 의 Thread 는 Thread 껍데기를 담당하는 Thread 객체와 실제 실행되는 부분을 담당하는 Runnable 객체로 구성이 되는데 Android 에서는 Multi Thread 를 지원을 하지만 여러 Thread 에서 메인 UI를 제어하지 않고 하나의 Thread 혹은 Handler 라는 객체에서 담당을 하도록 한다.



Thead의 생성의 Thread 객체를 생성할 때, Runnable 객체를 넘겨주어 생성을 한다.


public  Thread  (Runnable  runnable)



Thread 의 테스트를 위해 아래와 같은 메인 UI를 작성하였다.



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<ProgressBar
android:id="@+id/ProgressBar01"
style="?android:attr/progressBarStyleHorizontal"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
<Button
android:text="@+id/Button01"
android:id="@+id/Button01"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
</LinearLayout>



Progress bar 와 쓰레드 동작시킬 버튼이다.



아래 소스는 아주 간단하게 버튼이 눌렸을 때 Thread를 생성하여 Progress bar를 진행시키는 코드 이다.

ProgressBar progress01;
Button btn01;

progress01 = (ProgressBar)findViewById( R.id.ProgressBar01 );
btn01 = (Button)findViewById( R.id.Button01 );
btn01.setOnClickListener( this );

public void onClick( View v )
{
if ( v == btn01 )
{
new Thread(new Runnable()
{
public void run()
{
progress01.setProgress( 0 );
progress01.setMax( 100 );

for ( int i = 0; i <= 100; ++i )
{
progress01.setProgress( i );

SystemClock.sleep(100);
}

//Toast.makeText(MyThread.this, "Thread Finished", Toast.LENGTH_SHORT).show();
}
}
).start();
}
}





위의 소스 중에 Toast 메세지를 출력하는 부분이 있는데 이 부분의 주석을 해제하고 실행하면 아래와 같은 예외가 발생을 하는데 왜 그런지는 모르겠고 Backgroud Thread 에서 메인 UI를 제어하려고 시도해서 인 것을 생각이 된다.



Handler 를 이용한 Message Queueing 방식

다음은 Handler 를 이용한 message queueing 방식을 이용하는 Thread 를 생성하여 보았다. Thread 에서 Handler 에게 message 를 보내어 Handler 가 받은 message 를 처리하며 UI 작업을 하도록 하는 것이다.

Handler myHandler = new Handler()
{
public void handleMessage( Message msg )
{
if ( msg.what == -1 )
{
progress01.setProgress( 0 );
progress01.setMax( 100 );
}
else if ( msg.what == -100 )
{
Log.i( "INFO", "Finish Thread!!!" );
Toast.makeText(MyThread.this, "Finish Thread!!!", Toast.LENGTH_SHORT).show();
}
else
{
progress01.setProgress( msg.what );
}
}
};

public void onClick( View v )
{
if ( v == btn01 )
{
new Thread(new Runnable()
{
public void run()
{
Message msg = myHandler.obtainMessage();
msg.what = -1;
myHandler.sendMessage( msg );

for ( int i = 0; i <= 100; ++i )
{
msg = myHandler.obtainMessage();
msg.what = i;
myHandler.sendMessage( msg );

SystemClock.sleep(100);
}

msg = myHandler.obtainMessage();
msg.what = -100;
myHandler.sendMessage( msg );
}
}
).start();
}
}





Handler 에게로 Message를 보내기 위해서는 Handler 에서 obtainMessage 함수를 이용해 Message 객체를 받아와서 필요한 정보를 담아 다시 Handler 에게 보내는 것이다. Handler 는 받은 메세지를 순서대로 handleMessage 함수를 통해 처리한다.



이 때, Handler 에게 Message 를 보내는 방법이 여러가지가 있다.


  • final boolean sendMessage(Message  msg)

    Message 를 Handler 의 Message Queue 에 맨 마지막에 추가한다.




  • final boolean sendMessageAtFrontOfQueue(Message msg)

    Message 를 Handler 의 Message Queue 의 맨 앞에 추가한다.




  • boolean sendMessageAtTime(Message msg, long uptimeMillis)

    Message 를 특정 시각(장비 가동후 지난 시간)이 되면 Queue에 추가한다.




  • final boolean sendMessageDelayed(Message msg, long delayMillis)

    Message 를 특정 시간이 지난 후에 Queue에 추가한다.





Foreground Thread에 post 메세지에 의한 방법

또 다른 방법은 UI 핸들링을 하는 Foreground Thread 를 두고 Background Thread 에서 post 함수를 이용하여 처리하는 방식이다.

Handler myHandler = new Handler();

private Runnable fgTask = new Runnable()
{
@Override
public void run()
{
int nCur = progress01.getProgress();

if ( nCur != progress01.getMax() )
{
nCur++;
progress01.setProgress( nCur );
}
else
{
Toast.makeText( MyThread.this, "Finish Thread", Toast.LENGTH_SHORT).show();
}
}
};

public void onClick( View v )
{
if ( v == btn01 )
{
new Thread(new Runnable()
{
public void run()
{
for ( int i = 0; i <= 100; ++i )
{
myHandler.post( fgTask );

SystemClock.sleep(100);
}
}
}
).start();
}
}





위의 두가지 Thread 운영 방식은 유사하게 동작을 하며 버튼이 눌려지면 Thread 를 동작하여 Progress 바의 진행을 시킨다.

동작중인 Progress

동작중인 Progress

동작을 마친 Progress

동작을 마친 Progress



<

Original Post : http://neodreamer-dev.tistory.com/430