June 11, 2013

SimpleAdapter and ViewBinder

2 comments:
ListView 是 Android 一個極為常用的元件,經常看見為了「用自己定義的 xml 來產生 ListView row」而自己實作了一個 BaseAdapter。這樣的作法也不能說不對,我一開始也是這樣。為了烤蛋糕自己做了一個烤箱的事情,幾次之後還是覺得有點怪其實絕大多數的時候我們都不需要額外實作一個 Adapter,直接拿 SimpleAdapter 來用即可。

SimpleAdapter 需要一堆資料,還要有資料對應到 View 的表格。首先看一筆資料,每一筆資料都是一個 Map,從 JSON 的格式來看就像是

var julian = {
    "name": "Julian Chu",
    "avatar": R.drawable.julian,
    "location": "Taiwan",
    "prefer": "blue"
}

產生 Julian 的資料就會是

Map<String, Object> julian = new HashMap<String, Object>();
julian.put("name", "Julian Chu");
julian.put("avatar", R.drawable.julian);
julian.put("location", "Taiwan");
julian.put("prefer", new Integer(Color.BLUE));

假設資料放在 List 裡面,
private void appendData(List<Map<String, Object>> records, User user) {
    Map<String, Object> record = new HashMap<String, Object>();
    record.put("name", user.getName());
    record.put("avatar", user.getAvatar());
    record.put("location", user.getLocation());
    record.put("prefer", user.getPreferColor());
    records.add(record);
}

接著看 SimpleAdapter 的 contructor
SimpleAdapter(Context context,
                List<? extends Map<String,?>> data,
                int resource,
                String[] from,
                int[] to)

白話文解釋就是
  1. 我要建立一個 SimpleAdapter,先給我 context
  2. 給我一串資料,放在 List 裡面
  3. List 裡面的每一筆資料格式都是 Map<String, ?>,也就是 key=String, value=任意資料結構
  4. 給我一個 layout xml 檔(resource),產生 view 的時候會拿它來當 template
  5. 對於 Map 裡面的資料,我要拿哪些欄位來用(from)
  6. 取出的欄位,要對應到 xml 檔裡面的哪些 view (to)

假設我們的 layout row.xml 如下,它有一個 TextView 與 ImageView


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@id/row_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@id/row_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <ImageView
        android:id="@id/row_avatar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>


把名字對應到 TextView,大頭照對應到 ImageView 就是
String[] from = {"name", "avatar"};
int[] to = {R.id.row_name, R.id.row_avatar};

SimpleAdapter adapter = new SimpleAdapter(
                                context,
                                records,
                                R.layout.row,
                                from,
                                to
                            );

如此一來,SimpleAdapter 就會透過 Map.get(from[0]) 取出名字,然後再 findViewById(to[0]) 找到 TextView,接著就 TextView.setText(); 設定名字

也因此只要設定好對應關係就可以產生我們要的 view 了,其他生成 view 的事情就交給 SimpleAdapter 去做,我們不用再插手/不用自己再實作一份。

至此我們會有疑問:我們僅僅只是對 string key 跟 view id 建立關聯,但是 SimpleAdapter 怎麼知道要 setText?

更進一步的問題就會是:我們想要對 View 做更多客製化,好比「根據每個 User 的 Prefer Color 設定該 row 的背景顏色」,該怎麼做?

ViewBinder

第一個問題的解答是,SimpleAdapter 對 TextView, ImageView, Checkable 有作預設的處理。findViewById 之後會用 if (v instanceof TextView) 來判斷是不是 TextView,找到 TextView 就把 data 拿來 TextView.setText(data)。

針對第二個問題,SimpleAdapter 有個 inner class 叫 View Binder,我們可以對它實作

class TheBinder implements SimpleAdapter.ViewBinder {
    public boolean setViewValue(View view, Object data, String textRepresentation) {
        if (data instanceof Integer) {
            Integer color = (Integer)data;
            view.setBackgroundColor(color.intValue());
        }
    }
}

接著把 from, to 多做一組 mapping 即可

String[] from = {"name", "avatar", "prefer"};
int[] to = {R.id.row_name, R.id.row_avatar, R.id.container};
adapter.setViewBinder(new TheBinder());


這樣子我們就多教了 SimpleAdapter 一件事:看到 Integer 就把它當成背景顏色來設定

其實這個例子寫得滿蠢的,希望能夠這樣有保留到最重要的精神

May 16, 2013

Samsung Galaxy S2 鏡頭磨損

No comments:
我用的手機是 Nexus One (當年的驚世之作現在變成老古董了),鏡頭外層的材質應該是玻璃之類的吧,相當堅硬;另外周圍還有一層凸起,手機平放的時候鏡面也不會直接接觸到桌面,因此 Nexus One 的鏡頭到現在沒有絲毫刮痕

老婆用的 S2 就不一樣了,竟然把鏡頭設計成背面最突出的部份,材質可能是壓克力之類,非常容易刮傷。

後來才知道好像不少人會上一層保護貼,因為我一直以為把鏡頭設計成不易刮傷的材質是「基本觀念」,怎知某些產品規劃的想法不是我這種憨人能夠參透,等我理解到這一點時,老婆的手機鏡頭已經磨損得相當嚴重,拍出來的照片就像上了柔焦特效一般。

底下這張圖片,上半部相當迷濛,那是因為鏡頭已經刮花了,下半部是修復後的結果,照片看起來就很正常了


到手機行裡問了一下發現換那一塊鏡頭蓋要好一筆銀子,反正都準備要換了,死馬當活馬醫,自己嘗試修復一下,大不了就弄得更糟然後整塊換掉

修復的方法相當簡單,只要準備以下器具
  • 電視
  • 牙膏
  • 眼鏡拭鏡布
  • C1000 的砂紙

牙膏不要參有鹽粒或是其他奇奇怪怪的東西,最單純的牙膏就好了。我在家裡找到最細的砂紙是 C1000 的就將就拿來用了,如果有更細(數字比較大)的砂紙也可以拿來試試看,要用粗一點的我是不保證會不會讓情況更糟,至少我用 1000 號的砂紙沒遇到問題。

首先用 C1000 的砂紙「非常溫柔、非常輕」地磨,不知道是砂紙太厲害還是鏡頭蓋太脆弱,沒兩下就看見鏡頭蓋整個霧白。

鏡頭蓋雖然霧白,但是摸起來相當滑順。接著用拭鏡布沾些許牙膏來擦拭,因為牙膏本身就是研磨劑,此時就會有拋光的效果。

此時再打開電視,一邊擦一邊看電視,過一下子就能看見效果

底下這張是修復前的特寫,鏡頭蓋上面有非常明顯的白色磨損



底下這張是修復完的結果,左右兩邊的白色部份因為不會影響到拍攝,所以我就懶得繼續磨了。而原來有磨損的地方都不見了,Yeah!


Update: Donald 學長說,牙膏用完之後再換粗蠟、細蠟,結果會更光亮!

April 30, 2013

mSATA + HDD on Thinkpad x220

No comments:
前一陣子發現自己的硬碟的聲音有點怪怪的,擔心出了問題而在緊急的時刻壞掉,開始著手換新硬碟的事情。雖然平常有備份的習慣,仍然把所有的生活照片都放在筆電裡頭,畢竟照片要是弄丟可是再多錢也買不回來,帶在身邊總是比較安心。身邊有阿宅勸敗 SSD,著實讓我心動了一下,經常編譯 image 的人都知道,花上一兩個小時編譯的時間有多麼煩人。不過拿 SSD 儲存照片感覺有點浪費

耳聞 SSD 有壽命問題,也有人說硬碟並不見得比較長壽。無論如何,如果我要用 SSD 必定是為了工作,也一定會用很粗暴的方式對待。看了 x220 的 [手冊] 才注意到有 mSATA 這個東西。

無論是選用 SSD 或是一般的 HDD (硬碟),都還有另外一個 mSATA,因此我選用 HDD + mSATA,它的優點是
  • 我仍然有足夠容量的 HDD 放我的照片
  • 工作中的程式碼就丟到 mSATA 上面
  • 系統放在 HDD 上頭,不必擔心 mSATA 掛掉,也因為吃重的工作都丟給 mSATA,HDD 的壽命應該要比之前還要久一點
把最繁重的讀寫隔離在獨立的裝置上,對我個人而言,這大概是最好的解決方案了。在 Facebook 的 [Thinkpad 正體中文社團] 詢問鄉親的意見之後,我買了 Plextor 64M5M,現在的 Android BSP 一份大概是 20+G,64G 已經足夠我用好一陣子。(等到玩壞了,128G 應該也降價了XD),再買個 WD 500G 的硬碟,兩個加起來 3990,還算能接受。

「mSATA 爛掉,程式碼找不回來怎麼辦?」身為一個阿宅,當然要隨時用 git 管理,隨時把程式碼備份在遠端呀!

底下稍微紀錄一下這兩天做的事情,給未來的我當作筆記 (兩天後的我一定會忘光光怎麼做=.=)

Install mSATA

mSATA 也不過兩片 SD-Card 大小,拆下幾顆螺絲之後,把鍵盤跟支撐手腕的蓋子打開,撥開天線之後就可以看到插槽,整個安裝非常簡單,Youtube 上面有許多教學。只是看到天線用膠帶很隨意地黏在那裡,感覺有點....(聳肩)

藍色與白色天線底下的就是放置 mSATA 的空間
裝完之後用 gparted 分割一下就好,最前端留了 1MB 之後我用 ext4 當作 filesystem。接著在 fstab 讓它 mount 起來即可

/dev/sdb1 /mnt/msata     auto    defaults,noatime,nodiratime,discard,errors=remount-ro  0       0

Change HDD

HDD 比較費工,由於 x220 有 7mm 的限制,我選的是 WD 藍標 500G 的硬碟,硬體的更換 x220 已經相當簡單了,也是 Youtube 看一下,隨便拆幾顆螺絲就搞定。軟體部份,我把 home 整個 tar 起來放到外接硬碟,裝完系統之後再解開放回(恰好我的 uid 都是 1000 :P)。

備份

# 先把 X window 關掉吧,然後該 umount 的東西先卸載,沒用的東西先清掉
$ cd /media/external_storage
$ rm -rf /home/myid/.cache
$ sudo tar cf myid.tar /home/myid

接著匯出安裝過的 packages list
$ sudo dpkg --get-selections > dpkg_selections
那 etc 目錄的東西也可以備份一下

安裝系統

安裝 Debian 很輕鬆,抓下 netinst 的 CD ISO 檔之後,直接 dd 塞到 usb stick,就可以用 usb 開機安裝了。x220 有兩個 USB2.0 以及一個 USB3.0 的 port,如果 usb 碟插在 3.0 的 Port 會在接下來的安裝過程找不到檔案,不知詳細原因為何,換到 USB 2.0 的 port 就沒問題了

因為我習慣對系統加密,安裝的時候記得要進 Advanced Options/Expert install。一直到磁碟分割的階段,我用 GPT 對 HDD 分出四個 Partition
  1. /boot - 不加密
  2. / - 加密
  3. /swap - 加密
  4. /home - 加密
第一個 Partition 是 boot,當然不能加密,否則就不能開機了。建立完 boot 之後,依序再建立三個 partition,但是在各個 partition 的細項設定,把「用途」設定為「要進行加密的 Physical Volume



接著再選擇上方的「設定已加密的 Volume」來真正為該 partition 加密。系統會先清光該 Partition 上頭的東西,因為這是新硬碟沒必要浪費時間,所以清空的步驟可以取消跳過。


此時會多出三個已加密的 Partition,依序再為他們設定 mount point 與 filesystem 即可



安裝的時候可以不用安裝 desktop 的東西,基本系統安裝完成之後會把之前的 package 全部裝回來

回復備份

盡可能在 console 底下下指令,把之前的 packages 裝回來
# dpkg --set-selections < dpkg_selections
# apt-get update
# apt-get dselect-upgrade
重開機之後如果沒問題,關掉 X 回到 console 繼續把 home 給倒回來
# rm -rf /home/myid
# cd /home
# tar xf /media/external_storage/myid.tar

其他

其他一些眉眉角角的東西就順便紀錄一下吧

救回 Touchpad 中鍵

按住中鍵並且用 Trackpoint scroll,把 /etc/X11/xorg.conf 的 Synaptics 的區塊清掉 (對,我不想用它) 並加入以下這段
Section "InputClass"
 Identifier "Trackpoint Wheel Emulation"
 MatchProduct "TPPS/2 IBM TrackPoint"
 MatchDevicePath "/dev/input/event*"
 Option "EmulateWheel" "true"
 Option "EmulateWheelButton" "2"
 Option "Emulate3Buttons" "false"
 Option "XAxisMapping" "6 7"
EndSection

盡可能少用 swap


記憶體目前還滿夠用,叫系統沒事不要亂寫到 swap,在 /etc/sysctl.conf 加入
vm.swappiness=0

把 tmp 掛在記憶體上面


目前用 tmpfs 的方式把 tmp 掛起來,這樣讀寫都是在記憶體上頭。但我還不確定用 tmpfs 或是 ramfs 哪個好。(許多大廠的工程師會在 BSP build script 假設 /tmp 用不完,然後寫入一兩 G 的暫存檔 =.=)

我指定的大小是 200M,不夠的時候再重新 mount /tmp -o remount,size=300m 就好了
tmpfs /tmp tmpfs defaults,size=200m,mode=1777 0 0

安裝舊的 package


需要一些超舊,只存在於 stable 的 package,如 ia32-libs 與 sun-jdk。所以把 stable 加進 sources.list
deb http://debian.nctu.edu.tw/debian/ squeeze main contrib non-free

指定 Firefox 的 Cache Directory 到 mSATA


在 Firefox 的網址列打開 about:config,新增以下兩個 key 與 value

  • browser.cache.disk.parent_directory = /mnt/msata/myid/cache
  • browser.cache.offline.parent_directory = /mnt/msata/myid/cache

重開 Firefox 再打開 about:cache 就可以看見 cache 目錄放在 mSATA 上面了

March 26, 2013

CSS Flex Box Layout

No comments:
前兩天看到了 CSS3 定義了 Flex 的排版方法,玩了一下效果如圖

每個方塊的大小會亂數改變,在一個固定寬度的 container 之中排放,如果超出 container 的寬度就自動換行,還能夠指定相反的 order。

不過這個 layout 的 spec 歷經許多重大改變,詳情可以閱讀 W3C Flexbox 或是 MDN 的 Using CSS flexible,話雖如此,現在的 Firefox 仍不支援 flexbox 的換行,上面那張圖我是從 chrome 裡面測試的。



一個簡單的 html
 
 
  
 
 
 
  
然後一個簡單的 css,主要的功能目前還需要註明 vendor (就是 chrome 用的 webkit)
.element {
 min-width: 100px;
 min-height: 100px;
 margin:10px;
 border: 5px;
 border-style:solid;

 display:-webkit-box;
 display:box;
 -moz-box-pack:center;
 -moz-box-align:center;
 -webkit-box-pack:center;
 -webkit-box-align:center;
}

#container {
 width: 50%;
 border: 1px;
 border-style:solid;
 border-color:#AAA;
 display: -webkit-flex;
 display: flex;
 flex-direction: row;
 -webkit-flex-direction: row;
 flex-wrap: wrap;
 -webkit-flex-wrap: wrap-reverse;
}
js code 也只是產生幾個方塊之後亂數改變大小
'use restrict';

function start(container) {
  var nodes,
      MAX = 9;

  function randomColor() {
    // 000000000000 to 000000FFFFFF, it might be 000000FF
    var num = '000000' +
      (((0xFFFFFF) * Math.random()) | 0).toString(16);
    return '#' + num.substring(num.length - 6);
  };

  function randomInt() {
    // -25 ~ 25
    return ((Math.random() * 51) | 0) - 25;
  };

  function Node(i) {
    this.elmnt = document.createElement('div');
    this.elmnt.className = 'element';
    this.elmnt.style.backgroundColor = randomColor();
    this.elmnt.style.borderColor = randomColor();
    this.elmnt.style.webkitOrder = i;
    this.elmnt.textContent = i;
  };

  Node.prototype.adj = function() {
    this.elmnt.style.width = 150 + randomInt();
    this.elmnt.style.height = 150 + randomInt();
    return this;
  };

  function init() {
    var node,
      counter;
    nodes = new Array();
    for (counter = 0; counter < MAX; counter++) {
      node = new Node(counter);
      container.appendChild(node.adj().elmnt);
      nodes.push(node);
    }
  };

  init();
}
html/js/css 上手之後還挺好玩的,只是現階段的 css 排版真痛苦,CSS3 的功能彷彿看得見吃不到,我覺得 fragment 的情況比 Android 還要慘烈。

January 14, 2013

台北市捷運地圖

5 comments:
不知道哪天我會在什麼地方用到這樣的一張圖,就隨手畫了一下,授權是 CC-BY-SA,只要在 Metadata 裡面把作者名字放進去,想要把左下角的圖示拿掉也沒關係。(我自己也覺得在那邊放張圖有點醜)



比較麻煩的是站名擺放的位置,一方面要貼近站點,又要保持間距增加可讀性,還不能讓圖片拉成太大張。反正捷運網站的圖片英文字小到眼睛都快瞎了,我也就懶得把英文站名放上去。(再加上英文站名的排版會瘋掉)

畫得沒有很好看,應該還算堪用,有需要的人就隨意挾去用吧,附 svg 檔,看不慣的鄉親歡迎發揮黑手精神動手改。

ps. svg 檔我有分幾個 Layer,圖片背景白色,要修改的時候記得 set layer as editable。

update: