Android开发踩坑记

DialogFragment在新建后,即便设置布局宽度为MATCH_PARENT,实际展示出来的效果还是与屏幕有一定的边距,想要使宽度充满屏幕,需要进行以下操作

1. 在DialogFragment的onStart方法中,加入如下代码:

        dialog?.setCancelable(false)
        dialog?.setCanceledOnTouchOutside(false)
        val dm = DisplayMetrics()
        activity?.windowManager?.defaultDisplay.getMetrics(dm)
        dialog?.window?.setLayout(dm.widthPixels * 1, dm.heightPixels * 1)

2. 在res/values/styles.xml文件中,新增如下样式:

    <style name="MatchWidthDialog" parent="@style/Theme.AppCompat.Dialog">
        <item name="android:windowCloseOnTouchOutside">true</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>

3. 在DialogFragment的onCreate方法中,加入如下代码:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setStyle(STYLE_NO_TITLE, R.style.MatchWidthDialog)
    }


 

RecyclerView中notifyItemRemoved的一个坑

我们为了移除RecycleView的某一项,会用RecycleView的notifyItemRemoved(int position)方法,但是需要注意的是:
1、用该方法之后并不会刷新Item,也就是说不会重新bind数据,那么position也就没有刷新,每个Item对应的position还是原来的那个,那就会有问题,比如现在只剩下3个Item,而我们点击删除最后一个Item的时候,它的position是原来的position4,那么实际上就是调用RecycleView的notifyItemRemoved(4),就会出现越界。

那我们该怎么做呢?
1、删除之后重新:notifyDataSetChanged();,但是这样就没有删除动画。

2、使用notifyItemRangeChanged(int positionStart, int itemCount)

这个方法,是通知所有观察者: 从positinStart开始的itemCount这些个item已经改变了,与notifyItemRangeChanged(position, itemCount, null)等价
positionStart : 是从界面哪个位置的Item开始变化,比如你点击界面上的第二个ItemView positionStart是1
itemCount : 是已经发生变化的item的个数(包括自己,即正在点击这个),比如,你点击界面上的第二个ItemView,position [1,9] 发生变化,共计9个,因此我们计算是list.size() - position 

此时使用:

notifyItemRemoved(position);

notifyItemRangeChanged(position, mList.size() - position);

注意如果有headView的话还需要将position加上对应的headView数目,比如有一个headView,那么就需要:

notifyItemRemoved(position + 1);

notifyItemRangeChanged(position + 1, mList.size() - position);

1. Unable to add window --token null is not valid; is your activity running
$BadTokenException: Unable to add window -- 
token null is not valid; is your activity running 
 
E/AndroidRuntime(1412): at android.view.ViewRootImpl.setView(ViewRootImpl.java:538) 
......
该异常多见于Popup Widow组件的使用中抛出

原因:错误在PopupWindow.showAtLocation(findViewById(R.id.main), Gravity.BOTTOM,0,0); popwindow必须依附于某一个view,而在oncreate中view还没有加载完毕,必须要等activity的生命周期函数全部执行完毕,你需要依附的view加载好后才可以执行popwindow。

解决办法:showAtLocation()函数可以这样改

//修正后代码
findviewById(R.id.mView).post(new Runnable() {
                        @Override
                        public void run() {
                                popwindow.showAtLocation(mView, Gravity.CENTER, 0, 0);
 
                        }
                });
总结: PopupWindow必须在某个事件中显示或者是开启一个新线程去调用,不能直接在onCreate方法中显示一个Popupwindow,否则永远会有以上的错误。

参考:popupwindow - Problems creating a Popup Window in Android Activity - Stack Overflow

2. Unable to add window --token null is not for an applicaiton
$BadTokenException: Unable to add window -- 
token null is not for an application 
 
E/AndroidRuntime(1412): at android.view.ViewRootImpl.setView(ViewRootImpl.java:538) 
......
该异常多见于AlertDialog组件的使用中抛出。

//抛异常代码
new AlertDialog.Builder(getApplicationContext())  //不能用getApplicationContext()
            .setIcon(android.R.drawable.ic_dialog_alert)  
            .setTitle("Warnning")  
            .setPositiveButton("Yes", positiveListener)
            .setNegativeButton(  "No", negativeListener)
            .create().show(); 
原因:导致报这个错是在于new AlertDialog.Builder(mcontext),虽然这里的参数是AlertDialog.Builder(Context context),但我们不能使用getApplicationContext()获得的Context,而必须使用Activity,因为只有一个Activity才能添加一个窗体。

解决方法:将new AlertDialog.Builder(Context context)中的参数用Activity.this(Activity 是你的 Activity 的名称)或者getActivity()来填充就可以正确的创建一个Dialog了

//修正后代码
new AlertDialog.Builder(this)  //this可以替换为MainActivity.this或getActivity()
            .setIcon(android.R.drawable.ic_dialog_alert)  
            .setTitle("Warnning")  
            .setPositiveButton("Yes", positiveListener)
            .setNegativeButton(  "No", negativeListener)
            .create().show(); 
参考:java - Android custom dialog gives an error - Stack Overflow

3. Unable to add window --token android.os.BindeProxy@XXX is not valid; is your activity running
android.view.WindowManager$BadTokenException: Unable to add window -- 
token android.os.BinderProxy@4250d6d8 is not valid; is your activity running?
    at android.view.ViewRootImpl.setView(ViewRootImpl.java:698)
   
    ......
    at dalvik.system.NativeStart.main(Native Method)
原因:从错误信息我们也可以明白其原因,此问题根本原因就是由于将要弹出的dialog所要依附的View已经不存在导致的。当界面销毁后再弹出来;或者界面跳转时我们的view发生改变,dialog依附的context发生变化或者界面未运行了。

解决方法:界面已经销毁引起的错误就只能判断界面是否存在然后再弹出了。

//修正后代码
if(!isFinishing()) {
     alert.show();
 }
参考:Unable to add window token android.os.BinderProxy@4250d6d8 is not valid; is your activity running? - Stack Overflow

4. Unable to add window --token android.app.LocalActivityManager$LocalActivityRecord @XXX is not vaild; is your activity running
android.view.WindowManager$BadTokenException: Unable to add window --
token android.app.LocalActivityManager$LocalActivityRecord@43e5b158
is not valid; is your activity running?

//异常代码
TipDialog dialog = new TipDialog(XXX.this) ;
原因:因为new对话框的时候,参数context 指定成了this,即指向当前子Activity的context。但子Activity是动态创建的,不能保证一直存在。其父Activity的context是稳定存在的,所以有下面的解决办法.

解决方法:将context替换为getParent()即可。 注意:要创建dialog对象,上下文环境必须是activity,同时若ActivityGroup中嵌套ActivityGroup,嵌套多少就该使用多少个getParent()。

//修正后代码,只有最多一个parent的情形
TipDialog dialog = new TipDialog(getParent()) ;
 
//修正后代码,适用于一个或多个parent的情形
Activity activity = TestActivity.this;  
while (activity.getParent() != null) {  
    activity = activity.getParent();  
 }  
              
TipDialog dialog = new TipDialog(activity) ;  
 

5、当出现下面错误时候
    08-21 03:43:16.679: E/AndroidRuntime(1087): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.fragment/com.example.fragment.MainActivity}:
android.view.InflateException: Binary XML file line #5: Error inflating class fragment
    肯定是那个MainActivity.xml文件出了问题,就在第五行,我用的fragment,里面的一个class="com.sss.TitleFragment" 原来是这个写错了,
    那个包名写错了,改了就好,然后再xml里面写时候要注意是<fragment/>第一个是小写,不是大写,要记住


6、mTabWeixin.setOnClickListener((OnClickListener) this);  这样写会报错
08-21 04:06:16.920: E/AndroidRuntime(1274): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.fragment/com.example.fragment.MainActivity}: java.lang.ClassCastException: com.example.fragment.MainActivity cannot be cast to android.view.View$OnClickListener
 
 后来改成mTabWeixin.setOnClickListener(new OnClickListener(){ @Override 
        public void onClick(View arg0) {
             // TODO Auto-generated method stub
              stopService(intent);
          }});就可以了

ation"

7、RecyclerView 在 ConstraintLayout 中超出边界

1️⃣ Constraint 约束不完整

在 ConstraintLayout 中,如果子控件没有被水平和垂直方向同时约束
就会导致它的 wrap_content 尺寸无法正确计算,从而可能“超出”父布局范围

2️⃣ 使用 wrap_content 导致宽度不受控

RecyclerView 的内容是动态的,
wrap_content 在计算时可能会尝试“容纳所有子项”,
因此容易撑大,超出预期范围。

3️⃣ 外层 ConstraintLayout 使用 wrap_content

你外层的 ConstraintLayout :

android:layout_width="wrap_content" android:layout_height="wrap_content"

➡️ 这意味着父容器本身会根据子控件尺寸扩张,
当 RecyclerView 过宽时,父布局也会被“撑开”,造成视觉上“超出边界”。

8、Java调用kotlin扩展函数方法:

Kotlin扩展函数在编译后会转换为接收者作为第一个参数的静态方法
fun BaseVbFragment<*, *>.hideStatus() 变成 public static void hideStatus(BaseVbFragment<?, ?> receiver)
因此在Java中需要显式传递接收者对象作为第一个参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值