PopUpWindowはちょっとのダイアログや吹き出しなどを実装したいときに作りやすい。
KotlinでPopUpWindow書いている記事があまりないなと思い、勉強も兼ねて実装していたのだが、
ある画面が出て条件を満たす場合に出したい場合、そのままViewの上に描画しようとするとタイトル通りのエラー
原因としてはポップアップさせるタイミングが早すぎるので少しずらしてあげないといけない
そのため、
[1] ストーリーは違うが、ボタンが押された後にPopUpするとか、
[2] RxやAsync, Handler等で非同期処理
する必要がある
[1] については単純にButtonが押された場合にPopUpWindowを呼び出してあげればいい
[2] についてはこのように時間をずらして表示
private fun showPopUpUsingRx(popUp: PopUp) {
Completable.complete()
.delay(5, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnComplete { popUp.show(contact_button) }
.subscribe()
}
参考コードはこのような感じ
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showPopUpUsingRx(PopUp(this))
popup_container.setOnClickListener {
showPopUpUsingButton(PopUp(this))
}
}
private fun showPopUpUsingButton(popUp: PopUp) {
if (popUp.isShowing) {
popUp.dismiss()
popUp.show(contact_button)
} else {
popUp.show(contact_button)
}
}
private fun showPopUpUsingRx(popUp: PopUp) {
Completable.complete()
.delay(5, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnComplete { popUp.show(contact_button) }
.subscribe()
}
}
PopUp.kt
@SuppressLint("InflateParams")
class PopUp(private val context: Context) : PopupWindow() {
private val layoutInflater = LayoutInflater.from(context)
init {
contentView = layoutInflater.inflate(R.layout.popup_layout, null)
contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
isOutsideTouchable = true
isFocusable = true
contentView.popup.setOnClickListener {
dismiss()
}
setOnDismissListener {
Toast.makeText(context, "Popup closed", Toast.LENGTH_SHORT).show()
}
}
fun show(anchorView: View) {
showAsDropDown(anchorView, anchorView.measuredWidth / 4,
-anchorView.measuredHeight - calcNavHeight(), Gravity.NO_GRAVITY)
}
private fun calcNavHeight(): Int {
val resource = context.resources
val resourceId = resource.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) {
resource.getDimensionPixelOffset(resourceId)
} else {
0
}
}
}