按键事件onKeyDown和onKeyLongPress

12

我希望我的应用程序能够对音量按钮的普通按下和长按事件做出不同的反应。

我已经看过这个,但如果我一直按住音量按钮,我会在获得KeyLongPressed事件之前收到很多KeyDown事件。

我想要的是只有一个事件或另一个事件,而不是二者兼备,这样我就可以在短按时调节音量,在长按时跳过曲目。

你能帮我吗?

这是我的代码:

    @Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) 
    {
        Log.d("Test", "Long press!");
        return true;
    }
    return super.onKeyLongPress(keyCode, event);
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
        event.startTracking();
        Log.d("Test", "Short");
        return true;
    }

    return super.onKeyDown(keyCode, event);
}

非常感谢您的帮助!- Iris


尝试使用按键抬起事件(KeyEvent.KEYCODE_VOLUME_UP)而不是按键按下事件;如果长按会在按键抬起之前调用,如果长按被调用,则在keyup中使用标志停止操作。 - Sandeep P
那是音量增加键,不是指示你按住键或类似的指示。 - Rolf ツ
我目前正在尝试一些东西。 - Rolf ツ
类似:https://dev59.com/nnjZa4cB1Zd3GeqPkPRo - trante
4个回答

16

这里是我编写的代码。它像魔法一样有效。也许您可以优化它以获得更好的逻辑性。但您将通过它理解要点。关键是使用标志位。短按是指我们短时间按下并释放音量按钮。因此,onKeyUp 是可以帮助我们检测到短按操作的方法。

package com.example.demo;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;

public class TestVolumeActivity extends Activity {
    boolean flag = false;

    boolean flag2 = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash_screen);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_splash_screen, menu);
        return true;
    }

    @Override
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            Log.d("Test", "Long press!");
            flag = false;
            flag2 = true;
            return true;
        }
        return super.onKeyLongPress(keyCode, event);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            event.startTracking();
            if (flag2 == true) {
                flag = false;
            } else {
                flag = true;
                flag2 = false;
            }

            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {

            event.startTracking();
            if (flag) {
                Log.d("Test", "Short");
            }
            flag = true;
            flag2 = false;
            return true;
        }

        return super.onKeyUp(keyCode, event);
    }
}

所有长按的日志记录(未检测到短按):

10-18 02:06:15.369: D/Test(16834): Long press!
10-18 02:06:18.683: D/Test(16834): Long press!
10-18 02:06:21.566: D/Test(16834): Long press!
10-18 02:06:23.738: D/Test(16834): Long press!

所有短按操作的日志记录:

10-18 02:07:42.422: D/Test(16834): Short
10-18 02:07:43.203: D/Test(16834): Short
10-18 02:07:43.663: D/Test(16834): Short
10-18 02:07:44.144: D/Test(16834): Short

1
你比我快了一分钟 :) 我认为由于某种原因,他不会传递键盘抬起事件。 - lucian.pantelimon
2
为什么要这么多标志(flags),只需在 onKeyDown 内使用 if (event.getRepeatCount() == 0) 就可以了,如果是短按则把 isShortPress 设为 true,否则设为 false - xmen
@xmen W.K 是的,这当然可以用更少的标志来完成。实际上,这是为了展示您可以使用标志。 - VendettaDroid

12

根据SDK处理长按按钮的正确方式。

import android.app.Activity;
import android.util.Log;
import android.view.KeyEvent;


public class TestVolumeActivity extends Activity
{
    private static final String TAG = TestVolumeActivity.class.getSimpleName();

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if( keyCode == KeyEvent.KEYCODE_VOLUME_UP || 
            keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
        {
            event.startTracking();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyLongPress(int keyCode, KeyEvent event)
    {
        if(keyCode == KeyEvent.KEYCODE_VOLUME_UP){
            Log.d(TAG, "Long press KEYCODE_VOLUME_UP");
            return true;
        }
        else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){
            Log.d(TAG, "Long press KEYCODE_VOLUME_DOWN");
            return true;
        }
        return super.onKeyLongPress(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event)
    {
        if((event.getFlags() & KeyEvent.FLAG_CANCELED_LONG_PRESS) == 0){
            if(keyCode == KeyEvent.KEYCODE_VOLUME_UP){
                Log.e(TAG, "Short press KEYCODE_VOLUME_UP");
                return true;
            }
            else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){
                Log.e(TAG, "Short press KEYCODE_VOLUME_DOWN");
                return true;
            }
        }
        return super.onKeyUp(keyCode, event);
    }
}

1
更加标准和干净。我的唯一问题是长按后我的设备会发出嘟嘟声,我通过在 onKeyUp 中添加 else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { return true; } 来解决这个问题。 - corwin.amber

5

当我准备发布我的答案时,我发现有人已经找到了某种解决方案...

但这里是我的解决方案,简单而且非常有效。只需一个标志 ;)

此代码检测短按和长按,当发生长按时,不会触发短按!

注意:如果您想要正常的音量上下行为,请将onKeyPress方法中的return true更改为super调用,如下所示:

event.startTracking();
if(event.getRepeatCount() == 0){
    shortPress = true;
}
//return true;
return super.onKeyDown(keyCode, event);

没有使用super调用的代码:

private boolean shortPress = false;

@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
        shortPress = false;
        Toast.makeText(this, "longPress", Toast.LENGTH_LONG).show();
        return true;
    }
    //Just return false because the super call does always the same (returning false)
    return false;
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
        if(event.getAction() == KeyEvent.ACTION_DOWN){
            event.startTracking();
            if(event.getRepeatCount() == 0){
                shortPress = true;
            }
            return true;
        }
    }
    return super.onKeyDown(keyCode, event);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
        if(shortPress){
            Toast.makeText(this, "shortPress", Toast.LENGTH_LONG).show();
        } else {
            //Don't handle longpress here, because the user will have to get his finger back up first
        }
        shortPress = false;
        return true;
    }
    return super.onKeyUp(keyCode, event);
}

下面的代码已经添加了音量上键,请选择您需要的一侧 ;)
private boolean shortPress = false;

@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
        shortPress = false;
        Toast.makeText(this, "longPress Volume Down", Toast.LENGTH_LONG).show();
        return true;
    } else if(keyCode == KeyEvent.KEYCODE_VOLUME_UP){
        shortPress = false;
        Toast.makeText(this, "longPress Volume Up", Toast.LENGTH_LONG).show();
        return true;
    }
    //Just return false because the super call does always the same (returning false)
    return false;
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
        if(event.getAction() == KeyEvent.ACTION_DOWN){
            event.startTracking();
            if(event.getRepeatCount() == 0){
                shortPress = true;
            }
            return true;
        }
    }
    return super.onKeyDown(keyCode, event);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
        if(shortPress){
            Toast.makeText(this, "shortPress Volume Down", Toast.LENGTH_LONG).show();
        } else {
            //Don't handle longpress here, because the user will have to get his finger back up first
        }
        shortPress = false;
        return true;
    } else if(keyCode == KeyEvent.KEYCODE_VOLUME_UP){
        if(shortPress){
            Toast.makeText(this, "shortPress Volume up", Toast.LENGTH_LONG).show();
        } else {
            //Don't handle longpress here, because the user will have to get his finger back up first
        }
        shortPress = false;
        return true;

    }
    return super.onKeyUp(keyCode, event);
}

1
这是更好的答案,因为它使用了较少的变量和助记符代码。干得好。 - Avijit

0

我不知道这个答案是否会为您的问题提供可接受的解决方案,因为它依赖于定期获取频繁的KeyDown事件。

您可以尝试记住最后一个KeyDown事件被触发时的系统时间(我将其命名为tLast),并忽略所有KeyDown事件,直到您收到KeyLongPressed事件。

为了获得“正确”的KeyDown事件(在上一步中被忽略的事件),您可以有一个线程检查当前系统时间和tLast之间的时间差(我将其命名为tDelta),以便该时间差足够大而不被视为连续按下。

鉴于在短时间内引发了许多KeyDown事件,当事件间隔足够长时(tDelta大于固定值)理论上可以确定音量按钮未被连续按下。

这种解决方案的缺点是,如果用户快速按下音量按钮(tDelta 按下之间的时间小于用于评估连续按下的固定值),则多个按键将被忽略/视为连续按键。

另一个(次要的)缺点是,在解释常规按键之前会有一些延迟,因为 tDelta 必须大于在评估是否处理常规或连续按键时使用的固定值。

敬礼,

Lucian

编辑: 嗯...再想想:您是否正在使用 Android 实现的 KeyListener

如果您正在使用此功能,请查看为其定义的 onKeyUp 方法。

如果可以使用此方法,请使用它。它比上面介绍的解决方法更优雅,更高效,更易于维护和更直接。

KeyListener Javadoc


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