2012-03-17

使用 SurfaceView 介面來處理貼圖

如果不使用 Android 現成的 Widget (ImageView 與 ImageButton) 來處理圖片的話, 我覺得要在 Android 中貼圖並不是很方便, 從引用圖片來源開始, 一直到圖片的呈現, 整個過程十分冗長; 況且處理圖形的指令總是一長串 ... 但是, 現成的 Widget 也不一定能符合設計者的需要, 當應用程式需要較高的顯示效率與較獨特的操作介面時 (如遊戲軟體), 那麼那些方便的 Widget 就不太適合了 !

這篇的重點就在於使用 SurfaceView 介面來進行貼圖的動作, SurfaceView 介面最適合開發顯示速率較快的應用程式, 不過, 使用這個介面來處理圖片並不輕鬆 ! 就像我前面說過的那樣, 而且整個過程一開始並不容易理解, 一時之間也不容易解釋清楚, 那 ~ 有沒有淺顯易懂的方式 ? 我的答案是 -- 沒有 ! 建議你, 只有透過不斷的練習, 才能熟練它 ! 進一步理解它 !

我使用 SurfaceView 介面貼了一張圖, 如下 :

只貼一張圖, 座標 x : 50 , y : 100

程式碼如下 : (紅色字體為重點)
大部份的註解我順便寫在程式碼內了

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

==========================================

// 另一個類別 surfaceviewTest
public class DrawTest extends SurfaceView implements SurfaceHolder.Callback {
   
    //呼叫getHolder()方法來取得 SurfaceHolder,並指給 holder
    SurfaceHolder holder = getHolder();
    Bitmap bp;
    Canvas canvas;
    int x=50,y=100;  //貼圖在螢幕上的 x,y 座標

    // DrawTest 建構子
    public DrawTest(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
       
        //把這個 class 本身(extends SurfaceView)
        //透過 holder 的 Callback()方法連結起來
        //下面這行也可寫成 getHolder().addCallback(this);
        holder.addCallback(this);
       
        //設定圖片來源(此處使用預設的圖片)
        bp = BitmapFactory.decodeResource(getResources(),
                                                  R.drawable.icon);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // TODO Auto-generated method stub

        //在 canvas 畫布上貼圖的三個步驟

        //1. 鎖住畫布
        canvas = holder.lockCanvas();
        //2. 在畫布上貼圖
        canvas.drawBitmap(bp,x,y,null);
        //3. 解鎖並po出畫布
        holder.unlockCanvasAndPost(canvas);
    }
   
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO Auto-generated method stub
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
    }
}
==============================================

與 SurfaceHolder.Callback 相關聯的三個程序 :
public void surfaceCreated(...) -- surface 建立時
public void surfaceChanged(...) -- surface 改變時
public void surfaceDestroyed(...) -- surface 結束時

上述貼圖的主要步驟是寫在 surfaceCreated 程序中
一張靜態的圖是看不出 SurfaceView 的能力的, 如果要讓圖 "動"起來, 則還需要配合執行緒的運用, 透過執行緒不斷去改變圖片的座標並反覆貼上圖片, 就可以達到動態的效果囉 ! 想朝 Android App 遊戲相關的應用軟體發展, 練習在 SurfaceView 中貼圖只是一個開端, 先從這個基礎出發, 再配合物件導向的程式架構及執行緒的應用, 才能建構出一個完整的應用程式 .^_^.

您可以延伸閱讀本部落格使用 SurfaceView 類別的其他範例 :

動態物件的產生與捕捉
模擬物件的生命週期
模擬拖曳小圖示的 Android 程式

2012-03-16

Android 的 xml 檔案 (三) AndroidManifest.xml

AndroidManifest.xml 檔, 或稱為 [ 應用程式設定檔 ], 這個 xml 檔比較特殊一點, 每個應用程式都需要配置一個 Manifest.xml 檔, 如果要簡單解釋它的功用, 你可以把它想成是 [ 應用程式 ] 在執行前與 [ Android 系統架構 ] 之間所訂定的一種規範準則.

嗯 ... 講白話一點的話 ~ 就好比你進到電影院裡面, 按照你想看的電影與場次, 買了電影票, 然後你循著電影票上的資訊 : 場次、入場時間、哪一廳、座位號碼 ... 從進場就座到看完電影, 走出戲院. 這整個流程 ~ 你自己就好比是一個 [ 應用程式 ], 而整個電影院就好比是 [ Android 系統架構 ], 那張電影票就是 [ Manifest.xml 檔 ], 試想如果沒有遵循那張電影票上的資訊, 你在電影院內又該如何順利欣賞到電影 ? 這樣有沒有概念了呢 ? 哈哈哈 ~ 當然這只是比喻而已, 實際上還要更複雜些 ! >"<

先想像你自己是個 [ 應用程式 ], 這張就是你的電影票 ~ 哈哈哈!

在這裡僅就其中主要的幾個部份做介紹 :

 <manifest xmlns:android="http://schemas...(略)"
      package="a.b.c"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="7" />
    ......... (略, 內容列在下方)
</manifest>

開頭是以 <manifest>   .....  以 </manifest> 為結尾
代表此檔案為 Manifest.xml, 上述區段中包含了版本號碼之類的資訊.

<uses-sdk android:minSdkVersion="7" />
這行宣告, 表示你的應用程式適用在哪個 Android 系統版本上運作. 這個重要 ! 藉此聲明低於這個版本代號的 Android 系統就不適合安裝此應用程式囉 ! 上述的版本代號為  7 ( 對應版本實際為 2.1, 在此檔案中僅以代號表示 ) 在創建新專案時已先指定此參數了, 不需在此做更動.

下方是僅包含一個 activity 的應用程式設定檔內容 :

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

開頭以 <application> .... </applicatio n> 做結尾, 表示此區段的內容與應用程式有關.

<application> ~ </application>區段內各行的簡介:

android:icon="@drawable/icon"
此應用程式的圖示

android:label="@string/app_name"
此應用程式的名稱, 由於名稱已寫入 strings.xml 檔中, 所以在此看不到 ~ 此範例的 @string/app_name 變數內容為 "StrAndInt"

<activity android:name=".StrAndInt"
activity 在程式內的名稱為 "StrAndInt", activity 可想成是應用程式的初始架構.

android:label="@string/app_name">
activity 顯示在外的名稱, 此名稱已寫入 strings.xml 中, 此變數內容是 "StrAndInt"

<intent-filter> .... </intent-filter>
定義此 activity 的性質與內容

<action android:name="android.intent.action.MAIN" />
程式的進入點 (表示開頭由此 activity 先執行)

<category android:name="android.intent.category.LAUNCHER" />
應用程式出現的位置 ... 這個用圖說明比較清楚 ~ 下面兩張圖

按下紅框處進入應用程式選單

在此選單中可以看見此應用程式的圖示與名稱


上面的 AndroidManifest.xml 檔的內容較為陽春, 這個設定檔還可定義應用程式需啟用何種服務及宣告是否擁有兩個以上的 activity (只能選一個 activity 為程式啟始點), 其他細節部份留待您自行去學習, 本文僅作簡單介紹, 內容難免粗糙及疏漏, 尚請見諒 !  .^_^.

#本文僅就 xml 檔的概念來闡述, 其他細節請自行參考相關書籍.

2012-03-15

Android 的 xml 檔案 (二) main.xml

上次談了 strings.xml 檔案, 這次我來談談 main.xml 檔案.
main.xml 檔的作用就是用來設計畫面的格局; 有的人稱其為 [佈局檔], 預設是 main.xml, 如果你的應用程式要呈現一個以上的佈局畫面 (切換不同的畫面), 那麼你也可以設計多個佈局檔, 並且在一個 Activity 中輪番使用, 但是名稱不可相同, 例如 main.xml, main2.xml ... 至於何時該呈現哪個佈局畫面, 則必須透過程式來控制, 例如 :
if (條件)  setContentView(main.xml)  else  setContentView(main2.xml)

不過, 要想這麼做的話, 不如多創建一個 Activity 還來得比較實際些, 一個 Activity 控制一個佈局檔才不會把問題越搞越複雜.


main.xml 是否需要手動編寫 ?
在 Android 開發工具中提供了 Graphical Layout 功能, 讓開發者可以很方便地以拖曳元件的方式來佈置顯示畫面, 如下圖所示 :

Graphical Layout 設計視窗

在 Graphical Layout 設計視窗中, 可從左側的工具箱中將元件拖曳到畫面顯示區, 下方的 Properties 欄位還可以設定該元件的各項屬性, 這種直覺的設計方式為設計者省下不少寶貴的時間.

如下圖 :
我放置了一個 Button, 一個 TextView 及 一個 RatingBar, 共三個元件, 當你拖曳元件到顯示區後 (或刪除元件), 其對應的 xml 檔中 (此例為 main.xml) 亦會同步自動產生此畫面佈局的語法內容.


我們再從 Graphical Layout 視窗切換到 main.xml 視窗來看一下 xml 檔的對應內容, 天哪 ! 才放了三個元件而已, xml 檔的內容竟然那麼多 !

============================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:baselineAligned= "false"
android:layout_height="wrap_content"
android:orientation= "vertical" android:layout_width="fill_parent">
    <LinearLayout android:id="@+id/linearLayout1" android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:baselineAligned="true">
        <Button android:id="@+id/button2" android:layout_height="wrap_content"
android:text="Button" android:layout_width="wrap_content" android:layout_gravity="right"> </Button>
        <TextView android:id="@+id/textView2" android:layout_height="wrap_content"
android:text="TextView"
android:layout_width="wrap_content" android:textSize="30sp" android:layout_gravity="top"> </TextView>
    </LinearLayout>
    <RatingBar android:id="@+id/ratingBar1" android:layout_width="wrap_content"
android:layout_height="wrap_content"> </RatingBar>
</LinearLayout>
=============================================

如果沒有 Graphical Layout 的協助, 光是徒手編寫這些內容就夠累人的了 ! 更不用說還要撰寫後續的程式碼來控制這些元件. 不過呢 ~ 也不要太依賴 Graphical Layout 的功能喔.

有幾次我在設計時不小心按錯幾個功能項, 畫面中的元件位置因此大亂 ~ 不管怎麼調整還是調不回來, 有時是越調越亂 ! 遇到這種情形時, 我會切換到 xml 檔中直接去修改出問題的地方; 所以 ~ 先把 Graphical Layout 功能當做輔助工具, 必要時再直接去修改 xml 檔的內容會是比較好的做法 !

上述只是我的經驗分享, 如果你已經建立好你的工作方式, 請依照你原先的習慣就好 .^__^.

#本文僅就 xml 檔的概念來闡述, 其他細節請自行參考相關書籍.

2012-03-14

Android 的 xml 檔案 (一) strings.xml

當你創建了一個 Android Project (專案), ADT (Android 開發工具) 預設會建立三個 xml 檔案, 看下圖 : (畫紅線處)


預設有三個 xml 檔


main.xml : 畫面佈局檔

strings.xml : 字串資源檔

AndroidManifest.xml : 應用程式設定檔

這一篇先來說說 strings.xml 檔 :
運用 xml 檔的好處就是 [資源] 與 [程式碼] 可以分別處理. 這種將兩者分離的方式在編寫龐大的應用程式與字串資料時是非常有效率的機制.

開發工具提供了兩種介面可以編寫 strings.xml 檔的內容 : (如下圖)

介面一 : 配合說明與指引方式編寫內容
介面二 : 直接手動編寫內容

至於 strings.xml 檔的優點,下面是我整理出來的心得 :

strings.xml 檔的便利性 :
如果應用程式中需要用到的字串資料都直接寫進程式原始碼的話, 當遇到需要修改那些字串資料時, 勢必得在成堆的程式碼中一一將它們找出來並加以修改, 尤其當程式碼相當龐大時, 去修改那些寫進程式碼的字串資料是件非常沒有效率的事情 ! 況且 ~ 每次修改完程式原始碼還要重新編譯一次 ! 此時, 只需要單獨對 strings.xml 檔的資料做修改與存檔即可, 不會造成牽一髮而動全身的窘況.

strings.xml 的最大功效在於多國語系資料的應用 :
當你要設計有支援多國語系的應用程式時, 可在 strings.xml 中建立好你要支援的各語系資料, 使用者只需在程式運作前選好語系, 在應用程式執行時, 程式中的變數就能準確地把對應到 strings.xml 中的資料取出來. 假設不透過這個機制的幫忙, 單純只以程式來做多國語系的切換 ... 其複雜程度將難以想像 !

#本文僅就 xml 檔的概念來闡述, 其他細節請自行參考相關書籍.

如何截取 Android 模擬器的畫面

這一篇的重點在於如何截取 Android 模擬器的畫面, 在這之前先來談一下一般的螢幕截圖方式.

如何截取電腦螢幕的畫面, 這個小技巧我想應該很多人都會了吧 !
還不會 ? 讓我們來復習一下 ~

按下鍵盤的 Ptr Scrn / Sys Rq 鍵 (該鍵的位置在鍵盤偏右側上方, 有些鍵盤標示的字樣不同, 請找一下) 就可以把整個螢幕內容 Copy 到剪貼簿, 然後再將剪貼簿的內容用滑鼠右鍵 (貼上) 貼到小畫家或可以編輯圖檔的應用軟體中, 再進行縮圖或改圖 !

如果要用上述的方式來截取 Android 模擬器的畫面也是可以, 但後續還要將其主要畫面另外截取出來, 不是很方便; 況且截取範圍要是沒有選取好的話, 邊框看起來總是不太順.

幸好, 結合了 Android 開發工具的 Eclipse 編輯器, 其中就有一個貼心的功能讓我們可以很方便截取模擬器的畫面.

首先, 必須先讓 Android 模擬器啟動 (模擬器設定細節不在本文討論範圍), 啟動模擬器的方法如下圖所示 :

啟動 Android 模擬器的方式
功能表 -- Windows > Android SDK and AVD Manager
(其他設定細節非本文重點, 略過)

假設您的 Android 模擬器已經成功開啟
請按下 Eclipse 右上角的 DDMS 選項 (如下圖)
小圖示顯示為一 Android 綠色機器人圖樣, 或僅顯示 DDMS 字樣  
如果 DDMS 選項沒有出現, 請按下面步驟將 DDMS 選項叫出 :
功能表 -- Windows > Open Perspective > Other > DDMS
 
在 Eclipse 編輯器右上方按下 DDMS 選項

請按照紅圈標示的步驟 :
1 : DDMS 選項按下後, 進入 DDMS 管理畫面
2 : 接著按下 Screen Capture, 會呈現目前 Android 模擬器的畫面 

進入 DDMS 管理畫面


Screen Capture 視窗有幾個功能選項 : (如下圖)

Refresh : 立即更新模擬器目前的顯示狀態
Rotate : 旋轉模擬器畫面
Save : 存檔 ~ 以 png 格式存檔
Copy : 把模擬器的畫面直接拷貝到剪貼簿中
Done : 關閉此視窗, 與按右上方的 X 鈕相同

Android 模擬器的 Screen Capture 操作畫面

很簡單吧 ! 趕快操作看看 !

2012-03-07

模擬對號程式

主要功能說明:
模擬對號的小程式, 此程式採用 1 ~ 45 之內的六個數字.
先輸入得獎號碼, 接著再輸入有幾組要對號, 並陸續一組一組地把六個號碼輸入, 當輸入完畢後會直接顯示每一組對中了幾組號碼.

#include<iostream>
using namespace std;

//檢測錯誤函數 
int chkwrong(int chk[],int k)
{
      int i,j; 

      for (i=0; i<6; i++)  //檢查輸入範圍是否在 1~45,及是否超過六組號碼 

           if (chk[i]>45 || chk[i]<1 || k!=10) return 1;     

        for (i=0; i<5; i++)  //檢查是否輸入重複的號碼

              for (j=i+1; j<6; j++)   

                   if (chk[i]==chk[j]) return 1; //傳回 1 表示錯誤

    return 0; //傳回 0 表示正常無誤      

}


int main()
{

       int i,j,p=0;     //變數 p 用來檢測輸入是否超過 

       int sixnum[6];   //sixnum[6]為測試是否輸入超過六組號碼 

       int buy[255][7]; //買幾組 255 為最大,第二維的第[7]個用來存放中了幾個號碼  

       cout << "輸入此回合得獎的 6 個號碼: " << endl;

       for(i=0;i<6;i++)

            cin >> sixnum[i];

       p=cin.get(); //當 p 的值為 10,表示輸入為正常六個號碼 

       if(chkwrong(sixnum,p)==1) //呼叫 chkwrong() 函數,檢測錯誤 

       {

             cout << endl << "錯誤" << endl;

       system("PAUSE"); return 0;                         

    } 

       cout << endl << "輸入你總共有幾組號碼: ";

    cin >> j;    //此處變數 j 代表有幾組 

       int temp[6]; //暫存陣列,暫時存放每組的號碼,供測試輸入錯誤用 

       for (i=0; i<j; i++)

       {

            cout << endl << "輸入你要對號的 6 個號碼: (第 " << i+1 << " 組)" << endl;             

            for(int m=0;m<6;m++)              

            {  

                 cin >> buy[i][m]; 

                 temp[m]=buy[i][m]; 

            }

            p=cin.get();  

            if(chkwrong(temp,p)==1) //呼叫 chkwrong() 函數,檢測錯誤 

      {

                  cout << endl << "錯誤" << endl;

                  system("PAUSE"); return 0;                         

            } 

            for (int k=0; k<6; k++) //比對六個號碼,中幾號

                 for (int n=0; n<6; n++)

                      if(sixnum[k]==buy[i][n]) 

                           buy[i][6]+=1; //每比對成功一個號碼就累加 1 到 buy[][6] 中 

       }



   //以泡沫排序法由大到小,比對buy[][6]陣列的最後一維(中幾號) 

   for (i=0; i<j-1; i++) 

            for (int k=i+1; k<j; k++)

                 if(buy[i][6]<=buy[k][6]) 

             swap(buy[i][6],buy[k][6]); //swap()函數為交換兩數 

              cout << endl << "結果為: " << endl;    

              for(i=0;i<j;i++) 

                   cout << buy[i][6] << " ";

              cout << endl;

   system("PAUSE"); 

   return 0; 

}


操作說明 :
輸入號碼時可以在一行中以空白隔開每一個數字號碼 , 例如 ;
12 33 03 45 19 20 (接著按 Enter) 可以不必按照順序輸入號碼

程式細部修改 :
你也可以修改 int chkwrong(int chk[],int k) 函式內的 if (chk[i]>45 || chk[i]<1 || k!=10) return 1; 這一行的數值 45, 可以改變號碼的上限.

Android 的全螢幕設定

在設計 Android 程式時, 如果要以全螢幕顯示, 方法如下 :

1 . 在 import 中置入這兩行 :
      import android.view.Window;
      import android.view.WindowManager;

2 . 在 Activity 區段內的 onCreate() { .... } 之中, 置入相關的參數
      requestWindowFeature(....);
      getWindow().setFlags( .... );

程式的結構如下 :

import .... (略)
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {
...... (略)

   DrawViewTest mDrawViewTest;
   .....

   public void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(mDrawViewTest);

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(
        WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN );

   } ...... (略)
}


使用全螢幕顯示時, setContentView( ... ) 內的引用類別必須以 SurfaceView 為延伸架構 (如上例的 mDrawViewTest), 不然無法正常顯示 ! 因此, 原先佈置畫面的 xml 檔內容將派不上用場喔 !


至於 DrawViewTest (此類別的名稱可自訂) 的大致架構如下 :

import ... (略)

public class DrawViewTest extends SurfaceView implements SurfaceHolder.Callback { .... (略) }

DrawViewTest 類別內的細節非本文討論重點, 因此沒有列出細節.
我用兩張圖來做比較, 這樣應該會比較清楚是否設定全螢幕顯示的差別 !


這是沒有設定全螢幕的畫面, 還可以看見上方的 Title

以全螢幕顯示, 上方的 Title 消失囉! 我用程式在上面畫出一個橢圓形
這樣知道兩者的差別了吧 ! 在開發遊戲時, 全螢幕顯示最常被運用到, 而且 SurfaceView 類別的顯示速率也比一般的 View 類別要快上許多 ! .^_^.

2012-03-06

函式的呼叫與傳回值 (字串處理)

記得我之前剛接觸 C 語言的時候, 對於函式相互間的呼叫與應用花了不少時間去搞懂它, 而且學會函式間的呼叫與應用, 更是邁向物件導向程式設計的基礎, 當然 ! 在物件導向中主要是以類別 class 為基礎單位, 並非在此所談論的 "函式", 不過兩者的觀念與架構模式是很類似的, 呵呵呵 ~ 很多學程式設計的人都覺得基礎是很乏味的東西, 只想一開始就涉入最新的技術, 最後只會碰得滿頭包 ! 基礎是很重要的啊 !

設計功能如下 :

從主函式連續呼叫 getchar() 三次 (資料由鍵盤輸入), 取得三個字元變數a b c, 再由函式 char findmax(char ch1,char ch2,char ch3) 將最大的字元傳回並輸出

#include<iostream>
#include<conio.h>
using namespace std;

//getchar為保留字,不可直接用來作為函式名稱
//因此函式名稱改為 getchar2()

char *getchar2()
{
   char *gets;
   //一定要配置記憶體位置給gets, 否則執行後果無法預測(或錯誤)!
   gets=new char;
   cout << endl << "請輸入一個字元(不需按Enter)-->";
   *gets=getche();
   return gets;
}

char *findmax(char *ch1,char *ch2,char *ch3)
{
   if(*ch1>*ch2 && *ch1>*ch3) return ch1;
   if(*ch2>*ch3 && *ch2>*ch1) return ch2;
   return ch3;
}

int main()
{
   char *ch1,*ch2,*ch3;
   ch1=new char;ch2=new char;ch3=new char;
   ch1=getchar2();
   ch2=getchar2();
   ch3=getchar2();
   //在 findmax( ) 函式前加上 * 取值
   //這樣輸出的字元尾部才不會出現亂碼
   cout << endl << "傳回最大字元-->" << *(findmax(ch1,ch2,ch3)) << endl;
   system("pause");
   return 0;
}

==================================
下面這個範例與上面的程式有同工異曲之處, 請耐心解析看看其功能為何 ?

 #include<iostream>

using namespace std;

char *getfstring() //函式getfstring()
{
    char *gets;
    //一定要配置記憶體位置給gets,否則執行後果無法預測!
    gets=new char;
    cout << "請輸入一個字串-->";
    //cin >> gets; //如果使用 cin,輸入空白格時將無法讀取空白格後的字元
    // 改用 cin.getline(),這裏設最大長度為255,可自行更改數值
    cin.getline(gets,255);
    return gets;
}

int main()
{
    cout << endl << "傳回字串-->" << getfstring() << endl;
    system("pause");
    return 0;
}

2012-03-05

鍵盤輸入與傳回值輸出的示範 (使用字串)

這篇與我上一篇 po 的示範主要差別在於 "資料型態" 的處理, 處理的資料到底是 "數值" 還是 "字串" 絕對不能搞錯 ! 請一定要特別留意 ! 基本上兩者的程式架構大致相同.


建立一個函式 getfstring() 從鍵盤輸入一個字串並傳回, 不需要代入參數, 傳回值為字元指標形態. 再從主函式 main() 呼叫 getstring(), 並將傳回值輸出

#include<iostream>
using namespace std;

char *getfstring() //函式getfstring()
{
    char *gets;
    gets=new char; //一定要配置記憶體位置給gets,否則執行後果無法預測!
    cout << "請輸入一個字串-->";
    //cin >> gets; //如果使用 cin,輸入空白格時將無法讀取空白格後的字元
    // 改用 cin.getline(),這裏設最大長度為255,可自行更改數值
    cin.getline(gets,255);
    return gets;
}

int main()
{
    cout << endl << "傳回字串-->" << getfstring() << endl;
    system("pause");
    return 0;
}

鍵盤輸入與傳回值輸出的示範(使用數值)

建立一個函式 getfloat() 從鍵盤輸入一個浮點數, getfloat() 函式的傳回值為浮點數型態(float). 再從主函式 main() 呼叫 getfloat(), 並將傳回值輸出, getfloat() 函式不需要代入任何參數. 程式碼如下 :

#include<iostream>

using namespace std;

float getfloat() //函式getfloat()
{
    float getnum;
    cout << "請入一個浮點數";
    cin >> getnum;
    return getnum;
}

int main()
{
    cout << endl << "傳回值-->" << getfloat() << endl;
    system("pause");
    return 0;
}

這個範例主要是在於建立與呼叫函式的基礎練習 !

2012-03-04

陣列元素位置的起算法(二)

我在這篇中針對 [ 陣列 ] 的元素位置與順序做一番強調 ! 關於 [ 陣列 ] 元素位置的算法, 這個觀念非常非常重要 ! 如果在觀念上誤解的話, 那麼在存取陣列裡的資料時, 將會產生難以察覺的人為錯誤. 不可不慎 !

我門以下面這兩個例題來做說明 :

1.有一個二維陣列宣告如下,請問score[3][2]的值是多少?
int score[][3] = { {85,78,65},
{75,85,69},
{63,67,95},
{94,92,88},
{74,65,73} };

2.有一個二維陣列宣告如下,請問score[3][2]的值是多少?
int score[5][3] ={0};

這兩題可以用以下的程式碼來驗證 :

// c++ 程式碼
#include <iostream>
using namespace std;

int main()
{
   int score1[][3] = { {85,78,65}, {75,85,69}, {63,67,95},
                                {94,92,88}, {74,65,73} };
   cout << "score1[3][2] = " << score1[3][2] << endl;

   int score2[5][3] ={0};
   cout << "score2[3][2] = " << score2[3][2] << endl;
   system("pause");
}

題 1 答案 : 88
題 2 答案 : 0

解析 :
再度強調 ! 陣列基底由 0 起算, 不是由 1 起算 !
第 1 題 score[3][2] 的內容, 第一維陣列元素為 [3], 對應到大括號內的大括號, 我用顏色來標明 {  { .... }, { .... }, { ..... }, { .... }, { .... }  } 其元素位置相當於 0, 1, 2, 3, 4   ; 接著, 第二維陣列元素為 [2], 則表示指向上面那個紅色 { ..... } 內容中的 { 94,92,88 }, 其元素位置相當於 0, 1, 2

第 2 題 因為 int score[5][3] ={0}; 整個陣列內容都沒有設定完全, 因此二維陣列score[3][2]的內容也是一樣沒有被指定, 所以預設初值則為 0

2012-03-03

陣列元素位置的起算法(一)

這是我幫一位學生解析其程式的內容, 程式內最後 x 的值為何 ? 

#include <iostream>
using std::cout;
using std::endl;

int mystery2(const char *s);

int main( )
{
   char *string1 = "MingChi University"; //字串
   char string2[] = "Electrical Engineering";
   mystery2(&string1[2]);
   cout<< mystery2(&string2[3]) <<endl;
}

int mystery2(const char *s)
{
   static int x = 0;
   for ( ; *s != '\0' ; s++)
      x += (( *s != 'i' ) ? 1 : 0 );
   return x;
}

解析如下 :

程式中 mystery2(&string1[2]); 字串 "MingChi University" 會由第 3 個(含)算起 , 包括空白 , 於 mystery2 函式中作累計(累計的值放入 x) , 並將 "i" 省略不計 , 累計結果共 13 個字 , 接著 mystery2(&string2[3]); 字串 "Electrical Engineering" 會由第 4 個(含)算起 , 包括空白 , 同樣經過 mystery2 函式將 "i" 省略不計 , 累計共 16 個字 , 在 mystery2 函式中 x 宣告為 static , 因此執行第一次時 x 的值累計為 13 , 再執行第二次時 x 的值就從 13 開始累計 , 執行第二次完(16次) , 共累計了 13 +16 = 29 次 . 所以 x = 29 .
請注意陣列基底由0算起 .

此題目來源在以下網址 :
http://tw.knowledge.yahoo.com/question/question?qid=1607051402495

2012-03-01

Android 小程式 : 亂數與氣泡排序法

這一次我要挑戰用 Android 模擬器來完成以下的功能 :
以亂數產生10 組號碼 (0 ~ 99), 然後利用氣泡排序法以及降幕方式排列將結果顯示出來 !

運作原理 :
亂數產生的部分我使用 Math.random() 內建函數, 它會產生 0.0 ~ 0.1 的值, 由於其型態為 double, 我將它轉換型態成為 int 後, 讓它以整數型態呈現, 按下 [ 開始 ] 按鈕後產生 10 組亂數, 並同時進行排序, 程式中我用氣泡排序法, 讓數值由小到大排列.

第一次執行的畫面



按下開始鈕後的執行畫面

程式碼如下 :

import ... (略)

public class RndAndSort extends Activity {
    /** Called when the activity is first created. */
   
    int[] sort = new int[10];
    int rnd;
    String st,temp;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        final TextView textView1=(TextView)findViewById(R.id.textView1);
        final TextView textView2=(TextView)findViewById(R.id.textView2);
        final Button button1=(Button)findViewById(R.id.button1);
       
        button1.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                st = ""; temp = "";
                for (int i=0; i<10; i++) {
                    rnd = (int)(Math.random() * 100);
                    sort[i] = rnd;
                    temp = String.valueOf(rnd);
                    st += (temp + ", ");
                }
                textView1.setText(st);
               
                for (int i=0; i<9; i++)
                      for (int j=i+1; j<10; j++)
                        // 改變下式中的大、小於符號可變更排列順序
                        // < 由大到小, > 為小到大排列
                         if(sort[i]>sort[j])
                         {
                             int tempNum;
                             tempNum=sort[i];
                             sort[i]=sort[j];
                             sort[j]=tempNum;
                         }
                st = "";
                for (int i=0; i<10; i++)
                    st += (String.valueOf(sort[i]) + ", ");
                   
                textView2.setText(st);
            }
        });
    }
}

搜尋此網誌