android.view.WindowManager $ BadTokenException:无法添加窗口-令牌null无效;自定义组件

时间:2019-05-31 19:42:29

标签: android

我的布局很复杂,导致我创建了一些自定义组件。该应用程序要求我需要添加房间数量,并且在每个房间中,选择每个房间的成人数量和儿童数量。对于每个房间,我需要允许每个房间有多个孩子,但是对于每个18岁以下的孩子,我需要从微调器中了解他们的年龄。我可以全天添加和删除房间,以及添加/删除孩子。在每个房间中,我可以为每个孩子添加孩子和一个年龄下拉列表并将其删除。所有这些都没问题。当我尝试点击任何年龄选择器下拉菜单时,都会收到错误消息。我怀疑它与上下文和活动有关,但不确定在哪里。

布局看起来像enter image description here

我有一个片段来控制所有这些,它控制房间步进器并添加/删除房间。

HotelSearchFragment

class SearchHotelsFragment : Fragment(), StepperView.StepperListener, CustomCalendarView.DayClickListener {


private var listener: OnSearchUpdateListener? = null
private lateinit var mViewModel: HotelRepositoryViewModel
private lateinit var mBinding: com.lixar.allegiant.databinding.FragmentSearchHotelsBinding

private lateinit var mRoomList: MutableList<HotelSearchRoomInput>
private lateinit var mRoomMgrList: MutableList<RoomGuestInputManager>

private var mRoomCount = 1;

interface OnSearchUpdateListener {
    fun onSearchUpdate()
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    mViewModel = activity?.let { ViewModelProviders.of(it).get(HotelRepositoryViewModel::class.java) }!!

}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_search_hotels, container, false)
    mBinding.handler = clickHandler
    mBinding.roomStepper.setCallback(this)
    setupInitialRoom()
    hideKeyboard()

    return mBinding.root
}

public fun getSearchCriteria(): HotelSearchInput {
    val mRoomData = HotelSearchInput.builder()
            .locationCode("")
            .from(mViewModel.fromDateLocal)
            .to(mViewModel.toDateLocal)
            .rooms(mRoomList)
            .build()

    return mRoomData
}

private fun setupInitialRoom() {
    val roomMgr = activity?.applicationContext?.let { RoomGuestInputManager(it) }
    roomMgr?.setActivity(activity!!)
    roomMgr?.setRoomNumber(mRoomCount)
    mBinding.roomExpansionZone.addView(roomMgr)
    mRoomMgrList = listOf(roomMgr!!).toMutableList()
    mRoomList = listOf(roomMgr.mRoom!!).toMutableList()
}

  override fun onDecrement(id: Int, count: Int) {
    when (id) {
        R.id.room_stepper -> {
            val lastRoomMgr = mRoomMgrList.get(mRoomCount-1)
            mBinding.roomExpansionZone.removeView(lastRoomMgr)
            mRoomMgrList.remove(lastRoomMgr)
            mRoomList.remove(lastRoomMgr.mRoom)
            mRoomCount--
            // once we have ONLY one room grow the left side to hider room numbers
            if (mRoomCount == 1) {
                val room = mRoomMgrList.get(0)
                room.adjustRoomOnRemoval()
            }
        }
    }
}

override fun onIncrement(id: Int, count: Int) {
    when (id) {
        R.id.room_stepper -> {
            val roomMgr = activity?.applicationContext?.let { RoomGuestInputManager(it) }
            roomMgr?.setActivity(activity!!)
            mRoomCount++
            roomMgr?.setRoomNumber(mRoomCount)
            mBinding.roomExpansionZone.addView(roomMgr)
            mRoomList.add(roomMgr?.mRoom!!)
            mRoomMgrList.add(roomMgr)
            // once we have more than one room shrink the left side to allow for room numbers
            if (mRoomCount > 1) {
                val room = mRoomMgrList.get(0)
                room.adjustRoomOnAddition()
            }
        }
    }
}

}

在该片段中,我合并了一个自定义组件RoomGuestInputManager。它的工作是添加带有成人步进器和儿童步进器的“房间”,或将其删除

RoomGuestInputManager

class RoomGuestInputManager @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr), StepperView.StepperListener {
val FULLSIZE_LEFT = 0.0f
val FULLSIZE_RIGHT = 0.5f
val SIZE_LEFT = 0.2f
val SIZE_RIGHT = 0.6f

val mRoom = HotelSearchRoomInput.builder()
        .adults(1)   // there must be at least one adult per room - sorry kids
        .childrenAges(listOf(0))
        .build()
private lateinit var mBinding: RoomSelectLayoutBinding
private var mNumChildren = 0
private var mNumChildMgrs = 0
private var mActivity: Activity? = null
private lateinit var mChildMgrList: MutableList<RoomChildManager>

init {
    mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.room_select_layout, this, true)

    mBinding.adultStepper.setCallback(this)
    mBinding.childStepper.setCallback(this)
    mBinding.roomNumber.visibility = View.GONE

}

public fun setRoomNumber(room: Int) {
    val label = context.resources.getString(R.string.room_num)
    if (room == 1) {
        mBinding.roomNumber.visibility = View.GONE
        mBinding.guidelineleft.setGuidelinePercent(FULLSIZE_LEFT)
        mBinding.guidelineright.setGuidelinePercent(FULLSIZE_RIGHT)
    } else {
        mBinding.roomNumber.visibility = View.VISIBLE
        mBinding.roomNumber.text = String.format(label, room)
        mBinding.guidelineleft.setGuidelinePercent(SIZE_LEFT)
        mBinding.guidelineright.setGuidelinePercent(SIZE_RIGHT)
    }
}

public fun setActivity(activity: Activity) {
    mActivity = activity
}

public fun adjustRoomOnAddition() {
    mBinding.roomNumber.visibility = View.VISIBLE
    mBinding.guidelineleft.setGuidelinePercent(SIZE_LEFT)
    mBinding.guidelineright.setGuidelinePercent(SIZE_RIGHT)
}

public fun adjustRoomOnRemoval() {
    mBinding.roomNumber.visibility = View.GONE
    mBinding.guidelineleft.setGuidelinePercent(FULLSIZE_LEFT)
    mBinding.guidelineright.setGuidelinePercent(FULLSIZE_RIGHT)
}

public fun getGuestInfoThisRoom(): HotelSearchRoomInput {
    return mRoom
}

private fun setupNewChild() {
    val childMgr = RoomChildManager(mActivity?.applicationContext!!)
    mBinding.childExpansionZone.addView(childMgr)
    childMgr.setInitialChild(mNumChildren)
    if (mNumChildren == 1) {
        mChildMgrList = mutableListOf(childMgr)
    } else {
        mChildMgrList.add(childMgr)
    }
    mNumChildMgrs++
}

override fun onDecrement(id: Int, count: Int) {
    when (id) {
        R.id.adult_stepper -> {
            mRoom.adults().minus(1)
        }
        R.id.child_stepper -> {
            // depending on how many kids there are now, do we remove a layout or just make gone?
            if (mNumChildren == 1) {
                mBinding.childExpansionZone.removeAllViews()
                mChildMgrList.clear()
                mNumChildren = 0
            } else if (mNumChildren.rem(2) == 0) {
                // remove the secondary ageSelector
                val childMgr = mChildMgrList.get(mNumChildMgrs - 1)
                childMgr.removeChild()
                mNumChildren--
            } else {
                // remove the entire 2-selector layout
                val childMgr = mChildMgrList.get(mNumChildMgrs - 1)
                childMgr.removeChild()
                mChildMgrList.removeAt(mNumChildMgrs - 1)
                mBinding.childExpansionZone.removeView(childMgr)
                mNumChildMgrs--
                mNumChildren--
            }
        }
    }
}

override fun onIncrement(id: Int, count: Int) {
    when (id) {
        R.id.adult_stepper -> {
            mRoom.adults().plus(1)
        }
        R.id.child_stepper -> {
            // depending on how many kids there are now, do we add a layout
            if (mNumChildren == 0) {
                mNumChildren = 1
                setupNewChild()
            } else if (mNumChildren.rem(2) == 0) {
                mNumChildren++
                setupNewChild()
            } else {

                // expose 2nd selector
                val childMgr = mChildMgrList.get(mNumChildMgrs - 1)
                mNumChildren++
                childMgr.addChild(mNumChildren)
            }
        }
    }
}

该类控制第二个自定义组件RoomChildManager。工作是管理每个房间的年龄选择器,并确保我们有每个孩子的年龄。它成对添加/删除下拉选择器,以满足在触摸时标题出现错误的设计:         android.view.WindowManager $ BadTokenException:无法添加窗口-令牌null无效;您的活动正在进行吗?

RoomChildManager

class RoomChildManager @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr), DropdownAgeSelectView.AgeSelectListener {

private lateinit var mBinding: ChildAgeselectLayoutBinding
init {

    mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.child_ageselect_layout, this, true)

    mBinding.child2Age.visibility = View.GONE
    mBinding.child2Num.visibility = View.GONE
    mBinding.child1Age.setCallback(this)
    mBinding.child2Age.setCallback(this)

}

public fun setInitialChild(num: Int) {
    mBinding.child1Num.text = String.format(context.resources.getString(R.string.child_age), num)
}

public fun addChild(num: Int) {
    mBinding.child2Age.visibility = View.VISIBLE
    mBinding.child2Num.visibility = View.VISIBLE
    mBinding.child2Num.text = String.format(context.resources.getString(R.string.child_age), num)
}

public fun removeChild() {
    mBinding.child2Age.visibility = View.GONE
    mBinding.child2Num.visibility = View.GONE
}

public fun getChildAges(): List<Int> {
    return listOf(mBinding.child1Age.getSelection(), mBinding.child2Age.getSelection())
}

override fun onAgeSelect(id: Int, age: Int) {
    when (id) {
        R.id.child1_age -> {
            // do something
        }
        R.id.child2_age -> {
            // do something
        }
    }
}
}

任何想法为何触摸任何下拉菜单都会给我错误?

1 个答案:

答案 0 :(得分:0)

我知道这将是一个上下文问题,只是从错误消息中无法确切知道它所击中的位置。

事实证明,解决此问题的方法在于将上下文从HotelSearchFragment传递到RoomGuestInputManager的方式。我以为我可以得到片段的上下文就足够了。不,显然,他们会感到困惑,所以我必须准确说明活动的位置。在HotelSearchFragment中,我们将其设置为:

    val hotelActivity = context as HotelSelectActivity

然后我们可以像这样调用RoomInputGuestManager:

    val roomMgr = RoomGuestInputManager(hotelActivity)

,当我们创建子管理器时,也在RoomInputGuestManager内部

    val hotelActivity = context as HotelSelectActivity
    val childMgr = RoomChildManager(hotelActivity )

之后,一切正常。