如何在Android中正确支持点击可选文本视图
我有一个带有可选文本视图的线性布局如何在Android中正确支持点击可选文本视图,android,textview,android-linearlayout,android-event,Android,Textview,Android Linearlayout,Android Event,我有一个带有可选文本视图的线性布局 在LinearLayout上指定了单击侦听器 linear\u layout.setOnClickListener{ Toast.makeText(上下文,“单击线性布局”,Toast.LENGTH\u SHORT.show()) } 如果文本视图具有可选择的文本,则在单击文本视图时不会显示Toast。我尝试了一个解决方案,在通用函数中添加以下代码,用于所有文本视图 val textGestureDetector=GestureDetectorCompa
在LinearLayout上指定了单击侦听器
linear\u layout.setOnClickListener{
Toast.makeText(上下文,“单击线性布局”,Toast.LENGTH\u SHORT.show())
}
如果文本视图具有可选择的文本,则在单击文本视图时不会显示Toast。我尝试了一个解决方案,在通用函数中添加以下代码,用于所有文本视图
val textGestureDetector=GestureDetectorCompat(上下文,GestureDetector.SimpleOnGestureListener())
textGestureDetector.setOnDoubleTapListener(对象:GestureDetector.OnDoubleTapListener{
覆盖双点击(e:MotionEvent?):布尔值=false
override fun OnDoubleTapeEvent(e:MotionEvent?):布尔值=false
override fun OnSingleTapConfiged(e:MotionEvent?):布尔值{
textView.performClick()
返回真值
}
})
textView.setOnTouchListener{},事件->
textGestureDetector.onTouchEvent(事件)
假的
}
当我们在文本视图上指定click listener时,此解决方案确实有效,但当文本视图没有click listener时,click事件不会传播到线性布局
因为线性布局中可能有多个子文本视图,所以我不想显式地编写代码来在每个子文本单击时调用父文本的单击侦听器。有没有办法用通用解决方案解决此问题
编辑:
理想情况下,我希望创建一个自定义文本视图,该视图能够正确传播单击,而不管文本是否可选。这将有助于在我的整个应用程序中解决此问题。
正确传播单击意味着三件事:
- 双击文本视图选择文本
- 当文本视图具有click listener时,单击将调用它
- 当文本视图没有单击侦听器时,单击将调用具有该侦听器的祖先的单击侦听器。请注意,如果文本不可选择,则此场景已经运行良好
val textGestureDetector = GestureDetectorCompat(context, GestureDetector.SimpleOnGestureListener())
textGestureDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
override fun onDoubleTap(e: MotionEvent?): Boolean = false
override fun onDoubleTapEvent(e: MotionEvent?): Boolean = false
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
// pass the click event to your linear_layout
linearLayout.performClick()
return true
}
})
linear_layout.setOnClickListener {
Toast.makeText(context,"click just happened",Toast.LENGTH_LONG).show()
}
fun initTextViewSingleTapListener(root: ViewGroup, currentView: View, textGestureDetector: GestureDetectorCompat) {
if (currentView is TextView) {
currentView.setOnTouchListener { _, event ->
textGestureDetector.onTouchEvent(event)
false
}
return
}
if (currentView is ViewGroup) {
for (i in 0..currentView.childCount) {
val child = currentView.getChildAt(i)
if (child != null) {
initTextViewSingleTapListener(root, child, textGestureDetector)
}
}
}
}
文本视图
是文本可选择的,则单击该视图应被委派到线性布局
TextView
textGestureDetector
递归地设置到每个后代TextView
?在这种情况下,您不需要为每个子/后代手动执行此操作TextView
因此,首先,将onSingleTapConfirmed
中的事件委托给linear\u布局,如下所示:
val textGestureDetector = GestureDetectorCompat(context, GestureDetector.SimpleOnGestureListener())
textGestureDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
override fun onDoubleTap(e: MotionEvent?): Boolean = false
override fun onDoubleTapEvent(e: MotionEvent?): Boolean = false
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
// pass the click event to your linear_layout
linearLayout.performClick()
return true
}
})
linear_layout.setOnClickListener {
Toast.makeText(context,"click just happened",Toast.LENGTH_LONG).show()
}
fun initTextViewSingleTapListener(root: ViewGroup, currentView: View, textGestureDetector: GestureDetectorCompat) {
if (currentView is TextView) {
currentView.setOnTouchListener { _, event ->
textGestureDetector.onTouchEvent(event)
false
}
return
}
if (currentView is ViewGroup) {
for (i in 0..currentView.childCount) {
val child = currentView.getChildAt(i)
if (child != null) {
initTextViewSingleTapListener(root, child, textGestureDetector)
}
}
}
}
然后,将OnClickListener
添加到linear\u布局中,如下所示:
val textGestureDetector = GestureDetectorCompat(context, GestureDetector.SimpleOnGestureListener())
textGestureDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
override fun onDoubleTap(e: MotionEvent?): Boolean = false
override fun onDoubleTapEvent(e: MotionEvent?): Boolean = false
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
// pass the click event to your linear_layout
linearLayout.performClick()
return true
}
})
linear_layout.setOnClickListener {
Toast.makeText(context,"click just happened",Toast.LENGTH_LONG).show()
}
fun initTextViewSingleTapListener(root: ViewGroup, currentView: View, textGestureDetector: GestureDetectorCompat) {
if (currentView is TextView) {
currentView.setOnTouchListener { _, event ->
textGestureDetector.onTouchEvent(event)
false
}
return
}
if (currentView is ViewGroup) {
for (i in 0..currentView.childCount) {
val child = currentView.getChildAt(i)
if (child != null) {
initTextViewSingleTapListener(root, child, textGestureDetector)
}
}
}
}
最后,调用initTextViewSingleTapListener
(这将把您的自定义ontaListener
设置为TextView
,它是线性布局的后代)
可按以下方式实施:
val textGestureDetector = GestureDetectorCompat(context, GestureDetector.SimpleOnGestureListener())
textGestureDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
override fun onDoubleTap(e: MotionEvent?): Boolean = false
override fun onDoubleTapEvent(e: MotionEvent?): Boolean = false
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
// pass the click event to your linear_layout
linearLayout.performClick()
return true
}
})
linear_layout.setOnClickListener {
Toast.makeText(context,"click just happened",Toast.LENGTH_LONG).show()
}
fun initTextViewSingleTapListener(root: ViewGroup, currentView: View, textGestureDetector: GestureDetectorCompat) {
if (currentView is TextView) {
currentView.setOnTouchListener { _, event ->
textGestureDetector.onTouchEvent(event)
false
}
return
}
if (currentView is ViewGroup) {
for (i in 0..currentView.childCount) {
val child = currentView.getChildAt(i)
if (child != null) {
initTextViewSingleTapListener(root, child, textGestureDetector)
}
}
}
}
如果您的TextView
已经有一个侦听器,因此您不想替换它,那么只需添加一个类似的检查!currentView.hasOnClickListeners()
添加侦听器之前:
...
if (!currentView.hasOnClickListeners()) {
currentView.setOnTouchListener { _, event ->
textGestureDetector.onTouchEvent(event)
false
}
}
...
完整代码
请在下面找到完整的示例代码:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val textGestureDetector = GestureDetectorCompat(context, GestureDetector.SimpleOnGestureListener())
textGestureDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
override fun onDoubleTap(e: MotionEvent?): Boolean = false
override fun onDoubleTapEvent(e: MotionEvent?): Boolean = false
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
// pass the click event to the linear layout
linear_layout.performClick()
return true
}
})
linear_layout.setOnClickListener {
Toast.makeText(context,"click just happened",Toast.LENGTH_LONG).show()
}
initTextViewSingleTapListener(linear_layout, linear_layout, textGestureDetector)
}
fun initTextViewSingleTapListener(root: ViewGroup, currentView: View, textGestureDetector: GestureDetectorCompat) {
if (currentView is TextView) {
if (currentView.hasOnClickListeners()) {
// do nothing since your View already has a click listener on it.
return
}
currentView.setOnTouchListener { _, event ->
textGestureDetector.onTouchEvent(event)
false
}
return
}
if (currentView is ViewGroup) {
for (i in 0..currentView.childCount) {
val child = currentView.getChildAt(i)
if (child != null) {
initTextViewSingleTapListener(root, child, textGestureDetector)
}
}
}
}
更新
由于您的问题需求已经更改,我在这里添加了一个额外的解释
如果textSelectableTextView
应将单个点击事件委托给其最近的ViewGroup
祖先,则在initTextViewSingleTapListener()的
块中,如下所示:
if (currentView is ViewGroup) {
for (i in 0..currentView.childCount) {
val child = currentView.getChildAt(i)
if (child != null) {
initTextViewSingleTapListener(root, child, textGestureDetector)
}
}
}
将root
替换为currentView
,以便将所有单次点击事件委托给最近的ViewGroup
祖先,如下所示:
...
initTextViewSingleTapListener(currentView, child, textGestureDetector)
...