判斷 Activity 是否為 Task 的根
方法:Activity.isTaskRoot()
/**
* 判斷當(dāng)前 Activity 是否為 Activity 堆棧的根。
*
* @return
* 如果該 Activity 是堆棧的根則返回 true,否則返回 false
*/
boolean isTaskRoot()
把當(dāng)前應(yīng)用切換到后臺
方法:Activity.moveTaskToBack(boolean nonRoot)
說明:當(dāng)前棧中 Activity 順序為:A -> B,在 B 中調(diào)用該方法退到后臺,此時 Activity 堆棧中的順序不會改變,重新啟動應(yīng)用,會調(diào)用 B 的 onRestart -> onStart -> onResume 方法,不會調(diào)用 onCreate 方法。
/**
* 該方法可以實現(xiàn)類似 Home 鍵的功能,讓應(yīng)用退到后臺。
*
* @param nonRoot
* 如果是 false,則只有當(dāng)前 Activity 是 Task 的根時才會退到后臺
* 如果是 true,不管當(dāng)前 Activity 是否為 Task 的根都會退到后臺
* @return
* 如果該 Activity 已經(jīng)移動到后臺則返回 true,否則返回 false
*/
boolean moveTaskToBack(boolean nonRoot)
獲取資源對應(yīng)的尺寸值
方法:Resources.getDimension(int id)、getDimensionPixelSize(int id)、getDimensionPixelOffset(int id)
/**
* 基于當(dāng)前 DisplayMetrics 進(jìn)行轉(zhuǎn)換,獲取指定資源對應(yīng)的 float 類型的尺寸值。
*/
float getDimension(@DimenRes int id)
/**
* 獲取到資源對應(yīng)的 float 類型尺寸值后對小叔部分做四舍五入,轉(zhuǎn)換成 int 類型。
*/
int getDimensionPixelSize(@DimenRes int id)
/**
* 獲取到資源對應(yīng)的 float 類型尺寸值后做取整操作,舍棄小數(shù)部分,轉(zhuǎn)換成 int 類型。
*/
int getDimensionPixelOffset(@DimenRes int id)
Gradle version 2.10 is required
Gradle 升級到2.10版本后,使用 gradle uploadArchives 提示:
Gradle version 2.10 is required. Current version is 2.5. If using the gradle wrapper, try editing the distributionUrl in xxx/gradle/wrapper/gradle-wrapper.properties to gradle-2.10-all.zip
但是在項目中已經(jīng)修改了 gradle-wrapper.properties
解決方法:
在 Project 的 build.gradle 中添加:
buildscript {
System.properties['com.android.build.gradle.overrideVersionCheck'] = 'true'
...
}
TextView 自動判斷內(nèi)容是電話、Email、URL
方法:
1、在 XML 中指定android:autoLink屬性:
<TextView
android:autoLink="all"
... />
2、使用 Linkify.addLinks:
/**
* @param mask
* Linkify.WEB_URLS:判斷是否是網(wǎng)址
* Linkify.EMAIL_ADDRESSES:判斷是否是 Email 地址
* Linkify.PHONE_NUMBERS:判斷是否是電話
* Linkify.MAP_ADDRESSES:判斷是否是地圖的地址
* Linkify.ALL:WEB_URLS | EMAIL_ADDRESSES | PHONE_NUMBERS | MAP_ADDRESSES
*/
Linkify.addLinks(TextView text, int mask)
讀取 AndroidManifest 中配置的 meta-data 信息
1、讀取 application 下的 <meta-data> 元素:
ApplicationInfo ai = Context.getPackageManager().getApplicationInfo(
Context.getPackageName(), PackageManager.GET_META_DATA);
Object value = ai.metaData.get(String key);
2、讀取 activity 下的 <meta-data> 元素:
ActivityInfo ai = Context.getPackageManager().getActivityInfo(
Activity.getComponentName(), PackageManager.GET_META_DATA);
Object value = ai.metaData.get(String key);
3、讀取 service 下的 <meta-data> 元素:
ComponentName component = new ComponentName(Context, Service.class);
ServiceInfo si = Context.getPackageManager().getServiceInfo(
component, PackageManager.GET_META_DATA);
Object value = si.metaData.get(String key);
4、讀取 receiver 下的 <meta-data> 元素:
ComponentName component = new ComponentName(Context, Receiver.class);
ActivityInfo ai = getPackageManager().getReceiverInfo(
component, PackageManager.GET_META_DATA);
Object value = ai.metaData.get(String key);
使用 Gradle 進(jìn)行 NDK 開發(fā)時打印 Log
使用 Android Studio 進(jìn)行 NDK 開發(fā)時打印 LOG 出現(xiàn)提示信息:
undefined reference to `__android_log_print'
原因是 Android Studio 在 build 時會自動生成 Android.mk 文件,導(dǎo)致之前手動配置的 Android.mk 文件失效
解決方法:
修改 Module 的 build.gradle 配置文件:
defaultConfig {
ndk {
ldLibs "log", "z", "m"
}
...
}
使用 retrolambda 支持 Lambda 表達(dá)式
方法:
1、下載和安裝 jdk8
2、在 Project 中的 build.gradle 添加 retrolambda 的依賴:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.2.5'
}
}
3、在 Module 中的 build.gradle 添加 plugin:
apply plugin: 'me.tatarka.retrolambda'
4、在 Module 中的 build.gradle 指定 compileOptions:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
5、在 Module 中的 build.gradle 指定源碼編譯的級別:
retrolambda {
// 指定源碼編譯到兼容 Java 1.6 版本
javaVersion JavaVersion.VERSION_1_6
}
獲取媒體文件的信息
使用 MediaMetadataRetriever 類獲取媒體文件的信息。
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
// 設(shè)置數(shù)據(jù)源
mmr.setDataSource(filePath);
// 讀取媒體文件信息
mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_XXX);
// 讀取本地媒體文件的信息
mmr.setDataSource(filePath);
// 讀取網(wǎng)絡(luò)媒體文件的信息
mmr.setDataSource(urlPath, new HashMap<String, String>());
ListView 添加 padding 效果
該屬性定義了是否允許 ViewGroup 在 padding 內(nèi)繪制,默認(rèn)為 true(不允許在 padding 內(nèi)繪制)。
如果要實現(xiàn) ListView 的第一個或者最后一個 Item 有 padding 效果,但是滾動時不存在 padding,可以設(shè)置該屬性值為 false。
1、XML代碼:
<ListView
android:paddingTop=""
android:paddingBottom=""
android:clipToPadding="false"
... />
2、Java代碼:
listView.setPaddingTop();
listView.setPaddingBottom();
listView.setClipToPadding(false);
實現(xiàn)的效果見下圖:

Mac 系統(tǒng) Android Studio 的 Maven 本地倉庫的目錄
環(huán)境:Android Studio 2.0、Gradle 2.10
本地倉庫路徑為:~/.gradle/caches/modules-2/
Android Studio 清除本地緩存的 Gradle 項目
方法:rm -rf $HOME/.gradle/caches/
重啟應(yīng)用的方法
Intent intent = new Intent(this, Activity.class);
PendingIntent mPendingIntent = PendingIntent.getActivity(Context, requestCode,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager) Context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
解決ListView的Item有Button時Item本身不能點擊
在 Item 的根視圖上添加代碼:
<ListView
android:descendantFocusability="blocksDescendants"
... />
限制輸入框輸入的字符
使用 android:digits 屬性,只有該屬性指定的值才能輸入:
<EditText
android:digits="1234567890abcdefghijklmnopqrstuvwxyz"
... />
獲取當(dāng)前線程的ID和名稱
// 獲取線程的 ID
Thread.currentThread().getId();
// 獲取線程的名稱
Thread.currentThread().getName());
清除通知的信息
NotificationManager nm = (NotificationManager)
Context.getSystemService(NOTIFICATION_SERVICE);
// 清除指定的消息
nm.cancel(notificationId);
// 清除所有的消息
nm.cancelAll();
手機(jī)震動
Vibrator vibrator = (Vibrator) context.getSystemService(VIBRATOR_SERVICE);
// OFF/ON/OFF/ON...
long[] pattern = { 800, 40, 800, 40 };
// repeat: -1不重復(fù),非-1為從pattern的指定下標(biāo)開始重復(fù)
vibrator.vibrate(pattern, repeat);
使用plurals資源表示各種數(shù)量
<resources>
<plurals name="plurals_name">
<item quantity="one">Just One</item>
<item quantity="other">There are %d count</item>
</plurals>
</resources>
/*
* 例如:
* 1. Resources.getQuantityString(R.plurals.plurals_name, 1, 1);
* 返回:Just One
*
* 2. Resources.getQuantityString(R.plurals.plurals_name, 10, 10);
* 返回:There are 10 count
*/
Resources.getQuantityString(R.plurals.plurals_name, int quantity, Object... args);
獲取指定的進(jìn)程的運行狀態(tài)
/**
* 獲取指定的 Context 對應(yīng)的應(yīng)用的運行狀態(tài)。
*
* @param processName
* 進(jìn)程的名稱,例如:context.getPackageName()
*
* @return 若正在前臺運行則返回 1,若正在后臺運行則返回 2,否則返回 0
*/
public static int getRunningState(Context context, String processName) {
ActivityManager am = (ActivityManager) context.
getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processInfos =
am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo pi : processInfos) {
if (pi.processName.equals(processName)) {
if (pi.importance == ActivityManager.
RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return 1;
}
return 2;
}
}
return 0;
}
拍照時獲取屏幕的旋轉(zhuǎn)方向
可以通過 OrientationEventListener 事件監(jiān)聽屏幕旋轉(zhuǎn),拍照后使用 ExifInterface 寫入圖片的 Exif 信息
OrientationEventListener mOrientationEventListener =
new OrientationEventListener(this) {
@Override
public void onOrientationChanged(int orientation) {
if (45 <= orientation && orientation < 135) {
ExifInterface.setAttribute(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_ROTATE_180 + "");
} else if (135 <= orientation && orientation < 225) {
ExifInterface.setAttribute(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_ROTATE_270 + "");
} else if (225 <= orientation && orientation < 315) {
ExifInterface.setAttribute(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL + "");
} else {
ExifInterface.setAttribute(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_ROTATE_90 + "");
}
}
};
// 開啟屏幕方向旋轉(zhuǎn)事件的監(jiān)聽
mOrientationEventListener.enable();
// 關(guān)閉屏幕方向旋轉(zhuǎn)事件的監(jiān)聽
mOrientationEventListener.disable();
使用Matrix對Bitmap進(jìn)行旋轉(zhuǎn)和鏡像水平垂直翻轉(zhuǎn)
Matrix matrix = new Matrix();
// 鏡像垂直翻轉(zhuǎn)
matrix.postScale(1, -1);
// 鏡像水平翻轉(zhuǎn)
matrix.postScale(-1, 1);
// 旋轉(zhuǎn)90度
matrix.postRotate(90);
顯示軟鍵盤時頁面背景圖片不變形
頁面背景是一張圖片,并且在 AndroidManifest.xml 中已經(jīng)設(shè)置 android:windowSoftInputMode="adjustResize",此時軟鍵盤顯示時會對布局進(jìn)行壓縮,圖片發(fā)生改變。
可以使用 ScrollView 包裹背景圖片,這樣軟鍵盤彈起后圖片不會被壓縮,同時可以指定 ScrollView 的滾動條不顯示:android:scrollbars="none",并且禁止掉 ScrollView 的觸摸事件,提高用戶體驗。
RxJava的Schedulers
RxJava 的 Scheduler 有如下幾個:
Schedulers.computation():用于計算型工作,這個 Scheduler
使用的線程池大小為 CPU 核數(shù)。不要把 I/O 操作放在 computation()
中,否則 I/O 操作的等待時間會浪費 CPU。
下面是可能會用到Scheduler:
:用于計算型工作例如事件循環(huán)和回調(diào)處理,不要在I/O中使用這個函數(shù)(應(yīng)該使用Schedulers.io()函數(shù));
Schedulers.from(executor):使用指定的Executor作為Scheduler;
Schedulers.immediate():在當(dāng)前線程中立即開始執(zhí)行任務(wù);
Schedulers.io():用于I/O密集型工作例如阻塞I/O的異步操作,這個調(diào)度器由一個會隨需增長的線程池支持;對于一般的計算工作,使用Schedulers.computation();
Schedulers.newThread():為每個工作單元創(chuàng)建一個新的線程;
Schedulers.test():用于測試目的,支持單元測試的高級事件;
Schedulers.trampoline():在當(dāng)前線程中的工作放入隊列中排隊,并依次操作。
通過設(shè)置observeOn和subscribeOn調(diào)度器,我們定義了網(wǎng)絡(luò)請求使用哪個線程(Schedulers.newThread())。
EditText 長按禁止彈出復(fù)制、粘貼
editText.setLongClickable(false);
editText.setTextIsSelectable(false);
editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
});
對于某些系統(tǒng)的手機(jī),只設(shè)置以上的方法還是會顯示復(fù)制、粘貼的彈出框,還需要重寫 OnLongClickListener:
editText.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return true;
}
});
android:includeFontPadding
設(shè)置 TextView 是否包含頂部和底部的額外空白。
TextView 默認(rèn)在文字的上下方會留有額外的空白,導(dǎo)致顯示的文字不是垂直居中顯示,尤其是 TextView 的高度與文字高度差距很小且?guī)в斜尘皶r更明顯。
可以通過設(shè)置 android:includeFontPadding="false" 來避免這種情況。
實現(xiàn)多次連續(xù)點擊的事件
/*
* 定義保存點擊時間的數(shù)組,數(shù)組的長度是要實現(xiàn)幾次連續(xù)點擊的數(shù)量。
* 例如,要實現(xiàn)雙擊功能,定義:
* long mHits = new long[2];
*/
long[] mHits = new long[5];
void clickedView() {
System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
mHits[mHits.length - 1] = SystemClock.uptimeMillis();
// 2000是指多次點擊的操作在多少毫秒的范圍內(nèi)定義為連續(xù)點擊事件
if (mHits[0] > SystemClock.uptimeMillis() - 2000) {
// TODO 處理多次連續(xù)點擊的事件
}
}
防止多個對話框同時彈出重疊顯示
開啟 APP 時經(jīng)常會在首頁判斷很多邏輯,比如版本升級、選擇城市等,會彈出對話框提醒用戶,這時有可能會多個對話框同時重疊顯示,影響體驗,可以使用隊列的方式依次顯示對話框:
/**
* 用來依次顯示對話框,防止多個對話框重疊顯示。
*/
public class DialogQueue {
private Dialog mCurrentDialog;
private Queue<Dialog> mDialogQueue = new LinkedList<>();
/**
* 顯示對話框。該方法會占用 OnDismissListener,
* 如果 OnDismissListener 已被占用也可繼承 ISequenceDialog 實現(xiàn)。
*/
public void showDialogSequence(Dialog dialog) {
if (dialog != null) {
mDialogQueue.offer(dialog);
}
if (mCurrentDialog != null && mCurrentDialog.isShowing()) {
return;
}
mCurrentDialog = mDialogQueue.poll();
if (mCurrentDialog != null) {
mCurrentDialog.show();
final DialogInterface.OnDismissListener listener = new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialogInterface) {
mCurrentDialog = null;
showDialogSequence(null);
}
};
if (mCurrentDialog instanceof ISequenceDialog) {
((ISequenceDialog) mCurrentDialog).addOnDismissListener(listener);
} else {
mCurrentDialog.setOnDismissListener(listener);
}
}
}
public interface ISequenceDialog {
void addOnDismissListener(DialogInterface.OnDismissListener listener);
}
}