在Android上,“按住并保持”按钮需要使用onTouchListener更改状态(自定义XML选择器)。

21

我有一个需要具备“按住并保持”功能的按钮图形,因此我使用了onTouchListener而不是使用onClickListener,这样应用程序可以对其做出反应。

 MotionEvent.ACTION_DOWN,

 MotionEvent.ACTION_UP

根据这两个事件触发的速度,我可以在两者之间的时间内运行“pressAndHoldHandler”。

总之,长话短说:在同一个应用程序中,我有许多“基本”按钮不需要按住功能,因此它们使用onClickListener。

所有这些按钮都已经通过自己的XML选择器文件进行了图形化自定义:

<?xml version="1.0" encoding="UTF-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:state_enabled="false"
        android:drawable="@drawable/btn_chicken_off" />

    <item
        android:state_enabled="true"
        android:state_pressed="true"
        android:drawable="@drawable/btn_chicken_s3" />

    <item
        android:state_enabled="true"
        android:state_focused="true"
        android:drawable="@drawable/btn_chicken_s2" />

    <item
        android:state_enabled="true"
        android:drawable="@drawable/btn_chicken_off" />

</selector>
所以问题是:上述选择器不能通过onTouchListener访问。只有onClickListener将使用其自己方法的onClick()部分拉取状态更改,因此这些“按住”按钮永远不会改变状态。对用户来说反馈非常糟糕。
我目前正在通过在ACTION_DOWN和ACTION_UP的switch case内进行以下操作来强制执行以上内容:
if (action == MotionEvent.ACTION_DOWN) {
    btn_chicken.setBackgroundResource(R.drawable.btn_chicken_s3);
}
else
    if (action == MotionEvent.ACTION_UP) {
        btn_chicken.setBackgroundResource(R.drawable.btn_chicken_off);
    }

但这似乎是一种hack的方法,并且它缺少“聚焦但未按下”的阶段。

有人之前遇到过这个问题吗?


这正是我的问题。感谢您提供的良好描述。 - Suragch
5个回答

46

使用view.setPressed()函数自行模拟按下的行为。

当接收到ACTION_DOWN事件时,您可能希望启用按下状态,并在接收到ACTION_UP事件时禁用它。

此外,如果用户滑出按钮,则最好禁用它。可以像下面的示例中所示,捕获ACTION_OUTSIDE事件:

@Override
public boolean onTouch(View v, MotionEvent event) {

    switch (event.getAction() & MotionEvent.ACTION_MASK) {

    case MotionEvent.ACTION_DOWN:
        v.setPressed(true);
        // Start action ...
        break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_OUTSIDE:
    case MotionEvent.ACTION_CANCEL:
        v.setPressed(false);
        // Stop action ...
        break;
    case MotionEvent.ACTION_POINTER_DOWN:
        break;
    case MotionEvent.ACTION_POINTER_UP:
        break;
    case MotionEvent.ACTION_MOVE:
        break;
    }

    return true;
}

2
我认为 ACTION_OUTSIDE 实际上并没有被触发。这似乎是专门用于在对话框之外触摸的。 - yincrash
在调用 view.setPressed(false); 的好地方是 ACTION_CANCEL - Leo Landau
view.setPressed(false) 放在 MotionEvent.ACTION_UP: 中对我有效。即使我滑出视图而不是只抬起手指,我也无法让它不起作用。 - Suragch

5
确保在 onTouchListener() 函数的结尾返回 false。 :)

3
我想到了!只需要按照以下方式使用view.setSelected()
@Override
public boolean onTouch(View v, MotionEvent event) {
    if(event.getAction() == MotionEvent.ACTION_DOWN){
        yourView.setSelected(true);
        ...
        return true;
    }
    else if(event.getAction() == MotionEvent.ACTION_UP){
        yourView.setSelected(false);
        ...
        return true;
    }
    else 
        return false;
}

这将使您的选择器即使在视图具有onTouch侦听器的情况下也能正常工作 :)

3
你可以只设置按钮的onClickListener并保留其onClick方法为空。你的逻辑实现可以在onTouch中进行。这样,你就会有按下效果了。
另外,你不需要在选择器中使用所有这些状态,你可以简单地使用:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:state_pressed="true" android:drawable="@drawable/IMAGE_PRESSED" />

    <item android:drawable="@drawable/IMAGE" />

</selector>

好的,我尝试过了,不行:onTouch不是OnClickListener的一个方法。而且我仍然需要对那些使用轨迹球的人保留<item android:state_enabled="true" android:state_focused="true">。还有其他的想法吗? - Octoth0rpe
我是想让你保留ontouch方法,但是从中删除更改背景的代码。此外,设置视图的点击监听器(即yourView.setOnClickListener(new OnClick...)),并将其保留为空。这样你就会有按压效果了。 - Givi
我可以确认上述方法不起作用 :( @Octoth0rpe: 你找到了解决方法吗? - Andrés Pachon
添加一个空的 onClickListener 也会导致 onClickListener 抢占 onTouchListener 的事件 - 因此您将获得触摸反馈,但是您的 onTouchListener 现在变得无用,这引出了一个问题:“为什么要使用无用的 onTouchListener”。 - Tim Malseed

3
更好的解决方案:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_activated="true">
        <bitmap android:src="@drawable/image_selected"/>
    </item>
    <item>
        <bitmap android:src="@drawable/image_not_selected"/>
    </item>
</selector>

@Override
public void onClick(View v) {
    if (v.isActivated())
       v.setActivated(false);
    else
       v.setActivated(true);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接