Android - 如何检测屏幕上的触摸是 "scroll" 触摸?
Android - how to detect a touch on screen is a "scroll" touch?
我正在 Java 中创建一个 android 应用程序,其中屏幕周围有很多 <TextView>
,所有这些都定义了 onTouchListeners。它们被包裹在 <ScrollView>
中,因为它们占用的空间 space 多于屏幕上可用的空间。
我的问题是:当我滚动应用程序时,up/down,通过触摸屏幕并移动我的手指 up/down,滚动按预期工作,但触摸的 onTouchListener <TextView>
也被解雇了(这可能也是预料之中的)——不过我不希望这种情况发生。我希望在触摸屏幕滚动时忽略 onTouchListener。
我怎样才能做到这一点?当用户滚动并且 "accidentally" 在某个 <TextView>
.
上触发 onTouchListener 时,我不希望我的函数 运行
你可以这样识别移动动作:
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_MOVE)
{
}
return false;
}
});
经过更多搜索,我找到了 Stimsoni 的 this solution。这个想法是检查 ACTION_DOWN
和 ACTION_UP
事件之间的时间是否低于或高于 ViewConfiguration.getTapTimeout()
.
给出的值
来自文档:
[Returns] the duration in milliseconds we will wait to see if a touch event is a tap or a scroll. If the user does not move within this interval, it is considered to be a tap.
代码:
view.setOnTouchListener(new OnTouchListener() {
private long startClickTime;
@Override
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
startClickTime = System.currentTimeMillis();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (System.currentTimeMillis() - startClickTime < ViewConfiguration.getTapTimeout()) {
// Touch was a simple tap. Do whatever.
} else {
// Touch was a not a simple tap.
}
}
return true;
}
});
我遇到了和你一样的问题,我用ACTION_CANCEL
解决了。
motionEvent.getActionMasked()
等于 ACTION_CANCEL
之前感知到的动作(如 ACTION_DOWN
在你的情况下)现在是 "canceled" 其他手势,如滚动等。你的代码可能是这样的:
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent e) {
if (e.getActionMasked() == MotionEvent.ACTION_DOWN) {
// perceive a touch action.
} else if(e.getActionMasked() == MotionEvent.ACTION_UP ||
e.getActionMasked() == MotionEvent.ACTION_CANCEL) {
// ignore the perceived action.
}
}
希望对您有所帮助。
我有一个类似的问题,但使用一个 TextView,搜索将我带到这里。文本内容可能会占用比屏幕上更多的 space。
简单的工作示例:bpmcounter-android (Kotlin)
class MainActivity : AppCompatActivity() {
inner class GestureTap : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent?): Boolean {
// Do your buttonClick stuff here. Any scrolling action will be ignored
return true
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.textView)
textView.movementMethod = ScrollingMovementMethod()
val gestureDetector = GestureDetector(this, GestureTap())
textView.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) }
}
}
1 方法:
我发现执行此操作的最佳方法是检测保存点 x 和 y 的第一次触摸,然后再进行第二次触摸。如果第一次点击和第二次点击之间的距离很近(我将 10% 作为近似值),那么只需点击一下即可触摸,否则就是滚动。
/**
* determine whether two numbers are "approximately equal" by seeing if they
* are within a certain "tolerance percentage," with `tolerancePercentage` given
* as a percentage (such as 10.0 meaning "10%").
*
* @param tolerancePercentage 1 = 1%, 2.5 = 2.5%, etc.
*/
fun approximatelyEqual(desiredValue: Float, actualValue: Float, tolerancePercentage: Float): Boolean {
val diff = Math.abs(desiredValue - actualValue) // 1000 - 950 = 50
val tolerance = tolerancePercentage / 100 * desiredValue // 20/100*1000 = 200
return diff < tolerance // 50<200 = true
}
var xPoint = 0f
var yPoint = 0f
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
when(event.action) {
MotionEvent.ACTION_DOWN -> {
xPoint = event.x
yPoint = event.y
return true
}
MotionEvent.ACTION_UP -> {
if (!approximatelyEqual(xPoint, event.x, 10f) || !approximatelyEqual(yPoint, event.y, 10f)) {
//scrolling
} else {
//simple click
}
}
}
return false
}
2 方法:
做同样事情的另一种方法是使用 GestureDetector class:
interface GestureInterface {
fun setOnScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float)
fun onClick(e: MotionEvent)
}
class MyGestureDetector(val gestureInterfacePar: GestureInterface) : SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent): Boolean {
gestureInterfacePar.onClick(e)
return false
}
override fun onLongPress(e: MotionEvent) {}
override fun onDoubleTap(e: MotionEvent): Boolean {
return false
}
override fun onDoubleTapEvent(e: MotionEvent): Boolean {
return false
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
return false
}
override fun onShowPress(e: MotionEvent) {
}
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
gestureInterfacePar.setOnScroll(e1, e2, distanceX, distanceY)
return false
}
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
return super.onFling(e1, e2, velocityX, velocityY)
}
}
最后,将其与您的视图绑定:
val mGestureDetector = GestureDetector(context, MyGestureDetector(object : GestureInterface {
override fun setOnScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float) {
//handle the scroll
}
override fun onClick(e: MotionEvent) {
//handle the single click
}
}))
view.setOnTouchListener(OnTouchListener { v, event -> mGestureDetector.onTouchEvent(event) })
为我工作:
View.OnTouchListener() {
@Override
public boolean onTouch(View v,MotionEvent event)
{
if(event.getAction()!=MotionEvent.ACTION_DOWN)
{
// Before touch
}
else {
// When touched
}
return true
});
您无需采用如此复杂的方法来捕获“点击”事件。仅适用于此方法:-
//当然在触摸侦听器内部:-
科特林:-
if(event.action == MotionEvent.ACTION_UP && event.action != MotionEvent.ACTION_MOVE) {
// Click has been made...
// Some code
}
JAVA :-
只需将 event.action 替换为 event.getAction()
这对我有用
我正在 Java 中创建一个 android 应用程序,其中屏幕周围有很多 <TextView>
,所有这些都定义了 onTouchListeners。它们被包裹在 <ScrollView>
中,因为它们占用的空间 space 多于屏幕上可用的空间。
我的问题是:当我滚动应用程序时,up/down,通过触摸屏幕并移动我的手指 up/down,滚动按预期工作,但触摸的 onTouchListener <TextView>
也被解雇了(这可能也是预料之中的)——不过我不希望这种情况发生。我希望在触摸屏幕滚动时忽略 onTouchListener。
我怎样才能做到这一点?当用户滚动并且 "accidentally" 在某个 <TextView>
.
你可以这样识别移动动作:
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_MOVE)
{
}
return false;
}
});
经过更多搜索,我找到了 Stimsoni 的 this solution。这个想法是检查 ACTION_DOWN
和 ACTION_UP
事件之间的时间是否低于或高于 ViewConfiguration.getTapTimeout()
.
来自文档:
[Returns] the duration in milliseconds we will wait to see if a touch event is a tap or a scroll. If the user does not move within this interval, it is considered to be a tap.
代码:
view.setOnTouchListener(new OnTouchListener() {
private long startClickTime;
@Override
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
startClickTime = System.currentTimeMillis();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (System.currentTimeMillis() - startClickTime < ViewConfiguration.getTapTimeout()) {
// Touch was a simple tap. Do whatever.
} else {
// Touch was a not a simple tap.
}
}
return true;
}
});
我遇到了和你一样的问题,我用ACTION_CANCEL
解决了。
motionEvent.getActionMasked()
等于 ACTION_CANCEL
之前感知到的动作(如 ACTION_DOWN
在你的情况下)现在是 "canceled" 其他手势,如滚动等。你的代码可能是这样的:
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent e) {
if (e.getActionMasked() == MotionEvent.ACTION_DOWN) {
// perceive a touch action.
} else if(e.getActionMasked() == MotionEvent.ACTION_UP ||
e.getActionMasked() == MotionEvent.ACTION_CANCEL) {
// ignore the perceived action.
}
}
希望对您有所帮助。
我有一个类似的问题,但使用一个 TextView,搜索将我带到这里。文本内容可能会占用比屏幕上更多的 space。 简单的工作示例:bpmcounter-android (Kotlin)
class MainActivity : AppCompatActivity() {
inner class GestureTap : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent?): Boolean {
// Do your buttonClick stuff here. Any scrolling action will be ignored
return true
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.textView)
textView.movementMethod = ScrollingMovementMethod()
val gestureDetector = GestureDetector(this, GestureTap())
textView.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) }
}
}
1 方法:
我发现执行此操作的最佳方法是检测保存点 x 和 y 的第一次触摸,然后再进行第二次触摸。如果第一次点击和第二次点击之间的距离很近(我将 10% 作为近似值),那么只需点击一下即可触摸,否则就是滚动。
/**
* determine whether two numbers are "approximately equal" by seeing if they
* are within a certain "tolerance percentage," with `tolerancePercentage` given
* as a percentage (such as 10.0 meaning "10%").
*
* @param tolerancePercentage 1 = 1%, 2.5 = 2.5%, etc.
*/
fun approximatelyEqual(desiredValue: Float, actualValue: Float, tolerancePercentage: Float): Boolean {
val diff = Math.abs(desiredValue - actualValue) // 1000 - 950 = 50
val tolerance = tolerancePercentage / 100 * desiredValue // 20/100*1000 = 200
return diff < tolerance // 50<200 = true
}
var xPoint = 0f
var yPoint = 0f
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
when(event.action) {
MotionEvent.ACTION_DOWN -> {
xPoint = event.x
yPoint = event.y
return true
}
MotionEvent.ACTION_UP -> {
if (!approximatelyEqual(xPoint, event.x, 10f) || !approximatelyEqual(yPoint, event.y, 10f)) {
//scrolling
} else {
//simple click
}
}
}
return false
}
2 方法:
做同样事情的另一种方法是使用 GestureDetector class:
interface GestureInterface {
fun setOnScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float)
fun onClick(e: MotionEvent)
}
class MyGestureDetector(val gestureInterfacePar: GestureInterface) : SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent): Boolean {
gestureInterfacePar.onClick(e)
return false
}
override fun onLongPress(e: MotionEvent) {}
override fun onDoubleTap(e: MotionEvent): Boolean {
return false
}
override fun onDoubleTapEvent(e: MotionEvent): Boolean {
return false
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
return false
}
override fun onShowPress(e: MotionEvent) {
}
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
gestureInterfacePar.setOnScroll(e1, e2, distanceX, distanceY)
return false
}
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
return super.onFling(e1, e2, velocityX, velocityY)
}
}
最后,将其与您的视图绑定:
val mGestureDetector = GestureDetector(context, MyGestureDetector(object : GestureInterface {
override fun setOnScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float) {
//handle the scroll
}
override fun onClick(e: MotionEvent) {
//handle the single click
}
}))
view.setOnTouchListener(OnTouchListener { v, event -> mGestureDetector.onTouchEvent(event) })
为我工作:
View.OnTouchListener() {
@Override
public boolean onTouch(View v,MotionEvent event)
{
if(event.getAction()!=MotionEvent.ACTION_DOWN)
{
// Before touch
}
else {
// When touched
}
return true
});
您无需采用如此复杂的方法来捕获“点击”事件。仅适用于此方法:-
//当然在触摸侦听器内部:-
科特林:-
if(event.action == MotionEvent.ACTION_UP && event.action != MotionEvent.ACTION_MOVE) {
// Click has been made...
// Some code
}
JAVA :- 只需将 event.action 替换为 event.getAction()
这对我有用