在具有相同密度的不同Android设备上不同的甩动(滑动)速度

我正在编写自己的图像查看器,使用户能够向左/向右滑动以查看下一个\上一个图像。我想根据抛掷速度对图像变化进行动画处理。

为了检测甩动手势及其速度,我遵循了这个基本的手势检测,并按照接受的答案建议进行操作:

public class SelectFilterActivity extends Activity implements OnClickListener
{

    private static final int SWIPE_MIN_DISTANCE = 120;
    private static final int SWIPE_MAX_OFF_PATH = 250;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;
    private GestureDetector gestureDetector;
    View.OnTouchListener gestureListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /* ... */

        // Gesture detection
        gestureDetector = new GestureDetector(this, new MyGestureDetector());
        gestureListener = new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                return gestureDetector.onTouchEvent(event);
            }
        };

    }

    class MyGestureDetector extends SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            try {
                if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                    return false;
                // right to left swipe
                if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    Toast.makeText(SelectFilterActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show();
                }  else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    Toast.makeText(SelectFilterActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show();
                }
            } catch (Exception e) {
                // nothing
            }
            return false;
        }

    }

问题是,对于具有相同密度的不同Android设备,我获得了不同的速度值(velocityX和veloctyY)。基本上,这意味着在一台设备中,滑动感觉很慢且不敏感,而其他设备则感觉过于敏感。

我认为它可能与设备上的“默认”最大速度有关,可以使用以下方式获取 -

ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity()

在具有相同密度的不同设备上的结果与预期不同:

    Samsung S I 480x800 density 1.5 Android 2.1
    getScaledMinimumFlingVelocity(): 75
    getScaledMaximumFlingVelocity(): 3600
    ViewConfiguration.MAXIMUM_FLING_VELOCITY = 4000

    HTC Desire 480x800 density 1.5 Android 2.3.3
    getScaledMinimumFlingVelocity(): 75
    getScaledMaximumFlingVelocity(): 6000
    ViewConfiguration.MAXIMUM_FLING_VELOCITY = 4000

    Samsung S III mini 480x800 density 1.5 Android 4.1
    getScaledMinimumFlingVelocity(): 75
    getScaledMaximumFlingVelocity(): 12000
    ViewConfiguration.MAXIMUM_FLING_VELOCITY = 8000

您还可以看到,在低于 4.0 及更高版本的 Android 中,ViewConfiguration 有 2 个不同的值。MAXIMUM_FLING_VELOCITY

为什么具有相同密度和几乎相同API级别的不同设备没有相同的最大速度?当用户做出轻扫手势时,如何在不同的设备上获得相同的体验?我能否使用最大速度数据在所有 Android 设备上提供统一的体验?


答案 1

我遇到了类似的问题。您可以把速度归一化为介于 0 和 1 之间的值,而不是直接使用 的最大和最小飞行速度。ViewConfiguration

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    float maxFlingVelocity    = ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity();
    float velocityPercentX    = velocityX / maxFlingVelocity;          // the percent is a value in the range of (0, 1]
    float normalizedVelocityX = velocityPercentX * PIXELS_PER_SECOND;  // where PIXELS_PER_SECOND is a device-independent measurement

换言之,以百分比表示,为您提供甩球的“幂”,并且是应用程序逻辑中的速度(例如,以与设备无关的像素为单位的图像宽度)。velocityPercentXnormalizedVelocityX


答案 2

为什么具有相同密度和几乎相同API级别的不同设备没有相同的最大速度?

最小和最大速度参数由制造商设置。不同的设备型号通常会使用相同的密度存储桶进行资源选择,但通常不会共享相同的物理密度。假设存在一部 4 英寸电话和一部 5 英寸电话,它们具有相同的分辨率和相同的报告密度 1.5。想象一下,以相同的物理速度在两个屏幕上滑动。报告的像素/秒在小屏幕上会更高,尽管两款手机具有相同的分辨率和报告的密度。

如何处理最小和最大速度?假设较小屏幕的制造商在报告甩动手势之前确定75像素/秒是完美的最小手指速度。任何较小的都会导致触摸错误导致意外抛掷,任何较大的都需要过于刻意的手势来甩动某些东西。如果大型手机的制造商想要匹配与小型手机相同的完美物理行为(相同的手指速度),则需要将最小速度调整为较小的数字。由于物理特性,较大设备的制造商需要较少的像素/秒来启动甩动。屏幕灵敏度等其他因素可能适用于不同的制造商。

当用户做出轻扫手势时,如何在不同的设备上获得相同的体验?

使用以像素/秒为单位给出的速度来配置动画。只需避免使用与密度无关的参数,您应该能够精确匹配速度。请注意,这与最小或最大速度值无关。但是,您可能希望根据规定的密度将SWIPE_THRESHOLD_VELOCITY计算为“原始”像素值。这就是Android的Launcher2计算主屏幕项目甩动的密度无关阈值速度的方式:

int DENSITY_INDEPENDENT_THRESHOLD = 200;
Resources r = getResources();
float density = r.getDisplayMetrics().density;
SWIPE_THRESHOLD_VELOCITY = (int)(DENSITY_INDEPENDENT_THRESHOLD * density);

请注意,在上面的示例中,对于 4 英寸电话和 5 英寸电话,此阈值是相同的。这不会影响实际速度,因为我们在这里只谈论一个阈值。您只需要在5英寸手机上比在4英寸手机上滑动快20%即可突破阈值。Android 启动器在 y 方向上使用 -1500 的DENSITY_INDEPENDENT_THRESHOLD,但 200 听起来适合您的应用程序。

我能否使用最大速度数据在所有 Android 设备上提供统一的体验?

不。最大速度是您不需要正确制作动画的边缘情况。我怀疑除非用户在玩游戏,否则用户会以最大速度抛出任何东西,而且用户不会关心动画是否以6000像素/秒的速度完美匹配。

TL;DR 使用 onFling 参数中的原始速度来配置动画并精确匹配速度。避免使用与密度无关的参数来配置抛掷动画。


推荐