【问题标题】:Different fling (swipe) velocity on different Android devices with same density相同密度的不同 Android 设备上的不同投掷(滑动)速度
【发布时间】:2013-09-19 16:27:31
【问题描述】:

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

为了检测投掷手势及其速度,我遵循了basic gesture detection 并按照公认答案的建议进行操作:

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 和 velocityY)。基本上这意味着在一个设备上滑动感觉缓慢且不敏感,而其他设备感觉太敏感了。

我认为这可能与设备上的“默认”最大速度有关,可以使用 -

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

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

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

【问题讨论】:

    标签: java android android-animation android-view android-gesture


    【解决方案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
    

    换句话说,velocityPercentX 以百分比的形式为您提供 fling 的“力量”,normalizedVelocityX 是应用程序逻辑方面的速度(例如与设备无关的像素的图像宽度)。

    【讨论】:

    • 你从哪里得到 PIXELS_PER_SECOND?
    • @rstojano 它是任意的,因此您可以将其定义为任何您想要的浮点常量。如果您正在滚动屏幕,请从public static final float PIXELS_PER_SECOND = 10; 开始,看看您喜欢它。以上规范了跨不同设备的投掷。
    • 感谢@bryan 澄清这一点,现在对我来说很有意义。
    【解决方案2】:

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

    最小和最大速度参数由制造商设置。不同的设备型号通常会出于资源选择的目的使用相同的密度bucket,但通常不会共享相同的物理密度。假设存在具有相同分辨率和相同报告密度 1.5 的 4" 电话和 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 参数的原始速度来配置动画并精确匹配速度。避免使用与密度无关的参数来配置投掷动画。

    【讨论】:

      【解决方案3】:

      不要使用速度:它不容易使用。向右滑动是+号,向左滑动是-号。你可以这样做:

      event1.getRawY() 
      event2.getRawY()
      event1.getRawX() 
      event2.getRawX()
      

      确定滑动是向左还是向右:

      如果第一个事件大于第二个,则该人向左滑动,但您可以说如果最后一个事件x 小于第一个事件,则该人的滑动足够远并对其做出反应。你可以做相反的滑动是向右。此外,onFling 无法识别轻拍而只是触摸,因此您无需比较距离速度或长度即可逃脱。

      【讨论】:

      • 谢谢。我的问题不在于检测方向或忽略非甩动手势,我的问题是发生甩动手势后相同密度设备上的速度差异。
      • @RonTesler 您可以通过编程方式根据密度选择速度。您在 onCreate 中获得密度并根据密度更改 maxVelocity 变量。
      • 抱歉,我不确定我是否理解它如何回答我的问题。
      • 而不是使用 ViewConfiguration.MAXIMUM_FLING_VELOCITY = 8000,
      • @RonTesler 您可以根据设备的密度在活动的 oncreate 方法中设置 MAX_FLING_VELOCITY:EI 您可以说如果设备的密度为 x,则 MAX_VElOCITY 为 y
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多