1. android單實例運行方法
我們都知道Android平臺沒有任務(wù)管理器,而內(nèi)部App維護 者一個Activity history stack來實現(xiàn)窗口顯示和銷毀,對于常規(guī)從快捷方式運行來看都是startActivity可能會使用FLAG_ACTIVITY_NEW_TASK標(biāo) 記來打開一個新窗口,比如Launcher,所以考慮單任務(wù)的實現(xiàn)方法比較簡單,首先Android123糾正下大家一種錯誤的方法就是直接在 androidmanifest.xml的application節(jié)點中加入android:launchMode="singleInstance"這 句,其實這樣將不會起到任何作用,Apps內(nèi)部維護的歷史棧作用于Activity,我們必須在activity節(jié)點中加入 android:launchMode="singleInstance" 這句才能保證單實例,當(dāng)然一般均加在主程序啟動窗口的Activity。
2. px像素如何轉(zhuǎn)為dip設(shè)備獨立像素
最 近有網(wǎng)友問如何將px像素轉(zhuǎn)為dip獨立設(shè)備像素,由于Android的設(shè)備分辨率眾多,目前主流的為wvga,而很多老的設(shè)備為hvga甚至低端的 qvga,對于兼容性來說使用dip無非是比較方便的,由于他和分辨率無關(guān)和屏幕的密度大小有關(guān),所以推薦使用。? px= (int) (dip*density+0.5f) //這里android開發(fā)網(wǎng)提示大家很多網(wǎng)友獲取density(密度)的方法存在問題,從資源中獲取的是靜態(tài)定義的,一般為1.0對于HVGA是正好 的,而對于wvga這樣的應(yīng)該從WindowsManager中獲取,WVGA為1.5
這里可以再補充一下dip,sip的知識
3. Android中動態(tài)改變ImageView大小
很 多網(wǎng)友可能發(fā)現(xiàn)在layout.xml文件中定義了ImageView的絕對大小后,無法動態(tài)修改以后的大小顯示,其實Android平臺在設(shè)計UI控件 時考慮到這個問題,為了適應(yīng)不同的Drawable可以通過在xml的相關(guān)ImageView中加入android:scaleType="fitXY" 這行即可,但因為使用了縮放可能會造成當(dāng)前UI有所變形。使用的前提是限制ImageView所在的層,可以使用一個內(nèi)嵌的方法限制顯示。
4. 如何判斷Android手機當(dāng)前是否聯(lián)網(wǎng)?
如 果擬開發(fā)一個網(wǎng)絡(luò)應(yīng)用的程序,首先考慮是否接入網(wǎng)絡(luò),在Android手機中判斷是否聯(lián)網(wǎng)可以通過 ConnectivityManager 類的isAvailable()方法判斷,首先獲取網(wǎng)絡(luò)通訊類的實例 ConnectivityManager cwjManager=(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); ,使用cwjManager.getActiveNetworkInfo().isAvailable(); 來返回是否有效,如果為True則表示當(dāng)前Android手機已經(jīng)聯(lián)網(wǎng),可能是WiFi或GPRS、HSDPA等等,具體的可以通過 ConnectivityManager 類的getActiveNetworkInfo() 方法判斷詳細的接入方式,需要注意的是有關(guān)調(diào)用需要加入<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> 這個權(quán)限,android開發(fā)網(wǎng)提醒大家在真機上Market和Browser程序都使用了這個方法,來判斷是否繼續(xù),同時在一些網(wǎng)絡(luò)超時的時候也可以檢 查下網(wǎng)絡(luò)連接是否存在,以免浪費手機上的電力資源。
5. Drawable、Bitmap、Canvas和Paint的關(guān)系
很 多網(wǎng)友剛剛開始學(xué)習(xí)Android平臺,對于Drawable、Bitmap、Canvas和Paint它們之間的概念不是很清楚,其實它們除了 Drawable外早在Sun的J2ME中就已經(jīng)出現(xiàn)了,但是在Android平臺中,Bitmap、Canvas相關(guān)的都有所變化。
? 首先讓我們理解下Android平臺中的顯示類是View,但是還提供了底層圖形類android.graphics,今天所說的這些均為graphics底層圖形接口。
? Bitmap - 稱作位圖,一般位圖的文件格式后綴為bmp,當(dāng)然編碼器也有很多如RGB565、RGB888。作為一種逐像素的顯示對象執(zhí)行效率高,但是缺點也很明顯存儲效率低。我們理解為一種存儲對象比較好。
? Drawable - 作為Android平下通用的圖形對象,它可以裝載常用格式的圖像,比如GIF、PNG、JPG,當(dāng)然也支持BMP,當(dāng)然還提供一些高級的可視化對象,比如漸變、圖形等。
? Canvas - 名為畫布,我們可以看作是一種處理過程,使用各種方法來管理Bitmap、GL或者Path路徑,同時它可以配合Matrix矩陣類給圖像做旋轉(zhuǎn)、縮放等操作,同時Canvas類還提供了裁剪、選取等操作。
?? Paint - 我們可以把它看做一個畫圖工具,比如畫筆、畫刷。他管理了每個畫圖工具的字體、顏色、樣式。
? 如果涉及一些Android游戲開發(fā)、顯示特效可以通過這些底層圖形類來高效實現(xiàn)自己的應(yīng)用。
6. Activity切換導(dǎo)致的onCreate重復(fù)執(zhí)行
部 分網(wǎng)友會發(fā)現(xiàn)Activity在切換到后臺或布局從橫屏LANDSCAPE切換到PORTRAIT,會重新切換Activity會觸發(fā)一次 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問題
很 多網(wǎng)友對Android提供的ImageButton有個疑問,當(dāng)顯示Drawable圖片時就不會再顯示文字了,其實解決的方法有兩種,第一種就是圖片 中就寫入文字,但是這樣解決會增加程序體積,同時硬編碼方式會影響多國語言的發(fā)布。第二種解決方法很簡單,通過分析可以看到ImageButton的 layout,我們可以直接直接繼承,添加一個TextView,對齊方式為右側(cè)即可實現(xiàn)ImageButton支持文字右側(cè)顯示。
8. Android代碼優(yōu)化技術(shù)
1.Java內(nèi)存控制
? 對于字符串操作而言如果需要連加這樣的操作建議使用StringBuilder,經(jīng)過調(diào)試不難發(fā)現(xiàn)如果你的字符串每次連加,使用String需要的內(nèi)存開 銷會遠大于StringBuilder,然后Android手機常規(guī)的運行內(nèi)存大約在128MB左右,對于運行多任務(wù)就需要考慮了, Android開發(fā)網(wǎng) 提示因為Java有GC不需要手動釋放那么分配的時候就要格外的小心,頻繁的GC操作仍然是很影響性能的,在調(diào)試時我們可以通過logcat查看內(nèi)存釋放情況。
? 2.循環(huán)使用
? 平時在訪問一個屬性的時候效率遠比一個固定變量低,如果你的循環(huán)估計次數(shù)常常大于5,假設(shè)xxx.GetLength()方法的值一般大于5,推薦這樣寫,比如
? for(int i=0;i<xxx.GetLength();i++)
? 這里xxx.GetLength在每次循環(huán)都要調(diào)用,必然會影響程序效率,在游戲開發(fā)中顯得更為明顯,改進的方法應(yīng)該為
? int j=xxx.GetLength()
?? for(int i=0;i<j;i++)
? 3.圖片的優(yōu)化
? 在Android平臺中2維圖像處理庫BitmapFactory做的比較智能,為了減少文件體積和效率,常常不用很多資源文件,而把很多小圖片放在一個 圖片中,有切片的方式來完成,在J2ME中我們這樣是為了將少文件頭而解決MIDP這些設(shè)備的問題,而Android中雖然機型硬件配置都比較高,有關(guān) Android G1硬件配置可以參考 G1手機參數(shù)以及評測 ,但是當(dāng)資源多時這樣的運行效率還是令人滿意的,至少Dalvik優(yōu)化的還不是很夠。
9. Android開發(fā)進階之NIO非阻塞包(一)
對 于Android的網(wǎng)絡(luò)通訊性能的提高,我們可以使用Java上高性能的NIO (New I/O) 技術(shù)進行處理,NIO是從JDK 1.4開始引入的,NIO的N我們可以理解為Noblocking即非阻塞的意思,相對應(yīng)傳統(tǒng)的I/O,比如Socket的accpet()、 read()這些方法而言都是阻塞的。
? NIO主要使用了Channel和Selector來實現(xiàn),Java的Selector類似Winsock的Select模式,是一種基于事件驅(qū)動的,整 個處理方法使用了輪訓(xùn)的狀態(tài)機,如果你過去開發(fā)過Symbian應(yīng)用的話這種方式有點像活動對象,好處就是單線程更節(jié)省系統(tǒng)開銷,NIO的好處可以很好的 處理并發(fā),對于Android網(wǎng)游開發(fā)來說比較關(guān)鍵,對于多點Socket連接而言使用NIO可以大大減少線程使用,降低了線程死鎖的概率,畢竟手機游戲 有UI線程,音樂線程,網(wǎng)絡(luò)線程,管理的難度可想而知,同時I/O這種低速設(shè)備將影響游戲的體驗。
? NIO作為一種中高負載的I/O模型,相對于傳統(tǒng)的BIO (Blocking I/O)來說有了很大的提高,處理并發(fā)不用太多的線程,省去了創(chuàng)建銷毀的時間,如果線程過多調(diào)度是問題,同時很多線程可能處于空閑狀態(tài),大大浪費了CPU 時間,同時過多的線程可能是性能大幅下降,一般的解決方案中可能使用線程池來管理調(diào)度但這種方法治標(biāo)不治本。使用NIO可以使并發(fā)的效率大大提高。當(dāng)然 NIO和JDK 7中的AIO還存在一些區(qū)別,AIO作為一種更新的當(dāng)然這是對于Java而言,如果你開發(fā)過Winsock服務(wù)器,那么IOCP這樣的I/O完成端口可以 解決更高級的負載,當(dāng)然了今天Android123主要給大家講解下為什么使用NIO在Android中有哪些用處。
?? NIO我們分為幾個類型分別描述,作為Java的特性之一,我們需要了解一些新的概念,比如ByteBuffer 類,Channel,SocketChannel,ServerSocketChannel,Selector和SelectionKey。有關(guān)具體的使 用,Android開發(fā)網(wǎng)將在明天詳細講解。網(wǎng)友可以在Android SDK文檔中看下java.nio和java.nio.channels兩個包了解。 http://www.android123.com.cn/androidkaifa/695.html
了解下這種技術(shù),看看在馬上要做的項目中是否用得到
10. Android Theme和Styles內(nèi)部定義解析
昨天我們講到的有關(guān)在AndroidManifest.xml中定義Activity的theme方法來實現(xiàn)無標(biāo)題的方法,在 使用xml讓你的Activity無標(biāo)題方法 一文中講到的,很多網(wǎng)友不明白為什么這樣做,其實在Android123以前的文章中多次提到了styles樣式定義方法,今天Android開發(fā)網(wǎng)再次 把一些網(wǎng)友回顧了解下android樣式的內(nèi)部定義。在一個工程的res/values/theme.xml中我們可以方便的定義自己的風(fēng)格主題,比如下 面的cwjTheme中我們使用了基于android內(nèi)部的白色調(diào)的背景Theme.Light,設(shè)置windowsNoTitle為true代表沒有標(biāo) 題,背景顏色我們使用了android內(nèi)部定義的透明,同時設(shè)置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>?有關(guān)ListView控件我們自定義的風(fēng)格就是修改下系統(tǒng)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可以設(shè)置更多,比如讓cwjListView的字體顏色就加入textAppearance屬性,比如 <item name="textAppearance">@android:style/TextAppearance</item> 等等。
11.Android JSON解析示例代碼
來 自Google官方的有關(guān)Android平臺的JSON解析示例,如果遠程服務(wù)器使用了json而不是xml的數(shù)據(jù)提供,在Android平臺上已經(jīng)內(nèi)置 的org.json包可以很方便的實現(xiàn)手機客戶端的解析處理。下面Android123一起分析下這個例子,幫助Android開發(fā)者需要有關(guān) HTTP通訊、正則表達式、JSON解析、appWidget開發(fā)的一些知識。
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中開了個服務(wù)
??? }??? 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); //正則表達式處理,有關(guān)定義見下面的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)); //這里是打開相應(yīng)的網(wǎng)頁,所以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;
??????? }
??? }
}? 有關(guān)網(wǎng)絡(luò)通訊的實體類,以及一些常量定義如下:
? 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); //設(shè)置客戶端標(biāo)識??????? 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返回的數(shù)據(jù)流??????????? ByteArrayOutputStream content = new ByteArrayOutputStream();
??????????? int readBytes = 0;
??????????? while ((readBytes = inputStream.read(sBuffer)) != -1) {
??????????????? content.write(sBuffer, 0, readBytes); //轉(zhuǎn)化為字節(jié)數(shù)組流
??????????? }??????????? return new String(content.toByteArray()); //從字節(jié)數(shù)組構(gòu)建String
??????? } catch (IOException e) {
??????????? throw new ApiException("Problem communicating with API", e);
??????? }
??? }
}有關(guān)整個每日維基的widget例子比較簡單,主要是幫助大家積累常用代碼,了解Android平臺 JSON的處理方式,畢竟很多Server還是Java的。
12.Android中使用定時器TimerTask類介紹
在 Android平臺中需要反復(fù)按周期執(zhí)行方法可以使用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秒執(zhí)行一次定時器中的方法,比如本文為調(diào)用log.v打印輸出。? 如果想取消可以調(diào)用下面方法,取消定時器的執(zhí)行
?? while(!mTimerTask.cancel());
????? mTimer.cancel();? 最后Android123提示大家,如果處理的東西比較耗時還是開個線程比較好,Timer還是會阻塞主線程的執(zhí)行,更像是一種消息的執(zhí)行方式。當(dāng)然比Handler的postDelay等方法更適合處理計劃任務(wù)。
13.Android應(yīng)用Icon大小在不同分辨率下定義
對 于Android平臺來說,不同分辨率下Icon的大小設(shè)計有著不同的要求,對于目前主流的HDPI即WVGA級別來說,通常hdpi的應(yīng)用icon大小 為72x72,而標(biāo)準(zhǔn)的mdpi即hvga為48x48,對于目前HTC和Motorola推出的一些QVGA的使用了ldpi,圖標(biāo)為32x32,常見 的Android圖標(biāo)大小設(shè)計規(guī)范如下表所示:
Launcher
36 x 36 px
48 x 48 px
72 x 72 pxMenu
36 x 36 px
48 x 48 px
72 x 72 pxStatus Bar
24 x 24 px
32 x 32 px
48 x 48 pxTab
24 x 24 px
32 x 32 px
48 x 48 pxDialog
24 x 24 px
32 x 32 px
48 x 48 pxList View
24 x 24 px
32 x 32 px
48 x 48 px? 對于android界面設(shè)計的安全色,如下表
而對于系統(tǒng)自帶默認程序的圖標(biāo),下面為png的透明格式,直接鼠標(biāo)右鍵另存為即可
看看sdk文檔上的關(guān)于界面圖標(biāo)的詳細說明。
14.Android控件美化Shape你會用嗎?
如果你對Android系統(tǒng)自帶的UI控件感覺不夠滿意,可以嘗試下自定義控件,我們就以Button為例,很早以前Android123就寫到過 Android Button按鈕控件美化方法 里面提到了xml的selector構(gòu)造。當(dāng)然除了使用drawable這樣的圖片外今天Android開發(fā)網(wǎng)談下自定義圖形shape的方法,對于Button控件Android上支持以下幾種屬性shape、gradient、stroke、corners等。
? 我們就以目前系統(tǒng)的Button的selector為例說下:
????????? <shape>
??????????? <gradient
??????????????? android:startColor="#ff8c00"
??????????????? android:endColor="#FFFFFF"
??????????????? android:angle="270" />
??????????? <stroke
??????????????? android:width="2dp"
??????????????? android:color="#dcdcdc" />
??????????? <corners
??????????????? android:radius="2dp" />
??????????? <padding
??????????????? android:left="10dp"
??????????????? android:top="10dp"
??????????????? android:right="10dp"
??????????????? android:bottom="10dp" />
??????? </shape>??? 對于上面,這條shape的定義,分別為漸變,在gradient中startColor屬性為開始的顏色,endColor為漸變結(jié)束的顏色,下面的 angle是角度。接下來是stroke可以理解為邊緣,corners為拐角這里radius屬性為半徑,最后是相對位置屬性padding。
對于一個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的區(qū)別主要是體現(xiàn)在state_pressed按下或state_focused獲得焦點時,當(dāng)當(dāng)來判斷顯示什么類型,而沒有state_xxx屬性的item可以看作是常規(guī)狀態(tài)下。
15. Android開發(fā)者應(yīng)該保持以下特質(zhì)
Android123推薦新手應(yīng)該遵循
? 1. 深讀SDK文檔
? 2. 深讀SDK的APIDemo和Samples
? 3. 掌握GIT開源代碼
? 4. 多了解Android開源項目,學(xué)習(xí)別人的手法寫程序。
16. Android數(shù)組排序常見方法
? Android的數(shù)組排序方式基本上使用了Sun原生的Java API實現(xiàn),常用的有Comparator接口實現(xiàn)compare方法和Comparable接口的compareTo方法,我們對于一個數(shù)組列表比如 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,則執(zhí)行排序可以調(diào)用方法
Collections.sort(mList, cwjComparator);
17.Android控件TextProgressBar進度條上顯文字
Android 系統(tǒng)的進度條控件默認的設(shè)計的不是很周全,比如沒有包含文字的顯示,那么如何在Android進度條控件上顯示文字呢? 來自Google內(nèi)部的代碼來了解下,主要使用的addView這樣的方法通過覆蓋一層Chronometer秒表控件來實現(xiàn),整個代碼如下
?? public class TextProgressBar extends RelativeLayout implements OnChronometerTickListener {
??? public static final String TAG = "TextProgressBar";
??? static final int CHRONOMETER_ID = android.R.id.text1;
??? static final int PROGRESSBAR_ID = android.R.id.progress;
??? Chronometer mChronometer = null;
??? ProgressBar mProgressBar = null;
??? long mDurationBase = -1;
??? int mDuration = -1;??? boolean mChronometerFollow = false;
??? int mChronometerGravity = Gravity.NO_GRAVITY;
??? public TextProgressBar(Context context, AttributeSet attrs, int defStyle) {
??????? super(context, attrs, defStyle);
??? }??? public TextProgressBar(Context context, AttributeSet attrs) {
??????? super(context, attrs);
??? }??? public TextProgressBar(Context context) {
??????? super(context);
??? }??? //Android開發(fā)網(wǎng)提示關(guān)鍵部分在這里
??? @Override
??? public void addView(View child, int index, ViewGroup.LayoutParams params) {
??????? super.addView(child, index, params);
??????? int childId = child.getId();
??????? if (childId == CHRONOMETER_ID && child instanceof Chronometer) {
??????????? mChronometer = (Chronometer) child;
??????????? mChronometer.setOnChronometerTickListener(this);
??????????? // Check if Chronometer should move with with ProgressBar
??????????? mChronometerFollow = (params.width == ViewGroup.LayoutParams.WRAP_CONTENT);
??????????? mChronometerGravity = (mChronometer.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);
??????? } else if (childId == PROGRESSBAR_ID && child instanceof ProgressBar) {
??????????? mProgressBar = (ProgressBar) child;
??????? }
??? }??? @android.view.RemotableViewMethod
??? public void setDurationBase(long durationBase) {
??????? mDurationBase = durationBase;
??????? if (mProgressBar == null || mChronometer == null) {
??????????? throw new RuntimeException("Expecting child ProgressBar with id " +
??????????????????? "'android.R.id.progress' and Chronometer id 'android.R.id.text1'");
??????? }
??????? // Update the ProgressBar maximum relative to Chronometer base
??????? mDuration = (int) (durationBase - mChronometer.getBase());
??????? if (mDuration <= 0) {
??????????? mDuration = 1;
??????? }
??????? mProgressBar.setMax(mDuration);
??? }
??? public void onChronometerTick(Chronometer chronometer) {
??????? if (mProgressBar == null) {
??????????? throw new RuntimeException(
??????????????? "Expecting child ProgressBar with id 'android.R.id.progress'");
??????? }
??????? // Stop Chronometer if we're past duration
??????? long now = SystemClock.elapsedRealtime();
??????? if (now >= mDurationBase) {
??????????? mChronometer.stop();
??????? }??????? int remaining = (int) (mDurationBase - now);
??????? mProgressBar.setProgress(mDuration - remaining);
??????? if (mChronometerFollow) {
??????????? RelativeLayout.LayoutParams params;
??????????? params = (RelativeLayout.LayoutParams) mProgressBar.getLayoutParams();
??????????? int contentWidth = mProgressBar.getWidth() - (params.leftMargin + params.rightMargin);
??????????? int leadingEdge = ((contentWidth * mProgressBar.getProgress()) /
??????????????????? mProgressBar.getMax()) + params.leftMargin;
??????????? int adjustLeft = 0;
??????????? int textWidth = mChronometer.getWidth();
??????????? if (mChronometerGravity == Gravity.RIGHT) {
??????????????? adjustLeft = -textWidth;
??????????? } else if (mChronometerGravity == Gravity.CENTER_HORIZONTAL) {
??????????????? adjustLeft = -(textWidth / 2);
??????????? }
??????????? leadingEdge += adjustLeft;
??????????? int rightLimit = contentWidth - params.rightMargin - textWidth;
??????????? if (leadingEdge < params.leftMargin) {
??????????????? leadingEdge = params.leftMargin;
??????????? } else if (leadingEdge > rightLimit) {
??????????????? leadingEdge = rightLimit;
??????????? }
??????????? params = (RelativeLayout.LayoutParams) mChronometer.getLayoutParams();
??????????? params.leftMargin = leadingEdge;
??????????? mChronometer.requestLayout();
??????? }
??? }
}
18. Android內(nèi)存管理-SoftReference的使用
很 多時候我們需要考慮Android平臺上的內(nèi)存管理問題,Dalvik VM給每個進程都分配了一定量的可用堆內(nèi)存,當(dāng)我們處理一些耗費資源的操作時可能會產(chǎn)生OOM錯誤(OutOfMemoryError)這樣的異 常,Android123觀察了下國內(nèi)的類似Market客戶端設(shè)計,基本上都沒有采用很好的內(nèi)存管理機制和緩存處理。
? 如果細心的網(wǎng)友可能發(fā)現(xiàn)Android Market客戶端載入時,每個列表項的圖標(biāo)是異步刷新顯示的,但當(dāng)我們快速的往下滾動到一定數(shù)量比如50個,再往回滾動時可能我們看到了部分App的圖 標(biāo)又重新開始加載,當(dāng)然這一過程可能是從SQLite數(shù)據(jù)庫中緩存的,但是在內(nèi)存中已經(jīng)通過類似SoftReference的方式管理內(nèi)存。
? 在Java中內(nèi)存管理,引用分為四大類,強引用HardReference、弱引用WeakReference、軟引用SoftReference和虛引 用PhantomReference。它們的區(qū)別也很明顯,HardReference對象是即使虛擬機內(nèi)存吃緊拋出OOM也不會導(dǎo)致這一引用的對象被回 收,而WeakReference等更適合于一些數(shù)量不多,但體積稍微龐大的對象,在這四個引用中,它是最容易被垃圾回收的,而我們對于顯示類似 Android Market中每個應(yīng)用的App Icon時可以考慮使用SoftReference來解決內(nèi)存不至于快速回收,同時當(dāng)內(nèi)存短缺面臨Java VM崩潰拋出OOM前時,軟引用將會強制回收內(nèi)存,最后的虛引用一般沒有實際意義,僅僅觀察GC的活動狀態(tài),對于測試比較實用同時必須和 ReferenceQueue一起使用。
? 對于一組數(shù)據(jù),我們可以通過HashMap的方式來添加一組SoftReference對象來臨時保留一些數(shù)據(jù),同時對于需要反復(fù)通過網(wǎng)絡(luò)獲取的不經(jīng)常改變的內(nèi)容,可以通過本地的文件系統(tǒng)或數(shù)據(jù)庫來存儲緩存,希望給國內(nèi)做App Store這樣的客戶端一些改進建議。
19. 反射在Android開發(fā)中的利弊
由 于Android 2.2的推出,很多新的API加入導(dǎo)致很多項目移植需要考慮使用Java的反射機制Reflection來動態(tài)調(diào)用,動態(tài)調(diào)用的好處就是不需要使用引用文 件,直接通過JDK中聲明好的方法直接調(diào)用,本身原理基于JVM的,從Java 1.5開始支持,原理上就是根據(jù)類名而不實例化對象的情況下,獲得對象的方法或?qū)傩远苯诱{(diào)用。
? Android開發(fā)時反射能幫助我們多少?
? 1. 有些網(wǎng)友可能發(fā)現(xiàn)Android的SDK比較封閉,很多敏感的方法常規(guī)的用戶無法編譯,我們?nèi)绻戳舜a直接在反射中聲明動態(tài)調(diào)用即可。比如很多internal或I開頭的AIDL接口均可以通過反射輕松調(diào)用。
? 2. 反射對于Android123來說更重要的是考慮到應(yīng)用的兼容性,我們目前主要兼容從Android 1.5到2.2的項目,API Level從3到8可以方便的擴充,調(diào)用前我們預(yù)留一個標(biāo)志位聲明該API的最低以及最高的API Level為多少可以調(diào)用。
? 3. 對于調(diào)試Java的反射是功臣了,在Logcat中我們可以看到出錯的地方肯定有類似java.lang.reflect.XXX的字樣,這種自檢機制可以幫助我們方便的調(diào)試Android應(yīng)用程序。
? 反射的缺點有哪些?
? 1. 因為是動態(tài)執(zhí)行的,效率自然沒有預(yù)編譯時引用現(xiàn)有的庫效率高,就像平時我們Win32開發(fā)時,可以不用h文件,直接通過GetProcAddress一樣 去動態(tài)獲取方法的地址。當(dāng)然效率要根據(jù)復(fù)雜程度而決定,一般稍微復(fù)雜的處理性能損失可能超過20%,對于一些復(fù)雜的涉及Java自動類型轉(zhuǎn)換判斷,執(zhí)行時 間可能是直接引用的上千倍,所以最終我們調(diào)試時必須考慮性能問題。
? 2. 因為反射是動態(tài)的,所以需要處理很多異常,不然Dalvik崩潰出Force Close的概率會大很多,很簡單的一個反射就需要至少3個異常捕獲,本身try-catch效率就不是很高,自然進一步影響運行效率,對于 Android開發(fā)我們必須考慮這些問題。
? 3. 反射因為導(dǎo)致代碼臃腫,自然稍微復(fù)雜的幾個方法實用反射將會導(dǎo)致代碼可讀性和維護性降低,如果很抽象的調(diào)用Android開發(fā)網(wǎng)強烈不推薦這種方法。
? 最后要說的是Reflection并不是Java的專利,微軟的.Net也同樣支持,同時更多的動態(tài)語言如Ruby等均支持這一特性。
20.AsyncTask對比Thread加Handler
很 多網(wǎng)友可能發(fā)現(xiàn)Android平臺很多應(yīng)用使用的都是AsyncTask,而并非Thread和Handler去更新UI,這里Android123給大 家說下他們到底有什么區(qū)別,我們平時應(yīng)該使用哪種解決方案。從Android 1.5開始系統(tǒng)將AsyncTask引入到android.os包中,過去在很早1.1和1.0 SDK時其實官方將其命名為UserTask,其內(nèi)部是JDK 1.5開始新增的concurrent庫,做過J2EE的網(wǎng)友可能明白并發(fā)庫效率和強大性,比Java原始的Thread更靈活和強大,但對于輕量級的使 用更為占用系統(tǒng)資源。Thread是Java早期為實現(xiàn)多線程而設(shè)計的,比較簡單不支持concurrent中很多特性在同步和線程池類中需要自己去實現(xiàn) 很多的東西,對于分布式應(yīng)用來說更需要自己寫調(diào)度代碼,而為了Android UI的刷新Google引入了Handler和Looper機制,它們均基于消息實現(xiàn),有事可能消息隊列阻塞或其他原因無法準(zhǔn)確的使用。
? Android開發(fā)網(wǎng)推薦大家使用AsyncTask代替Thread+Handler的方式,不僅調(diào)用上更為簡單,經(jīng)過實測更可靠一些,Google在 Browser中大量使用了異步任務(wù)作為處理耗時的I/O操作,比如下載文件、讀寫數(shù)據(jù)庫等等,它們在本質(zhì)上都離不開消息,但是AsyncTask相比 Thread加Handler更為可靠,更易于維護,但AsyncTask缺點也是有的比如一旦線程開啟即dobackground方法執(zhí)行后無法給線程 發(fā)送消息,僅能通過預(yù)先設(shè)置好的標(biāo)記來控制邏輯,當(dāng)然可以通過線程的掛起等待標(biāo)志位的改變來通訊,對于某些應(yīng)用Thread和Handler以及 Looper可能更靈活。
21. Android Drawable疊加處理方法
大 家可能知道Bitmap的疊加處理在Android平臺中可以通過Canvas一層一層的畫就行了,而Drawable中如何處理呢? 除了使用BitmapDrawable的getBitmap方法將Drawable轉(zhuǎn)換為Bitmap外,今天Android123給大家說下好用簡單的 LayerDrawable類,LayerDrawable顧名思義就是層圖形對象。下面直接用一個簡單的代碼表示:
??? Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.cwj);
??? Drawable[] array = new Drawable[3];???? array[0] = new PaintDrawable(Color.BLACK); //黑色
???? array[1] = new PaintDrawable(Color.WHITE); //白色
???? array[2] = new BitmapDrawable(bm); //位圖資源
??? LayerDrawable ld = new LayerDrawable(array); //參數(shù)為上面的Drawable數(shù)組
??????? ld.setLayerInset(1, 1, 1, 1, 1);? //第一個參數(shù)1代表數(shù)組的第二個元素,為白色
??????? ld.setLayerInset(2, 2, 2, 2, 2); //第一個參數(shù)2代表數(shù)組的第三個元素,為位圖資源
??? mImageView.setImageDrawable(ld);? 上面的方法中LayerDrawable是關(guān)鍵,Android開發(fā)網(wǎng)提示setLayerInset方法原型為public void setLayerInset (int index, int l, int t, int r, int b) 其中第一個參數(shù)為層的索引號,后面的四個參數(shù)分別為left、top、right和bottom。對于簡單的圖片合成我們可以將第一和第二層的 PaintDrawable換成BitmapDrawable即可實現(xiàn)簡單的圖片合成。
22. onRetainNonConfigurationInstance和getLastNonConfigurationInstance
很 多網(wǎng)友可能知道Android橫豎屏切換時會觸發(fā)onSaveInstanceState,而還原時會產(chǎn)生 onRestoreInstanceState,但是Android的Activity類還有一個方法名為 onRetainNonConfigurationInstance和getLastNonConfigurationInstance這兩個方法。
?? 我們可以通過? onRetainNonConfigurationInstance 代替 onSaveInstanceState,比如距離2
? @Override
? public Object onRetainNonConfigurationInstance()
{???
?????? //這里需要保存的內(nèi)容,在切換時不是bundle了,我們可以直接通過Object來代替
????? return obj;
}在恢復(fù)窗口時,我們可以不使用 onRestoreInstanceState,而代替的是 getLastNonConfigurationInstance 方法。我們可以直接在onCreate中使用,比如
? Object obj = getLastNonConfigurationInstance();???? 最終obj的內(nèi)容就是上次切換時的內(nèi)容。
? 這里Android123提醒大家,每次Activity橫豎屏切換時onCreate方法都會被觸發(fā)。
23. Android中String資源文件的format方法
很 多時候我們感性Google在設(shè)計Android時遵守了大量MVC架構(gòu)方式,可以讓寫公共代碼、美工和具體邏輯開發(fā)人員獨立出來。有關(guān)Android的 資源文件values/strings.xml中如何實現(xiàn)格式化字符串呢? 這里Android123舉個簡單的例子,以及最終可能會用到哪些地方。
<?xml version="1.0" encoding="utf-8"?>
<resources>
??? <string name="app_name">cwj_Demo</string>
??? <string name="hello">android開發(fā)網(wǎng)</string>
</resources>?上面是一段簡單的字符串資源文件,沒有用到格式化,因為比較簡單直接描述了意思,當(dāng)我們設(shè)計一個類似 Delete xxx File ? 的時候,我們可能需要在Java中動態(tài)獲取 xxx 的名稱,所以定義資源時使用格式化可以輕松解決,不需要一堆String去拼接或StringBuffer一個一個append這樣的愚蠢方法,看例子
??? <string name="alert">Delete %1$s File</string>?? 這里%1$s代表這是一個字符串型的,如果是整數(shù)型可以寫為%1$d,類似printf這樣的格式化字符串函數(shù),當(dāng)然如果包含了多個需要格式化的內(nèi)容,則 第二個可以寫為%2$s或%2$d了,那么最終在Java中如何調(diào)用呢? 看下面的例子:
?? 例一: 整數(shù)型的
? <string name="alert">I am %1$d years old</string>? 定義的是這樣的
?? 當(dāng)然,我們杜絕意外情況,比如冒出個secret這樣的string類型的,注意上面是%1$d不是%1$s,所以默認標(biāo)準(zhǔn)的合并成為
?? int nAge=23;
?? String sAgeFormat = getResources().getString(R.string.alert);?
?? String sFinalAge = String.format(sAgeFormat, nAge);?
??? 這樣執(zhí)行完后,就組成了 I am 23 years old,是不是很方便啊.? 當(dāng)然了,下面看下String字符串時的情況.
? 例二: 字符串型的
? 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#中那樣顯示的標(biāo)出參數(shù)的數(shù)字,比如%1或%n,這里數(shù)字代表參數(shù)的第n個。本行最終sFinalInfo顯示的內(nèi)容為
? My name is cwj , I am form Shanghai 。當(dāng)然了你有什么不懂的地方可以來函至 android123@163.com
24. Android工程內(nèi)嵌資源文件的兩種方法
Android軟件一般處理大的資源通過sdcard比如在線下載資源到sdcard,而apk中內(nèi)嵌資源或二進制文件時一般使用下面的兩種方法:
? 方法一
? 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系統(tǒng)處理Assert有個bug,在AssertManager中不能處理單個超過1MB 的文件,不然會報異常具體數(shù)值大家可以測試下傳個稍大的文件,我們在兩年前的文章中有提到,而第一種raw沒這個限制可以放個4MB的Mp3文件沒問題。
25. Android自定義View以及l(fā)ayout屬性全攻略
對于Android系統(tǒng)的自定義View可能大家都熟悉了,對于自定義View的屬性添加,以及Android的Layout的命名空間問題,很多網(wǎng)友還不是很清楚,今天Android123一起再帶大家溫習(xí)一下
? CwjView myView=new CwjView(context);
? 如果用于游戲或整個窗體的界面,我們可能直接在onCreate中setContentView(myView); 當(dāng)然如果是控件,我們可能會需要從Layout的xml中聲明,比如
? <cn.com.android123.CwjView
?? android:layout_width="wrap_content"
?? android:layout_height="wrap_content"
? />? 當(dāng)然,我們也可以直接從父類聲明比如
? <View class="cn.com.android123.CwjView"
?? android:layout_width="wrap_content"
?? android:layout_height="wrap_content"
? />上面我們僅用了父類View的兩個屬性,均來自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"
?? />我們可以看到上面的三個屬性,是我們自定義的。作為標(biāo)準(zhǔn)xml規(guī)范,可能還包含了類似 xmlns: android ="http://schemas.android.com/apk/res/ android "? 這樣的語句,對于定義完整的View,我們的命名空間為cwj,這里可以寫為 xmlns: cwj =http://schemas.android.com/apk/res/ cn.com.android123.cwjView 或 xmlns: cwj =http://schemas.android.com/apk/res/ android 都可以 。
? 對于定義的cwj命名空間和age、university以及city的三個屬性我們?nèi)绾味x呢? 在工程的res/values目錄中我們新建一個cwj_attr.xml文件,編碼方式為utf-8是一個好習(xí)慣,內(nèi)容如下
<?xml version="1.0" encoding="utf-8" ?>
<resources>
? <declare-styleable name="CwjView">
? <attr name="age" format="integer" />
? <attr name="city" format="string" />
? <attr name="university" format="string" />
? </declare-styleable>
</resources>? 這里我們可能對format不是很熟悉,目前Android系統(tǒng)內(nèi)置的格式類型有integer比如ProgressBar的進度值,float比如 RatingBar的值可能是3.5顆星,boolean比如ToggleButton的是否勾選,string比如TextView的text屬性,當(dāng) 然除了我們常見的基礎(chǔ)類型外,Android的屬性還有特殊的比如color是用于顏色屬性的,可以識別為#FF0000等類型,當(dāng)然還有 dimension的尺寸類型,比如23dip,15px,18sp的長度單位,還有一種特殊的為reference,一般用于引用@+id/cwj @drawable/xxx這樣的類型。
? 當(dāng)然什么時候用reference呢? 我們就以定義一個顏色為例子,
? <attr name="red" format="color|reference" />? 這里我們用了邏輯或的運算符,定義的紅色是顏色類型的,同時可以被引用
? 當(dāng)然,對于我們自定義的類中,我們需要使用一個名為obtainStyledAttributes的方法來獲取我們的定義。在我們自定義View的構(gòu)造方法(Context context, AttributeSet attrs)的重載類型中可以用
? public CwjView(Context context, AttributeSet attrs) {
? super(context, attrs);
??????? TypedArray a = context.obtainStyledAttributes(attrs,
????????? R.styleable.cwj_attr);
??????? mAge = a.getInteger(R.styleable.CwjView_age, 22);
??????? mCity = a.getString(R.styleable.CwjView_city, "shanghai");
??????? mUniversity= a.getString(R.styleable.CwjView_university, "sjtu");
??????? a.recycle(); //Android123提示大家不要忘了回收資源}
這樣類的全局成員變量 mAge、mCity就獲取了我們需要的內(nèi)容,當(dāng)然根據(jù)layout中的數(shù)值我們自定義的CwjView需要動態(tài)的處理一些數(shù)據(jù)的情況,可以使用AttributeSet類的getAttributeResourceValue方法獲取。
public CwjView(Context context, AttributeSet attrs)
{
? super(context, attrs);
? resId = attrs.getAttributeResourceValue("cn.com.android123.CwjView", "age", 100);?
? resId = attrs.getAttributeResourceValue("cn.com.android123.CwjView", "city", "shanghai");
? //resID就可以任意使用了
}以上兩種方法中,參數(shù)的最后一個數(shù)值為默認的,如果您有不明白的地方可以來函到 android123@163.com 我們會在第一時間回復(fù)。
26. 自定義Android主題風(fēng)格theme.xml方法
在 Android中可以通過自定義主題風(fēng)格方式來實現(xiàn)個性化以及復(fù)用,首先我們創(chuàng)建theme.xml主題文件,保存位置為工程的res/values /theme.xml ,這里我們可以可以為主題起一個名稱,比如CWJ,這里去除了xml的文件頭<?xml version="1.0" encoding="utf-8"?>這行,我們在工程中只需在androidmanifest.xml文件的Activity節(jié)點中加入 android:theme="@style/Theme.CWJ" 屬性,則這個Activity就使用了這種主題風(fēng)格,整個xml的關(guān)鍵代碼如下:
<resources>
??? <style name="Theme.CWJ" parent="android:Theme">
??????? <item name="android:windowBackground">@drawable/android123</item>
??? </style>
</resources>? 其中上面的代碼中,我們定義設(shè)置全局android:windowBackground即背景值為/res/drawable中的android123圖 片為背景,更多的屬性定義可以參考view的layout xml屬性設(shè)置,比如我們設(shè)置所有字體顏色、大體大小和樣式,可以在style節(jié)點中加入
? <item name="android:textColor">#fff</item>
? <item name="android:textSize">14sp</item>
? <item name="android:textStyle">bold</item>?當(dāng)然我們可以將上面的android123的圖片改進下,使用一個xml文件替代,比如使用bitmap對象,則/res/drawable/android123.xml的完整代碼變?yōu)?
? <?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圖片,當(dāng)然這里可以識別各種類型的圖片,其中android:tileMode是 bitmap的內(nèi)部屬性,其中tileMode設(shè)置為repeat代表重復(fù),這樣可以節(jié)省bitmap資源,比如我們的背景是一層樓,那么全屏可以顯示同 樣的為5層效果,而圖片僅是一層大小,對于資源利用相對更高。
? 當(dāng)然bitmap的屬性tileMode的值為repeat外還有其他的值比如clamp、mirror,這些值并沒有在SDK中并沒有找到定義,通過上次Android開發(fā)網(wǎng)的 Android自定義View以及l(fā)ayout屬性全攻略 一文,我們可以聯(lián)想到bitmap屬于android.graphics.Bitmap 包,由于是android框架,所以下載git的base包,找到該類,類的實例化時android123已經(jīng)在 Android自定義View以及l(fā)ayout屬性全攻略 說的很清楚,所以我們定位到res\values中找到attr.xml有關(guān)bitmap的定義即可,有關(guān)bitmap的更多屬性如? antialias、filter和dither都可以找到使用。
27. android調(diào)試工具monkey壓力測試實戰(zhàn)
很 多Android開發(fā)者可能因為沒有充分測試自己的軟件造成很容易出現(xiàn)FC(Force Close)的問題,這里我們可以通過使用Android固件中自帶的monkey工具來做軟件的壓力測試,monkey工具可以模擬各種按鍵,觸屏,軌 跡球、activity等事件,這里Android123提示大家說白了monkey就是一個小猴子隨機狂玩你的android軟件,看看會不會產(chǎn)生異 常。
? 具體的使用我們通過Android SDK給我們的adb調(diào)試橋鏈接設(shè)備或模擬器,進入Linux Shell狀態(tài),當(dāng)然我們可以輸入adb shell獲取設(shè)備的shell,也可以直接通過adb命令執(zhí)行,比如說adb shell monkey來查看monkey工具中的參數(shù)說明,如圖:?
? 我們要測試的apk文件要在android設(shè)備中已經(jīng)安裝,當(dāng)然模擬器中也可以測試的。執(zhí)行adb shell monkey -p cn.com.android123.cwj -v 100 我們執(zhí)行這句的中包含了p參數(shù),這里代表已安裝軟件的packageName,而v代表查看monkey生成的詳細隨機事件名,最后的數(shù)字100為我們測 試的隨機事件數(shù)量為100.有關(guān)更多的測試方法,請查看上圖中的參數(shù),整個測試比較簡單單很有效,不妨試試。
28. 自定義View
有關(guān)Android的自定義View的框架今天我們一起討論下,對于常規(guī)的游戲,我們在View中需要處理以下幾種問題: 1.控制事件 2.刷新View 3. 繪制View
? 1. 對于控制事件今天我們只處理按鍵事件onKeyDown,以后的文章中將會講到屏幕觸控的具體處理onTouchEvent以及Sensor重力感應(yīng)等方法。
? 2. 刷新view的方法這里主要有invalidate(int l, int t, int r, int b) 刷新局部,四個參數(shù)分別為左、上、右、下。整個view刷新 invalidate(),刷新一個矩形區(qū)域 invalidate(Rect dirty) ,刷新一個特性Drawable, invalidateDrawable(Drawable drawable) ,執(zhí)行invalidate類的方法將會設(shè)置view為無效,最終導(dǎo)致onDraw方法被重新調(diào)用。由于今天的view比較簡單,Android123提 示大家如果在線程中刷新,除了使用handler方式外,可以在Thread中直接使用postInvalidate方法來實現(xiàn)。
? 3. 繪制View主要是onDraw()中通過形參canvas來處理,相關(guān)的繪制主要有drawRect、drawLine、drawPath等等。 view方法內(nèi)部還重寫了很多接口,其回調(diào)方法可以幫助我們判斷出view的位置和大小,比如onMeasure(int, int) Called to determine the size requirements for this view and all of its children.? 、onLayout(boolean, int, int, int, int) Called when this view should assign a size and position to all of its children 和onSizeChanged(int, int, int, int) Called when the size of this view has changed. 具體的作用,大家可以用Logcat獲取當(dāng)view變化時每個形參的變動。
? 下面cwjView是我們?yōu)榻窈笥螒蛟O(shè)計的一個簡單自定義View框架,我們可以看到在Android平臺自定義view還是很簡單的,同時Java支持多繼承可以幫助我們不斷的完善復(fù)雜的問題。
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); //如果不讓父類處理記住調(diào)用setMeasuredDimension
?? }
?? @Override
?? protected void onLayout (boolean changed, int left, int top, int right, int bottom)
?? {
??? super.onLayout (changed,left,top, ight,bottom) ;
?? }?? @Override
?? protected void onDraw(Canvas canvas) {
????? Paint bg = new Paint();
????? bg.setColor(Color.Red);
????? canvas.drawRect(0, 0, getWidth()/2, getHeight()/2, bg); //將view的左上角四分之一填充為紅色?
?? }?? @Override
?? public boolean onTouchEvent(MotionEvent event) {
???????? return super.onTouchEvent(event); //讓父類處理屏幕觸控事件
?? }?? @Override
?? public boolean onKeyDown(int keyCode, KeyEvent event) { //處理按鍵事件,響應(yīng)的軌跡球事件為 public boolean onTrackballEvent (MotionEvent event)
????? switch (keyCode) {
????? case KeyEvent.KEYCODE_DPAD_UP:
???????? break;
????? case KeyEvent.KEYCODE_DPAD_DOWN:
???????? break;
????? case KeyEvent.KEYCODE_DPAD_LEFT:
???????? break;
????
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
