onWindowFocusChanged 正确的使用方式

330 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情

背景

今天接到一个需求,需要在以前的列表部分添加一个栏目,同时要一个事件,效果图如下:

image.png 心想这个不是分分钟搞定吗,结果不到十分钟搞完,搞完之后自己一测试,直接懵逼了,怎么我操作我的按钮把别的栏目的状态也改了,我回过头仔细看一下源码

override fun onWindowFocusChanged(hasFocus: Boolean) {
    super.onWindowFocusChanged(hasFocus)
    initSwitchView()
}

private fun initSwitchView() {
    var isWriteInvoice = AppPrefsUtils.getBoolean(BaseConstant.SP_IS_WRITE_INVOICE, true)
    var isPrintInvoice = AppPrefsUtils.getBoolean(BaseConstant.SP_IS_PRINT_INVOICE, true)
    var isPrintInvoiceDetail =
        AppPrefsUtils.getBoolean(BaseConstant.SP_IS_PRINT_INVOICE_DETAIL, true)

    var isDeliveryWriteInvoice =
        AppPrefsUtils.getBoolean(BaseConstant.SP_IS_DELIVERY_WRITE_INVOICE, true)
    cmvSwitchWriteInvoice.setRightSwitchBtnStatus(isWriteInvoice)

    cmvDeliveryWriteInvoice.setRightSwitchBtnStatus(isDeliveryWriteInvoice)

    cmvSwitchPrintInvoice.setRightSwitchBtnStatus(isPrintInvoice)
    cmvSwitchPrintDetail.setRightSwitchBtnStatus(isPrintInvoiceDetail)
}

怎么初始化给控件赋值放到onWindowFocusChanged这个方法里面,难怪会改变别的栏目的状态.

继续进入对应控件的方法中去分析,发现一个问题

public void setCurrentState(Boolean isOpen){
   if(isOpen&&mCurrentState==State.CLOSE){
      mCurrentState = State.OPEN;
      animate(mStrokeRadius, BALL_X_RIGHT, greyColor, greenColor);
   }else if(!isOpen&&mCurrentState==State.OPEN){
      mCurrentState = State.CLOSE;
      animate(BALL_X_RIGHT, mStrokeRadius, greenColor, greyColor);
   }
}

这个BALL_X_RIGHT的值是需要等onSizeChanged执行完之后才有值

protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   mViewHeight = h;
   mViewWidth = w;

   // 默认描边宽度是控件宽度的1/30, 比如控件宽度是120dp, 描边宽度就是4dp
   switchViewStrockWidth = w * 1.0f / 30;

   mStrokeRadius = mViewHeight / 2;
   mSolidRadius = (mViewHeight - 2 * switchViewStrockWidth) / 2;
   BALL_X_RIGHT = mViewWidth - mStrokeRadius;


   mSwitchBallx = mStrokeRadius;
   mBgStrokeRectF = new RectF(0, 0, mViewWidth, mViewHeight);

}

感觉有可能是控件还没有绘制完导致设置状态的显示有问题.在解决问题之前我们要看看onWindowFocusChanged的作用

onWindowFocusChanged

会调用onWindowFocusChanged方法的场景:

  • 在Activity窗口获得或失去焦点时被调用,例如创建时首次呈现在用户面前;
  • 当前Activity被其他Activity覆盖;
  • 当前Activity转到其他Activity或按Home键回到主屏,自身退居后台;
  • 用户退出当前Activity。

以上几种情况都会调用,并且当Activity被创建时是在onResume之后被调用,当Activity被覆盖或者退居后台或者当前Activity退出时,它是在onPause之后被调用。

这个方法在某种场合下还是很有用的,例如程序启动时想要获取视特定视图组件的尺寸大小,在onCreate中可能无法取到,因为窗口Window对象还没创建完成,这个时候我们就需要在onWindowFocusChanged里获取。

解决办法

现在需要解决的问题在UI测量完成之后调用给控件设置的方法

可以通过ViewTreeObserver这个类来监听addOnPreDrawListener或者addOnGlobalLayoutListener事件解决这个问题,我选择使用addOnPreDrawListener这个方法来处理

btSave.viewTreeObserver.addOnGlobalLayoutListener {
    initSwitchView()
}

备注:btSave是xml里面的任意一个控件