判斷 Activity 是否為 Task 的根
方法:Activity.isTaskRoot()
/**
* 判斷當前 Activity 是否為 Activity 堆棧的根。
*
* @return
* 如果該 Activity 是堆棧的根則返回 true,否則返回 false
*/
boolean isTaskRoot()
把當前應用切換到后臺
方法:Activity.moveTaskToBack(boolean nonRoot)
說明:當前棧中 Activity 順序為:A -> B,在 B 中調用該方法退到后臺,此時 Activity 堆棧中的順序不會改變,重新啟動應用,會調用 B 的 onRestart -> onStart -> onResume 方法,不會調用 onCreate 方法。
/**
* 該方法可以實現類似 Home 鍵的功能,讓應用退到后臺。
*
* @param nonRoot
* 如果是 false,則只有當前 Activity 是 Task 的根時才會退到后臺
* 如果是 true,不管當前 Activity 是否為 Task 的根都會退到后臺
* @return
* 如果該 Activity 已經移動到后臺則返回 true,否則返回 false
*/
boolean moveTaskToBack(boolean nonRoot)
獲取資源對應的尺寸值
方法:Resources.getDimension(int id)
、getDimensionPixelSize(int id)
、getDimensionPixelOffset(int id)
/**
* 基于當前 DisplayMetrics 進行轉換,獲取指定資源對應的 float 類型的尺寸值。
*/
float getDimension(@DimenRes int id)
/**
* 獲取到資源對應的 float 類型尺寸值后對小叔部分做四舍五入,轉換成 int 類型。
*/
int getDimensionPixelSize(@DimenRes int id)
/**
* 獲取到資源對應的 float 類型尺寸值后做取整操作,舍棄小數部分,轉換成 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
但是在項目中已經修改了 gradle-wrapper.properties
解決方法:
在 Project 的 build.gradle 中添加:
buildscript {
System.properties['com.android.build.gradle.overrideVersionCheck'] = 'true'
...
}
TextView 自動判斷內容是電話、Email、URL
方法:
1、在 XML 中指定android:autoLink
屬性:
<TextView
android:autoLink="all"
... />
2、使用 Linkify.addLinks
:
/**
* @param mask
* Linkify.WEB_URLS:判斷是否是網址
* 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 進行 NDK 開發時打印 Log
使用 Android Studio 進行 NDK 開發時打印 LOG 出現提示信息:
undefined reference to `__android_log_print'
原因是 Android Studio 在 build 時會自動生成 Android.mk 文件,導致之前手動配置的 Android.mk 文件失效
解決方法:
修改 Module 的 build.gradle 配置文件:
defaultConfig {
ndk {
ldLibs "log", "z", "m"
}
...
}
使用 retrolambda 支持 Lambda 表達式
方法:
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();
// 設置數據源
mmr.setDataSource(filePath);
// 讀取媒體文件信息
mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_XXX);
// 讀取本地媒體文件的信息
mmr.setDataSource(filePath);
// 讀取網絡媒體文件的信息
mmr.setDataSource(urlPath, new HashMap<String, String>());
ListView 添加 padding 效果
該屬性定義了是否允許 ViewGroup 在 padding 內繪制,默認為 true(不允許在 padding 內繪制)。
如果要實現 ListView
的第一個或者最后一個 Item 有 padding 效果,但是滾動時不存在 padding,可以設置該屬性值為 false。
1、XML代碼:
<ListView
android:paddingTop=""
android:paddingBottom=""
android:clipToPadding="false"
... />
2、Java代碼:
listView.setPaddingTop();
listView.setPaddingBottom();
listView.setClipToPadding(false);
實現的效果見下圖:
Mac 系統 Android Studio 的 Maven 本地倉庫的目錄
環境:Android Studio 2.0、Gradle 2.10
本地倉庫路徑為:~/.gradle/caches/modules-2/
Android Studio 清除本地緩存的 Gradle 項目
方法:rm -rf $HOME/.gradle/caches/
重啟應用的方法
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"
... />
獲取當前線程的ID和名稱
// 獲取線程的 ID
Thread.currentThread().getId();
// 獲取線程的名稱
Thread.currentThread().getName());
清除通知的信息
NotificationManager nm = (NotificationManager)
Context.getSystemService(NOTIFICATION_SERVICE);
// 清除指定的消息
nm.cancel(notificationId);
// 清除所有的消息
nm.cancelAll();
手機震動
Vibrator vibrator = (Vibrator) context.getSystemService(VIBRATOR_SERVICE);
// OFF/ON/OFF/ON...
long[] pattern = { 800, 40, 800, 40 };
// repeat: -1不重復,非-1為從pattern的指定下標開始重復
vibrator.vibrate(pattern, repeat);
使用plurals資源表示各種數量
<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);
獲取指定的進程的運行狀態
/**
* 獲取指定的 Context 對應的應用的運行狀態。
*
* @param processName
* 進程的名稱,例如: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;
}
拍照時獲取屏幕的旋轉方向
可以通過 OrientationEventListener
事件監聽屏幕旋轉,拍照后使用 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 + "");
}
}
};
// 開啟屏幕方向旋轉事件的監聽
mOrientationEventListener.enable();
// 關閉屏幕方向旋轉事件的監聽
mOrientationEventListener.disable();
使用Matrix對Bitmap進行旋轉和鏡像水平垂直翻轉
Matrix matrix = new Matrix();
// 鏡像垂直翻轉
matrix.postScale(1, -1);
// 鏡像水平翻轉
matrix.postScale(-1, 1);
// 旋轉90度
matrix.postRotate(90);
顯示軟鍵盤時頁面背景圖片不變形
頁面背景是一張圖片,并且在 AndroidManifest.xml 中已經設置 android:windowSoftInputMode="adjustResize"
,此時軟鍵盤顯示時會對布局進行壓縮,圖片發生改變。
可以使用 ScrollView
包裹背景圖片,這樣軟鍵盤彈起后圖片不會被壓縮,同時可以指定 ScrollView
的滾動條不顯示:android:scrollbars="none"
,并且禁止掉 ScrollView
的觸摸事件,提高用戶體驗。
RxJava的Schedulers
RxJava 的 Scheduler 有如下幾個:
Schedulers.computation()
:用于計算型工作,這個 Scheduler
使用的線程池大小為 CPU 核數。不要把 I/O 操作放在 computation()
中,否則 I/O 操作的等待時間會浪費 CPU。
下面是可能會用到Scheduler:
:用于計算型工作例如事件循環和回調處理,不要在I/O中使用這個函數(應該使用Schedulers.io()函數);
Schedulers.from(executor):使用指定的Executor作為Scheduler;
Schedulers.immediate():在當前線程中立即開始執行任務;
Schedulers.io():用于I/O密集型工作例如阻塞I/O的異步操作,這個調度器由一個會隨需增長的線程池支持;對于一般的計算工作,使用Schedulers.computation();
Schedulers.newThread():為每個工作單元創建一個新的線程;
Schedulers.test():用于測試目的,支持單元測試的高級事件;
Schedulers.trampoline():在當前線程中的工作放入隊列中排隊,并依次操作。
通過設置observeOn和subscribeOn調度器,我們定義了網絡請求使用哪個線程(Schedulers.newThread())。
EditText 長按禁止彈出復制、粘貼
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) {
}
});
對于某些系統的手機,只設置以上的方法還是會顯示復制、粘貼的彈出框,還需要重寫 OnLongClickListener
:
editText.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return true;
}
});
android:includeFontPadding
設置 TextView
是否包含頂部和底部的額外空白。
TextView
默認在文字的上下方會留有額外的空白,導致顯示的文字不是垂直居中顯示,尤其是 TextView
的高度與文字高度差距很小且帶有背景時更明顯。
可以通過設置 android:includeFontPadding="false"
來避免這種情況。
實現多次連續點擊的事件
/*
* 定義保存點擊時間的數組,數組的長度是要實現幾次連續點擊的數量。
* 例如,要實現雙擊功能,定義:
* 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是指多次點擊的操作在多少毫秒的范圍內定義為連續點擊事件
if (mHits[0] > SystemClock.uptimeMillis() - 2000) {
// TODO 處理多次連續點擊的事件
}
}
防止多個對話框同時彈出重疊顯示
開啟 APP 時經常會在首頁判斷很多邏輯,比如版本升級、選擇城市等,會彈出對話框提醒用戶,這時有可能會多個對話框同時重疊顯示,影響體驗,可以使用隊列的方式依次顯示對話框:
/**
* 用來依次顯示對話框,防止多個對話框重疊顯示。
*/
public class DialogQueue {
private Dialog mCurrentDialog;
private Queue<Dialog> mDialogQueue = new LinkedList<>();
/**
* 顯示對話框。該方法會占用 OnDismissListener,
* 如果 OnDismissListener 已被占用也可繼承 ISequenceDialog 實現。
*/
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);
}
}