Android開發網上的一些重要知識點[經驗分享]

1. android單實例運行方法php

咱們都知道Android平臺沒有任務管理器,而內部App維護者一個Activity history stack來實現窗口顯示和銷燬,對於常規從快捷方式運行來看都是startActivity可能會使用FLAG_ACTIVITY_NEW_TASK標記來打開一個新窗口,好比Launcher,因此考慮單任務的實現方法比較簡單,首先Android123糾正下你們一種錯誤的方法就是直接在androidmanifest.xml的application節點中加入android:launchMode="singleInstance"這句,其實這樣將不會起到任何做用,Apps內部維護的歷史棧做用於Activity,咱們必須在activity節點中加入android:launchMode="singleInstance" 這句才能保證單實例,固然通常均加在主程序啓動窗口的Activity。

2. px像素如何轉爲dip設備獨立像素html

最近有網友問如何將px像素轉爲dip獨立設備像素,因爲Android的設備分辨率衆多,目前主流的爲wvga,而不少老的設備爲hvga甚至低端的qvga,對於兼容性來講使用dip無非是比較方便的,因爲他和分辨率無關和屏幕的密度大小有關,因此推薦使用。  px= (int) (dip*density+0.5f) //這裏android開發網提示你們不少網友獲取density(密度)的方法存在問題,從資源中獲取的是靜態定義的,通常爲1.0對於HVGA是正好的,而對於wvga這樣的應該從WindowsManager中獲取,WVGA爲1.5 這裏能夠再補充一下dip,sip的知識

3. Android中動態改變ImageView大小java

不少網友可能發如今layout.xml文件中定義了ImageView的絕對大小後,沒法動態修改之後的大小顯示,其實Android平臺在設計UI控件時考慮到這個問題,爲了適應不一樣的Drawable能夠經過在xml的相關ImageView中加入android:scaleType="fitXY" 這行便可,但由於使用了縮放可能會形成當前UI有所變形。使用的前提是限制ImageView所在的層,可使用一個內嵌的方法限制顯示。

4. 如何判斷Android手機當前是否聯網?android

若是擬開發一個網絡應用的程序,首先考慮是否接入網絡,在Android手機中判斷是否聯網能夠經過 ConnectivityManager 類的isAvailable()方法判斷,首先獲取網絡通信類的實例 ConnectivityManager cwjManager=(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); ,使用cwjManager.getActiveNetworkInfo().isAvailable(); 來返回是否有效,若是爲True則表示當前Android手機已經聯網,多是WiFi或GPRS、HSDPA等等,具體的能夠經過ConnectivityManager 類的getActiveNetworkInfo() 方法判斷詳細的接入方式,須要注意的是有關調用須要加入<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> 這個權限,android開發網提醒你們在真機上Market和Browser程序都使用了這個方法,來判斷是否繼續,同時在一些網絡超時的時候也能夠檢查下網絡鏈接是否存在,以避免浪費手機上的電力資源。

5. Drawable、Bitmap、Canvas和Paint的關係git

不少網友剛剛開始學習Android平臺,對於Drawable、Bitmap、Canvas和Paint它們之間的概念不是很清楚,其實它們除了Drawable外早在Sun的J2ME中就已經出現了,可是在Android平臺中,Bitmap、Canvas相關的都有所變化。   首先讓咱們理解下Android平臺中的顯示類是View,可是還提供了底層圖形類android.graphics,今天所說的這些均爲graphics底層圖形接口。   Bitmap - 稱做位圖,通常位圖的文件格式後綴爲bmp,固然編碼器也有不少如RGB56五、RGB888。做爲一種逐像素的顯示對象執行效率高,可是缺點也很明顯存儲效率低。咱們理解爲一種存儲對象比較好。   Drawable - 做爲Android平下通用的圖形對象,它能夠裝載經常使用格式的圖像,好比GIF、PNG、JPG,固然也支持BMP,固然還提供一些高級的可視化對象,好比漸變、圖形等。   Canvas - 名爲畫布,咱們能夠看做是一種處理過程,使用各類方法來管理Bitmap、GL或者Path路徑,同時它能夠配合Matrix矩陣類給圖像作旋轉、縮放等操做,同時Canvas類還提供了裁剪、選取等操做。    Paint - 咱們能夠把它看作一個畫圖工具,好比畫筆、畫刷。他管理了每一個畫圖工具的字體、顏色、樣式。   若是涉及一些Android遊戲開發、顯示特效能夠經過這些底層圖形類來高效實現本身的應用。 

6. Activity切換致使的onCreate重複執行web

部分網友會發現Activity在切換到後臺或佈局從橫屏LANDSCAPE切換到PORTRAIT,會從新切換Activity會觸發一次onCreate方法,咱們能夠在androidmanifest.xml中的activit元素加入這個屬性android:configChanges="orientation|keyboardHidden" 便可,好比 <activity android:name=".android123"  android:configChanges="orientation|keyboardHidden"android:label="@string/app_name">   同時在Activity的Java文件中重載onConfigurationChanged(Configuration newConfig)這個方法,這樣就不會在佈局切換或窗口切換時重載onCreate等方法。代碼以下: @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
     if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
     {
//land
     }
     else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
     {
//port
     }
    }

7. Android的ImageButton問題正則表達式

不少網友對Android提供的ImageButton有個疑問,當顯示Drawable圖片時就不會再顯示文字了,其實解決的方法有兩種,第一種就是圖片中就寫入文字,可是這樣解決會增長程序體積,同時硬編碼方式會影響多國語言的發佈。第二種解決方法很簡單,經過分析能夠看到ImageButton的layout,咱們能夠直接直接繼承,添加一個TextView,對齊方式爲右側便可實現ImageButton支持文字右側顯示。

8. Android代碼優化技術算法

1.Java內存控制   對於字符串操做而言若是須要連加這樣的操做建議使用StringBuilder,通過調試不難發現若是你的字符串每次連加,使用String須要的內存開銷會遠大於StringBuilder,而後Android手機常規的運行內存大約在128MB左右,對於運行多任務就須要考慮了,Android開發網提示由於Java有GC不須要手動釋放那麼分配的時候就要格外的當心,頻繁的GC操做仍然是很影響性能的,在調試時咱們能夠經過logcat查看內存釋放狀況。   2.循環使用   平時在訪問一個屬性的時候效率遠比一個固定變量低,若是你的循環估計次數經常大於5,假設xxx.GetLength()方法的值通常大於5,推薦這樣寫,好比   for(int i=0;i<xxx.GetLength();i++)   這裏xxx.GetLength在每次循環都要調用,必然會影響程序效率,在遊戲開發中顯得更爲明顯,改進的方法應該爲   int j=xxx.GetLength()    for(int i=0;i<j;i++)   3.圖片的優化   在Android平臺中2維圖像處理庫BitmapFactory作的比較智能,爲了減小文件體積和效率,經常不用不少資源文件,而把不少小圖片放在一個圖片中,有切片的方式來完成,在J2ME中咱們這樣是爲了將少文件頭而解決MIDP這些設備的問題,而Android中雖然機型硬件配置都比較高,有關Android G1硬件配置能夠參考G1手機參數以及評測,可是當資源多時這樣的運行效率仍是使人滿意的,至少Dalvik優化的還不是很夠。

9. Android開發進階之NIO非阻塞包(一)sql

對於Android的網絡通信性能的提升,咱們可使用Java上高性能的NIO (New I/O) 技術進行處理,NIO是從JDK 1.4開始引入的,NIO的N咱們能夠理解爲Noblocking即非阻塞的意思,相對應傳統的I/O,好比Socket的accpet()、read()這些方法而言都是阻塞的。   NIO主要使用了Channel和Selector來實現,Java的Selector相似Winsock的Select模式,是一種基於事件驅動的,整個處理方法使用了輪訓的狀態機,若是你過去開發過Symbian應用的話這種方式有點像活動對象,好處就是單線程更節省系統開銷,NIO的好處能夠很好的處理併發,對於Android網遊開發來講比較關鍵,對於多點Socket鏈接而言使用NIO能夠大大減小線程使用,下降了線程死鎖的機率,畢竟手機遊戲有UI線程,音樂線程,網絡線程,管理的難度可想而知,同時I/O這種低速設備將影響遊戲的體驗。   NIO做爲一種中高負載的I/O模型,相對於傳統的BIO (Blocking I/O)來講有了很大的提升,處理併發不用太多的線程,省去了建立銷燬的時間,若是線程過多調度是問題,同時不少線程可能處於空閒狀態,大大浪費了CPU時間,同時過多的線程多是性能大幅降低,通常的解決方案中可能使用線程池來管理調度但這種方法治標不治本。使用NIO可使併發的效率大大提升。固然NIO和JDK 7中的AIO還存在一些區別,AIO做爲一種更新的固然這是對於Java而言,若是你開發過Winsock服務器,那麼IOCP這樣的I/O完成端口能夠解決更高級的負載,固然了今天Android123主要給你們講解下爲何使用NIO在Android中有哪些用處。    NIO咱們分爲幾個類型分別描述,做爲Java的特性之一,咱們須要瞭解一些新的概念,好比ByteBuffer類,Channel,SocketChannel,ServerSocketChannel,Selector和SelectionKey。有關具體的使用,Android開發網將在明天詳細講解。網友能夠在Android SDK文檔中看下java.nio和java.nio.channels兩個包瞭解。http://www.android123.com.cn/androidkaifa/695.html
瞭解下這種技術,看看在立刻要作的項目中是否用獲得

10. Android Theme和Styles內部定義解析shell

昨天咱們講到的有關在AndroidManifest.xml中定義Activity的theme方法來實現無標題的方法,在使用xml讓你的Activity無標題方法 一文中講到的,不少網友不明白爲何這樣作,其實在Android123之前的文章中屢次提到了styles樣式定義方法,今天Android開發網再次把一些網友回顧瞭解下android樣式的內部定義。在一個工程的res/values/theme.xml中咱們能夠方便的定義本身的風格主題,好比下面的cwjTheme中咱們使用了基於android內部的白色調的背景Theme.Light,設置windowsNoTitle爲true表明沒有標題,背景顏色咱們使用了android內部定義的透明,同時設置listView控件的樣式爲cwjListView,xml樣式代碼以下:   <?xml version="1.0" encoding="utf-8"?> 
<resources> 
<style name="cwjTheme" parent="android:Theme.Light"> 
   <item name="android:windowNoTitle">true</item> 
   <item name="android:windowBackground">@android:color/transparent</item> 
   <item name="android:listViewStyle">@style/cwjListView</item> 
</style>  有關ListView控件咱們自定義的風格就是修改下系統listview這個控件的每行分隔符樣式,這裏咱們在工程下res/drawable文件夾下放一個圖片名爲list_selector圖片,這樣咱們的cwjListView的代碼能夠這樣寫   <style name="cwjListView" parent="@android:style/Widget.ListView"> 
     <item name="android:listSelector">@drawable/list_selector</item> 
   </style> 
</resources>   經過定義style能夠設置更多,好比讓cwjListView的字體顏色就加入textAppearance屬性,好比 <item name="textAppearance">@android:style/TextAppearance</item> 等等。

11.Android JSON解析示例代碼

來自Google官方的有關Android平臺的JSON解析示例,若是遠程服務器使用了json而不是xml的數據提供,在Android平臺上已經內置的org.json包能夠很方便的實現手機客戶端的解析處理。下面Android123一塊兒分析下這個例子,幫助Android開發者須要有關 HTTP通信、正則表達式、JSON解析、appWidget開發的一些知識。 public class WordWidget extends  AppWidgetProvider { //appWidget
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {
        context.startService(new Intent(context, UpdateService.class)); //避免ANR,因此Widget中開了個服務
    }     public static class UpdateService extends Service {
        @Override
        public void onStart(Intent intent, int startId) {
            // Build the widget update for today
            RemoteViews updateViews = buildUpdate(this);             ComponentName thisWidget = new ComponentName(this, WordWidget.class);
            AppWidgetManager manager = AppWidgetManager.getInstance(this);
            manager.updateAppWidget(thisWidget, updateViews);
        }         public RemoteViews buildUpdate(Context context) {
            // Pick out month names from resources
            Resources res = context.getResources();
            String[] monthNames = res.getStringArray(R.array.month_names);              Time today = new Time();
            today.setToNow();             String pageName = res.getString(R.string.template_wotd_title,
                    monthNames[today.month], today.monthDay);
            RemoteViews updateViews = null;
            String pageContent = "";             try {
                SimpleWikiHelper.prepareUserAgent(context);
                pageContent = SimpleWikiHelper.getPageContent(pageName, false);
            } catch (ApiException e) {
                Log.e("WordWidget", "Couldn't contact API", e);
            } catch (ParseException e) {
                Log.e("WordWidget", "Couldn't parse API response", e);
            }             Pattern pattern = Pattern.compile(SimpleWikiHelper.WORD_OF_DAY_REGEX); //正則表達式處理,有關定義見下面的SimpleWikiHelper類
            Matcher matcher = pattern.matcher(pageContent);
            if (matcher.find()) {
                updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_word);                 String wordTitle = matcher.group(1);
                updateViews.setTextViewText(R.id.word_title, wordTitle);
                updateViews.setTextViewText(R.id.word_type, matcher.group(2));
                updateViews.setTextViewText(R.id.definition, matcher.group(3).trim());                 String definePage = res.getString(R.string.template_define_url,
                        Uri.encode(wordTitle));
                Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage)); //這裏是打開相應的網頁,因此Uri是http的url,action是view即打開web瀏覽器
                PendingIntent pendingIntent = PendingIntent.getActivity(context,
                        0 /* no requestCode */, defineIntent, 0 /* no flags */);
                updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent); //單擊Widget打開Activity             } else { 
                updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_message);
                CharSequence errorMessage = context.getText(R.string.widget_error);
                updateViews.setTextViewText(R.id.message, errorMessage);
            }
            return updateViews;
        }         @Override
        public IBinder onBind(Intent intent) {
            // We don't need to bind to this service
            return null;
        }
    }
}   有關網絡通信的實體類,以及一些常量定義以下:   public class SimpleWikiHelper {
    private static final String TAG = "SimpleWikiHelper";     public static final String WORD_OF_DAY_REGEX =
            "(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}";     private static final String WIKTIONARY_PAGE =
            "http://en.wiktionary.org/w/api.php?action=query&prop=revisions&titles=%s&" +
            "rvprop=content&format=json%s";     private static final String WIKTIONARY_EXPAND_TEMPLATES =
            "&rvexpandtemplates=true";     private static final int HTTP_STATUS_OK = 200;     private static byte[] sBuffer = new byte[512];     private static String sUserAgent = null;      public static class ApiException extends Exception {
        public ApiException(String detailMessage, Throwable throwable) {
            super(detailMessage, throwable);
        }         public ApiException(String detailMessage) {
            super(detailMessage);
        }
    }     public static class ParseException extends Exception {
        public ParseException(String detailMessage, Throwable throwable) {
            super(detailMessage, throwable);
        }
    }     public static void prepareUserAgent(Context context) {
        try {
            // Read package name and version number from manifest
            PackageManager manager = context.getPackageManager();
            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
            sUserAgent = String.format(context.getString(R.string.template_user_agent),
                    info.packageName, info.versionName);         } catch(NameNotFoundException e) {
            Log.e(TAG, "Couldn't find package information in PackageManager", e);
        }
    }     public static String getPageContent(String title, boolean expandTemplates)
            throws ApiException, ParseException {
        String encodedTitle = Uri.encode(title);
        String expandClause = expandTemplates ? WIKTIONARY_EXPAND_TEMPLATES : "";         String content = getUrlContent(String.format(WIKTIONARY_PAGE, encodedTitle, expandClause));
        try {
            JSONObject response = new JSONObject(content);
            JSONObject query = response.getJSONObject("query");
            JSONObject pages = query.getJSONObject("pages");
            JSONObject page = pages.getJSONObject((String) pages.keys().next());
            JSONArray revisions = page.getJSONArray("revisions");
            JSONObject revision = revisions.getJSONObject(0);
            return revision.getString("*");
        } catch (JSONException e) {
            throw new ParseException("Problem parsing API response", e);
        }
    }     protected static synchronized String getUrlContent(String url) throws ApiException {
        if (sUserAgent == null) {
            throw new ApiException("User-Agent string must be prepared");
        }         HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(url);
        request.setHeader("User-Agent", sUserAgent); //設置客戶端標識         try {
            HttpResponse response = client.execute(request);             StatusLine status = response.getStatusLine();
            if (status.getStatusCode() != HTTP_STATUS_OK) {
                throw new ApiException("Invalid response from server: " +
                        status.toString());
            }             HttpEntity entity = response.getEntity();
            InputStream inputStream = entity.getContent(); //獲取HTTP返回的數據流             ByteArrayOutputStream content = new ByteArrayOutputStream();             int readBytes = 0;
            while ((readBytes = inputStream.read(sBuffer)) != -1) {
                content.write(sBuffer, 0, readBytes); //轉化爲字節數組流
            }             return new String(content.toByteArray()); //從字節數組構建String
        } catch (IOException e) {
            throw new ApiException("Problem communicating with API", e);
        }
    }
} 有關整個每日維基的widget例子比較簡單,主要是幫助你們積累經常使用代碼,瞭解Android平臺 JSON的處理方式,畢竟不少Server仍是Java的。

12.Android中使用定時器TimerTask類介紹

在Android平臺中須要反覆按週期執行方法可使用Java上自帶的TimerTask類,TimerTask相對於Thread來講對於資源消耗的更低,除了使用Android自帶的AlarmManager使用Timer定時器是一種更好的解決方法。 咱們須要引入import java.util.Timer; 和 import java.util.TimerTask; private Timer  mTimer = new Timer(true);
private TimerTask mTimerTask;     mTimerTask = new TimerTask()
    {
      public void run()
      {
       Log.v("android123","cwj");
      }        
     };
     mTimer.schedule(mTimerTask, 5000,1000);  //在1秒後每5秒執行一次定時器中的方法,好比本文爲調用log.v打印輸出。   若是想取消能夠調用下面方法,取消定時器的執行    while(!mTimerTask.cancel());
      mTimer.cancel();   最後Android123提示你們,若是處理的東西比較耗時仍是開個線程比較好,Timer仍是會阻塞主線程的執行,更像是一種消息的執行方式。固然比Handler的postDelay等方法更適合處理計劃任務。

13.Android應用Icon大小在不一樣分辨率下定義

對於Android平臺來講,不一樣分辨率下Icon的大小設計有着不一樣的要求,對於目前主流的HDPI即WVGA級別來講,一般hdpi的應用icon大小爲72x72,而標準的mdpi即hvga爲48x48,對於目前HTC和Motorola推出的一些QVGA的使用了ldpi,圖標爲32x32,常見的Android圖標大小設計規範以下表所示:  Launcher
36 x 36 px
48 x 48 px
72 x 72 px  Menu
36 x 36 px
48 x 48 px
72 x 72 px  Status Bar
24 x 24 px
32 x 32 px
48 x 48 px  Tab
24 x 24 px
32 x 32 px
48 x 48 px  Dialog
24 x 24 px
32 x 32 px
48 x 48 px  List View
24 x 24 px
32 x 32 px
48 x 48 px   對於android界面設計的安全色,以下表   而對於系統自帶默認程序的圖標,下面爲png的透明格式,直接鼠標右鍵另存爲便可 

看看sdk文檔上的關於界面圖標的詳細說明。 14.Android控件美化Shape你會用嗎?

若是你對Android系統自帶的UI控件感受不夠滿意,能夠嘗試下自定義控件,咱們就以Button爲例,很早之前Android123就寫到過Android Button按鈕控件美化方法裏面提到了xml的selector構造。固然除了使用drawable這樣的圖片外今天Android開發網談下自定義圖形shape的方法,對於Button控件Android上支持如下幾種屬性shape、gradient、stroke、corners等。   咱們就以目前系統的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爲漸變結束的顏色,下面的angle是角度。接下來是stroke能夠理解爲邊緣,corners爲拐角這裏radius屬性爲半徑,最後是相對位置屬性padding。 對於一個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提示你們,以上幾個item的區別主要是體如今state_pressed按下或state_focused得到焦點時,噹噹來判斷顯示什麼類型,而沒有state_xxx屬性的item能夠看做是常規狀態下。

15. Android開發者應該保持如下特質

Android123推薦新手應該遵循   1. 深讀SDK文檔   2. 深讀SDK的APIDemo和Samples   3. 掌握GIT開源代碼   4. 多瞭解Android開源項目,學習別人的手法寫程序。

16. Android數組排序常見方法

  Android的數組排序方式基本上使用了Sun原生的Java API實現,經常使用的有Comparator接口實現compare方法和Comparable接口的compareTo方法,咱們對於一個數組列表好比ArrayList能夠經過這兩個接口進行排序和比較,這裏Android123給你們一個例子 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,則執行排序能夠調用方法 Collections.sort(mList, cwjComparator);

17.Android控件TextProgressBar進度條上顯文字

Android系統的進度條控件默認的設計的不是很周全,好比沒有包含文字的顯示,那麼如何在Android進度條控件上顯示文字呢? 來自Google內部的代碼來了解下,主要使用的addView這樣的方法經過覆蓋一層Chronometer秒錶控件來實現,整個代碼以下    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開發網提示關鍵部分在這裏     @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內存管理-SoftReference的使用

不少時候咱們須要考慮Android平臺上的內存管理問題,Dalvik VM給每一個進程都分配了必定量的可用堆內存,當咱們處理一些耗費資源的操做時可能會產生OOM錯誤(OutOfMemoryError)這樣的異常,Android123觀察了下國內的相似Market客戶端設計,基本上都沒有采用很好的內存管理機制和緩存處理。   若是細心的網友可能發現Android Market客戶端載入時,每一個列表項的圖標是異步刷新顯示的,但當咱們快速的往下滾動到必定數量好比50個,再往回滾動時可能咱們看到了部分App的圖標又從新開始加載,固然這一過程多是從SQLite數據庫中緩存的,可是在內存中已經經過相似SoftReference的方式管理內存。   在Java中內存管理,引用分爲四大類,強引用HardReference、弱引用WeakReference、軟引用SoftReference和虛引用PhantomReference。它們的區別也很明顯,HardReference對象是即便虛擬機內存吃緊拋出OOM也不會致使這一引用的對象被回收,而WeakReference等更適合於一些數量很少,但體積稍微龐大的對象,在這四個引用中,它是最容易被垃圾回收的,而咱們對於顯示相似Android Market中每一個應用的App Icon時能夠考慮使用SoftReference來解決內存不至於快速回收,同時當內存短缺面臨Java VM崩潰拋出OOM前時,軟引用將會強制回收內存,最後的虛引用通常沒有實際意義,僅僅觀察GC的活動狀態,對於測試比較實用同時必須和ReferenceQueue一塊兒使用。   對於一組數據,咱們能夠經過HashMap的方式來添加一組SoftReference對象來臨時保留一些數據,同時對於須要反覆經過網絡獲取的不常常改變的內容,能夠經過本地的文件系統或數據庫來存儲緩存,但願給國內作App Store這樣的客戶端一些改進建議。

19. 反射在Android開發中的利弊

因爲Android 2.2的推出,不少新的API加入致使不少項目移植須要考慮使用Java的反射機制Reflection來動態調用,動態調用的好處就是不須要使用引用文件,直接經過JDK中聲明好的方法直接調用,自己原理基於JVM的,從Java 1.5開始支持,原理上就是根據類名而不實例化對象的狀況下,得到對象的方法或屬性而直接調用。   Android開發時反射能幫助咱們多少?   1. 有些網友可能發現Android的SDK比較封閉,不少敏感的方法常規的用戶沒法編譯,咱們若是翻看了代碼直接在反射中聲明動態調用便可。好比不少internal或I開頭的AIDL接口都可以經過反射輕鬆調用。   2. 反射對於Android123來講更重要的是考慮到應用的兼容性,咱們目前主要兼容從Android 1.5到2.2的項目,API Level從3到8能夠方便的擴充,調用前咱們預留一個標誌位聲明該API的最低以及最高的API Level爲多少能夠調用。   3. 對於調試Java的反射是功臣了,在Logcat中咱們能夠看到出錯的地方確定有相似java.lang.reflect.XXX的字樣,這種自檢機制能夠幫助咱們方便的調試Android應用程序。   反射的缺點有哪些?   1. 由於是動態執行的,效率天然沒有預編譯時引用現有的庫效率高,就像平時咱們Win32開發時,能夠不用h文件,直接經過GetProcAddress同樣去動態獲取方法的地址。固然效率要根據複雜程度而決定,通常稍微複雜的處理性能損失可能超過20%,對於一些複雜的涉及Java自動類型轉換判斷,執行時間多是直接引用的上千倍,因此最終咱們調試時必須考慮性能問題。   2. 由於反射是動態的,因此須要處理不少異常,否則Dalvik崩潰出Force Close的機率會大不少,很簡單的一個反射就須要至少3個異常捕獲,自己try-catch效率就不是很高,天然進一步影響運行效率,對於Android開發咱們必須考慮這些問題。   3. 反射由於致使代碼臃腫,天然稍微複雜的幾個方法實用反射將會致使代碼可讀性和維護性下降,若是很抽象的調用Android開發網強烈不推薦這種方法。   最後要說的是Reflection並非Java的專利,微軟的.Net也一樣支持,同時更多的動態語言如Ruby等均支持這一特性。

20.AsyncTask對比Thread加Handler

不少網友可能發現Android平臺不少應用使用的都是AsyncTask,而並不是Thread和Handler去更新UI,這裏Android123給你們說下他們到底有什麼區別,咱們平時應該使用哪一種解決方案。從Android 1.5開始系統將AsyncTask引入到android.os包中,過去在很早1.1和1.0 SDK時其實官方將其命名爲UserTask,其內部是JDK 1.5開始新增的concurrent庫,作過J2EE的網友可能明白併發庫效率和強大性,比Java原始的Thread更靈活和強大,但對於輕量級的使用更爲佔用系統資源。Thread是Java早期爲實現多線程而設計的,比較簡單不支持concurrent中不少特性在同步和線程池類中須要本身去實現不少的東西,對於分佈式應用來講更須要本身寫調度代碼,而爲了Android UI的刷新Google引入了Handler和Looper機制,它們均基於消息實現,有事可能消息隊列阻塞或其餘緣由沒法準確的使用。   Android開發網推薦你們使用AsyncTask代替Thread+Handler的方式,不只調用上更爲簡單,通過實測更可靠一些,Google在Browser中大量使用了異步任務做爲處理耗時的I/O操做,好比下載文件、讀寫數據庫等等,它們在本質上都離不開消息,可是AsyncTask相比Thread加Handler更爲可靠,更易於維護,但AsyncTask缺點也是有的好比一旦線程開啓即dobackground方法執行後沒法給線程發送消息,僅能經過預先設置好的標記來控制邏輯,固然能夠經過線程的掛起等待標誌位的改變來通信,對於某些應用Thread和Handler以及Looper可能更靈活。

21. Android Drawable疊加處理方法

你們可能知道Bitmap的疊加處理在Android平臺中能夠經過Canvas一層一層的畫就好了,而Drawable中如何處理呢? 除了使用BitmapDrawable的getBitmap方法將Drawable轉換爲Bitmap外,今天Android123給你們說下好用簡單的LayerDrawable類,LayerDrawable顧名思義就是層圖形對象。下面直接用一個簡單的代碼表示:     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); //參數爲上面的Drawable數組
        ld.setLayerInset(1, 1, 1, 1, 1);  //第一個參數1表明數組的第二個元素,爲白色
        ld.setLayerInset(2, 2, 2, 2, 2); //第一個參數2表明數組的第三個元素,爲位圖資源
    mImageView.setImageDrawable(ld);   上面的方法中LayerDrawable是關鍵,Android開發網提示setLayerInset方法原型爲public void setLayerInset (int index, int l, int t, int r, int b) 其中第一個參數爲層的索引號,後面的四個參數分別爲left、top、right和bottom。對於簡單的圖片合成咱們能夠將第一和第二層的PaintDrawable換成BitmapDrawable便可實現簡單的圖片合成。

22. onRetainNonConfigurationInstance和getLastNonConfigurationInstance

不少網友可能知道Android橫豎屏切換時會觸發onSaveInstanceState,而還原時會產生onRestoreInstanceState,可是Android的Activity類還有一個方法名爲onRetainNonConfigurationInstance和getLastNonConfigurationInstance這兩個方法。    咱們能夠經過  onRetainNonConfigurationInstance 代替 onSaveInstanceState,好比距離2   @Override
  public Object onRetainNonConfigurationInstance()
{    
       //這裏須要保存的內容,在切換時不是bundle了,咱們能夠直接經過Object來代替
      return obj;
} 在恢復窗口時,咱們能夠不使用 onRestoreInstanceState,而代替的是 getLastNonConfigurationInstance 方法。咱們能夠直接在onCreate中使用,好比   Object obj = getLastNonConfigurationInstance();     最終obj的內容就是上次切換時的內容。   這裏Android123提醒你們,每次Activity橫豎屏切換時onCreate方法都會被觸發。

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

不少時候咱們感性Google在設計Android時遵照了大量MVC架構方式,可讓寫公共代碼、美工和具體邏輯開發人員獨立出來。有關Android的資源文件values/strings.xml中如何實現格式化字符串呢? 這裏Android123舉個簡單的例子,以及最終可能會用到哪些地方。 <?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <string name="app_name">cwj_Demo</string> 
    <string name="hello">android開發網</string> 
</resources>  上面是一段簡單的字符串資源文件,沒有用到格式化,由於比較簡單直接描述了意思,當咱們設計一個相似 Delete xxx File ? 的時候,咱們可能須要在Java中動態獲取 xxx 的名稱,因此定義資源時使用格式化能夠輕鬆解決,不須要一堆String去拼接或StringBuffer一個一個append這樣的愚蠢方法,看例子     <string name="alert">Delete %1$s File</string>   這裏%1$s表明這是一個字符串型的,若是是整數型能夠寫爲%1$d,相似printf這樣的格式化字符串函數,固然若是包含了多個須要格式化的內容,則第二個能夠寫爲%2$s或%2$d了,那麼最終在Java中如何調用呢? 看下面的例子:    例一: 整數型的   <string name="alert">I am %1$d years old</string>  定義的是這樣的    固然,咱們杜絕意外狀況,好比冒出個secret這樣的string類型的,注意上面是%1$d不是%1$s,因此默認標準的合併成爲    int nAge=23;    String sAgeFormat = getResources().getString(R.string.alert);     String sFinalAge = String.format(sAgeFormat, nAge);      這樣執行完後,就組成了 I am 23 years old,是否是很方便啊.  固然了,下面看下String字符串時的狀況.   例二: 字符串型的   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);    咱們看到了整個,整個定義相似MFC的CString::Format或Mac OS中的NSLog,可是須要顯示相似C#中那樣顯示的標出參數的數字,好比%1或%n,這裏數字表明參數的第n個。本行最終sFinalInfo顯示的內容爲   My name is cwj , I am form Shanghai 。固然了你有什麼不懂的地方能夠來函至 android123@163.com

24. Android工程內嵌資源文件的兩種方法

Android軟件通常處理大的資源經過sdcard好比在線下載資源到sdcard,而apk中內嵌資源或二進制文件時通常使用下面的兩種方法:   方法一   res/raw目錄下存放,好比cwj.dat一個二進制文件,咱們能夠讀取能夠直接  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系統處理Assert有個bug,在AssertManager中不能處理單個超過1MB的文件,否則會報異常具體數值你們能夠測試下傳個稍大的文件,咱們在兩年前的文章中有提到,而第一種raw沒這個限制能夠放個4MB的Mp3文件沒問題。

25. Android自定義View以及layout屬性全攻略

對於Android系統的自定義View可能你們都熟悉了,對於自定義View的屬性添加,以及Android的Layout的命名空間問題,不少網友還不是很清楚,今天Android123一塊兒再帶你們溫習一下   CwjView myView=new CwjView(context);   若是用於遊戲或整個窗體的界面,咱們可能直接在onCreate中setContentView(myView); 固然若是是控件,咱們可能會須要從Layout的xml中聲明,好比   <cn.com.android123.CwjView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
  />   固然,咱們也能夠直接從父類聲明好比   <View class="cn.com.android123.CwjView"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
  /> 上面咱們僅用了父類View的兩個屬性,均來自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"
   /> 咱們能夠看到上面的三個屬性,是咱們自定義的。做爲標準xml規範,可能還包含了相似 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的三個屬性咱們如何定義呢? 在工程的res/values目錄中咱們新建一個cwj_attr.xml文件,編碼方式爲utf-8是一個好習慣,內容以下 <?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系統內置的格式類型有integer好比ProgressBar的進度值,float好比RatingBar的值多是3.5顆星,boolean好比ToggleButton的是否勾選,string好比TextView的text屬性,固然除了咱們常見的基礎類型外,Android的屬性還有特殊的好比color是用於顏色屬性的,能夠識別爲#FF0000等類型,固然還有dimension的尺寸類型,好比23dip,15px,18sp的長度單位,還有一種特殊的爲reference,通常用於引用@+id/cwj @drawable/xxx這樣的類型。   固然何時用reference呢? 咱們就以定義一個顏色爲例子,   <attr name="red" format="color|reference" />  這裏咱們用了邏輯或的運算符,定義的紅色是顏色類型的,同時能夠被引用   固然,對於咱們自定義的類中,咱們須要使用一個名爲obtainStyledAttributes的方法來獲取咱們的定義。在咱們自定義View的構造方法(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就獲取了咱們須要的內容,固然根據layout中的數值咱們自定義的CwjView須要動態的處理一些數據的狀況,可使用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就能夠任意使用了
} 以上兩種方法中,參數的最後一個數值爲默認的,若是您有不明白的地方能夠來函到android123@163.com 咱們會在第一時間回覆。

26. 自定義Android主題風格theme.xml方法

在Android中能夠經過自定義主題風格方式來實現個性化以及複用,首先咱們建立theme.xml主題文件,保存位置爲工程的res/values/theme.xml ,這裏咱們能夠能夠爲主題起一個名稱,好比CWJ,這裏去除了xml的文件頭<?xml version="1.0" encoding="utf-8"?>這行,咱們在工程中只需在androidmanifest.xml文件的Activity節點中加入android:theme="@style/Theme.CWJ" 屬性,則這個Activity就使用了這種主題風格,整個xml的關鍵代碼以下: <resources>
    <style name="Theme.CWJ" parent="android:Theme">
        <item name="android:windowBackground">@drawable/android123</item>
    </style>
</resources>   其中上面的代碼中,咱們定義設置全局android:windowBackground即背景值爲/res/drawable中的android123圖片爲背景,更多的屬性定義能夠參考view的layout xml屬性設置,好比咱們設置全部字體顏色、大致大小和樣式,能夠在style節點中加入   <item name="android:textColor">#fff</item>
  <item name="android:textSize">14sp</item>
  <item name="android:textStyle">bold</item>  固然咱們能夠將上面的android123的圖片改進下,使用一個xml文件替代,好比使用bitmap對象,則/res/drawable/android123.xml的完整代碼變爲   <?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" />    這裏咱們使用了一個bitmap對象來解析cwj_image圖片,固然這裏能夠識別各類類型的圖片,其中android:tileMode是bitmap的內部屬性,其中tileMode設置爲repeat表明重複,這樣能夠節省bitmap資源,好比咱們的背景是一層樓,那麼全屏能夠顯示一樣的爲5層效果,而圖片僅是一層大小,對於資源利用相對更高。   固然bitmap的屬性tileMode的值爲repeat外還有其餘的值好比clamp、mirror,這些值並無在SDK中並無找到定義,經過上次Android開發網的 Android自定義View以及layout屬性全攻略 一文,咱們能夠聯想到bitmap屬於android.graphics.Bitmap 包,因爲是android框架,因此下載git的base包,找到該類,類的實例化時android123已經在 Android自定義View以及layout屬性全攻略 說的很清楚,因此咱們定位到res\values中找到attr.xml有關bitmap的定義便可,有關bitmap的更多屬性如  antialias、filter和dither均可以找到使用。

27. android調試工具monkey壓力測試實戰

不少Android開發者可能由於沒有充分測試本身的軟件形成很容易出現FC(Force Close)的問題,這裏咱們能夠經過使用Android固件中自帶的monkey工具來作軟件的壓力測試,monkey工具能夠模擬各類按鍵,觸屏,軌跡球、activity等事件,這裏Android123提示你們說白了monkey就是一個小猴子隨機狂玩你的android軟件,看看會不會產生異常。   具體的使用咱們經過Android SDK給咱們的adb調試橋連接設備或模擬器,進入Linux Shell狀態,固然咱們能夠輸入adb shell獲取設備的shell,也能夠直接經過adb命令執行,好比說adb shell monkey來查看monkey工具中的參數說明,如圖:      咱們要測試的apk文件要在android設備中已經安裝,固然模擬器中也能夠測試的。執行adb shell monkey -p cn.com.android123.cwj -v 100 咱們執行這句的中包含了p參數,這裏表明已安裝軟件的packageName,而v表明查看monkey生成的詳細隨機事件名,最後的數字100爲咱們測試的隨機事件數量爲100.有關更多的測試方法,請查看上圖中的參數,整個測試比較簡單單頗有效,不妨試試。

28. 自定義View

有關Android的自定義View的框架今天咱們一塊兒討論下,對於常規的遊戲,咱們在View中須要處理如下幾種問題: 1.控制事件 2.刷新View 3. 繪製View   1. 對於控制事件今天咱們只處理按鍵事件onKeyDown,之後的文章中將會講到屏幕觸控的具體處理onTouchEvent以及Sensor重力感應等方法。   2. 刷新view的方法這裏主要有invalidate(int l, int t, int r, int b) 刷新局部,四個參數分別爲左、上、右、下。整個view刷新 invalidate(),刷新一個矩形區域 invalidate(Rect dirty) ,刷新一個特性Drawable, invalidateDrawable(Drawable drawable) ,執行invalidate類的方法將會設置view爲無效,最終致使onDraw方法被從新調用。因爲今天的view比較簡單,Android123提示你們若是在線程中刷新,除了使用handler方式外,能夠在Thread中直接使用postInvalidate方法來實現。   3. 繪製View主要是onDraw()中經過形參canvas來處理,相關的繪製主要有drawRect、drawLine、drawPath等等。view方法內部還重寫了不少接口,其回調方法能夠幫助咱們判斷出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獲取當view變化時每一個形參的變更。   下面cwjView是咱們爲從此遊戲設計的一個簡單自定義View框架,咱們能夠看到在Android平臺自定義view仍是很簡單的,同時Java支持多繼承能夠幫助咱們不斷的完善複雜的問題。 public class cwjView extends View {     public cwjView(Context context) {
      super(context);
      setFocusable(true); //容許得到焦點
      setFocusableInTouchMode(true); //獲取焦點時容許觸控
   }    @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); //若是不讓父類處理記住調用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) { //處理按鍵事件,響應的軌跡球事件爲 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);   //這裏面是原始的大小,須要從新計算能夠修改本行      //dosomething    }

29. Canvas和Paint實例

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

30. View類詳解

在Android遊戲開發之旅二中咱們講到了View和SurfaceView的區別,今天Android123從View類開始着重的介紹Android圖形顯示基類的相關方法和注意點。   自定義View的經常使用方法: onFinishInflate() 當View中全部的子控件均被映射成xml後觸發 onMeasure(int, int) 肯定全部子元素的大小 onLayout(boolean, int, int, int, int) 當View分配全部的子元素的大小和位置時觸發 onSizeChanged(int, int, int, int) 當view的大小發生變化時觸發 onDraw(Canvas) view渲染內容的細節 onKeyDown(int, KeyEvent) 有按鍵按下後觸發 onKeyUp(int, KeyEvent) 有按鍵按下後彈起時觸發 onTrackballEvent(MotionEvent) 軌跡球事件 onTouchEvent(MotionEvent) 觸屏事件 onFocusChanged(boolean, int, Rect) 當View獲取或失去焦點時觸發  onWindowFocusChanged(boolean) 當窗口包含的view獲取或失去焦點時觸發 onAttachedToWindow() 當view被附着到一個窗口時觸發 onDetachedFromWindow() 當view離開附着的窗口時觸發,Android123提示該方法和  onAttachedToWindow() 是相反的。 onWindowVisibilityChanged(int) 當窗口中包含的可見的view發生變化時觸發   以上是View實現的一些基本接口的回調方法,通常咱們須要處理畫布的顯示時,重寫onDraw(Canvas)用的的是最多的:   @Override
   protected void onDraw(Canvas canvas) {
    //這裏咱們直接使用canvas對象處理當前的畫布,好比說使用Paint來選擇要填充的顏色    Paint paintBackground = new Paint();
   paintBackground.setColor(getResources().getColor(R.color.xxx));  //從Res中找到名爲xxx的color顏色定義
   canvas.drawRect(0, 0, getWidth(), getHeight(), paintBackground); //設置當前畫布的背景顏色爲paintBackground中定義的顏色,以0,0做爲爲起點,以當前畫布的寬度和高度爲重點即整塊畫布來填充。      具體的請查看Android123將來講到的Canvas和Paint,在Canvas中咱們能夠實現畫路徑,圖形,區域,線。而Paint做爲繪畫方式的對象能夠設置顏色,大小,甚至字體的類型等等。 } 固然還有就是處理窗口還原狀態問題(通常用於橫豎屏切換),除了在Activity中能夠調用外,開發遊戲時咱們儘可能在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中若是須要強制調用繪製方法onDraw,可使用invalidate()方法,它有不少重載版本,同時在線程中的postInvailidate()方法將在Android遊戲開發之旅六中的 自定義View完整篇講到。

31. View和SurfaceView

在Android遊戲當中充當主要的除了控制類外就是顯示類,在J2ME中咱們用Display和Canvas來實現這些,而Google Android中涉及到顯示的爲view類,Android遊戲開發中比較重要和複雜的就是顯示和遊戲邏輯的處理。這裏咱們說下android.view.View和android.view.SurfaceView。SurfaceView是從View基類中派生出來的顯示類,直接子類有GLSurfaceView和VideoView,能夠看出GL和視頻播放以及Camera攝像頭通常均使用SurfaceView,到底有哪些優點呢? SurfaceView能夠控制表面的格式,好比大小,顯示在屏幕中的位置,最關鍵是的提供了SurfaceHolder類,使用getHolder方法獲取,相關的有Canvas  lockCanvas() 
Canvas  lockCanvas(Rect dirty)  、void  removeCallback(SurfaceHolder.Callback callback)、void  unlockCanvasAndPost(Canvas canvas) 控制圖形以及繪製,而在SurfaceHolder.Callback 接口回調中能夠經過下面三個抽象類能夠本身定義具體的實現,好比第一個更改格式和顯示畫面。 abstract void  surfaceChanged(SurfaceHolder holder, int format, int width, int height)
abstract void  surfaceCreated(SurfaceHolder holder)
abstract void  surfaceDestroyed(SurfaceHolder holder)
  對於Surface相關的,Android底層還提供了GPU加速功能,因此通常實時性很強的應用中主要使用SurfaceView而不是直接從View構建,同時Android123將來後面說到的OpenGL中的GLSurfaceView也是從該類實現。 

32. Android程序內存管理必讀

不少開發者都是從J2ME或J2EE上過來的,對於內存的使用和理解並非很到位,Android開發網本次給你們一些架構上的指導,防止出現豆腐渣工程的出現。Android做爲以Java語言爲主的智能平臺對於咱們開發一些高性能和質量的軟件來講了解Android程序內存管理機制是必須的。 Android的Dalvik VM在基礎方面和Sun JVM沒有什麼大的區別僅僅是字節碼的優化,咱們要知道何時用gc何時用recycle以及到底用不用finalization,由於Java對內存的分配只須要new開發者不須要顯示的釋放內存,可是這樣形成的內存泄露問題的概率反而更高。   1.對於常規開發者而言須要瞭解 Java的四種引用方式,好比強引用,軟引用,弱引用以及虛引用。一些複雜些的程序在長期運行極可能出現相似OutOfMemoryError的異常。 2.並不要過多的期望gc,不用的對象能夠顯示的設置爲空,好比obj=null,這裏Android123提示你們,java的gc使用的是一個有向圖,判斷一個對象是否有效看的是其餘的對象能到達這個對象的頂點,有向圖的相對於鏈表、二叉樹來講開銷是可想而知。 3.Android爲每一個程序分配的對內存能夠經過Runtime類的totalMemory() freeMemory() 兩個方法獲取VM的一些內存信息,對於系統heap內存獲取,能夠經過Dalvik.VMRuntime類的getMinimumHeapSize() 方法獲取最小可用堆內存,同時顯示釋放軟引用能夠調用該類的gcSoftReferences() 方法,獲取更多的運行內存。 4.對於多線程的處理,若是併發的線程不少,同時有頻繁的建立和釋放,能夠經過concurrent類的線程池解決線程建立的效率瓶頸。 5. 不要在循環中建立過多的本地變量。 有關Android和Java的系統性能分析,Android123將在之後的文章中詳細講述如何調試Java分析內存泄露以及Android上的gdb調試器分析得出內存性能改進。

33. Android中內嵌字體實現個性化

在Android中咱們的應用能夠靈活的內嵌本身的字體文件,實現各個手機上能夠正常的顯示個性化文字,咱們都知道TextView的setTypeface方法能夠設置目標文字的顯示特性,好比字體、顏色、粗體、斜體等。咱們直接找一個TrueTypeFont的字體文件即.ttf,對於Win32系統的用戶能夠直接在Windows/fonts文件夾中能找到不少。好比微軟雅黑就不錯,但是體積太大,因爲Android的Assets類有單個文件1MB體積的限制,咱們先找個英文字體作測試。這裏咱們將字體文件android123.ttf放到工程的assets文件夾的fonts目錄中。      Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/android123.ttf");   
     TextView tv = (TextView)findViewById(R.id.text);            tv.setTypeface(tf);    //設置TextView的風格
        tv.setText("CWJ Test");  
        tv.setTextSize(12); 
        tv.setTextColor(Color.RED);

34. 獲取和設置ListView的選擇項

獲取當前選中項  int curPos = listView.getFirstVisiblePosition(); 固然是用getItemAtPosition(int nPos)方法也能夠 ,設置當前選擇位置 listView.setSelectedPosition(lastPos);  對於基於AbsListView爲基類的ListView等控件都可以使用這種方法。

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

不少網友可能直接將本身的J2ME項目生硬的移植到Android平臺,其實Google爲咱們提供好了文件大小和時間日期解析類,它位於android.text.format這個包中,它提供了強大的標準化解析方法:   1. IP地址解析類 在android.text.format.Formatter中提供了String formatIpAddress(int addr) 這個方法能夠輕鬆方便的將socket中的int型轉成相似127.0.0.1的IP格式,須要注意的是Linux平臺的字節順序,即小字節序、低字節序little-endian。   2. 文件大小解析類 細心的網友可能還看到了android.text.format.Formatter中的formatFileSize方法,該方法String formatFileSize (Context context, long number) ,第二個參數是long型,通常爲File對象的最後修改時間或建立時間的方法,最終返回相似 12KB、5Bytes的值,20MB的字符串。   3. 日期時間解析類 ,該類位於android.text.format.DateFormat這個package中,該類提供了Java中的三種時間對象,Android123提示你們下面三種方法爲靜態能夠直接調用,以下:   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.   咱們可能看到了第一個參數均爲inFormat這是一個CharSequence接口的String類型,它提供了靈活的時間格式解析字符串描述,Android開發網提示你們注意大小寫要區分,如    April 6, 1970 at 3:23am 例子,那麼inFormat參數的寫法和最終執行的結果以下對照,下面就以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"   對於判斷一個時間是否爲24小時制式能夠經過android.text.format.DateFormat類的static boolean  is24HourFormat(Context context)方法來判斷。

36. Android代碼性能優化技巧

目前來講Android 2.2的JIT性能有了本質的提升,不過對於老版本的程序提升Java執行效率還有不少語言特色來講,今天Android123提到的不是語法糖,而是基礎的問題,對於Java 1.5以後將會有明顯的改進。下面的例子來自SDK:  static class Foo {
        int mSplat;
    }
    Foo[] mArray = ... 
上面的靜態類Foo的執行效果和性能,咱們分三個方法zero、one和two來作對比。
    public void zero() {  //大多數人可能簡單直接這樣寫
        int sum = 0;
        for (int i = 0; i < mArray.length; ++i) {
            sum += mArray .mSplat;
        }
    }
    public void one() { //經過本地對象改進性能
        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的新語法特性能夠大幅改進性能
        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開發注意點 Part One

Android已經的不少細節問題咱們經過平臺開發總結不斷完善這個列表,若是你有相關的內容能夠聯繫android123@163.com .    1、AssetManager - 已知單個文件處理不能大於1MB,因此若是資源很大,建議使用Zip格式壓縮存放。    2、ScrollView中嵌入ListView - 這個做法可能會出現你的ListView僅僅顯示1行半。    3、Android自帶的Zip處理類對文件名編碼沒法識別,也沒有提供顯示的設置方法,在zlib中寫死了。    4、使用一些資源對象記住關閉,好比對於文件流對象最後           FileOutputStream os = xxx;            try {
                //dosomething
            } finally {
                os.close();  //顯示的使用finally關閉文件對象。
            }           對於Cursor而言,在移動位置時首先判斷Cursor是否爲空,最終使用完仍然須要 close方法,若是重用,可使用deactivate方法釋放當前資源,經過requery方法再次查詢。   5、SDK中標記爲 deprecated 字樣的,常規狀況下是有更好的方法能夠替代,短時間內能夠放心使用。這些方法通常高版本的SDK均可以向上兼容,目前還沒有發現Android放棄某些API的支持。   6、Notification的Intent沒法傳遞到目標的Activity,Service和Broardcast沒有測試過,中途須要經過PendingIntent,可能這裏出現了問題。

38. Android上HTTP協議通信狀態獲取

一般狀況下輕量級的Http傳輸Android平臺能夠直接使用Sun Java的HttpURLConnection類方法處理,好比果本身定義一次請求header能夠經過setRequestProperty設置,而咱們須要獲取的Http Web Server狀態能夠經過HttpURLConnection.getResponseCode() 的方法獲取。   固然Http協議返回值常見的有 200 爲成功,400爲請求錯誤,404爲未找到,500爲服務器內部錯誤,403無權查看,302爲重定向等等。   對於Android平臺提供更完善的Apache類有HttpClient 、HttpPost、HttpResponse、HttpGet和HttpEntity,其中對於數據報頭header構造經過HttpEntity,而返回狀態值能夠經過HttpResponse獲取。   有關Android客戶端和Server通信類相關的開發咱們將會在之後文章中作大量實例介紹。

39. Android佈局Java代碼構造法

通常狀況下對於Android程序佈局咱們每每使用XML文件來編寫,這樣能夠提升開發效率,可是考慮到代碼的安全性以及執行效率,能夠經過Java代碼執行建立,雖然Android編譯過的xml是二進制的,可是加載xml解析器的效率對於資源佔用仍是比較大的,通常一個簡單的TextView,好比     <TextView
    android:id="@+id/textControl "
    android:layout_width="100px"
    android:layout_height="wrap_content" />    能夠等價於下面的Java代碼:    LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(100, LayoutParams.WRAP_CONTENT); //寬度爲100px,高爲自適應最小的高度    // setOrientation(VERTICAL); 設置佈局爲垂直    TextView textControl = new TextView(this);//若是從一個XXXLayout.,好比LinearLayout爲View的基類時這裏this應該換成爲建立改類的Context
   textControl.setText("Android開發網歡迎您");
   addView( textControl, textParams );    固然Java處理效率比XML快得多,可是對於一個複雜界面的編寫,可能須要一些套嵌考慮,若是你思惟靈活的話,使用Java代碼來佈局你的Android應用程序是一個更好的方法。

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

對於Android平臺上軟件的性能測試能夠經過如下幾種方法來分析效率瓶頸,目前Google在Android軟件開發過程當中已經引入了多種測試工具包,好比Unit測試工程,調試類,還有模擬器的Dev Tools均可以直接反應執行性能。    1. 在模擬器上的Dev Tools能夠激活屏幕顯示當前的FPS,CPU使用率,能夠幫助咱們測試一些3D圖形界面的性能。    2.  通常涉及到網絡應用的程序,在效率上和網速有不少關係,這裏須要屢次的調試才能實際瞭解。    3. 對於邏輯算法的效率執行,咱們使用Android上最廣泛的,計算執行時間來查看:       long start = System.currentTimeMillis();
      //android開發網提示這裏作實際的處理do something
      long duration = System.currentTimeMillis() - start;       最終duration保存着實際處理該方法須要的毫秒數。這裏相似Win32上的GetTickCount,在Win 32和Symbian上都提供了高精度的性能計數器和低階計時器,這裏在Dalvik VM上的Java層這種方法對於通常的應用足以。    4. GC效率跟蹤,若是你執行的應用比較簡單,能夠在DDMS中查看下Logcat的VM釋放內存狀況,大概模擬下那些地方能夠緩存數據或改進算法的。    5. 線程的使用和同步,Android平臺上給咱們提供了豐富的多任務同步方法,但在深層上並無過多的好比自旋鎖等高級應用,不過對於Service和appWidget而言,他們實際的產品中都應該以多線程的方式處理,以釋放CPU時間,對於線程和堆內存的查看這些均可以在DDMS中看到。    更多的調試和性能測試方法Android123將在之後的內容中出現。

41. Splash Screen開場屏在Android中的實現

不少網友可能發現近期Tencent推出的手機QQ Android版包含了一個開場屏Splash Screen載入效果,一般遊戲或大型軟件打開時可能須要一個釋放解析資源的過程,須要一個前臺的動畫播放和後臺的邏輯處理線程配合,固然對於簡單的軟件也能夠加一個Splash Screen做爲美化。在Android平臺上如何實現呢?   首先建立一個Activirty,在SetContentView時直接經過ImageView建立一個全屏的圖片,Android123提示你們還要考慮好分辨率和當前設備一致,onCreate添加代碼以下:   new Handler().postDelayed(new Runnable(){   // 爲了減小代碼使用匿名Handler建立一個延時的調用
            public void run() {  
                Intent i = new Intent(SplashScreen.this, Main.class);    //經過Intent打開最終真正的主界面Main這個Activity
                SplashScreen.this.startActivity(i);    //啓動Main界面
                SplashScreen.this.finish();    //關閉本身這個開場屏
            }  
        }, 5000);   //5秒,夠用了吧

42. Android的Activity你知多少呢?

看到這個標題不少網友確定回答,我知道Activity是Android上的窗口基類,瞭解Activity的生命週期好比onCreate onStop等,呵呵,按照這樣說Android123還知道Activity的實現實際上是從ApplicationContext,而ApplicationContext是從Context這個抽象類派生而來的,固然咱們看到顯示的是View或者ViewGroup,固然今天說的不是這些東西,而是不少網友來問的Android爲何不設計一個任務管理器,固然從Android 1.5開始ActivityManager類提供了restartPackage能夠關閉一個程序,須要加上<uses-permission android:name="android.permission.RESTART_PACKAGES"/>這個權限,不過咱們注意到,長按Home鍵能夠看到之前程序的運行,同時能夠快速的切換回來。這就是Android獨有的程序生命週期管理機制 Activity歷史棧。   咱們在一個普通的程序主窗口A中打開了一個窗口B,而窗口B打開了窗口C,可是按下Back鍵後結果出乎了預期,是的這就是Activity的history stack的緣由,在數據結構中棧是FIFO的,阻止咱們不肯意看的狀況的發生則能夠在打開新Activity時加上標記FLAG_ACTIVITY_NO_HISTORY,代碼以下:     Intent i= new Intent(this, cwj.class);  
    i.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);   //Android開發網提示你們相關的還有Intent.FLAG_ACTIVITY_CLEAR_TOP,都試試
    startActivity(i);    固然更多的程序Activity控制能夠再androidmanifest.xml中定義。

43. JSONObject在Android上的應用

若是你過去開發過AJAX應用,相信對JSONObject不會陌生吧,做爲基於JavaScript的數據交換格式,能夠直接代替Xml,這裏Android從1.0開始就徹底支持JSONObject。在平時應用中直接引入import org.json.JSONObject;便可方便使用。固然同類的還有SOAP。   在常規使用方便JSONObject對象能夠實現相似Bundle或Parcel能夠封裝數據,代替一個XML的ITEM,但最大的優點是能夠執行一些簡單的方法,好比說getString、has、put、getBoolean、getInt等數據類型的存取操做。Android123提示你們對於常規的項目開發,今天本文不考慮Server端的佈局,在Android平臺上處理這些比較簡單,主要是一些http的請求處理。能夠直接引入import org.apache.http.xxx來實現web server層的數據交換,若是你沒有專業的Server開發技術,能夠經過簡單的Web配合JSON方式快速實現本身的交互式應用。

44. Android高性能文件類MemoryFile

不少網友抱怨Android處理底層I/O性能不是很理想,若是不想使用NDK則能夠經過MemoryFile類實現高性能的文件讀寫操做。MemoryFile顧名思義就是內存文件的意思,若是你過去從事過Win32開發,那麼它的原理就是MapViewOfFile(),固然開發過Linux的網友可能很快就聯想到了mmap(),是的該類就是他們的託管代碼層封裝,位於android.os.MemoryFile這個位置,從Android 1.0開始就被支持。   MemoryFile適用於哪些地方呢?   對於I/O須要頻繁操做的,主要是和外部存儲相關的I/O操做,MemoryFile經過將 NAND或SD卡上的文件,分段映射到內存中進行修改處理,這樣就用高速的RAM代替了ROM或SD卡,性能天然提升很多,對於Android手機而言同時還減小了電量消耗。Android123提示網友該類實現的功能不是不少,直接從Object上繼承,經過JNI的方式直接在C底層執行。 主要的構造方法 MemoryFile(String name, int length) ,這裏第二個參數爲文件大小,須要說明的是Android的MemoryFile和傳統的mmap還有一點點區別,畢竟是手機,它內部的內存管理方式ashmem會從內核中回收資源。畢竟目前部分低端機型的RAM也比較吃緊。  synchronized boolean  allowPurging(boolean allowPurging)  //容許ashmem清理內存,線程安全同步的方式。
void  close() //關閉,由於在Linux內部mmap佔用一個句柄,不用時必定要釋放了
InputStream  getInputStream()  返回讀取的內容用Java層的InputStream保存
OutputStream  getOutputStream()  把一個OutputSream寫入到MemoryFile中
boolean  isPurgingAllowed() //判斷是否容許清理
int  length()  //返回內存映射文件大小 下面就是咱們熟悉的,讀寫細節,主要是對字符數組的操做,這裏你們要計算好每一個文件類型的佔用,同時考慮到效率對於本身分配的大小考慮粒度對齊。
int  readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
void  writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)     具體的實際應用,Android開發網將在下次和你們講到。

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

對於字符串處理Android爲咱們提供了一個簡單實用的TextUtils類,若是處理比較簡單的內容不用去思考正則表達式不妨試試這個在android.text.TextUtils的類,主要的功能以下:   是否爲空字符 static boolean  isEmpty(CharSequence str)  拆分字符串  public static String[] split (String text, String expression) ,Android開發網提示你們仔細看例子以下 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)   肯定大小寫是否有效在當前位置的文本TextUtils.getCapsMode(CharSequence cs, int off, int reqModes)   使用HTML編碼這個字符串  static String  TextUtils.htmlEncode(String s)   

46. InputSream輸入流轉String字符串,Android開發工具類

在Android平臺上使用Java層處理I/O時主要使用流,這裏Android開發網給你們一個方便的類,能夠處理InputStream輸入流轉爲String字符串,在效率上,咱們使用了字符串拼接StringBuilder類減小內存碎片以及BefferedReader類實現一個緩存。     private String Stream2String(InputStream is) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is), 16*1024); //強制緩存大小爲16KB,通常Java類默認爲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開發必讀

有時候咱們在一個Android程序中可能會複用佈局文件,這時能夠在一個xml文件中複用過去的佈局文件,可是和常規的使用不一樣的是,須要加上相似包含頭文件同樣的include關鍵字,好比下面咱們須要包含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控件開發之ToggleButton原理

在Android平臺上比較有特點的就是ToggleButton控件,雖然它的功能和CheckBox有些相似,可是他們的用處仍是有必定的區別好比ToggleButton本來有圖片裝飾,經過ToggleButton能夠很清楚的顯示某些狀態。它們均從Button爲基類的CompoundButton中實現,其真假事件從Checkable來實現。   public abstract class  CompoundButton extends  Button implements Checkable {
    private boolean mChecked; //狀態是否選中
    private int mButtonResource;
    private boolean mBroadcasting;
    private Drawable mButtonDrawable; //按鈕的圖標
    private OnCheckedChangeListener mOnCheckedChangeListener; //選中狀態改變監聽
    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(); //更新當前狀態的按鈕圖標             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提示狀態改變時須要更換按鈕的圖標
        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的實現相對繁瑣了些,主要是考慮狀態是否已經選中等狀況的消息通知,Android開發網提醒你們而ToggleButton相對CompuundButton增長的給用戶而言主要是開關的文字顯示。 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實例代碼演示Android異步任務

上次咱們講到了Android提供了一個較線程更簡單的處理多任務的方法AsyncTask異步任務類,相對於線程來講AsyncTask對於簡單的任務處理更安全,其內部的實現方法使用了Android的Handler機制,對於常見的文件下載可使用AsyncTask類來處理,在Browser瀏覽器中就是用了該類下載Web服務器URL的Favicon圖標。   首先Android123以簡單的下載例子演示該類的大體結構,以下 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");
    }
}   最終咱們執行 DownloadFilesTask().execute(url1, url2, url3); 便可。   在Android瀏覽器中下載Favicon圖標的實現以下: 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) { //構造方法
        mActivity = activity;
        mContentResolver = cr;
        mCursor = c;
        mOriginalUrl = view.getOriginalUrl();
        mUrl = view.getUrl();
        mUserAgent = view.getSettings().getUserAgentString();
    }     public DownloadTouchIcon(ContentResolver cr, Cursor c, String url) { //實現本類的構造
        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(); //將圖標保存到InputStream中,由於是二進制內容
                    if (content != null) {
                        Bitmap icon = BitmapFactory.decodeStream( //從流中取出Bitmap,這裏使用了BitmapFactory類的靜態方法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;
        }     最終圖標要保存到瀏覽器的內部數據庫中,系統程序均保存爲SQLite格式,Browser也不例外,由於圖片是二進制的因此使用字節數組存儲數據庫的BLOB類型         final ByteArrayOutputStream os = new ByteArrayOutputStream();
        icon.compress(Bitmap.CompressFormat.PNG, 100, os); //將Bitmap壓縮成PNG編碼,質量爲100%存儲
        ContentValues values = new ContentValues(); //構造SQLite的Content對象,這裏也可使用raw sql代替
        values.put(Browser.BookmarkColumns.TOUCH_ICON,os.toByteArray()); //寫入數據庫的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開發網經過兩個AsyncTask類演示了多種類型的任務構造,這裏你們注意返回類型,本節演示了Android平臺上Content Provider、AsyncTask、Bitmap、HTTP以及Stream的相關操做,你們如何想很快提升開發水平其實只要理解Google如何去實現Android系統常規構架就能夠輕鬆入門谷歌移動平臺。

50. Android自定義View實例AnalogClock源碼

針對Android底層View的直接構造不少網友沒有實戰經驗,本次Android開發網結合目前平臺開源代碼一塊兒經過AnalogClock類來理解View的直接繼承。AnalogClock就是Home Screen上的那個帶有兩根指針的錶盤類。它的實現咱們直接從開源代碼能夠了解到:   public class AnalogClock extends View {
    private Time mCalendar;     private Drawable mHourHand; //時針
    private Drawable mMinuteHand; //分針
    private Drawable mDial; //錶盤背景     private int mDialWidth; //錶盤寬度
    private int mDialHeight; //錶盤高度     private boolean mAttached; //附着狀態     private final Handler mHandler = new Handler(); //定一個Handler類實現更新時間
    private float mMinutes;
    private float mHour;
    private boolean mChanged; //時間是否改變     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); //加載時針圖片資源
        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(); //獲取當前系統時間         mDialWidth = mDial.getIntrinsicWidth(); //獲取錶盤圖片的寬度
        mDialHeight = mDial.getIntrinsicHeight(); //高度,同上
    }     @Override
    protected void  onAttachedToWindow() {
        super.onAttachedToWindow();         if (!mAttached) {
            mAttached = true;
            IntentFilter filter = new IntentFilter(); //註冊一個消息過濾器,獲取時間改變、時區改變的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實例直接屏幕 @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); //計算時針旋轉的角度,android123提示就是那個時針圖片的旋轉角度,直接反應的就是錶盤上那個針的時間
        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); //同理,分針旋轉的角度         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() {  //獲取時間改變,計算當前的時分秒
        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() { //監聽獲取時間改變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(); //獲取新的時間
            invalidate(); //刷新屏幕,強制類調用onDraw方法實現分針時針的走動
        }
    };    看了本例根據,Android開發很簡單吧,感興趣的網友能夠爲本程序加入一個秒針,不過Android123提醒網友的是可能對於電池,以及系統運行效率產生必定的影響,不過做爲練習你們能夠試一試。

51. ArrayList LinkedList Set HashMap介紹

  在Android開發中咱們常常須要對數據進行分類和操做,對於輕量級的數據存儲咱們可能不須要動用SQLite或效率以及類庫不完善的XML,因爲SharedPreferences不具有數據枚舉方法,若是僅僅是一個String或Int數組能夠經過一個標記分割設計外,咱們仍是主要來看看Android或者說Java提供的基礎數據類型輔助類ArrayList LinkedList Set HashMap的介紹,若是你熟悉C++的STL或Boost庫能夠略過本文。    在Java中提供了Collection和Map接口。其中List和Set繼承了Collection接口;同時用Vector、ArrayList、LinkedList三個類實現List接口,HashSet、TreeSet實現Set接口。直接有HashTable、HashMap、TreeMap實現Map接口。     Vector基於Array的List,性能也就不可能超越Array,而且Vector是「sychronized」的,這個也是Vector和ArrayList的惟一的區別。     ArrayList:同Vector同樣是一個基於Array的,可是不一樣的是ArrayList不是同步的。因此在性能上要比Vector優越一些。Android123提示你們適用於順序性的查找     LinkedList:不一樣於前面兩種List,它不是基於Array的,做爲鏈表數據結構方式,因此不受Array性能的限制。當對LinkedList作添加,刪除動做的時候只要更改nextNode的相關信息就能夠實現了因此它適合於進行頻繁進行插入和刪除操做。這就是LinkedList的優點,固然對於元素的位置獲取等方面就遜色不少。     List:         1. 全部的List中只能容納單個不一樣類型的對象組成的表,而不是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都實現了Collection接口,可是他們的實現方式卻大不同。List基本上都是以Array爲基礎。可是Set則是在HashMap的基礎上來實現的,這個就是Set和List的根本區別。      HashSet:HashSet的存儲方式是把HashMap中的Key做爲Set的對應存儲項,HashMap的key是不能有重複的。HashSet能快速定位一個元素,可是放到HashSet中的對象須要實現hashCode()方法0。     TreeSet:將放入其中的元素按序存放,這就要求你放入其中的對象是可排序的。TreeSet不一樣於HashSet的根本是TreeSet是有序的。它是經過SortedMap來實現的。     Set總結: 1. Set實現的基礎是Map(HashMap); 2. Set中的元素是不能重複的,若是使用add(Object obj)方法添加已經存在的對象,則會覆蓋前面的對象,不能包含兩個元素e一、e2(e1.equals(e2))。     Map是一種把鍵對象和值對象進行關聯的容器,Map有兩種比較經常使用的實現: HashTable、HashMap和TreeMap。     HashMap也用到了哈希碼的算法,以便快速查找一個鍵,     TreeMap則是對鍵按序存放,所以它有一些擴展的方法,好比firstKey(),lastKey()等。     HashMap和Hashtable的區別。 HashMap容許空(null)鍵(key)或值(value),因爲非線程安全,效率上可能高於Hashtable。 Hashtable不容許空(null)鍵(key)或值(value)。    有關更多實用的Android開發技巧咱們將在後面的文章中着重介紹。

52. ConditionVariable Android線程同步

ConditionVariable類位於android.os.ConditionVariable,它能夠幫助Android線程同步。在SDK上的介紹ConditionVariable不一樣於標準Java位於java.lang.Object wait() 和 notify() ,這個類能夠等待本身,這就意味着 open(), close() 和 block() 可能會假死 ,若是使用ConditionVariable類的open()在調用 block() 以前, block() 將不會阻塞,相反將會返回當即。    該類一共有4個方法    boolean  block(long timeout)
  阻止當前線程知道條件是open,或直到超時,這裏參數long timeout爲超時設置,Android123提示你們若是大家從事過Win32開發,這個方法相似DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds); 函數。   void  block()
  阻止當前線程知道條件 open ,是上面的無超時等待重載版本。   void  close()
重置條件爲 close狀態。 void  open()
Open條件,釋放全部線程的阻塞.   ConditionVariable在建立時還有一種構造方法是 public ConditionVariable (boolean state) ,若是爲true,默認時爲opened,若是爲false則是closed. ,默認public ConditionVariable () 爲close().

53.Android開發之Eclipse調試技巧

使用Google提供的ADT插件能夠在Eclipse上很輕鬆的調試Android程序,咱們切換到DDMS標籤,選擇「Devices」標籤,咱們能夠看到會出現相似下面的Debug Process(調試進程)、Update Threads(更新線程)、Update Heap(更新堆)、Cause GC(引發垃圾回收)、Stop Process(中止進程)、Screen Capture(屏幕截圖)、Reset adb(重啓Android Debug Bridge)   這裏咱們就能夠很好的觀察Android程序運行時的各類狀態,好比進程信息、線程分析、堆內存的佔用,結束一個進程,固然這些操做都是在DDMS框架下進行的,日程開發的程序是沒法執行調用的。若是遇到adb調試橋運行不穩定時能夠選擇reset adb來從新啓動adb.exe進程,整個界面如圖: 不少網友對於一些常的Android規程序性能測試、文件管理或屏幕截圖均使用Eclipse中的DDMS插件來查看,其實經過SDK中提供的Dalvik Debug Monitor能夠很好的調試Android程序,這裏能夠更直觀的現實設備的各類信息,除了Logcat、VM Heap堆查看、Thread線程狀態外,在菜單的Device中能夠找到Screen capture來截圖、File Explorer進行文件同步操做,使用Show process status能夠顯示設備當前的進程狀態,以及 快速的過濾Logcat信息,能夠分析無線狀態radio state、程序狀態app state等等。這裏支持模擬器和真機的顯示,該工具能夠再android-sdk-windows-1.5_r1\tools\ddms.bat找到,目前咱們測試環境爲Windows平臺,下次講述下CentOS中的操做,如圖: Android性能與調試很重要  用於手持的移動設備,Android軟件性能上須要多加考慮。首先Java VM在資源佔用上開銷是很大的,不少垃圾GC處理機制直接影響到內存釋放和整個平臺運行的流暢度。   1.節省電量   手機軟件必須考慮的問題是省電,若是須要大型處理儘可能由服務器處理,直接把結果返回到手持設備上。多線程也是一種奢侈的使用,可是I/O存儲必需這樣才能保證流暢度,線程的阻塞將會下降用戶體驗,可是線程間切換調度的開銷一直是重點。Android在DDMS中加入了Thread查看。   2.內存佔用   在Eclipse+ADT插件的開發方式中,咱們在DDMS中能夠看到Heap堆內存的顯示,Android開發網提示的是Java內存分配方式的問題,儘可能產生少的對象,好比字符串操做若是連加比較多,可使用StringBuilder代替String類。在遊戲開發中常常用到的圖片能夠經過切片的方式從一個大的png圖片上截取,或者放在gif文件做爲逐幀保存,這樣能夠共用文件頭減少體積。   3.調試工具   Android調試工具主要是模擬器中的Dev Tools和DDMS中的Logcat查看。固然模擬器自帶的Dev Tools在功能上仍是很詳細的,能夠顯示CPU佔用率,內存使用量,在單元測試時須要多加分析。 Android開發工具Dev Tools介紹ndroid提供了不少開發調試工具除了ADB、TraceView、Logcat外,今天這個名爲Dev Tools的Android開發調試工具隱藏在Android模擬器中,爲咱們提供了強大的調試支持。咱們在功能表中找到Dev Tools,運行後能夠看到有不少條目,好比Development Settings,用來開發設置,進入後咱們看到了好比Show CPU Usage這樣的實用功能顯示CPU佔用率,幫助Android開發人員分析當前軟件性能狀況,今天就分析下Development Settings中的選項: Wait for debugger 等待調試器Enable ADB 啓用ADB(android調試橋)Show running processs (顯示運行中的進程)Show screen updates (顯示屏幕更新)   下面是一些常規的調試選項,Android開發網友情提示開啓這些選項後可能會影響運行效率,這些探測選項也是CPU敏感的。 Immediately destroy activites (當即銷燬activities)Show CPU usage (顯示CPU佔用率)Show background (顯示北京)Show Sleep state on LED (在休眠狀態下LED開啓)Keep screen on while plugged in (保持屏幕開啓當插入後)Show GTalk service connection status (顯示GTalk服務鏈接狀態) 

相關文章
相關標籤/搜索