[Android] 筆記

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

方法:android:clipToPadding

該屬性定義了是否允許 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)的效果見下圖:

clipToPadding

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);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容