判断 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);
}
}