亚洲激情欧美,国产免费丝袜调教视频,日本高清免费一本视频网站,毛片在线看免费版,在线看久,亚洲精品美女网站在线观看,大陆一级黄色a乱人国产片

Android開發(fā)網(wǎng)上的一些重要知識點(diǎn)[經(jīng)驗(yàn)分享]2

文章分類:公司動態(tài) 發(fā)布時(shí)間:2013-07-27 原文作者:admin 閱讀( )

看看sdk文檔上的關(guān)于界面圖標(biāo)的詳細(xì)說明。 14.Android控件美化Shape你會用嗎?

如果你對Android系統(tǒng)自帶的UI控件感覺不夠滿意,可以嘗試下自定義控件,我們就以Button為例,很早以前Android123就寫到過Android Button按鈕控件美化方法里面提到了xml的selector構(gòu)造。當(dāng)然除了使用drawable這樣的圖片外今天Android開發(fā)網(wǎng)談下自定義圖形shape的方法,對于Button控件Android上支持以下幾種屬性shape、gradient、stroke、corners等。   我們就以目前系統(tǒng)的Button的selector為例說下:           <shape>
            <gradient
                android:startColor="#ff8c00"
                android:endColor="#FFFFFF"
                android:angle="270" />
            <stroke
                android:width="2dp"
                android:color="#dcdcdc" />
            <corners
                android:radius="2dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>     對于上面,這條shape的定義,分別為漸變,在gradient中startColor屬性為開始的顏色,endColor為漸變結(jié)束的顏色,下面的angle是角度。接下來是stroke可以理解為邊緣,corners為拐角這里radius屬性為半徑,最后是相對位置屬性padding。 對于一個(gè)Button完整的定義可以為   <?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="#ff8c00"
                android:endColor="#FFFFFF"
                android:angle="270" />
            <stroke
                android:width="2dp"
                android:color="#dcdcdc" />
            <corners
                android:radius="2dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>     <item android:state_focused="true" >
        <shape>
            <gradient
                android:startColor="#ffc2b7"
                android:endColor="#ffc2b7"
                android:angle="270" />
            <stroke
                android:width="2dp"
                android:color="#dcdcdc" />
            <corners
                android:radius="2dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>     <item>       
        <shape>
            <gradient
                android:startColor="#ff9d77"
                android:endColor="#ff9d77"
                android:angle="270" />
            <stroke
                android:width="2dp"
                android:color="#fad3cf" />
            <corners
                android:radius="2dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>
</selector> 注意Android123提示大家,以上幾個(gè)item的區(qū)別主要是體現(xiàn)在state_pressed按下或state_focused獲得焦點(diǎn)時(shí),當(dāng)當(dāng)來判斷顯示什么類型,而沒有state_xxx屬性的item可以看作是常規(guī)狀態(tài)下。

15. Android開發(fā)者應(yīng)該保持以下特質(zhì)

Android123推薦新手應(yīng)該遵循   1. 深讀SDK文檔   2. 深讀SDK的APIDemo和Samples   3. 掌握GIT開源代碼   4. 多了解Android開源項(xiàng)目,學(xué)習(xí)別人的手法寫程序。

16. Android數(shù)組排序常見方法

  Android的數(shù)組排序方式基本上使用了Sun原生的Java API實(shí)現(xiàn),常用的有Comparator接口實(shí)現(xiàn)compare方法和Comparable接口的compareTo方法,我們對于一個(gè)數(shù)組列表比如ArrayList可以通過這兩個(gè)接口進(jìn)行排序和比較,這里Android123給大家一個(gè)例子 private final Comparator cwjComparator = new Comparator() {         private final Collator   collator = Collator.getInstance();
        public final int compare(Object a, Object b) {
            CharSequence  a = ((Item) a).sName;
            CharSequence  b = ((Item) b).sID;
            return collator.compare(a, b);
        }
    }; 我們的ArrayList對象名為mList,則執(zhí)行排序可以調(diào)用方法 Collections.sort(mList, cwjComparator);

17.Android控件TextProgressBar進(jìn)度條上顯文字

Android系統(tǒng)的進(jìn)度條控件默認(rèn)的設(shè)計(jì)的不是很周全,比如沒有包含文字的顯示,那么如何在Android進(jìn)度條控件上顯示文字呢? 來自Google內(nèi)部的代碼來了解下,主要使用的addView這樣的方法通過覆蓋一層Chronometer秒表控件來實(shí)現(xiàn),整個(gè)代碼如下    public class TextProgressBar extends RelativeLayout implements OnChronometerTickListener {
    public static final String TAG = "TextProgressBar";
    static final int CHRONOMETER_ID = android.R.id.text1;
    static final int PROGRESSBAR_ID = android.R.id.progress;
    Chronometer mChronometer = null;
    ProgressBar mProgressBar = null;
    long mDurationBase = -1;
    int mDuration = -1;     boolean mChronometerFollow = false;
    int mChronometerGravity = Gravity.NO_GRAVITY;
    public TextProgressBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }     public TextProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }     public TextProgressBar(Context context) {
        super(context);
    }     //Android開發(fā)網(wǎng)提示關(guān)鍵部分在這里     @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        int childId = child.getId();
        if (childId == CHRONOMETER_ID && child instanceof Chronometer) {
            mChronometer = (Chronometer) child;
            mChronometer.setOnChronometerTickListener(this);
            // Check if Chronometer should move with with ProgressBar
            mChronometerFollow = (params.width == ViewGroup.LayoutParams.WRAP_CONTENT);
            mChronometerGravity = (mChronometer.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);
        } else if (childId == PROGRESSBAR_ID && child instanceof ProgressBar) {
            mProgressBar = (ProgressBar) child;
        }
    }     @android.view.RemotableViewMethod
    public void setDurationBase(long durationBase) {
        mDurationBase = durationBase;
        if (mProgressBar == null || mChronometer == null) {
            throw new RuntimeException("Expecting child ProgressBar with id " +
                    "'android.R.id.progress' and Chronometer id 'android.R.id.text1'");
        }
        // Update the ProgressBar maximum relative to Chronometer base
        mDuration = (int) (durationBase - mChronometer.getBase());
        if (mDuration <= 0) {
            mDuration = 1;
        }
        mProgressBar.setMax(mDuration);
    }
    public void onChronometerTick(Chronometer chronometer) {
        if (mProgressBar == null) {
            throw new RuntimeException(
                "Expecting child ProgressBar with id 'android.R.id.progress'");
        }
        // Stop Chronometer if we're past duration
        long now = SystemClock.elapsedRealtime();
        if (now >= mDurationBase) {
            mChronometer.stop();
        }         int remaining = (int) (mDurationBase - now);
        mProgressBar.setProgress(mDuration - remaining);
        if (mChronometerFollow) {
            RelativeLayout.LayoutParams params;
            params = (RelativeLayout.LayoutParams) mProgressBar.getLayoutParams();
            int contentWidth = mProgressBar.getWidth() - (params.leftMargin + params.rightMargin);
            int leadingEdge = ((contentWidth * mProgressBar.getProgress()) /
                    mProgressBar.getMax()) + params.leftMargin;
            int adjustLeft = 0;
            int textWidth = mChronometer.getWidth();
            if (mChronometerGravity == Gravity.RIGHT) {
                adjustLeft = -textWidth;
            } else if (mChronometerGravity == Gravity.CENTER_HORIZONTAL) {
                adjustLeft = -(textWidth / 2);
            }
            leadingEdge += adjustLeft;
            int rightLimit = contentWidth - params.rightMargin - textWidth;
            if (leadingEdge < params.leftMargin) {
                leadingEdge = params.leftMargin;
            } else if (leadingEdge > rightLimit) {
                leadingEdge = rightLimit;
            }
            params = (RelativeLayout.LayoutParams) mChronometer.getLayoutParams();
            params.leftMargin = leadingEdge;
            mChronometer.requestLayout();
        }
    }

18. Android內(nèi)存管理-SoftReference的使用

很多時(shí)候我們需要考慮Android平臺上的內(nèi)存管理問題,Dalvik VM給每個(gè)進(jìn)程都分配了一定量的可用堆內(nèi)存,當(dāng)我們處理一些耗費(fèi)資源的操作時(shí)可能會產(chǎn)生OOM錯(cuò)誤(OutOfMemoryError)這樣的異常,Android123觀察了下國內(nèi)的類似Market客戶端設(shè)計(jì),基本上都沒有采用很好的內(nèi)存管理機(jī)制和緩存處理。   如果細(xì)心的網(wǎng)友可能發(fā)現(xiàn)Android Market客戶端載入時(shí),每個(gè)列表項(xiàng)的圖標(biāo)是異步刷新顯示的,但當(dāng)我們快速的往下滾動到一定數(shù)量比如50個(gè),再往回滾動時(shí)可能我們看到了部分App的圖標(biāo)又重新開始加載,當(dāng)然這一過程可能是從SQLite數(shù)據(jù)庫中緩存的,但是在內(nèi)存中已經(jīng)通過類似SoftReference的方式管理內(nèi)存。   在Java中內(nèi)存管理,引用分為四大類,強(qiáng)引用HardReference、弱引用WeakReference、軟引用SoftReference和虛引用PhantomReference。它們的區(qū)別也很明顯,HardReference對象是即使虛擬機(jī)內(nèi)存吃緊拋出OOM也不會導(dǎo)致這一引用的對象被回收,而WeakReference等更適合于一些數(shù)量不多,但體積稍微龐大的對象,在這四個(gè)引用中,它是最容易被垃圾回收的,而我們對于顯示類似Android Market中每個(gè)應(yīng)用的App Icon時(shí)可以考慮使用SoftReference來解決內(nèi)存不至于快速回收,同時(shí)當(dāng)內(nèi)存短缺面臨Java VM崩潰拋出OOM前時(shí),軟引用將會強(qiáng)制回收內(nèi)存,最后的虛引用一般沒有實(shí)際意義,僅僅觀察GC的活動狀態(tài),對于測試比較實(shí)用同時(shí)必須和ReferenceQueue一起使用。   對于一組數(shù)據(jù),我們可以通過HashMap的方式來添加一組SoftReference對象來臨時(shí)保留一些數(shù)據(jù),同時(shí)對于需要反復(fù)通過網(wǎng)絡(luò)獲取的不經(jīng)常改變的內(nèi)容,可以通過本地的文件系統(tǒng)或數(shù)據(jù)庫來存儲緩存,希望給國內(nèi)做App Store這樣的客戶端一些改進(jìn)建議。

19. 反射在Android開發(fā)中的利弊

由于Android 2.2的推出,很多新的API加入導(dǎo)致很多項(xiàng)目移植需要考慮使用Java的反射機(jī)制Reflection來動態(tài)調(diào)用,動態(tài)調(diào)用的好處就是不需要使用引用文件,直接通過JDK中聲明好的方法直接調(diào)用,本身原理基于JVM的,從Java 1.5開始支持,原理上就是根據(jù)類名而不實(shí)例化對象的情況下,獲得對象的方法或?qū)傩远苯诱{(diào)用。   Android開發(fā)時(shí)反射能幫助我們多少?   1. 有些網(wǎng)友可能發(fā)現(xiàn)Android的SDK比較封閉,很多敏感的方法常規(guī)的用戶無法編譯,我們?nèi)绻戳舜a直接在反射中聲明動態(tài)調(diào)用即可。比如很多internal或I開頭的AIDL接口均可以通過反射輕松調(diào)用。   2. 反射對于Android123來說更重要的是考慮到應(yīng)用的兼容性,我們目前主要兼容從Android 1.5到2.2的項(xiàng)目,API Level從3到8可以方便的擴(kuò)充,調(diào)用前我們預(yù)留一個(gè)標(biāo)志位聲明該API的最低以及最高的API Level為多少可以調(diào)用。   3. 對于調(diào)試Java的反射是功臣了,在Logcat中我們可以看到出錯(cuò)的地方肯定有類似java.lang.reflect.XXX的字樣,這種自檢機(jī)制可以幫助我們方便的調(diào)試Android應(yīng)用程序。   反射的缺點(diǎn)有哪些?   1. 因?yàn)槭莿討B(tài)執(zhí)行的,效率自然沒有預(yù)編譯時(shí)引用現(xiàn)有的庫效率高,就像平時(shí)我們Win32開發(fā)時(shí),可以不用h文件,直接通過GetProcAddress一樣去動態(tài)獲取方法的地址。當(dāng)然效率要根據(jù)復(fù)雜程度而決定,一般稍微復(fù)雜的處理性能損失可能超過20%,對于一些復(fù)雜的涉及Java自動類型轉(zhuǎn)換判斷,執(zhí)行時(shí)間可能是直接引用的上千倍,所以最終我們調(diào)試時(shí)必須考慮性能問題。   2. 因?yàn)榉瓷涫莿討B(tài)的,所以需要處理很多異常,不然Dalvik崩潰出Force Close的概率會大很多,很簡單的一個(gè)反射就需要至少3個(gè)異常捕獲,本身try-catch效率就不是很高,自然進(jìn)一步影響運(yùn)行效率,對于Android開發(fā)我們必須考慮這些問題。   3. 反射因?yàn)閷?dǎo)致代碼臃腫,自然稍微復(fù)雜的幾個(gè)方法實(shí)用反射將會導(dǎo)致代碼可讀性和維護(hù)性降低,如果很抽象的調(diào)用Android開發(fā)網(wǎng)強(qiáng)烈不推薦這種方法。   最后要說的是Reflection并不是Java的專利,微軟的.Net也同樣支持,同時(shí)更多的動態(tài)語言如Ruby等均支持這一特性。

20.AsyncTask對比Thread加Handler

很多網(wǎng)友可能發(fā)現(xiàn)Android平臺很多應(yīng)用使用的都是AsyncTask,而并非Thread和Handler去更新UI,這里Android123給大家說下他們到底有什么區(qū)別,我們平時(shí)應(yīng)該使用哪種解決方案。從Android 1.5開始系統(tǒng)將AsyncTask引入到android.os包中,過去在很早1.1和1.0 SDK時(shí)其實(shí)官方將其命名為UserTask,其內(nèi)部是JDK 1.5開始新增的concurrent庫,做過J2EE的網(wǎng)友可能明白并發(fā)庫效率和強(qiáng)大性,比Java原始的Thread更靈活和強(qiáng)大,但對于輕量級的使用更為占用系統(tǒng)資源。Thread是Java早期為實(shí)現(xiàn)多線程而設(shè)計(jì)的,比較簡單不支持concurrent中很多特性在同步和線程池類中需要自己去實(shí)現(xiàn)很多的東西,對于分布式應(yīng)用來說更需要自己寫調(diào)度代碼,而為了Android UI的刷新Google引入了Handler和Looper機(jī)制,它們均基于消息實(shí)現(xiàn),有事可能消息隊(duì)列阻塞或其他原因無法準(zhǔn)確的使用。   Android開發(fā)網(wǎng)推薦大家使用AsyncTask代替Thread+Handler的方式,不僅調(diào)用上更為簡單,經(jīng)過實(shí)測更可靠一些,Google在Browser中大量使用了異步任務(wù)作為處理耗時(shí)的I/O操作,比如下載文件、讀寫數(shù)據(jù)庫等等,它們在本質(zhì)上都離不開消息,但是AsyncTask相比Thread加Handler更為可靠,更易于維護(hù),但AsyncTask缺點(diǎn)也是有的比如一旦線程開啟即dobackground方法執(zhí)行后無法給線程發(fā)送消息,僅能通過預(yù)先設(shè)置好的標(biāo)記來控制邏輯,當(dāng)然可以通過線程的掛起等待標(biāo)志位的改變來通訊,對于某些應(yīng)用Thread和Handler以及Looper可能更靈活。

21. Android Drawable疊加處理方法

大家可能知道Bitmap的疊加處理在Android平臺中可以通過Canvas一層一層的畫就行了,而Drawable中如何處理呢? 除了使用BitmapDrawable的getBitmap方法將Drawable轉(zhuǎn)換為Bitmap外,今天Android123給大家說下好用簡單的LayerDrawable類,LayerDrawable顧名思義就是層圖形對象。下面直接用一個(gè)簡單的代碼表示:     Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.cwj);
    Drawable[] array = new Drawable[3];      array[0] = new PaintDrawable(Color.BLACK); //黑色
     array[1] = new PaintDrawable(Color.WHITE); //白色
     array[2] = new BitmapDrawable(bm); //位圖資源
    LayerDrawable ld = new LayerDrawable(array); //參數(shù)為上面的Drawable數(shù)組
        ld.setLayerInset(1, 1, 1, 1, 1);  //第一個(gè)參數(shù)1代表數(shù)組的第二個(gè)元素,為白色
        ld.setLayerInset(2, 2, 2, 2, 2); //第一個(gè)參數(shù)2代表數(shù)組的第三個(gè)元素,為位圖資源
    mImageView.setImageDrawable(ld);   上面的方法中LayerDrawable是關(guān)鍵,Android開發(fā)網(wǎng)提示setLayerInset方法原型為public void setLayerInset (int index, int l, int t, int r, int b) 其中第一個(gè)參數(shù)為層的索引號,后面的四個(gè)參數(shù)分別為left、top、right和bottom。對于簡單的圖片合成我們可以將第一和第二層的PaintDrawable換成BitmapDrawable即可實(shí)現(xiàn)簡單的圖片合成。

22. onRetainNonConfigurationInstance和getLastNonConfigurationInstance

很多網(wǎng)友可能知道Android橫豎屏切換時(shí)會觸發(fā)onSaveInstanceState,而還原時(shí)會產(chǎn)生onRestoreInstanceState,但是Android的Activity類還有一個(gè)方法名為onRetainNonConfigurationInstance和getLastNonConfigurationInstance這兩個(gè)方法。    我們可以通過  onRetainNonConfigurationInstance 代替 onSaveInstanceState,比如距離2   @Override
  public Object onRetainNonConfigurationInstance()
{    
       //這里需要保存的內(nèi)容,在切換時(shí)不是bundle了,我們可以直接通過Object來代替
      return obj;
} 在恢復(fù)窗口時(shí),我們可以不使用 onRestoreInstanceState,而代替的是 getLastNonConfigurationInstance 方法。我們可以直接在onCreate中使用,比如   Object obj = getLastNonConfigurationInstance();     最終obj的內(nèi)容就是上次切換時(shí)的內(nèi)容。   這里Android123提醒大家,每次Activity橫豎屏切換時(shí)onCreate方法都會被觸發(fā)。

23. Android中String資源文件的format方法

很多時(shí)候我們感性Google在設(shè)計(jì)Android時(shí)遵守了大量MVC架構(gòu)方式,可以讓寫公共代碼、美工和具體邏輯開發(fā)人員獨(dú)立出來。有關(guān)Android的資源文件values/strings.xml中如何實(shí)現(xiàn)格式化字符串呢? 這里Android123舉個(gè)簡單的例子,以及最終可能會用到哪些地方。 <?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <string name="app_name">cwj_Demo</string> 
    <string name="hello">android開發(fā)網(wǎng)</string> 
</resources>  上面是一段簡單的字符串資源文件,沒有用到格式化,因?yàn)楸容^簡單直接描述了意思,當(dāng)我們設(shè)計(jì)一個(gè)類似 Delete xxx File ? 的時(shí)候,我們可能需要在Java中動態(tài)獲取 xxx 的名稱,所以定義資源時(shí)使用格式化可以輕松解決,不需要一堆String去拼接或StringBuffer一個(gè)一個(gè)append這樣的愚蠢方法,看例子     <string name="alert">Delete %1$s File</string>   這里%1$s代表這是一個(gè)字符串型的,如果是整數(shù)型可以寫為%1$d,類似printf這樣的格式化字符串函數(shù),當(dāng)然如果包含了多個(gè)需要格式化的內(nèi)容,則第二個(gè)可以寫為%2$s或%2$d了,那么最終在Java中如何調(diào)用呢? 看下面的例子:    例一: 整數(shù)型的   <string name="alert">I am %1$d years old</string>  定義的是這樣的    當(dāng)然,我們杜絕意外情況,比如冒出個(gè)secret這樣的string類型的,注意上面是%1$d不是%1$s,所以默認(rèn)標(biāo)準(zhǔn)的合并成為    int nAge=23;    String sAgeFormat = getResources().getString(R.string.alert);     String sFinalAge = String.format(sAgeFormat, nAge);      這樣執(zhí)行完后,就組成了 I am 23 years old,是不是很方便啊.  當(dāng)然了,下面看下String字符串時(shí)的情況.   例二: 字符串型的   String sName="cwj"   String sCity="Shanghai"    資源定義為   <string name="alert2">My name is %1$s , I am form %2$s</string>     則Java中只需要   String sInfoFormat = getResources().getString(R.string.alert2);    String sFinalInfo=String.format(sInfoFormat, sName, sCity);    我們看到了整個(gè),整個(gè)定義類似MFC的CString::Format或Mac OS中的NSLog,但是需要顯示類似C#中那樣顯示的標(biāo)出參數(shù)的數(shù)字,比如%1或%n,這里數(shù)字代表參數(shù)的第n個(gè)。本行最終sFinalInfo顯示的內(nèi)容為   My name is cwj , I am form Shanghai 。當(dāng)然了你有什么不懂的地方可以來函至 android123@163.com

24. Android工程內(nèi)嵌資源文件的兩種方法

Android軟件一般處理大的資源通過sdcard比如在線下載資源到sdcard,而apk中內(nèi)嵌資源或二進(jìn)制文件時(shí)一般使用下面的兩種方法:   方法一   res/raw目錄下存放,比如cwj.dat一個(gè)二進(jìn)制文件,我們可以讀取可以直接  InputStream is=context.getResources().openRawResource(R.raw.cwj);    方法二   工程根目錄下的assets文件夾中存放,比如assets/cwj.dat 這樣我們使用下面的代碼   AssetManager am = context.getAssets();  
  InputStream is = am.open(cwj.dat);     這里Android123提示大家Google的Android系統(tǒng)處理Assert有個(gè)bug,在AssertManager中不能處理單個(gè)超過1MB的文件,不然會報(bào)異常具體數(shù)值大家可以測試下傳個(gè)稍大的文件,我們在兩年前的文章中有提到,而第一種raw沒這個(gè)限制可以放個(gè)4MB的Mp3文件沒問題。

25. Android自定義View以及l(fā)ayout屬性全攻略

對于Android系統(tǒng)的自定義View可能大家都熟悉了,對于自定義View的屬性添加,以及Android的Layout的命名空間問題,很多網(wǎng)友還不是很清楚,今天Android123一起再帶大家溫習(xí)一下   CwjView myView=new CwjView(context);   如果用于游戲或整個(gè)窗體的界面,我們可能直接在onCreate中setContentView(myView); 當(dāng)然如果是控件,我們可能會需要從Layout的xml中聲明,比如   <cn.com.android123.CwjView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
  />   當(dāng)然,我們也可以直接從父類聲明比如   <View class="cn.com.android123.CwjView"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
  /> 上面我們僅用了父類View的兩個(gè)屬性,均來自android命名空間,而名稱為layout_width或layout_height,我們自定義的控件可能有更多的功能,比如     <cn.com.android123.CwjView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
  cwj:age="22"
   cwj:university="sjtu"
   cwj:city="shanghai"

   /> 我們可以看到上面的三個(gè)屬性,是我們自定義的。作為標(biāo)準(zhǔn)xml規(guī)范,可能還包含了類似 xmlns:android="http://schemas.android.com/apk/res/android"  這樣的語句,對于定義完整的View,我們的命名空間為cwj,這里可以寫為 xmlns:cwj=http://schemas.android.com/apk/res/cn.com.android123.cwjView 或 xmlns:cwj=http://schemas.android.com/apk/res/android 都可以   對于定義的cwj命名空間和age、university以及city的三個(gè)屬性我們?nèi)绾味x呢? 在工程的res/values目錄中我們新建一個(gè)cwj_attr.xml文件,編碼方式為utf-8是一個(gè)好習(xí)慣,內(nèi)容如下 <?xml version="1.0" encoding="utf-8" ?>
<resources>
  <declare-styleable name="CwjView">
  <attr name="age" format="integer" />
  <attr name="city" format="string" />
  <attr name="university" format="string" />
  </declare-styleable>
</resources>   這里我們可能對format不是很熟悉,目前Android系統(tǒng)內(nèi)置的格式類型有integer比如ProgressBar的進(jìn)度值,float比如RatingBar的值可能是3.5顆星,boolean比如ToggleButton的是否勾選,string比如TextView的text屬性,當(dāng)然除了我們常見的基礎(chǔ)類型外,Android的屬性還有特殊的比如color是用于顏色屬性的,可以識別為#FF0000等類型,當(dāng)然還有dimension的尺寸類型,比如23dip,15px,18sp的長度單位,還有一種特殊的為reference,一般用于引用@+id/cwj @drawable/xxx這樣的類型。   當(dāng)然什么時(shí)候用reference呢? 我們就以定義一個(gè)顏色為例子,   <attr name="red" format="color|reference" />  這里我們用了邏輯或的運(yùn)算符,定義的紅色是顏色類型的,同時(shí)可以被引用   當(dāng)然,對于我們自定義的類中,我們需要使用一個(gè)名為obtainStyledAttributes的方法來獲取我們的定義。在我們自定義View的構(gòu)造方法(Context context, AttributeSet attrs)的重載類型中可以用   public CwjView(Context context, AttributeSet attrs) {
  super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs,
          R.styleable.cwj_attr);
        mAge = a.getInteger(R.styleable.CwjView_age, 22);
        mCity = a.getString(R.styleable.CwjView_city, "shanghai");
        mUniversity= a.getString(R.styleable.CwjView_university, "sjtu");
        a.recycle(); //Android123提示大家不要忘了回收資源 } 這樣類的全局成員變量 mAge、mCity就獲取了我們需要的內(nèi)容,當(dāng)然根據(jù)layout中的數(shù)值我們自定義的CwjView需要?jiǎng)討B(tài)的處理一些數(shù)據(jù)的情況,可以使用AttributeSet類的getAttributeResourceValue方法獲取。 public CwjView(Context context, AttributeSet attrs)
{
  super(context, attrs);
  resId = attrs.getAttributeResourceValue("cn.com.android123.CwjView", "age", 100);  
  resId = attrs.getAttributeResourceValue("cn.com.android123.CwjView", "city", "shanghai");
  //resID就可以任意使用了
} 以上兩種方法中,參數(shù)的最后一個(gè)數(shù)值為默認(rèn)的,如果您有不明白的地方可以來函到android123@163.com 我們會在第一時(shí)間回復(fù)。

26. 自定義Android主題風(fēng)格theme.xml方法

在Android中可以通過自定義主題風(fēng)格方式來實(shí)現(xiàn)個(gè)性化以及復(fù)用,首先我們創(chuàng)建theme.xml主題文件,保存位置為工程的res/values/theme.xml ,這里我們可以可以為主題起一個(gè)名稱,比如CWJ,這里去除了xml的文件頭<?xml version="1.0" encoding="utf-8"?>這行,我們在工程中只需在androidmanifest.xml文件的Activity節(jié)點(diǎn)中加入android:theme="@style/Theme.CWJ" 屬性,則這個(gè)Activity就使用了這種主題風(fēng)格,整個(gè)xml的關(guān)鍵代碼如下: <resources>
    <style name="Theme.CWJ" parent="android:Theme">
        <item name="android:windowBackground">@drawable/android123</item>
    </style>
</resources>   其中上面的代碼中,我們定義設(shè)置全局android:windowBackground即背景值為/res/drawable中的android123圖片為背景,更多的屬性定義可以參考view的layout xml屬性設(shè)置,比如我們設(shè)置所有字體顏色、大體大小和樣式,可以在style節(jié)點(diǎn)中加入   <item name="android:textColor">#fff</item>
  <item name="android:textSize">14sp</item>
  <item name="android:textStyle">bold</item>  當(dāng)然我們可以將上面的android123的圖片改進(jìn)下,使用一個(gè)xml文件替代,比如使用bitmap對象,則/res/drawable/android123.xml的完整代碼變?yōu)?nbsp;  <?xml version="1.0" encoding="utf-8"?> <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
     android:src="@drawable/cwj_image"
     android:tileMode="repeat" />    這里我們使用了一個(gè)bitmap對象來解析cwj_image圖片,當(dāng)然這里可以識別各種類型的圖片,其中android:tileMode是bitmap的內(nèi)部屬性,其中tileMode設(shè)置為repeat代表重復(fù),這樣可以節(jié)省bitmap資源,比如我們的背景是一層樓,那么全屏可以顯示同樣的為5層效果,而圖片僅是一層大小,對于資源利用相對更高。   當(dāng)然bitmap的屬性tileMode的值為repeat外還有其他的值比如clamp、mirror,這些值并沒有在SDK中并沒有找到定義,通過上次Android開發(fā)網(wǎng)的 Android自定義View以及l(fā)ayout屬性全攻略 一文,我們可以聯(lián)想到bitmap屬于android.graphics.Bitmap 包,由于是android框架,所以下載git的base包,找到該類,類的實(shí)例化時(shí)android123已經(jīng)在 Android自定義View以及l(fā)ayout屬性全攻略 說的很清楚,所以我們定位到res\values中找到attr.xml有關(guān)bitmap的定義即可,有關(guān)bitmap的更多屬性如  antialias、filter和dither都可以找到使用。

27. android調(diào)試工具monkey壓力測試實(shí)戰(zhàn)

很多Android開發(fā)者可能因?yàn)闆]有充分測試自己的軟件造成很容易出現(xiàn)FC(Force Close)的問題,這里我們可以通過使用Android固件中自帶的monkey工具來做軟件的壓力測試,monkey工具可以模擬各種按鍵,觸屏,軌跡球、activity等事件,這里Android123提示大家說白了monkey就是一個(gè)小猴子隨機(jī)狂玩你的android軟件,看看會不會產(chǎn)生異常。   具體的使用我們通過Android SDK給我們的adb調(diào)試橋鏈接設(shè)備或模擬器,進(jìn)入Linux Shell狀態(tài),當(dāng)然我們可以輸入adb shell獲取設(shè)備的shell,也可以直接通過adb命令執(zhí)行,比如說adb shell monkey來查看monkey工具中的參數(shù)說明,如圖:     我們要測試的apk文件要在android設(shè)備中已經(jīng)安裝,當(dāng)然模擬器中也可以測試的。執(zhí)行adb shell monkey -p cn.com.android123.cwj -v 100 我們執(zhí)行這句的中包含了p參數(shù),這里代表已安裝軟件的packageName,而v代表查看monkey生成的詳細(xì)隨機(jī)事件名,最后的數(shù)字100為我們測試的隨機(jī)事件數(shù)量為100.有關(guān)更多的測試方法,請查看上圖中的參數(shù),整個(gè)測試比較簡單單很有效,不妨試試。

28. 自定義View

有關(guān)Android的自定義View的框架今天我們一起討論下,對于常規(guī)的游戲,我們在View中需要處理以下幾種問題: 1.控制事件 2.刷新View 3. 繪制View   1. 對于控制事件今天我們只處理按鍵事件onKeyDown,以后的文章中將會講到屏幕觸控的具體處理onTouchEvent以及Sensor重力感應(yīng)等方法。   2. 刷新view的方法這里主要有invalidate(int l, int t, int r, int b) 刷新局部,四個(gè)參數(shù)分別為左、上、右、下。整個(gè)view刷新 invalidate(),刷新一個(gè)矩形區(qū)域 invalidate(Rect dirty) ,刷新一個(gè)特性Drawable, invalidateDrawable(Drawable drawable) ,執(zhí)行invalidate類的方法將會設(shè)置view為無效,最終導(dǎo)致onDraw方法被重新調(diào)用。由于今天的view比較簡單,Android123提示大家如果在線程中刷新,除了使用handler方式外,可以在Thread中直接使用postInvalidate方法來實(shí)現(xiàn)。   3. 繪制View主要是onDraw()中通過形參canvas來處理,相關(guān)的繪制主要有drawRect、drawLine、drawPath等等。view方法內(nèi)部還重寫了很多接口,其回調(diào)方法可以幫助我們判斷出view的位置和大小,比如onMeasure(int, int) Called to determine the size requirements for this view and all of its children.  、onLayout(boolean, int, int, int, int) Called when this view should assign a size and position to all of its children 和onSizeChanged(int, int, int, int) Called when the size of this view has changed. 具體的作用,大家可以用Logcat獲取當(dāng)view變化時(shí)每個(gè)形參的變動。   下面cwjView是我們?yōu)榻窈笥螒蛟O(shè)計(jì)的一個(gè)簡單自定義View框架,我們可以看到在Android平臺自定義view還是很簡單的,同時(shí)Java支持多繼承可以幫助我們不斷的完善復(fù)雜的問題。 public class cwjView extends View {     public cwjView(Context context) {
      super(context);
      setFocusable(true); //允許獲得焦點(diǎn)
      setFocusableInTouchMode(true); //獲取焦點(diǎn)時(shí)允許觸控
   }    @Override
   protected Parcelable onSaveInstanceState() {  //處理窗口保存事件
      Parcelable pSaved = super.onSaveInstanceState();
      Bundle bundle = new Bundle();
     //dosomething
      return bundle;
   }
   @Override
   protected void onRestoreInstanceState(Parcelable state) {  //處理窗口還原事件
      Bundle bundle = (Bundle) state;      //dosomething
     super.onRestoreInstanceState(bundle.getParcelable("cwj"));
      return;
   }
       @Override
   protected void onSizeChanged(int w, int h, int oldw, int oldh) //處理窗口大小變化事件
   {
      super.onSizeChanged(w, h, oldw, oldh);
   }    @Override
   protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)  
   {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec); //如果不讓父類處理記住調(diào)用setMeasuredDimension
   }
   @Override
   protected void onLayout (boolean changed, int left, int top, int right, int bottom)
   {
    super.onLayout (changed,left,top, ight,bottom) ;
   }    @Override
   protected void onDraw(Canvas canvas) {
      Paint bg = new Paint();
      bg.setColor(Color.Red);
      canvas.drawRect(0, 0, getWidth()/2, getHeight()/2, bg); //將view的左上角四分之一填充為紅色  
   }    @Override
   public boolean onTouchEvent(MotionEvent event) {
         return super.onTouchEvent(event); //讓父類處理屏幕觸控事件
   }    @Override
   public boolean onKeyDown(int keyCode, KeyEvent event) { //處理按鍵事件,響應(yīng)的軌跡球事件為 public boolean onTrackballEvent (MotionEvent event)
      switch (keyCode) {
      case KeyEvent.KEYCODE_DPAD_UP:
         break;
      case KeyEvent.KEYCODE_DPAD_DOWN:
         break;
      case KeyEvent.KEYCODE_DPAD_LEFT:
         break;
      case KeyEvent.KEYCODE_DPAD_RIGHT:
         break;
      case KeyEvent.KEYCODE_DPAD_CENTER: //處理中鍵按下
         break;
      default:
         return super.onKeyDown(keyCode, event);
      }
      return true;
   } }   上面我們可以看到onMeasure使用的是父類的處理方法,如果我們需要解決自定義View的大小,可以嘗試下面的方法    @Override
   protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)  
   {
      height = View.MeasureSpec.getSize(heightMeasureSpec); 
      width = View.MeasureSpec.getSize(widthMeasureSpec); 
      setMeasuredDimension(width,height);   //這里面是原始的大小,需要重新計(jì)算可以修改本行      //dosomething    }

29. Canvas和Paint實(shí)例

昨天我們在Android游戲開發(fā)之旅三 View詳解中提到了onDraw方法,有關(guān)詳細(xì)的實(shí)現(xiàn)我們今天主要說下Android的Canvas和Paint對象的使用實(shí)例。   Canvas類主要實(shí)現(xiàn)了屏幕的繪制過程,其中包含了很多實(shí)用的方法,比如繪制一條路徑、區(qū)域、貼圖、畫點(diǎn)、畫線、渲染文本,下面是Canvas類常用的方法,當(dāng)然Android開發(fā)網(wǎng)提示大家很多方法有不同的重載版本,參數(shù)更靈活。   void drawRect(RectF rect, Paint paint) //繪制區(qū)域,參數(shù)一為RectF一個(gè)區(qū)域   void drawPath(Path path, Paint paint) //繪制一個(gè)路徑,參數(shù)一為Path路徑對象    void  drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)   //貼圖,參數(shù)一就是我們常規(guī)的Bitmap對象,參數(shù)二是源區(qū)域(Android123提示這里是bitmap),參數(shù)三是目標(biāo)區(qū)域(應(yīng)該在canvas的位置和大小),參數(shù)四是Paint畫刷對象,因?yàn)橛玫搅丝s放和拉伸的可能,當(dāng)原始Rect不等于目標(biāo)Rect時(shí)性能將會有大幅損失。    void  drawLine(float startX, float startY, float stopX, float stopY, Paint paint)  //畫線,參數(shù)一起始點(diǎn)的x軸位置,參數(shù)二起始點(diǎn)的y軸位置,參數(shù)三終點(diǎn)的x軸水平位置,參數(shù)四y軸垂直位置,最后一個(gè)參數(shù)為Paint畫刷對象。   void  drawPoint(float x, float y, Paint paint) //畫點(diǎn),參數(shù)一水平x軸,參數(shù)二垂直y軸,第三個(gè)參數(shù)為Paint對象。
  void drawText(String text, float x, float y, Paint paint)  //渲染文本,Canvas類除了上面的還可以描繪文字,參數(shù)一是String類型的文本,參數(shù)二x軸,參數(shù)三y軸,參數(shù)四是Paint對象。   void  drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) //在路徑上繪制文本,相對于上面第二個(gè)參數(shù)是Path路徑對象   從上面來看我們可以看出Canvas繪制類比較簡單同時(shí)很靈活,實(shí)現(xiàn)一般的方法通常沒有問題,同時(shí)可以疊加的處理設(shè)計(jì)出一些效果,不過細(xì)心的網(wǎng)友可能發(fā)現(xiàn)最后一個(gè)參數(shù)均為Paint對象。如果我們把Canvas當(dāng)做繪畫師來看,那么Paint就是我們繪畫的工具,比如畫筆、畫刷、顏料等等。   Paint類常用方法: void  setARGB(int a, int r, int g, int b)  設(shè)置Paint對象顏色,參數(shù)一為alpha透明通道
void  setAlpha(int a)  設(shè)置alpha不透明度,范圍為0~255 void  setAntiAlias(boolean aa)  //是否抗鋸齒 void  setColor(int color)  //設(shè)置顏色,這里Android內(nèi)部定義的有Color類包含了一些常見顏色定義
.
void  setFakeBoldText(boolean fakeBoldText)  //設(shè)置偽粗體文本
void  setLinearText(boolean linearText)  //設(shè)置線性文本
PathEffect  setPathEffect(PathEffect effect)  //設(shè)置路徑效果
Rasterizer  setRasterizer(Rasterizer rasterizer) //設(shè)置光柵化
Shader  setShader(Shader shader)  //設(shè)置陰影  void  setTextAlign(Paint.Align align)  //設(shè)置文本對齊
void  setTextScaleX(float scaleX)  //設(shè)置文本縮放倍數(shù),1.0f為原始
void  setTextSize(float textSize)  //設(shè)置字體大小
Typeface  setTypeface(Typeface typeface)  //設(shè)置字體,Typeface包含了字體的類型,粗細(xì),還有傾斜、顏色等。 void  setUnderlineText(boolean underlineText)  //設(shè)置下劃線
最終Canvas和Paint在onDraw中直接使用 @Override
   protected void onDraw(Canvas canvas) {     Paint paintRed=new Paint();     paintRed.setColor(Color.Red);     canvas.drawPoint(11,3,paintRed); //在坐標(biāo)11,3上畫一個(gè)紅點(diǎn)
  }   下一次Android123將會具體講到強(qiáng)大的Path路徑,和字體Typeface相關(guān)的使用。

30. View類詳解

在Android游戲開發(fā)之旅二中我們講到了View和SurfaceView的區(qū)別,今天Android123從View類開始著重的介紹Android圖形顯示基類的相關(guān)方法和注意點(diǎn)。   自定義View的常用方法: onFinishInflate() 當(dāng)View中所有的子控件均被映射成xml后觸發(fā) onMeasure(int, int) 確定所有子元素的大小 onLayout(boolean, int, int, int, int) 當(dāng)View分配所有的子元素的大小和位置時(shí)觸發(fā) onSizeChanged(int, int, int, int) 當(dāng)view的大小發(fā)生變化時(shí)觸發(fā) onDraw(Canvas) view渲染內(nèi)容的細(xì)節(jié) onKeyDown(int, KeyEvent) 有按鍵按下后觸發(fā) onKeyUp(int, KeyEvent) 有按鍵按下后彈起時(shí)觸發(fā) onTrackballEvent(MotionEvent) 軌跡球事件 onTouchEvent(MotionEvent) 觸屏事件 onFocusChanged(boolean, int, Rect) 當(dāng)View獲取或失去焦點(diǎn)時(shí)觸發(fā)  onWindowFocusChanged(boolean) 當(dāng)窗口包含的view獲取或失去焦點(diǎn)時(shí)觸發(fā) onAttachedToWindow() 當(dāng)view被附著到一個(gè)窗口時(shí)觸發(fā) onDetachedFromWindow() 當(dāng)view離開附著的窗口時(shí)觸發(fā),Android123提示該方法和  onAttachedToWindow() 是相反的。 onWindowVisibilityChanged(int) 當(dāng)窗口中包含的可見的view發(fā)生變化時(shí)觸發(fā)   以上是View實(shí)現(xiàn)的一些基本接口的回調(diào)方法,一般我們需要處理畫布的顯示時(shí),重寫onDraw(Canvas)用的的是最多的:   @Override
   protected void onDraw(Canvas canvas) {
    //這里我們直接使用canvas對象處理當(dāng)前的畫布,比如說使用Paint來選擇要填充的顏色    Paint paintBackground = new Paint();
   paintBackground.setColor(getResources().getColor(R.color.xxx));  //從Res中找到名為xxx的color顏色定義
   canvas.drawRect(0, 0, getWidth(), getHeight(), paintBackground); //設(shè)置當(dāng)前畫布的背景顏色為paintBackground中定義的顏色,以0,0作為為起點(diǎn),以當(dāng)前畫布的寬度和高度為重點(diǎn)即整塊畫布來填充。      具體的請查看Android123未來講到的Canvas和Paint,在Canvas中我們可以實(shí)現(xiàn)畫路徑,圖形,區(qū)域,線。而Paint作為繪畫方式的對象可以設(shè)置顏色,大小,甚至字體的類型等等。 } 當(dāng)然還有就是處理窗口還原狀態(tài)問題(一般用于橫豎屏切換),除了在Activity中可以調(diào)用外,開發(fā)游戲時(shí)我們盡量在View中使用類似 @Override
   protected Parcelable onSaveInstanceState() {
      Parcelable p = super.onSaveInstanceState();
      Bundle bundle = new Bundle();
      bundle.putInt("x", pX);
      bundle.putInt("y", pY);
      bundle.putParcelable("android123_state", p);
      return bundle;
   }
   @Override
   protected void onRestoreInstanceState(Parcelable state) { 
      Bundle bundle = (Bundle) state;
      dosomething(bundle.getInt("x"), bundle.getInt("y")); //獲取剛才存儲的x和y信息
      super.onRestoreInstanceState(bundle.getParcelable("android123_state"));
      return;
   }   在View中如果需要強(qiáng)制調(diào)用繪制方法onDraw,可以使用invalidate()方法,它有很多重載版本,同時(shí)在線程中的postInvailidate()方法將在Android游戲開發(fā)之旅六中的 自定義View完整篇講到。

31. View和SurfaceView

在Android游戲當(dāng)中充當(dāng)主要的除了控制類外就是顯示類,在J2ME中我們用Display和Canvas來實(shí)現(xiàn)這些,而Google Android中涉及到顯示的為view類,Android游戲開發(fā)中比較重要和復(fù)雜的就是顯示和游戲邏輯的處理。這里我們說下android.view.View和android.view.SurfaceView。SurfaceView是從View基類中派生出來的顯示類,直接子類有GLSurfaceView和VideoView,可以看出GL和視頻播放以及Camera攝像頭一般均使用SurfaceView,到底有哪些優(yōu)勢呢? SurfaceView可以控制表面的格式,比如大小,顯示在屏幕中的位置,最關(guān)鍵是的提供了SurfaceHolder類,使用getHolder方法獲取,相關(guān)的有Canvas  lockCanvas() 
Canvas  lockCanvas(Rect dirty)  、void  removeCallback(SurfaceHolder.Callback callback)、void  unlockCanvasAndPost(Canvas canvas) 控制圖形以及繪制,而在SurfaceHolder.Callback 接口回調(diào)中可以通過下面三個(gè)抽象類可以自己定義具體的實(shí)現(xiàn),比如第一個(gè)更改格式和顯示畫面。 abstract void  surfaceChanged(SurfaceHolder holder, int format, int width, int height)
abstract void  surfaceCreated(SurfaceHolder holder)
abstract void  surfaceDestroyed(SurfaceHolder holder)
  對于Surface相關(guān)的,Android底層還提供了GPU加速功能,所以一般實(shí)時(shí)性很強(qiáng)的應(yīng)用中主要使用SurfaceView而不是直接從View構(gòu)建,同時(shí)Android123未來后面說到的OpenGL中的GLSurfaceView也是從該類實(shí)現(xiàn)。 

32. Android程序內(nèi)存管理必讀

很多開發(fā)者都是從J2ME或J2EE上過來的,對于內(nèi)存的使用和理解并不是很到位,Android開發(fā)網(wǎng)本次給大家一些架構(gòu)上的指導(dǎo),防止出現(xiàn)豆腐渣工程的出現(xiàn)。Android作為以Java語言為主的智能平臺對于我們開發(fā)一些高性能和質(zhì)量的軟件來說了解Android程序內(nèi)存管理機(jī)制是必須的。 Android的Dalvik VM在基礎(chǔ)方面和Sun JVM沒有什么大的區(qū)別僅僅是字節(jié)碼的優(yōu)化,我們要知道什么時(shí)候用gc什么時(shí)候用recycle以及到底用不用finalization,因?yàn)镴ava對內(nèi)存的分配只需要new開發(fā)者不需要顯示的釋放內(nèi)存,但是這樣造成的內(nèi)存泄露問題的幾率反而更高。   1.對于常規(guī)開發(fā)者而言需要了解 Java的四種引用方式,比如強(qiáng)引用,軟引用,弱引用以及虛引用。一些復(fù)雜些的程序在長期運(yùn)行很可能出現(xiàn)類似OutOfMemoryError的異常。 2.并不要過多的指望gc,不用的對象可以顯示的設(shè)置為空,比如obj=null,這里Android123提示大家,java的gc使用的是一個(gè)有向圖,判斷一個(gè)對象是否有效看的是其他的對象能到達(dá)這個(gè)對象的頂點(diǎn),有向圖的相對于鏈表、二叉樹來說開銷是可想而知。 3.Android為每個(gè)程序分配的對內(nèi)存可以通過Runtime類的totalMemory() freeMemory() 兩個(gè)方法獲取VM的一些內(nèi)存信息,對于系統(tǒng)heap內(nèi)存獲取,可以通過Dalvik.VMRuntime類的getMinimumHeapSize() 方法獲取最小可用堆內(nèi)存,同時(shí)顯示釋放軟引用可以調(diào)用該類的gcSoftReferences() 方法,獲取更多的運(yùn)行內(nèi)存。 4.對于多線程的處理,如果并發(fā)的線程很多,同時(shí)有頻繁的創(chuàng)建和釋放,可以通過concurrent類的線程池解決線程創(chuàng)建的效率瓶頸。 5. 不要在循環(huán)中創(chuàng)建過多的本地變量。 有關(guān)Android和Java的系統(tǒng)性能分析,Android123將在以后的文章中詳細(xì)講述如何調(diào)試Java分析內(nèi)存泄露以及Android上的gdb調(diào)試器分析得出內(nèi)存性能改進(jìn)。

33. Android中內(nèi)嵌字體實(shí)現(xiàn)個(gè)性化

在Android中我們的應(yīng)用可以靈活的內(nèi)嵌自己的字體文件,實(shí)現(xiàn)各個(gè)手機(jī)上可以正常的顯示個(gè)性化文字,我們都知道TextView的setTypeface方法可以設(shè)置目標(biāo)文字的顯示特性,比如字體、顏色、粗體、斜體等。我們直接找一個(gè)TrueTypeFont的字體文件即.ttf,對于Win32系統(tǒng)的用戶可以直接在Windows/fonts文件夾中能找到很多。比如微軟雅黑就不錯(cuò),可是體積太大,由于Android的Assets類有單個(gè)文件1MB體積的限制,我們先找個(gè)英文字體做測試。這里我們將字體文件android123.ttf放到工程的assets文件夾的fonts目錄中。      Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/android123.ttf");   
     TextView tv = (TextView)findViewById(R.id.text);            tv.setTypeface(tf);    //設(shè)置TextView的風(fēng)格
        tv.setText("CWJ Test");  
        tv.setTextSize(12); 
        tv.setTextColor(Color.RED);

34. 獲取和設(shè)置ListView的選擇項(xiàng)

獲取當(dāng)前選中項(xiàng)  int curPos = listView.getFirstVisiblePosition(); 當(dāng)然是用getItemAtPosition(int nPos)方法也可以 ,設(shè)置當(dāng)前選擇位置 listView.setSelectedPosition(lastPos);  對于基于AbsListView為基類的ListView等控件均可以使用這種方法。

35. android.text.format文件大小和日期解析類

很多網(wǎng)友可能直接將自己的J2ME項(xiàng)目生硬的移植到Android平臺,其實(shí)Google為我們提供好了文件大小和時(shí)間日期解析類,它位于android.text.format這個(gè)包中,它提供了強(qiáng)大的標(biāo)準(zhǔn)化解析方法:   1. IP地址解析類 在android.text.format.Formatter中提供了String formatIpAddress(int addr) 這個(gè)方法可以輕松方便的將socket中的int型轉(zhuǎn)成類似127.0.0.1的IP格式,需要注意的是Linux平臺的字節(jié)順序,即小字節(jié)序、低字節(jié)序little-endian。   2. 文件大小解析類 細(xì)心的網(wǎng)友可能還看到了android.text.format.Formatter中的formatFileSize方法,該方法String formatFileSize (Context context, long number) ,第二個(gè)參數(shù)是long型,一般為File對象的最后修改時(shí)間或創(chuàng)建時(shí)間的方法,最終返回類似 12KB、5Bytes的值,20MB的字符串。   3. 日期時(shí)間解析類 ,該類位于android.text.format.DateFormat這個(gè)package中,該類提供了Java中的三種時(shí)間對象,Android123提示大家下面三種方法為靜態(tài)可以直接調(diào)用,如下:   final static CharSequence  format(CharSequence inFormat, Date inDate)  //傳入Date對象
  Given a format string and a Date object, returns a CharSequence containing the requested date. final static CharSequence  format(CharSequence inFormat, Calendar inDate)  //Calendar對象
Given a format string and a Calendar object, returns a CharSequence containing the requested date. final static CharSequence  format(CharSequence inFormat, long inTimeInMillis)  //long對象
Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a CharSequence containing the requested date.   我們可能看到了第一個(gè)參數(shù)均為inFormat這是一個(gè)CharSequence接口的String類型,它提供了靈活的時(shí)間格式解析字符串描述,Android開發(fā)網(wǎng)提示大家注意大小寫要區(qū)分,如    April 6, 1970 at 3:23am 例子,那么inFormat參數(shù)的寫法和最終執(zhí)行的結(jié)果如下對照,下面就以Android123的CWJ生日為例子如下 "MM/dd/yy h:mmaa" -> "11/03/87 11:23am"
"MMM dd, yyyy h:mmaa" -> "Nov 3, 1987 11:23am"
"MMMM dd, yyyy h:mmaa" -> "November  3, 1987 11:23am"
"E, MMMM dd, yyyy h:mmaa" -> "Tues, November 3, 1987 11:23am"
"EEEE, MMMM dd, yyyy h:mmaa" -> "Tuesday, Nov 3, 1987 11:23am"   對于判斷一個(gè)時(shí)間是否為24小時(shí)制式可以通過android.text.format.DateFormat類的static boolean  is24HourFormat(Context context)方法來判斷。

36. Android代碼性能優(yōu)化技巧

目前來說Android 2.2的JIT性能有了本質(zhì)的提高,不過對于老版本的程序提高Java執(zhí)行效率還有很多語言特點(diǎn)來說,今天Android123提到的不是語法糖,而是基礎(chǔ)的問題,對于Java 1.5之后將會有明顯的改進(jìn)。下面的例子來自SDK: static class Foo {
        int mSplat;
    }
    Foo[] mArray = ... 
上面的靜態(tài)類Foo的執(zhí)行效果和性能,我們分三個(gè)方法zero、one和two來做對比。
    public void zero() {  //大多數(shù)人可能簡單直接這樣寫
        int sum = 0;
        for (int i = 0; i < mArray.length; ++i) {
            sum += mArray.mSplat;
        }
    }
    public void one() { //通過本地對象改進(jìn)性能
        int sum = 0;
        Foo[] localArray = mArray;
        int len = localArray.length;
        for (int i = 0; i < len; ++i) {
            sum += localArray.mSplat;
        }
    }
    public void two() { //推薦的方法,通過Java 1.5的新語法特性可以大幅改進(jìn)性能
        int sum = 0;
        for (Foo a : mArray) {
            sum += a.mSplat;
        } 
    } zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop. one() is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit. two() is fastest for devices without a JIT, and indistinguishable from one() for devices with a JIT. It uses the enhanced for loop syntax introduced in version 1.5 of the Java programming language.

37. Android開發(fā)注意點(diǎn) Part One

Android已經(jīng)的很多細(xì)節(jié)問題我們通過平臺開發(fā)總結(jié)不斷完善這個(gè)列表,如果你有相關(guān)的內(nèi)容可以聯(lián)系android123@163.com .    一、AssetManager - 已知單個(gè)文件處理不能大于1MB,所以如果資源很大,建議使用Zip格式壓縮存放。    二、ScrollView中嵌入ListView - 這個(gè)作法可能會出現(xiàn)你的ListView僅僅顯示1行半。    三、Android自帶的Zip處理類對文件名編碼無法識別,也沒有提供顯示的設(shè)置方法,在zlib中寫死了。    四、使用一些資源對象記住關(guān)閉,比如對于文件流對象最后           FileOutputStream os = xxx;            try {
                //dosomething
            } finally {
                os.close();  //顯示的使用finally關(guān)閉文件對象。
            }           對于Cursor而言,在移動位置時(shí)首先判斷Cursor是否為空,最終使用完仍然需要 close方法,如果重用,可以使用deactivate方法釋放當(dāng)前資源,通過requery方法再次查詢。   五、SDK中標(biāo)記為 deprecated 字樣的,常規(guī)情況下是有更好的方法可以替代,短期內(nèi)可以放心使用。這些方法一般高版本的SDK都可以向上兼容,目前尚未發(fā)現(xiàn)Android放棄某些API的支持。   六、Notification的Intent無法傳遞到目標(biāo)的Activity,Service和Broardcast沒有測試過,中途需要通過PendingIntent,可能這里出現(xiàn)了問題。

38. Android上HTTP協(xié)議通訊狀態(tài)獲取

通常情況下輕量級的Http傳輸Android平臺可以直接使用Sun Java的HttpURLConnection類方法處理,比如果自己定義一次請求header可以通過setRequestProperty設(shè)置,而我們需要獲取的Http Web Server狀態(tài)可以通過HttpURLConnection.getResponseCode() 的方法獲取。   當(dāng)然Http協(xié)議返回值常見的有 200 為成功,400為請求錯(cuò)誤,404為未找到,500為服務(wù)器內(nèi)部錯(cuò)誤,403無權(quán)查看,302為重定向等等。   對于Android平臺提供更完善的Apache類有HttpClient 、HttpPost、HttpResponse、HttpGet和HttpEntity,其中對于數(shù)據(jù)報(bào)頭header構(gòu)造通過HttpEntity,而返回狀態(tài)值可以通過HttpResponse獲取。   有關(guān)Android客戶端和Server通訊類相關(guān)的開發(fā)我們將會在以后文章中做大量實(shí)例介紹。

39. Android布局Java代碼構(gòu)造法

一般情況下對于Android程序布局我們往往使用XML文件來編寫,這樣可以提高開發(fā)效率,但是考慮到代碼的安全性以及執(zhí)行效率,可以通過Java代碼執(zhí)行創(chuàng)建,雖然Android編譯過的xml是二進(jìn)制的,但是加載xml解析器的效率對于資源占用還是比較大的,一般一個(gè)簡單的TextView,比如     <TextView
    android:id="@+id/textControl "
    android:layout_width="100px"
    android:layout_height="wrap_content" />    可以等價(jià)于下面的Java代碼:    LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(100, LayoutParams.WRAP_CONTENT); //寬度為100px,高為自適應(yīng)最小的高度    // setOrientation(VERTICAL); 設(shè)置布局為垂直    TextView textControl = new TextView(this);//如果從一個(gè)XXXLayout.,比如LinearLayout為View的基類時(shí)這里this應(yīng)該換成為創(chuàng)建改類的Context
   textControl.setText("Android開發(fā)網(wǎng)歡迎您");
   addView( textControl, textParams );    當(dāng)然Java處理效率比XML快得多,但是對于一個(gè)復(fù)雜界面的編寫,可能需要一些套嵌考慮,如果你思維靈活的話,使用Java代碼來布局你的Android應(yīng)用程序是一個(gè)更好的方法。

40. 測試Android軟件性能主要方法

對于Android平臺上軟件的性能測試可以通過以下幾種方法來分析效率瓶頸,目前Google在Android軟件開發(fā)過程中已經(jīng)引入了多種測試工具包,比如Unit測試工程,調(diào)試類,還有模擬器的Dev Tools都可以直接反應(yīng)執(zhí)行性能。    1. 在模擬器上的Dev Tools可以激活屏幕顯示當(dāng)前的FPS,CPU使用率,可以幫助我們測試一些3D圖形界面的性能。    2.  一般涉及到網(wǎng)絡(luò)應(yīng)用的程序,在效率上和網(wǎng)速有很多關(guān)系,這里需要多次的調(diào)試才能實(shí)際了解。    3. 對于邏輯算法的效率執(zhí)行,我們使用Android上最普遍的,計(jì)算執(zhí)行時(shí)間來查看:       long start = System.currentTimeMillis();
      //android開發(fā)網(wǎng)提示這里做實(shí)際的處理do something
      long duration = System.currentTimeMillis() - start;       最終duration保存著實(shí)際處理該方法需要的毫秒數(shù)。這里類似Win32上的GetTickCount,在Win 32和Symbian上都提供了高精度的性能計(jì)數(shù)器和低階計(jì)時(shí)器,這里在Dalvik VM上的Java層這種方法對于一般的應(yīng)用足以。    4. GC效率跟蹤,如果你執(zhí)行的應(yīng)用比較簡單,可以在DDMS中查看下Logcat的VM釋放內(nèi)存情況,大概模擬下那些地方可以緩存數(shù)據(jù)或改進(jìn)算法的。    5. 線程的使用和同步,Android平臺上給我們提供了豐富的多任務(wù)同步方法,但在深層上并沒有過多的比如自旋鎖等高級應(yīng)用,不過對于Service和appWidget而言,他們實(shí)際的產(chǎn)品中都應(yīng)該以多線程的方式處理,以釋放CPU時(shí)間,對于線程和堆內(nèi)存的查看這些都可以在DDMS中看到。    更多的調(diào)試和性能測試方法Android123將在以后的內(nèi)容中出現(xiàn)。

41. Splash Screen開場屏在Android中的實(shí)現(xiàn)

很多網(wǎng)友可能發(fā)現(xiàn)近期Tencent推出的手機(jī)QQ Android版包含了一個(gè)開場屏Splash Screen載入效果,通常游戲或大型軟件打開時(shí)可能需要一個(gè)釋放解析資源的過程,需要一個(gè)前臺的動畫播放和后臺的邏輯處理線程配合,當(dāng)然對于簡單的軟件也可以加一個(gè)Splash Screen作為美化。在Android平臺上如何實(shí)現(xiàn)呢?   首先創(chuàng)建一個(gè)Activirty,在SetContentView時(shí)直接通過ImageView創(chuàng)建一個(gè)全屏的圖片,Android123提示大家還要考慮好分辨率和當(dāng)前設(shè)備一致,onCreate添加代碼如下:   new Handler().postDelayed(new Runnable(){   // 為了減少代碼使用匿名Handler創(chuàng)建一個(gè)延時(shí)的調(diào)用
            public void run() {  
                Intent i = new Intent(SplashScreen.this, Main.class);    //通過Intent打開最終真正的主界面Main這個(gè)Activity
                SplashScreen.this.startActivity(i);    //啟動Main界面
                SplashScreen.this.finish();    //關(guān)閉自己這個(gè)開場屏
            }  
        }, 5000);   //5秒,夠用了吧

42. Android的Activity你知多少呢?

看到這個(gè)標(biāo)題很多網(wǎng)友肯定回答,我知道Activity是Android上的窗口基類,了解Activity的生命周期比如onCreate onStop等,呵呵,按照這樣說Android123還知道Activity的實(shí)現(xiàn)其實(shí)是從ApplicationContext,而ApplicationContext是從Context這個(gè)抽象類派生而來的,當(dāng)然我們看到顯示的是View或者ViewGroup,當(dāng)然今天說的不是這些東西,而是很多網(wǎng)友來問的Android為什么不設(shè)計(jì)一個(gè)任務(wù)管理器,當(dāng)然從Android 1.5開始ActivityManager類提供了restartPackage可以關(guān)閉一個(gè)程序,需要加上<uses-permission android:name="android.permission.RESTART_PACKAGES"/>這個(gè)權(quán)限,不過我們注意到,長按Home鍵可以看到以前程序的運(yùn)行,同時(shí)可以快速的切換回來。這就是Android獨(dú)有的程序生命周期管理機(jī)制 Activity歷史棧。   我們在一個(gè)普通的程序主窗口A中打開了一個(gè)窗口B,而窗口B打開了窗口C,但是按下Back鍵后結(jié)果出乎了預(yù)期,是的這就是Activity的history stack的原因,在數(shù)據(jù)結(jié)構(gòu)中棧是FIFO的,阻止我們不愿意看的情況的發(fā)生則可以在打開新Activity時(shí)加上標(biāo)記FLAG_ACTIVITY_NO_HISTORY,代碼如下:     Intent i= new Intent(this, cwj.class);  
    i.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);   //Android開發(fā)網(wǎng)提示大家相關(guān)的還有Intent.FLAG_ACTIVITY_CLEAR_TOP,都試試
    startActivity(i);    當(dāng)然更多的程序Activity控制可以再androidmanifest.xml中定義。

43. JSONObject在Android上的應(yīng)用

如果你過去開發(fā)過AJAX應(yīng)用,相信對JSONObject不會陌生吧,作為基于JavaScript的數(shù)據(jù)交換格式,可以直接代替Xml,這里Android從1.0開始就完全支持JSONObject。在平時(shí)應(yīng)用中直接引入import org.json.JSONObject;即可方便使用。當(dāng)然同類的還有SOAP。   在常規(guī)使用方便JSONObject對象可以實(shí)現(xiàn)類似Bundle或Parcel可以封裝數(shù)據(jù),代替一個(gè)XML的ITEM,但最大的優(yōu)勢是可以執(zhí)行一些簡單的方法,比如說getString、has、put、getBoolean、getInt等數(shù)據(jù)類型的存取操作。Android123提示大家對于常規(guī)的項(xiàng)目開發(fā),今天本文不考慮Server端的布局,在Android平臺上處理這些比較簡單,主要是一些http的請求處理。可以直接引入import org.apache.http.xxx來實(shí)現(xiàn)web server層的數(shù)據(jù)交換,如果你沒有專業(yè)的Server開發(fā)技術(shù),可以通過簡單的Web配合JSON方式快速實(shí)現(xiàn)自己的交互式應(yīng)用。

44. Android高性能文件類MemoryFile

很多網(wǎng)友抱怨Android處理底層I/O性能不是很理想,如果不想使用NDK則可以通過MemoryFile類實(shí)現(xiàn)高性能的文件讀寫操作。MemoryFile顧名思義就是內(nèi)存文件的意思,如果你過去從事過Win32開發(fā),那么它的原理就是MapViewOfFile(),當(dāng)然開發(fā)過Linux的網(wǎng)友可能很快就聯(lián)想到了mmap(),是的該類就是他們的托管代碼層封裝,位于android.os.MemoryFile這個(gè)位置,從Android 1.0開始就被支持。   MemoryFile適用于哪些地方呢?   對于I/O需要頻繁操作的,主要是和外部存儲相關(guān)的I/O操作,MemoryFile通過將 NAND或SD卡上的文件,分段映射到內(nèi)存中進(jìn)行修改處理,這樣就用高速的RAM代替了ROM或SD卡,性能自然提高不少,對于Android手機(jī)而言同時(shí)還減少了電量消耗。Android123提示網(wǎng)友該類實(shí)現(xiàn)的功能不是很多,直接從Object上繼承,通過JNI的方式直接在C底層執(zhí)行。 主要的構(gòu)造方法 MemoryFile(String name, int length) ,這里第二個(gè)參數(shù)為文件大小,需要說明的是Android的MemoryFile和傳統(tǒng)的mmap還有一點(diǎn)點(diǎn)區(qū)別,畢竟是手機(jī),它內(nèi)部的內(nèi)存管理方式ashmem會從內(nèi)核中回收資源。畢竟目前部分低端機(jī)型的RAM也比較吃緊。  synchronized boolean  allowPurging(boolean allowPurging)  //允許ashmem清理內(nèi)存,線程安全同步的方式。
void  close() //關(guān)閉,因?yàn)樵贚inux內(nèi)部mmap占用一個(gè)句柄,不用時(shí)一定要釋放了
InputStream  getInputStream()  返回讀取的內(nèi)容用Java層的InputStream保存
OutputStream  getOutputStream()  把一個(gè)OutputSream寫入到MemoryFile中
boolean  isPurgingAllowed() //判斷是否允許清理
int  length()  //返回內(nèi)存映射文件大小 下面就是我們熟悉的,讀寫細(xì)節(jié),主要是對字符數(shù)組的操作,這里大家要計(jì)算好每個(gè)文件類型的占用,同時(shí)考慮到效率對于自己分配的大小考慮粒度對齊。
int  readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
void  writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)     具體的實(shí)際應(yīng)用,Android開發(fā)網(wǎng)將在下次和大家講到。

45. TextUtils類-Android字符串處理類

對于字符串處理Android為我們提供了一個(gè)簡單實(shí)用的TextUtils類,如果處理比較簡單的內(nèi)容不用去思考正則表達(dá)式不妨試試這個(gè)在android.text.TextUtils的類,主要的功能如下:   是否為空字符 static boolean  isEmpty(CharSequence str)  拆分字符串  public static String[] split (String text, String expression) ,Android開發(fā)網(wǎng)提示大家仔細(xì)看例子如下 String.split() returns [''] when the string to be split is empty. This returns []. This does not remove any empty strings from the result. For example split("a,", "," ) returns {"a", ""}. 拆分字符串使用正則 public static String[] split (String text, Pattern pattern)   確定大小寫是否有效在當(dāng)前位置的文本TextUtils.getCapsMode(CharSequence cs, int off, int reqModes)   使用HTML編碼這個(gè)字符串  static String  TextUtils.htmlEncode(String s)   

46. InputSream輸入流轉(zhuǎn)String字符串,Android開發(fā)工具類

在Android平臺上使用Java層處理I/O時(shí)主要使用流,這里Android開發(fā)網(wǎng)給大家一個(gè)方便的類,可以處理InputStream輸入流轉(zhuǎn)為String字符串,在效率上,我們使用了字符串拼接StringBuilder類減少內(nèi)存碎片以及BefferedReader類實(shí)現(xiàn)一個(gè)緩存。     private String Stream2String(InputStream is) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is), 16*1024); //強(qiáng)制緩存大小為16KB,一般Java類默認(rèn)為8KB
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {  //處理換行符
                    sb.append(line + "\n"); 
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return sb.toString();
        }     }

47. layout資源包含,android開發(fā)必讀

有時(shí)候我們在一個(gè)Android程序中可能會復(fù)用布局文件,這時(shí)可以在一個(gè)xml文件中復(fù)用過去的布局文件,但是和常規(guī)的使用不同的是,需要加上類似包含頭文件一樣的include關(guān)鍵字,比如下面我們需要包含layout文件夾下的view.xml布局文件,需要<include layout="@layout/view" />  這樣下,完整的如下,大家可以試一試。 <?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" 
    >  
<TextView    
    android:layout_width="wrap_content"   
    android:layout_height="wrap_content"   
    android:text="@string/cwj" 
    />
<include layout="@layout/view" /> 
<include android:id="@+id/block" layout="@layout/item" />   <TextView    
    android:layout_width="wrap_content"   
    android:layout_height="wrap_content"   
    android:text="@string/android123" 
    />  
</LinearLayout> 

48.Android控件開發(fā)之ToggleButton原理

在Android平臺上比較有特色的就是ToggleButton控件,雖然它的功能和CheckBox有些類似,但是他們的用處還是有一定的區(qū)別比如ToggleButton原本有圖片裝飾,通過ToggleButton可以很清楚的顯示某些狀態(tài)。它們均從Button為基類的CompoundButton中實(shí)現(xiàn),其真假事件從Checkable來實(shí)現(xiàn)。   public abstract class CompoundButton extends Button implements Checkable {
    private boolean mChecked; //狀態(tài)是否選中
    private int mButtonResource;
    private boolean mBroadcasting;
    private Drawable mButtonDrawable; //按鈕的圖標(biāo)
    private OnCheckedChangeListener mOnCheckedChangeListener; //選中狀態(tài)改變監(jiān)聽
    private OnCheckedChangeListener mOnCheckedChangeWidgetListener;     private static final int[] CHECKED_STATE_SET = {
        R.attr.state_checked
    };     public CompoundButton(Context context) {
        this(context, null);
    }     public CompoundButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }     public CompoundButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);         TypedArray a =
                context.obtainStyledAttributes(
                        attrs, com.android.internal.R.styleable.CompoundButton, defStyle, 0);         Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); 
        if (d != null) {
            setButtonDrawable(d);
        }         boolean checked = a
                .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false);
        setChecked(checked);         a.recycle(); //顯式的GC
    }     public void toggle() {
        setChecked(!mChecked);
    }     @Override
    public boolean performClick() {
              toggle();
        return super.performClick();
    }     public boolean isChecked() {
        return mChecked;
    }     public void setChecked(boolean checked) {
        if (mChecked != checked) {
            mChecked = checked;
            refreshDrawableState(); //更新當(dāng)前狀態(tài)的按鈕圖標(biāo)             if (mBroadcasting) {
                return;
            }             mBroadcasting = true;
            if (mOnCheckedChangeListener != null) {
                mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
            }
            if (mOnCheckedChangeWidgetListener != null) {
                mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
            }             mBroadcasting = false;           
        }
    }     public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        mOnCheckedChangeListener = listener;
    }     void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {
        mOnCheckedChangeWidgetListener = listener;
    }     public static interface OnCheckedChangeListener {
        void onCheckedChanged(CompoundButton buttonView, boolean isChecked);
    }      public void setButtonDrawable(int resid) {
        if (resid != 0 && resid == mButtonResource) {
            return;
        }         mButtonResource = resid;         Drawable d = null;
        if (mButtonResource != 0) {
            d = getResources().getDrawable(mButtonResource);
        }
        setButtonDrawable(d);
    }     public void setButtonDrawable(Drawable d) {
        if (d != null) {
            if (mButtonDrawable != null) {
                mButtonDrawable.setCallback(null);
                unscheduleDrawable(mButtonDrawable);
            }
            d.setCallback(this);
            d.setState(getDrawableState());
            d.setVisible(getVisibility() == VISIBLE, false);
            mButtonDrawable = d;
            mButtonDrawable.setState(null);
            setMinHeight(mButtonDrawable.getIntrinsicHeight());
        }         refreshDrawableState();
    }     @Override
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
        boolean populated = super.dispatchPopulateAccessibilityEvent(event);         if (!populated) {
            int resourceId = 0;
            if (mChecked) {
                resourceId = R.string.accessibility_compound_button_selected;
            } else {
                resourceId = R.string.accessibility_compound_button_unselected;
            }
            String state = getResources().getString(resourceId);
            event.getText().add(state);
            event.setChecked(mChecked);
        }         return populated;
    }     @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);         final Drawable buttonDrawable = mButtonDrawable;
        if (buttonDrawable != null) {
            final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
            final int height = buttonDrawable.getIntrinsicHeight();             int y = 0;             switch (verticalGravity) {
                case Gravity.BOTTOM:
                    y = getHeight() - height;
                    break;
                case Gravity.CENTER_VERTICAL:
                    y = (getHeight() - height) / 2;
                    break;
            }             buttonDrawable.setBounds(0, y, buttonDrawable.getIntrinsicWidth(), y + height);
            buttonDrawable.draw(canvas);
        }
    }     @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        }
        return drawableState;
    }     @Override
    protected void drawableStateChanged() { //android123提示狀態(tài)改變時(shí)需要更換按鈕的圖標(biāo)
        super.drawableStateChanged();
        if (mButtonDrawable != null) {
            int[] myDrawableState = getDrawableState();
            mButtonDrawable.setState(myDrawableState);
            invalidate();
        }
    }     @Override
    protected boolean verifyDrawable(Drawable who) {
        return super.verifyDrawable(who) || who == mButtonDrawable;
    }     static class SavedState extends BaseSavedState {
        boolean checked;              SavedState(Parcelable superState) {
            super(superState);
        }
        private SavedState(Parcel in) {
            super(in);
            checked = (Boolean)in.readValue(null);
        }         @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeValue(checked);
        }         @Override
        public String toString() {
            return "CompoundButton.SavedState{"
                    + Integer.toHexString(System.identityHashCode(this))
                    + " checked=" + checked + "}";
        }         public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }             public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }     @Override
    public Parcelable onSaveInstanceState() {
        // Force our ancestor class to save its state
        setFreezesText(true);
        Parcelable superState = super.onSaveInstanceState();         SavedState ss = new SavedState(superState);         ss.checked = isChecked();
        return ss;
    }     @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        setChecked(ss.checked);
        requestLayout();
    }
} 從上面來看我們知道CompuundButton的實(shí)現(xiàn)相對繁瑣了些,主要是考慮狀態(tài)是否已經(jīng)選中等情況的消息通知,Android開發(fā)網(wǎng)提醒大家而ToggleButton相對CompuundButton增加的給用戶而言主要是開關(guān)的文字顯示。 public class ToggleButton extends CompoundButton {
    private CharSequence mTextOn;
    private CharSequence mTextOff;
    private Drawable mIndicatorDrawable;     private static final int NO_ALPHA = 0xFF;
    private float mDisabledAlpha;
    public ToggleButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a =
            context.obtainStyledAttributes(
                    attrs, com.android.internal.R.styleable.ToggleButton, defStyle, 0);
        mTextOn = a.getText(com.android.internal.R.styleable.ToggleButton_textOn);
        mTextOff = a.getText(com.android.internal.R.styleable.ToggleButton_textOff);
        mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.ToggleButton_disabledAlpha, 0.5f);
        syncTextState();
        a.recycle();
    }     public ToggleButton(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.buttonStyleToggle);
    }     public ToggleButton(Context context) {
        this(context, null);
    }     @Override
    public void setChecked(boolean checked) {
        super.setChecked(checked);
        syncTextState();
    }     private void syncTextState() {
        boolean checked = isChecked();
        if (checked && mTextOn != null) {
            setText(mTextOn);
        } else if (!checked && mTextOff != null) {
            setText(mTextOff);
        }
    }     public CharSequence getTextOn() {
        return mTextOn;
    }     public void setTextOn(CharSequence textOn) {
        mTextOn = textOn;
    }     public CharSequence getTextOff() {
        return mTextOff;
    }     protected void onFinishInflate() {
        super.onFinishInflate();
        updateReferenceToIndicatorDrawable(getBackground());
    }     @Override
    public void setBackgroundDrawable(Drawable d) {
        super.setBackgroundDrawable(d);
        updateReferenceToIndicatorDrawable(d);
    }     private void updateReferenceToIndicatorDrawable(Drawable backgroundDrawable) {
        if (backgroundDrawable instanceof LayerDrawable) {
            LayerDrawable layerDrawable = (LayerDrawable) backgroundDrawable;
            mIndicatorDrawable =
                    layerDrawable.findDrawableByLayerId(com.android.internal.R.id.toggle);
        }
    }
    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        if (mIndicatorDrawable != null) {
            mIndicatorDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
        }
    }
}

49. AsyncTask實(shí)例代碼演示Android異步任務(wù)

上次我們講到了Android提供了一個(gè)較線程更簡單的處理多任務(wù)的方法AsyncTask異步任務(wù)類,相對于線程來說AsyncTask對于簡單的任務(wù)處理更安全,其內(nèi)部的實(shí)現(xiàn)方法使用了Android的Handler機(jī)制,對于常見的文件下載可以使用AsyncTask類來處理,在Browser瀏覽器中就是用了該類下載Web服務(wù)器URL的Favicon圖標(biāo)。   首先Android123以簡單的下載例子演示該類的大致結(jié)構(gòu),如下 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls);
            publishProgress((int) ((i / (float) count)100));
        }
        return totalSize;
    }
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}   最終我們執(zhí)行 DownloadFilesTask().execute(url1, url2, url3); 即可。   在Android瀏覽器中下載Favicon圖標(biāo)的實(shí)現(xiàn)如下: class DownloadTouchIcon extends AsyncTask<String, Void, Bitmap> {
    private final ContentResolver mContentResolver;
    private final Cursor mCursor;
    private final String mOriginalUrl;
    private final String mUrl;
    private final String mUserAgent;
    /* package */ BrowserActivity mActivity;     public DownloadTouchIcon(BrowserActivity activity, ContentResolver cr,
            Cursor c, WebView view) { //構(gòu)造方法
        mActivity = activity;
        mContentResolver = cr;
        mCursor = c;
        mOriginalUrl = view.getOriginalUrl();
        mUrl = view.getUrl();
        mUserAgent = view.getSettings().getUserAgentString();
    }     public DownloadTouchIcon(ContentResolver cr, Cursor c, String url) { //實(shí)現(xiàn)本類的構(gòu)造
        mActivity = null;
        mContentResolver = cr;
        mCursor = c;
        mOriginalUrl = null;
        mUrl = url;
        mUserAgent = null;
    }     @Override
    public Bitmap doInBackground(String... values) {   //返回Bitmap類型
        String url = values[0];         AndroidHttpClient client = AndroidHttpClient.newInstance(mUserAgent);
        HttpGet request = new HttpGet(url);        HttpClientParams.setRedirecting(client.getParams(), true); //處理302等重定向問題         try {
            HttpResponse response = client.execute(request);             if (response.getStatusLine().getStatusCode() == 200) { //如果OK
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    InputStream content = entity.getContent(); //將圖標(biāo)保存到InputStream中,因?yàn)槭嵌M(jìn)制內(nèi)容
                    if (content != null) {
                        Bitmap icon = BitmapFactory.decodeStream( //從流中取出Bitmap,這里使用了BitmapFactory類的靜態(tài)方法decodeStream
                                content, null, null);
                        return icon;
                    }
                }
            }
        } catch (IllegalArgumentException ex) {
            request.abort();
        } catch (IOException ex) {
            request.abort();
        } finally {
            client.close();
        }
        return null;
    }     @Override
    protected void onCancelled() {
        if (mCursor != null) {
            mCursor.close();
        }
    }     @Override
    public void onPostExecute(Bitmap icon) {
          if (mActivity != null) {
             mActivity.mTouchIconLoader = null;
        }         if (icon == null || mCursor == null || isCancelled()) {
            return;
        }     最終圖標(biāo)要保存到瀏覽器的內(nèi)部數(shù)據(jù)庫中,系統(tǒng)程序均保存為SQLite格式,Browser也不例外,因?yàn)閳D片是二進(jìn)制的所以使用字節(jié)數(shù)組存儲數(shù)據(jù)庫的BLOB類型         final ByteArrayOutputStream os = new ByteArrayOutputStream();
        icon.compress(Bitmap.CompressFormat.PNG, 100, os); //將Bitmap壓縮成PNG編碼,質(zhì)量為100%存儲
        ContentValues values = new ContentValues(); //構(gòu)造SQLite的Content對象,這里也可以使用raw sql代替
        values.put(Browser.BookmarkColumns.TOUCH_ICON,os.toByteArray()); //寫入數(shù)據(jù)庫的Browser.BookmarkColumns.TOUCH_ICON字段         if (mCursor.moveToFirst()) {
            do {
                mContentResolver.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI, mCursor.getInt(0)),values, null, null);
            } while (mCursor.moveToNext());
        }
        mCursor.close();
    }
}   本次Android開發(fā)網(wǎng)通過兩個(gè)AsyncTask類演示了多種類型的任務(wù)構(gòu)造,這里大家注意返回類型,本節(jié)演示了Android平臺上Content Provider、AsyncTask、Bitmap、HTTP以及Stream的相關(guān)操作,大家如何想很快提高開發(fā)水平其實(shí)只要理解Google如何去實(shí)現(xiàn)Android系統(tǒng)常規(guī)構(gòu)架就可以輕松入門谷歌移動平臺。

50. Android自定義View實(shí)例AnalogClock源碼

針對Android底層View的直接構(gòu)造很多網(wǎng)友沒有實(shí)戰(zhàn)經(jīng)驗(yàn),本次Android開發(fā)網(wǎng)結(jié)合目前平臺開源代碼一起通過AnalogClock類來理解View的直接繼承。AnalogClock就是Home Screen上的那個(gè)帶有兩根指針的表盤類。它的實(shí)現(xiàn)我們直接從開源代碼可以了解到:   public class AnalogClock extends View {
    private Time mCalendar;     private Drawable mHourHand; //時(shí)針
    private Drawable mMinuteHand; //分針
    private Drawable mDial; //表盤背景     private int mDialWidth; //表盤寬度
    private int mDialHeight; //表盤高度     private boolean mAttached; //附著狀態(tài)     private final Handler mHandler = new Handler(); //定一個(gè)Handler類實(shí)現(xiàn)更新時(shí)間
    private float mMinutes;
    private float mHour;
    private boolean mChanged; //時(shí)間是否改變     public AnalogClock(Context context) {
        this(context, null);
    }     public AnalogClock(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }     public AnalogClock(Context context, AttributeSet attrs,
                       int defStyle) {
        super(context, attrs, defStyle);
        Resources r = mContext.getResources();
        TypedArray a =
                context.obtainStyledAttributes(
                        attrs, com.android.internal.R.styleable.AnalogClock, defStyle, 0);         mDial = a.getDrawable(com.android.internal.R.styleable.AnalogClock_dial); //加載表盤資源
        if (mDial == null) {
            mDial = r.getDrawable(com.android.internal.R.drawable.clock_dial);
        }         mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour); //加載時(shí)針圖片資源
        if (mHourHand == null) {
            mHourHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_hour);
        }         mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute); //加載分針圖片
        if (mMinuteHand == null) {
            mMinuteHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_minute);
        }         mCalendar = new Time(); //獲取當(dāng)前系統(tǒng)時(shí)間         mDialWidth = mDial.getIntrinsicWidth(); //獲取表盤圖片的寬度
        mDialHeight = mDial.getIntrinsicHeight(); //高度,同上
    }     @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();         if (!mAttached) {
            mAttached = true;
            IntentFilter filter = new IntentFilter(); //注冊一個(gè)消息過濾器,獲取時(shí)間改變、時(shí)區(qū)改變的action             filter.addAction(Intent.ACTION_TIME_TICK);
            filter.addAction(Intent.ACTION_TIME_CHANGED);
            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);             getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
        }           mCalendar = new Time();         onTimeChanged();
    }     @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mAttached) {
            getContext().unregisterReceiver(mIntentReceiver); //反注冊消息過濾器
            mAttached = false;
        }
    }     @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize =  MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize =  MeasureSpec.getSize(heightMeasureSpec);         float hScale = 1.0f;
        float vScale = 1.0f;         if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
            hScale = (float) widthSize / (float) mDialWidth;
        }         if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {
            vScale = (float )heightSize / (float) mDialHeight;
        }         float scale = Math.min(hScale, vScale);         setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec),
                resolveSize((int) (mDialHeight * scale), heightMeasureSpec));
    }     @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mChanged = true;
    }     主要的繪圖重寫View的onDraw方法,我們可以看到通過canvas實(shí)例直接屏幕 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);         boolean changed = mChanged;
        if (changed) {
            mChanged = false;
        }         int availableWidth = mRight - mLeft;
        int availableHeight = mBottom - mTop;         int x = availableWidth / 2;
        int y = availableHeight / 2;         final Drawable dial = mDial;
        int w = dial.getIntrinsicWidth();
        int h = dial.getIntrinsicHeight();         boolean scaled = false;         if (availableWidth < w || availableHeight < h) {
            scaled = true;
            float scale = Math.min((float) availableWidth / (float) w,
                                   (float) availableHeight / (float) h);
            canvas.save();
            canvas.scale(scale, scale, x, y);
        }         if (changed) {
            dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
        }
        dial.draw(canvas);         canvas.save();
        canvas.rotate(mHour / 12.0f * 360.0f, x, y); //計(jì)算時(shí)針旋轉(zhuǎn)的角度,android123提示就是那個(gè)時(shí)針圖片的旋轉(zhuǎn)角度,直接反應(yīng)的就是表盤上那個(gè)針的時(shí)間
        final Drawable hourHand = mHourHand;
        if (changed) {
            w = hourHand.getIntrinsicWidth();
            h = hourHand.getIntrinsicHeight();
            hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
        }
        hourHand.draw(canvas);
        canvas.restore();         canvas.save();
        canvas.rotate(mMinutes / 60.0f * 360.0f, x, y); //同理,分針旋轉(zhuǎn)的角度         final Drawable minuteHand = mMinuteHand;
        if (changed) {
            w = minuteHand.getIntrinsicWidth();
            h = minuteHand.getIntrinsicHeight();
            minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
        }
        minuteHand.draw(canvas);
        canvas.restore();         if (scaled) {
            canvas.restore();
        }
    }     private void onTimeChanged() {  //獲取時(shí)間改變,計(jì)算當(dāng)前的時(shí)分秒
        mCalendar.setToNow();         int hour = mCalendar.hour;
        int minute = mCalendar.minute;
        int second = mCalendar.second;         mMinutes = minute + second / 60.0f;
        mHour = hour + mMinutes / 60.0f;
        mChanged = true;
    }     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //監(jiān)聽獲取時(shí)間改變action
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
                String tz = intent.getStringExtra("time-zone");
                mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
            }             onTimeChanged(); //獲取新的時(shí)間
            invalidate(); //刷新屏幕,強(qiáng)制類調(diào)用onDraw方法實(shí)現(xiàn)分針時(shí)針的走動
        }
    };    看了本例根據(jù),Android開發(fā)很簡單吧,感興趣的網(wǎng)友可以為本程序加入一個(gè)秒針,不過Android123提醒網(wǎng)友的是可能對于電池,以及系統(tǒng)運(yùn)行效率產(chǎn)生一定的影響,不過作為練習(xí)大家可以試一試。

51. ArrayList LinkedList Set HashMap介紹

  在Android開發(fā)中我們經(jīng)常需要對數(shù)據(jù)進(jìn)行分類和操作,對于輕量級的數(shù)據(jù)存儲我們可能不需要?jiǎng)佑肧QLite或效率以及類庫不完善的XML,由于SharedPreferences不具備數(shù)據(jù)枚舉方法,如果僅僅是一個(gè)String或Int數(shù)組可以通過一個(gè)標(biāo)記分割設(shè)計(jì)外,我們還是主要來看看Android或者說Java提供的基礎(chǔ)數(shù)據(jù)類型輔助類ArrayList LinkedList Set HashMap的介紹,如果你熟悉C++的STL或Boost庫可以略過本文。    在Java中提供了Collection和Map接口。其中List和Set繼承了Collection接口;同時(shí)用Vector、ArrayList、LinkedList三個(gè)類實(shí)現(xiàn)List接口,HashSet、TreeSet實(shí)現(xiàn)Set接口。直接有HashTable、HashMap、TreeMap實(shí)現(xiàn)Map接口。     Vector基于Array的List,性能也就不可能超越Array,并且Vector是“sychronized”的,這個(gè)也是Vector和ArrayList的唯一的區(qū)別。     ArrayList:同Vector一樣是一個(gè)基于Array的,但是不同的是ArrayList不是同步的。所以在性能上要比Vector優(yōu)越一些。Android123提示大家適用于順序性的查找     LinkedList:不同于前面兩種List,它不是基于Array的,作為鏈表數(shù)據(jù)結(jié)構(gòu)方式,所以不受Array性能的限制。當(dāng)對LinkedList做添加,刪除動作的時(shí)候只要更改nextNode的相關(guān)信息就可以實(shí)現(xiàn)了所以它適合于進(jìn)行頻繁進(jìn)行插入和刪除操作。這就是LinkedList的優(yōu)勢,當(dāng)然對于元素的位置獲取等方面就遜色很多。     List:         1. 所有的List中只能容納單個(gè)不同類型的對象組成的表,而不是Key-Value鍵值對。例如:[ tom,1,c ];         2. 所有的List中可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ];         3. 所有的List中可以有null元素,例如[ tom,null,1 ];         4. 基于Array的List(Vector,ArrayList)適合查詢,而LinkedList(鏈表)適合添加,刪除操作。 雖然Set同List都實(shí)現(xiàn)了Collection接口,但是他們的實(shí)現(xiàn)方式卻大不一樣。List基本上都是以Array為基礎(chǔ)。但是Set則是在HashMap的基礎(chǔ)上來實(shí)現(xiàn)的,這個(gè)就是Set和List的根本區(qū)別。      HashSet:HashSet的存儲方式是把HashMap中的Key作為Set的對應(yīng)存儲項(xiàng),HashMap的key是不能有重復(fù)的。HashSet能快速定位一個(gè)元素,但是放到HashSet中的對象需要實(shí)現(xiàn)hashCode()方法0。     TreeSet:將放入其中的元素按序存放,這就要求你放入其中的對象是可排序的。TreeSet不同于HashSet的根本是TreeSet是有序的。它是通過SortedMap來實(shí)現(xiàn)的。     Set總結(jié): 1. Set實(shí)現(xiàn)的基礎(chǔ)是Map(HashMap); 2. Set中的元素是不能重復(fù)的,如果使用add(Object obj)方法添加已經(jīng)存在的對象,則會覆蓋前面的對象,不能包含兩個(gè)元素e1、e2(e1.equals(e2))。     Map是一種把鍵對象和值對象進(jìn)行關(guān)聯(lián)的容器,Map有兩種比較常用的實(shí)現(xiàn): HashTable、HashMap和TreeMap。     HashMap也用到了哈希碼的算法,以便快速查找一個(gè)鍵,     TreeMap則是對鍵按序存放,因此它有一些擴(kuò)展的方法,比如firstKey(),lastKey()等。     HashMap和Hashtable的區(qū)別。 HashMap允許空(null)鍵(key)或值(value),由于非線程安全,效率上可能高于Hashtable。 Hashtable不允許空(null)鍵(key)或值(value)。    有關(guān)更多實(shí)用的Android開發(fā)技巧我們將在后面的文章中著重介紹。

52. ConditionVariable Android線程同步

ConditionVariable類位于android.os.ConditionVariable,它可以幫助Android線程同步。在SDK上的介紹ConditionVariable不同于標(biāo)準(zhǔn)Java位于java.lang.Object wait() 和 notify() ,這個(gè)類可以等待自己,這就意味著 open(), close() 和 block() 可能會假死 ,如果使用ConditionVariable類的open()在調(diào)用 block() 之前, block() 將不會阻塞,相反將會返回立即。    該類一共有4個(gè)方法    boolean  block(long timeout)
  阻止當(dāng)前線程知道條件是open,或直到超時(shí),這里參數(shù)long timeout為超時(shí)設(shè)置,Android123提示大家如果你們從事過Win32開發(fā),這個(gè)方法類似DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds); 函數(shù)。   void  block()
  阻止當(dāng)前線程知道條件 open ,是上面的無超時(shí)等待重載版本。   void  close()
重置條件為 close狀態(tài)。 void  open()
Open條件,釋放所有線程的阻塞.   ConditionVariable在創(chuàng)建時(shí)還有一種構(gòu)造方法是 public ConditionVariable (boolean state) ,如果為true,默認(rèn)時(shí)為opened,如果為false則是closed. ,默認(rèn)public ConditionVariable () 為close().

53.Android開發(fā)之Eclipse調(diào)試技巧

使用Google提供的ADT插件可以在Eclipse上很輕松的調(diào)試Android程序,我們切換到DDMS標(biāo)簽,選擇“Devices”標(biāo)簽,我們可以看到會出現(xiàn)類似下面的Debug Process(調(diào)試進(jìn)程)、Update Threads(更新線程)、Update Heap(更新堆)、Cause GC(引起垃圾回收)、Stop Process(停止進(jìn)程)、Screen Capture(屏幕截圖)、Reset adb(重啟Android Debug Bridge)   這里我們就可以很好的觀察Android程序運(yùn)行時(shí)的各種狀態(tài),比如進(jìn)程信息、線程分析、堆內(nèi)存的占用,結(jié)束一個(gè)進(jìn)程,當(dāng)然這些操作都是在DDMS框架下進(jìn)行的,日程開發(fā)的程序是無法執(zhí)行調(diào)用的。如果遇到adb調(diào)試橋運(yùn)行不穩(wěn)定時(shí)可以選擇reset adb來重新啟動

原文來自:tbkj
?