하나의 DB에 테이블 8개, 총 레코드 수 200여개..
DB를 update식으로 초기화 하려고했더니 어플이 응답이 없어지면서 종료되는 현상이 일어난다..

db.beginTransaction();
   try {
     ...
     db.setTransactionSuccessful();
   } finally {
     db.endTransaction();
   }
트랜잭션을 사용하면 데이타를 빠르게 처리할 수 있다.
위 소스는 개발자사이트에서 Transaction을 검색한 결과.

자세히는 모르지만 쿼리를 날릴때마다 트랜잭션이 발생하는데 그걸 멈추었다가 모든 쿼리문을 날리고
트랜잭션을 발생시켜 쿼리를 한번에 처리하는 방식인듯 싶다..

	/* delete DB */
	public void deleteDB() {
		mDBManager.getWritableDatabase();

		mDB.beginTransaction();
		try {
			mDB.execSQL("DELETE FROM aaa;");
			mDB.execSQL("DELETE FROM bbb;");
			mDB.execSQL("DELETE FROM ccc;");
			mDB.execSQL("DELETE FROM ddd;");
			mDB.execSQL("DELETE FROM eee;");
			mDB.execSQL("DELETE FROM fff;");
			mDB.execSQL("DELETE FROM ggg;");
			initializeDatabases(mDB);
			mDB.setTransactionSuccessful();
		} finally {
			mDB.endTransaction();
		}
		
		mDBManager.close();
	}
속도도 빨라졌으니 그냥 테이블을 삭제시키고 처음 DB를 만들때 insert해주는 초기화 부분을 다시불러 초기화 해주었다.
응답이 없다고 종료되던 현상이 1초도 안걸리고 초기화 되었다 ~ ! 



'Android > Databases' 카테고리의 다른 글

데이터베이스 검색  (0) 2011.07.13
데이터베이스 삭제 및 갱신  (2) 2011.07.03
데이터베이스 업그레이드하기  (0) 2011.07.03
데이터베이스 사용하기  (1) 2011.07.02



먼저 설정창에 쓰일 layout 파일을 만든다.

option.xml
 

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:key="et_name"
android:title="이름"
/>
<CheckBoxPreference
android:key="cb_draw"
android:title="Drawing?"
android:summary="이미지 보기"
android:defaultValue="true"
/>
<PreferenceCategory
android:title="카테고리">
<PreferenceScreen
android:key="fluit"
android:title="과일">
<CheckBoxPreference
android:key="fluit_01"
android:title="사과"/>
<CheckBoxPreference
android:key="fluit_02"
android:title="배"/>
<CheckBoxPreference
android:key="fluit_03"
android:title="포도"/>
</PreferenceScreen>
</PreferenceCategory>
</PreferenceScreen>

레이아웃은 PrefenceScreen으로 해야하며 위 코드는 EditText, CheckBox, Category가 들어있다.
기본적으로 key, title, summary의 값을 지닌다. 



LiveWallPaPer_Canvas.java
package pe.berabue.livewallpaper;

import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.util.Log;

public class LiveWallPaPer_Canvas extends PreferenceActivity implements OnPreferenceClickListener, OnPreferenceChangeListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.layout.option);
        
        EditTextPreference editName = (EditTextPreference)findPreference("et_name");
        CheckBoxPreference checkDraw = (CheckBoxPreference)findPreference("cb_draw");
        editName.setOnPreferenceChangeListener(this);
        checkDraw.setOnPreferenceClickListener(this);
        
        // fluit category
        CheckBoxPreference checkFluit_01 = (CheckBoxPreference)findPreference("fluit_01");
        checkFluit_01.setOnPreferenceClickListener(this);
    }

	@Override
	public boolean onPreferenceClick(Preference preference) {
		if ( preference.getKey().equals("cb_draw")) {
			boolean isDraw = preference.getSharedPreferences().getBoolean("cb_draw", false);
			Log.v(null,"Click CheckPreference : "+isDraw);
			
			WallPaPer_Canvas.isDraw = isDraw;
		}
		if ( preference.getKey().equals("fluit_01") ) {
			Log.v(null,"Click fluit_01");
		}
		return false;
	}

	@Override
	public boolean onPreferenceChange(Preference preference, Object newValue) {
		if ( preference.getKey().equals("et_name") ) {
			preference.setSummary((CharSequence) newValue);
			Log.v(null,""+newValue);
		}
		return false;
	}
}
PreferenceActivity를 상속받는다.
addPreferencesFromResource(R.layout.option);
 먼저 작성해둔 option.xml 을 불러들인다.
xml파일에서 작성해둔 key값으로 리스너를 등록시킨다.

WallPaPer_Canvas.java 의 그림을 그려주는 부분에 isDraw를 추가시켜 놓았는데 Drawing?이 체크되면 이미지를 보이고 체크가 해제되면 이미지를 보여주지 않도록 바꾸어 놓았다.

WallPaPer_Canvas.jaca -> DrawFrame();
if (canvas != null) {
	if ( isDraw )
		canvas.drawBitmap(imgIcon, 0, 0, null);
}
 



ex.xml
 

<?xml version="1.0" encoding="UTF-8"?> <wallpaper xmlns:android="http://schemas.android.com/apk/res/android" android:thumbnail="@drawable/icon" android:description="@string/test" android:settingsActivity="pe.berabue.livewallpaper.LiveWallPaPer_Canvas"/>

ex.xml 파일내의 settingsActivity 옵션에 "패키지명.액티비티명"을 입력 시키면 완성 ~



'Android > Live WallPaper' 카테고리의 다른 글

[ Live WallPaper - 02 ] 이미지 띄우기  (9) 2011.06.10
[ Live Wallpaper - 01 ] 기본 구조  (0) 2011.06.10



데이터베이스의 레코드를 검색하는 방법을 알아본다.

검색 메서드는 다른 SQL문과는 달리 결과셋을 리턴해야 하므로 execSQL 메서드가 아닌 rawQuery 메서드를 실행해야 한다고 한다.

		int index = 0;
		cursor = mDB.rawQuery("SELECT item, price FROM shop", null);
		while(cursor.moveToNext()) {
			arrShop[index][0] = cursor.getInt(0);
			arrShop[index][1] = cursor.getInt(1);
	        index ++;
		}

shop 이라는 테이블안에 item, price 라는 레코드가 들어있음.
cursor 를 moveToNext();로 하나씩 레코드를 검색한다. 만약 더이상 레코드가 없으면 while문을 빠져나오게 된다.
레코드의 순서를 미리 알고있다면 cursor.getInt(순서); 식으로 불러오면된다.

shop 테이블 안에 레코드의 순서는 item, price 순서대로 만들어 놓았으므로 각각 0번과 1번을 갖게된다.





앞서 데이터베이스의 간단한 사용방법과 데이터의 추가, 업그레이드 방법을 알아보았다.
이번에는 데이터 삭제 및 갱신하는 방법에 대하여 알아본다.
기존에 테스트하던 앱을 제거 후 새로운코드로 재설치 하였다.

DB_Test.java
package pe.berabue.dbtest;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class DB_Test extends Activity {
	
	private DBManager mDBManager;
	
	/** Called when the activity is first created. */
	@Override
 	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		findViewById(R.id.btn_insert).setOnClickListener(mClickListener);
		findViewById(R.id.btn_delete).setOnClickListener(mClickListener);
		findViewById(R.id.btn_update).setOnClickListener(mClickListener);
		findViewById(R.id.btn_select).setOnClickListener(mClickListener);
		
		mDBManager = new DBManager(this);
		mDBManager.getReadableDatabase();
	//	mDBManager.getWritableDatabase();
		mDBManager.close();
    }
	
	Button.OnClickListener mClickListener = new View.OnClickListener() {
		public void onClick(View v) {
			switch(v.getId()) {			
			case R.id.btn_insert:	mDBManager.insert(mDBManager);	break;
			case R.id.btn_delete:	mDBManager.delete(mDBManager);	break;
			case R.id.btn_update:	mDBManager.update(mDBManager);	break;
			case R.id.btn_select:	mDBManager.select(mDBManager);	break;
			}
		}
	};
}
액티비티에 4개의 버튼을 구현해 놓고 각기 다른 기능을 넣어두었다.
4번째 버튼인 select버튼은 미구현.


DBManager.java
package pe.berabue.dbtest;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DBManager extends SQLiteOpenHelper {

	public static final String	DB_NAME		= "dbtest.db";
	public static final int		DB_VERSION	= 1;
	
	private SQLiteDatabase mDB;
	private String str;
	private int temp;
	
	public DBManager(Context context) {
		super(context, DB_NAME, null, DB_VERSION);
		
		str = "str_";
		temp = 0;
	}
	
	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("CREATE TABLE Android( _id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT, price INTEGER);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Cupcake'			, 500		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Donut'			, 1000		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Eclair'			, 1500		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Froyo'			, 10000		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Ginger bread'	, 100000	);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Honeycomb'		, 999999	);");
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("INSERT INTO Android VALUES (null, 'Icecream Sandwich'	, 1	);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Jellybean'			, 2	);");
	}
	
	@Override
	public void onOpen(SQLiteDatabase db) {
		super.onOpen(db);
		mDB = db;
	}

	public void insert(DBManager mDBManager) {
		mDBManager.getWritableDatabase();
		mDB.execSQL("INSERT INTO Android VALUES (null, '"+ str +"', "+ temp +" );");
		mDBManager.close();
		
		str += temp;
		temp++;
		Log.v(null,"Insert DB : "+temp);
	}
	
	public void delete(DBManager mDBManager) {
		mDBManager.getWritableDatabase();
		mDB.execSQL("DELETE FROM Android WHERE price = "+ (temp-1) +";");
		mDBManager.close();
		
		temp--;
		Log.v(null,"Delete DB : "+temp);
	}
	
	public void update(DBManager mDBManager) {
		mDBManager.getWritableDatabase();
		mDB.execSQL("UPDATE Android SET price = "+ (temp+10) +" WHERE price = "+ (temp-1) +";");
		mDBManager.close();

		Log.v(null,"Update DB : "+temp);
	}
	
	public void select(DBManager mDBManager) {

	}
}
open();
 DB를 열때 mDB에 객체를 저장시켜 놓는다.
insert();
 DB를 읽기/쓰기 용으로 열고 name과 price를 입력시켜 테이블에 추가한다.
delete();
 DB를 읽기/쓰기 용으로 열고 Android 테이블에 price가 temp-1인 데이터를 찾아 삭제시킨다.
update(); 
 DB를 읽기/쓰기 용으로 열고 Android 테이블에 price가 temp-1인 데이터를 찾아 price를 temp+10값으로 변경시킨다. 


main.xml


    



'Android > Databases' 카테고리의 다른 글

데이터베이스 Transaction 사용하기  (0) 2011.07.26
데이터베이스 검색  (0) 2011.07.13
데이터베이스 업그레이드하기  (0) 2011.07.03
데이터베이스 사용하기  (1) 2011.07.02



데이터를 넣어 앱을 배포하였는데 다음버전에서 데이터가 추가되거나 삭제되어야 한다면?
데이터베이스 버전을 바꾸고 재배포 하면된다.


package pe.berabue.dbtest;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBManager extends SQLiteOpenHelper {

	public static final String	DB_NAME		= "dbtest.db";
	public static final int		DB_VERSION	= 2;
	
	
	public DBManager(Context context) {
		super(context, DB_NAME, null, DB_VERSION);
	}
	
	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("CREATE TABLE Android( _id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT, price INTEGER);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Cupcake'			, 500		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Donut'			, 1000		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Eclair'			, 1500		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Froyo'			, 10000		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Ginger bread'	, 100000	);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Honeycomb'		, 999999	);");
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("INSERT INTO Android VALUES (null, 'Icecream Sandwich'	, 1	);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Jellybean'			, 2	);");
	}
}
만약 기존의 DB배포 버전이 1이였다면, 재배포 하는 DB의 버전을 2로 올려준다.
그러면 DB의 버전이 바뀌었으므로 DB가 실행될때 자동으로 onUpgrade(); 메서드를 호출한다.
 


2011/07/02 - [Android/Databases] - 데이터베이스 사용하기
 
위 소스에 onUpgrade(); 부분과 DB버전만 변형시켜서 실행해보았다.
7,8번 데이터가 추가된 것을 확인 할 수 있다. 



'Android > Databases' 카테고리의 다른 글

데이터베이스 Transaction 사용하기  (0) 2011.07.26
데이터베이스 검색  (0) 2011.07.13
데이터베이스 삭제 및 갱신  (2) 2011.07.03
데이터베이스 사용하기  (1) 2011.07.02



프로젝트를 진행하다보면 데이터를 저장해야 할 때가 있다.
데이터를 저장하는 방법은 프레프런스, 파일, DB 등이 있다.

SQLite는 다른 데이터베이스에 비해 사용 가능한 자료형이 많지않고 5가지를 지원한다.
NULL
INTEGER : 1,2,3,4,6,8bytes의 정수값
REAL : 8bytes의 부동소수점값
TEXT : UTF-8, UTF-16BE, UTE-16LE 인코딩의 문자열
BLOB : 입력된 그대로 저장

SELECT 명령을 제외한 대부분의 명령을 execSQL(String sql); 메서드로 실행 할 수 있다. execSQL("CREATE TABLE 테이블명( _id INTEGER PRIMARY KEY AUTOINCREMENT,변수 자료형);");
 _id 필드는 자동으로 값이 증가한다. 변수+자료형 필드는 콤마(,)를 사용해 여러개를 지정 할 수 있다.
execSQL("INSERT INTO 테이블명 VALUES (데이터);");
 레코드 추가
execSQL("DELETE FROM 테이블명 WHERE 조건;");
 레코드 삭제, WHERE문을 적지 않으면 테이블 삭제
execSQL("UPDATE 테이블명 SET 갱신내용 WHERE 조건;");
 레코드 업데이트
 

DB_Test.java
package pe.berabue.dbtest;

import android.app.Activity;
import android.os.Bundle;

public class DB_Test extends Activity {
	
	private DBManager mDBManager;
//	private SQLiteDatabase mDB;
	
	/** Called when the activity is first created. */
	@Override
 	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
        
		mDBManager = new DBManager(this);
		mDBManager.getReadableDatabase();
	//	mDBManager.getWritableDatabase();
		mDBManager.close();
    }
}
기능이 있는 화면은 아니다.
mDBManager.getReadableDatabase();  읽기전용으로 DB를 불러온다. 이 때 생성된 DB가 없으면 onCreate(); DB가 있지만 버전이 바뀌었다면 onUpgrade();를 호출한다.
mDBManager.getWritableDatabase();  읽고/쓰기가 가능하다. getReadableDatabase();과 마찬가지로 onCreate(); onUpgrade();를 호출. mDBManager.close();
 위 두 구문을 실행 한 뒤에 호출하여 DB를 닫아준다.


DBManager.java
package pe.berabue.dbtest;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBManager extends SQLiteOpenHelper {

	public static final String	DB_NAME		= "dbtest.db";
	public static final int		DB_VERSION	= 1;
	
	
	public DBManager(Context context) {
		super(context, DB_NAME, null, DB_VERSION);
	}
	
	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("CREATE TABLE Android( _id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT, price INTEGER);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Cupcake'			, 500		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Donut'			, 1000		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Eclair'			, 1500		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Froyo'			, 10000		);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Ginger bread'	, 100000	);");
		db.execSQL("INSERT INTO Android VALUES (null, 'Honeycomb'		, 999999	);");
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub
	}

}
생성할 DB의 이름과 버전을 상수로 정의해 놓았다.
생성자에서 DB이름과 버전을 정의해 놓고 DB가 사용될때 버전이 더 높다면 onUpgrade();를 통해 내용을 수정시킬 수 있다.
db.execSQL("INSERT INTO 테이블명 VALUES (데이터);");
 데이터를 삽입할때 사용하는 쿼리문.
Android 라는 테이블 하나를 생성하고 Android 테이블에 안드로이드 코드명 C ~ H의 코드네임과 멋대로 가격을 입력시켜 놓았다.
실제 단말기에서 DB를 열어보려면 루팅을 해야 하고, 에뮬레이터로 실행시키면 DDMS를 통해 DB파일이 생성되었는지 확인이 가능하다.

명령프롬프트 창을 이용해 DB를 들여다보자.



데이터베이스는 data/data/패키지명/databases 경로에 저장되어있다.
해당경로로 이동을 하고 'sqlite3 DB명'을 사용해 저장된 DB를 실행시키면 버전과 도움말을 보는 방법을 알려준다.
해당DB의 테이블을 보고싶다면 .tables를 입력한다.
위에서 생성한 Android라는 테이블과 기본적인 android_metadata라는 테이블이 존재한다.
테이블안에 들어있는 데이터를 보려면 select * from 테이블명; 을 입력한다.

입력한 그대로 1번부터 6번까지의 데이터가 출력된다.
 




'Android > Databases' 카테고리의 다른 글

데이터베이스 Transaction 사용하기  (0) 2011.07.26
데이터베이스 검색  (0) 2011.07.13
데이터베이스 삭제 및 갱신  (2) 2011.07.03
데이터베이스 업그레이드하기  (0) 2011.07.03



    	if ( Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    		String path = Environment.getExternalStorageDirectory()+"/android/data/pe.berabue.maptools/.image";
    		File file = new File(path);

    		if( !file.exists() ) {// 원하는 경로에 폴더가 있는지 확인
    			file.mkdirs();

    			for ( int i = 0; i < 27; i++ ) {
    				byte [] buffer = new byte[8*1024];
    				int length = 0;
    				InputStream is = this.getResources().openRawResource(R.drawable.map1+i);  
    				BufferedInputStream bis = new BufferedInputStream(is);    

    				try {
    					FileOutputStream fos = new FileOutputStream(path+"/map"+(i+1)+".png");
    					while ((length = bis.read(buffer)) >= 0)
    						fos.write(buffer, 0, length);
    					fos.flush();  
    					fos.close();
    				} catch (Exception e) {} 
    			}
    		}
    	}

프로젝트 drawable 폴더안에 있는 png파일을 sdCard로 옮기는 방법.

먼저 sdCard가 연결이 되어 있는지 확인을 한다.
그리고 파일을 옮겨놓을 경로를 path에 입력을 시킨다.
원하는 경로의 폴더가 있는지 확인 후 없으면 새로 만들어준다.

for문이 28번 돌아가는 이유는 테스트할때 이미지가 28개였기때문에..

조금 변형시키면 raw폴더안 파일이나 다른 파일들도 충분히 이동 가능할듯!



'Android > File' 카테고리의 다른 글

ZipEntry를 활용한 압축해제  (0) 2013.06.02
단말기 내부에 폴더 및 txt파일 생성하기  (0) 2011.03.02
Sd Card 이미지 읽어오기  (0) 2011.02.14
txt 파일 읽어오기  (0) 2011.02.11
SD Card에 txt 파일로 저장하기  (0) 2011.02.05



WallPePer_Canvas.java

package pe.berabue.livewallpaper;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class WallPaPer_Canvas extends WallpaperService {
    	
	private final Handler mHandler = new Handler();
	
	@Override
	public void onCreate() {
		super.onCreate();
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
	}

	@Override
	public Engine onCreateEngine() {
		return new CanvasEngine();
	}

	private class CanvasEngine extends Engine {  

		private Bitmap imgIcon;
		
		private final Runnable mRunnable = new Runnable() {
			public void run() {
				drawFrame();
			}
		};
		private boolean isVisible;
	        
		public CanvasEngine() {
			imgIcon = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.icon);
		}
		
		@Override
		public void onCreate(SurfaceHolder surfaceHolder) {
			super.onCreate(surfaceHolder);
			setTouchEventsEnabled(true);
		}

		@Override
		public void onDestroy() {
			super.onDestroy();
			mHandler.removeCallbacks(mRunnable);
		}

		@Override
		public void onVisibilityChanged(boolean visible) {
			isVisible = visible;

			if (visible) {
				drawFrame();
			} else {
				mHandler.removeCallbacks(mRunnable);
			}
		}

		@Override
		public void onSurfaceCreated(SurfaceHolder holder) {
			super.onSurfaceCreated(holder);
		}
		
		@Override
		public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
			super.onSurfaceChanged(holder, format, width, height);
			Log.v(null," :::: onSurfaceChanged : "+format+" / "+width+", "+height);
			drawFrame();
		}

		@Override
		public void onSurfaceDestroyed(SurfaceHolder holder) {
			super.onSurfaceDestroyed(holder);
			isVisible = false;
			mHandler.removeCallbacks(mRunnable);
		}

		@Override
		public void onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels) {
			Log.v(null," :::: onOffsetsChanged : "+xOffset+", "+yOffset+", "+xStep+", "+yStep+", "+xPixels+", "+yPixels);
			drawFrame();
		}

		@Override
		public void onTouchEvent(MotionEvent event) {
			if (event.getAction() == MotionEvent.ACTION_DOWN) {
				Log.v(null," :::: onTouchEvent");
			}
			super.onTouchEvent(event);
		}

		public void drawFrame() {
			final SurfaceHolder holder = getSurfaceHolder();

			Canvas canvas = null;
			try {
				canvas = holder.lockCanvas();
				if (canvas != null) {
					canvas.drawBitmap(imgIcon, 0, 0, null);
				}
			} finally {
				if (canvas != null)
					holder.unlockCanvasAndPost(canvas);
			}

			mHandler.removeCallbacks(mRunnable);
			if (isVisible) {
				mHandler.postDelayed(mRunnable, 1000 / 25);
			}
		}
	}
}

mHandler를 등록하고 이미지를 준비시킨다.

onVisibilityChanged();  화면이 보여지고 있는지 가려졌는지를 알아내 isVisible에 넣는다.  화면이 보여지고 있다면 drawFrame();으로 들어간다. drawFrame();  실직적으로 이미지가 그려지는 곳.  이미지 처리를 완료하고 핸들러를 제거한다. 화면이 계속 보여지는 중이라면 원하는 딜레이로 핸들러를 다시 등록하여준다.
 

화면이 가려지거나(다른어플 실행, 화면꺼짐 등) 해당 Live Wallpaper를 종료시킬때 핸들러를 제거하여준다.
아래 화면과 같이 타이틀바 영역을 포함하여 좌표가 시작된다.


 



'Android > Live WallPaper' 카테고리의 다른 글

[ Live WallPaper - 03 ] 설정 화면 만들기  (0) 2011.07.15
[ Live Wallpaper - 01 ] 기본 구조  (0) 2011.06.10



AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="pe.berabue.livewallpaper"
      android:versionCode="1"
      android:versionName="1.0">

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".LiveWallPaPer_Canvas"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

  <service android:name=".WallPaPer_Canvas"  
        android:label="@string/app_name"  
        android:permission="android.permission.BIND_WALLPAPER">  
<intent-filter>  
<action android:name="android.service.wallpaper.WallpaperService"/>  
</intent-filter>  
<meta-data android:name="android.service.wallpaper" android:resource="@xml/ex"/>  
</service>  
    </application>
    
    <uses-sdk android:minSdkVersion="7" />
    <uses-feature android:name="android.software.live_wallpaper" />
</manifest>

xml파일을 syntaxhighlighter로 변환하면 이상하게 /> 문자열이 자동으로 바뀌어버리는......
아무튼, Activity는 필수가 아니다. 기본적으로 service부분과 uses-feature 부분만 넣어주면된다.



ex.xml

<?xml version="1.0" encoding="UTF-8"?>
<wallpaper 
    xmlns:android="http://schemas.android.com/apk/res/android"  
    android:thumbnail="@drawable/icon" 
    android:description="@string/test"
    android:settingsActivity="PreferenceActivity"/>

Live Wallpaper를 적용시킬때 나오는 아이콘과 설명 그리고 설정화면을 등록해준다.
android:settingsActivity부분을 넣지않으면 설정 버튼이 나오지 않는다.



WallPePer_Canvas.java

package pe.berabue.livewallpaper;

import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class WallPaPer_Canvas extends WallpaperService {
    	
	@Override
	public void onCreate() {
		super.onCreate();
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
	}

	@Override
	public Engine onCreateEngine() {
		return new CanvasEngine();
	}

	private class CanvasEngine extends Engine {  
        
		public CanvasEngine() {

		}
		
		@Override
		public void onCreate(SurfaceHolder surfaceHolder) {
			super.onCreate(surfaceHolder);
		}

		@Override
		public void onDestroy() {
			super.onDestroy();
		}

		@Override
		public void onVisibilityChanged(boolean visible) {

		}

		@Override
		public void onSurfaceCreated(SurfaceHolder holder) {
			super.onSurfaceCreated(holder);
		}
		
		@Override
		public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
			super.onSurfaceChanged(holder, format, width, height);
			Log.v(null," :::: onSurfaceChanged : "+format+" / "+width+", "+height);
		}

		@Override
		public void onSurfaceDestroyed(SurfaceHolder holder) {
			super.onSurfaceDestroyed(holder);
		}

		@Override
		public void onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels) {
			Log.v(null," :::: onOffsetsChanged : "+xOffset+", "+yOffset+", "+xStep+", "+yStep+", "+xPixels+", "+yPixels);
		}

		@Override
		public void onTouchEvent(MotionEvent event) {
			if (event.getAction() == MotionEvent.ACTION_DOWN) {
				Log.v(null," :::: onTouchEvent");
			}
			super.onTouchEvent(event);
		}
	}
}

Activity대신 WallpaperService를 상속받는데 그러면 onCreateEngine() 메서드를 구현해 주어야 한다. Engine을 상속받은 CanvasEngine클래스가 중요한 부분. 클래스 이름에 Canvas를 붙인 이유는.. 목표가 OpenGL용 live wallpaper이기 때문에...(?)

onVisibilityChanged();  메서드는 화면이 보여질때와 가려질때를 알아낸다. onOffsetsChanged();  화면을 밀어 옆으로 이동할때 실행된다. 





기존에 사용하던 소스에서 Thread를 별도의 클래스로 빼냄.

GameMain.java
public class GameMain extends Activity {
/** Called when the activity is first created. */
	
	public static GLGameSurfaceView mGLView;
	public static GLGameRenderer mGLRenderer;
	public static GameThread mThread;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 
        
        mGLRenderer = new GLGameRenderer(this);
        mGLView = new GLGameSurfaceView(this);
        mThread = new GameThread(this);

        setContentView(mGLView);
        mGLView.setVisibility(View.VISIBLE);
        
        mThread.start();
    }
    
	@Override
	public void onPause() {
		mThread.pauseThread();
		super.onPause();
	}

	@Override
	public void onResume() {
		mThread.resumeThread();
		super.onResume();
	}
}

GLGameRenderer, GLGameSurfaceView, GameThread 객체를 생성하고 Thread를 시작한다.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
 화면을 항상 켬


GLGameRenderer.java
package pe.berabue.game;

import javax.microedition.khronos.egl.*;
import javax.microedition.khronos.opengles.*;

import android.content.*;
import android.opengl.GLSurfaceView.Renderer;

public class GLGameRenderer implements Renderer
{

	public static Context context;

	public GLGameRenderer(Context context)
	{
		GLGameRenderer.context = context;
	}
	
	@Override
	public void onDrawFrame(GL10 gl)
	{
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height)
	{
		gl.glOrthof(0.0f, 800.0f, 480.0f, 0.0f, 1.0f, -1.0f);
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glViewport(0, 0, width, height);
		
		gl.glEnable(GL10.GL_TEXTURE_2D);
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		gl.glEnable(GL10.GL_BLEND);
		gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig arg1)
	{
		gl.glClearDepthf(1.0f);
		gl.glDisable(GL10.GL_DEPTH_TEST);
		gl.glMatrixMode(GL10.GL_PROJECTION);
	}
}

onDrawFrame();
 지속적인 호출을 통해 그림을 그리는 부분.
onSurfaceChanged();
 화면이 바뀔때 호출
onSurfaceCreated();
 화면이 생성될때 호출


GLGameSurfaceView.java
package pe.berabue.game;

import android.content.*;
import android.opengl.*;
import android.view.*;

public class GLGameSurfaceView extends GLSurfaceView
{
	public GLGameSurfaceView(Context context)
	{
		super(context);
		setRenderer(GameMain.mGLRenderer);
		setRenderMode(RENDERMODE_WHEN_DIRTY); // One drawing
		setFocusableInTouchMode(true);
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		return true;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return true;
	}
}

setRenderMode(RENDERMODE_WHEN_DIRTY);
 GLGameRenderer()의 onDrawFrame()을 1회 호출.



GameThread.java
package pe.berabue.game;

import android.content.*;

public class GameThread extends Thread 
{
	public static boolean isRun;
	public static boolean isWait;
	
	public GameThread(Context context)
	{
		isRun = true;
		isWait = false;
	}

	public synchronized void run()
	{
		while(isRun)
		{
			try
			{
				GameMain.mGLView.requestRender(); // onDrawFrame
				Thread.sleep(10);
			}
			catch (InterruptedException e1)
			{
				e1.printStackTrace();
			}
			if (isWait)
			{
				try
				{
					wait();
				}
				catch (Exception e)
				{
					// Ignore error
				}
			}
		}
	}
	
	/* Thread pause */
	public void pauseThread()
	{
		isWait = true;
		synchronized (this)
		{
			this.notify();
		}
	}

	/* Thread Resume */
	public void resumeThread()
	{
		isWait = false;
        synchronized (this)
        {
             this.notify();
        }
    }
}

GameMain.mGLView.requestRender();
 GLGameRenderer()의 onDrawFrame() 호출 





gl.glDisable(GL10.GL_DEPTH_TEST);
 A물체를 그리고, 그 위에 B물체를 그렸을때 먼저 그린 A가 B를 가리는 현상이 나타날 수 있다.
 위 메서드를 사용하면 정상적으로 먼저 그린 A보다 나중에 그린 B가 위쪽에 그려지게 된다.

gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
  두 버퍼를 지운다.

gl.glClearColor(float red, float green, float blue, float alpha);
 ARGB 색상으로 지운다. 

gl.glEnable(GL10.GL_TEXTURE_2D);
 텍스쳐를 활성화 시킨다.

gl.glTranslatef(0.0f, 0.0f, -5.0f);
 기본 좌표는 화면의 중심이다. 좌표계를 범위만큼 움직인다. x를 0만큼, y를 0만큼, z를 -5.0만큼 움직인다.
 z값이 음수 일때 안쪽으로 들어가며 양수 일때 액정 바깥쪽으로 올라온다.

gl.glRotatef(45.0f, 0.0f, 0.0f, 0.0f);
 x,y,z 축을 45도 회전시킨다.


gl.glLoadIdentity();
 좌표계를 초기화시킨다.

gl.glEnable(GL10.GL_SCISSOR_TEST);
gl.glScissor(x, y, width, height);
gl.glDisable(GL10.GL_SCISSOR_TEST); 

 4개의 점 x, y, width, height 부분의 영역만 렌더링한다.





텍스쳐 입히기( 이미지 덮어 씌우기 )
아래 내용은 100% 정확한 것이 아니며, 여기저기서 보고 실행해본 결과를 토대로 작성하였다.

기존 소스에서 몇 부분만 추가. Triangle 클래스는 사용하지 않고 Quad 클래스만 사용하여 연습해본다.


Quad.java
import java.nio.*;
import javax.microedition.khronos.opengles.*;
import android.content.*;
import android.graphics.*;
import android.opengl.*;

public class Quad extends Shape {
	
	private FloatBuffer vertexBuffer;
	private ShortBuffer indexBuffer;
	private FloatBuffer textureBuffer;
	
	private int[] textureName;
	
	private float[] vertices = {
			-0.5f	, 0.5f	, 0.0f, // 0, Left Top
			0.5f	, 0.5f	, 0.0f,	// 1, Right Top
			0.5f	, -0.5f	, 0.0f,	// 2, Right Bottom
			-0.5f	, -0.5f	, 0.0f	// 3, Left Bottom
	};

	private short[] index = {
		0, 1, 2,
		0, 2, 3
	};
	
	private float[] texture = {
		0.0f, 0.0f,
		0.0f, 1.0f,
		1.0f, 1.0f,
		1.0f, 0.0f
	};
	
	public Quad() {
		textureName = new int[1];

		vertexBuffer = getFloatBufferFromFloatArray(vertices);
		indexBuffer = getByteBufferFromByteArray(index);
		textureBuffer = getFloatBufferFromTextureArray(texture);
	}

	/* Draw */
	public void draw(GL10 gl) {

		gl.glFrontFace(GL10.GL_CW);
		
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
		gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
		
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		
		gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, index.length, GL10.GL_UNSIGNED_SHORT, indexBuffer);
		
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);		
		gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
	}
	
	/* Initialize Texture */
	public void InitTexture(GL10 gl, Context context) {

		Bitmap imgPanda = BitmapFactory.decodeResource(context.getResources(), R.drawable.panda);

		gl.glGenTextures(1, textureName, 0);					// 텍스쳐 포인터 설정
		gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName[0]);	// 텍스쳐 사용 연결

		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, imgPanda, 0);
		
		imgPanda.recycle();
	}
}
우선 추가된 부분을 보면.
FloatBuffer textureBuffer;
 텍스쳐 버퍼.
 
int[] textureName;
 텍스쳐 포인터를 담는다.
 
float[] texture;
 텍스쳐 좌표. 좌표를 어떻게 하느냐에 따라 이미지가 이상하게 붙어버린다.
 어떤 순서로 붙는지 모르겠다. 


InitTexture();
  GLGameRenderer.java 에서 접근.
  우선 Bitmap 이미지를 가져온다.
  주의할점!! 이미지의 가로, 세로 길이가 '2의 배수'가 아니면 이미지가 나오지않는다.
 
 gl.glGenTextures(1, textureName, 0);
  텍스쳐 포인터 생성.
 
 gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName[0]);
  텍스쳐 연결.
 
 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 
  이미지 자동 확대, 축소.

 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, imgPanda, 0); 
  비트맵을 OpenGL 텍스쳐로 변경.

 
Draw();
 gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
  텍스쳐 좌표 설정.
 
 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
  텍스쳐 활성화.
 
 gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
  비활성화.




이전까지와는 다르게 텍스쳐 버퍼가 추가되었다.

Shape.java
import java.nio.*;

public class Shape {

	FloatBuffer getFloatBufferFromFloatArray(float array[]) {
		ByteBuffer tempBuffer = ByteBuffer.allocateDirect(array.length * 4);
		tempBuffer.order(ByteOrder.nativeOrder());
		FloatBuffer buffer = tempBuffer.asFloatBuffer();
		buffer.put(array);
		buffer.position(0);
		return buffer;
	}
	
	ShortBuffer getByteBufferFromByteArray(short[] index) {
		ByteBuffer b = ByteBuffer.allocateDirect(index.length * 2);
		b.order(ByteOrder.nativeOrder());
		ShortBuffer buffer = b.asShortBuffer();
		buffer.put(index);
		buffer.position(0);
		return buffer;
	}
	
	FloatBuffer getFloatBufferFromTextureArray(float texture[]) {
		ByteBuffer tbb = ByteBuffer.allocateDirect(texture.length * 4);
		tbb.order(ByteOrder.nativeOrder());
		FloatBuffer buffer = tbb.asFloatBuffer();
		buffer.put(texture);
		buffer.position(0);
		return buffer;
	}
}
버퍼............



GLGameRenderer.java
@Override
	public void onDrawFrame(GL10 gl) {
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT);//|GL10.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity();
		
	//	mTriangle.draw(gl);
		mQuad.draw(gl);
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {
		mQuad.InitTexture(gl, context);

		gl.glClearColor(0, 1, 0, 0.5f);										// RGBA
		gl.glEnable(GL10.GL_TEXTURE_2D);									// 텍스쳐 활성
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);	// ??	
	}
onSurfaceCreated();
 mQuad.InitTexture(gl, context);   텍스쳐 초기화.
 
gl.glEnable(GL10.GL_TEXTURE_2D);   텍스쳐를 활성화 시킨다.





삼각형과 사각형을 그려볼 차례다.

아래 코드 실행시 삼각형과 사각형이 겹쳐서 나온다... 좌표 바꾸기가 귀찮아서.. 

 

GLGameRenderer.java

public class GLGameRenderer implements Renderer {

	private Triangle mTriangle;
	private Quad mQuad;
	
	public GLGameRenderer(Context context) {
		mTriangle = new Triangle();
		mQuad = new Quad();
	}
	
	@Override
	public void onDrawFrame(GL10 gl) {
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT);	// 화면을 깨끗하게
		gl.glLoadIdentity();
		
		mTriangle.draw(gl);
		mQuad.draw(gl);
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		gl.glViewport(0, 0, width, height);		// 화면크기 지정
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {
		gl.glClearColor(0, 1, 0, 0.5f);										// RGBA
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);	// ??
	}
}

Triangle, Quad 객체를 생성한다.
onDrawFrame();
 생성한 객체의 draw 메서드를 통해 그림을 그린다.



Triangle.java
import java.nio.*;
import javax.microedition.khronos.opengles.*;

public class Triangle extends Shape {
	
	private FloatBuffer vertexBuffer;
	
	private float[] vertices = {
			0.0f	, 1.0f	, 0.0f,	// 0, Top
			1.0f	, -1.0f	, 0.0f, // 1, Right Bottom
			-1.0f	, -1.0f	, 0.0f 	// 2, Left Bottom
	};
	
	public Triangle() {
		vertexBuffer = getFloatBufferFromFloatArray(vertices);
	}
	
	/* 그리기 */
	public void draw(GL10 gl) {
		gl.glFrontFace(GL10.GL_CW);									// 시계 방향
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);		// 버퍼 포인터
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);				// 버퍼 활성화
		gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);					// 그린다
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);				// 버퍼 비활성화
	}
}

vertices[];
 삼각형을 이루는 꼭지점 3개를 지정한다.
 좌표는 핸드폰 중심을 기준으로 상하좌우 0.0f ~ 1.0f. ( 1.0f는 화면 끝점이 된다. )
 
vertexBuffer = getFloatBufferFromFloatArray(vertices);
 버퍼부분은 어떻게 돌아가는지 모르겠지만 공식과도 같다한다.

gl.glFrontFace(GL10.GL_CW);
 각 꼭지점을 시계방향으로 그린다. 0 -> 1 -> 2 순서로 그림.
 GL_CCW - 반시계 방향.

gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
 
꼭지점 값이 있는 실수형 버퍼를 넣는다.

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

 꼭지점 배열값을 사용해 그린다.

gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)
;
 도형 내부가 채워진 삼각형을 그린다.
 GL_LINE_LOOP - 도형을 선으로 표현
 GL_POINTS - 도형을 점으로 표현

gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

 그리기 종료(?)



Quad.java
import java.nio.*;
import javax.microedition.khronos.opengles.*;

public class Quad extends Shape {
	
	private FloatBuffer vertexBuffer;
	private ByteBuffer indexBuffer;
	
	private float[] vertices = {
			-0.5f	, 0.5f	, 0.0f, // 0, Left Top
			0.5f	, 0.5f	, 0.0f,	// 1, Right Top
			0.5f	, -0.5f	, 0.0f,	// 2, Right Bottom
			-0.5f	, -0.5f	, 0.0f	// 3, Left Bottom
	};
	private byte[] index = {
		0, 1, 2,
		0, 2, 3
	};
	
	public Quad() {
		vertexBuffer = getFloatBufferFromFloatArray(vertices);
		indexBuffer = getByteBufferFromByteArray(index);
	}
	
	void draw(GL10 gl) {
		gl.glFrontFace(GL10.GL_CW);
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDrawElements(GL10.GL_TRIANGLES, index.length, GL10.GL_UNSIGNED_BYTE, indexBuffer);
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);		
	}
}
OpenGL은 도형을 그릴때 삼각형을 모아서 다른 도형을 그린다고 한다.
우선 사각형을 이루는 4개의 꼭지점을 지정한다.
index[];
 꼭지점을 어떻게 삼각형으로 만들지 정한다.
 3개의 꼭지점( 0, 1, 2 )을 연결해 하나의 삼각형으로 만들고, 또다른 삼각형( 0, 2, 3 )을 만든다.

삼각형과는 다르게 indexBuffer도 필요하다.

그려지는 부분또한 삼각형이랑 다른데..
gl.glDrawElements(GL10.GL_TRIANGLES, index.length, GL10.GL_UNSIGNED_BYTE, indexBuffer);
 index 개수와 indexBuffer를 넣는다.


 
Shape.java
import java.nio.*;

public class Shape {

	FloatBuffer getFloatBufferFromFloatArray(float array[]) {
		ByteBuffer tempBuffer = ByteBuffer.allocateDirect(array.length * 4);
		tempBuffer.order(ByteOrder.nativeOrder());
		FloatBuffer buffer = tempBuffer.asFloatBuffer();
		buffer.put(array);
		buffer.position(0);
		return buffer;
	}
	
	ByteBuffer getByteBufferFromByteArray(byte array[]) {
		ByteBuffer buffer = ByteBuffer.allocateDirect(array.length);
		buffer.put(array);
		buffer.position(0);
		return buffer;
	}
}
버퍼 부분.. 잘 모름...... 아무나 가르쳐줘요 ~ ~ ~


 





MapManager.java의 mListener 부분에 추가시켜준다.
    		sAddress = "";
    		sAddress = getGeocode(location.getLatitude(), location.getLongitude()); // 주소 받아옴
    		Toast.makeText(contxet, ""+sAddress, Toast.LENGTH_SHORT).show();
위치 정보를 받아오면 해당 위치의 주소를 반환시킨다.


MapManager.java에 추가시켜준다.
/* 주소 검색 */
	private String getGeocode(double ALatitude, double ALongitude) {
		String sResult = "";

    	Geocoder mGeocoder = new Geocoder(contxet);
        try {
            Iterator
mAddresses = mGeocoder.getFromLocation(ALatitude, ALongitude, 1).iterator(); if (mAddresses != null) { while (mAddresses.hasNext()) { Address namedLoc = mAddresses.next(); String addLine = namedLoc.getAddressLine(0); sResult += String.format("%s\n", addLine); } } return sResult; } catch (IOException e) { return "주소 검색 실패"; } } // getGeocode
위치 정보 수신시 접근. 주소를 반환한다.
getFromLocation(위도, 경도, 1~5); 3번째 인자값이 커지면 더 많은 주소를 반환. 



'Android > Etc.' 카테고리의 다른 글

정적 라이브러리 만들기  (0) 2012.09.25
Max OS에서 NDK 개발하기  (1) 2012.07.04
Orientation 고정, 키보드 숨기기  (0) 2011.04.17
멀티터치 구현하기  (0) 2011.03.16
Permission  (0) 2011.01.26



추가 파일
MapManager.java : 위치 찾기
DialogManager.java : 다이얼로그 관리


MainActivity에 MapManager 인스턴스 생성.

MapManager.java
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapController;

public class MapManager {

	private DialogManager mDialogManager;
	private MapController mMapController;
	private LocationManager mLocationManager;
	private String mProvider;					// 공급자
	
	public MapManager(Context context) {
		mDialogManager = new DialogManager(context);
		
		mMapController = MyGps.mMapController;
		
		mLocationManager = (LocationManager)context.getSystemService(context.LOCATION_SERVICE);
	//	mProvider = mLocationManager.GPS_PROVIDER; 				// GPS
		mProvider = mLocationManager.NETWORK_PROVIDER;			// NETWORK
	}
	
	/* 위치 찾기 */
	public void SearchLocation() {
		mLocationManager.requestLocationUpdates(mProvider, 1000, 0, mListener);
		mDialogManager.Loading();
	}
	
    LocationListener mListener = new LocationListener() {
    	public void onLocationChanged(Location location) {
    		GeoPoint point = new GeoPoint((int)(location.getLatitude()*1E6), (int)(location.getLongitude()*1E6));
    		mMapController.animateTo(point); 									// 위치로 이동
    		mLocationManager.removeUpdates(mListener);							// 해제
    		mDialogManager.LoadingEnd();
    	}

    	public void onProviderDisabled(String provider) {
    		// 추가 : 공급자 사용불가 처리
    	}

    	public void onProviderEnabled(String provider) {
    	}

    	public void onStatusChanged(String provider, int status, Bundle extras) {
    	}
	}; // mListener
}
- mProvider : 공급자 선택 ( GPS를 사용하면 더욱 정확한 위치 확인가능. )
mLocationManager.requestLocationUpdates(mProvider, 1000, 0, mListener); (공급자, 1초, 0m, mListener);  시간과 거리는 AND가 아닌 OR. ex) 100초, 1000m 일경우, 1000m를 움직이지 않아도 100초 후에는 자동으로 갱신이 된다. 
- MainActivity에서 만든 메뉴 1번에 SearchLocation();을 연결한다. 메뉴 선택 후 '위치찾기'시 위치를 찾는중이라는 다이얼로그를 띄워준다. 
 위치를 찾으면 다이얼로그와 위치찾기를 해제시킨다. ( 배터리 소모 방지 ) 


DialogManager.java
import android.app.ProgressDialog;
import android.content.Context;

public class DialogManager {

	private Context context;
	private ProgressDialog mProgressDialog;
	
	public DialogManager(Context context) {
		this.context = context;
	}
	
	/* loading 구현*/
    public void Loading() {
    	mProgressDialog = ProgressDialog.show(context, null, "위치 정보 받는중...", true, false);
    }
    
    /* loading 끝 */
    public void LoadingEnd() {
    	mProgressDialog.dismiss();
    }
}
위치를 찾을때는 보여주고, 찾고 난 후 제거해 준다.





타이틀바를 없애도 풀스크린 사용.
단말기를 기울였을때 화면을 바꾸고 싶지 않거나, 쿼티자판을 뺏을때 화면이 갱신되는 것을 막는다.

<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name="Test" 
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:screenorientation="landscape" android:configchanges="orientation|keyboardHidden">
</activity>
</application>



'Android > Etc.' 카테고리의 다른 글

Max OS에서 NDK 개발하기  (1) 2012.07.04
넥서스S USB 드라이버 다운  (0) 2011.04.18
멀티터치 구현하기  (0) 2011.03.16
Permission  (0) 2011.01.26
데이터 저장 (SharedPreferences)  (0) 2011.01.25



enGLTest.java - 메인 액티비티
package pe.berabue.opengl;

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

public class OpenGLTest extends Activity {
    /** Called when the activity is first created. */
	
	public static GLGameSurfaceView mGLView;
	public static GLGameRenderer mGLRenderer;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 조명 항상켜기
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 
        
        mGLRenderer = new GLGameRenderer(this);
        mGLView = new GLGameSurfaceView(this);
        setContentView(mGLView);
    }
    
	@Override
	public void onPause() {
		mGLView.mGameThread.pauseThread();
		super.onPause();
	}

	@Override
	public void onResume() {
		mGLView.mGameThread.resumeThread();
		super.onResume();
	}
}


GLGameSurfaceView.java - 쓰레드가 있는 부분 
package pe.berabue.opengl;

import android.content.*;
import android.opengl.*;
import android.util.*;
import android.view.*;

public class GLGameSurfaceView extends GLSurfaceView{
	
	public static GameThread mGameThread;

	public static boolean isRun;
	public static boolean isWait;
	
	public GLGameSurfaceView(Context context) {
		super(context);
		setRenderer(OpenGLTest.mGLRenderer);
		setRenderMode(RENDERMODE_WHEN_DIRTY); // 한번만 그려줌
		setFocusableInTouchMode(true);
		
		mGameThread = new GameThread(context);
		mGameThread.start();
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		Log.v(null,"onKeyDown");
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		Log.v(null,"onTouchEvent");
		return super.onTouchEvent(event);
	}
	
	// Thread
	public class GameThread extends Thread {

		public GameThread(Context context) {
			isRun = true;
			isWait = false;
		}

		public void run() {
			while(isRun) {
				try {
					OpenGLTest.mGLView.requestRender(); // onDrawFrame
					mGameThread.sleep(10);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				synchronized (this) {
					if (isWait) {
						try { wait(); } catch (Exception e) {
							// 에러 무시
						}
	                }
				}//syn
			}
		}
		
		/* Thread pause */
		public void pauseThread() {
			isWait = true;
			synchronized (this) {
				this.notify();
			}
		}

		/* Thread Resume */
		public void resumeThread() {
			isWait = false;
	        synchronized (this) {
	             this.notify();
	        }
	    }
	}
}
setRenderMode(RENDERMODE_WHEN_DIRTY); 
GLGameRenderer의 그려주는 부분에서 1회만 그려준다. 파라메터를 RENDERMODE_CONTINUOUSLY로 바꿀 경우 계속 그리기.
1회만 그리는 이유는 멀티쓰레드를 사용하여 원활한 키 입력이나 터치를 받기 위해서 라는데.. 동기화는 다음에 생각하기로...

OpenGLTest.mGLView.requestRender();
requestRender();는 onDrawFrame();을 호출한다. GLGameRenderer의 onDrawFrame();호출.


GLGameRenderer.java - 실제로 그려지는 부분
package pe.berabue.opengl;

import javax.microedition.khronos.egl.*;
import javax.microedition.khronos.opengles.*;

import android.content.*;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.*;

public class GLGameRenderer implements Renderer {

	public GLGameRenderer(Context context) {
		
	}
	
	@Override
	public void onDrawFrame(GL10 gl) {
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);
		// 실제 그려지는 부분..
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {
		gl.glClearColor(0, 1, 0, 0.5f);	// RGBA 배경화면  0-> 1 점점밝아짐
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);	// ??
	}

}

Renderer을 구현(implemets)하게되면 기본적으로 위 3개의 메서드를 추가해야 된다.
onDrawFrame(); 실제로 그려지는 부분. 이 부분을 주기적으로 호출해 그림을 그려준다.
onSurfaceChanged(); 화면이 바뀔때 실행되는 부분.(실행시, orientation이 변경될때 등)
onSurfaceCreated(); 처음 실행 후 만들어질때 실행된다. (홈키를 누르거나 전화를 받고 다시 화면이 나올때도 실행)


GLGameRenderer에 조금 더 추가하면 기본적인 틀이 완성. 오늘은 여기까지.


실행화면

 

 




최종목표 : OpenGL을 사용하여 안드로이드용 간단한 2D, 3D 게임 만들기.


멀티터치는 2.1 이상의 버전에서 구현 가능하다.

	/* onTouch Event */
	public boolean onTouchEvent(MotionEvent e) {
		if ( e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_MOVE ) {
			int touchCount = e.getPointerCount();
						
			if ( touchCount > 1 ) {
				x1 = (int)e.getX(1);
				y1 = (int)e.getY(1);
			}
			else {
				x = (int)e.getX(0);
				y = (int)e.getY(0);
			}
		}
		return true;
	} // Touch Event 끝

간단한코드. e.getPointerCount()를 사용하여 몇개가 터치되고 있는지를 받아온다.
위 소스는 두개만을 받아왔다.
리스트를 사용하면 최대 지원가능한 수 까지 받아올 수 있을듯.

'Android > Etc.' 카테고리의 다른 글

Max OS에서 NDK 개발하기  (1) 2012.07.04
넥서스S USB 드라이버 다운  (0) 2011.04.18
Orientation 고정, 키보드 숨기기  (0) 2011.04.17
Permission  (0) 2011.01.26
데이터 저장 (SharedPreferences)  (0) 2011.01.25

+ Recent posts