2011年12月25日 星期日

Android學習_建立程式內(application)的全域變數(Global Variable)

在Activity間傳遞變數可以利用Bundle的方式來處理,但是如果當Activity太多,又每一個都可能運用到相同的參數,如每一個頁面都要去判斷登入者來決定可使用的功能,這時候可能會將值儲存在所謂的全域變數。

步驟:
1. 建立繼承於Application的類別

package tw.com.ola;
import android.app.Application;
public class GlobalVariable extends Application {
public String UserID = "";
}
說明:
*必須要繼承Application
*可以自行撰寫getter或setter,就跟一般的類別檔一樣
*上述程式碼僅建立一個外部可讀寫的變數UserID

2. 設定AndroidManifest

<application android:name=".GlobalVariable" android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Activity_CheckAndroid" android:label="@string/Activity_CheckAndroid_Title" android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
說明:
*一般AndroidManifest中有一個application標籤,原本沒有android:name屬性,將第一步驟建立的類別檔加入,例:android:name=".GlobalVariable"。

3. 將值放入與將值讀出

//放入
GlobalVariable globalVariable = (GlobalVariable)context.getApplicationContext();
globalVariable.UserID = key_UserID;

//讀出
GlobalVariable globalVariable = (GlobalVariable)context.getApplicationContext();
String UserID = globalVariable.UserID;
說明:
*如此就可以將各個Activity都需要的變數,進行儲存與讀取的動作。

2011年12月20日 星期二

iOS_於XCode4.2 Empty Application建立ESRI iOS開發環境

如果使用XCode4.2版在建立新專案的時,就不會出現ArcGIS的template,所以可能要自己建立相關的環境(或許過一陣子就有template可以用),既然要建立環境當然就是從Empty Application開始最有意義。

步驟:
1. 建立Empty Application專案

2. 取個名字,將該專案建立起來。

3. 加入ArcGIS library(於專案Build Phases內的Under the Link Binary with Libraries按“+”鍵),ArcGIS library並不在預設之內所以要點選“Add Other...”(預設位置:${HOME} /Library/SDKs/ArcGIS/iOS.sdk/usr/local/lib)。預設${HOME} /Library/是隱藏的資料夾,必須在command line輸入“chflags nohidden ~/Library/”,完成後${HOME} /Library/其實叫做“資源庫”。

4. 因為ArcGIS的library有使用到一些預設的framework與library,所以空專案必須要將其內容都加進去。(資料來源)
********************************************************
CoreGraphics.framework
CoreLocation.framework
Foundation.framework
QuartzCore.framework
UIKit.framework
CoreText.framework (dependency introduced at v1.8)
libstdc++.dylib (dependency introduced at v1.8)
MediaPlayer.framework (dependency introduced at v2.0)
MobileCoreServices.framework (dependency introduced at v2.0)
libz.dylib (dependency introduced at v2.1)
Security.framework (dependency introduced at v2.1)
********************************************************
連同上一步驟加上去的共12項。

5. 設定Build Setting(搜尋Other Linker Flags增加-all_load與-ObjC)

6. 設定Build Setting(搜尋User Header Search輸入$(HOME)/Library/SDKs/ArcGIS/iOS.sdk/usr/local/include/**)

7. 都設定完成後,就可以先執行一下,看是否有跳出錯誤。

8. 接下來就可以測試是否可以正確接到ESRI的Map Service
h檔

#import
#import "ArcGIS.h"
#define kTiledMapServiceURL @"http://your url/ArcGIS/rest/services/MapServer"

@interface AppDelegate:UIViewController {
AGSMapView *_mapView;
}
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, retain) AGSMapView *mapView;
@end


m檔

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize mapView = _mapView;

-(void) initApp
{
CGRect appRect = [[UIScreen mainScreen] applicationFrame];
_mapView = [[AGSMapView alloc] initWithFrame:appRect];
[self.window addSubview:_mapView];

AGSTiledMapServiceLayer *tiledLayer = [[AGSTiledMapServiceLayer alloc] initWithURL:[NSURL URLWithString:kTiledMapServiceURL]];
[self.mapView addMapLayer:tiledLayer withName:@"Tiled Layer"];

[tiledLayer release];

AGSSpatialReference *sr = [AGSSpatialReference spatialReferenceWithWKID:102443];
double xmin, ymin, xmax, ymax;
xmin = 281082;
ymin = 2739078;
xmax = 341217;
ymax = 2791975;

// zoom to the United States
AGSEnvelope *env = [AGSEnvelope envelopeWithXmin:xmin ymin:ymin xmax:xmax ymax:ymax spatialReference:sr];
[self.mapView zoomToEnvelope:env animated:YES];
}

-(void)dealloc
{
[_mapView release];
[_window release];
[super dealloc];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self initApp];
[self.window makeKeyAndVisible];
return YES;
}

9. 都完成後,可以執行看看效果。


如此我們就可以從一個完全空白的專案到一個TiledMapService讀進來。

2011年12月18日 星期日

巨龍之魂_死亡之翼的狂亂 KO

經過發呆→秒滅→發呆→秒滅→發呆→秒滅→發呆→秒滅,終於在忘記開英勇的狀況下看到了動畫!

感謝:阿龍、飛魚、辰藍、飆魔、真影、小乾乾、KAKA、洛琪、嘉嘉、我!


終於不是我是關鍵了! @O@

2011年12月15日 星期四

SQL Spatial學習_利用既有數字欄位計算空間欄位

若在資料表中已有坐標資料,就可以利用其直接計算空間欄位。

語法:
update CPDATA SET Shape =geometry::STPointFromText ('POINT('+ cast(E as varchar) +' '+ cast(N as varchar) +')',102443)

iOS學習_ESRI iOS SDK download and run MapViewDemo

等待很久的MacBook Pro終於在星期二到貨,真是喜憂參半,喜的是在課程結束前到貨,憂的是也沒有別的理由說沒辦法測試了。首先要完成的就是安裝開發環境,並進行初步測試。

所以安裝完XCode後,接下來就是ESRI iOS SDK的安裝與MapViewDemo的執行。

1. 下載ESRI iOS SDK(位置)

*下載必須要先註冊為會員。

2. 安裝ESRI iOS SDK
目前下載的檔案為AGSAPIiOSV21.pkg,安裝很簡單幾乎不用進行什麼設定。


3. 執行MapViewDemo範例。下載完成後可以到ArcGIS Resource Center來看各項說明文件,於ArcGIS API for iOS/Getting Started/Using the Samples(網址)內有提到在安裝SDK時會附帶一個最基本的範例(MapViewDemo)。

並且提到該安裝位置為:${HOME}/Library/SDKs/Samples/MapViewDemo,找資料夾的方法為在桌面點選上面的:前往/ 前往檔案夾,並輸入~/Library/SDKs/Samples/MapViewDemo


4. 執行MapViewDemo
點擊專案檔就可以直接看到接著ESRI地圖服務的簡單範例。


接下來就是要開始測試基礎功能與實機的安裝,不知道要到何年何月 lol

2011年12月12日 星期一

jQuery筆記_拆解JSON

有時候我們所得到的JSON可能不會很單純的是下面這樣的格式。

{"A":"1","B":"2"}

有可能得到的是:

{
"Name" : "shi fu",
"Aliases" : {
"AName" : "ola"
},
"Item" : [
{
"name" : "location",
"type" : "esri",
"alias" : "ola",
"length" : 10
}
],
"Object" : [
{
"attributes" : {
"Name" : "ola wang"
}
}
]
}


如果我們使用:

$.each(response, function (key, val) {
alert(key);
alert(val);
}
就可以看到Aliases與Object後面跳出[object object],原因是有些val並非單一的數值,而是陣列型態。

所以如果我們需要取得Object裡面的attributes內的第一個名字"ola wang"

$.each(response, function (key, val) {
if (key == "Object") {
var Name = val[0].attributes["Name"];
}
});

jQuery除錯_訪問不同網域之服務出現「No Transport」錯誤

最近持續在一些"接近尾聲"的案子打轉,其中有一個案子幾乎都要使用javascript來處理,對於js不太靈光的我當然遇到很多問題,其中一個是使用ajax()去呼叫其他網域服務時會出現No Transport的錯誤。

環境:
1. ESRI ArcGIS Server所提供的服務。
2. 選擇回傳JSON格式。
3. 呼叫的服務與程式屬於不同網域

經過Google與實際測試後,有幾種方式可以正確取得數值。
1. 使用ajax(),dataType為html:

function getData() {
jQuery.support.cors = true;
$.ajax({
type: "GET",
dataType: "html",
url: url,
contentType: "application/json; charset=utf-8",
success: showData,
error: function (xhr, ajaxOptions, thrownError) {
alert("err: " + thrownError);
}
});
}

function showData(response) {
var obj = jQuery.parseJSON(response);
$.each(obj, function (key, val) {
//拆解JSON
});
}
說明:
1. 重點在於jQuery.support.cors = true;的設定。(官方說明)
2. 該方法所返回的response必須先轉成JSON格式,再進行拆解。

2. 使用ajax(),dataType為jsonp:

function getData() {
$.ajax({
type: "GET",
dataType: "jsonp",
url: url,
contentType: "application/json; charset=utf-8",
success: showData,
error: function (xhr, ajaxOptions, thrownError) {
alert("err: " + thrownError);
}
});
}

function showData(response) {
$.each(response, function (key, val) {
//拆解JSON
});
}
說明:
1. 將dataType設為jsonp(JSON with Padding),但是必須對方服務支援該方式。
2. 該方法所返回的response已為JSON格式。

3. 使用$.getJSON()

function getData() {
jQuery.support.cors = true;
$.getJSON(url,{}, showData);
}

function showData(response) {
$.each(response, function (key, val) {
//拆解JSON
});
}
說明:
1. 該方法所返回的response已為JSON格式。
2. 雖然看似比較單純,但是沒有提供可以做錯誤處理地方。

---------------------------------------------------

*上述三種方式於IE8與Chrome15.0.874.121皆可正常取得回傳數值。
*如果比較深入去查可以發現圍繞著XMLHttpRequest對於跨域的說明。(參考文件)
*如果自己要利用jsonp的方式來實現跨域,"似乎"安全性要格外注意。

2011年12月7日 星期三

Oracle除錯_ORA-01756: 引號字串未以恰當方式終止

通常我們會使用QueryString來傳遞網頁與網頁中的參數,當我們決定這麼做的同時,弱點掃瞄工具就會塞很多可能造成程式錯誤或SQL Injection的內容,今天幫同事修正弱點掃描的內容,其中一項就是因為掃描工具將「」塞到QueryString中,造成Oracle在查詢時出現『引號字串未以恰當方式終止』這個錯誤。

而根本的解決之道並不是將該值置換或是進行其他字串的處理,而是在進行查詢時以參數的方式代入查詢內容,而非直接組SQL的查詢字串。


Imports Oracle.DataAccess.Client

sSql = "select * from Table Name = :Name "
Dim sCon As New Oracle.DataAccess.Client.OracleConnection(Constr)
Dim sCmd As New Oracle.DataAccess.Client.OracleCommand(sSql, sCon)
sCon.Open()

Dim param As OracleParameter = sCmd.CreateParameter()
param.ParameterName = ":Name "
param.Value = Name
param.OracleDbType = OracleDbType.Varchar2
sCmd.Parameters.Add(param)


說明:
1. Oracle的參數用冒號(:)
2. 使用OracleDbType 要先引入Oracle.DataAccess.Client

上述參數設定部分也可以簡化成

sCmd.Parameters.Add("Name ", OracleDbType.Varchar2).Value = Name

2011年12月6日 星期二

Android學習_唯一值(Android_id、IMEI、Wifi MAC)

如果希望可以"鎖"某些事情只能讓特定機器執行,不管是手機上的功能、伺服器的服務或是系統的登入,都必須先取得一個唯一值,網路上也有許多取得唯一值得方法,看了很多相關的文章,似乎每一種方法都有些許問題,可能還是需要看狀況來決定該用哪種方式:

1. IMEI(android developer說明)
TelephonyManager tM=(TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
String imei = tM.getDeviceId();
Log.v("ola_log", "imei:" + imei);
權限:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
說明:手機的IMEI值。
缺點:由權限(READ_PHONE_STATE)可看出,IMEI值為"電話"的屬性,對於Wifi版本的平板會取得NULL值。


2. Android_id(android developer說明)
String android_id = Secure.getString(this.getBaseContext().getContentResolver(), Secure.ANDROID_ID);
Log.v("ola_log", "android_id:" + android_id);
說明:設備第一次啟動時產生的序號。
缺點:網路上部分文章指出,某些廠牌會有android_id重複的情況產生;當機器回原廠設定時,Android_id將會變更。


3. Wifi MAC(android developer說明)
WifiManager wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
Log.v("ola_log", "MacAddress:" + wifiInfo.getMacAddress());
權限:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
說明:Wifi的MAC。
缺點:網路上部分文章指出,機器沒有開啟Wifi時取不到MAC;但我關閉平板Wifi還是正常取到值。


4. Build.SERIAL(android developer說明)
String BuildSERIAL = android.os.Build.SERIAL;
Log.v("ola_log", "BuildSERIAL:" + BuildSERIAL);
說明:硬體的唯一值。
缺點:該值必須要API Level 9才支援;這個問題看專案類型可大可小,對於某些專案來說甚至不是問題。

5. UUID(android developer說明)
String UUID_Value = UUID.randomUUID().toString();
Log.v("ola_log", "UUID_Value:" + UUID_Value);
說明:randomUUID可以隨機的製造一個唯一值,或是利用其他內建方法製造唯一值(EX:nameUUIDFromBytes (byte[] name))。
缺點:必須要自己處理UUID的製造及儲存,若使用randomUUID則代表的是"該次安裝的唯一值"。



感覺一件很簡單的事情,其實也不單純勒。

2011年12月2日 星期五

SQL筆記_將某欄位重新編號

若是有需要將某欄位重新編流水號碼,可利用:

declare @num INT
select @num = 0
UPDATE TableName SET @num = @num + 1,IDField = @num


筆記一下。

2011年12月1日 星期四

Android/Java_將字串去掉科學記號

在寫與坐標相關的應用時,通常我們所接到的坐標值都是121XXXXXX、24XXXXXX,也就是一個是九碼代表經度,而八碼代表緯度,如果都這樣直接儲存並不會遇到什麼問題,但若是希望秀出來的值為121.XXXXXX、24.XXXXXX這種方式,就會進行一些"簡單"的轉換。

而當我們把121.123456這樣的數值乘以1E6時,會發現會傳回1.21123456E8這樣的科學記號,如果從頭到尾都使用數字也不會有太大關係,但若是中途有進行一些字串的轉換,就會變得有點麻煩,所以我們"可能"會希望還是以121123456這樣的方式表示:


DecimalFormat df1 = new DecimalFormat("#########");
String in_Xcoord = String.valueOf(df1.format(Float.parseFloat("121.123456")*1E6));
String in_Ycoord = String.valueOf(df1.format(Float.parseFloat("24.123456")*1E6));

主要是利用DecimalFormat,給予相對應的數值格式。

如果要將121123456轉成121.123456,只需要單純的轉換。

String fac_Xcoord = String.valueOf(Float.parseFloat(fac_Xcoord_1e6)/1E6);
String fac_Ycoord = String.valueOf(Float.parseFloat(fac_Ycoord_1e6)/1E6);


若是要將數字以金錢的方式呈現(每三位一個逗號),也可以利用這樣的方式。
new DecimalFormat(",###").format(ShowMoney) + "元";

2011年11月26日 星期六

做個腦還是做隻手?

大概在兩年前,有一位朋友說:「說實在話,錢灑出去寫程式的人隨便找」
大概在一年前,有一位大人說:「工程師要有生涯規劃,不可能一直寫程式」
大概在半年前,有一位資深說:「我們需要知道可以做到什麼程度,不用會做到」

所以,感覺上實際由自己的手從無到有,似乎是一個遲早要擺脫的模式?
更有另一種的感覺是,越早脫離似乎越成功?
那麼升級成思考的腦,脫離手的日子似乎是大家的願望?

每個人都想要當腦,都想要當PM,都想要當管理職,都想要"規劃",
那?誰要來"實做"?誰要當產出者?誰要來做事?誰要當手?
------------------------------------------------------
手與腦的貴賤

普遍存在社會上的認知,腦總是比手要高階,實際將專案完成的人總是比說話的沉默,管理、規劃及設計的確各個都是高深的學問,但實際將其完成的各項技術不也是需要認真的鑽研?

有另一句常常聽到的話「我老了,新的技術追不上了,但觀念讓我用一輩子」,半年前有一位大人問我「這樣的東西需要怎樣可以學會?」,我腦中浮現的不是語言的名稱、不是技術的辭彙,更不是什麼鬼架構,而是一顆砰砰跳的心,其實當下我一時語塞,只說「就想學,然後花時間就可以了」。

所以,老了真的是不去看些新東西的藉口嗎?
還在腦海裡面的觀念,真的可以用一輩子嗎?

或許三、五年,或十年後我還在這樣的產業,也會說類似的話,但其實自己心中明白,因為不想去經歷實作的辛苦,所以希望用所謂的'管理技術'來完成,可是我們竟然在心中默默的覺得自己比這些人更加高級與尊貴。
------------------------------------------------
手與腦的組合

這兩個簡單的角色,不外乎幾種配對:

1. 聰穎的腦+靈巧的手
一種簡直無敵的組合,許多高官都認為他們的人力分配就是這種組合,PM有無比的經驗與判斷能力,而工程師有著足以應付亂七八糟要求的能力,如果有這種夢幻組合,這將是一場愉快的工作體驗。

2. 有經驗的腦+拙劣的手
這是一種想起來應該很普遍的組合,由充滿經驗的資深人員,帶領並傳承相關的經驗與知識給年輕一輩,建立一種正向的工作環境,感覺上是一個很棒的模式,但通常跟下一種模式只是一線之隔。

3. 不懂的腦+懂得比腦多的手
這是第二種模式的變形,普遍發生在變化快速的產業,通常手上掌握的技術,會在兩三年內變成讓人皺眉的寂靜,而根紮不深的人,更容易變成一個自以為是的腦。

4. 停止的腦+無力的手
「悲劇」

5. 手腦合一
這是一種很奇妙的演進,有些人當腦,資源會自然而然的安排妥適,甚至三頭六臂;但有些人則必須手腦合一,手即是腦,腦即是手。
--------------------------------------------------
你為什麼是隻手?

1. 因為你會殺雞,剛進研究室的時候,指導教授跟我們講了一個小故事「殺雞的故事」,內容是:

一群人去當兵,有一天突然要殺一隻活跳跳的雞,排長問「誰會殺雞?」,一片靜默.......
排長眼睛一瞄,指著一個方向說「就你了」,
那方向的人狐疑,不知道是誰被指到了,
一位舉起手指著自己說「是我嗎?」
排長:「對!就是你!今天你殺雞」

就在一陣雞飛狗跳後,呆子A殺了人生的第一隻雞,往後雞來了誰殺?呆子A就這樣殺了一整個軍旅生涯的雞。

2. 因為你應徵手

打開104會看到有些公司徵PM,實際去談有時會發現,這一個職缺好像不用會什麼實質上的技術,管理、規劃似乎才是重點,所以很快的一個不懂的腦產生了。

曾經我跟一位在當PM的學姊聊天:
「學姊,你當PM,那有需要先會寫嗎?」
「我不會,但是我覺得我是一個好PM。」
「怎麼說勒?」
「因為我不會亂答應事情,不確定的功能我會先回來問工程師做不做得到」

會先徵求工程師的意見,已經變成一個好PM的判斷標準?我的以為是,事情的難易不應該"光由"實做的人來判斷,如果腦連這些事情都不能掌握,要怎麼相信規劃的東西可以執行?最後又是金玉其外..。

也有一種腦,認為別人的事情都相當的容易,
他們會說「就我所知,這東西沒什麼了不起的」
「我知道這對你很簡單」
「我處理好最困難的部分了,你就照著這規劃實作吧」
其實不管是實際很麻煩、困難的內容,被說的簡單至極,或是輕鬆簡單的內容,被說的難如登天,其實都是一個很詭異的事情。
---------------------------------------------------
轉型做腦,脫離技術文件的泥沼簡單嗎?

大人們會告訴你「工程師要有生涯規劃」,講起來好像要做哪方面的人由自己決定?,所以我們應該在某一個時刻致力於轉型,從實作的手慢慢往腦來前進,但其實在實際情況上也不全是這樣。

如果你不願意實作,那有兩條路:
1. 死的很慘,做什麼都不行。
2. 直接變腦,大人自然會分配靈巧的手給你,過的相對快樂。

如果你挺愛研究東西,也有兩條路:
1. 做什麼都行,無窮無盡的做到死。
2. 說「我老了,新的技術追不上了」,燒掉文件,殺掉開發工具加上沉默。

不管是哪一種,共通的特性是遠離那些技術,停止花時間在鑽研小東西上面,因為我們要做偉大的'管理';所以組織充滿「不懂的腦+懂得比腦多的手」,倚老賣老、老人不做事、說的比做的簡單,這樣類似的言語將會充滿整個辦公空間,而辦公室留晩的總是那些人。

所以就實質面,如果我是上面的人會怎麼希望?
正面的想:大家教學相長,不斷傳承,正向的成長。
負面的想:腦更外向的變成嘴,手安靜的做不做思考。

所以,原本認為總不能什麼都不懂,什麼都不自己實作的人,反而陷在於泥沼之中。
------------------------------------------------
「我們需要知道可以做到什麼程度,不用會做到」

對於這句話,腦的角度看,我認為不夠;手的角度看,我認為很幹,那麼,這句話應該是誰的定義?應該是「嘴」。
那腦該怎麼樣?「需要知道怎麼樣可以做到,不用完全會細節,但也不應完全不會」
那手呢?「需要找出實做的方法,掌握細節的項目」

所以看看你家的嘴、你家的腦、你家的手,
嘴有確切了解可以做到的程度嗎?
腦有實際明瞭怎麼樣可以做到嗎?
手有認真努力將細節做的更好嗎?
-------------------------------------------------
做個腦還是做隻手?

如果當腦,我還是希望自己仍然是一雙堪用的手,
如果當手,我還是希望自己擁有那一顆遠眺的腦。

根要紮,也需要新知的滋養。

2011年11月22日 星期二

Android錯誤排除_Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it

使用SQLite的時候,很有可能出現這個訊息:

『Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it』

意思是沒辦法讀取CursorWindow這個Cursor的第0行,請確定訪問Cursor之前,已經完成初始化的動作。

這時候,我們會一直去看有沒有初始化,然後找不到錯誤;

這錯誤有可能是因為去讀取沒有定義的欄位名稱。

例:
String dis_Explore = cursor.getString(cursor.getColumnIndex("dis_Explore"));
但是欄位中並沒有dis_Explore這個欄位。

這樣的錯誤很有可能發生在資料庫定義改變或是增刪欄位時。

2011年11月21日 星期一

要MP還是HP?

還是學生的時候,常在考試需要熬夜的時候,跟朋友說:
「你覺得hp高好?還是mp高比較高?」
通常不會有人知道我在說什麼...
「如果你現在睡覺,明天考試的時候你就擁有很高的血量,可以撐很久,但是都不會寫,可能撐完整場也打不到考卷的60%」
「但是,如果你現在熬夜看書,你就擁有很高的mp,可以用魔法打考卷,但是你可能撐不久,可能發兩發25就倒了」
「所以到底該早睡覺?還是該熬夜看書勒?」

總之,上面的對話,可以非常容易得到一個結論:「要在期限前努力的看書,然後在考試前充分的休息」,說起來這是一個廢話,但是廢話通常讓人做不到。

-------------------------------------------------------------
對應到生涯上,我們為了無形的考試,
「工作三年有百萬存款了嗎?」
「30歲,要結婚了嗎?」
「35歲,5子有幾個了呀?」
「40歲,是不是做了個官了?」
總之,有很多很多自己的希望與別人的期待。

那...如果我想要準備,好好考這些試,我到底該準備什麼?





就在我坐著想了10分鐘,應該要準備『更多的運氣』。

iOS學習_App生命週期

寫App的第一件事情就是生命週期,以一個簡單的流程圖記載一下。

- didFinishLaunchingWithOptions:撰寫程式初始化內容。
- applicationWillResignActive:撰寫資料儲存的內容。
- applicationDidBecomeActive:撰寫資料回復的內容。

iOS程式進到背景後,僅有限的網路功能(串流、上傳等)、GPS、撥放音樂,可以繼續進行,其他自行開發的程式都將會停止。

iOS學習_Objective-C超基本亂記2

第三堂課就在磅礡大雨中進行,為了往後可以複習一下觀念,還是補一下筆記。
------------------------------------------------------------

1.繼承

#import "要繼承的類別.h"

@interface className:要繼承的類別 {
}
說明:
- 繼承用":"
- 在繼承前記得先import

2. 覆寫
-進行覆寫時,不需要增加overwrite等關鍵字,取名相同則直接進行覆寫。

3. 使用父層方法
- 要使用父層方法,與許多程式語法相同,使用super。

4. 使用自己
- 要取得自己,使用self

5. 類別初始化
- Objective-c並沒有建構函式,若是要進行初始化,可自行以一般函式的寫法來達成。

h檔定義
- (ola *)initWith:(NSString *)nameValue age:(CGFloat)ageValue;

m檔實做
- (ola *)initWith:(NSString *)nameValue age:(CGFloat)ageValue;
{
self = [super init];
if (self)
{
self.name = NameValue;
self.age = ageValue;
}
return self;
}
說明:
- 實做時,先呼叫父層的init方法,若繼承於NSObject,則代表呼叫[NSObject init],成功建立實體後,塞入初始化數值,最後回傳自己。(也就是例中ola類別)

6. id動態型別
- 有時候一個Array裡面可能有多種類別,這時可以利用動態型別來承接。

if ([temp isMemberOfClass:[ola class]])
判斷temp是否為ola型別。(無多型概念)


if ([temp isKindOfClass:[ola class]])
判斷temp是否為ola型別的相關種類(有多型概念)


if ([temp respondsToSelector:@selector[initWith]])
判斷temp是否有initWith的方法

7. try的使用

@try
{
}
@catch (NSException *e)
{
NSLog(@"%@,%@" , [e name], [e reason]);
}
@finally
{
//對錯都執行的部分
}
說明:
- [e name]取得錯誤名稱
- [e reason]取得錯誤原因

8. 自行拋出錯誤

[NSException exceptionWithName:@"錯誤名稱" reason:@"錯誤原因" user:nil];


9. 靜態屬性
- Objective-c沒有靜態屬性,所以是以C++的語法,寫在import之後,@interface之前。

static int count;

使用時,必須要先告知編譯器要使用外部的屬性

extern int count;
count++;


10. 靜態方法
於h檔與m檔中,以+號開頭

11. 補充類別(Category)
h檔

#import "ola.h"

@interface ola (olaOps)
- (void) size;
@end
說明:
- #import "ola.h":import要被補充的內容
- @interface ola (olaOps):olaOps類別名稱,ola 未被補充的類別

m檔

#import "olaOps.h"

@implementation ola(olaOps)

- (void) size
{
NSLog(@"17");
}

@end
說明:
- 實際使用時,類別與補充類別都要import,Xcode才會有提示可以使用。
- 可以利用該方式,補充NSString,NSObject等基本類別。

12. 通訊協定(Protocol)
- 類似其他語言的interface的概念。
- 使用時使用大括號,多個Protocol用逗點隔開。
- 可利用@required與@optional來指定是否一定要實做。

13. 前處理(define)
- 告知編譯器要置換的文字(replace的概念)

#define ola ABCDEFG
說明:
- 程式中看到ola的字樣,會先置換成ABCDEFG,編譯器才會編譯。
- 後面沒有";",中間沒有"="

-------------------------------------------------------

對沒碰過的人來說,內容真的很多。

2011年11月14日 星期一

asp.net小抄_抓取使用者IP

有時候會被要求,後端的管理系統必須要記載使用者的IP。

原本是使用:
string UserIP = HttpContext.Current.Request.UserHostAddress;
但發現無法抓取到源頭的IP。

後來改為:

string UserIP = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

2011年11月11日 星期五

iOS學習_基本的記憶體管理

很快的iOS課程來到第二堂,繼第一堂課後又加深了一些感想:

1. Mac電腦需求越來越急迫。
2. 如果要一邊寫iOS、一邊寫網站、一邊寫Android一定會很痛苦。
---------------------------------------------------------
第二堂課最重要的是『記憶體管理』
1. 若是有建立記憶體空間,便需要進行記憶體管理

NSString *Value = [[NSString alloc] initWithFormat:@"%@",@"ola"];
說明:
- [NSString alloc]為宣告記憶體空間。
- initWithFormat為初始化的方法。

2. 可以藉由Reference Counting來看出被引用的次數

NSString *Value = [[NSString alloc] initWithFormat:@"%@",@"ola"];
[Value retainCount];
說明:
- retainCount方法可以查詢出該物件記憶體空間被引用的次數,若Counting為零,則該空間會被回收。

3. 不良管理將會造成dangling(要使用、但已被回收)與memory leak(不需要了,但沒回收)的問題。
- dangling錯誤訊息為:EXC_BAD_ACCESS,一發生即會造成程式當機。
- memory leak不會立刻造成程式錯誤,但有可能慢慢將手機記憶體耗光而當掉。

4. 若是已經宣告的記憶體空間,被別的指標引用到,則可以手動將引用次數加一(Reference Counting+1),來確保該空間不會被回收

[Value retain];


5. release方法並不是"直接"將該空間釋放,而是將Reference Counting -1

[Value release];


6. 常數、沒有alloc的型態不需要進行記憶體管理,iOS會自行處理。

7. 標準有管理記憶體的類別寫法
h檔

#interface

@interface OlaClass:NSObject{
NSString *name;
}
- (void)setName:(NSString *)vname;
- (NSString *)name;
@end


m檔

#import "OlaClass.h"

@implementation OlaClass

- (void)setName:(NSString *)vname;
{
if (name)
{
[name release];
}
name = vanem;
[name retain];
}

- (NSString *)name;
{
return name;
}

- (void)dealloc
{
[name release];
[super dealloc]
}

@end
說明:
- 寫setter時,若是沒有進行retain,則會造成指定的值釋放記憶體後,產生dangling pointer的問題,所以必須retain,保留該空間。但若都不進行release則會造成Reference Counting不斷加上去,最後產生memory leak問題,所以應在每一次setter的時候先判斷是否存在,進行release。

- 經過上述處理,複寫dealloc方法,並執行release,來將加一的Reference Counting減回去。

8. 若是用合成方法,則可以更快速的完成上述類別
h檔

#interface

@interface OlaClass:NSObject{
NSString *name;
}
@property NSString *name;

@end


m檔

#import "OlaClass.h"

@implementation OlaClass
@synthesize (readwrite, retain ,nonatomic) name;

- (void)dealloc
{
[name release];
[super dealloc]
}

@end
說明:
- 合成方法:h檔加property 、m檔加synthesize
- 讀寫:readwrite(有getter、有setter)
- 讀寫:readonly(只有getter)
- Setter設定:assign(不做記憶體操作)
- Setter設定:retain(做retain並且release)
- Setter設定:copy(做copy並且release)
- 單一性:atomic(考慮多執行緒)
- 單一性:nonatomic(無考慮多執行緒)←通常用這個
- 用合成方法,還是要複寫dealloc

2011年11月9日 星期三

iOS學習_Objective-C超基本亂記

經過這些日子一陣混亂以後,已經註定要接觸apple系列,就在沒有不喜歡但也沒有像當初Android一樣超熱血的情況下,開始了第一堂課程,結束後有幾個感想:
1. 應該趕快買一台MacBook Pro。
2. 讓很多人來學Objective-C的apple真的很厲害。
3. 要比Android花更多更多的時間,才能到'自以為會一點'的心情。
4. 如果跟之前一樣,一直在各種語言(專案)轉換,痛苦程度倍增。

筆記:
1. 變數宣告
int age = 18;
(C裡面的int宣告,Objective-C撰寫常會與c語言混合撰寫)

NSString *name = @"ola";
(NSString為Objective-C的型態,前有NS前置詞皆為Objective-C內容,*號為c語言指標概念。因NSString為物件,物件前必須加@符號)

CGFloat size = 18.5
(CGFloat為小數宣告)

NSDate *today = [NSDate date]
(NSDate為日期物件,取得今日日期的方法為[NSDate date]→執行NSDate內的date方法,取得今天日期)

2. log的用法
NSLog(NSString)
例:NSLog(@"今天%@,姓名:%@,年紀:%d",today,name,age);
NSString要顯示時,可先用格式符號代替,再於後面對應相關變數。
%@:物件
%d:整數
%f:浮點

3. 類別相關事項
一個分為兩種檔案:.h及.m
.h:標頭檔,用以定義屬性、方法與繼承何者
.m:實作的內容

.h檔

#interface

@interface OlaClass:NSObject{
NSString *name;
CGFloat age;
}

- (NSString *)PrintName;
- (void)setName:(NSString *)vname;
- (NSString *)name;
- (void)setAge:(CGFloat)vage;
- (CGFloat)age;
@end

說明:
1. 宣告name與age兩個屬性(不需要宣告public等內容,因為所有的屬性都不能直接讀取,要存取屬性都必須藉由getter與setter)
2. {}中間為宣告屬性的地方,}與@end中間為宣告方法的地方。
3. - (NSString *)PrintName; →宣告PrintName實體方法,回傳NSString,'-'為實體方法。+為靜態方法。
4. - (void)setName:(NSString *)vname; →宣告vname實體方法,沒有傳回值,但傳入一個NSString。


.m檔

#import "OlaClass.h"

@implementation OlaClass

- (NSString *)PrintName
{
return [NSString stringwithFormat:@"name: %@,age:%d",name,age];
}

- (void)setName:(NSString *)vname;
{
name = vanem;
}

- (NSString *)name;
{
return name;
}

- (void)setAge:(CGFloat)vage;
{
age = vage;
}

- (CGFloat)age;
{
return age;
}

@end

說明:
1. @implementation與@end中間為實做方法的地方。
2. 在寫Set時,必須避開原本的屬性名稱,無法直接命名相同,而用類似this.name的方式表示指定不同的變數。

4. 方法使用
[name length]:使用name的length方法。
[ola setname:@"ola"]:使用ola的setname方法,並傳入NSString。

5. 其他
*每一版的Xcode都會有蠻大幅度的改變,升級時要注意。
*模擬器模擬兩指操作:option+滑鼠
*模擬器模擬螢幕旋轉:command+方向鍵
*寫程式的提示鍵與取消提示都是esc鍵
*didFinishLaunchingWithOptions為啟動時執行的方法

加油加油~~~~凹嗚~~~~~

2011年11月6日 星期日

Android學習_Spinner改變字型大小(TextSize)

在Android2.X版時,Spinner會跳出一個類似遮罩的選單,方便使用者點選,這時候有些人會覺得該選單字體太大,會希望可以做一些改變。

但到了Android3.X版時,Spinner的預設模式卻變成像網頁的下拉式選單一樣,對於手指較大的人在操作上卻顯得有些不太方便,所以反而會希望將該字體變大。

總之,不管是變大變小,Spinner改變字體時有兩個部分:

第一部分為顯示選單值的地方,第二部分則是選單的項目。

改變方式:
1. 準備一個TextView為Base的XML檔案

<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
style="?android:attr/spinnerDropDownItemStyle"
android:textSize="25sp" />


2. 指定spinner樣式時,使用上述XML檔
A. 改變第一部分字體:(big_spinner_dropdown_item為上述檔案)

SimpleCursorAdapter adapter = new SimpleCursorAdapter(context, R.layout.big_spinner_dropdown_item,cursor, new String[] { PutFieldName }, new int[] {android.R.id.text1});


B. 改變第二部分字體:

adapter.setDropDownViewResource(R.layout.big_spinner_dropdown_item);


所以若是剛好用cursor放入spinner內,又希望兩部分字體都變大:

if (cursor.getCount()>0)
{
cursor.moveToFirst();
SimpleCursorAdapter adapter = new SimpleCursorAdapter(context, R.layout.big_spinner_dropdown_item,cursor, new String[] { PutFieldName }, new int[] {android.R.id.text1});
adapter.setDropDownViewResource(R.layout.big_spinner_dropdown_item);
spinner.setAdapter(adapter);
}

2011年10月25日 星期二

asp.net_圖片縮放_另存縮圖_Image Resize

有一類很普遍的應用:

我們可以藉由各種方式取得使用者上傳的的照片到File Server,而另一面則藉由網際網路以不同的介面來觀看這些上傳的圖片。而在這樣單純的應用裡,一值都有一個讓人關心的問題「使用者可以更快看到圖片嗎?」

然而,我們都瞭解想要更快的看到圖片,必須:
1. 有更大的頻寬
2. 有更小的圖片

頻寬通常不是我們所可以決定的,所以"更小且適當"的圖片將是我們希望的。

所以,我們要在一個恰當的時機,把圖片縮小:
1. 上傳圖片前─於使用者端先將圖片縮小後再上傳。
2. 上傳圖片時─使用者上傳原始大小,再於伺服器端另存出縮圖。
3. 下載圖片時─於使用者下載圖片時,另存縮圖讓使用者下載。
4. 下載圖片後─於圖片下載到使用者端後,再進行縮圖動作。

這樣非一般性的處理,通常要在'一次性'的操作來執行是比較恰當的,所以我們應該在上傳前或上傳時進行,但這牽扯到File Server是否要儲存原始圖片,若是答案是必須的,那麼我們只有一個最恰當的時間做縮圖這件事情→於上傳圖片到伺服器後,將其另存一份縮圖檔;至於縮圖需要縮到什麼程度,就必須要視看圖端的需求。


protected void ChangeImageSize(string FileName, string FilePath , int SmallWidth)
{
System.Drawing.Image img = System.Drawing.Image.FromFile(FilePath + FileName);
ImageFormat ThisFormat = img.RawFormat;
int FixWidth = SmallWidth;
int FixHeight = FixWidth * img.Height / img.Width;

Bitmap ImgOutput = new Bitmap(img, FixWidth, FixHeight);
ImgOutput.Save(FilePath + "small_" + FileName, ThisFormat);

img.Dispose();
ImgOutput.Dispose();
}

若以行動裝置目前的照相機,一張相片大約為1.5mb,在另存為640*480的圖片後約為47kb,對於顯示縮圖能夠達到很好的效果,也可以讓使用者介面操作起來更為順暢。

2011年10月21日 星期五

Android錯誤排除_Activity ActivityName has leaked window com.android.internal.policy.impl.PhoneWindow DecorView@40d0ad20 that was originally added here

有時候在關閉Android的Activity時,會跳出"Activity ActivityName has leaked window com.android.internal.policy.impl.PhoneWindow DecorView@40d0ad20 that was originally added here "這個錯誤訊息。

原因:有Dialog未關閉,但Activity就關閉了。

解決方法:在Activity結束時,呼叫dismiss()方法將其關閉。

@Override
protected void onDestroy() {
LoginProgressDialog.dismiss();
super.onDestroy();
}

Android錯誤排除_close() was never explicitly called on database

如果使用SQLite儲存資料,然後在除錯時都會去看log了話,就有可能看到一個錯誤訊息:"close() was never explicitly called on database"。

原因:開啟連線,但未關閉。

解決方法:
1. 於適當的地方呼叫SQLiteOpenHelper的close()方法。
2. 所謂適當的地方,也就是我們使用完的時候,通常是在Activity關閉時;所以可以複寫onDestroy方法,判斷SQLiteOpenHelper是否存在,若存在則將其關閉。

@Override
protected void onDestroy() {
super.onDestroy();
if (dbHelper != null)
{
dbHelper.close();
}
}

單一簽入(SingleSignOn)的簡單作法_Part3_自製單簽架構

如果你需要的單簽是,於A登入→關閉瀏覽器→開啟B則自動登入B,那可以換一篇文章了。

如果你需要的單簽是,於A登入→按下某鈕轉至B→並自動於B登入,或是於不同的介面(A、B系統、手持裝置等)使用同組帳密登入,那可以看一下

架構:

說明:
1. 在資料庫增加tokenInfo的表格,並且建立UserID、token、isActive與updatetime欄位。
2. 以WebService撰寫三個服務,分別為:
A. CreateToken:依照輸入的帳號、密碼產生token,並存入tokenInfo表內。
B. CheckToken:由傳入的token至表tokenInfo,以isActive與updatetime判斷是否有效,若有效傳回UserID,並且更新時間。
C. GetUserInfo:判斷token是否有效,若有效傳回所需的使用者資訊。

情境:
1. 打開A系統,輸入帳密以後,藉由CreateToken產生token,於A系統點擊B系統之超連結,並將該token值置於url參數,轉址至B系統;B系統於pageload去判斷是否有token,若有則利用CheckToken判斷token是否有效,有效則自動登入。

2. 行動裝置將可利用該方式,與Web系統使用同一個帳號密碼,於行動裝置上呼叫WebService來建立token,爾後需驗證之操作則進行CheckToken的動作。



註:產生token的方式,則可以簡單的利用random的方式於字串陣列中隨機組成。

註2:於手持裝置呼叫WebService時,可能會怕知道位址的人用別的程式呼叫,即可利用上述方式,做一個簡單的登入控管,並於每一個webService方法都做token的驗證,來避免被其他人呼叫的可能。

asp.net_有proxy時連接WebService

很多公司都有proxy,很多東西測不通也多是proxy的問題,如果要連接的WebService在公司外部,那就必須進行一些設定。


public string GetUID(string token1)
{
try
{
WebProxy myProxy = new WebProxy("http://proxy.domain.priv:80", true); //proxy主機位址
myProxy.Credentials = new NetworkCredential("帳號", "密碼", "domain"); //可使用proxy的帳號密碼
tw.gov.cp.se.AAMediator govpc = new tw.gov.cp.se.AAMediator();
govpc.Proxy = myProxy;
string govcpData = govpc.ValidateToken1(token1);
XmlDocument xml = new XmlDocument();
xml.LoadXml(govcpData);
string UID = xml.GetElementsByTagName("UID").Item(0).FirstChild.Value;
return UID;
}
catch
{
return "Error";
}
}

上面程式碼要改的有三個地方
1. 帳號:輸入可以穿過proxy的帳號
2. 密碼:就密碼
3. domain:依照proxy的名稱有所不同

單一簽入(SingleSignOn)的簡單作法_Part2_與我的E政府(自然人憑證為例)

因為專案的要求,與我的E政府進行單簽時,僅就自然人憑證的部分,所以以下用自然人憑證單一登入作為說明:

環境:
1. 程式:asp.net
2. 語法:C#
3. 系統登入機制:使用asp.net提供的帳號管理

準備工作:
1. 資料庫增加一個"asp.net帳號管理"與UserID的Mapping表,以身份證字號為key。
2. 撰寫新增帳號的function(功能:當遇到新使用者,利用Membership.CreateUser新增asp.net帳號,並同步寫入Mapping表)

我的登入流程:
1. 由系統按登入鈕後,轉址至我的E政府網站,並依照E政府規定格式擺上returnurl與level參數(例:https://www.cp.gov.tw/portal/Clogin.aspx?returnurl=導回頁網址&level=2)
2. 使用者輸入正確的Ping碼並驗證成功後,E政府會依照上步驟returnurl參數將網頁轉址回來。(註:returnurl必須與"既有服務檔案表"內填寫的網址相同)
3. 回到導回頁後,利用Request.Params["twGovT1"].ToString()取得token1,並適當地將其記錄下來。

string token1 = Request.Params["twGovT1"] == null ? "" : Request.Params["twGovT1"].ToString();

4. 利用token1向E政府提供之WebService取得身份證字號(服務位置:https://se.cp.gov.tw/SEWebApplication/AAmediator.asmx,服務名稱:ValidateToken1)

public string GetUID(string token1)
{
try
{
tw.gov.cp.se.AAMediator govpc = new tw.gov.cp.se.AAMediator();
string govcpData = govpc.ValidateToken1(token1);
XmlDocument xml = new XmlDocument();
xml.LoadXml(govcpData);
string UID = xml.GetElementsByTagName("UID").Item(0).FirstChild.Value;
return UID;
}
catch
{
return "Error";
}
}

若是環境有proxy設定,請參考:asp.net_有proxy時連接WebService

5. 利用步驟四得到的身份證字號與資料庫內的會員資料比對,找出會員UID。
6. 以該UID登入asp.net的帳號管理。

FormsAuthentication.RedirectFromLoginPage("user01", false);


簡單來說:
1. 由E政府取得身分證字號。
2. 由身分證字號找到Mapping表內的User。
3. 系統幫User登入。

單一簽入(SingleSignOn)的簡單作法_Part1_導讀

自從去年開始執行某個令人虛脫的案子後,「單一簽入、Single Sign On、整合登入介面」等等的詞句就一直在耳邊響起,也可能各個單位實在花太多錢在莫名其妙系統的建立上,所以不約而同的都提出希望可以改成單簽的架構。

但通常,之所以會有單簽的願望,狀況會有三個:
1. 是政府單位,他們配合「政府整合單一入口計畫」,所以你要跟"我的E政府進行"界接。
2. 該單位做了很多系統建置的案子,然後每次都不同廠商,所以有很多很多的登入介面。
3. 因為單簽很"飛遜",所以他自然而然將單簽寫到契約裡面了。

所以我們會有三種角色:
1. 要去使用我的E政府來進行單簽。
2. 要去用別的廠商所提供的單簽方案。
3. 要去提供別的廠商單簽。

如果,你要和"我的E政府"進行單簽,準備步驟如下:
1. 下載「e平台服務介接申請表」與「既有服務檔案表」申請表(位置)
2. 表內需要政府單位的OID,可以先上這裡查詢,若沒有了話趕快請承辦員發文。
3. 比較重要的是「既有服務檔案表」,上面必須填寫要進行單簽的網域位置(舊表格僅需填寫服務網址,新表格為WSDL服務位址與IP位址);但是在申請的時候通常不會知道正式的網域或是IP,所以這張最重要的就是填寫你要測試的位址(通常就是公司有對外的server)
4. 接來下交給承辦去簽文,然後等通過的消息。
5. 程式請看下篇。

如果,你要用別的廠商提供的單簽方案:
1. 問他
2. 問他
3. 問他

如果,你要提供別的廠商進行單簽:
1. 看下下篇。


單簽的另一個重點是,如果你在寫行動裝置程式,又你的程式必須進行一些簡單的權限控制,那你就很有可能必須要自己做一個簡單的單簽,讓你的Web與行動裝置使用。

2011年10月19日 星期三

系統裝機注意事項(SSL、負載平衡、Port)

1. 憑證可以申請一次,給很多台電腦用嗎?
Ans:可以,但是必須要先安裝至匯出請求檔的電腦上,再從該電腦連同金鑰匯出(pfx),就可以安裝到同一個DomainName下的電腦上了。

2. 負載平衡廠商問:有特殊需求嗎?要回答什麼?
Ans:必須確保同一批request中,都回到同一台電腦,也就是同一個IP Source都交給同一台處理,確保asp.net不會產生Viewstatus因為跳轉不同Server產生程式錯誤。

3. Port我都開了,還是不能連
Ans:於Server上安裝telnet(server 2008預設沒裝,安裝方法),利用"telnet ip port"來確認是否真的通了。

port即便開了,也有可能網管沒有設定特定電腦的連線,而還是不通,所以可以問『是否需要針對A電腦連B電腦做特殊設定來喚回他的記憶』

4. 備份VM為什麼演練完成以後,還要重新安排job?
Ans:因為每一台VM都有他自己的ID,當把VM移除再進行還原後,雖然機器內容是一樣的,但是對於VM來說ID已經改變,而備份排程式根據VM的ID來進行備份,所以當ID改變以後,就必須將排程重新設定,確保備份還原後的VM。

5. 80與443port是誰要開?
Ans:80 port為IIS架設以後預設使用的Port,理論上IIS正常服務,80 port也開好了;而443 port,為https所走的port號,必須要安裝SSL憑證,並於站台的"繫結"處設定,完成後Server將打開443 port。

Android學習_以Cursor為下拉式選單(Spinner)的來源,Text與Value取得方式

若是程式裡面運用到下拉式選單(Spinner),來源大概有三種:
1. 寫死在程式裡面
2. 寫在XML檔案裡面
3. 放在SQLite裡面(放預設值出問題請看本篇)

如果希望程式比較動態,通常我們會選擇放到SQLite裡面,然後做一個頁面讓使用者可以自行增減項目,所以我們可以由SQLite的查詢取得Cursor,放入也相當簡單:

if (cursor.getCount()>0)
{
cursor.moveToFirst();
SimpleCursorAdapter adapter = new SimpleCursorAdapter(context, android.R.layout.simple_spinner_item,cursor, new String[] { "ItemName" }, new int[] {android.R.id.text1});
spinner.setAdapter(adapter);
}

*以上使用Android預設的樣式simple_spinner_item來顯示下拉式選單

放進去以後,馬上面臨的下一個問題是,下拉式選單中可能需要Text與Value,也就是一個是顯示的內容,一個是程式要用的對應碼。

通常該對應碼,都會儲存在SQLite的某一個欄位中,所以思維應該是如何去取得被選項目的cursor。

有兩個時候必須要取得:
1. 點選spinner的時候
由第一個傳回的參數AdapterView轉為Cursor:

private Spinner.OnItemSelectedListener spinner_AddReturn_CaseType1_Selected = new Spinner.OnItemSelectedListener(){
@Override
public void onItemSelected(AdapterView arg0, View arg1, int arg2,long arg3) {
Cursor cursor = (Cursor) arg0.getSelectedItem();
String SelectItemCode = cursor.getString(cursor.getColumnIndex("ItemCode"));
}
};


2. 要儲存至某地的時候
直接取得後做轉換:

Cursor cursor_PrjIDType = (Cursor) spinner_AddReturn_PrjIDType.getSelectedItem();


取得Cursor後,就可以很輕鬆的讀取任何你希望取得的內容。

Android學習_自訂樣式_以按鈕為例(加上改變文字)

經過Android學習_自訂樣式_以按鈕為例的設定,可以將按鈕的背景改變,但很多時候我們會希望連同按鈕上面的字一同變化,那就必須要做多一點的設定:

1. 先經由前篇,完成背景設定的XML檔案(C步驟)

2. 新開一個於Drawable的文字設定檔

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:color="#777777" />
<item android:state_pressed="true" android:color="#1559B0" />
<item android:state_enabled="false" android:color="#ffffff" />
</selector>


3. 於res\values內的resources標籤內,加入style的設定描述

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Button" parent="@android:style/Widget.Button">
<item name="android:gravity">center_vertical|center_horizontal</item>
<item name="android:textColor">@drawable/btn_textcolor_selector</item>
<item name="android:shadowColor">#FF000000</item>
<item name="android:shadowDx">1</item>
<item name="android:shadowDy">2</item>
<item name="android:shadowRadius">0.2</item>
<item name="android:textSize">20dip</item>
<item name="android:textStyle">bold</item>
<item name="android:background">@drawable/btn_selector</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
</style>
</resources>

重點有兩個地方,textColor與background都是去讀取步驟1與2的設定檔,也就是按鈕按下時,背景會隨著背景設定檔改變,而文字則會依照文字設定檔改變,至於有沒有合併的撰寫方式,初步是沒有測出其他方式。

4. 最後,在按鈕的屬性上設定style

<Button
android:id="@+id/btn_main_Login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登入"
style="@style/Button"
></Button>


可以注意到一件事情,drawable的每一個設定,都是一個XML檔案,但Style卻是一個標籤,所以可以將很多Style都放在同一個resources標籤內。

Android學習_自訂樣式_以按鈕為例

寫程式就是這樣,一開始做一個簡簡單單介面,等到功能都可以達到了,就會開始想要改變樣式,希望程式界面變得更好看,這時候諸如我這樣美術細胞很少,大概只會深底白字、灰底深字,連美勞都做不好的一般工程師就會開始感到無力,但完全不做美工的世界也不知道在哪裡?

所以,想要更漂亮的介面,至少先學會怎麼改變,再想怎樣可以變得更好看。

如果要變更按鈕的樣式,大概有兩個方向,一個是"按鈕外殼的改變",另一個是"按鈕上面字的改變"

首先說明改變外殼的方式,方法分為兩種:
1. 使用程式碼的方式

A. 於res點擊右鍵,新增一個Android XML File

B. 輸入檔案名稱後,選擇create Drawable,並於root選擇selector

C. 新建完成以後,可以發現於selecto的標籤內只能加入item標籤,而該標籤內只能放上以android:state開頭的屬性,而觀察字面上的意思就可以猜出,這是用來判斷每一種狀態時,要顯示的按鈕樣式。如果希望一般狀態與按下按鈕時有所區別就可以利用兩個item標籤來設定:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<gradient android:startColor="#FFA10A" android:endColor="#FFB947" android:angle="90" />
<stroke android:width="2dip" android:color="#cccccc" />
<corners android:radius="3dp" />
<padding android:left="10dp" android:top="10dp" android:right="10dp" android:bottom="10dp" />
</shape>
</item>
<item>
<shape>
<gradient android:startColor="#1559B0" android:endColor="#3F92FF" android:angle="90" />
<stroke android:width="2dip" android:color="#cccccc" />
<corners android:radius="3dip" />
<padding android:left="10dp" android:top="10dp" android:right="10dp" android:bottom="10dp" />
</shape>
</item>
</selector>

D. 設定完上述檔案後,就可以到按鈕標籤處將該設定檔引入

<Button
android:id="@+id/btn_main_Login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登入"
android:background="@drawable/buttonstyle"
></Button>


那如果自己或是有人可以幫你做按鈕的底圖,當然也可以利用這樣的方式,將圖變為按鈕的外框。
2. 使用背景圖案的方式
直接在item標籤內設定要顯示的圖:

<item android:drawable="@drawable/pressed"
android:state_pressed="true"/>

2011年10月5日 星期三

人的一生



剛剛無意間在youtube上面看到一個有趣的日本動畫,現在大概進行到第10秒左右,生活中也真的很多事情就是比較再比較,快還要更快,好還要更好的在走...一直衝刺的目的是?

不知道從幾歲開始,我非常喜歡看老子、莊子等漫畫書,甚至翻了不下數十次,裡面有一篇也在講這件事情:

一個菜鴨不像其他鴨子玩樂,而是每天很認真的學英文、學電腦、成為最頂尖的菜鴨,但到了屠宰之時,他還是成為了桌上餚;到了天堂他問神明『我這麼認真,為什麼還是換來屠宰?我認真的學習難道錯了嗎?』,神說:認真過沒錯,玩樂過也沒錯。

總之,怎麼過是個人的選擇,有開心的小朋友、也有悲傷的小朋友;有陽光的青少年、也有陰沉的青少年;有熱血的上班族、也有憤世的上班族;有開明的父母、也有無理的父母;有歡樂的老人,當然也有哀怨的;大家的終點都一樣,所以人的一生才真正是一件"過程比較重要"的事情。

事物並不能夠分為極端的兩面,但唯有擁有一邊的經歷才能體會另外一面的時光,

而有菜鴨的衝才有可能感覺到閒暇的輕鬆氣氛。

如果人生是遊樂場,關園時能開心歸賦才真的是『 』。

Flex4學習_捲軸使用(Scroller)_flash做APP_Part6

系列文章:
1. Android學習_以WebView讀取SWF檔_flash做APP_Part1
2. Android學習_以Flash Builder 4.5製作Air放入Android_flash做APP_Part2
3. Android學習_為什麼Flash Builder 4.5做的APP可以跨平台_flash做APP_Part3
4. Android學習_頁面切換_flash做APP_Part4
5. Flex4學習_button change icon_flash做APP_Part5
6. Flex4學習_捲軸使用(Scroller)_flash做APP_Part6

在製作手機程式,很常會碰的問題是:要顯示的內容很多,但是螢幕不夠大。這個時候通常會使用捲軸來完成,而在Flash Builder 4.5製作Mobile專案時,有支援手勢滑動的標籤有兩種一個為List一個為Scroller。

只有測試Scroller就以Scroller來說明:

<s:Scroller x="120" y="10" width="70%" height="100%" >
<s:VGroup width="100%">
<s:Label id="lab_show" y="12" width="100%" text=""/>
<s:Image id="image_show" />
</s:VGroup>
</s:Scroller>

其實很簡單,就是利用Scroller標籤將需要捲軸的內容包起來,並且設定他的height屬性(若不設定將不會產生捲軸)

但其實這樣的東西我就找了兩個多小時 T.T。

Flex4學習_button change icon_flash做APP_Part5

系列文章:
1. Android學習_以WebView讀取SWF檔_flash做APP_Part1
2. Android學習_以Flash Builder 4.5製作Air放入Android_flash做APP_Part2
3. Android學習_為什麼Flash Builder 4.5做的APP可以跨平台_flash做APP_Part3
4. Android學習_頁面切換_flash做APP_Part4
5. Flex4學習_button change icon_flash做APP_Part5
6. Flex4學習_捲軸使用(Scroller)_flash做APP_Part6

因為Flex實在是太久沒用,之前學的Flex3已經有點忘記,這次用Flex4當然更是一直問人又Google,其中有一個很大的變化,就是在Flex3的時候,可以很簡單的設定icon的屬性就可以達成圖片按鈕的效果,但在Flex4把這個屬性拿掉了,轉而要利用skin的方式設定。

方法:
1. 建立一個skin檔案(右鍵新增一個Packge,再於新增MXML Skin)

<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
alpha.disabled="0.5">

<fx:Metadata>
[HostComponent("components.ImageButton")]
</fx:Metadata>

<fx:Script>
<![CDATA[
static private const exclusions:Array = ["labelElement"];
override public function get colorizeExclusions():Array
{
return exclusions;
}
]]>
</fx:Script>

<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>

<!-- UP ICON -->
<s:BitmapImage source="{getStyle('icon')}"
verticalCenter="0" left="5" includeIn="disabled" />


<!-- UP ICON -->
<s:BitmapImage source="{getStyle('icon')}"
verticalCenter="0" left="5" includeIn="up" />

<!-- OVER ICON -->
<s:BitmapImage source="{getStyle('icon')}"
verticalCenter="0" left="5" includeIn="over" />

<!-- DOWN ICON -->
<s:BitmapImage source="{getStyle('icon')}"
verticalCenter="0" left="5" includeIn="down" />

<!-- Text SKIN-->
<s:Label id="labelDisplay" textAlign="center" verticalAlign="middle"
maxDisplayedLines="1" left="0" bottom="0" color="#FFA10A">
</s:Label>

</s:SparkSkin >

新建好以後,可以發現Builder已經幫我們加了很多程式碼,而因為我們要做圖片的變更所以必須將UP ICON後面的程式碼補上,重點有三個:
A. 建立了四個BitmapImage,皆有includeIn的屬性,分別代表了:disabled(預設)、up(拿起)、over(滑動)與down(按下)四個動作,所以我們可以針對這四個動作給予不同的圖片。
B. 放置了一個名為labelDisplay的Label,『labelDisplay』為規定的id名稱,Builder將會把文字放到該名稱的Label內。
C. BitmapImage的source可以在這邊直接指定(source=Embed('images/car_right.png')),因為我們並不會希望一個按鈕就要建一個skin,會希望利用外面給定的方式,所以這邊source以關聯的方式指定(source="{getStyle('icon')})。

2. 於Button指定skin

<s:Button id="btn_Home1" label="A" skinClass="skin.button"/>

skin.button的skin是packge的名稱,button為剛剛建立的skin名稱

3. 指定圖片(利用Style)

<fx:Style>
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";
#btn_Home1 {
icon: Embed(source="images/car_left.png");
}
</fx:Style>

說明:
A. #btn_Home1表示指定id為btn_Home1的按鈕。
B. icon對應到skin內BitmapImage的source屬性。

效果:

Android學習_頁面切換_flash做APP_Part4

系列文章:
1. Android學習_以WebView讀取SWF檔_flash做APP_Part1
2. Android學習_以Flash Builder 4.5製作Air放入Android_flash做APP_Part2
3. Android學習_為什麼Flash Builder 4.5做的APP可以跨平台_flash做APP_Part3
4. Android學習_頁面切換_flash做APP_Part4
5. Flex4學習_button change icon_flash做APP_Part5
6. Flex4學習_捲軸使用(Scroller)_flash做APP_Part6

因為出發點為電子書程式的測試,所以在交接前也做了與其相關的內容測試,首先是頁面的切換。

於前面的文章有提到,利用Flash Builder 4.5所建立的Mobile專案,是以View為基礎,可以直接想成Android裡面的Activity,所以一開始最關心的莫過於要怎麼從A View切換到B View:

切換View的語法:

navigator.pushView(ViewClass:Class);

關閉View的語法:

navigator.popView();


若是你在剩下一個View的時候執行了navigator.popView()的語法,你會發現整個程式會變成只剩下白色的畫面,原因是即使你關閉了所有的View,他背後還有AIR的環境在繼續執行(相關觀念參考),所以若要關閉整個程式,必須要將AIR的環境一併關閉。

程式關閉語法

NativeApplication.nativeApplication.exit();

Android學習_為什麼Flash Builder 4.5做的APP可以跨平台_flash做APP_Part3

系列文章:
1. Android學習_以WebView讀取SWF檔_flash做APP_Part1
2. Android學習_以Flash Builder 4.5製作Air放入Android_flash做APP_Part2
3. Android學習_為什麼Flash Builder 4.5做的APP可以跨平台_flash做APP_Part3
4. Android學習_頁面切換_flash做APP_Part4
5. Flex4學習_button change icon_flash做APP_Part5
6. Flex4學習_捲軸使用(Scroller)_flash做APP_Part6

以Flash Builder 4.5來開發手機程式有一個好處,你所寫的一份程式碼可以編譯成IOS或Android的版本,原因是什麼?為什麼這樣做就可以達到跨平台的方式?

以Android為例,利用Flash Builder 4.5開發出來的APP並不是"直接"執行在Android Runtime之上,而是外面又包了一層AIR,所以我們寫的程式是執行在AIR之上,而AIR執行在Android Runtime之上,而adobe除了提供了Android的接口以外,也提供了IOS的接口;因為他們提供了兩個平台的AIR環境,而達成跨兩個平台的需求,也就是說如果又建立了WP7的AIR環境,那麼該程式將可以跨三個平台。感覺就像下圖:


雖然聽起來這是一個很棒的方式,但是這取決於Adobe所提供的AIR環境是否可以完整無縫的與其他平台整合,另外執行效能可能也是需要考慮的地方。

Android學習_以Flash Builder 4.5製作Air放入Android_flash做APP_Part2

系列文章:
1. Android學習_以WebView讀取SWF檔_flash做APP_Part1
2. Android學習_以Flash Builder 4.5製作Air放入Android_flash做APP_Part2
3. Android學習_為什麼Flash Builder 4.5做的APP可以跨平台_flash做APP_Part3
4. Android學習_頁面切換_flash做APP_Part4
5. Flex4學習_button change icon_flash做APP_Part5
6. Flex4學習_捲軸使用(Scroller)_flash做APP_Part6

Adobe在Flash Builder 4.5或CS5後,可以建立『Flex Mobile Project』。

建置的流程很直覺:
1. 決定專案名稱

2. 選擇預設的模板

3. 切換到Permissions可以指定需要的專案權限,以Android來說他預設勾選了網路連線(INTERNET)的權限。

4. 設定debug檔案產出的位置。

5. 專案及引入lib的設定。

專案建立完成:

觀察專案配置,可以看到他預設只產生一個View,我們可以拉幾個按鈕以後於專案點擊右鍵來執行看看。

若是第一次進行測試,可以先選擇Run Configurations來設定測試環境;如果要使用模擬器,可以選擇On desktop並指定哪隻手機,若是實機則可以選擇On Device。

效果:

雖然僅一個沒有讀取資料的簡單頁面,但程式起來的時間可以明顯感受到速度並不優且佔用的記憶體也相當的大(Sensation為23mb),但是程式完成後的效果比起利用WebView讀取SWF的方式好非常多,整體介面也與一般的APP並無差異,所以決定利用該方案繼續進行電子書的簡單測試。

Android學習_以WebView讀取SWF檔_flash做APP_Part1

系列文章:
1. Android學習_以WebView讀取SWF檔_flash做APP_Part1
2. Android學習_以Flash Builder 4.5製作Air放入Android_flash做APP_Part2
3. Android學習_為什麼Flash Builder 4.5做的APP可以跨平台_flash做APP_Part3
4. Android學習_頁面切換_flash做APP_Part4
5. Flex4學習_button change icon_flash做APP_Part5
6. Flex4學習_捲軸使用(Scroller)_flash做APP_Part6

昨天突然接到一個需求,有一個案子想要利用Flex或Flash來進行手機程式的開發,整體要求只有一句話『於手機上面顯示出電子書的感覺』,所以有三個可以做的方向。

1. 利用Android裡面的WebView讀取已經製作完成的SWF檔案。

2. 利用Flash Builder 4.5以上版本建立Mobile專案。

3. 直接利用JAVA Code來進行開發。

因為只負責測試環境,所以第三項就不考慮,因為預設是要交給會使用Action Script語法的人繼續開發。

---------------------------------------------------------------------------
為了單純,當然是希望直接用第一種方法,也就是利用Android的WebView來讀取SWF檔案。

作法:
1. xml的配置:只放下一個WebView。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

<WebView android:id="@+id/webView1"
android:layout_width="match_parent"
android:layout_height="match_parent"></WebView>

</LinearLayout>


2. 製作一個SWF檔案,放置到專案目錄(assets內)。
這邊要注意,製作的swf檔案必須先確認是否可以正常執行,若是發現編譯出來的swf檔案都只有底色,上面的按鈕及圖片等都沒出現,可以利用選單的Prject→Export Release Build來進行編譯。

**2012/11/19補充Export Release Build位置圖:

3. 撰寫Java Code。關鍵程式碼只有四句:
A. 找到webView
B. 設定該webView使用外掛。
C. 指定要讀取的url。(本機位置的語法:file:///android_asset/SmallBook.swf)
D. 利用loadUrl方法讀取該SWF。

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

WebView webView = (WebView) findViewById(R.id.webView1);
webView.getSettings().setPluginsEnabled(true);
String url ="file:///android_asset/SmallBook.swf";
webView.loadUrl(url);
}

接下來應該就可以在手機裡面看到swf檔案,但是整體執行起來的效果"應該"不會很滿意,所以接下來方案二。

2011年10月2日 星期日

Android小抄_將路徑轉為URI

如果已經取得了檔案的路徑的字串,那怎麼轉成URI呢?


String ImagePath = "file://" + 路徑;
Uri uri = Uri.parse(ImagePath);


重點在於要加上『file://』,才能成為合法的URI,不然該URI會無法表示相對應的檔案位置。

EX:file:///ola/android.txt

補充:
或是用Environment.getExternalStorageDirectory()取得sdcard位置。

TakePicFileName = "ola.jpg";
TakePicFilePath = Environment.getExternalStorageDirectory().toString() + "/DCIM/";
File tmpFile = new File(TakePicFilePath, TakePicFileName);
Uri uri = Uri.fromFile(tmpFile);

2011年9月30日 星期五

適應生活而朝向什麼的改變?

最近公務人員放榜了,幾家歡喜幾家愁,讓我不禁問自己『是不是應該考慮一下公務員這個職業』。

我:你覺得我應該去準備公務員嗎?
嘉:嗯?
我:但是我覺得那好像不太適合我去做,我能忍受在公文裡面翻來覆去嗎?
嘉:如果去做了,應該就可以慢慢適應吧!?
我:我能嗎?這好像跟我想做的事情相左耶。
嘉:你也適應現在的工作了呀!
我:我有適應嗎?我還是覺得很多事情很鳥。
嘉:但是以前比較氣憤的口氣,現在只是在敘述而已。
我:所以在情緒上已經默默接受嗎?
嘉:很多事情聽起來不合理,你也說的很平淡。
我:嗯。

一個人在某個環境待久了,似乎都還是會產生一些改變,不管是能力、言語、思想、體型,或最重要的心態;大概兩年前一位年輕人說:『我覺得這樣工作會沒有熱情』,一個老一輩的工程師回他『我做這幾十年來每天對工作都充滿熱情』,我想這句為了場面的假話,不知道有多少人在心裡竊笑;雖然我也不過工作3年半多,但我認識『人』這種生物也準備進入第30個年頭,如果『熱情』就是他傳遞出來的感覺,我很希望熱情消磨越快越好。

我想所謂的『熱情』當然不是該位資深工程師心中所充滿的東西,我們明白那是對事情的一種執著,一種信念,一種堅持的能量,而絕大部分的『工作』會慢慢地消磨這種能量,接著我們會說:『工作』給了我一個月固定的錢,支付著我的房貸、我的車貸、小孩的教育費、每年出國的旅遊基金(雖然我一樣都沒有),而我們越常對於工作抱怨能量下降的就越快,而能量越低就對工作的抱怨越多,惡性循環之下工作馬上就變成『真的只是工作』;接著我們會聽到有些人稱這個程序叫做『工作的歷練』,每一個剛進社會的年輕人都是一個帶有菱角的石頭,他們終究在社會這個泥漿裡越磨越光滑,然後我們會稱這件事情叫做『長大了』。

所以我聽到『你適應了』這句話,心中的感受不是升級,而是一種自我墮落的感覺,會不會在五、六年後,我也會昧著良心說出一些鬼話?就因為我又磨到更適應了?其實很多對於『長大』與『適應』的定義,包含了很多看更淡、計較更少、一種人生如浮雲的感覺,除了名與利,甚至包含『熱情』與『希望』。

我真的很希望對於『工作』除了金錢的獲得以外,有著更多對於自己或團隊『成果』的期待,是一種秀出來心中會微微興奮,一種要讓對方驚艷的感覺,而不是去比遮臉的速度。

總之,我從10分憤世忌俗的年輕人變成了9.9分,但我很希望未來的歲月可以有『說實話的勇氣』、『無奈中保有堅持』、『開創的熱情』與『平靜的心』。

最後願我未來告訴小孩的道理也一直落實在自己身上,而不是說出一種自己未達成的遺憾。

SQL 2008_令人無力的評估版到期

如果:

1. 你灌了SQL 2008評估版。

2. 你在180天看到到期訊息,才想起來這件事情。

3. 你怎麼利用維護升級都沒辦法解決SSMS沒辦法開啟的問題。

那麼我勸你立刻進行我最後的做法:

1. 備份你的資料庫檔案。

2. 移除你的SQL 2008。

3. 重新安裝他。

這時你需要一個重要的語法→如何移除SQL 2008(官方參考)

1. 開啟命令提示 cmd

2. 輸入:CD %ProgramFiles%\Microsoft SQL Server\100\Setup Bootstrap\Release

3. 輸入:setup.exe /ACTION =uninstall /FEATURES = Feature_List /INSTANCENAME = MSSQLSERVER

若要安裝,請參考→SQL2008R2完整安裝攻略

註:聽說未到期前可正常利用維護升級方式將其改為正版,但過期後只能利用重灌的方式,但是我也不能測試,我想如果你看到這篇文章,那你也不能測試了。如果你設定還記得了話,重灌到系統恢復只要30-40分。good luck

2011年9月28日 星期三

asp.net_openxml 換行標籤

前幾天發了一篇『利用open xml於word套版列印』,同事在使用時碰到一個問題:雖然資料庫有記載換行字元,但於word檢視時,卻沒有換行的效果。

之前曾經發過一篇『textbox→SQL→Label(換行符號處理)』來處理換行的問題,原理是找到換行字元將其置換成HTML的換行標籤(br標籤)

所以相同的道理是,在open xml想要做出換行的效果,就必須找到open xml的換行標籤。

答案:『<w:br />』

string changeline = "<w:br />";
ValueString.Replace("\r\n", changeline));

2011年9月27日 星期二

Android學習_getWritableDatabase called recursively.

如果有在Android裡面使用SQLite,都會知道要複寫兩個方法,onCreate與onUpgrade,若是希望程式安裝時,資料庫裡面就已經有一些預設值,那麼我們就會想在onCreate裡面insert需要的資料;但這時很有可能會碰到一個錯誤訊息:java.lang.IllegalStateException: getWritableDatabase called recursively.。

原因:在onCreate若要取得SQLiteDatabase的不能使用getWritableDatabase的方法。

解法:觀察onCreate方法中,已經預設傳入SQLiteDatabase,所以在onCreate方法內可以直接使用,而不需要另外取得。

原本的Insert

public long insert(String table, String fields[], String values[])
{
SQLiteDatabase db = this.getWritableDatabase();
/* 將新增的值放入ContentValues */
ContentValues cv = new ContentValues();
for (int i = 0; i < fields.length; i++)
{
cv.put(fields[i], values[i]);
}
return db.insert(table, null, cv);
}

於onCreate中使用的Insert

public long insertDefault(SQLiteDatabase db,String table, String fields[], String values[])
{
ContentValues cv = new ContentValues();
for (int i = 0; i < fields.length; i++)
{
cv.put(fields[i], values[i]);
}
return db.insert(table, null, cv);
}


差別也就只在於SQLiteDatabase的取得。

Android學習_uri轉byte

在進行圖片的處理時,常常會遇到需要將手機內圖片(照片)轉成byte再傳出去(例如facebook的圖片上傳)。

步驟:
1. 將uri傳成inputstream

ContentResolver resolver = getContentResolver();
resolver.openInputStream(uri);


*20120604補充第一步驟程式碼:
byte[] buffer;
ContentResolver resolver = getContentResolver();
buffer = readStream(resolver.openInputStream(Uri.parse(Picuri.toString())));


2. 將inputstream轉成byte[]

public static byte[] readStream(InputStream inStream) throws Exception {
byte[] buffer = new byte[1024];
int len = -1;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
byte[] data = outStream.toByteArray();
outStream.close();
inStream.close();
return data;
}


接著,就可以將byte[]擺放傳入自己或別人的方法中,完成圖片的傳遞。

2011年9月24日 星期六

Android小品_Ola Account

說也奇怪,最近回家打開電視,不是理財節目就是政論節目,『夢想街57號』是我比較愛'聽'的節目,裡面的阿娟將許多理財投資觀念用很淺顯易懂的方式闡述,但其實講來講去最根本的當然就是'開源節流';說到開源大家都有很多很多的方式,可以買股票、買基金、談加薪、換工作、兼小差、嫁個有錢人,但節流第一步則不是學會怎麼節省,而是先瞭解自己的現金流,所謂『瞭解現金流』其實也就是記帳。

遙想當年我還在宜蘭打籃球,研究室打電動(?)的時候,不管是用小本子或是excel,都還有一個很好的記帳習慣,但真正開始工作以後,反而停止了這個好習慣;前一陣子在很多廣告牆與公車上有一句曝光率很高的廣告詞『未來的你會感謝現在的你』,所以也讓29.8歲的我想到另一個廣告詞『30歲了,還在這裡做什麼?』。

總之,開源還要想辦法,節流總是可以做了,那記帳就是第一步!

那身為一個半路出家、底子不穩、腦袋很小、很想在生活中使用自己程式的工程師,我馬上就決定要寫自己的記帳程式。
-----------------------------------------------------------------------------
但是記帳本說:你太小看我了;所以剛開始寫沒多久就後悔了,記帳程式可以擴充的範圍實在太廣,想要寫出一個"完善的",似乎也不是一時半刻可以完成,身為一個沒什麼毅力跟腦力的29.8歲窮鬼,實在是不想等到40歲才開始記帳,所以在時程不變、人力不變、品質也不想變的情況下,只好降低程式規模,做人要果決,就在資料庫ola、程式ola、介面ola與計畫經理ola四個人討論的結果,決定做出一個世界超級無敵陽春的......記支出本。

需要的功能完全針對客戶ola的需求:
1. 要可以記下項目跟金錢。
2. 可以修改記下的內容。
3. 一進程式就可以看到本月與本日金額
4. 可以查詢歷史資料
5. 含有一些簡單的統計

所以,登~登~登~登!

1. 一進到畫面有本月份的食、衣、住、行、育、樂、其他的金額統計,接著是本日的各項花費列表,右下角是本月與本日花費的總計。

2. 按下『又花錢』按鈕會跳出輸入對話框,可以輸入項目、金額、日期、時間跟備註。

3. 點選列表會跳出修改的對話框,可以對所有項目進行修改。

4. 長按列表項目會跳出刪除項目的對話框。

5. 點選手機選單按鍵,會跳出歷史與匯出/入的項目。

6. 點選『歷史』項目,會跳到歷史資料頁面。上方為年、月、日的切換選單,若選年則會列出該年總花費、接著是顯示各項目花費、再下面是以月區分的總計;右下角可以切換查詢時間點,若是輸入2011年9月24日,則按下"年"時出現2011年總計、按下"月"出現2011年9月總計、按下"日"則出現該日期各細項。

7. 按下"月"顯示該月各天總計。(點選列表各天可切換到該天項目列表)

8. 按下"日"顯示該天各細項。(點選細項可進入修改頁面)

9. 因為一邊寫一邊用,怕會不小心把資料刪掉,所以增加匯出/入功能。(匯出JSON格式)


最後,許個願,希望這次記帳不要停下來!加油!

2011年9月23日 星期五

asp.net_利用open xml於word套版列印

在每一個單位,每一個處室,不管是公家還是私人,每一個有人在的地方,通常都會有一個需求:我要列印資料。

這時候我們應該可能也許,就會把資料匯成xls、doc、txt、csv,並且跟他說:印吧!

接著,他會說:我要按照我的格式(中式表格:A儲存格跟B左右合併、C跟D上下合併、標題字大之類的),那麼我們就會依照這個格式做一個相同的html檔案,把從資料庫撈出來的資料放到該放的位置,接著說:棒吧!按照你格式的表格。

接著,某些"精實的"外星人就會說:你這....感覺不是word,就是跟我原本的表格長得不太一樣,不能做一樣嗎?這樣我很困擾耶。

好!一般如我的工程師這時候就會放空,然後說服他:你看word跟我的html用word開根本就一模一樣、一模一樣、一模一樣(晃晃晃)

總之,以往在處理類似這樣的功能,都需要花費很大的力氣才能完成一張,有時候表格很複雜的時候更是讓人想要見見原本設計表格的人(?),最近同事又遇到類似的狀況,需求的單位"一定要"作出"一模一樣"的表格列印功能。

最後同事給我三個關鍵字:有空嗎、黑暗執行緒、套版列印跟一個網址
----------------------------------------------------------------

依照黑大的相關文章,也試做了一下整個流程,我個人是覺得效果非常的好,好到我覺得我以前是呆子。

觀念:office2007以後使用標準的openxml作為檔案的定義方式,所以我們可以藉由修改xml的方式來置換已經於word裡面的文字。所以我們可以做好樣板→置換文字→下載檔案的方式來完成套版列印的功能。

流程:
1. 下載需要的sdk(Open XML SDK 2.0 for Microsoft Office)
進到下載頁面以後,會看到兩個可以下載的項目(OpenXMLSDKTool.msi與OpenXMLSDKv2.msi),OpenXMLSDKv2是程式開發所需要使用的SDK,OpenXMLSDKTool則是觀看openxml的工具。


2. 安裝剛剛所下載的兩個msi檔案。

3. 開啟一個專案檔(asp.net),並將需要的dll複製到bin資料夾。
A. DocumentFormat.OpenXml.dll:這個檔案為OpenXMLSDKv2所附的,位置在C:\Program Files (x86)\Open XML SDK\V2.0\lib。
B. WindowsBase.dll:這個為framework原本就有的檔案,位置在C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0。

4. 準備一個docx的樣版(上面有提到office2007才更改為openxml的方式,所以該方法僅適用於docx檔案)。
這個樣板的範例是模仿黑大的方式,需要置換的內容利用"[$$]"包起來,但實際上這是一個提供程式辨別的代號,如果想要使用別種符號定義也可以。

5. 撰寫程式碼
A. 將要放入樣板的資料以Dictionary包裝,比如說我要放入四個資訊:

Dictionary<string, string> dataObject = new Dictionary<string, string>();
dataObject.Add("Name","Ola");
dataObject.Add("Tel", "123");
dataObject.Add("Address", "台北市");
dataObject.Add("Mail", "wangshifu1220@gmail.com");


B. 準備兩個路徑(一個為樣板的原始路徑、另一個為填寫內容後檔案的路徑)

C.置換openxml的程式碼(參考黑大文章)

public static byte[] MakeDocx(string tempFile, string templateFile, Dictionary dct)
{
File.Copy(templateFile, tempFile);
using (WordprocessingDocument wd = WordprocessingDocument.Open(tempFile, true))
{ parse(wd.MainDocumentPart, dct);}
}

private static void parse(OpenXmlPart oxp, Dictionary<string, string> dct)
{
string xmlString = null;
using (StreamReader sr = new StreamReader(oxp.GetStream()))
{ xmlString = sr.ReadToEnd(); }
foreach (string key in dct.Keys)
{ xmlString = xmlString.Replace("[$" + key + "$]", dct[key]);}
using (StreamWriter sw = new StreamWriter(oxp.GetStream(FileMode.Create)))
{ sw.Write(xmlString); }
}

整個道理就是程式在MakeDocx先複製一份樣板檔案,然後讀取WordprocessingDocument的內容,並且交給parse的函式;而在parse函式內將該內容以StreamReader.ReadToEnd()的方式讀取出來,並且去尋找出文件內有[$$]的地方,置換成我們預先儲存在Dictionary的內容。

所以上面[$Name$]的部分就會被置換成ola。

-----------------------------------------------------
依照上述的步驟我們就可以很方便的先處理樣板的docx,並且用置換字串的方式動態改變內容,再將完成的檔案讓使用者下載,來完成一個"完全一模一樣格式"的匯出或列印功能。

注意:在實作過程中,有可能會發生文字無法置換的情況,原因是在進行樣板docx設計時,有可能因為我們的操作,而讓辨識的內容(指文中的[$$])間存在了很多不應該出現標籤,而導致程式無法辨識,若要確認是否因為該原因可以利用Open XML SDK 2.0 Productivity Tool(OpenXMLSDKTool.msi安裝後)來開啟docx檔,即可觀看openxml的內容,用以確認。

2011年9月22日 星期四

Android學習_使用facebook sdk_Part3上傳照片

1. 完成開發facebook相關APP的準備工作
2. 將"文字訊息"貼上塗鴉牆

大部分開發者下一步就是希望可以上傳圖片。

可以先看一下之前傳文字到塗鴉牆的語法:

facebook.request("/me/feed", params, "POST");

第一個參數是要傳到的位置(/me/feed代表塗鴉牆)
第二個參數是傳遞的內容(使用putString方法放入)
第三個參數是傳遞的方法

好,由這邊可以知道要上傳圖片勢必要先找到放置的位置與傳遞的內容;而圖片的放置位置對於facebook來說就是"相簿",而在網路上傳遞圖片的內容當然就是Stream一類。

所以我們要找到上傳的位置,與將圖片塞入params(Bundle)的方法:
1. 上傳位置:如果你去開啟facebook的相簿,可以發現每一本相簿都有一個唯一的ID,而在facebook sdk裡面也當然就是以這個ID作為辨識的方式,所以位置是"/相簿ID/photos"。(相簿ID為15碼)
2. 傳遞內容:在Bundle裡面可以利用putByteArray將類似圖片的內容傳遞出去,所以不管我們要上傳的圖片來自於哪裡,都必須先將該圖片轉為byte[]的格式,再利用params.putByteArray("picture", buffer);的語法加入。

有了以上的各項資訊以後,我們還是不能把圖片正確的上傳,這是因為若是要上傳資訊到facebook就必須要有足夠的權限(當時貼上塗鴉牆的權限為publish_stream),而上傳圖片的權限為user_photos。

程式碼:
1. 加入適當的權限

private void facebookSetting(){
facebook.authorize(this, new String[] { "email","publish_stream", "read_stream","user_photos" },

new DialogListener() {
@Override
public void onComplete(Bundle values) {}

@Override
public void onFacebookError(FacebookError error) {}

@Override
public void onError(DialogError e) {}

@Override
public void onCancel() {}
}
);
}

2. 上傳圖片

public void postToWall_photos(String AlbumID ,String message , byte[] buffer) {
Bundle params = new Bundle();
params.putString("message", message);
try {
params.putByteArray("picture", buffer);
} catch (Exception e) {
e.printStackTrace();
}

try {
facebook.request("/" + AlbumID + "/photos", params, "POST");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

*注意:文字訊息規定於putString時,第一個參數帶"message";而圖片使用putByteArray的時候第一個參數帶"picture"。

*提醒:圖片不管是手機內的資源(uri),即時畫出來的圖片(Bitmap)或是一個Stream類的東西,都必須先轉換成byte[],才符合上傳的的格式。

*將uri轉byte[]請參考

如果因為本篇完成上傳圖片,請留:WOW!有圖有真相。

2011年9月15日 星期四

Android學習_GridView的使用

在使用者介面的安排上,我們常常會使用ListView來展示多組資料,就像下圖的感覺:

但有時候,我們要呈現的項目並不是這麼的寬,可能只是一些簡單的文字或是小圖片,而我們希望他們以格子的方式排列,這時候通常就會想到GridView這類的控制項。

之前操作ListView時,通常都會先定義一個表示"每一列"的layout檔案,來產生出不同風格的列表樣式;在GridView也是一樣,我們可以先定義一個layout來表示"每一格"的樣式:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

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

上面的程式碼表示呈現出GridView中的每一格都有兩個TextView可以放值,而且兩個TextView是呈現垂直排列。

除了定義自己的layout以外,我們必須在要呈現出GridView的Activity的layout上擺上一個GridView標籤:

<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridview_type"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:numColumns="7"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:columnWidth="90dp"
android:stretchMode="columnWidth"
android:gravity="center"
/>

有幾個GridView獨特的變數紀錄一下
1. android:numColumns:表示每一橫列要展示的個數(若設為auto_fit,似乎就是一行三個)
2. android:verticalSpacing:行跟行中間的間距
3. android:horizontalSpacing:列跟列中間的間距
4. android:columnWidth:每一格的寬度
5. android:stretchMode:格子拉伸模式(none:不要伸縮、spacingWidth:拉伸每列的間距、columnWidth:拉伸每格、spacingWidthUniform:均勻拉伸間距)

呈現出來的效果就像圖中紅框部分:


而要將資料放入的方法,還是應先將資料放入Adapter,再利用setAdapter的方法將資料置入。
可以參考以下三篇:
一維陣列:Android學習_ArrayAdapter的使用
自行處理的二維陣列:Android學習_SimpleAdapter的使用
Cursor的處理:Android學習_SimpleCursorAdapter的使用

Android學習_SimpleCursorAdapter的使用

之前有將一個已經組好的list倒入SimpleAdapter顯示,但我們使用SQLite時,若已經取得了一個Cursor,如果還要以上述的方式重新整理成一個list,就顯得有些不方便,所以我們需要的是另一個繼承於BaseAdapter的SimpleCursorAdapter。

首先,觀察一下SimpleCursorAdapter所需要的變數,共有五個:
1. Content(content)
2. Layout(int)
3. Cursor(Cursor)
4. from(String[])
5. to(int[])

由參數大致上就可以了解用法了,與其他的Adapter相同,我們可以先準備一個定義好的layout,並且將當中諸如textview等物件都放置好;並且以SQLite的Select方式獲得一個Cursor,最後將對應要顯示的內容以一個字串陣列的方式輸入from變數,程式碼如下:
1. 定義一個Layout(Android xml檔案)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<CheckedTextView android:id="@+id/listTextView1"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="2px"
android:layout_height="2px"
android:textColor="@drawable/green"
android:visibility="invisible"
>
</CheckedTextView>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<CheckedTextView android:id="@+id/listTextView_t"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@drawable/Orange"
android:textSize="40px"
>
</CheckedTextView>

<CheckedTextView android:id="@+id/listTextView2"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@drawable/green"
android:textSize="40px"
>
</CheckedTextView>
</LinearLayout>

<CheckedTextView android:id="@+id/listTextView3"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@drawable/gray"
android:textSize="25px"
>
</CheckedTextView>

<CheckedTextView android:id="@+id/listTextView4"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@drawable/gray"
android:layout_marginRight="10px"
android:textSize="20px"
>
</CheckedTextView>

</LinearLayout>
</LinearLayout>

2. Java程式碼:

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.listview_mitem,
OnedayCursor, new String[] { "_id","item","money","datevlaue","type" }, new int[] {
R.id.listTextView1,R.id.listTextView2,R.id.listTextView3,R.id.listTextView4 ,R.id.listTextView_t});
RecordListView.setAdapter(adapter);

說明:
1. R.layout.listview_mitem為剛剛定義的layout
2. OnedayCursor為查詢出來的Cursor
3. new String[] { "_id","item","money","datevlaue","type" }為要顯示的欄位,必須要對應OnedayCursor查詢時所定義的欄位名稱。
4. new int[] {
R.id.listTextView1,R.id.listTextView2,R.id.listTextView3,R.id.listTextView4 ,R.id.listTextView_t}最後一個則是指上述欄位應擺放到的物件為何,也就是layout裡面放的物件。


如此,我們就可以很輕鬆的將Cursor的內容,利用ListView展示出來。

所以不管是
一維陣列:Android學習_ArrayAdapter的使用
自行處理的二維陣列:Android學習_SimpleAdapter的使用
Cursor的處理:Android學習_SimpleCursorAdapter的使用(也就是本篇)
我們都可以將各項資訊放置到希望的位置。