2012年7月3日 星期二

Android 開發筆記 - ImageButton/ImageView 圖形放大問題

android_imagebutton_resize


大概算是第二或第三次寫 Android app 吧,碰到一個很怪的現象,在 WVGA (480x800解析度),以一張 100px 方圖,橫放四張,理論上應該還會剩 480px - 4*(100px) = 80px 左右的範圍,但實際上圖形就是顯示不對,整個 ImageView/ImageButton 所顯示的圖被放大了,沒有剩下的空間,請教同事後,同事說這是 px 轉換成 dp 問題,但他還是說不出個所以然,後來經測試發現,這個問題是因為我把圖檔擺在 res/drawable 目錄下(在 Android 2.3.3 SDK 下,此目錄預設不存在),但 WVGA 預設是用 res/drawable-hdpi ,所以,只要把圖擺在 res/drawable-hdpi 內則會顯示正常。


android_imagebutton_size


相關文件:



故,如果裝置是 hdpi 的,那圖擺在 drawable 的話,系統為了讓個裝置顯示的大小一致,所以會自動幫你做轉換,所以只要把圖擺在 drawable-hdpi 後,即可避開轉換,而得到解決。


Android 開發筆記 - 模仿 iOS UIPageControl 分頁效果

老話一句:為了給使用者有一致性的體驗,在 Android 環境上,使用 PagerAdapter/ViewPager 實作 iOS UIPageControl 效果。需額外 library (android-support-v4.jar) 來使用,請參考 Support Library | Android Developers 流程。


概念:


在 layout.xml 裡頭多新增一個 android.support.v4.view.ViewPager,接著在程式方面,用一個 List<View> mListViews 記錄有幾個 page 要切換,接著再實作一個 PagerAdapter 處理翻頁的動作,最後再把 mViewPage.setAdapter(mPagerAdapter); 就搞定囉


程式 PageControlActivity:


public class PageControlActivity extends Activity {
        private MyPagerAdapter mPagerAdapter;
       private ViewPager mViewPager;
       private List<View> mListViews;

       @Override
       protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.pagecontrol);

              mPagerAdapter = new MyPagerAdapter();
              mViewPager = (ViewPager)findViewById(R.id.viewPager);
              mViewPager.setAdapter(mPagerAdapter);

              mListViews = new ArrayList<View>();
              LayoutInflater mInflater = getLayoutInflater();
              // page 1:
              mListViews.add(mInflater.inflate(R.layout.main, null));
              // page 2:
              mListViews.add(mInflater.inflate(R.layout.main, null));
       }

       protected class MyPagerAdapter extends PagerAdapter {


              @Override
              public int getCount() {
                     if(mListViews==null)
                            return 0;
                     return mListViews.size();
              }


              @Override
              public boolean isViewFromObject(View arg0, Object arg1) {
                     //return false;
                     return arg0==(arg1);
              }

              @Override
              public void destroyItem(View container, int position, Object object) {
                     ((ViewPager) container).removeView(mListViews.get(position));
              }

              @Override
              public Object instantiateItem(View container, int position) {
                     ((ViewPager) container).addView(mListViews.get(position),0);
                     return mListViews.get(position);
              }

              /*
              @Override
              public void finishUpdate(View container) {
                     int position = ((ViewPager) container).getCurrentItem();
                     for( int i=0 ; i<mImageButtons.size() ; ++i )
                            if( i == position )
                                   ((ImageButton)mImageButtons.get(i)).setImageDrawable(getResources().getDrawable(R.drawable.selected));
                            else
                                   ((ImageButton)mImageButtons.get(i)).setImageDrawable(getResources().getDrawable(R.drawable.unselected));
              }
              // */
       }
}


Layout(pagecontrol.xml):


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

       <android.support.v4.view.ViewPager
              android:layout_width="fill_parent"
              android:layout_height="350dp"
              android:id="@+id/viewPager"/>

       <!-- Pages buttons -->
       <!--
       <LinearLayout
              android:background="@null"
              android:layout_gravity="center"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">


              <ImageButton
                     android:id="@+id/btn_page1"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:contentDescription="@null"
                     android:background="@null"
                     android:src="@drawable/selected" />


              <ImageButton
                     android:layout_width="30dp"
                     android:layout_height="wrap_content"
                     android:contentDescription="@null"
                     android:background="@null"
                     android:src="@null" />

              <ImageButton
                     android:id="@+id/btn_page2"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:contentDescription="@null"
                     android:background="@null"
                     android:src="@drawable/unselected" />


              </LinearLayout>
       -->
</LinearLayout>


如此一來,僅需新增 mListViews 就可以動態增加很多頁,如果要完全跟 iOS UIPageControl 一樣,那就要在底部加上頁數的顯示,這時候就可以在 MyPagerAdapter 裡頭實作 finishUpdate 處理,當選到某一頁時,把那頁的圖示標記起來,可參考上述註解的地方,這些應該也能程式化處理。


 


2012年7月2日 星期一

使用 Namecheap 註冊 domain


自從有了台 Linode 後,又開始敗家了 XD 首先是看了國內的報價後(大概台幣400以上),就嘗試從國外 domain registrar 買一個 domain 來使用了,先逛了 godaddy.com ,看了他的價碼,原本打算下手了,但不禁意想起幾個月前的 SOPA 案子,所以就順手查了一下大家又改用哪個,最後就找到 namecheap.com


逛了一下 namecheap 後,發現他有提供一些簡單又免費的服務,恰好很適合像我這種短期使用的,所以就下手了,挑了最便宜的 *.info 的,並用 Paypal 付款,總共花不到台幣 180 元,大概就像買一本書吧 XD 此外也發現,其實還滿多優惠活動的,就像上週看 *.info 只要 2.98 美金,這週就變成 3.99 囧,所以不急的話,可以看看有沒其他優惠活動可以搭配,印象中找資料的過程還看到 0.98 的價格。


此次加購的(有的在 godaddy.com 要花錢):



  • Free WhoisGuard (第一年免費, 一年 $2.88 美金)


    • 由於法律規定,domain 註冊者必須將申請資料擺進 whois databases (如 email address),這是公開的資料庫,所以容易被 spammers 撈去發廣告信,所以有 WhoisGuard 的服務,幫你把 email address 做個屏蔽,其實也只是用個 email address 幫你 forward 而已,所以想要省錢就用假的吧 XD


  • Free OX Personal Email ( 第一年免費, 一年 $2.99 美金 )


    • 簡易的 email 服務,例如申請的是 example.com 好了,可以擁有設定 user@example.com 轉到指定的 email address 服務


  • PositiveSSL (第一年 $1.99 美金, 之後一年 $9 美金)


    • 簡單的說,別人逛到你的 https 等相關服務,瀏覽器不會跳出 "不信任的憑證" 囉



簡易 DNS 管理介面:


host_management


整體上應該還滿夠用了,不見得需要自行管理 DNS 啦。


[Linux] iptables 防火牆筆記 @ Ubuntu 12.04

很久沒碰機器了,自從大學畢業後,就打定不佔用學術資源,所以就沒擺任何機器來使用(但用別人管的機器XD),隨著經濟能力的好轉,上次被同事勸敗 Linode 512 後,開始進入管自己的機器啦,首先就是要設定防火牆,這東西以前修課有印象,記得那時是 FreeBSD 4 跟 ipfw ,只是差不多都忘光了 XD


首先 Linode 的 Ubuntu 12.04 server 預設有裝 iptables ,所以就用 iptables 了,網路上資源還不少,這次就試著用安全性較高的防火牆規則,那就是預設從外頭來進來連線都是不給過的。痛過之後,就可以慢慢加東西了 XD


$ iptables [-P 策略(INPUT,OUTPUT,FORWARD)][-t 表格(filter,nat,mangle,raw,security)] [-A 新增規則(end)] [-I 新增規則(begin)] [-I 網路卡(in)] [-O 網路卡(out)] [-s 封包IP來源] [-d 封包IP目的地] [-p 通訊協定(tcp,udp,icmp,all)] [-sport 來源阜] [-dport 輸出阜] [-j 動作(Accept,Drop)]


查看目前的設定:


$ sudo iptables -L



$ sudo iptables-save


簡易 Script:


#!/bin/sh


# BIN
BIN_IPTABLES=`which iptables`


# reset rules
$BIN_IPTABLES -F
$BIN_IPTABLES -X
$BIN_IPTABLES -Z


# init policies
$BIN_IPTABLES -P INPUT DROP
$BIN_IPTABLES -P OUTPUT ACCEPT
$BIN_IPTABLES -P FORWARD ACCEPT


# setup connection state
$BIN_IPTABLES -A INPUT -i lo -j ACCEPT
$BIN_IPTABLES -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT


# service port
$BIN_IPTABLES -A INPUT -i eth0 -p tcp --dport 80 -j ACCEPT
$BIN_IPTABLES -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT


設定開機啟動:


$ mkdir ~/conf/
$ vim ~/conf/firewall.sh
$ sudo ln -s ~/conf/firewall.sh /etc/init.d/firewall.sh
$ sudo chmod 755 /etc/init.d/firewall.sh
$ sudo update-rc.d -f firewall.sh defaults


如此一來,應該就暫時夠用吧 :p 剩下的慢慢惡搞囉


2012年6月24日 星期日

用力的~翻滾吧!管他在正反之間~


翻滾吧!用熱血飛上青天...這周末花了點時間重整周邊的開發環境,複習了 GAEHeroku 後,無意間找到這篇 Google Groups > CloudTW › GAE → Heroku → Linode,並回憶自己的經驗後,深深地回想起學生時代看過的動漫:鋼之鍊金術師!那一道道「等價交換」的理化原理扎扎實實地用在生活、人性甚至科技上。


今年是很特別的一年,從年初就這般告訴自己,隨時時間越來越接近年中,一件件不在預料中的事蹦了出來,突然也能感受到馬雲說的話:「只要是錢能解決的問題都不是問題」(其實,這句話我比較有印象是某政治家的口頭禪),隨著時間的增減,感觸極深,那就是時間好少啊。小時不懂事時,時間在電視、電動中度過,等到年少輕狂時,卻又在球場、升學壓力中,等到好像快自由時,卻陷入經濟、兵役中,等到可以喘口氣時,又碰上人生問題,其實壓力不曾減少,只會隨著時間與空間轉換,唯一不變的那就是一天只有 24 小時,在這樣的定理下,漸漸地不會去羨慕什麼,也不用去模仿什麼,因為你就是你,他就是他。


這一年多來不小心學到的壞習慣,那就是思考、計較勝於行動。由於單位目標,常常必須花很多時間去找尋資料,也訂閱了一堆有的沒有的 RSS,透過這些資訊的吸收,的確有一定程度的成長,但是轉個彎來思考,更代表自己一定程度的佇足。當別人跟你說 ooo 很炫,你可以很快地舉出例子說 xxx 已經做過了;當別人說這個好像可以做,又不禁意地潑了別人冷水。


後來,我發現這件事似乎是 25~35 年紀的說話語氣,例如提出一些創業的觀點時,會有一票人用近似反對的觀點對談 XD 只是很神奇地,碰到 35 ~ 50 歲的長輩時,他們反而會希望年輕人大膽去做,年輕就是本錢,彷彿希望年輕一代把自己過去未做的事補齊!其實,創業終究只是個代名詞,國父說的好,把一件事從頭做到完就是成功,我覺得這真的對極了,實在不必要把創業的標準訂太高,能夠達成做自己喜歡的事(這動力會促使你把其他不喜歡做的事也搞定),也就不錯啦,不見得要去探索藍海,在紅海中悠游自在不也不錯嘛。


今晚重灌電腦時,同事塞了一本書叫我看一看,還滿建議有興趣的可以翻一下:



不到 300 頁,剛好裝完系統+更新完系統的時間,我就看完了 XD 我覺得裏頭的東西不見得是新穎的,但可以回顧別人是如何過生活。很巧地也讓我回顧我工作近三年的兩個單位文化,文中所提的十分有感觸,例如管理者道歉文化,有一派認為會給人沒執行能力、魄力,另一派會覺得是拉近關係等。整本書的優點大於缺點,若是硬要說不好的,那就得問問廣大的鄉民吧 cc 別忘了媒體、行銷:



感謝周邊優秀的同事,不一樣的成長背景,醞釀著不一樣的能量,以前總覺得自己的觀點還算新穎,現在覺得周邊一堆人都很厲害呢,然而,生命的長度有限,每個人選擇的路線不見得一樣,記得要多多給予別人正面的能量,相信這世界會因此變得更好!接下來的生活嘛,大概可以參考學弟當兵日記,把一堆 RSS 退掉,專心地提升行動力。