一个View如何实现onClick、onLongClick和onTouch
How to implement onClick, onLongClick and onTouch to one View
我有一个视图(它是 RecyclerView 中的 ViewHolder,但我认为它不会有任何区别)实现了 onClick()
、onLongClick()
和 onTouch()
。我遇到的问题是 longClick
永远不会被执行。我在其他问题中读到了它,但我不知道该怎么做以及我应该在什么时候 return true
\ false
in onTouch()
。这是我的代码;
ViewHolder class 中的函数 bind()
实现了 View.OnTouchListener
:
binding.background.setOnClickListener {
Log.d("Click")
}
binding.background.setOnLongClickListener {
Log.d("Long Click")
}
binding.background.setOnTouchListener(this) // here I see warning "Custom view `ConstraintLayout` has setOnTouchListener called on it but does not override performClick"
覆盖 onTouch()
override fun onTouch(v: View?, event: MotionEvent?): Boolean
{
return when (event!!.action)
{
MotionEvent.ACTION_DOWN ->
{
x1 = event.x
true
}
MotionEvent.ACTION_UP ->
{
x2 = event.x
return if (abs(x2 - x1) > MIN_DISTANCE)
{
Log.d("Was swiped")
true
}
else
{
Log.d("Wasn't swiped")
v?.performClick()
false
}
}
else -> false
}
}
此函数仅检测视图上的幻灯片。我应该在 onTouch()
中更改什么以使 longClick
正常工作?另外,有没有更简单的方法来检测 RecyclerView 项目上的幻灯片?
尝试在 onTouchEvent 视图中使用 GestureDetector,如下所示
final GestureDetector longPressGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent e) {
Log.d(TAG, "onLongPress -> long press detected");
}
});
public boolean onTouchEvent(MotionEvent event) {
return longPressGestureDetector.onTouchEvent(event); //Will handle gesture API calls. The Gesture Detector can be used for Swipe actions as well.
};
如果有人遇到这样的问题我是这样解决的:
(它是回收器视图项目,可以左右移动或滚动)
ViewHolder 字段:
private var xStart = 0F
private var lastY = 0F
private var yStart = 0F
private val handler: Handler = Handler()
private var isLongClickCanceled = false
private var wasLongClicked = false
private var startScrolling = false
private var status = 0
...
private const val LONG_CLICK_TIME = 700L
private const val CLICK_DISTANCE = 75
private const val PANEL_SIZE = 125
触控功能
override fun onTouch(v: View?, event: MotionEvent?): Boolean
{
if (v != null && event != null)
{
v.parent.requestDisallowInterceptTouchEvent(true)
when (event.action)
{
MotionEvent.ACTION_DOWN ->
{
Log.d("Down")
xStart = event.x
yStart = event.y
isLongClickCanceled = false
wasLongClicked = false
startScrolling = false
handler.postDelayed({ //long click
wasLongClicked = true
val leftPanel = (v as ViewGroup).getChildAt(0)
val rightPanel = v.getChildAt(1)
val paramsLP = leftPanel.layoutParams
val paramsRP = rightPanel.layoutParams
paramsLP.width = 1
paramsRP.width = 1
leftPanel.layoutParams = paramsLP
rightPanel.layoutParams = paramsRP
v.performLongClick()
}, LONG_CLICK_TIME)
xStart = event.x
}
MotionEvent.ACTION_UP ->
{
Log.d("Up")
handler.removeCallbacksAndMessages(null)
if (!startScrolling && !isLongClickCanceled && !wasLongClicked)
{
if ((event.eventTime - event.downTime) < LONG_CLICK_TIME) //click
{
Log.d("Status $status")
if (status == 0)
{
v.performClick()
}
else
{
val leftPanel = (v as ViewGroup).getChildAt(0)
val rightPanel = v.getChildAt(1)
val paramsLP = leftPanel.layoutParams
val paramsRP = rightPanel.layoutParams
paramsLP.width = 1
paramsRP.width = 1
leftPanel.layoutParams = paramsLP
rightPanel.layoutParams = paramsRP
status = 0
selectCar(binding.car!!.carID, false)
}
}
}
else
{
val leftPanel = (v as ViewGroup).getChildAt(0)
val rightPanel = v.getChildAt(1)
val paramsLP = leftPanel.layoutParams
val paramsRP = rightPanel.layoutParams
when
{
status > (PANEL_SIZE / 2) ->
{
paramsLP.width = PANEL_SIZE
paramsRP.width = 1
selectCar(binding.car!!.carID, false)
}
status < -(PANEL_SIZE / 2) ->
{
paramsRP.width = PANEL_SIZE
paramsLP.width = 1
selectCar(binding.car!!.carID, true)
}
else ->
{
paramsLP.width = 1
paramsRP.width = 1
status = 0
selectCar(binding.car!!.carID, false)
}
}
leftPanel.layoutParams = paramsLP
rightPanel.layoutParams = paramsRP
}
}
MotionEvent.ACTION_MOVE ->
{
if (startScrolling)
{
scroll((lastY - event.rawY).toInt())
lastY = event.rawY
}
else
{
if (!wasLongClicked)
{
if (isLongClickCanceled)
{
val deltaX = (event.x - xStart).toInt()
if (abs(deltaX) > 75)
{
val leftPanel = (v as ViewGroup).getChildAt(0)
val rightPanel = v.getChildAt(1)
val paramsLP = leftPanel.layoutParams
val paramsRP = rightPanel.layoutParams
if (deltaX > 0)
{
status = (deltaX - CLICK_DISTANCE)
paramsLP.width = min(status, PANEL_SIZE)
paramsRP.width = 1
}
else if (deltaX < 0)
{
status = (deltaX + CLICK_DISTANCE)
paramsRP.width = min(-status, PANEL_SIZE)
paramsLP.width = 1
}
leftPanel.layoutParams = paramsLP
rightPanel.layoutParams = paramsRP
}
}
else if (abs(yStart - event.y) > CLICK_DISTANCE)
{
lastY = event.rawY
startScrolling = true
handler.removeCallbacksAndMessages(null)
}
else if (!isLongClickCanceled && abs(xStart - event.x) >= CLICK_DISTANCE)
{
isLongClickCanceled = true
handler.removeCallbacksAndMessages(null)
}
}
}
}
}
}
return true
}
传递给 ViewHolder 的滚动函数
{ dy -> binding.recViewCar.scrollBy(0, dy) }
这段代码可以执行Click、LongClick和Touching视图。当用户长按时,不会执行移动动作。将手指放在屏幕上并移动超过 CLICK_DISTANCE
后,点击和长按不会被执行
我有一个视图(它是 RecyclerView 中的 ViewHolder,但我认为它不会有任何区别)实现了 onClick()
、onLongClick()
和 onTouch()
。我遇到的问题是 longClick
永远不会被执行。我在其他问题中读到了它,但我不知道该怎么做以及我应该在什么时候 return true
\ false
in onTouch()
。这是我的代码;
ViewHolder class 中的函数 bind()
实现了 View.OnTouchListener
:
binding.background.setOnClickListener {
Log.d("Click")
}
binding.background.setOnLongClickListener {
Log.d("Long Click")
}
binding.background.setOnTouchListener(this) // here I see warning "Custom view `ConstraintLayout` has setOnTouchListener called on it but does not override performClick"
覆盖 onTouch()
override fun onTouch(v: View?, event: MotionEvent?): Boolean
{
return when (event!!.action)
{
MotionEvent.ACTION_DOWN ->
{
x1 = event.x
true
}
MotionEvent.ACTION_UP ->
{
x2 = event.x
return if (abs(x2 - x1) > MIN_DISTANCE)
{
Log.d("Was swiped")
true
}
else
{
Log.d("Wasn't swiped")
v?.performClick()
false
}
}
else -> false
}
}
此函数仅检测视图上的幻灯片。我应该在 onTouch()
中更改什么以使 longClick
正常工作?另外,有没有更简单的方法来检测 RecyclerView 项目上的幻灯片?
尝试在 onTouchEvent 视图中使用 GestureDetector,如下所示
final GestureDetector longPressGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent e) {
Log.d(TAG, "onLongPress -> long press detected");
}
});
public boolean onTouchEvent(MotionEvent event) {
return longPressGestureDetector.onTouchEvent(event); //Will handle gesture API calls. The Gesture Detector can be used for Swipe actions as well.
};
如果有人遇到这样的问题我是这样解决的:
(它是回收器视图项目,可以左右移动或滚动)
ViewHolder 字段:
private var xStart = 0F
private var lastY = 0F
private var yStart = 0F
private val handler: Handler = Handler()
private var isLongClickCanceled = false
private var wasLongClicked = false
private var startScrolling = false
private var status = 0
...
private const val LONG_CLICK_TIME = 700L
private const val CLICK_DISTANCE = 75
private const val PANEL_SIZE = 125
触控功能
override fun onTouch(v: View?, event: MotionEvent?): Boolean
{
if (v != null && event != null)
{
v.parent.requestDisallowInterceptTouchEvent(true)
when (event.action)
{
MotionEvent.ACTION_DOWN ->
{
Log.d("Down")
xStart = event.x
yStart = event.y
isLongClickCanceled = false
wasLongClicked = false
startScrolling = false
handler.postDelayed({ //long click
wasLongClicked = true
val leftPanel = (v as ViewGroup).getChildAt(0)
val rightPanel = v.getChildAt(1)
val paramsLP = leftPanel.layoutParams
val paramsRP = rightPanel.layoutParams
paramsLP.width = 1
paramsRP.width = 1
leftPanel.layoutParams = paramsLP
rightPanel.layoutParams = paramsRP
v.performLongClick()
}, LONG_CLICK_TIME)
xStart = event.x
}
MotionEvent.ACTION_UP ->
{
Log.d("Up")
handler.removeCallbacksAndMessages(null)
if (!startScrolling && !isLongClickCanceled && !wasLongClicked)
{
if ((event.eventTime - event.downTime) < LONG_CLICK_TIME) //click
{
Log.d("Status $status")
if (status == 0)
{
v.performClick()
}
else
{
val leftPanel = (v as ViewGroup).getChildAt(0)
val rightPanel = v.getChildAt(1)
val paramsLP = leftPanel.layoutParams
val paramsRP = rightPanel.layoutParams
paramsLP.width = 1
paramsRP.width = 1
leftPanel.layoutParams = paramsLP
rightPanel.layoutParams = paramsRP
status = 0
selectCar(binding.car!!.carID, false)
}
}
}
else
{
val leftPanel = (v as ViewGroup).getChildAt(0)
val rightPanel = v.getChildAt(1)
val paramsLP = leftPanel.layoutParams
val paramsRP = rightPanel.layoutParams
when
{
status > (PANEL_SIZE / 2) ->
{
paramsLP.width = PANEL_SIZE
paramsRP.width = 1
selectCar(binding.car!!.carID, false)
}
status < -(PANEL_SIZE / 2) ->
{
paramsRP.width = PANEL_SIZE
paramsLP.width = 1
selectCar(binding.car!!.carID, true)
}
else ->
{
paramsLP.width = 1
paramsRP.width = 1
status = 0
selectCar(binding.car!!.carID, false)
}
}
leftPanel.layoutParams = paramsLP
rightPanel.layoutParams = paramsRP
}
}
MotionEvent.ACTION_MOVE ->
{
if (startScrolling)
{
scroll((lastY - event.rawY).toInt())
lastY = event.rawY
}
else
{
if (!wasLongClicked)
{
if (isLongClickCanceled)
{
val deltaX = (event.x - xStart).toInt()
if (abs(deltaX) > 75)
{
val leftPanel = (v as ViewGroup).getChildAt(0)
val rightPanel = v.getChildAt(1)
val paramsLP = leftPanel.layoutParams
val paramsRP = rightPanel.layoutParams
if (deltaX > 0)
{
status = (deltaX - CLICK_DISTANCE)
paramsLP.width = min(status, PANEL_SIZE)
paramsRP.width = 1
}
else if (deltaX < 0)
{
status = (deltaX + CLICK_DISTANCE)
paramsRP.width = min(-status, PANEL_SIZE)
paramsLP.width = 1
}
leftPanel.layoutParams = paramsLP
rightPanel.layoutParams = paramsRP
}
}
else if (abs(yStart - event.y) > CLICK_DISTANCE)
{
lastY = event.rawY
startScrolling = true
handler.removeCallbacksAndMessages(null)
}
else if (!isLongClickCanceled && abs(xStart - event.x) >= CLICK_DISTANCE)
{
isLongClickCanceled = true
handler.removeCallbacksAndMessages(null)
}
}
}
}
}
}
return true
}
传递给 ViewHolder 的滚动函数
{ dy -> binding.recViewCar.scrollBy(0, dy) }
这段代码可以执行Click、LongClick和Touching视图。当用户长按时,不会执行移动动作。将手指放在屏幕上并移动超过 CLICK_DISTANCE
后,点击和长按不会被执行