Android開發網上的一些重要知識點【1】

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。html

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

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

這裏能夠再補充一下dip,sip的知識git

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

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

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

若是擬開發一個網絡應用的程序,首先考慮是否接入網絡,在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的關係 json

不少網友剛剛開始學習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重複執行

部分網友會發現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非阻塞包(一)

對於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內部定義解析

昨天咱們講到的有關在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工具中的參數說明,如圖: 

android monkey debug

  咱們要測試的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完整篇講到。

相關文章
相關標籤/搜索