
'안드로이드' 카테고리의 다른 글
Fingerprint 추적 방법 (0) | 2011.01.27 |
---|---|
디바이스의 고유 정보값 얻어오기 (0) | 2011.01.27 |
웹킷 브라우저 (0) | 2011.01.27 |
안드로이드 2.0 Service API 변화 (0) | 2011.01.27 |
Handler (0) | 2011.01.27 |
Fingerprint 추적 방법 (0) | 2011.01.27 |
---|---|
디바이스의 고유 정보값 얻어오기 (0) | 2011.01.27 |
웹킷 브라우저 (0) | 2011.01.27 |
안드로이드 2.0 Service API 변화 (0) | 2011.01.27 |
Handler (0) | 2011.01.27 |
안드로이드에 내장된 브라우저는 구조가 굉장히 복잡하기 때문에 android.webkit이라는 독립적인 패키지에 담겨있다. 그리고 WebView라는 위젯을 사용해 간단하게 화면에 HTML을 표시하는 정도로 활용할 수 있고, 아니면 들어 있는 기능을 총동원해 강력한 브라우징 기능을 구현할 수도 있다.
1. 단순한 브라우저
간단한 기능만 사용한다면 WebView 역시 안드로이드의 다른 위젯과 별반 다를게 없다. 그저 레이아웃에 추가하고, 자바 코드에서 URL만 지정해주면 해당하는 URL의 내용을 바로 표시해준다.
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_width="fill_parent"
android:layout_height="fill_parent"
>
<WebView android:id="@+id/webkit"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
자바코드 예제
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;public class BrowserDemo1 extends Activity {
WebView browser;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
browser=(WebView)findViewById(R.id.webkit);
browser.loadUrl("http://commonsware.com");
}
}
인터넷을 사용할 수 있는 권한을 확보하도록 AndroidMainifest.xml 파일을 수정해야 한다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.webkit">
<uses-permission android:name="android.permission.INTERNET" />
<application>
<activity android:name=".BrowserDemo1" android:label="BrowserDemo1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
- WebView 위젯에서는 자바스크립트가 기본적으로 꺼져 있다. 자바스크립트를 동작하도록 활성화하려면 WebView 인스턴스의 getSettings().setJavaScriptEnabled(true) 메소드를 호출하면 된다.
2. 원하는 내용 표시
WebView 안에 원하는 내용을 표시하는 방법 두가지
- loadUrl() 메소드를 사용해 WebView에 URL을 넘겨주고 해당하는 페이지의 내용을 가져와 표시하도록 하는 방법
- loadData() 메소드에는 표현하려는 HTML을 직접 전달한다.
WebView 위젯을 사용하기 좋은 경우
- 애플리케이션 패키지와 함께 설치된 메뉴얼을 화면에 표시하고자 할 때
- 다른 작업을 처리한 결과로 받아온 HTML 형태의 문자열을 표시할 때
ex) Atom 피트에서 뽑아낸 본문 내용을 화면에 표시할 때
- 안드로이드에 내장된 위젯 대신 HTML 만으로 애플리케이션 화면을 구성하려는 경우
loadData() 메소드 설명
- loadData() 메소드의 가장 간단한 형태는 본문내용, MIME 타입, 문자열 인코딩 등의 내용을 모두 문자열로 지정한다.(MIME 타입은 text/html이며, 인코딩은 UTF-8을 사용)
browser.loadData("<html><body>Hello, world!</body></html>",
"text/html", "UTF-8");
3. 브라우저 내비게이션
WebView에는 기본적으로 브라우저 내비게이션 툴바가 포함돼 있지 않다. 앞으로, 뒤로 등의 기능을 갖고 있는 내비게이션 툴바를 별로 사용할 일이 없거나 화면을 최대한 활용해야 하는 경우에는 툴바가 없는 편이 낫다. 반면 브라우저 내비게이션 기능을 사용자에게 제공하려면 내비게이션 기능과 관련된 인터페이스를 개바자가 직접 만들어야 하는 단점도 있다.
WebView 위젯에는 다음과 같은 다양한 브라우져 내비게이션 기능이 내장
- reload() 메소드 : 현재 표시하는 페이지 내용을 새로 고침
- goBack() 메소드 : 브라우저 URL 기록 가운데 바로 이전 주소로 되돌아간다.
- goForward() 메소드 : 브라우저 URL 기록 가운데 바로 다음 주소로 나아간다.
- goBackOrForward() 메소드 : 브라우저의 URL 기록을 앞뒤 원하는 방향으로 이동할 수 있다. 넘겨주는 값이 0보다 작으면 해당 숫자만큼 뒤로 돌아가고, 0보다 크면 해당 숫자만큼 앞으로 나아간다.
- canGoBackOrForward() 메소드 : 넘겨주는 숫자값만큼 브라우저 URL 기록을 따라 이동할 수 있는지 확인한다.
- clearCache() 메소드 : 브라우저가 임시로 갖고 있던 파일을 제거한다.
- clearHistory() 메소드는 브라우저 URL 기록을 제거한다.
4. WebViewClient
WebView 위젯을 사용해 애플리케이션 내부 데이터를 표현하려면, 사용자가 표시된 HTML 링크를 플릭하는 등의 상황에서 제어권을 넘겨받아야 할 필요가 있다.
- 특정 작업에 대한 제어권을 확보하려면 WebViewClient 인스턴스를 하나 생성해 WebView 위젯의 setWebViewClient() 메소드에 넘겨줘야 한다.
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;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
browser=(WebView)findViewById(R.id.webkit);
browser.setWebViewClient(new Callback());
loadTime();
}
// 현재 시각을 간단한 HTML 형태로 생성해 WebView 위젯에 적용
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);
}
}
}
5. 설정과 옵션
데스트탑에서 사용하던 웹브라우저를 보면 각종 설정과 옵션이 가득하다. 수많은 설정이나 옵션과 툴바 컨트롤 사이에서 기본 글꼴을 지정한다거나 자바스크립트의 동작 상태를 지정하는 등 다양한 기능에 대해 설정값을 지정할 수 있다.
WebSetting 클래스의 설정 메소드
- setDefaultFontSize() 메소드 : setTextSize() 메소드를 사용해 기본 글꼴 크기를 지정할 수 있다.
- setJavaScriptEnabled() 메소드 : 자바스크립트를 켜거나 끌 수 있다.
- setUserAgent() 메소드 : 0값을 넘겨주면 WebView에서 대상 웹사이트에 스스로가 휴대폰용 브라우저라는 사실을 user-agent값을 통해 알려준다.
출처 : http://nopd.textcube.com/?indexTimestamp=1264487399&c=5
디바이스의 고유 정보값 얻어오기 (0) | 2011.01.27 |
---|---|
타이틀 바 꾸미기 (0) | 2011.01.27 |
안드로이드 2.0 Service API 변화 (0) | 2011.01.27 |
Handler (0) | 2011.01.27 |
hardware keyboard의 존재 여부 및 상태 확인 (0) | 2011.01.27 |
public final void startForeground(int id, Notification notification);
public final void stopForeground(boolean removeNotification);
private static final Class[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
private static final Class[] mStopForegroundSignature = new Class[] {
boolean.class};
private NotificationManager mNM;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
try {
mStartForeground = getClass().getMethod("startForeground",
mStartForegroundSignature);
mStopForeground = getClass().getMethod("stopForeground",
mStopForegroundSignature);
} catch (NoSuchMethodException e) {
// Running on an older platform.
mStartForeground = mStopForeground = null;
}
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
try {
mStartForeground.invoke(this, mStartForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke startForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke startForeground", e);
}
return;
}
// Fall back on the old API.
setForeground(true);
mNM.notify(id, notification);
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat(int id) {
// If we have the new stopForeground API, then use it.
if (mStopForeground != null) {
mStopForegroundArgs[0] = Boolean.TRUE;
try {
mStopForeground.invoke(this, mStopForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke stopForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("MyApp", "Unable to invoke stopForeground", e);
}
return;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
mNM.cancel(id);
setForeground(false);
}
Application 이 StartService 를 호출 한다.
Service 의 onCreate, onStart() 함수가 호출 되고, 특정한 작업을 수행하기 위해 백그라운드 스레드가 생성된다.
시스템이 메모리 부족으로 인해 현재 작동중인 서비스를 강제로 종료 한다.
잠시 후에, 메모리 상황이 호전되어 Service 가 재시작한다. 이 때, Service 의 onCreate 함수는 호출 되지만, onStart 는 호출 되지 않는다. (startService 는 호출 되지 않았음으로..)
// This is the old onStart method that will be called on the pre-2.0 // platform. On 2.0 or later we override onStartCommand() so this // method will not be called. @Override public void onStart(Intent intent, int startId) { handleStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { handleStart(intent, startId); return START_NOT_STICKY; } void handleStart(Intent intent, int startId) { // do work
New "running services" user interface
* 이 후 이어지는 내용은 새롭게 추가된 Running Service 를 확인 할 수 있는 UI 어플리케이션에 관한 내용이라 대충 대충 건너 띄도록 하겠습니다.
안드로이드 2.0 부터는 새롭게 "Running Services" Activity 가 어플리케이션 세팅에 추가 되었다. 사용자는 목록에 표시된 Service 를 터치해서 종료할 수 있다.
화면 하단부에는 현재 시스템의 메모리 상황에 대한 정보가 표시된다.
타이틀 바 꾸미기 (0) | 2011.01.27 |
---|---|
웹킷 브라우저 (0) | 2011.01.27 |
Handler (0) | 2011.01.27 |
hardware keyboard의 존재 여부 및 상태 확인 (0) | 2011.01.27 |
안드로이드 개발 팁 (0) | 2011.01.27 |
Handler란?
한 스레드는 그 내부의 연산만 가능하며 다른 스레드의 UI를 건드릴 수 없습니다. 그런데 만약 스레드들이 서로 영향을 줄 수 없다면 스레드의 존재 이유가 없을 것입니다. 이를 해결하기 위해서 서로 다른 스레드 간의 참조를 위해서 스레드 간에 통신할 수 있는 장치를 만들었는데 그것이 핸들러[Handler]입니다. 핸들러는 스레드 간에 메시지 객체나 러너블 객체를 통해 통신할 수 있는 장치이며,하나 의 핸들러는 하나의 스레드와 관련을 맺습니다. 핸들러는 자신이 생성된 스레드에 짝이 되며 다른 스레드와 통신을 수행하게 됩니다. |
핸들러에 메시지가 도착하게 되면 아래의 메서드가 호출됩니다. public void handleMessage(Message msg) 인수로 메시지 객체를 전달 받는데 이는 스레드 간에 통신을 해야 할 내용에 관한 객체입 니다. 몇가지 정보가 추가 될 수 있기 때문에 여러 개의 필드를 가지고 있습니다. 메시지 필드 : 설명 1. int what : Message의 ID 2. int arg1 : Message가 보낼 수 있는 첫번째 정보. 3. int arg2 : Message가 보낼 수 있는 두번째 정보. 4 Object obj : Integer로 표현 불가능 할 경우 객체를 보냄. 5. Messenger replyTo : 응답을 받을 객체를 지정. |
Message를 전송할 때는 다음의 메서드를 사용합니다. 1. boolean Hanler.sendEmptyMessage(int what); - Message 의 ID에 해당하는 값을 전달할 때 사용함. 2. boolean Handler.sendMessage(Message msg); - ID만으로 불가능하고 좀 더 내용이 있는 정보를 전송할 때 사용. 3. boolean sendMessageAtFrontOfQueue(Message msg); - 메시지가 큐에 순서대로 쌓여서 FIFO(First In Frist Out)형태로 처리되지만, 이 메서드를 사용하면 노래방에서 우선 예약 하듯이 사용가능합니다. |
//Handler 시작 부분 public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); BackThread thread = new BackThread(); thread.setDaemon(true); thread.start(); } //Handler Class class BackThread extends Thread { @Override public void run() { // TODO Auto-generated method stub while(true) { //처리 내용 //핸들러 메세지 보내는 부분 mHandler.sendEmptyMessage(0); try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception ; } } }; //핸들러를 받아서 처리할 내용 Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { if(msg.what == 0){ //처리할 내용 } }; } } |
앞선 메서드로 특정 정보를 보낼 수 있지만 메시지를 보내는 대신에 객체를 보낼 수도 있습니다. boolean post(Runnable r) 핸들러로 다음의 매서드를 통해 Runnable 객체를 보내면 해당 객체의 run 메서드가 실행됩니다. 이럴 경우 메시지를 받는 쪽은 다른 것을 정의하지 않고 핸들러만 정의하면 해당 내용을 수행할 수 있습니다. |
class BackThread extends Thread { @Override public void run() { // TODO Auto-generated method stub while(true) { //처리 내용 mHandler.post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub // 객체를 받아서 처리할 내용 } }); try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception ; } } } //핸들러를 받아서 처리할 내용 Handler mHandler = new Handler(); } |
앞선 예제처럼 스레드가 이너 클래스로 구현되었을 경우 멤버의 공유가 가능하지만 그렇 지 않고 분리된 클래스의 경우에는 더 상세한 코드의 구현이 필요합니다. 이경우 스레드는 전달 받은 핸들러를 자신의 멤버로 따로 저장해야 하고 (공유가 불가능 하기 때문입니다.) 메시지 객체에 추가 정보를 저장해서 보내야 합니다. |
public class TestSampleProject extends Activity { int mManinValue = 0; int mBackValue = 0; TextView mMainText; TextView mBackTest; BackThread thread; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); thread = new BackThread(mHandler ); thread.setDaemon(true); thread.start(); } Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { if(msg.what == 0) { //처리할 내용 } }; }; } class BackThread extends Thread { Handler mHandler; public BackThread(Handler handler) { // TODO Auto-generated constructor stub mHandler = handler; } @Override public void run() { // TODO Auto-generated method stub Message msg = new Message(); msg.what = 0; msg.arg1 = 1; mHandler.sendMessage(msg); try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception } } } |
매번 핸들러를 구현할 때마다 new 연산자로 새로 생성한다면 메모리도 계속해서 사용할 것이고 그에 다른 가비지 컬렉션의 작업도 생길 것입니다. 이러면 속도도 느려지는 결과가 발생합니다. 이런 경우를 보완하고자 메시지 풀 이라는 임시 캐쉬를 유지시켜서 빠른 작업이 가능하게 합니다. static Message obtain(message orig) static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) - obtain 메서드는 메시지 풀에서 비슷한 메시지가 있다면 이를 재사용하게 합니다. void recycle() - 사용한 메시지를 풀에 집어 넣는 역할을 하는데 풀에 한번 집어 넣게 되면 시스템이 관리하므로 더 이상 관여할 수 없습니다. |
... class BackThread extends Thread { Handler mHandler; public BackThread(Handler handler) { // TODO Auto-generated constructor stub mHandler = handler; } @Override public void run() { // TODO Auto-generated method stub while(true) { //처리할 내용 Message msg = Message.obtain(mHandler, 0 , 1 , 0); mHandler.sendMessage(msg); try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception } } } } |
출처 : http://blog.naver.com/crowdark7?Redirect=Log&logNo=109201348
웹킷 브라우저 (0) | 2011.01.27 |
---|---|
안드로이드 2.0 Service API 변화 (0) | 2011.01.27 |
hardware keyboard의 존재 여부 및 상태 확인 (0) | 2011.01.27 |
안드로이드 개발 팁 (0) | 2011.01.27 |
activity 시작하면서 soft keyboard 자동으로 실행시키기 (0) | 2011.01.27 |
안드로이드 2.0 Service API 변화 (0) | 2011.01.27 |
---|---|
Handler (0) | 2011.01.27 |
안드로이드 개발 팁 (0) | 2011.01.27 |
activity 시작하면서 soft keyboard 자동으로 실행시키기 (0) | 2011.01.27 |
Android Emulator의 성능을 빠르게 하기 (0) | 2011.01.27 |
1. Touch mode에서도 Keypad를 쓸수 있게 하기위해
- View를 생성할때 setFocusableInTouchMode(true);를 호출 한다.
2. View mode를 Portrait 나 Landscape로 고정하고 싶을때
- Manifest의 Activity 에 android:screenOrientation="landscape"을 추가한다.
3. 슬라이드를 열고 닫을때 onDestroy()가 호출되지 않도록 하기위해
- Manifest의 Activity 에 android:configChanges="orientationkeyboardHidden" 을 추가한다.
- "orientationkeyboardHidden" 이 둘중에 하나라도 빠지면 슬라이드를 열때 onDesroy()가 호출된다.
4. Scoket 통신을 할수 있도록 하기 위해
- 퍼미션을 추가 한다. uses-permission android:name="android.permission.INTERNET"
5. 진동을 사용할수 있도록 하기 위해
- 퍼미션을 추가한다. uses-permission android:name="android.permission.VIBRATE"
6. 안테나 영역 없에기
- Manifest의 Activity 에 android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
7. Manifest의 versionName 갖고오기.
- mContext.getPackageManager().getPackageInfo("package name", 0).versionName
ex)
android:versionCode="1"
android:versionName="1.0.0">
android:configChanges="orientationkeyboardHidden"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
DigitsKeyListener digit =
new DigitsKeyListener(true, true); // first true : is signed, second one : is decimal
digit.setKeyListener( MyDigitKeyListener );
위와같이 하거나 xml 에서
android:inputType="number"
10. Android Option menu 실행 소스
openOptionsMenu();
9. Android Menu 만들기
- xml 소스
res/menu/menu.menu
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/adjust"
android:title="수정"
android:orderInCategory="1" >
</item>
<item
android:id="@+id/delete"
android:title="삭제"
android:orderInCategory="2" >
</item>
</menu>
- 자바 소스
// OptionMenu
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.del_adjust, menu);
return true;
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
3. Android toast
1-Type
Toast.makeText.(this, "토스트 메세지", Toast.LENGTH_SHORT).show();
2-Type
Context context = getApplicationContext();
String msg = "";
int duration = Toast.LENGTH_SHORT;
Toast.makeText(context, b, duration).show();
2. Android View 백그라운드 색 변경
TextView a = null;
a.setBackgroundColor(Color.WHITE);
1. Android timer 이용
http://docs.androidside.com/docs/reference/java/util/TimerTask.html
--------------------------------------------------------------------------------------------------
모토로이 볼륨 올리고 내리는 버튼 키값을 알아내서
edittext 에 원하는 값을 넣는 방법 볼룸 위아래 버튼을 누르면
화면에 벨로시 볼륨 조절하는 창이 나타나는데 완전히 키값을 가로채는 방법
et_editText.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if(event.getAction() == KeyEvent.ACTION_DOWN){
if(keyCode == 24 || keyCode == 25){
et_editText.setText("1234567890");
return true;
}
}
return false;
}
});
- 전체적으로 필요한 기능
// 종료 후 재 부팅시 기능이 죽지않고 유지되게 하는 방법들..
http://www.androidpub.com/android_dev_qna/189549
- 박사마 만드는데 필요한 기능
// view에서 페이지 넘기는 기능
API Demos에서 Animation에 간단하게 Fade in, Zoom in 효과가 있네요.
내가 만드는 부분에 필요한 부분들
// 강제로 클릭을 발생시키는 이벤트
Handler (0) | 2011.01.27 |
---|---|
hardware keyboard의 존재 여부 및 상태 확인 (0) | 2011.01.27 |
activity 시작하면서 soft keyboard 자동으로 실행시키기 (0) | 2011.01.27 |
Android Emulator의 성능을 빠르게 하기 (0) | 2011.01.27 |
ANR 발생 원인 (0) | 2011.01.27 |
텍스트 입력 창이 있는 경우 soft input을 자동으로 띄워주는 것이 UX 측면에서 좋다. 그래서 대부분 iPhone이나 Android의 app들은 자동으로 soft input을 띄워준다.
Typically the emulator runs as a device that has a keyboard, in which casethe system deliberately does not automatically show the IME because the user
hardware keyboard의 존재 여부 및 상태 확인 (0) | 2011.01.27 |
---|---|
안드로이드 개발 팁 (0) | 2011.01.27 |
Android Emulator의 성능을 빠르게 하기 (0) | 2011.01.27 |
ANR 발생 원인 (0) | 2011.01.27 |
안드로이드에서 마우스 이벤트 처리하기 (0) | 2011.01.27 |
안드로이드 개발 팁 (0) | 2011.01.27 |
---|---|
activity 시작하면서 soft keyboard 자동으로 실행시키기 (0) | 2011.01.27 |
ANR 발생 원인 (0) | 2011.01.27 |
안드로이드에서 마우스 이벤트 처리하기 (0) | 2011.01.27 |
Activity Lifecycle - 2[필독] (0) | 2011.01.27 |
1. keyDispatchingTimedOut 에 의해서 발생되는 원인
5초내에 키또는 터치등에 대한 입력의 반응이 없을 경우, BroadcastReceiver가 10초내에 작업을 종료하지 않을 경우 발생합니다. 안드로이드 문서에서 확인하세요. http://developer.android.com/ |
activity 시작하면서 soft keyboard 자동으로 실행시키기 (0) | 2011.01.27 |
---|---|
Android Emulator의 성능을 빠르게 하기 (0) | 2011.01.27 |
안드로이드에서 마우스 이벤트 처리하기 (0) | 2011.01.27 |
Activity Lifecycle - 2[필독] (0) | 2011.01.27 |
Telephony & SMS & Search & Play Media & MMS & Web Intent (0) | 2011.01.27 |
안드로이드에서는 기본 입력 장치가 Touch이다. 실제로는 몇가지 입력 장치를 위한 준비가 되어 있는 것으로 보이며 인터넷에서 마우스를 위한 패치된 코드가 있다.
http://gitorious.org/0xdroid/frameworks_base/commit/a236aff3fcfca4ae3e200de48bfbb76daf2b7bb9
코드 패치 하기 전에 aesop에서 안드로이드의 key/touch event dispatch 과정을 정리한 내용이 있는데 이를 참고하면 좀 더 이해가 쉬울 것 같다.
실제 패치하여 동작시키면 완벽한 것은 아니고 몇가지 문제점을 가지고 있다.
일단 수정된 사항 위주로 정리해 본다.
/frameworks/base/core/java/android/view/RawInputEvent.java
public class RawInputEvent {
// Event class as defined by EventHub.
public static final int CLASS_KEYBOARD = 0x00000001;
public static final int CLASS_ALPHAKEY = 0x00000002;
public static final int CLASS_TOUCHSCREEN = 0x00000004;
public static final int CLASS_TRACKBALL = 0x00000008;
public static final int CLASS_MOUSE= 0x00000010;
// More special classes for QueuedEvent below.
public static final int CLASS_CONFIGURATION_CHANGED = 0x10000000;
......
}
코드를 보면 알곘지만 keyboard, alphakey, touchscreen, trackball 을 기본 지원 장치로 준비해 두었다. 이 패치는 trackball의 내용을 수정하여 mouse에 대응하도록 작업되었다.
/frameworks/base/include/ui/EventHub.h
class EventHub : public RefBase
{
public:
EventHub();
status_t errorCheck() const;
// bit fields for classes of devices.
enum {
CLASS_KEYBOARD = 0x00000001,
CLASS_ALPHAKEY = 0x00000002,
CLASS_TOUCHSCREEN = 0x00000004,
CLASS_TRACKBALL = 0x00000008,
CLASS_MOUSE = 0x00000010
};
......
}
java 소스와 c 소스를 모두 고쳐주어야 하기 때문에 위와 같이 두 곳에서 타입을 추가해준다.
/frameworks/base/libs/ui/EventHub.cpp
int EventHub::open_device(const char *deviceName)
{
......
if (test_bit(BTN_MOUSE, key_bitmask)) {
uint8_t rel_bitmask[(REL_MAX+1)/8];
memset(rel_bitmask, 0, sizeof(rel_bitmask));
LOGV("Getting relative controllers...");
if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0)
{
if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
if (test_bit(BTN_LEFT,key_bitmask) &&
test_bit(BTN_RIGHT,key_bitmask))
device->classes |= CLASS_MOUSE;
else
device->classes |= CLASS_TRACKBALL;
}
}
}
}
touchscreen의 경우 디바이스 드라이버로부터 전달되는 좌표 정보는 절대 좌표이지만 mouse나 trackball 의 경우는 상대적인 좌표 이동 정보를 받게 된다. mouse나 trackball의 좌표 처리를 REL_X와 REL_Y 조건을 비교하면 알 수 있는데 mouse와 trackball을 구분하기 위해서 BTN_LEFT, BTN_RIGHT의 유무도 체크하고 있다. (개인적인 생각으로 TRACK BALL도 추후에는 두 경우가 있을 것 같은데 구분하는 이유를 모르겠다.)
/frameworks/base/services/java/com/android/server/KeyInputQueue.java
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
// Is it a key event?
if (type == RawInputEvent.EV_KEY &&
(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
(scancode < RawInputEvent.BTN_FIRST ||
scancode > RawInputEvent.BTN_LAST)) {
boolean down;
if (ev.value != 0) {
down = true;
di.mDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
} else if (ev.type == RawInputEvent.EV_KEY) {
if (ev.scancode == RawInputEvent.BTN_TOUCH &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
di.mAbs.changed = true;
di.mAbs.down = ev.value != 0;
}
if (ev.scancode == RawInputEvent.BTN_MOUSE &&
(((classes&RawInputEvent.CLASS_TRACKBALL) != 0) ||
((classes&RawInputEvent.CLASS_MOUSE) != 0))) {
di.mRel.changed = true;
di.mRel.down = ev.value != 0;
// send = true;
}
} else if (ev.type == RawInputEvent.EV_ABS &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
if (ev.scancode == RawInputEvent.ABS_X) {
di.mAbs.changed = true;
di.mAbs.x = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_Y) {
di.mAbs.changed = true;
di.mAbs.y = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
di.mAbs.changed = true;
di.mAbs.pressure = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
di.mAbs.changed = true;
di.mAbs.size = ev.value;
}
} else if (ev.type == RawInputEvent.EV_REL &&
(((classes&RawInputEvent.CLASS_TRACKBALL) != 0) ||
((classes&RawInputEvent.CLASS_MOUSE) != 0))) {
// Add this relative movement into our totals.
if (ev.scancode == RawInputEvent.REL_X) {
di.mRel.changed = true;
di.mRel.x += ev.value;
} else if (ev.scancode == RawInputEvent.REL_Y) {
di.mRel.changed = true;
di.mRel.y += ev.value;
}
}
if (send || ev.type == RawInputEvent.EV_SYN) {
if (mDisplay != null) {
if (!mHaveGlobalMetaState) {
computeGlobalMetaStateLocked();
}
MotionEvent me;
me = di.mAbs.generateMotion(di, curTime, true,
mDisplay, mOrientation, mGlobalMetaState);
if (false)
Log.v(TAG, "Absolute: x=" + di.mAbs.x
+ " y=" + di.mAbs.y + " ev=" + me);
if (me != null) {
if (WindowManagerPolicy.WATCH_POINTER) {
Log.i(TAG, "Enqueueing: " + me);
}
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_TOUCHSCREEN, me);
}
me = di.mRel.generateMotion(di, curTime, false,
mDisplay, mOrientation, mGlobalMetaState);
if (false)
Log.v(TAG, "Relative: x=" + di.mRel.x
+ " y=" + di.mRel.y + " ev=" + me);
if (me != null) {
if ((classes & RawInputEvent.CLASS_TRACKBALL) != 0) {
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_TRACKBALL, me);
} else {
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_MOUSE, me);
}
}
}
}
}
};
Thread를 수정해서 mouse 입력 처리를 할 수 있도록 한다. 크게 수정해야 될 곳은 3곳이다. 처음 두곳은 CLASS_TRACKBALL에 의해서만 relative evnet가 발생하는 것을 CLASS_MOUSE로도 가능하게 한다. 마지막은 원래는 relative event를 발생시키는 것이 CLASS_TRACKBALL 뿐이지만 이제는 CLASS_MOUSE도 발생시키므로 이를 구분해서 addLocked()을 호출하기 위한 코드이다.
/frameworks/base/services/java/com/android/server/WindowManagerService.java
마지막 수정이 실제 화면에 보여주기 위한 코드이다. 안드로이드의 경우 CLASS_TRACKBALL을 정의해서 mouse나 trackball에 대한 입력을 처리할 수 있도록은 해 두었는데 실제 화면상에 mouse cursor를 그리는 부분은 작업이 되어 있지 않다. 이를 처리하는 내용이 주된 내용이다.
화면상에 mouse cursor를 그려주기 위해서 Canvas와 Path를 import! 한다.
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor {
SurfaceSession mFxSession;
private DimAnimator mDimAnimator = null;
Surface mBlurSurface;
boolean mBlurShown;
Surface mMouseSurface;
int mShowMouse = 0;
int mMlx;
int mMly;
int mMlw;
int mMlh;
......
}
mouse cursor는 새로운 surface에 그려주고 이를 surface flinger를 이용해서 합쳐주어야 하기 때문에 mMouseSurface를 추가한다. 다른 변수는 상대 좌표에서 마지막 그려진 좌표 등을 저장하기 위한 변수를 정의한다.
......
try {
if (ev != null) {
curTime = ev.when;
int eventType;
if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
eventType = eventType((MotionEvent)ev.event);
} else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
ev.classType == RawInputEvent.CLASS_TRACKBALL ||
ev.classType == RawInputEvent.CLASS_MOUSE) {
eventType = LocalPowerManager.BUTTON_EVENT;
} else {
eventType = LocalPowerManager.OTHER_EVENT;
}
try {
long now = SystemClock.uptimeMillis();
if ((now - mLastBatteryStatsCallTime)
>= MIN_TIME_BETWEEN_USERACTIVITIES) {
mLastBatteryStatsCallTime = now;
mBatteryStats.noteInputEvent();
}
} catch (RemoteException e) {
// Ignore
}
mPowerManager.userActivity(curTime, false, eventType, false);
switch (ev.classType) {
case RawInputEvent.CLASS_KEYBOARD:
KeyEvent ke = (KeyEvent)ev.event;
if (ke.isDown()) {
lastKey = ke;
keyRepeatCount = 0;
lastKeyTime = curTime;
nextKeyTime = lastKeyTime
+ KEY_REPEAT_FIRST_DELAY;
if (DEBUG_INPUT) Log.v(
TAG, "Received key down: first repeat @ "
+ nextKeyTime);
} else {
lastKey = null;
// Arbitrary long timeout.
lastKeyTime = curTime;
nextKeyTime = curTime + LONG_WAIT;
if (DEBUG_INPUT) Log.v(
TAG, "Received key up: ignore repeat @ "
+ nextKeyTime);
}
dispatchKey((KeyEvent)ev.event, 0, 0);
mQueue.recycleEvent(ev);
break;
case RawInputEvent.CLASS_TOUCHSCREEN:
//Log.i(TAG, "Read next event " + ev);
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
case RawInputEvent.CLASS_MOUSE:
MotionEvent mmev = (MotionEvent)ev.event;
int mcx = mMlx + (int)(mmev.getX()* mmev.getXPrecision());
int mcy = mMly + (int)(mmev.getY()* mmev.getYPrecision());
mcx = ((mcx < 0) ? 0 :(mcx >= mDisplay.getWidth() ?(mDisplay.getWidth()-1):mcx));
mcy = ((mcy < 0) ? 0 :(mcy >= mDisplay.getHeight()?(mDisplay.getHeight() - 1):mcy));
mmev.setLocation((float) mcx, (float) mcy);
dispatchPointer(ev, mmev, 0, 0);
if (mMouseSurface != null && (mMlx != mcx || mMly != mcy)) {
// Should we use lock? synchronized(mWindowMap) {
Surface.openTransaction();
if (DEBUG_INPUT)
Log.i(TAG,
"Open transaction for the mouse surface");
WindowState top =
(WindowState)mWindows.get(mWindows.size() - 1);
try {
if (DEBUG_INPUT)
Log.i(TAG, "Move surf, x: " +
Integer.toString(mcx) + " y:"
+ Integer.toString(mcy));
mMouseSurface.setPosition(mcx,mcy);
mMouseSurface.setLayer(top.mAnimLayer + 1);
if (mShowMouse != 1) {
mMouseSurface.show();
mShowMouse = 1;
}
mMlx = mcx;
mMly = mcy;
} catch ( RuntimeException e) {
Log.e(TAG, "Failure showing mouse surface",e);
}
Surface.closeTransaction();
}
break;
case RawInputEvent.CLASS_TRACKBALL:
dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
break;
case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
configChanged = true;
break;
default:
mQueue.recycleEvent(ev);
break;
}
}
}
}
첫번째 수정은 위에서와 마찬가지로 CLASS_TRACKBALL로만 되어 있는 것에 CLASS_MOUSE에 대한 코드를 추가한 것이다.
두번째 위치는 mouse event 정보로 올라온 값으로부터 mouse가 그려져야 할 위치를 계산하는 코드이다.
setPosition으로 mMouseSurface의 위치를 설정하고 show()를 호출하여 mouse cursor를 보여준다. 한번 show되면 두번 호출하지 않기 위하여 mShowMouse를 이용해서 flag 관리를 한다.
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
final long currentTime = SystemClock.uptimeMillis();
final int dw = mDisplay.getWidth();
final int dh = mDisplay.getHeight();
final int N = mWindows.size();
int i;
// FIRST LOOP: Perform a layout, if needed.
performLayoutLockedInner();
if (mFxSession == null) {
mFxSession = new SurfaceSession();
}
if (mMouseSurface == null) {
int mMx, mMy, mMw, mMh;
Canvas mCanvas;
Path mPath = new Path();
if (DEBUG_INPUT)
Log.i(TAG, "Create Mouse Surface");
mMw = 20;
mMh = 20;
mMx = (mDisplay.getWidth() - mMw) / 2;
mMy = (mDisplay.getHeight() - mMh) / 2;
try {
/*
*First Mouse event, create Surface
*/
mMouseSurface =
new Surface(mFxSession,
0,-1,mMw,mMy,
PixelFormat.TRANSPARENT,
Surface.FX_SURFACE_NORMAL);
mCanvas = mMouseSurface.lockCanvas(null);
mCanvas.drawColor(0x0);
mPath.moveTo!(0.0f,0.0f);
mPath.lineTo(16.0f, 0.0f);
mPath.lineTo(0.0f, 16.0f);
mPath.close();
mCanvas.clipPath(mPath);
mCanvas.drawColor(0x66666666);
mMouseSurface.unlockCanvasAndPost(mCanvas);
mMouseSurface.openTransaction();
mMouseSurface.setSize(mMw,mMh);
mMouseSurface.closeTransaction();
} catch (Exception e) {
Log.e(TAG, "Exception creating mouse surface",e);
}
mMlx = mMx;
mMly = mMy;
mMlw = mMw;
mMlh = mMh;
}
......
}
여기에서는 mMouseSurface를 생성해 주고, mMouseSurface에서 mCanvas와 mPath를 이용해서 mouse cursor를 그려준다. 실제로 여기에서는 mMouseSurface에 그림 그려주는 것과 좌표를 초기화 해주는 것 외에는 특별한 작업은 하지 않는다. mouse 입력에 따른 동작은 이전 코드에서 처리된다.
출처 : http://blog.daum.net/_blog/BlogTypeView.do?blogid=0CNH3&articleno=16011039#ajax_history_home
Android Emulator의 성능을 빠르게 하기 (0) | 2011.01.27 |
---|---|
ANR 발생 원인 (0) | 2011.01.27 |
Activity Lifecycle - 2[필독] (0) | 2011.01.27 |
Telephony & SMS & Search & Play Media & MMS & Web Intent (0) | 2011.01.27 |
Device를 Rotate시 꼭 알아두어야 할사항. (0) | 2011.01.27 |
dispatchTouchEvent
// 좌표관련들..
getWidth()랑 getHeight()로 전체 좌표값을 얻어오고 %로 비율 정해서 좌표값 설정하세요
출처 : http://winchester.tistory.com/