[Android] 筆記

判斷 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 效果

方法:android:clipToPadding

該屬性定義了是否允許 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);

實現的效果見下圖:

clipToPadding

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);
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,661評論 25 708
  • 在 Activity 獲取字符串資源: this.getString(R.string.hello) 從 Cont...
    allencaicai閱讀 961評論 0 1
  • 故書不厭百回讀,熟讀深思子自知——宋.蘇軾《送安驚落第詩》 小弟初學安卓,該文算是小弟的學習過程,網絡摘抄的概念,...
    RMaple_Qiu閱讀 353評論 0 3
  • 這是自己在項目當中踩過的坑而總結整理的筆記,僅供自己。署名android developer 基礎知識 此部分包含...
    whamu22閱讀 743評論 0 2
  • 今天,斌找我,他因為要回家,說電動車讓我幫他騎,我說可以。但實際情況是,我只開一次電動車,還不是很熟悉,但我還是答...
    仰望之夏閱讀 354評論 0 0