Android 中常用的計量單位
Android有時候需要一些計量單位,比如在布局Layout檔案中可能需要指定具體單位等。
常用的計量單位有:px、dip(dp)、sp,以及一些不常用的pt、in、mm。下面詳細介紹下這些計量單位之間的區別和聯絡。
in:英寸(長度單位);
mm:毫米(長度單位);
pt:磅/點,1/72英寸(一個標準的長度單位);
sp:全名 scaled pixels-best for text size,放大像素,與刻度無關,可以根據使用者的字型大小就行縮放,主要用來處理字型的大小;
px:螢幕中的像素;
dip(dp):裝置獨立像素,一種基於螢幕密度的抽象單位;因為不通裝置中有不同的顯示效果,所以為瞭解決在不通解析度手機上運行不至於相差太大的問題,引入了dip計量單位,這種計量單位與行動裝置硬體無關。
說道密度,這裡簡單介紹下。手機密度值(Density)表示每英寸有多少個顯示點,與手機的解析度是兩個概念,但是解析度與密度之間又互相關聯,兩者轉換公式為:
密度值是120,螢幕實際解析度為:240px×400px(兩個點對應一個解析度);
密度值是160,螢幕實際解析度為:320px×533px(3個點對應兩個解析度);
密度值是240,螢幕實際解析度為:480px×800px(一個點對應一個解析度)。
比如,QVGA與WQVGA屏的密度值是120,HVGA屏密度值是160,WVGA屏密度值是240.
res資來源目錄,因為啟動並執行裝置的不同,對應的資源檔目錄也不同。其真正的原因是,資來源目錄是根據密度的不同來進行劃分的:
密度值是120,對應的資來源目錄是drawable-ldpi;
密度值是160,對應的資來源目錄是drawable-mdpi;
密度值是240,對應的資來源目錄是drawable-hdpi。
根據以上介紹,在布局中應該盡量使用dip(dp)作為單位;而定義作為文字大小的單位則推薦使用sp。
Context
Context 類是一個抽象類別,它的子類很多,比如 Activity 、 TabActivity 、Service 等。很多方法中需要傳入 Context 參數才可執行個體對象,例如 Toast 執行個體對象時,第一個對象需傳入 Context 對象。其實 Context 從字面上可以理解為類似於控制代碼,聯絡內容相關的意思。因為 Activity 是 Context 的子類,所以一般在 Activity 中使用 Context 的時候,可以用 this 來代替,但是如果在內部類中(如利用內部類使用監聽組件),就不能使用 this 來代替 Context ,而是使用 "ActName.this" ,這裡的 ActName 指的是 Activity 類的類名。
在Android中的 Context 可以有很多操作,但是最主要的功能是載入和訪問資源,具體可以看下官方文檔:http://developer.android.com/reference/android/content/Context.html
Resources與getResources
在 Android 資源(Resource)都會自動由 R.java 資源檔產生對應的靜態 ID ,通過 R 資源檔對資源產生的 ID 來引用。這樣在資源需要修改的時候,就不用去程式原始碼中修改,直接修改對應res下的資源檔即可。
在原始碼中,如果需要對資來源目錄下的 string.xml 中定義的字串變數進行訪問,只需要通過 getResources 的方式引用即可。
例如,需要引用 string.xml 中的一個字串,其變數為 "hello_world",擷取方式如下:
getResources().getString(R.string.hello_world);
再如需要引用 drawable 目錄下的一張名為"goodby_times.png"的圖片,擷取方式如下:
getResources().getDrawable(R.drawable.goodby_times);
當然一些函數不僅支援傳入 String 類型,也支援傳入引用 ID 。例如 TextView 中的setText() 函數,這個方法不僅支援傳入 String 類型,還支援 R 檔案引用 ID 的參數。"R.string.strName"中的 strName 表示在 string.xml 中定義的字串在 R 資源檔中產生的對應 ID 索引。
findViewById與LayoutInflater
LayoutInflater 的作用類似於 findViewById(),兩者不同之處在於 LayoutInflater 是用來執行個體化 xml 布局檔案中的布局:而 findViewById,顧名思義,是通過 ID 來找到 xml 布局檔案中定義的組件,比如 EditText、TextView、Button 等等。
關於用 LayoutInflater 來執行個體布局的方式有兩種:
1.通過傳入 Context 參數來獲得 LayoutInflater 執行個體,然後調用 LayoutInflater 類中的 inflate 函數來得到布局執行個體。
LayoutInflater inflater = LayoutInflater.from(Context context) ;View view = inflater.inflate(R.layout.activity_main, null);
2.通過系統服務來擷取到 LayoutInflater 執行個體,然後調用 LayoutInflater 類中的 inflate 函數來得到布局執行個體。
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);View view = inflater.inflate(R.layout.activity_main, null);
儘管執行個體布局的形式不同,但是這兩種布局方式的性質沒有區別。
多個Activity之間跳轉/退出/傳遞資料操作
主要代碼:
package yc.example.activityex;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity implements OnClickListener { private Button btnOpen, btnHide, btnExit;// 聲明按鈕 private static String[] arrStr = new String[] { "有自己的目標,那就去實現它,語言總蒼白無力。", "他的速度快逾閃電,連殘影都模糊不清,如同一團風。", "他腦海中,只有一個念頭:快!再快一點!再快一點!" }; private static int ARR_INT = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 執行個體化按鈕 btnOpen = (Button) this.findViewById(R.id.btnOpen); btnHide = (Button) this.findViewById(R.id.btnHide); btnExit = (Button) this.findViewById(R.id.btnExit); // 給每個按鈕添加監聽器 btnOpen.setOnClickListener(this); btnHide.setOnClickListener(this); btnExit.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnOpen: // 建立一個意圖,並設定需開啟的Activity Intent intent = new Intent(MainActivity.this, OtherActivity.class); if(ARR_INT>=arrStr.length) ARR_INT =0; // 發送資料 intent.putExtra("info", arrStr[ARR_INT++]); // 啟動另外一個Activity this.startActivity(intent); break; case R.id.btnHide: /* * finish()函數表示退出當前 Activity 。 * 執行此函數會調用生命週期中的onStop()與onDestory()函數,但是這僅僅是將當前的 Activity 推到後台, * 程式中的資源仍然存在,如果 Android 運行記憶體不是很緊張的情況下,程式是不會真正退出的。 */ this.finish(); break; case R.id.btnExit: /* * System.exit(0) 函數表示退出當前的程式。 當 Android * 執行到此函數的時候,本應用程式的資源將被回收,並退出此程式。 */ System.exit(0); // 退出程式 break; } }}MainActivity.classpackage yc.example.activityex;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.widget.TextView;public class OtherActivity extends Activity { private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tv = new TextView(this); setContentView(tv); //得到當前 Activity 的意圖 Intent intent = this.getIntent(); //擷取資料 String info = intent.getStringExtra("info"); //將擷取到的資料設定成 TextView 文本 tv.setText(info); }}OtherActivity.class
PS:記得在 AndroidManifest.xml 檔案中聲明建立的Activity,不然應用會由於找不到活動而報異常。
<!-- 下面是註冊建立的Activity(OtherActivity) --> <activity android:name="yc.example.activityex.OtherActivity"></activity>
橫豎屏切換處理的三種方式
Android 手機中運行應用的時候,一般使用者都是豎屏,但是如果突然將手機橫屏,那麼很可能就會造成程式出現異常,因為在 Android 中每次螢幕切換都會重啟當前的 Activity 。這種情況下,異常的解決方式有一下三種。
1.鎖定橫豎屏切換
此方式只需要在 AndroidManifest.xml 檔案中,對 Activity 定義螢幕方向屬性只能為橫屏或者豎屏即可
將螢幕固定為豎屏顯示
<activity android:screenOrientation="portrait">
將螢幕固定為橫屏顯示
<activity android:screenOrientation="landscape">
其他參數:
"unspecified":預設值 由系統來判斷顯示方向.判定的策略是和裝置相關的,所以不同的裝置會有不同的顯示方向.
"landscape":橫屏顯示(寬比高要長)
"portrait":豎屏顯示(高比寬要長)
"user":使用者當前首選的方向
"behind":和該Activity下面的那個Activity的方向一致(在Activity堆棧中的)
"sensor":有物理的感應器來決定。如果使用者旋轉裝置這螢幕會橫豎屏切換。
"nosensor":忽略物理感應器,這樣就不會隨著使用者旋轉裝置而更改了("unspecified"設定除外)。
因為一個 Android 應用程式中可能會有多個 Activity ,那麼可以根據需要去配置每一個 Activity 的顯示方式,如果不設定,預設可以橫豎屏切換。
或者在源碼中設定橫豎屏:
設定豎屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
設定橫屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
2.原始碼中處理橫豎屏切換事件
首先在AndroidManifest.xml中為Activity設定configChanges屬性
android:configChanges="orientation|screenSize"
然後在對應的 Activity 原始碼中重寫 onConfigurationChanged() 函數即可。這樣處理後,當橫豎屏切換的時候,就會響應其 Activity 中的 onConfigurationChanged() 函數,然後對橫豎屏做出判斷處理。
package yc.example.helloworld;import android.app.Activity;import android.content.res.Configuration;import android.os.Bundle;import android.util.Log;import android.widget.TextView;public class MainActivity extends Activity { private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("--Main--", "onCreate"); textView=(TextView)findViewById(R.id.tv); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.i("--Main--", "onConfigurationChanged"); if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { textView.setText("當前螢幕為豎屏"); } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { textView.setText("當前螢幕為橫屏"); } }}
此方式就不會在切換橫豎屏的時候,Activity 預設重啟了。
3.重寫 onRestoreInstanceState() 與 onSaveInstanceState() 函數代碼:
@Overrideprotected void onRestoreInstanceState(Bundle savedInstanceState) {super.onRestoreInstanceState(savedInstanceState);Log.i("YInfo", "onRestoreInstanceState()");}@Overrideprotected void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);Log.i("YInfo", "onSaveInstanceState()");}
在螢幕切換橫豎屏的時候,會響應 onSaveInstanceState() 函數,然後重啟載入當前的 Activity ,最後響應 onRestoreInstanceState() 函數,所以可以通過重寫這兩個函數,進行螢幕橫豎屏切換時的處理。
以上3種處理橫豎屏切換方式,根據當前應用來進行選擇。