visited 13 states (5.77%)
Create your own visited map of The World or Triposo world travel guide for Android

2015-01-22

Android中以timer / sleep / delay / 更新 UI 的方法

主要是希望達成Sleep / Delay 的功並讓UI可更新,Android 不能套用 Java or C 的 sleep 方法,列出幾種可以取代的方法

以下幾個方法都可以達到 "Delay" 的效果 (A1 -> (wait 10s) -> A2 -> (wait 10s) -> A3...)

  1. Thread + sleep (Thread.sleep(xxx))
  2. Handler (handler.postDelayed(Runnable, 2000);) => Call back 到 Runnable 的 run()
  3. Timer (timer.schedule(TimerTask, 10000);) => Call back 到 TimerTask 的 run()
  4. Thread + Handler
  5. Timer + Handler
  6. SystemClock.sleep(2000);
  7. asynctask
  8. Handlerthread
  9. IntentService

若要更新 UI,可以在 UI/Main thread 或者 other thread:
  1. Thread + Handler
  2. Timer + Handler
  3. Timer: postInvalidate (update a view when you're not in the current UI thread.) => http://blog.csdn.net/lixinso/archive/2009/06/23/4290432.aspx
  4. Activity.runOnUiThread(Runnable) => http://lak4cyut.blogspot.com/2011/03/android-tip-activityrunonuithread.html
  5. View.post(Runnable)
  6. View.postDelayed(Runnable, long)
Handler / Timer 簡單的應用區分:

Handler / Timer 使用的時機:
  1. 當 Delay or Sleep 用
  2. 類似迴圈的應用
  3. 定期觸發的定期行為
只要講到 Timer or Handler,一定會跟幾個名詞綁在一起: Thread / Handler / Timer / Timertask / Message / Looper / AsyncTask

2015-01-07

BCB製作靜態Library並提供其他專案使用的方法

在C++ Builder中建立及使用libary(靜態聯結函式庫)需要依照以下步驟進行

1. 開啟C++ Builder

2. File/New/Other 選擇Libray 按OK

3. 此時會自動產生一個 Project1.bpf檔

4. 接著將要被打包的 .cpp透過 Add to project加入到專案中 ( .h不用)

5. 將此專案儲存起來 (可依照功能/目的來命名,不一定要和 .cpp/.h同名)

6. 將這些 .cpp用到的 .h放到此專案目錄下

7. Build此專案

8. 完成後,在目錄下會產生一個和專案同名的 .lib檔

9. 在拿到 .lib及 .h檔後,將這兩個檔案放到專案執行的目錄下或者,

10. .lib可放在Windows能搜尋到的目錄下即可,但 .h 一定要放在專案執行的目錄下

11. 透過Add File to project將 .lib加入到專案中 ( .h不用)

12. 在程式開頭處加入 #include "xxx.h"

13. 完成

2014-12-09

Android應用程式中設定螢幕方向或設定旋轉後行為與動態變更銀幕顯示方向

強制設定螢幕方向:

打開工程中的 AndroidManifest.xml 檔,在 中,添加屬性資訊:

android:screenOrientation="portrait"  ->垂直
android:screenOrientation="landscape" ->水平

設定螢幕旋轉後行為:

打開工程中的 AndroidManifest.xml 檔,在 中,添加一條屬性資訊:

android:configChanges=”orientation|keyboardHidden|keyboard”  ->API level <= 12
android:configChanges=”orientation|keyboardHidden|keyboard|screenSize” -> API level > 13

這個屬性指的是,當後邊屬性值代表的事件發生時,Activity 會執行某個函數,orientation 指的是當螢幕旋轉時,keyboardHidden 與keyboard指鍵盤輔助功能改變。“|”為或符號,指這兩個中任意一個發生,就執行 Activity 某個函數。如果你的開發 API 等級等於或高於 13,你還需要再加入screenSize,因為 screenSize 會在螢幕旋轉時改變。

再重寫 onConfigurationChanged() 方法:
@Override
public void onConfigurationChanged(Configuration newConfig) {
    // TODO Auto-generated method stub
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        // your code
    }
    else {
        // your code
    }
}

在 if、else 中可以設定所需要的動作與畫面規劃,若使用了 setContentView(R.layout.xxxx) 函數,就可以實現:每次螢幕旋轉時,調用不同的佈局。

動態更改螢幕方向:

有些時候,我們不需要把程式寫死,需要在程式中有需要的時候旋轉螢幕,例如:在“設置”裏添加一個 ListView 項,可通過點擊選擇橫屏或豎屏。(比如:電子書軟體)
假設有一個按鈕,我們僅重寫 OnClick() 函數:

@Override
public void onClick(View v) {
    // 如果豎排,則改為橫排
    if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
    // 如果橫排,則改為豎排
    else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}
重點就是:getRequestedOrientation() 函數、setRequestedOrientation() 函數的使用。
注意:使用這種方法,必須事先在 AndroidManifest.xml 的 中,添加android:screenOrientation 屬性值,不然 getRequestedOrientation() 可能會出問題。

2014-12-08

Android應用程式中Timer元件的使用範例

以Timer元件做一個計時器來了解Timer的設計與應用

activity_main.xml:



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="碼表:" />

        <TextView
            android:id="@+id/timer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:00" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="開始" />

        <Button
            android:id="@+id/stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="停止" />
        
        <Button
            android:id="@+id/zero"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="歸零" />        
            
        <Button
            android:id="@+id/end"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="結束" />        
    </LinearLayout>

</LinearLayout>
MainActivity.java:



package com.example.timer;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	private TextView timer;
	private Button start;
	private Button stop;
	private Button zero;
	private Button end;
	private boolean startflag=false;
	private int tsec=0,csec=0,cmin=0;


	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		timer = (TextView)findViewById(R.id.timer);
		start = (Button)findViewById(R.id.start);
		stop = (Button)findViewById(R.id.stop);
		zero = (Button)findViewById(R.id.zero);
		end = (Button)findViewById(R.id.end);
		
		//宣告Timer
		Timer timer01 =new Timer();
		
		//設定Timer(task為執行內容,0代表立刻開始,間格1秒執行一次)
		timer01.schedule(task, 0,1000);

		//Button監聽
		start.setOnClickListener(listener);
		stop.setOnClickListener(listener);
		zero.setOnClickListener(listener);
		end.setOnClickListener(listener);
	}
	
	//TimerTask無法直接改變元件因此要透過Handler來當橋樑
	private Handler handler = new Handler(){
		 public  void  handleMessage(Message msg) {
			 super.handleMessage(msg);
			 switch(msg.what){
			 case 1:
		 csec=tsec%60;
		 cmin=tsec/60;
                 String s="";
                 if(cmin <10 s="<span" style="color: #2a00ff;">"0"
+cmin; }else{ s=""+cmin; } if(csec < 10){ s=s+":0"+csec; }else{ s=s+":"+csec; } //s字串為00:00格式 timer.setText(s); break; } } }; private TimerTask task = new TimerTask(){ @Override public void run() { // TODO Auto-generated method stub if (startflag){ //如果startflag==true則每秒tsec+1 tsec++; Message message = new Message(); //傳送訊息1 message.what =1; handler.sendMessage(message); } } }; private OnClickListener listener =new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()){ case R.id.start: startflag=true; break; case R.id.stop: startflag=false; break; case R.id.zero: tsec=0; //TextView 初始化 timer.setText("00:00"); break; case R.id.end: finish(); break; } } }; @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }

2014-12-04

Android 中 startActivity的兩種寫法

方法一:

Intent piIntent = new Intent();
piIntent.setClass(this, ProgramInfoActivity.class);
startActivity(piIntent);
方法二:
startActivity(new Intent(this, ProgramInfoActivity.class));

Android開發中Eclipse 出現 INSTALL_FAILED_INSUFFICIENT_STORAGE的解決方式

在 Eclipse 開發 Android 時出現以下的錯誤

[2014-xx-xx 19:09:29 – xxx] Installation error:  INSTALL_FAILED_INSUFFICIENT_STORAGE

[2014-xx-xx 19:09:29 – xxx] Please check logcat output for more details.

[2014-xx-xx 19:09:29 – xxx] Launch canceled!

問題是出在APK無法安裝在手機上執行

解決方法有兩種

1.到 AndroidManifest.xml 添加下面的代碼,強迫將 apk 安裝在 sd 卡上

android:installLocation="preferExternal"


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="tw.example"
    android:installLocation="preferExternal"
    android:versionCode="1"
    android:versionName="1.0" >
2. 移除之前的程式 重新編譯 android APK 再執行

2014-12-01

Android中監聽tab頁面切換後所對應的動作

常常在設計Android軟體中,為了排版的精簡與分類,使用到TAB物件設計版面,這時就需要知道如何在特定的頁面被選取時,做出對應的動作。重點在於傳入的 tabId 也就是每個分頁的標籤名稱,有了這個資訊就可區分是哪一個頁面被選取。
public class MainActivity extends FragmentActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mTabHost = (TabHost)findViewById(R.id.tabHost);
        mTabHost.setup();
         
        mTabHost.setOnTabChangedListener(
                new OnTabChangeListener(){
                    @Override
                    public void onTabChanged(String tabId) {
                        //do what you want to do
                        Toast.makeText(getApplicationContext(), "TabId=" + tabId, Toast.LENGTH_LONG).show();
                    }
                }
            );
    }
}

2014-11-28

Andorid軟體中設定TabHost的字體與樣式

在開發多個tab頁面的應用程式時,可能需要對每個tab的標籤做些修改,以下是相關的程式
import java.lang.reflect.Field;
import android.app.Activity;
import android.os.Build;
import android.R;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.TextView;
import android.widget.TabHost.OnTabChangeListener;
public class TabTest2 extends Activity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(com.TabTest2.R.layout.main);
final TabHost tabs=(TabHost)findViewById(com.TabTest2.R.id.tabhost);
final TabWidget tabWidget=(TabWidget) findViewById(R.id.tabs);
int width =45;
int height =48;
Field mBottomLeftStrip;
Field mBottomRightStrip;
tabs.setup();



tabs.addTab(tabs.newTabSpec("first tab")
.setIndicator("news",getResources().getDrawable(com.TabTest2.R.drawable.c))
.setContent(com.TabTest2.R.id.tab1));
tabs.addTab(tabs.newTabSpec("second tab")
.setIndicator("help",getResources().getDrawable(com.TabTest2.R.drawable.e))
.setContent(com.TabTest2.R.id.tab2));
tabs.addTab(tabs.newTabSpec("third tab")
.setIndicator("setting",getResources().getDrawable(com.TabTest2.R.drawable.efg))
.setContent(com.TabTest2.R.id.tab2));

tabs.setCurrentTab(0);


for (int i =0; i /**
* 設置高度、寬度,不過寬度由於設置為fill_parent,在此對它沒效果 */ tabWidget.getChildAt(i).getLayoutParams().height = height; tabWidget.getChildAt(i).getLayoutParams().width = width; /** * 設置tab中標題文字的顏色,不然預設為黑色 */ final TextView tv = (TextView) tabWidget.getChildAt(i).findViewById(android.R.id.title); tv.setTextColor(this.getResources().getColorStateList(android.R.color.white)); /** * 此方法是為了去掉系統預設的色白的底角 * * 在 TabWidget中mBottomLeftStrip、mBottomRightStrip * 私有變數,可以通過反射來獲取 * * **/ if (Float.valueOf(Build.VERSION.RELEASE) <= 2.1) { try { mBottomLeftStrip = tabWidget.getClass().getDeclaredField ("mBottomLeftStrip"); mBottomRightStrip = tabWidget.getClass().getDeclaredField ("mBottomRightStrip"); if(!mBottomLeftStrip.isAccessible()) { mBottomLeftStrip.setAccessible(true); } if(!mBottomRightStrip.isAccessible()){ mBottomRightStrip.setAccessible(true); } mBottomLeftStrip.set(tabWidget, getResources().getDrawable (com.TabTest2.R.drawable.linee)); mBottomRightStrip.set(tabWidget, getResources().getDrawable (com.TabTest2.R.drawable.linee)); } catch (Exception e) { e.printStackTrace(); } } else { } View v = tabWidget.getChildAt(i); if(tabs.getCurrentTab()==i){ v.setBackgroundDrawable(getResources().getDrawable(com.TabTest2.R.drawable.shine)); } else { v.setBackgroundDrawable(getResources().getDrawable(com.TabTest2.R.drawable.seven)); } } /** * 當點擊tab選項的時候,更改當前的背景 */ tabs.setOnTabChangedListener(new OnTabChangeListener(){ @Override public void onTabChanged(String tabId) { // TODO Auto-generated method stub for (int i =0; i < tabWidget.getChildCount(); i++) { View v = tabWidget.getChildAt(i); if(tabs.getCurrentTab()==i){ v.setBackgroundDrawable(getResources().getDrawable(com.TabTest2.R.drawable.shine)); } else { v.setBackgroundDrawable(getResources().getDrawable(com.TabTest2.R.drawable.seven)); } } }}); } }
也可以用這種方式
TabHost tabHost = getTabHost();
for(int i=0;i<tabhost.getTabWidget().getChildCount();i++) 
        { 
            TextView tv = (TextView) tabhost.getTabWidget().getChildAt(i).findViewById(android.R.id.title); //Unselected Tabs
            tv.setTextColor(Color.parseColor("#ffffff"));
        } 
        TextView tv = (TextView) tabhost.getCurrentTabView().findViewById(android.R.id.title); //for Selected Tab
        tv.setTextColor(Color.parseColor("#000000"))

2014-11-04

Java語言中 static / final 基本用法

static:

可以用來宣告一個函數或者變數,當一個函數或者變數被宣告為static時,

就具有唯一值的概念。此函數或變數永遠只佔著一組記憶體空間。不管該類別被new幾個object,

該值永遠都會是一樣的。

class test{
     static int iValue = 0;
     public test() {
     }
}
public class main {
     public static void main(String[] argv)  {
          test test1 = new test();
          test test2 = new test();
          System.out.println(test1.iValue);
          test2.iValue  = 10;
          System.out.println(test2.iValue);
          System.out.println(test1.iValue);
     }
}

當test這個類別裡的資料成員iValue被宣告為static型態, 

在main function裡面分別去new兩個object出來。 分別是test1, test2,

先印出test1的iValue出來(印出0,初始值是0), 

接著我們在test2這個object去將iValue設值設為10,

再去印出test2的iValue出來(此時會印出10), 

這時候再印出test1的iValue出來會發現印出的是10。 

原因是因為我們將iValue宣告成static了。而iValue成為一個共同的變數。 

這就是static 的一個特性,

特性一:永遠會是一個唯一值!

且static 還有一個特性,

特性二:static可以透過類別直接存取使用!

意思是我們其實不需要new 一個object出來。

也可以透過類別直接去存取該變數
class test{
     static int iValue = 0;
     public test() {
     }
}
public class main {
     public static void main(String[] argv)  {
          System.out.println(test.iValue);
     }
}

在main中,並沒有new一個object出來,也可以存取到test這個類別中的資料成員(iValue)

final: 相較於static,final的概念就容易了解許多。

可以用來宣告一個類別、函數、或者變數。 

類別:當宣告在類別上時,該類別就無法被繼承。

函數:當一個函數被宣告為final時,則繼承他的子類別無法覆寫 

變數:當一個變數被宣告為final時,意思是他是一個常數,是無法被修改的。 

使用static final需要注意一點,如上面所述說的,當我們有一個常數要宣告時, 

會宣告如下

public static final String strValue = "abc";

為什麼宣告這樣會有個風險呢?

因為java在進行compile的時候, 會將宣告成static final的變數,直接包進去程式裡面。 

System.out.println(strValue);

當你有類似存取strValue的code時,我們去把java會把compile成的class,decompile來看,

會發現它變成如下的code

System.out.println("abc");

風險在於若不是用易除邊譯除錯的IDE來寫程式,

有可能會誤將strValue的值改為"123"或其他值, 若忘了重新compile,

有其他物件去存取strValue就會發生錯誤。

我們可以利用static block來進行宣告的動作,如下:

static String strValue;
static{
     strValue = "abc";
}

如果用這樣的宣告方式,即可避免上面述說的問題!

private / protected / public 存取修飾詞說明

在說明這四個關鍵字之前,我想就class之間的關係做一個簡單的定義,對於繼承自己的class,base class可以認為他們都是自己的子女,而對於和自己一個目錄下的classes,認為都是自己的朋友。
private, protected, public 為C++類別中的存取設定
其用法如下

private
private宣告的函式或變數 只能由自己類別所使用
不能由外部使用 連繼承自己的類別也不能使用
以下為合法使用法
class A
{
private:
  bool m_bFlag;
public:
  void pub_funA()
  {
    m_bFlag = true;
  };
}
承上 以下為錯誤使用法
class B : public A
{
public:
  void pub_funB()
  {
    m_bFlag = false;
  };
}
類別B繼承A但仍舊不能使用其private成員
以下也是錯誤的
A obj_a;
obj_a.m_bFlag = false;


protected
protected 就比 private 鬆了一點 繼承類別准許存取 protected 成員 但外部使用仍然禁止
以下為合法使用法
class C
{
protected:
  int m_nValue;
}

class D : public C
{
public:
  void pub_funC()
  {
    m_nValue = 0;
  };
}
以下這樣用是錯誤的
C obj_c;
obj_c.m_nValue = 1;
當然下面這樣也是錯的
C *pobj_c;
pobj_c->m_nValue = 1;


public
public就不用說了 就是怎麼用都可以 
作用域   當前類 同一package 子孫類 其他package
public     OK    OK    OK      OK
protected     OK    OK       OK     x
friendly      OK    OK     ×        x
private    OK     ×       ×    
不寫時默認為friendly