92
ANDROID 智慧型機程式設計 思創軟體 / 林彥宏 資深軟體程師 http://lyhcode.info 基礎實作課程

Android 智慧型手機程式設計

Embed Size (px)

Citation preview

Page 1: Android 智慧型手機程式設計

ANDROID智慧型⼿手機程式設計

思創軟體 / 林彥宏資深軟體⼯工程師http://lyhcode.info

基礎實作課程

Page 2: Android 智慧型手機程式設計

建⽴立開發環境

Page 3: Android 智慧型手機程式設計

ADT Bundle

• developer.android.com/sdk

• Eclipse IDE with built-in ADT (Android Developer Tools)

Page 4: Android 智慧型手機程式設計

DDMSDalvik Debug Monitor Server

Page 5: Android 智慧型手機程式設計

Android Screen Monitor

http://goo.gl/mnMeaS

java -jar asm.jar

ctrl + 1 = 100%ctrl + 7 = 75%ctrl + 5 = 50%

縮放

執⾏行

Page 6: Android 智慧型手機程式設計

AirDroid

Install “AirDroid”from

Google Play Store

Page 7: Android 智慧型手機程式設計

線上免費課程 from Coursera

Programming Mobile Applications for Android Handheld Systems

Page 8: Android 智慧型手機程式設計

Android App 原始碼範例

https://github.com/aporter/coursera-android

從 GitHub 網站下載

Page 9: Android 智慧型手機程式設計

第⼀一課

Page 10: Android 智慧型手機程式設計

打造使⽤用者介⾯面

Page 11: Android 智慧型手機程式設計

ActivityLife Cycle

Page 12: Android 智慧型手機程式設計

UI

友善的

Page 13: Android 智慧型手機程式設計
Page 14: Android 智慧型手機程式設計
Page 15: Android 智慧型手機程式設計

UI 的設計⽅方式

• 程序式(procedural)在程式碼動態產⽣生畫⾯面 UI 元件

• 宣告式(declarative)在 Layout XML 定義畫⾯面 UI 元件

Page 16: Android 智慧型手機程式設計

指尖操作⾏行為

Page 17: Android 智慧型手機程式設計

手機程式的調色盤

Holo����������� ������������������  Colors����������� ������������������  Generator����������� ������������������  

Action����������� ������������������  Bar����������� ������������������  Style����������� ������������������  Generator����������� ������������������  

Adobe����������� ������������������  Color����������� ������������������  CC

Page 18: Android 智慧型手機程式設計

Create a new Android Project

• Target SDK: Android 4.x

• Using Blank Activity

Page 19: Android 智慧型手機程式設計

設定 API Level

• 編輯AndroidManifest.xml

• 設定 API Level 版本代號 <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />

Page 20: Android 智慧型手機程式設計

Android API Level 對照表

Page 21: Android 智慧型手機程式設計

• Change Layout => LinearLayout

• Add RadioButton

• Add Button

• Add TextView

Page 22: Android 智慧型手機程式設計

畫⾯面佈置

• 修改 res/layout/activity_main.xml

• 調整 layout_width 為 fill_parent <Button android:id="@+id/button1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="showLocation" android:text="查詢地點" />

Page 23: Android 智慧型手機程式設計

• Wrap Radio Buttonswith RadioGroup

Page 24: Android 智慧型手機程式設計

Click Event

1

2

3

<Button ... android:onClick=“clickOpenMap” ...

public void clickOpenMap(View view) { ...

button1.setOnClickListener(new Button.OnClickListener() { ...

4

class MyButtonListener implements OnClickListener

public class MainActivity extends Activity implements OnClickListener

按鈕點擊事件的四種處理⽅方式

Page 25: Android 智慧型手機程式設計

使⽤用 OnClickListener 類別

1. button1.setOnClickListener(new Button.OnClickListener() { 2. @Override 3. public void onClick(View v) { 4. ... 5. } 6. });

按鈕點擊後會執⾏行 onClick() ⽅方法

Page 26: Android 智慧型手機程式設計

Show Location in TextView

1. public void showLocation(View view) {2. switch (radioGroup1.getCheckedRadioButtonId()) {3. case R.id.radioButton1:4. textView1.setText("澄清湖的位置是 22.660665,120.3509745");5. break;6. case R.id.radioButton2:7. textView1.setText("⻄西⼦子灣的位置是 22.629167,120.262778");8. break;9. }10.}

Page 27: Android 智慧型手機程式設計

Intent

1. Intent intent = new Intent(); 2. intent.setClass( 3. MainActivity.this, 4. MainActivity2.class); 5. startActivity(intent);

參考資料• http://ccckmit.wikidot.com/ga:intentexample

Page 28: Android 智慧型手機程式設計

常⾒見的 Intent ⽤用法

• http://google.com

• geo:22.660,120.350

• tel: 0800080123

打開網站

打開地圖

撥打電話

Page 29: Android 智慧型手機程式設計

Open Map with Intent

1. public void openMap(View view) {2. Uri uri;3. Intent intent;4. switch (radioGroup1.getCheckedRadioButtonId()) {5. case R.id.radioButton1:6. uri = Uri.parse("geo:22.660,120.350");7. intent = new Intent(Intent.ACTION_VIEW, uri);8. startActivity(intent);9. break;10. }11.}

Page 30: Android 智慧型手機程式設計

Intent Example⼿手機常⽤用功能快捷鍵

程式實作

【學習⺫⽬目標】1. Button2. Intent

Page 31: Android 智慧型手機程式設計

RadioButton Example景點查詢⼯工具

程式實作

【學習⺫⽬目標】1. RadioButton2. TextView3. Intent

Page 32: Android 智慧型手機程式設計

第⼆二課

Page 33: Android 智慧型手機程式設計

顯⽰示訊息與偵錯記錄Logging / Debugging

Page 34: Android 智慧型手機程式設計

Dialog對話框

彈出後等待使⽤用者確認後才消失

Page 35: Android 智慧型手機程式設計

Dialog Example

1. AlertDialog.Builder builder;2. builder = new AlertDialog.Builder(view.getContext());

4. builder5. .setTitle(“Something Wrong")6. .setMessage(“Incorrect value, try again.”)7. .setNeutralButton("OK", null)8. .create()9. .show();

}builderstyle

Page 36: Android 智慧型手機程式設計

Toast

Toast.makeText(view.getContext(), "You've 3 messages.", Toast.LENGTH_LONG);

message body

停留時間彈出後過幾秒⾃自動消失

Page 37: Android 智慧型手機程式設計

訊息記錄(LOG)

• 給程式開發與測試⼈人員觀察程式執⾏行狀態

• 測試與除錯階段的重要資訊來源

Page 38: Android 智慧型手機程式設計

輸出除錯訊息

System.out.println

Log.println tagpriority

使⽤用標準輸出(stdout)

使⽤用 Android 系統的 Log 機制

缺點:無法對訊息進⼀一步分類

Page 39: Android 智慧型手機程式設計

訊息類型PRIORITY 名稱 ⽤用途

DEBUG 僅供除錯

INFORMATION ⼀一般訊息

WARNING 資料或處理異常

ERROR 嚴重的錯誤;可能導致程式終⽌止

Page 40: Android 智慧型手機程式設計

使⽤用 Log 類別

Log.println(Log.WARN, "MyActivity", "User account not exists.");

tagpriority message body

Page 41: Android 智慧型手機程式設計

Tag 的命名慣例

public class MainActivity extends Activity {

private static final String TAG = "MainActivity";

Log.println(Log.WARN, TAG, "User account not exists.");…

通常使⽤用該記錄存在的類別名稱(Class Name)

Page 42: Android 智慧型手機程式設計

Log 類別提供的⽅方法簡寫

• v(String, String) (verbose) • d(String, String) (debug) • i(String, String) (information) • w(String, String) (warning) • e(String, String) (error)

依其重要程度(優先度)區分

}常⽤用

Page 43: Android 智慧型手機程式設計

使⽤用 LogCat 監看除錯訊息

依照 Priority 篩選過濾

Page 44: Android 智慧型手機程式設計

Dialog and Toast Example景點查詢⼯工具

程式實作

【學習⺫⽬目標】1. Dialog / Toast2. Log

Page 45: Android 智慧型手機程式設計

第三課

Page 46: Android 智慧型手機程式設計

多執⾏行緒與網路存取

Page 47: Android 智慧型手機程式設計

應⽤用程式沒有回應

• 程式執⾏行超過五秒,UI 仍未能與使⽤用者互動

• 常發⽣生在:

• 處理複雜的演算法

• 等待網路傳輸資料

"Application Not Responding" (ANR)

Page 48: Android 智慧型手機程式設計

處理 ANR 的⽅方法

• Thread / Runnable

• AsyncTask

• Handler

多執⾏行緒程式設計(Multithreading)

Page 49: Android 智慧型手機程式設計

AsyncTask 範例

1. private class MyTask extends AsyncTask<Void, Void, Void> { 2. protected Long doInBackground(...) { 3. ... 4. } 5. }

Keeping Your App Responsivehttp://developer.android.com/training/articles/perf-anr.html

Page 50: Android 智慧型手機程式設計

程式實作

• 修改 res/layout/activity_main.xml

• 加⼊入 Button 「顯⽰示地圖」

• 加⼊入 ImageView 在畫⾯面下⽅方

Page 51: Android 智慧型手機程式設計
Page 52: Android 智慧型手機程式設計

activity_main.xml

<ImageView android:id="@+id/imageView1" android:layout_width="fill_parent" android:layout_height="150dp" android:src="@drawable/ic_launcher" />

<Button android:id="@+id/button3" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="showMap" android:text="顯⽰示地圖" />

Button

ImageView

Page 53: Android 智慧型手機程式設計

findViewById

public class MainActivity extends Activity {

private RadioGroup radioGroup1;private TextView textView1;private ImageView imageView1;

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);

radioGroup1 = (RadioGroup) this.findViewById(R.id.radioGroup1);textView1 = (TextView) this.findViewById(R.id.textView1);imageView1 = (ImageView) this.findViewById(R.id.imageView1);

}

Page 54: Android 智慧型手機程式設計

權限設定使⽤用 Manifest 權限設定⼯工具

Page 55: Android 智慧型手機程式設計

權限設定(XML定義)

• 編輯 AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

Page 56: Android 智慧型手機程式設計

實作 showMap ⽅方法

public void showMap(View view) {

switch (radioGroup1.getCheckedRadioButtonId()) {case R.id.radioButton1:

//...

break;}

}

Page 57: Android 智慧型手機程式設計

lat 與 lng 變數

Double lat = null, lng = null;

switch (radioGroup1.getCheckedRadioButtonId()) {case R.id.radioButton1:

lat = 22.660;lng = 120.350;

break;}

if (lat != null && lng != null) { //... }

Page 58: Android 智慧型手機程式設計

AsyncTask 實作if (lat != null && lng != null) {

String url = "...";

new AsyncTask<String, Void, Drawable>() {@Overrideprotected Drawable doInBackground(String... params) {

try {InputStream is = new URL(params[0]).openStream();return Drawable.createFromStream(is, "src");

} catch (Exception ex) {ex.printStackTrace();

}return null;

}

@Overrideprotected void onPostExecute(Drawable result) {

if (result != null) {imageView1.setImageDrawable(result);

}super.onPostExecute(result);

}}.execute(url);

}

Page 59: Android 智慧型手機程式設計

2. 點擊「顯⽰示地圖」

1. 選擇景點

3. 地圖顯⽰示

Page 60: Android 智慧型手機程式設計

加⼊入「查詢天氣」按鈕

<Button android:id="@+id/button4" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="showWeather" android:text="查詢天氣" />

Page 61: Android 智慧型手機程式設計

showWeather ⽅方法實作public void showWeather(View view) {

Double lat = null, lng = null;

switch (radioGroup1.getCheckedRadioButtonId()) {case R.id.radioButton1:

lat = 22.660;lng = 120.350;

break;case R.id.radioButton2:

lat = 22.629;lng = 120.262;

break;}

}

Page 62: Android 智慧型手機程式設計

OpenWeatherMap API

http://openweathermap.org

Page 63: Android 智慧型手機程式設計

Web Services 存取天氣資料

http://api.openweathermap.org/data/2.5/weather?lat=22.629&lon=120.262&&units=metric

http://api.openweathermap.org/data/2.5/weather?q=Taipei,TW&units=metric

Page 64: Android 智慧型手機程式設計

OpenWeatherMapWeb Services APIAndroid App

HTTP Request

Data ResponseXML, JSON, HTML, …

Page 65: Android 智慧型手機程式設計

AsyncTask 實作

if (lat != null && lng != null) {String url = "...";

new AsyncTask<String, Void, String>() {@Overrideprotected String doInBackground(String... params) {

//...}

@Overrideprotected void onPostExecute(String json) {

//...}

}.execute(url);}

Page 66: Android 智慧型手機程式設計

存取 HTTP Web Services

protected String doInBackground(String... params) {try {

BufferedReader reader = new BufferedReader( new InputStreamReader(new URL(params[0]).openStream()));

String json = reader.readLine();reader.close();return json;

} catch (Exception ex) {ex.printStackTrace();

}return null;

}

Page 67: Android 智慧型手機程式設計

解析 JSON 資料

protected void onPostExecute(String json) {if (json != null) {

try {JSONObject jsonObject = new JSONObject(json);Double temp = jsonObject.getJSONObject("main").getDouble("temp");textView1.setText(temp + "℃");

}catch (Exception ex) {

ex.printStackTrace();}

}super.onPostExecute(json);

}

Page 68: Android 智慧型手機程式設計

2. 點擊「查詢天氣」

1. 選擇景點

3. 顯⽰示地區氣溫

Page 69: Android 智慧型手機程式設計

第四課

Page 70: Android 智慧型手機程式設計

建⽴立新專案 MyBrowser

Page 71: Android 智慧型手機程式設計

activity_main.xml

Page 72: Android 智慧型手機程式設計

MainActivitypublic class MainActivity extends Activity {

EditText editText1;WebView webView1;Button button1;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText1 = (EditText) this.findViewById(R.id.editText1); webView1 = (WebView) this.findViewById(R.id.webView1); button1 = (Button) this.findViewById(R.id.button1); }

Page 73: Android 智慧型手機程式設計

權限設定

Page 74: Android 智慧型手機程式設計

WebView 設定

//設定只⽤用 WebView 開啟網址 webView1.setWebViewClient(new WebViewClient()); //打開 JavaScript webView1.getSettings().setJavaScriptEnabled(true);

Page 75: Android 智慧型手機程式設計

按鈕 onClick 設定

<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/editText1" android:layout_alignRight="@+id/webView1" android:text="好⼿手氣" android:onClick="loadWebPage" />

Page 76: Android 智慧型手機程式設計

public void loadWebPage(View view) {

webView1.loadData("Loading...", "text/plain", "UTF-8"); String keyword = editText1.getText().toString(); try {

keyword = URLEncoder.encode(keyword, "UTF-8");} catch (UnsupportedEncodingException e) {

// TODO Auto-generated catch blocke.printStackTrace();

}

String url = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=" + keyword;

new AsyncTask<String, Void, String>() {@Overrideprotected String doInBackground(String... params) {

return null;}

@Overrideprotected void onPostExecute(String json) {}

}.execute(url); }

Page 77: Android 智慧型手機程式設計

doInBackground

@Overrideprotected String doInBackground(String... params) {

try {InputStream stream = new URL(params[0]).openStream();InputStreamReader isr = new InputStreamReader(stream, "UTF-8");BufferedReader reader = new BufferedReader(isr);String json = reader.readLine();return json;

} catch (Exception ex) {ex.printStackTrace();

}return null;

}

Page 78: Android 智慧型手機程式設計

onPostExecute

@Overrideprotected void onPostExecute(String json) {

if (json != null) {try {

JSONObject jsonObject = new JSONObject(json);

JSONArray results = jsonObject.getJSONObject("responseData").getJSONArray("results");

if (results.length() > 0) {String url = results.getJSONObject(0).getString("url");webView1.loadUrl(url);

}else {

webView1.loadData("Not found...", "text/plain", "UTF-8");}

}catch (Exception ex) {

ex.printStackTrace();}

}}

Page 79: Android 智慧型手機程式設計

儲存紀錄

this.getPreferences(MODE_PRIVATE).edit().putString("LAST_QUERY", editText1.getText().toString());

Page 80: Android 智慧型手機程式設計

讀取記錄

editText1.setText(this.getPreferences(MODE_PRIVATE).getString("LAST_QUERY", "PCHome"));

Page 81: Android 智慧型手機程式設計
Page 82: Android 智慧型手機程式設計

建⽴立新專案

Page 83: Android 智慧型手機程式設計

2D 繪圖

setContentView(new View(this) { @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setStyle(Paint.Style.FILL); paint.setColor(Color.WHITE); canvas.drawPaint(paint); paint.setColor(Color.GREEN); canvas.drawCircle(100, 100, 20, paint); } });

Page 84: Android 智慧型手機程式設計

使⽤用 Canvas 製作動畫

• 使⽤用 onDraw 更新畫⾯面

• 缺點:每次都要重新繪製整個畫⾯面

Page 85: Android 智慧型手機程式設計

public class MyView extends View implements Runnable {

private Handler handler = new Handler();

private int x = 0;private int y = 0;

private int offsetX = 5;private int offsetY = 5;

public MyView(Context context) {super(context);

this.setFocusable(false);

new Thread(this).start();}

@Overrideprotected void onDraw(Canvas canvas) {}

@Overridepublic void run() {}

}

Page 86: Android 智慧型手機程式設計

@Overrideprotected void onDraw(Canvas canvas) {

super.onDraw(canvas);

Paint paint = new Paint();paint.setStyle(Paint.Style.FILL);paint.setColor(Color.WHITE);canvas.drawPaint(paint);paint.setColor(Color.GREEN);canvas.drawCircle(x+=offsetX, y+=offsetY, 20, paint);

if (x > canvas.getWidth() || x < 0) {offsetX = -offsetX;

}if (y > canvas.getHeight() || y < 0) {

offsetY = -offsetY;}

}

Page 87: Android 智慧型手機程式設計

@Overridepublic void run() {

while (true) {handler.post(new Runnable() {

@Overridepublic void run() {

invalidate();}

});}

}

Page 88: Android 智慧型手機程式設計
Page 89: Android 智慧型手機程式設計

Paint paint = new Paint();paint.setStyle(Paint.Style.FILL);paint.setColor(Color.BLACK);canvas.drawPaint(paint);

int clockX = canvas.getWidth() / 2;int clockY = canvas.getHeight() / 2;int clockRadius = (canvas.getWidth() - 150) / 2; paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(3);paint.setColor(Color.RED);canvas.drawCircle(clockX, clockY, clockRadius, paint);

paint.setStyle(Paint.Style.FILL);paint.setColor(Color.RED);canvas.drawCircle(clockX, clockY, 10, paint);

Page 90: Android 智慧型手機程式設計
Page 91: Android 智慧型手機程式設計

private double getAngle(int index, int size) {return Math.PI / size * 2 * (index - size / 4);

}

private int getStopX(int x, double angle, int radius) {return (int)(x + Math.cos(angle) * radius);

}

private int getStopY(int y, double angle, int radius) {return (int)(y + Math.sin(angle) * radius);

}

Page 92: Android 智慧型手機程式設計