Android日常开发踩坑篇

一、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跑马灯失效

常规解决方法:

  1. xml里面view要配置singleLine=true,而不要使用maxLines=1。
  2. 在初始化完textview之后,需要手动设置一下:textview. setSelected(true)。
  3. 如果还不行的话,自定义一个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;
        }
    }
    
  4. 这时候还不行的话,那么就需要可以用比较非常规的方式,通过在主线程post一个runnable来setSelected(true),具体延迟多少时间可以根据自己需要来定:
    textview.postDelayed(new Runnable() {
        @Override
        public void run() {
            setSelected(false);
            setSelected(true);
        }
    }, 3000);
    
  5. 如果再不行,那就是终极绝招了,记得把你的textview高度设置为wrap_content,而不要设置固定高度,在有一次发现textview首次加载后跑马灯失效,后面继续展示出来是正常的,然后通过这种方式解决了问题。

九、部分手机使用全屏DialogFragments时会被状态栏覆盖

解决方案:

  • (1) 在父布局增加fitSystemWindow属性,实测可以:
android:fitsSystemWindows="true"

十、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>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值