天天看點

記錄知識點與技巧(一)

去掉ActionBar

android:theme="@android:style/Theme.Holo.NoActionBar"
           
actionBar = getActionBar(); //得到ActionBar
actionBar.hide(); //隐藏ActionBar
           
requestWindowFeature(Window.FEATURE_NO_TITLE);
           

設定全屏

requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
           
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
           

Activity生命周期

記錄知識點與技巧(一)

Activity由于在背景狀态時,被回收之後如果恢複資料

onSaveInstanceState()//回調方法保證活動被回收之前調用
@Override
protected void onSaveInstanceState(Bundle outState) {
	super.onSaveInstanceState(outState);
	String tempData = "Something you just typed";
	outState.putString("data_key", tempData);
}
           
<span style="font-size:12px;">//展示資料
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	Log.d(TAG, "onCreate");
	requestWindowFeature(Window.FEATURE_NO_TITLE);
	setContentView(R.layout.activity_main);
	if (savedInstanceState != null) {
		String tempData = savedInstanceState.getString("data_key");
		Log.d(TAG, tempData);
	}
	……
}</span>
           

Activity的啟動模式

<activity> 标簽指定android:launchMode 屬性來選擇啟動模式

standard

singleTop

singleTask

singleInstance

standard
預設模式,啟動一個新的活動後,系統不會在乎這個活動是否已經在傳回棧中存在,每次啟動都會建立該活動的一個新的執行個體。
singleTop
當活動的啟動模式指定為singleTop,在啟動活動時如果發現傳回棧的棧頂已經是該活動,則認為可以直接使用它,不會再建立新的活動執行個體。
singleTask
當活動的啟動模式指定為singleTask,每次啟動該活動時系統首先會在傳回棧中檢查是否存在該活動的執行個體,如果發現已經存在則直接使用該執行個體,并把在這
個活動之上的所有活動統統出棧,如果沒有發現就會建立一個新的活動執行個體。
singleInstance
指定為singleInstance 模式的活動會啟用一個新的傳回棧來管理這個活動(其實如果singleTask 模式指定了不同的taskAffinity,也會啟動一個新的傳回棧)。
這種模式下會有一個單獨的傳回棧來管理這個活動,不管是哪個應用程式來通路這個活動,都共用的同一個傳回棧,也就解決了共享活動執行個體的問題。
           

假設有三個Activity,跳轉列印日志如下

記錄知識點與技巧(一)
可以看到, SecondActivity 的Task id 不同于FirstActivity 和ThirdActivity , 這說明SecondActivity 确實是存放在一個單獨的傳回棧裡的,而且這個棧中隻有SecondActivity 這一個活動。
然後我們按下Back 鍵進行傳回,你會發現ThirdActivity 竟然直接傳回到了FirstActivity,再按下Back 鍵又會傳回到SecondActivity,再按下Back 鍵才會退出程式,這是為什麼呢?
其實原理很簡單,由于FirstActivity 和ThirdActivity 是存放在同一個傳回棧裡的,當在ThirdActivity 的界面按下Back 鍵,ThirdActivity 會從傳回棧中出棧,那麼FirstActivity 就成為了棧頂活動顯示在界面上,是以也就出現了從ThirdActivity 直接傳回到FirstActivity 的情況。然後在FirstActivity 界面再次按下Back 鍵,這時目前的傳回棧已經空了,于是就顯示了另一個傳回棧的棧頂活動,即SecondActivity。最後再次按下Back 鍵,這時所有傳回棧都已經空了,也就自然退出了程式。
           

知曉目前是在哪一個活動

public class BaseActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d("BaseActivity", getClass().getSimpleName());
	}
}
           
記錄知識點與技巧(一)

随時随地退出程式

public class ActivityCollector {
	public static List<Activity> activities = new ArrayList<Activity>();
	public static void addActivity(Activity activity) {
		activities.add(activity);
	}
	public static void removeActivity(Activity activity) {
		activities.remove(activity);
	}
	public static void finishAll() {
		for (Activity activity : activities) {
			if (!activity.isFinishing()) {
				activity.finish();
			}
		}
	}
}
           

接下來修改BaseActivity 中的代碼,如下所示:

public class BaseActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d("BaseActivity", getClass().getSimpleName());
		ActivityCollector.addActivity(this);
	}
	@Override
	protected void onDestroy() {
		super.onDestroy();
		ActivityCollector.removeActivity(this);
	}
}
           

控件的visibility屬性

visible 表示控件是可見的,這個值是預設值,不指定android:visibility 時,控件都是可見的。
invisible 表示控件不可見,但是它仍然占據着原來的位置和大小,可以了解成控件變成透明狀态了。
gone 則表示控件不僅不可見,而且不再占用任何螢幕空間。
           

相對布局RelativeLayout部分屬性含義

android:layout_alignLeft	本元素的左邊緣和某元素的的左邊緣對齊
android:layout_alignTop	本元素的上邊緣和某元素的的上邊緣對齊
android:layout_alignRight	本元素的下邊緣和某元素的的下邊緣對齊
android:layout_alignBottom 本元素的右邊緣和某元素的的右邊緣對齊
android:layout_alignParentLeft	是否緊靠父元素左邊
android:layout_alignParentTop	是否緊靠父元素上方
android:layout_alignParentRight 是否緊靠父元素右邊
android:layout_alignParentBottom 是否緊靠父元素的下方
android:layout_centerInParent	在父元素中上下左右居中顯示
android:layout_above 在某元素的的上方
android:layout_below 在某元素的下方
android:layout_toLeftOf 在某元素的左邊
android:layout_toRightOf 在某元素的右邊
           

将字型變粗

//英文中使用android:textStyle=”bold”但是不能将中文加粗
TextView tv = (TextView)findViewById(R.id.TextView01); 
TextPaint tp = tv.getPaint(); 
tp.setFakeBoldText(true);
           

View樹

記錄知識點與技巧(一)

如何制作.9圖

記錄知識點與技巧(一)

在Android sdk 目錄下有一個tools 檔案夾,在這個檔案夾中找到draw9patch.bat 檔案,我們就是使用它來制作Nine-Patch 圖檔的。輕按兩下打開之後,在導航欄點選File→Open 9-patch将圖檔加載進來

記錄知識點與技巧(一)

我們可以在圖檔的四個邊框繪制一個個的小黑點,在上邊框和左邊框繪制的部分就表示當圖檔需要拉伸時就拉伸黑點标記的區域,在下邊框和右邊框繪制的部分則表示内容會被放置的區域。

動态添加碎片

動态添加碎片主要分為5 步。
1. 建立待添加的碎片執行個體。
2. 擷取到FragmentManager,在活動中可以直接調用getFragmentManager()方法得到。
3. 開啟一個事務,通過調用beginTransaction()方法開啟。
4. 向容器内加入碎片,一般使用replace()方法實作,需要傳入容器的id 和待添加的碎
片執行個體。
5.送出事務,調用commit()方法來完成。
           

在碎片中模拟傳回棧

其實很簡單,FragmentTransaction 中提供了一個addToBackStack()方法,可以用于将一個事務添加到傳回棧中

AnotherRightFragment fragment = new AnotherRightFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.
beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(null);
transaction.commit();
           

在活動中得到相應碎片的執行個體

RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
//在碎片中得到對應的activity執行個體
MainActivity activity = (MainActivity) getActivity();
           

碎片完整的生命周期示意圖

記錄知識點與技巧(一)

碎片還提供了一些附加的回調方法

1. onAttach()
當碎片和活動建立關聯的時候調用。
2. onCreateView()
為碎片建立視圖(加載布局)時調用。
3. onActivityCreated()
確定與碎片相關聯的活動一定已經建立完畢的時候調用。
4. onDestroyView()
當與碎片關聯的視圖被移除的時候調用。
5. onDetach()
當碎片和活動解除關聯的時候調用。
           

如何使用限定符(Qualifiers)

在res目錄下建立layout-large 檔案夾,在這個檔案夾下建立一個布局,也叫做activity_main.xml。layout/activity_main 布局隻包含了一個碎片,即單頁模式,而layout-large/
activity_main 布局包含了兩個碎片,即雙頁模式。其中large 就是一個限定符,那些螢幕被認為是large 的裝置就會自動加載layout-large 檔案夾下的布局,而小螢幕的裝置則還是會加載layout 檔案夾下的布局。
           

雙頁模式:

記錄知識點與技巧(一)

單頁模式:

記錄知識點與技巧(一)

Android 中一些常見的限定符可以參考下表:

記錄知識點與技巧(一)

使用最小寬度限定符

使用large 限定符成功解決了單頁雙頁的判斷問題 ,不過很快又有一個新的問題出現了,large 到底是指多大呢?有的時候我們希望可以更加靈活地為不同裝置加載布局, 不管它們是不是被系統認定為“ large ”, 這時就可以使用最小寬度限定符(Smallest-width Qualifier)了。

最小寬度限定符允許我們對螢幕的寬度指定一個最小指(以dp 為機關),然後以這個最小值為臨界點,螢幕寬度大于這個值的裝置就加載一個布局,螢幕寬度小于這個值的裝置就加載另一個布局。

在res 目錄下建立layout-sw600dp 檔案夾,這就意味着,當程式運作在螢幕寬度大于600dp 的裝置上時,會加載layout-sw600dp/activity_main 布局,當程式運作在螢幕寬度小于600dp 的裝置上時,則仍然加載預設的layout/activity_main 布局。需要注意一點,最小寬度限定符是在Android 3.2 版本引入的;

廣播機制(BroadcastReceiver)

為了友善于進行系統級别的消息通知,Android 引入了一套類似的廣播消息機制

廣播機制簡介

為什麼說Android 中的廣播機制更加靈活呢?這是因為Android 中的每個應用程式都可以對自己感興趣的廣播進行注冊,這樣該程式就隻會接收到自己所關心的廣播内容,這些廣播可能是來自于系統的,也可能是來自于其他應用程式的。Android 提供了一套完整的API,

允許應用程式自由地發送和接收廣播Android 中的廣播主要可以分為兩種類型,标準廣播和有序廣播。

标準廣播(Normal broadcasts)是一種完全異步執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,是以它們之間沒有任何先後順序可言。這種廣播的效率會比較高,但同時也意味着它是無法被截斷的。

sendBroadcast(intent);
           
記錄知識點與技巧(一)

有序廣播(Ordered broadcasts)則是一種同步執行的廣播,在廣播發出之後,同一時刻隻會有一個廣播接收器能夠收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播才會繼續傳遞。是以此時的廣播接收器是有先後順序的,優先級高的廣播接收器就可以先收到廣播消息,并且前面的廣播接收器還可以截斷正在傳遞的廣播,這樣後面的廣播接收器就無法收到廣播消息了。

sendOrderedBroadcast(intent, null);
           
記錄知識點與技巧(一)

有序廣播中如何設定廣播接收器的先後順序呢?

//intent-filter中的priority指代廣播接收器的先後順序
<receiver android:name=".MyBroadcastReceiver">
<intent-filter android:priority="100" >
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
           

android:priority執行 優先級,預設為0,值越大優先級越高,1000是最大值。

使用本地廣播

前面我們發送和接收的廣播全部都是屬于系統全局廣播,即發出的廣播可以被其他任何
的任何應用程式接收到,并且我們也可以接收來自于其他任何應用程式的廣播。這樣就很容
易會引起安全性的問題,比如說我們發送的一些攜帶關鍵性資料的廣播有可能被其他的應用
程式截獲,或者其他的程式不停地向我們的廣播接收器裡發送各種垃圾廣播.
           

Android提供LocalBroadcastManager來對本地廣播進行管理;

//使用LocalBroadcastManager擷取執行個體
localBroadcastManager = LocalBroadcastManager.getInstance(this);
// 發送本地廣播
localBroadcastManager.sendBroadcast(intent); 
// 注冊本地廣播監聽器
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);
//選擇是否允許廣播繼續傳遞
abortBroadcast();
           

Tip:

動态注冊的廣播接收器一定都要取消注冊才行

需要添加查詢系統網絡狀态的權限

android:name="android.permission.ACCESS_NETWORK_STATE"
           

如果要在廣播接收器裡啟動活動的,一定要給Intent 加入FLAG_ACTIVITY_NEW_TASK 這個标志

另外還有一點需要說明,本地廣播是無法通過靜态注冊的方式來接收的

Android資料存儲五種方式

1. 使用SharedPreferences存儲資料

 2. 檔案存儲資料      

 3 .SQLite資料庫存儲資料

 4 .使用ContentProvider存儲資料

 5 .網絡存儲資料

攔截短信

//系統發出的短信廣播是一條有序廣播,是以可以攔截掉
public class MainActivity extends Activity {
	……
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		……
		receiveFilter = new IntentFilter();
		receiveFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
		receiveFilter.setPriority(100);
		messageReceiver = new MessageReceiver();
		registerReceiver(messageReceiver, receiveFilter);
	}
	……
	class MessageReceiver extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
			……
			abortBroadcast();
		}
	}
}
           

調用攝像頭

// 建立File對象,用于存儲拍照後的圖檔
File outputImage = new File(Environment.
getExternalStorageDirectory(), "tempImage.jpg");
try {
	if (outputImage.exists()) {
		outputImage.delete();
	}
outputImage.createNewFile();
} catch (IOException e) {
	e.printStackTrace();
}
imageUri = Uri.fromFile(outputImage);
Intent intent = new Intent("android.media.action. IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO); // 啟動相機程式
           
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	switch (requestCode) {
		case TAKE_PHOTO:
			if (resultCode == RESULT_OK) {
				Intent intent = new Intent("com.android.camera.action.CROP");
				intent.setDataAndType(imageUri, "image/*");
				intent.putExtra("scale", true);
				intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
				startActivityForResult(intent, CROP_PHOTO); // 啟動裁剪程式
			}
			break;
		case CROP_PHOTO:
			if (resultCode == RESULT_OK) {
				try {
					Bitmap bitmap = BitmapFactory.decodeStream
					(getContentResolver()
					.openInputStream(imageUri));
					picture.setImageBitmap(bitmap); // 将裁剪後的照片顯示出來
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			}
			break;
			default:
			break;
	}
}@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	switch (requestCode) {
		case TAKE_PHOTO:
			if (resultCode == RESULT_OK) {
				Intent intent = new Intent("com.android.camera.action.CROP");
				intent.setDataAndType(imageUri, "image/*");
				intent.putExtra("scale", true);
				intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
				startActivityForResult(intent, CROP_PHOTO); // 啟動裁剪程式
			}
			break;
		case CROP_PHOTO:
			if (resultCode == RESULT_OK) {
				try {
					Bitmap bitmap = BitmapFactory.decodeStream
					(getContentResolver()
					.openInputStream(imageUri));
					picture.setImageBitmap(bitmap); // 将裁剪後的照片顯示出來
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			}
			break;
			default:
			break;
	}
}
           

使用Service還是Thread

(1)預設情況下,Service其實是運作在主線程中的,如果需要執行複雜耗時的操作,必須在Service中再建立一個Thread來執行任務或者使用intentService,大同小異。
(2)Service的優先級高于背景挂起的Activity,當然,也高于Activity所建立的Thread,是以,系統可能在記憶體不足的時候優先殺死背景的Activity或者Thread,而不會輕易殺死Service元件,即使被迫殺死Service,也會在資源可用時重新開機被殺死的Service
           

Android 中的定時任務

一種是使用Java API 裡提供的Timer 類;

一種是使用Android 的Alarm 機制;

但Timer不具有喚醒CPU 的功能,有可能導緻Timer 中的定時任務無法正常運作;

Alarm 機制

AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
           

四種值可選

ELAPSED_REALTIME:讓定時任務的觸發時間從系統開機開始算起,但不會喚醒CPU。

ELAPSED_REALTIME_WAKEUP:表示讓定時任務的觸發時間從系統開機開始算起,但會喚醒CPU。

RTC :讓定時任務的觸發時間從1970 年1月1 日0 點開始算起,但不會喚醒CPU。

RTC_WAKEUP:表示讓定時任務的觸發時間從1970 年1 月1 日0 點開始算起,但會喚醒CPU。

 AlarmManager的常用API

1、set(int type,long startTime,PendingIntent pi)

該方法用于設定一次性鬧鐘,第一個參數表示鬧鐘類型,第二個參數表示鬧鐘執行時間,第三個參數表示鬧鐘響應動作。

2、setRepeating(int type,long startTime,long intervalTime,PendingIntent pi)

該方法用于設定重複鬧鐘,第一個參數表示鬧鐘類型,第二個參數表示鬧鐘首次執行時間,第三個參數表示鬧鐘兩次執行的間隔時間,第三個參數表示鬧鐘響應動作。

3、setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi)

該方法也用于設定重複鬧鐘,與第二個方法相似,不過其兩個鬧鐘執行的間隔時間不是固定的而已。

擷取到系統開機至今所經曆時間的毫秒數

SystemClock.elapsedRealtime()

擷取到1970 年1 月1 日0 點至今所經曆時間的毫秒數

System.currentTimeMillis()

PendingIntent的FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT

當使用FLAG_UPDATE_CURRENT時:

PendingIntent.getActivity(context, 0, notificationIntent,PendingIntent.FLAG_CANCEL_CURRENT時);

FLAG_UPDATE_CURRENT會更新之前PendingIntent的消息,比如,你推送了消息1,并在其中的Intent中putExtra了一個值“ABC”,在未點選該消息前,繼續推送第二條消息,并在其中的Intent中putExtra了一個值“CBA”,好了,這時候,如果你單擊消息1或者消息2,你會發現,他倆個的Intent中讀取過來的資訊都是“CBA”,就是說,第二個替換了第一個的内容。

當使用FLAG_CANCEL_CURRENT時:

依然是上面的操作步驟,這時候會發現,點選消息1時,沒反應,第二條可以點選。

導緻上面兩個問題的原因就在于第二個參數requestCode,當requestCode值一樣時,後面的就會對之前的消息起作用,是以為了避免影響之前的消息,requestCode每次要設定不同的内容。

...

......

............