一、AppCompat
1、把androidx.appcompat升级到1.1.0以后,出现端内语言切换在Android 5~7 失效的问题。
Kotlin解决方案:
override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
if (overrideConfiguration != null) {
val uiMode = overrideConfiguration.uiMode
overrideConfiguration.setTo(baseContext.resources.configuration)
overrideConfiguration.uiMode = uiMode
}
super.applyOverrideConfiguration(overrideConfiguration)
}
Java解决方案:
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (overrideConfiguration != null) {
int uiMode = overrideConfiguration.uiMode;
overrideConfiguration.setTo(getBaseContext().getResources().getConfiguration());
overrideConfiguration.uiMode = uiMode;
}
super.applyOverrideConfiguration(overrideConfiguration);
}
二、RecyclerView
1、RecyclerView的Holder的itemView设置透明度不生效
由于Recycylerview默认会带有动画DefaultItemAnimator,所以就会导致setAlpha不生效,解决方法是取消ItemAnimatior。
RecyclerView.setItemAnimator(null);
三、消息队列
1、能否catch一个在子线程执行的Runnable?
如下代码所示,当处于A线程,然后在B线程post一个runnable,runnable里面的代码有可能会抛出异常,那么我们不能够catch这个runanble的异常,因为我们都知道,当我们往线程里面post一个runnable的时候,它会放到这个线程的消息队列里面,然后在未来某一个时间点出队执行,但是我们try catch是在A线程中顺序执行的,所以有可能当我们的try catch执行完毕,但是runnable里面的代码还未开始执行,所以这样写是错误的。
// 当前线程处于非UI线程
try {
ThreadUtils.runOnUiThread(() -> {
// 切换到UI线程
if (bitmap != null && !bitmap.isRecycled()) {
Bitmap copyedBitmap = bitmap.copy(bitmap.getConfig(), true);
}
});
} catch (Exception e) {
e.printStackTrace();
}
要改成正确写法,还是要把try catch放到具体执行线程的代码中,如下所示:
ThreadUtils.runOnUiThread(() -> {
try {
if (bitmap != null && !bitmap.isRecycled()) {
Log.i(TAG, "bindView(). fetch bg success");
Bitmap copyedBitmap = bitmap.copy(bitmap.getConfig(), true);
}
} catch (Exception e) {
e.printStackTrace();
}
});
四、Camera2
1、判断Camera2是否兼容设备
Camera2虽然是支持API21以上的设备,但是它同时对设备有不同的兼容程度,目前划分为以下四个等级:
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
它们的可支持程度为 LEGACY < LIMITED < FULL < LEVEL_3。我们可以通过api来获取Camera2对当前设备的支持程度:
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
当我们发现设备的level很低时,建议还是放弃camera2,替换回camera1,例如如果设备的level为INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,那么使用Camera2对每一帧进行操作,都会非常卡顿。
A LEGACY device does not support per-frame control, manual sensor control, manual post-processing, arbitrary cropping regions, and has relaxed performance constraints.
五、资源名称
1、invalid symbol错误
在自定义属性,如果自定义属性值为final,default,continue等值,如下所示,就会导致编译时报invalid symbol的错误。同样地,如果在给资源id或者资源名字设为这些值也会报错invalid symbol。
<declare-styleable name="ResultListLayout">
<attr name="result_type" format="enum">
<enum name="win" value="1" />
<enum name="lose" value="2" />
<enum name="default" value="3" />
</attr>
</declare-styleable>
六、Android获取屏幕高度
public static int getScreenRealHeight(@Nullable NonNull context) {
if (VERSION.SDK_INT < 17) {
return getScreenHeight(context);
} else {
int orientation = context.getResources().getConfiguration().orientation : AppUtils.getContext().getResources().getConfiguration().orientation;
orientation = orientation == 1 ? 0 : 1;
if (sRealSizes[orientation] == null) {
WindowManager windowManager =(WindowManager)context.getSystemService("window");
if (windowManager == null) {
return getScreenHeight(context);
}
Display display = windowManager.getDefaultDisplay();
Point point = new Point();
display.getRealSize(point);
sRealSizes[orientation] = point;
}
return sRealSizes[orientation].y;
}
}
public static int getScreenHeight(@NonNull Context context) {
return context.getResources().getDisplayMetrics().heightPixels;
}
七、Android镜像翻转
view.setScaleX=-1f 即可实现控件的镜像翻转。
八、Textview跑马灯失效
常规解决方法:
- xml里面view要配置singleLine=true,而不要使用maxLines=1。
- 在初始化完textview之后,需要手动设置一下:textview. setSelected(true)。
- 如果还不行的话,自定义一个View继承TextView:
public class RollTextView extends TextView { public RollTextView (Context context) { super(context); } public RollTextView (Context context, AttributeSet attrs) { super(context, attrs); } public RollTextView (Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean isFocused() { return true; } }
- 这时候还不行的话,那么就需要可以用比较非常规的方式,通过在主线程post一个runnable来setSelected(true),具体延迟多少时间可以根据自己需要来定:
textview.postDelayed(new Runnable() { @Override public void run() { setSelected(false); setSelected(true); } }, 3000);
- 如果再不行,那就是终极绝招了,记得把你的textview高度设置为wrap_content,而不要设置固定高度,在有一次发现textview首次加载后跑马灯失效,后面继续展示出来是正常的,然后通过这种方式解决了问题。
九、部分手机使用全屏DialogFragments时会被状态栏覆盖
解决方案:
- (1) 在父布局增加fitSystemWindow属性,实测可以:
android:fitsSystemWindows="true"
- (2) 其他方案,可以参考《Android开发 - 解决DialogFragment在全屏时View被状态栏遮住的问题》。
十、java.lang.RuntimeException: Parcelable encountered IOException writing serializable object
原因:实现了Serializable的类里面含有未实现Serializable接口的类就会有此crash。
解决:实现了Serializable的类里面的其他类也需要实现Serializable接口。
十一、RelativeLayout里面多个ViewStub被infalte出来后位置不对的问题
stackoverflow的参考链接:[https://stackoverflow.com/questions/13045531/relativelayout-and-viewstub-inflation]
(https://stackoverflow.com/questions/13045531/relativelayout-and-viewstub-inflation)
比如说,如下布局里面,当viewstub1和viewstub2被inflate出来后,viewstub2会覆盖在viewstub1上面,layout_toEndOf属性没有生效,这对于想要在RelativeLayout里面使用ViewStub的同学来说是一个非常大的问题。
原因:熟悉ViewStub源码的同学不难发现问题原因就是,当ViewStub的inflate被调用后,ViewStub在parent中占的坑就会被layout对应的view所替换,ViewStub会被父View remove掉,然后父View把ViewStub所在index的位置把layout的View add进去。所以当我们把layout_toEndOf的值设置为ViewStub的id时,很明显ViewStub已经被remove掉了,所以这个属性就不生效了。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ViewStub
android:id="@+id/viewstub1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout="@layout/layout_1" />
<ViewStub
android:id="@+id/viewstub2"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_toEndOf="@id/viewstub1"
android:layout="@layout/layout_2" />
</RelativeLayout>
解决:ViewStub提供了一个额外的属性inflatedId,当layout的View被加载出来后,就会把该View的id指定为inflatedId,所以我们只需要指定ViewStub的inflatedId,并让viewstub2的layout_toEndOf的值设置为viewstub1的inflatedId即可。代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ViewStub
android:id="@+id/viewstub1"
android:layout_width="100dp"
android:layout_height="100dp"
android:inflatedId="@+id/viewstub1"
android:layout="@layout/layout_1" />
<ViewStub
android:id="@+id/viewstub2"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_toEndOf="@id/viewstub1"
android:layout="@layout/layout_2" />
</RelativeLayout>