onProgressChanged запускается, даже если нет изменений прогресса
У меня есть приложение для 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
класс вместо примитива boolean
для того, чтобы я обойти ограничение, что все переменные за пределами 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;
}
}
1 ответ
Я думаю, что проблема в onTouchEvent
реализация вашего VerticalSeekBar
потому что вы обрабатываете каждый MotionEvent.ACTION_MOVE
получено.
Из документации:
Новый метод onTouchEvent() запускается с событием ACTION_MOVE всякий раз, когда изменяется текущее положение контакта, давление или размер. Как описано в Обнаружении общих жестов, все эти события записываются в параметре MotionEvent onTouchEvent().
Поскольку касание пальцем не всегда является наиболее точной формой взаимодействия, обнаружение сенсорных событий часто основано больше на движении, чем на простом контакте. Чтобы помочь приложениям различать жесты, основанные на движении (например, смахивание), и жесты, не связанные с движением (например, одним касанием), в Android включено понятие "сенсорный сдвиг". Отклонение касания относится к расстоянию в пикселях, которое может пройти пользовательское касание до того, как жест будет интерпретирован как жест, основанный на движении. Более подробное обсуждение этой темы см. В разделе "Управление сенсорными событиями в ViewGroup".
То есть вы думаете, что ваши пальцы полностью неподвижны, но ваши бары поиска получают ACTION_MOVE
События.
В вашем случае аппроксимация "сенсорного наклона" теперь является хорошей идеей, потому что рассчитанный сенсорный спад огромен для ваших целей, так как сенсорный спад определяется как:
"Отклонение касания" относится к расстоянию в пикселях, которое может пройти пользовательское касание до того, как жест будет интерпретирован как прокрутка. Сенсорный наклон обычно используется для предотвращения случайной прокрутки, когда пользователь выполняет какую-либо другую операцию касания, например касание элементов на экране.
Чтобы решить вашу проблему, вы можете рассчитать расстояние между последней управляемой позицией и текущей, чтобы вызвать 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);
}