Sean's Note

2015年5月13日 星期三

Using StateListDrawable

想要在 View 上呈現 Click 的效果(顏色的變化),用 XML 來實作,只要在 drawable
檔裡定義 selector 屬性,然後將該 drawable 設給該 View 的 android:background
即可。範例如下:
  
// color_selector_view_bg
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item 
        android:state_pressed="true"
        android:drawable="@color/grey"
         >
    </item>
    <item 
     android:drawable="@color/white"
         >
    </item>
</selector>
  
<view>
  android:id="@+id/view_header" 
  android:layout_height="wrap_content" 
  android:layout_width="wrap_content">
  android:background="@drawable/color_selector_view_bg"
</view>

但想要用程式碼來實作的話,就稍微麻煩一些些,得用到 StateListDrawable 這個類別,
來設定個別狀態所對應的 drawable。加入的順序不同,行為也不同,越廣義的狀態要
晚加入,例如 state_enabled 就應該要最後加入。
以下是範例:
  
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, new ColorDrawable(colorGrey));
stateListDrawable.addState(new int[]{android.R.attr.state_selected}, new ColorDrawable(colorWhite));
stateListDrawable.addState(new int[]{android.R.attr.state_enabled}, new ColorDrawable(colorwhite));
view.setBackground(stateListDrawable);

另外要注意,每個 view 不能共用 StateListDrawable,尤其是應用在 ListView 和
GridView 上,否則按下 A view,B view 也跟者改變了。

Show error on EditText to inform user

現在才發現原來 EditText 有個 showError() 的方法,

可以提示使用者輸入錯誤或未輸入的提示。

Ref: http://www.technotalkative.com/android-show-error-in-edittext/

2015年4月9日 星期四

Calculate UILabel for proper height

在多國語言中,最常見發現的問題就是字串 truncated,所以在拉 UILabel 時,
高就不能設死,如果設太小,落落長的語言如西文和德文,就會 truncated,
設太多又會讓簡短的繁簡中留有太多空白,這時就可以用計算的方式來設得剛剛好。
NSString 底下有個方法可以用來計算:

- boundingRectWithSize:options:attributes:context:

+ (CGFloat)getMaximalHeightForText:(NSString *)text withFont:(UIFont *)font withWidth:(CGFloat)width {
    CGRect frame = [text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) // Give CGFLOAT_MAX for height to calculate desired height.
                                      options:(NSLineBreakByWordWrapping | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) 
                                   attributes:@{NSFontAttributeName:font}
                                      context:nil];
    return frame.size.height+5; // 5 is added for compensation.
}

2015年4月7日 星期二

[讀書筆記] Refactoring - Chapter7 - 8

Chapter 7. Moving Features Between Objects

Move Method
如果一個在 Class A 的函式 M 用到了更多 Class B  的變數或方法,那是不是可以考慮將函式 M 移至 Class B?

Move Field
與 Move Method 同理。

Extract Class
檢視一個 Class 是否可以利用 Move Method 和 Move Field 在拆分為多個 Class。

Inline Class
與 Extract Class 反之。

Hide Delegate
Client 想取得 Class A 的 delegate Class B 的某資訊時,不需將 Class B 完全揭露給 Client,可以在 Class A 新增一個函式將資訊回傳即可。

Remove Middle Man
與 Hide Delegate 反之,如果上述新增的方法太多,就直接揭露吧。

Introduce Foreign Method
當在使用一個無法更動的 Server Class 時,考慮在 Client 裡新增一個以 Server Class 為參數的函式吧。

Introduce Local Extension
當有太多的 Foreign Method 時可考慮 subclass 或 wrapper Server Class。Wrapper 比較麻煩的是得寫出原 Server Class 所提供的函式。

Chapter 8. Organizing Data

Self Encapsulate Field
對於 Class 自身的成員變數,可不可以直接存取,還是要透過 accessors,有兩方學派的人馬各持己見。如果 Class 本身有可能被 subclass,且 Subclass 對於成員變數有不同的定義或計算方法,accessors 仍然是解決的方法,此外,直接存取也無妨。

Replace Data Value with Object
當資料可能有其他子資料或行為時,考慮把它寫成物件吧。

Change Value to Reference
EX: 把原本 Order 與 Customer 的組合關係(Composition),改成一般的結合關係(Associations),同名的 Custom 應該只有一個 Object。

Change Reference to Value
EX: 把原本 Company 與 Currency 的結合關係(Associations),改成組合關係(Composition)。我們不在乎兩個 "USD" Currency 是不是同一個 Object,只要知道他們相等就好。

Replace Array with Object
Array 應該是拿來放有相同型態的物件集合,如果要放不一樣的,不如使用物件吧。

Duplicate Observed Data 
Observer pattern 

Change Unidirectional Association to Bidirectional 


Change Bidirectional  Association to Unidirectional


Replace Magic Number with Symbolic Constant
這還蠻常用的,提高可讀性。

Encapsulate Field
封裝成員變數,public to private + accessors。

Encapsulate Collection
add + remove - setter method。

Replace Record with Data Class
封裝成員變數,public to private + accessors

Replace Type Code with Class
實用。

Replace Type Code with Subclass
如果 type code 影響了 class 的行為,考慮繼承吧。

Replace Type Code with State/Strategy
詳見範例。

Replace Subclass with Fields 
用 Factory Method 來取代 constructor,詳見範例。

2015年3月27日 星期五

[iOS8] 隱私權設定

iOS 的隱私權,譬如說“相機”,一但被應用程式請求過,
在“設定”->“隱私權”-> “相機”的列表就會出現該應用程式,
而下次應用程式就無法在請求權限,使用者只能到設定去更改,
即使重新安裝也一樣。若開發時,想要測試,可以到“設定”->“一般”->“重置”->
“重置定位服務與隱私權”去重置設定,這樣應用程式就可以再次請求使用者。

2015年3月12日 星期四

[讀書筆記] Refactoring - Chapter2 - 6

Chapter 2. Principles in Refactoring

Refactoring (noun): a change made to the internal structure of software to make it
easier to understand and cheaper to modify without changing its observable behavior.

Refactor (verb): to restructure software by applying a series of refactorings without
changing its observable behavior.


The Rule of Three: Three strikes and you refactor. 
開始做第一件事情的時候,就毫無顧忌的直接做吧。當開始做第二件相似的事情,瞥一下重複的地方,但還是一樣照做。直到第三次又做相似的事情,就重構吧!

Chapter 3. Bad Smells in Code

看完第六章再來看這章可能會比較清楚。

Duplicate Code

Long Method
當發現有註解(Comments)在說明一區甚至是一行的程式碼時,可以考慮將其取出,另寫成函式(Extract Method),配上清楚的函式名稱,也許會更清楚易懂。

Large Class
當一個類別極為龐大時,通常也隱含的許多變數的存在,當一個類別有許多變數,那也有極大的可能出現重複的程式碼,此時我們可以用 Extract Class 或 Extract Subclass 來解決問題 。

Long Parameter List


Chapter 6. Composing Method

前面的章節落落長說了一堆,終於來到比較有重點的一章。

Extract Method
如同其名,大概也是最常用到的 Refactoring 方法。可分成三種情形:
1. 沒有區域變數 - 直接就把一段程式碼搬到另一個函式 void newMethod()。
2. 有區域變數,且其值不因新函式而改變  - 把區域變數當成新函式的參數 void newMethod(int param1, ...)。
3.1. 有區域變數,且其值因新函式而改變,且供後段程式碼所用 - 把區域變數當成新函式的回傳結果 int newMethod()。
3.2. 有區域變數,且其值因新函式而改變,但不為後段程式碼所用 - 把區域變數當成新函式的區域變數。

Inline Method
剛好與 Extract Method 相反,當一個函式裡的程式碼,簡單到一看就知道在做什麼時,可以直接把函式拿掉,而把程式碼搬進 Caller 裡。

Inline Temp
這好像比較少遇到,例子中是說如果有一暫存變數只被簡單的 expression 賦值了一次,然後就當作結果回傳,這個暫存變數可以被省略,直接回傳 expression 。

Replace Temp with Query
如果有一暫存變數只被賦值了一次,可以用一個函式將其取代,結果雖然會發現新的函式因為取代了暫存變數而被引用了多次,感覺上會有效率上的問題(因為重複計算了多次),但繼續 refactor 下去時,有時會有意想不到的發現(程式碼變得更乾淨清楚了)。

Introduce Explaining Variable
這可以用來提高條件判斷式的可讀性,如果一個條件判斷式裡有很多條複雜的邏輯判斷,我們也許可以將其分別取出,用幾個命名清楚的布林變數來取代。也適用於複雜的 expression。不過有時也會考量用 Extract Method。

Split Temporary Variable
簡單來說就是不要讓一變數重複賦值,如果有不同目的,那就在宣告一個新的變數吧!

Remove Assignments to Parameters
不要讓函式裡的參數再次賦值,這並不會引響外部的結果,又容易造成混淆,定義一個新的變數吧!

Replace Method with Method Object
有些很長的函式難以用 Extract Method 來 Refactor 時,就可以用此法。建立一個 Class 來取代原函式,原 Class、參數和區域變數成為新 Class 的成員變數。

Substitute Algorithm
砍掉重練!(誤)



2015年2月17日 星期二

initWithNibName 和 loadNibNamed 方法的區別

initWithNibNameloadNibNamed 都可以用來建立 Custom ViewController,
// 延遲載入,用到時才會值
MyViewController *viewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];

MyViewController *viewController = [[[NSBundle mainBundle] loadNibNamed:@"MyViewController" owner:self options:nil] objectAtIndex:0];

但是在 NIB 上的設定會有不同:
initWithNibName 的 XIB 需要在 Placeholders 設置 File's Owner 的 Custom Class,
View Controller 則不須設置 Custom Class。而且拉的元件是 UIView。
反之,用 loadNibNamed 的 XIB 不能在 Placeholders 設置 File's Owner 的 Custom
Class,而 View Controller 則須設置 Custom Class。而且拉的元件是 UIViewController。
若兩者誤用,就會跑出 setValue:forUndefinedKey 的錯誤。