java - 即使没有进度变化,onProgressChanged 也会运行

标签 java android seekbar android-seekbar android-vertical-seekbar

我有一个带有两个搜索栏的 Android 应用程序,并且我已将它们设置为除非同时按下它们,否则该应用程序不会响应。我的问题是,当它们都被按下时,方法 onProgressChanged(SeekBareekBar, int Progress, boolean fromUser) 开始在无限循环中持续运行。即使我将手指完全放在栏上,也会发生这种情况(这意味着任何一个栏上的进度都没有改变,因此不应调用 onProgressChanged 方法)。有谁知道为什么会发生这种情况吗?

这是一些相关代码:

void setupLRSpeedBars(final int UIID, final Bool bar1, final Bool bar2) {
    SeekBar sb = (SeekBar) findViewById(UIID);
    sb.setProgress(9);
    sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if (bar1.isTrue()) {
                progress -= 9;
                transmit((UIID == R.id.leftSpeedBar ? "L" : "R") +
                         (progress >= 0 ? "+" : "") +
                          Integer.toString(progress));
            }
        }

        public void onStartTrackingTouch(SeekBar seekBar) {
            bar2.set(true);
        }

        public void onStopTrackingTouch(SeekBar seekBar) {
            bar2.set(false);
            seekBar.setProgress(9);
            //transmit((UIID == R.id.leftSpeedBar ? "L" : "R") + "+0");
            transmit("s");
        }
    });
}

另请注意,我使用了自定义 Bool 类而不是原始 boolean 类,以便我能够绕过 之外的所有变量的限制code>OnSeekBarChangeListener 被标记为 final。 (我需要在 onSeekBarChangeListeners 之间发送信息,以便确定用户是否同时将手指按住两个搜索栏) 我有预感这可能是导致我的问题的原因,但我不知道原因是什么。

class Bool {
    private boolean bool;
    public Bool () {}
    public Bool(boolean bool) { this.bool = bool; }
    public void set (boolean bool) { this.bool = bool; }
    public boolean get () { return bool; }
    public boolean isTrue () { return bool; }
}

编辑: 我还会注意到我正在使用自定义的垂直搜索栏。这是代码:

package android.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;

public class VerticalSeekBar extends SeekBar {

    private OnSeekBarChangeListener myListener;
    public VerticalSeekBar(Context context) {
        super(context);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    @Override
    public void setOnSeekBarChangeListener(OnSeekBarChangeListener mListener){
        this.myListener = mListener;
    }

    @Override
    public synchronized void setProgress(int progress){
        super.setProgress(progress);
        onSizeChanged(getWidth(), getHeight(), 0, 0);
    }

    protected void onDraw(Canvas c) {
        c.rotate(-90);
        c.translate(-getHeight(), 0);

        super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if(myListener!=null)
                    myListener.onStartTrackingTouch(this);
                break;
            case MotionEvent.ACTION_MOVE:
                setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
                onSizeChanged(getWidth(), getHeight(), 0, 0);
                myListener.onProgressChanged(this, getMax() - (int) (getMax() * event.getY() / getHeight()), true);
                break;
            case MotionEvent.ACTION_UP:
                myListener.onStopTrackingTouch(this);
                break;

            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }
}

最佳答案

我认为问题出在 VerticalSeekBaronTouchEvent 实现中,因为您正在处理收到的每个 MotionEvent.ACTION_MOVE

来自the documentation :

A new onTouchEvent() is triggered with an ACTION_MOVE event whenever the current touch contact position, pressure, or size changes. As described in Detecting Common Gestures, all of these events are recorded in the MotionEvent parameter of onTouchEvent().

Because finger-based touch isn't always the most precise form of interaction, detecting touch events is often based more on movement than on simple contact. To help apps distinguish between movement-based gestures (such as a swipe) and non-movement gestures (such as a single tap), Android includes the notion of "touch slop." Touch slop refers to the distance in pixels a user's touch can wander before the gesture is interpreted as a movement-based gesture. For more discussion of this topic, see Managing Touch Events in a ViewGroup.

也就是说,您认为您的手指完全静止,但您的搜索栏正在接收 ACTION_MOVE 事件。

在您的情况下,“触摸斜率”近似现在是一个好主意,因为计算出的触摸斜率对于您的目的来说是巨大的,如 touch slop is defined as :

"Touch slop" refers to the distance in pixels a user's touch can wander before the gesture is interpreted as scrolling. Touch slop is typically used to prevent accidental scrolling when the user is performing some other touch operation, such as touching on-screen elements.

要解决您的问题,您可以计算最后一个管理位置与当前位置之间的距离来触发 onProgressChanged:

private static final float MOVE_PRECISION = 5; // You may want to tune this parameter
private float lastY;

// ...

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (!isEnabled()) {
        return false;
    }

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            lastY = event.getY();

            if (myListener != null)
                myListener.onStartTrackingTouch(this);
            break;
        case MotionEvent.ACTION_MOVE:
            if (calculateDistanceY(event) > MOVE_PRECISION) {
                setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
                onSizeChanged(getWidth(), getHeight(), 0, 0);
                myListener.onProgressChanged(this, getMax() - (int) (getMax() * event.getY() / getHeight()), true);

                lastY = event.getY();
            }
            break;
        case MotionEvent.ACTION_UP:
            myListener.onStopTrackingTouch(this);
            break;

        case MotionEvent.ACTION_CANCEL:
            break;
    }
    return true;
}

private float calculateDistanceY (MotionEvent event) {
    return Math.abs(event.getY() - lastY);
}

关于java - 即使没有进度变化,onProgressChanged 也会运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35793417/

相关文章:

java - 缓冲区从文件读取音频数据给出零

java - == 运算符对 String 有什么用?

java - 在 Eclipse 编辑器中覆盖关闭操作

Android 全屏同时仍然显示状态栏

android - 设置SeekBar的背景

java - 为什么我无法删除 Android 应用程序中的操作栏?

java - 如何使用谷歌应用程序引擎从数据存储中检索信息并使用java在Android应用程序的 ListView 中显示它?

android - 将 FLAC 或 AMR_WB 中的音频流式传输到 Google Speech API

java - 去除 SeekBar 阴影

android - 让 Seekbar 拇指更容易捕获