網頁

2010年11月26日 星期五

Android OpenGl 初始化設定

OPENGL 劃一個三角形 首先需要設定 OPENGL的參數,因為我們使用3D 所以於設定的時候啟動深度緩衝,因為3D繪圖會遇到一個問題,那一個三角形要先畫哪個後畫,你會想說很簡單由遠的地方開始畫,然後再畫近的,這個想法不錯,但是如果這兩個三角形是有切面的時候這樣的遠近算法就會失效,解決這個方法最快最簡的方法就是深度緩衝,深度緩衝就是建立一塊和畫面相同大小的記憶體(Depth buffer),當三角形要畫上每一像素的時候就去檢查這個像素的深度是否比較靠近鏡頭,如果比較近就可以畫上該像素,這就是深度緩衝的原理。
不過原理不太需要了解要畫3D記得打開他。

OPEN GL 是一個有限狀態機,所謂有限狀態機就是當你設定之後他就不會變動,除非你下指令把它取消,所以下了指指令
gl.glEnable(GL10.GL_DEPTH_TEST); //啟動深度測試
表示已經啟動了深度緩衝。



//視窗改變大小
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
float ratio = (float) width / height;

gl.glViewport(0, 0, width, height); //設定opengl 繪圖視窗,全螢幕的話就是如左寫法
gl.glMatrixMode(GL10.GL_PROJECTION); //以下將設定opengl的 投射 參數(攝影機參數)
gl.glLoadIdentity(); //清除 投射矩陣
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); //設定可視座標也是重新定義座標,畫面座標 左邊 右邊 上邊 下邊 深度開始 深度結束

gl.glMatrixMode(GL10.GL_MODELVIEW); //表示任何的轉變將會影響到模型觀點的矩陣.
gl.glLoadIdentity(); //清除模型矩陣

}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);//設定最棒的顏色修正
gl.glClearColor(0.0f, 0, 0, 0); //清除背景顏色RGBA
gl.glShadeModel(GL10.GL_SMOOTH);//啟用平滑陰影

gl.glClearDepthf(1.0f); //清除深度緩衝
gl.glEnable(GL10.GL_DEPTH_TEST); //啟動深度測試
gl.glDepthFunc(GL10.GL_LEQUAL); //深度測試類型
}




Android OPENGL 程式架構



Android 要撰寫OPENGL
於CLASS 中要 implements Renderer
implements Renderer之後Eclipse 要求須要
import android.opengl.GLSurfaceView.Renderer;

然後必須建立三個事件,通常Eclipse會幫忙完成這件事情,
或是直接Copy 如下程式碼貼上使用


import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;

public class OpenglRendererClass implements Renderer{

@Override
public void onDrawFrame(GL10 arg0) {
// TODO Auto-generated method stub

}

@Override
public void onSurfaceChanged(GL10 arg0, int arg1, int arg2) {
// TODO Auto-generated method stub

}

@Override
public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) {
// TODO Auto-generated method stub

}
}

這樣就建立起了Android 使用 Open gl 的基本架構
onDrawFrame 這個方法會一直被呼叫,呼叫的速度我猜測大概是每畫完一次畫面,就立刻再度被呼叫,所以他可以看成更新畫面的迴圈。
所有畫圖的指令都在寫在這個方法裡面。

onSurfaceChanged這個我解釋成,當畫面大小變動的時候,例如第一次執行,畫面大小調整成全螢幕,當畫面大小改變的時候須要改變OPENGL畫面設定參數

onSurfaceCreatedy字面上的意義比較簡單,當OPENGL被建立的時候,這個方法裡拿來做一些OPENGL初始化設定的動作。


有了這個基本的CLASS之後,我們怎麼讓ACTIVE 顯示我們的OPENG呢



import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;

public class Opengl1 extends Activity {
/** Called when the activity is first created. */
Renderer render = new OpenglRendererClass();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

GLSurfaceView glView = new GLSurfaceView(this);
glView.setRenderer(render);
setContentView(glView);
}
}


什麼都沒有 黑黑的一片。 但是沒有出錯就是好的開始。

2010年10月30日 星期六

關於圖片隨著dpi改變

關於圖片隨著dpi改變
http://blog.k-res.net/?p=642 中有介紹
细分了很多文件夹处理以支持不同设备的分辨率加载对应的图片,如drawable-hdpi,drawable-ldpi,drawable-mdpi 等,如果没有注意这个问题而将贴图图片随意安置的话,在decode的时候系统会默认根据设备dpi的不同对目标图片格式解码的同时进行大小调整,也就是 说有可能破坏原本已经是2^n大小的贴图图片,导致原本在模拟器上正确的绘图在真机上变成大白板!
解决这个问题的方法可以将图片放到不受dpi影响的drawable-nodpi中

2010年10月27日 星期三

宅男要看,設計師更要知道

兩矩形快速偵測碰撞

兩矩形快速偵測碰撞公式
If (B.X1 > A.X2 Or B.Y1 > A.Y2 Or B.X2 < A.X1 Or B.Y2 < A.Y1) Then
碰撞
Else
沒碰撞
End If

2010年10月25日 星期一

Android 貼圖速度

以下由模擬器測試結果

一次的getPixel 是 0.06ms
一次的DrawRect(0,0,100,100) 一次大約是0.12ms (565的環境下)
一次的DrawRect(0,0,100,100) 一次大約是0.17ms (ARGB 4444 8888的環境下)

一次的DrawRect(10,10,210,210) 一次大約是0.25ms (565的環境下)

一次的drawBitmap(Plant, 0, 0, paint); 大約是0.38ms (RGB565的環境下) Png 200 *200 無透明
一次的drawBitmap(Plant, 0, 0, paint); 大約是0.39ms (RGB565的環境下) JPG 200 *200 無透明

一次的drawBitmap(Plant, 0, 0, paint); 大約是0.18ms (RGB565的環境下) JPG 100 * 100 無透明

一次的drawBitmap(Plant, 0, 0, paint); 大約是0.54ms (RGB565的環境下) PNG 100 * 100 有透明
一次的drawBitmap(Plant, 0, 0, paint); 大約是0.52ms (ARGB8888的環境下) PNG 100 * 100 有透明

一次的drawBitmap(Plant, 0, 0, paint); 大約是2ms (RGB565的環境下) PNG 200 *200 有透明
一次的drawBitmap(Plant, 0, 0, paint); 大約是1.5ms (ARGB8888的環境下) PNG 200 *200 有透明

一次的drawCircle(50, 50, 50, paint); 一次大約是0.37ms (RGB565的環境下)
一次的DrawText 大約是0.11ms (565的環境下)
一次的DrawText 大約是0.12ms (ARGB 4444 8888的環境下)

2010年9月3日 星期五

多國語言

多國語言的開發 以下網址介紹的很好
介紹給大家
JacobTsai's Blog

2010年8月5日 星期四

Android AlertDialog 教學(上)









AlertDialog(對話方塊)
很像是Android 的 MessageBox ,不過其實他的功能比windows上的 MessageBox 還要強大,且動作上不太相同。

1.windows的 MessageBox 只能顯示訊息,可以設定按鈕數量,
Android 的 AlertDialog 可以顯示訊息,還可以顯示列表,甚至可以是一個xml
也就是說,可以在layout編輯器裡新增一個表單(XML),裡可以放文字物件,EDITTEXT物件,按鈕物件,然後於AlertDialog 上顯示出來,也可以是或是選單(單選,複選都可)。

2.Windows上的MessageBox 是"阻塞"特性,就是當MessageBox 沒有被按下確定或是取消按鈕,程式不會繼續往下執行。
Android 的 AlertDialog 只是單存的對話方塊跳出來,程式會繼續往下執行,這裡還需注意。


1.先來依個最簡單的 對話方塊(AlertDialog)
程式碼如下

private void ShowAlertDialog()
{
Builder MyAlertDialog = new AlertDialog.Builder(this);
MyAlertDialog.setTitle("標題");
MyAlertDialog.setMessage("我是內容");
MyAlertDialog.show();
}





新增一個按鈕,加入可以更動訊息,讓這個對話方塊更好用
使用方法
ShowMsgDialog("哈哈哈");


private void ShowMsgDialog(String Msg)
{
Builder MyAlertDialog = new AlertDialog.Builder(this);
MyAlertDialog.setTitle("標題");
MyAlertDialog.setMessage(Msg);
DialogInterface.OnClickListener OkClick = new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which) {
//如果不做任何事情 就會直接關閉 對話方塊
}
};;
MyAlertDialog.setNeutralButton("中間按鈕",OkClick );
MyAlertDialog.show();
}






然而按鈕可以分成左邊,中間,右邊 來使用,
OkClick是按下時候處理的事件,因為示範所以只寫
OkClick
,實際上需要三個事件對應三個按鈕


//三個按鈕的對話方塊展示
private void ShowAlertDialogAnd3Button()
{
Builder MyAlertDialog = new AlertDialog.Builder(this);
MyAlertDialog.setTitle("標題");
MyAlertDialog.setMessage("我是內容");
//建立按下按鈕
DialogInterface.OnClickListener OkClick = new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which) {
//如果不做任何事情 就會直接關閉 對話方塊
}
};
MyAlertDialog.setPositiveButton("左邊按鈕",OkClick );
MyAlertDialog.setNeutralButton("中間按鈕",OkClick );
MyAlertDialog.setNegativeButton("右邊按鈕",OkClick );
MyAlertDialog.show();
}







對話方塊還可以使用LIST選單,以下示範單選
//有選單的對話方塊
final String[] ListStr = {"Android","其實","很簡單","且","如果選擇項目太多","Android也會","自動的可以拖曳喔!~","真好用"};

private void ShowAlertDialogAndList()
{

Builder MyAlertDialog = new AlertDialog.Builder(this);
MyAlertDialog.setTitle("標題");
//建立選擇的事件
DialogInterface.OnClickListener ListClick = new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which) {
ShowMsgDialog(ListStr[which]);

}
};
//建立按下取消什麼事情都不做的事件
DialogInterface.OnClickListener OkClick = new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which) {
}
};
MyAlertDialog.setItems(ListStr, ListClick);
MyAlertDialog.setNeutralButton("取消",OkClick );
MyAlertDialog.show();
}


項目太多自動生成可以拉動的選單。

2010年7月16日 星期五

關於ListView

這裡有一篇好文章,http://www.javaeye.com/topic/540423


寫慣了windows上的程式,一直以為ListView是一個物件,
研究之後發現他比較像是一個佈局,這個佈局裡可以存放各式樣的XML
所以MARKET是ListView,YOUTUBE DOWNLOADER也是ListView的使用,很難想像它們相同ListView
所以ListView也不會太簡單的讓我想加文字就像windows這樣隨便加,
詳細的狀況等我學會一點再來心得分享一番。

2010年7月14日 星期三

Android軟體生命週期

Android 主要是使用在手機上,所以應用程式必須要對於程式中斷做出相對應的政策,才是好的程式,要不然電話一來,程式就當機,或是引響到手機通話功能,最糟的是,當程式離開了,還有一個無窮回圈一值在背景執行,導致手機很快就沒電,這樣的設計都是很不好的。

所以了解Android的生命週期很重要,我想要以CASE的方式介紹生命週期
了解生命週期之前必須先了解Android 並不會關閉程式,執行程式的時候中途按下 BACK 或是 MENU離開程式回到桌面時,程式依然會保留在記憶體內沒有被釋放,所以才會有task kill 這類程式來強制關閉應用程式。

心法:
畫面失去焦點 啟動pause 回到焦點 resume
畫面被蓋掉 啟動 Stop 畫面回來 START




1.當程式啟動的時候執行三個流程
OnCreat->OnStart -> OnResume

2.這時候按下Back 回到畫面 執行三個流程
OnPaus-->OnStop -->OnDestory

3.啟動另外一個ACTIVE

舊Active pause --> 新Active on Creat --> 新Active onStart --> 新Active onResume 舊Active Stop

4.程式執行時按下電源鈕讓手機進入休眠
Active pause

5.程式由休眠再度打開電源
Active Resume


6.程式執行時電話來了
Active onPause-->Active OnStop

8.接聽後掛掉電話
ReStart -->Start-->Resume

9.手機簡訊來了
不會暫停程式

10.手機撥來只響一聲就掛掉
Active onPause--> Resume

11螢幕切換橫向縱向,基本上會先強制關閉程式,然後再度讀取所以會
onPause-->OnStop-->OnDestory-->OnCreat->OnStart -> OnResume



值得注意的是
onStop OnPause 程式的所有變數與物件都沒有被釋放,
都還是保持原來的這還好處理。

有時候程式裡會有兩三個Active 切換的時候會呼叫OnCreat
當 OnCreat的時候可怕的事情發生了,所有變數都回復到預設值,
雖然他並沒有被OnDestory但是他被OnCreat了,
所有的變數重新再度被定義為初始狀態,這是值得注意的。

2010年7月2日 星期五

Android 動態的增加物件以按鈕為例子

寫了一範例

這個範例 一開始有一個按鈕, 按一下新增一個按鈕
按下新的按鈕則顯示誰被按了

歡迎來我的部落格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

//原創作者 軟貓軟體 http://toimy.blogspot.com/
public class AddButtonActivity extends Activity implements OnClickListener {
LinearLayout layout;
private Button btn1;
private int CurrentButtonNumber = 0; //CurrentButtonNumber流水號 設定物件ID
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//先建立一個 面板放置所有元件
layout= new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL); //面板的擺設方式為垂直
btn1 = new Button(this);
btn1.setId(CurrentButtonNumber);
CurrentButtonNumber++;
btn1.setText("SoftCat Go Button");
btn1.setOnClickListener(this); //如果要這樣寫 需加入 implements OnClickListener 於 Activity
layout.addView(btn1, 150, 50); //addView(物件,寬度高度)
setContentView(layout); //設定畫面顯示自己的面板
}
public void onClick(View v)
{
switch(v.getId()){

case 0://增加按鈕
Button TmpBtn = new Button(v.getContext());
TmpBtn.setText("I am Button" + String.valueOf(CurrentButtonNumber) );
TmpBtn.setId(CurrentButtonNumber);
TmpBtn.setOnClickListener(this);
LinearLayout.LayoutParams param =new LinearLayout.LayoutParams(100,50);
CurrentButtonNumber++;
layout.addView(TmpBtn, param);
break;
default://其他按鈕
alertbox("I be hit.....oh...","you hit me . I am "+ String.valueOf(v.getId()));
break;
}
}
//顯示對話方塊
protected void alertbox(String title, String mymessage)
{
new AlertDialog.Builder(this)
.setMessage(mymessage)
.setTitle(title)
.setCancelable(true)
.setNeutralButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){}
})
.show();
}
}

2010年6月23日 星期三

關於介面與執行序Only the original thread that created a view hierarchy can touch its views.

Android 是一個多功多執行的作業系統,雖然可以多執行緒
但是遵循著mvc模式 ,所以當非UI的執行序,嘗試去改變ui介面的程式就會出現錯誤而關閉程式
例如

寫了一個遊戲,遊戲的核心是一個執行緒,定期需要計算遊戲的內容,且需要定時的更新畫面,然而跟跟新畫面,需要呼叫 invalidate 這個方法,偏偏這個方法使會變更介面,所以android只好把你強迫關閉程式。告訴你Only the original thread that created a view hierarchy can touch its views

最簡單的方法就是不要使用 invalidate 改用 postInvalidate();

但只是剛好invalidate 有 postInvalidate(); 可以幫助更新畫面,但是如果要更變tTextBox內容,或是 setContentView(view); 那可就沒那麼幸運了。

事實上這樣龜毛的規則並不是Android訂製的,c#的開發使用者也是有著相同的困腦,
但在c# 會使用委派來將 要改變介面的事情 透過委派 交給 UI的執行序去執行。

然而在Android 使用了比較值覺得寫法
runOnUiThread(new Runnable(){
public void run() {
//要改變介面的程式寫在這裡
}});

這樣android就會裡面的程式碼,改由ui的執行序來執行,而不會出錯了,
所以類推本來寫postInvalidate(); 可以改寫成

runOnUiThread(new Runnable(){
public void run() {
invalidate
}});



通常錯誤如下:
06-23 15:47:40.123: ERROR/AndroidRuntime(393): Uncaught handler: thread Thread-10 exiting due to uncaught exception
06-23 15:47:40.135: ERROR/AndroidRuntime(393): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

2010年6月19日 星期六

Android 多點觸控

Android 的多點觸控以現在的api來看 最多3點觸控
於程式中加入
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
即可抓取觸控的座標以及事件
x[0] = (int) event.getX(0); //單點觸控的座標
y[0] = (int) event.getY(0);
x[1] = (int) event.getX(1); //第二點觸控的座標
y[1] = (int) event.getY(1);
x[2] = (int) event.getX(2);//第三點觸控的座標
y[2] = (int) event.getY(2);

可惜的是我拿得是 DESIRE 最多兩點觸控,第三點是沒有作用的,且有交叉誤判的現象

抓取事件
if (event.getAction() == MotionEvent.ACTION_MOVE )
{
這個事件在模擬與實機有很大的出入,模擬器只會移動一下觸發一次,而實機摸下去之後不管有沒有移動 都會瘋狂的觸發。
}
if (event.getAction() == MotionEvent.ACTION_DOWN )
{
指尖按下
}
if (event.getAction() == MotionEvent.ACTION_OUTSIDE )

if (event.getAction() == MotionEvent.ACTION_POINTER_1_DOWN )
基本上 如果使用了 MotionEvent.ACTION_DOWN 這個事件永遠不會觸發
if (event.getAction() == MotionEvent.ACTION_POINTER_2_DOWN )
第二點按下
if (event.getAction() == MotionEvent.ACTION_POINTER_3_DOWN )
{
第三點按下
}
if (event.getAction() == MotionEvent.ACTION_POINTER_3_UP )
{
第三點放開 兩點觸控的 也不會產生這個事件
}



好了看完很多疑問,為什麼point 1 23 的 move
兩點原因
1.不管是哪個點移動 都是觸發 move 旗標
2.按下去之後就瘋狂觸發所以乾脆三點都檢查


move up 1 2
要由一系列動作解釋

按下第一鍵
觸發 down

按下第二鍵
觸發 point 2 down

放開第二鍵 觸發
觸發 point 2 up

放開第一鍵 觸發 up (注意不是point 1 up)



再次實驗

按下第一鍵
觸發 down

按下第二鍵
觸發 point 2 down

放開第一鍵 觸發
觸發 point 1 up
這時候 本來的point 2 變成point 1 座標 也變成由point 1取得雖然他應該是第二鍵

放開第一鍵 觸發 up (注意不是point 2 up)

Android 常用小技巧

//全螢幕
貼再 Activity 的onCreate
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN , WindowManager.LayoutParams.FLAG_FULLSCREEN);


//強迫橫銀幕
在AndroidManifest.xml 中的 的 activity 屬性宣告


軟貓玩具
http://www.blogger.com/

//開啟使用網路權限
在AndroidManifest.xml 中的加入


//不讓手機自動進入休眠,持續背光
1.先在
在AndroidManifest.xml 中的加入


2.在程式中宣告
private PowerManager.WakeLock mWakeLock = mPowerManager.newWakeLock(mPowerManager.SCREEN_BRIGHT_WAKE_LOCK, "BackLight");
啟動持續背光模式
mWakeLock.acquire(); 通常加在 protected void onResume() 要不然
離開程式返回的時候效果就消失了

關閉持續背光
mWakeLock.release(); 通常加在 protected void onPause()
否則程式離開程式之後手機一直耗電

//使用GPS
在AndroidManifest.xml 中的加入



程式中宣告
LocationManager locationManger = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

註冊gps 觸發事件為 LocationListener
locationManger.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, LocationListener); //1000ms 跟新一次 時間越短越耗電

private final LocationListener LocationListener = new LocationListener()
{

@Override
public void onLocationChanged(Location location)
//GPS移動的時候
@Override
public void onProviderDisabled(String provider)
@Override
public void onProviderEnabled(String provider)

@Override
public void onStatusChanged(String provider, int status, Bundle extras)
//gps定位完成沒偵測
};

2010年3月29日 星期一

Android 貼圖教學,透明貼圖教學





遊戲設計最重要的一門課題,貼圖
觀看了月球登入的sample遊戲之後,發現月球登入使用了兩種方式的貼圖
貼圖之前必須要先將圖形放到資源檔裡,專案資料夾下方的res裡的drawable
到了2.0之後的版本,android會自動分出三個資料夾 drawable-h m L dpi 分別是讓開法者存放
不同解析度的圖形,因為現在手機的解析度已經亂的可以,低階手機到高階手機解析度都不一
該怎麼用我也還沒學到,實驗之後發現不管放在哪個資料夾都會自動更新到R.java裡面

關於該放什麼圖檔,還有透明的問題,Android 的貼圖看Sample裡並沒有特別寫到透明的程式碼
如果是Directx 的貼圖還需設定圖形透通顏色色定,不過在Android裡看來是不用寫任何程式碼,只要存放png圖檔,png圖檔本身就支援透明色,設定的透明色於android中的貼圖就自動的透明貼圖了,使用上還蠻方便的,不過這樣反而有個疑問是如果這張圖形是動態產生的,且想要透明怎麼辦
阿栽....... 有需要再來說。
先使用 jetboy裡的太空船,
把他copy到drawable-hdpi
第一種貼圖 宣告成
private Drawable PIC1;
取得資源檔圖形放入Drawable中
PIC1 = this.getResources().getDrawable(R.drawable.ship);

貼圖的方式
PIC1.setBounds(左x, 上y, 右x, 下y); //設定貼出範圍, 自動縮放圖形寬與高
PIC1.draw(canvas); //將圖形貼於canvas 畫布上


第二種貼圖方式
宣告成Bitmap 格式
private Bitmap PICBitmap;
取得資源檔圖形,轉換成Bitmap
PICBitmap = BitmapFactory.decodeResource(this.getResources() ,
R.drawable.ship);
//於x,y 貼出 沒有使用縮放圖形,原圖貼出
canvas.drawBitmap(PICBitmap, x, y, null);



完整程式碼
package com.DrawTest;


import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;

public class DrawTest extends Activity {
/** Called when the activity is first created. */
private Drawable PIC1;
private Bitmap PICBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyView myview = new MyView(this);
setContentView(myview);
PIC1 = this.getResources().getDrawable(R.drawable.ship);
PICBitmap = BitmapFactory.decodeResource(this.getResources() ,
R.drawable.ship);
}

private class MyView extends View
{
public MyView(Context context)
{
super(context);
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
canvas.drawCircle(120, 40, 30, paint);
canvas.drawRect(90,170,150,200, paint);
PIC1.setBounds(10, 10, 100, 100);
PIC1.draw(canvas);

PIC1.setBounds(120, 120, 200, 200);
PIC1.draw(canvas);

canvas.drawBitmap(PICBitmap, 0, 200, null);

}
}
}

附上小技巧 去掉標題bar的寫法圖片 與飛行船的貼圖測試

2010年3月25日 星期四

Android 繪圖

package Com.DrawTest;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.os.Bundle;
import android.view.View;

public class DrawTest extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyView myview = new MyView(this);
setContentView(myview);
}

private class MyView extends View
{
public MyView(Context context)
{
super(context);
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
canvas.drawCircle(120, 40, 30, paint);
canvas.drawRect(90,170,150,200, paint);
//canvas.drawBitmap(bitmap, matrix, paint)
}
}
}

2010年3月24日 星期三

Android Socket教學

Android是一隻網路功能強大的手機作業系統,網路的撰寫不算困難也很好玩
特別是連線對戰的遊戲,一群朋友一起拿著自己的手機和對方廝殺,這個畫面一定很有趣
今天先來解說一寫我在學習撰寫網路連線的時候遇到的一些困難和解決方法
介紹伺服端ServerSocket 物件
網路的連線一定要有一端當作主機(Server),一端當作客戶 (Client),Android裡將這兩種
連線方式分成兩種物件,分別是ServerSocket 和 Socket , 如果要開發主機端就使用ServerSocket物件, 開發客戶端就用Socket.

通常伺服端的撰寫流程都是 宣告ServerSocket物件->實體化並且傳入通訊Port->Accept等待連線->產生新的執行續,將Socket傳給新的執行續-> 回到Accept等待


ServerSocket主要功能就是聆聽Client的要求,並且產生新的socket和Clinet連線, ServerSocket的工作就告一段落,重新再度回到等待,至於和Clinet之前的傳送接收則是由新的新的執行續,和client去溝通。

宣告物件實體化如下
import java.net.ServerSocket;
public static final int SERVERPORT = 7777;
ServerSocket serverSocket = new ServerSocket(SERVERPORT);

會發這樣寫,於執行階段會產生一個權限問題,
Permission denied (maybe missing INTERNET permission)


只要在AndroidManifest.xml 裡設定新增 uses-permission 名字為 android.permission.INTERNET
或是於xml裡直接輸入


即可。

接下來
Accept等待連線->產生新的執行續,將Socket傳給新的執行續
Socket NewSocket = serverSocket.accept();

serverSocket.accept();會將程式阻塞,程式會停止在這裡不動,所以最好在使用serverSocket.accept()是一個不同於main的執行序,程式會一直等到有使用者者嘗試連線
程式才會繼續往下跑,並且產生一個NewSocket物件與 使用者進行互動連線

這時候如果還有其他使用者來要求連線,程式沒有辦法接受,要接受使用者要求連線必須讓程式
停在serverSocket.accept(); , 所以不建議accept之後就開始接收資料與回應資料,應立即產生新的執行序 並將NewSocket 交付出去,讓新的執行序執行傳送接收工作,並立刻回到serverSocket.accept();繼續等待連線,才可以服務多數使用者。

弄到這裡我們應該可以連線到android ,如果是實體手機是可以的,但如果是模擬器
需要如下動作

1.先啟動模擬器
2.再開啟 windows的CMD 執行 telnet 127.0.0.1 5554
5554代表模擬器的port
3.輸入
redir add tcp:7777:7777
打完之後 顯示ok表示成功。

這麼做的意思是,這台電腦接收到7777 port的資料請轉給 模擬器7777 裡面
例如再ie瀏覽器上 http://127.0.0.1:7777 使用debug就可以看到資訊已經送到模擬器裡



關於傳送接收,下回繼續。

2010年3月17日 星期三

MAC電腦視窗畫面截取,MAC畫面拍照

windows下想要截取視窗(拍照)只要按下print screen鍵就可以
但是在MAC下要拍螢幕上的畫面使用 Command + Shift + 4 三個按鈕
這時候滑鼠就會變成坐標的方式可以圈選所要拍照的範圍
如果要像windows那樣可以對單一視窗拍照,只要按下空白鍵
就可以對單一視窗拍照。

只是問題來了,畫面拍完了,照片在哪裡阿?
MAC的小畫家在哪裡呢?
這時候需要一套繪圖軟體,推薦GIMP
他是一套和photoshop可批敵的軟體,重點是他是免費且跨平台的繪圖軟體
不過下載的過程當中呢喃一下
windows xp 跳過vista 失敗的其中一項原因就是軟體支援度的問題
很多軟體在vista下沒有辦法執行,
但是在蘋果這樣的問題似乎是不被重視的,apple喜歡自斷筋骨
我改了作業系統,之前的程式幾乎都不能相容,強迫所有軟體廠商重新撰寫
其實這是很糟糕的現象.......

所以GIMP也要特地為 10.6 出一個版本,還好有出,要不然我也用不到這套軟體
抓以前的版本是沒有辦法相容的。


安裝完成GIMP就可以將拍照的話面貼到gimp中,然後存檔。




2010年3月15日 星期一

C#, 檔案是否存在,資料夾是否存在

加入參考
using System.IO;

判斷目錄是否存在

//判斷C槽是否存在
if (Directory.Exists("C:"))
{
//有
}

判斷檔案是否存在

//判斷執行檔路徑裡檔案CMD.txt是否存在
if (System.IO.File.Exists(Application.StartupPath + @"\COM.TXT"))
{
//有
}

C#儲存圖檔-小畫家3

儲存圖檔之前要先知道要儲存的檔案格式(BMP JPG GIF PNG)
而儲存圖檔的方式也很簡單,只要對BitMap 物件作Save 就可以
因為之前我們的範例都是使用BitMap 當作PictureBox的 Buffer
現在只要對Buffer 做SAVE就是把PICTUER 的圖形做儲存,
得知現在儲存的圖形格式有兩種方式,第一個方式是 對話方塊的過慮器
           saveFileDialog1.Filter = "逼恩批|*.bmp|傑批居|*.jpg|批恩居|*.png"; //檔案過濾器  
saveFileDialog1.InitialDirectory = Application.StartupPath; //程式啟動路徑,為預設路徑
saveFileDialog1.FileName = ""; //清除預設檔名
saveFileDialog1.ShowDialog(); //顯示對話方塊

if (openFileDialog1.FileName.Equals(""))
{
MessageBox.Show("取消選取");
}
else
{
switch (saveFileDialog1.FilterIndex)
{
case 1:
buffer.Save(saveFileDialog1.FileName , System.Drawing.Imaging.ImageFormat.Bmp);
break;
case 2:
buffer.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Jpeg );
break;
case 3:
buffer.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Gif );
break;
default:
break;
}
}

第二種方式,取出字串
再由字串比對

saveFileDialog1.Filter = "逼恩批|*.bmp|傑批居|*.jpg|批恩居|*.png"; //檔案過濾器  
saveFileDialog1.InitialDirectory = Application.StartupPath; //程式啟動路徑,為預設路徑
saveFileDialog1.FileName = ""; //清除預設檔名
saveFileDialog1.ShowDialog(); //顯示對話方塊

if (openFileDialog1.FileName.Equals(""))
{
MessageBox.Show("取消選取");
}
else
{
switch (saveFileDialog1.FileName.Substring(saveFileDialog1.FileName.Length - 3, 3).ToUpper())
{
case "BMP":
buffer.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
break;
case "JPG":
buffer.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
break;
case "GIF":
buffer.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Gif );
break;
case "PNG":
buffer.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Png );
break;
default:
MessageBox.Show ("錯誤檔名");
break;
}
}
看起來是第一種寫法比較好,第二種寫法單存練習,如何取出最後三個字元,然後將其大寫

2010年3月14日 星期日

C#讀取圖形-小畫家2

C#讀取圖形有點意外的簡單
於上一個章節已經學會對會方塊,現在我們使用對話方塊來讀取圖形
Graphics g = this.pictureBox1.CreateGraphics();
Bitmap br = new Bitmap(openFileDialog1.FileName) ;
//放置您所指定的圖片
//並指定圖片要放置的位置,(X,Y) = (0,0)
g.DrawImage(br, 0, 0);
支援的圖檔名稱還真不少,GIF.JPG.JPEG.BMP.WMF.PNG

不過這樣寫有個問題,就是畫面會消失,其他視窗蓋到畫面,圖形就被吃掉了,
用一個簡單的方法解決,建立一個Bimmap 對應到PictureBox.Image
只要PictureBox 被其他視窗擋到,PictureBox 自動重繪的時候就會去Image抓圖,

    public partial class Form1 : Form
    {
        Bitmap buffer;
        public Form1()
        {
            InitializeComponent();
            buffer = new Bitmap(pictureBox1.Width, pictureBox1.Height ); 
        }
        private void OpenFileButton_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "逼恩批|*.bmp|傑批居|*.jpg|批恩居|*.png"; //檔案過濾器
            openFileDialog1.Multiselect = false;                  //取消多從選擇 = 單選   
            openFileDialog1.InitialDirectory = Application.StartupPath;   //程式啟動路徑,為預設路徑
            openFileDialog1.FileName = "";                              //清除預設檔名
            openFileDialog1.ShowDialog();                                    //顯示對話方塊
            if  (openFileDialog1.FileName.Equals( "" ))
            {
                MessageBox.Show("取消選取" );  
            }
            else
            {
                buffer = new Bitmap(openFileDialog1.FileName);
                //放置您所指定的圖片
                //並指定圖片要放置的位置,(X,Y) = (0,0)
                pictureBox1.Width = buffer.Width;
                pictureBox1.Height = buffer.Height;
                pictureBox1.Image = buffer;
            }
        }
    }

C#對話方塊

我是一個不會C#的人
跟著書去學C# 很無趣,於是給自己定了一個目標,寫一個小畫家
第一部先建立一個介面,先學習怎麼使用C# 工作列
工作列這樣就可以了,使用起來就像是按鈕一樣。接著學習怎麼呼叫 WINDOWS的開啟檔案對話方塊
openFileDialog1.Filter = "逼恩批|*.bmp|傑批居|*.jpg|批恩居|*.png"; //檔案過濾器
openFileDialog1.Multiselect = false; //取消多從選擇 = 單選
openFileDialog1.InitialDirectory = Application.StartupPath; //程式啟動路徑,為預設路徑
openFileDialog1.FileName = ""; //清除預設檔名
openFileDialog1.ShowDialog(); //顯示對話方塊 if (openFileDialog1.FileName.Equals( "" ))
{
MessageBox.Show("取消選取" );
}
else
{
MessageBox.Show(openFileDialog1.FileName );
}

C#用起來就是這麼有成就感。

C#二進位檔讀取 存檔 文字檔讀取 存檔

對於檔案的處理,很多人都很害怕,特別是二進位存取,
聽到二進位的存取就莫名的害怕,
不過於C#這些東西都變的非常的簡單,因為C#的函數都幫使用者作好了
不需要瞭解太多,只需要會用就可以,至於怎麼撰寫不再多說因為書上很多
今天要說的是整理成一些方便的函數

成功失敗 = 讀取二進位檔( 路徑檔名 , ref 讀取的結果 )
成功失敗 = 寫入二進位檔( 路徑檔名 , 寫入的陣列 )
成功失敗 = 讀取文字檔( 路徑檔名 , ref 讀取的文字 )
成功失敗 = 寫入文字檔( 路徑檔名 , 寫入的文字 )

這裡大概有些人會驚訝,如果檔案很大,那陣列或是字串會不會死掉,
在C#裡的陣列大小可以定到 2G,也就是檔案如果小於2G都可以讀取近來
ㄧ個String的變數其實可以放到記憶體爆掉,不過當然我們不會做這麼危險的事情
但是把ㄧ整本書的文字都放在一個string的變數裡是輕而ㄧ舉的喔

所以就大膽的用下去,




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace AkingTool
{
class FileIOTool
{
public static int BinWrite(string SaveFileName, byte[] InData)
{
try
{
//開啟建立檔案
FileStream myFile = File.Open(SaveFileName, FileMode.Open , FileAccess.ReadWrite);
BinaryWriter myWriter = new BinaryWriter(myFile);
myWriter.Write(InData);
myWriter.Close();
myFile.Close();
return 1;
}catch (InvalidCastException e)
{
return -1;
}

}

public static int BinRead(string OpenFileName, ref byte[] InData)
{

try
{
//開啟檔案
FileStream myFile = File.Open(OpenFileName, FileMode.Open , FileAccess.ReadWrite);
//引用myReader類別
BinaryReader myReader = new BinaryReader(myFile);
int dl = System.Convert.ToInt32 (myFile.Length);
//讀取位元陣列
InData = myReader.ReadBytes(dl);
//讀取資料
//釋放資源
myReader.Close();
myFile.Close();
return 1;
}
catch (InvalidCastException e)
{
return -1;
}
}

public static int StringWrite(string SaveFileName, string InData)
{
try
{
//建立檔案
FileStream myFile = File.Open(SaveFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamWriter myWriter = new StreamWriter(myFile);
//建立位元陣列
myWriter.Write(InData);
//釋放資源
myWriter.Close();
myFile.Close();
return 1;
}
catch (InvalidCastException e)
{
return -1;
}
}

public static int StringRead(string OpenFileName, ref string InData)
{
try
{
//開啟檔案
FileStream myFile = File.Open(OpenFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
//引用myReader類別
StreamReader myReader = new StreamReader(myFile);
int dl = System.Convert.ToInt32(myFile.Length);
//讀取位元陣列
InData = myReader.ReadToEnd();
//讀取資料
//釋放資源
myReader.Close();
myFile.Close();
return 1;
}
catch (InvalidCastException e)
{
return -1;
}
}
}
}
寫個範例讀寫一下



private void button1_Click(object sender, EventArgs e)
{
string G;
G = "abcdefghijklmnopqrstuvwxyz\nabcdefghijklmnopqrstuvwxyz";
AkingTool.FileIOTool.StringWrite(@"c:\kkk", G);
}

private void button2_Click(object sender, EventArgs e)
{

string K = "";
AkingTool.FileIOTool.StringRead(@"c:\kkk",ref K);
MessageBox.Show(K);
}

private void button3_Click(object sender, EventArgs e)
{
byte[] S = new byte[1024 * 1024]; //寫出1M的檔案
S[10000] = 200;
AkingTool.FileIOTool.BinWrite(@"C:\Bin.bin", S);
}

private void button4_Click(object sender, EventArgs e)
{
byte[] ReadS = new byte[0]; //'讀取1M 的檔案,大小先設定為0 後來自動調整為 1M
AkingTool.FileIOTool.BinRead (@"C:\Bin.bin",ref ReadS);
MessageBox.Show ( ReadS[10000].ToString() );
}

2010年2月28日 星期日

適應MacBook Pro

2009/12/13日買了這台MacBook Pro 買來之後就被我冷落在桌上
冷落的原因是因為他是 麥金塔系統,很不習慣,公司要求一定要買一套WINDOWS的正版軟體
現在市面上也買不到XP,所以買了WIN7 專業版,回來使用了boot camp灌進去之後
發現很多問題,win7 不能灌vb6 , 挖~公司的工作也不能做,
所以每天帶去公司開機,然後下班關機帶回家,覺得這台電腦很礙眼............
我想說到這裡很多蘋果死忠愛好者會很不爽.....
經過了三個月,這幾天,因為舊電腦實在是慢到不行,所以花了幾天的時間
決定放棄WIN7把我的mac 灌成winxp,灌成xp以為什麼事情都解決了
發現今天肩膀好痛,因為老是按錯鍵,壓力更大了
我的工作是程式設計師,上班就是猛敲程式,經常使用到home pause end 這些特殊案件
而這些按鍵mac book pro 上是沒有的,要用組合鍵
所以如果想要全選這一行
就要按 fn + Shift + 左鍵 ,
一下子之間趕起程式,一直在找按鍵壓力就顯示在肩膀上了
不過有些感覺閃過腦中

適應就是從不習慣到習慣的過程
而人生就是要適應人必須學會適應
說起來很饒舌,簡單來說就是活到老學到老
學習vb6的時候.Net就出來了,現在已經到.net 2010了可怕喔
永遠來不及學新的,不過這是一個過程,永遠都要學習,不要拒絕
人生就是要適應人必須學會適應

最近就會開始學習開發iphone,再貼上幾篇教學吧