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
Android 程序員必須知道的 53 個知識點
2. px 像素如何轉爲dip 設備獨立像素:::
最近有網友問如何將 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 的知識java
3. Android 中動態改變 ImageView 大小大小
不少網友可能發如今 layout.xml 文件中定義了 ImageView 的絕對大小後,沒法動態修改之後的大小顯示,其實 Android
平臺在設計 UI 控件時考慮到這個問題,爲了適應不一樣的 Drawable 能夠經過在 xml 的相關 ImageView 中加入
android:scaleType="fitXY" 這行便可,但由於使用了縮放可能會形成當前 UI 有所變形。
使用的前提是限制 ImageView 所在的層,可使用一個內嵌的方法限制顯示。android
4. 如何判斷 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 程序都使用了這個方法,來判斷是否繼續,同時在一些
網絡超時的時候也能夠檢查下網絡鏈接是否存在,以避免浪費手機上的電力資源。git
5. Drawable、、 Bitmap、、 Canvas 和和 Paint 的關係
不少網友剛剛開始學習 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
}
}web
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
瞭解下這種技術,看看在立刻要作的項目中是否用獲得sql
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);
}
//正則表達式處理,有關定義見下面的 SimpleWikiHelper 類
Pattern pattern = Pattern.compile(SimpleWikiHelper. WORD_OF_DAY_REGEX);
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
MenuMenuMenu::::
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
TabTabTab::::
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;