我的布局很复杂,导致我创建了一些自定义组件。该应用程序要求我需要添加房间数量,并且在每个房间中,选择每个房间的成人数量和儿童数量。对于每个房间,我需要允许每个房间有多个孩子,但是对于每个18岁以下的孩子,我需要从微调器中了解他们的年龄。我可以全天添加和删除房间,以及添加/删除孩子。在每个房间中,我可以为每个孩子添加孩子和一个年龄下拉列表并将其删除。所有这些都没问题。当我尝试点击任何年龄选择器下拉菜单时,都会收到错误消息。我怀疑它与上下文和活动有关,但不确定在哪里。
我有一个片段来控制所有这些,它控制房间步进器并添加/删除房间。
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。它的工作是添加带有成人步进器和儿童步进器的“房间”,或将其删除
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无效;您的活动正在进行吗?
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
}
}
}
}
任何想法为何触摸任何下拉菜单都会给我错误?
答案 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 )
之后,一切正常。