1,adb 隐藏导航栏
immersive.full:全屏
immersive.status:隐藏状态栏
immersive.navigation:隐藏导航栏
- 隐藏 App 导航栏
adb shell settings put global policy_control immersive.navigation=package1, package2
- 撤销 App 操作
adb shell settings put global policy_control immersive.off=package1, package2
- 隐藏所有 App 导航栏,除了 package1
adb shell settings put global policy_control immersive.navigation=apps, -package1
- 撤销全部操作
adb shell settings put global policy_control null
2,FileProvider
对于面向 Android 7.0 的应用,Android 框架执行的 StrictModeAPI 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常 。
- 最简单粗暴的就是将 targetSdkVersion 改成24以下(RE管理器就是23)
- 在Android 7.0+ 上使用 `FileProvider.getUriForFile 将 file://Uri 转换成 content://Uri 。
- 利用反射忽略 Android 7.0+ 的 content://uri 检查:
val method: Method = StrictMode::class.java.getDeclaredMethod("disableDeathOnFileUriExposure") method.invoke("")
官网:
FileProvider
3,同一 Activity 的多个实例以任务的形式显示在概览屏幕中
- 仅仅设置Intent.FLAG_ACTIVITY_NEW_DOCUMENT,系统将 Activity 视为新任务显示在概览屏幕中;当主 Activity 启动新 Activity 时,系统会搜遍现有任务,看看是否有任务的 Intent 与 Activity 的 Intent 组件名称和 Intent 数据(
intent.setData()
)相匹配。 如果未找到任务,则会以该 Activity 作为其根创建新任务。如果找到的话,则会将该任务转到前台并将新 Intent 传递给 onNewIntent() 。 - 当一起设置 Intent.FLAG_ACTIVITY_NEW_DOCUMENT
和 FLAG_ACTIVITY_MULTIPLE_TASK 标志时,系统始终会以目标 Activity 作为根创建新任务(类似launchMode="standard"
模式)。 - 使用 AppTask 类移除任务:在于概览屏幕创建新任务的 Activity 中,您可以通过调用 finishAndRemoveTask() 方法将任务从概览屏幕中删除。
实现类似微信小程序单独后台卡片显示:
val intent = Intent(activity, MDViewActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) // 以后台任务列表的形式打开
intent.data = Uri.fromFile(File(file)) // 设置不同 Uri 数据实现不同文档以单独任务卡片展示。
activity.startActivity(intent)
// 默认动画像在两个App之间跳转,所以加上自定义跳转动画实现在同一个应用跳转的效果。
activity.overridePendingTransition(R.anim.slide_right_in, R.anim.slide_left_out)
参考官网:
概览屏幕
4,更改最近任务列表的 Title、Icon 和 Color
TaskDescription td = new TaskDescription(mTitle, mIcon, mColor);
activity.setTaskDescription(td);
5,系统状态栏导航栏相关
// Android 5.0+ 全屏显示
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// <item name="windowActionBar">false</item> & <item name="windowNoTitle">true</item>
supportActionBar?.hide()
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // 底部控件要设置 fitsSystemWindows=true
window.statusBarColor = getColor(android.R.color.transparent)
}
// Android 6.0+ 灰色状态栏图标
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var suv = decorView.systemUiVisibility
suv = suv or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
decorView.systemUiVisibility = suv
}
// Android 8.0+ 亮色导航栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var suv = decorView.systemUiVisibility
suv = suv or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
decorView.systemUiVisibility = suv
window.navigationBarColor = getColor(android.R.color.transparent)
}
//诺基亚Rom
getWindow().setNavigationBarColor(ContextCompat.getColor(this, android.R.color.white));
1, statusBarColor (API level 21)
setStatusBarColor (API level 21)
2, windowLightStatusBar (API level 23)
SYSTEM_UI_FLAG_LIGHT_STATUS_BAR (API level 23)
3, navigationBarColor (API level 21)
setNavigationBarColor (API level 21)
4, windowLightNavigationBar (API level 27)
SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR (API level 26)
5, navigationBarDividerColor (API level 27)
setNavigationBarDividerColor (API level P)
6,16进制颜色透明度动态
/** Adds alpha to a hex color
* @param originalColor color, without alpha
* @param alpha from 0.0 to 1.0
* @return the original color with alpha
* */
fun getColorWithAlpha(originalColor: String, alpha: Double): String {
val alphaFixed = Math.round(alpha * 255)
var alphaHex = java.lang.Long.toHexString(alphaFixed)
if (alphaHex.length == 1) {
alphaHex = "0" + alphaHex
}
return originalColor.replace("#", "#" + alphaHex)
}
7,RecyclerView恢复浏览位置
// 要恢复的界面的第一条 item 位置
val position = layoutManager.findFirstVisibleItemPosition()
val view = layoutManager.findViewByPosition(position)
if (view != null) {
// 第一条 item 位置距顶部距离
val top = view.top
}
// 恢复,立即恢复无动画无感觉
layoutManager.scrollToPositionWithOffset(position, top)
8,java.lang.ExceptionInInitializerError异常
此异常是 静态初始化程序中发生意外异常的信号
public class A{
private static A a = new A();
private static HashMap<String, String>() b = new HashMap<String, String>();
private A(){
b.put("key", "value");
}
public static A getInstance(){
return a;
}
}
原因:当此类单例调用 getInstance()
时,类A开始加载,而静态代码块和静态变量是在类的加载时进行初始化,是所有类对象所共享的,静态变量的加载顺序是按它们在源文件中声明的顺序来进行,所以类A加载,就开始加载静态变量a,接着调用a的构造,直接调用 b.put("key", "value");
,但是静态变量b还没有开始初始化,所以就出现了此错误。
解决:解决此错误只需要将上面静态变量定义的位置对换一下就可以了。
注意:因为 ExceptionInInitializerError 导致了类无法加载,由于类加载失败了,因此JVM会抛出 NoClassDefFoundError ,在分析 NoClassDefFoundError 的原因,最好看下日志文件中有没有 ExceptionInInitializerError ,然后再考虑要不要检查 classpath。
9,从demens.xml文件获取长度值
getDimension()、getDimensionPixelSize()和getDimenPixelOffset()的结果值都是实际像素值px;
getDimension():返回实际数值;
getDimensionPixelSize():返回的是实际数值的四舍五入;
getDimensionPixelOffset():返回的是实际数值去掉后面的小数点。
10,View随ScrollView滑动透明度变化
public class TranslucentScrollView extends ScrollView {
//渐变的视图
private View mAlphaView;
//渐变视图高度
private int alphaViewHeight = 0;
/**
* @param alphaView 透明变化的视图
* @param alphaViewHeight 透明变化视图高度
*/
public void setTransView(View alphaView, int alphaViewHeight) {
mAlphaView = alphaView;
//初始视图-透明
mAlphaView.setAlpha(0);
mAlphaView.setEnabled(false);
this.alphaViewHeight = alphaViewHeight;
}
/**
* 获取透明度比例
* @return
*/
private float getTransAlpha() {
float scrollY = getScrollY();
float offset = 1 - Math.max((alphaViewHeight - scrollY) / alphaViewHeight, 0f);
return Math.abs(offset);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mAlphaView != null) {
float alpha = getTransAlpha();
mAlphaView.setAlpha(alpha);
mAlphaView.setEnabled(alpha > 0.1);
}
}
}
也可以使用CoordinatorLayout配合layout_behavior实现
11,ScrollView嵌套RecyclerView滑动冲突
简单方法一:
LinearLayoutManager manger = new LinearLayoutManager(getActivity()){
@Override
public boolean canScrollVertically() {
return false;
}
};
12,RecyclerView使用LinearLayoutManager时,item的match_parent不起作用
解决方法:
View itemView = inflater.inflate(R.layout.item_swipe_layout, parent, false);
原因:
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
//看源码得知当你传入的root不为null时才会setLayoutParams,所以item的match_parent不生效
13,自绘形状的水波纹
//btn_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@color/black"/>
<corners android:radius="4dp"/>
</shape>
</item>
<item android:drawable="@drawable/btn_border"/>
</ripple>
//btn_border.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="4dp"/>
<stroke
android:width="@dimen/border_width"
android:color="@color/accent"/>
</shape>
14,SpannableStringBuilder单TextView多样式
//添加文字删除线
public static SpannableStringBuilder getStrikethroughString(String str) {
SpannableStringBuilder strikethroushStr = new SpannableStringBuilder(str);
strikethroushStr.setSpan(
new StrikethroughSpan(),
0,
str.length(),
Spanned.SPAN_INCLUSIVE_INCLUSIVE);
return strikethroushStr;
}
textView.setText(getStrikethroughString("测试文字"));
【Android】强大的SpannableStringBuilder
15,不同语言单复数 getQuantityString()
textView.setText(getResources().getQuantityString(
R.plurals.choose_day, days, days));
//string.xml
<plurals name="choose_day">
<item quantity="one"><xliff:g id="count_Single">"%1$d day "</xliff:g></item>
<item quantity="other"><xliff:g id="count_Plural">"%1$d days "</xliff:g></item>
</plurals>
16,ScrollView 嵌套 RecyclerView 时,自动显示到 RecyclerView 的位置
原理:
1,ScrollView 会滑到获取焦点的子 view 的位置;
2,RecyclerView 的 focusableOnTouchMode 属性默认是 true;
解决:
1,ScrollView 第一个子 View 设置 android:focusableInTouchMode="true"
;
2,ScrollView 的直接子 ViewGroup 设置android:descendantFocusability=”blocksDescendants”
属性;
3,自定义 ScrollView ,页面隐藏时记录滑动位置,显示时滑动到上一次保存的位置;
其它:
1,android:focusableInTouchMode="true"
:可以通过触摸获取焦点;
2,android:descendantFocusability=”blocksDescendants”
:Viewgroup 会覆盖子类控件而直接获得焦点;
17,Log信息过长打印不全
/**
* 信息太长,分段打印
*
* @param tag
* @param msg
*/
public static void e(String tag, String msg) {
//因为String的length是字符数量不是字节数量所以为了防止中文字符过多,
// 把4*1024的MAX字节打印长度改为2001字符数
int max_str_length = 2001 - tag.length();
//大于4000时
while (msg.length() > max_str_length) {
Log.e(tag, msg.substring(0, max_str_length));
msg = msg.substring(max_str_length);
}
//剩余部分
Log.e(tag, msg);
}
18,打开联系人选择列表
@OnClick(R.id.iv_contact)
public void onViewClicked() {
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(intent, PICK_CONTACT_REQUEST);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// If the request went well (OK) and the request was PICK_CONTACT_REQUEST
if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
Cursor cursor = getActivity().getContentResolver().query(uri, null, null, null, null);
String num = "", name = "";
if (cursor != null && cursor.moveToNext()) {
name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
num = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
//替换掉不合法的字符
num = num.replace("-", "");
num = num.replace(" ", "");
cursor.close();
}
Log.e(">>>>>>>>", "onActivityResult:" + name + "-" + num);
}
}
}
}
打开的这个联系人选择界面是系统默认会提供的,通常这个界面被称为 Android Contact Picker 。并且该界面会列出所有有号码的联系人,包括一个联系人下有多个号码。
19,隐藏与显示软键盘
/**
* 隐藏软键盘
*/
public static void hideSoftInput(View view) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
/**
* 显示软键盘
*/
public static void showSoftInput(View view) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(view, 0);
}
}
20,禁止App字体大小随系统设置改变而改变
// BaseActivity
@Override
protected void attachBaseContext(Context newBase) {
Context targetContext;
final Resources res = newBase.getResources();
final Configuration configuration = res.getConfiguration();
boolean isUpdate = false;
// 防止字体大小修改
if (configuration.fontScale != 1) {
configuration.fontScale = 1;
isUpdate = true;
}
// 防止显示大小修改
DisplayMetrics displayMetrics = res.getDisplayMetrics();
final float densityDpi = displayMetrics.densityDpi;
final int defaultDensityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
if (densityDpi > defaultDensityDpi) {
configuration.densityDpi = defaultDensityDpi;
isUpdate = true;
}
if (isUpdate) {
targetContext = newBase.createConfigurationContext(configuration);
} else {
targetContext = newBase;
}
super.attachBaseContext(targetContext);
}
网友评论