onProgressChanged 是 运行 即使没有进度变化

onProgressChanged being run even when there is no progress change

我有一个带有两个搜索栏的 android 应用程序,我已经将它们设置为除非同时按下它们,否则应用程序不会响应。我的问题是,当它们都被按下时,方法 onProgressChanged(SeekBar seekBar, 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 class 而不是原始 boolean,以便我绕过 boolean 之外的所有变量的限制=18=] 标记为 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" 近似值现在是一个好主意,因为计算出的触摸斜率对于您的目的来说是巨大的,因为 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);
}