Предотвратить изменение прогресса SeekBar одним касанием

Я использую SeekBar в своем приложении для Android. Когда пользователь один раз нажимает в любом месте SeekBar, его значение прогресса изменяется. Я хочу, чтобы значение прогресса менялось только тогда, когда пользователь скользит пальцем SeekBar (точно так же, как UISlider в iOS).

Я попытался установить для свойства clickable SeekBar значение false, но это не сработало. Как я могу добиться желаемого поведения?


person lohithadas    schedule 26.02.2011    source источник


Ответы (12)


На этой неделе я столкнулся с той же проблемой и решил ее с помощью пользовательской панели поиска: следуя моему коду:

public class Slider extends SeekBar {

private Drawable mThumb;

public Slider(Context context) {
    super(context);

}

public Slider(Context context, AttributeSet attrs) {
    super(context, attrs);

}

@Override
public void setThumb(Drawable thumb) {
    super.setThumb(thumb);
    mThumb = thumb;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {

        if (event.getX() >= mThumb.getBounds().left
                && event.getX() <= mThumb.getBounds().right
                && event.getY() <= mThumb.getBounds().bottom
                && event.getY() >= mThumb.getBounds().top) {

            super.onTouchEvent(event);
        } else {
            return false;
        }
    } else if (event.getAction() == MotionEvent.ACTION_UP) {
        return false;
    } else {
        super.onTouchEvent(event);
    }

    return true;
}}

Надеюсь это поможет

person Slim    schedule 07.06.2013
comment
FTW: thumb.getBounds().contains((int)event.getX(), (int)event.getY()) - person greg7gkb; 10.01.2014
comment
попробовал это решение, это решает информацию о заголовке, но это приведет к неожиданному изменению размера большого пальца, например, к большему размеру большого пальца. - person Arduino; 08.01.2020

Проверьте эту другую тему:

Пользователь не может взаимодействовать с панелью поиска

Я пробовал следующее, и это работает хорошо. Обратите внимание, что моя панель поиска имеет свойство android:max, установленное на 100, например:


<SeekBar
android:id="@+id/EnableBar"
android:layout_span="2"
android:max="100"
/>

package com.androidbook.hiworld;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.SeekBar;


public class HiWorldActivity extends Activity {
    int originalProgress;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        SeekBar seek = (SeekBar)findViewById(R.id.EnableBar);

        seek.setOnSeekBarChangeListener(
        new SeekBar.OnSeekBarChangeListener() {
           public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
               ((TextView)findViewById(R.id.SeekTxt)).setText("Value: "+progress);
               if(fromTouch == true){
                  // only allow changes by 1 up or down
                  if ((progress > (originalProgress+24))
                       || (progress < (originalProgress-24))) {
                     seekBar.setProgress( originalProgress);
                  } else {
                      originalProgress = progress;
                  }
               } 
           }

           @Override
           public void onStopTrackingTouch(SeekBar seekBar) {
               //Nothing here..                
           }

           @Override
           public void onStartTrackingTouch(SeekBar seekBar) {
               originalProgress = seekBar.getProgress();
           }
        });
    }
}
person Eradicatore    schedule 01.08.2011
comment
почему это значение +/- 24? - person vallllll; 12.06.2014

Уже поздно, но может кому пригодится решение. Я расширяю пользовательский SeekBar и переопределяю onTouchEvent.

package com.timera.android.common.view;

import android.content.Context;
import android.util.AttributeSet; 
import android.view.MotionEvent;
import android.widget.SeekBar;

public class OnlySeekableSeekBar extends SeekBar {

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

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

public OnlySeekableSeekBar(Context context) {
    super(context);
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        final int width = getWidth();
        final int available = width - getPaddingLeft() - getPaddingRight();
        int x = (int) event.getX();
        float scale;
        float progress = 0;
        if (x < getPaddingLeft()) {
            scale = 0.0f;
        } else if (x > width - getPaddingRight()) {
            scale = 1.0f;
        } else {
            scale = (float) (x - getPaddingLeft()) / (float) available;
        }
        final int max = getMax();
        progress += scale * max;
        if (progress < 0) {
            progress = 0;
        } else if (progress > max) {
            progress = max;
        }

        if (Math.abs(progress - getProgress()) < 10)
            return super.onTouchEvent(event);
        else
            return false;
    default:
        return super.onTouchEvent(event);
    }
}
}
person rstk    schedule 28.11.2013

Я придумал решение этой проблемы, оно не очень красивое, но оно должно сработать...

Вот идея:

  1. Создайте невидимое наложение, которое закроет весь слайдер, кроме кнопки большого пальца.

    (Я использовал затемненную панель поиска в качестве наложения)

  2. Когда большой палец нажат, вызовите метод «bringToFront» на ползунке

  3. Когда большой палец отпущен, вызовите метод «bringToFront» для невидимого наложения.

Обратите внимание, чтобы это работало, вы должны изменить размер наложения так, чтобы оно закрывало все, кроме кнопки большого пальца (я предлагаю использовать два наложения [по одному для каждой стороны кнопки большого пальца]).

Когда вы отпустите кнопку большого пальца, вы должны изменить размер наложений.

... как я уже сказал, это некрасиво. Я уверен, что есть гораздо лучшие способы сделать это, но если вам нужно это сделать, я бы попробовал это.

   yourBarChangeListener yourBarChangeListener = new SeekBar.OnSeekBarChangeListener() { 

    @Override 
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
     } 

    @Override 
    public void onStartTrackingTouch(SeekBar seekBar) {
     // YOUR CODE HERE 
    } 
    @Override 
    public void onStopTrackingTouch(SeekBar seekBar) { 
    } 
}; 
yourBar.setOnSeekBarChangeListener(yourBarChangeListener);
person bornSwift    schedule 22.04.2011
comment
Как вы определяете, когда нажат большой палец? - person bugfixr; 01.07.2011
comment
Спасибо, это был полный хак. Работал на меня - person Anupam; 16.10.2013

Этот отлично работает с прослушивателем событий внутри SeekBar.

PS. Я улучшил этот код от Слима, чтобы сделать его более универсальным.

/**
 * ProtectedSeekBar
 * 01/27/15
 */
public class ProtectedSeekBar extends SeekBar {

    private Drawable mThumb;

    public ProtectedSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    public ProtectedSeekBar(Context context) {
        super(context);
    }

    @Override
    public void setThumb(Drawable thumb) {
        super.setThumb(thumb);
        mThumb = thumb;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN) {
            if( event.getX() < mThumb.getBounds().left ||
                event.getX() > mThumb.getBounds().right ||
                event.getY() > mThumb.getBounds().bottom ||
                event.getY() < mThumb.getBounds().top) {
                return false;
            }
        }
        return super.onTouchEvent(event);
    }
}
person zinuzoid    schedule 27.01.2015
comment
SeekBar предоставляет метод getThumb; устраняет ли это необходимость хранить большой палец самостоятельно? - person stkent; 02.03.2017
comment
@stkent SeekBar.getThumb представлен в API ›= 16. Так что да, этот метод можно удалить, если вам не нужна обратная совместимость. - person zinuzoid; 06.03.2017

    int oldProgress;
    boolean isOn = true;
    vSeek.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            oldProgress = seekBar.getProgress();
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            if (oldProgress == seekBar.getProgress()) {
                if (isOn) {
                    seekBar.setThumb(getResources().getDrawable(R.drawable.blck_btn));
                    isOn = false;
                } else {
                    seekBar.setThumb(getResources().getDrawable(R.drawable.blck_btn_selected));
                    isOn = true;
                }
            }
        }

Вы можете сравнить прогресс, когда остановите отслеживание.

person lordmegamax    schedule 26.02.2013

Глядя на код @lordmegamax, я нашел кое-что, что не работает.

  1. int oldProgress не может оставаться внутри onCreate, вам нужно объявить его снаружи.
  2. если вы seekBar всегда начинаете с начала, onStartTrackingTouch всегда будет возвращать 0, поэтому ваш onStopTrackingTouch никогда не будет работать.

Немного подумав, я нашел решение.

//SeekBar Slide
    skbLogin.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
        //Force Slide From Beginning
        public void onStartTrackingTouch(SeekBar seekBar) { continuosProgress = false; }
        //Execute when reach the max
        public void onStopTrackingTouch(SeekBar seekBar) {
            if(continuosProgress)
                if(seekBar.getProgress() == 100) btnLogin();
                else skbRollBack();
            else
                seekBar.setProgress(0);
        }
        //Check Slide from Beginning
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if(progress < 10) continuosProgress = true;
        }
    });

Надеюсь, поможет. наилучшие пожелания

PS: это мой первый пост, извините, если что-то не так.

person iMee    schedule 26.04.2013

С помощью этого кода вы можете отключить большой палец для перемещения пользователя

     SeekBar progress = (SeekBar) findViewById(R.id.timer);
      seekBar.setProgress(time);

где "время" - целочисленная переменная и хранит прогресс

     progress.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress,
                boolean fromUser) {
            if(fromUser){
                seekBar.setProgress(time);
            }

        }
    });
person AndroidLad    schedule 09.04.2015

Еще один вариант, который я еще не предложил: подкласс, который переводит полученные события движения, чтобы каждый жест начинался в центре большого пальца. Это оказывает следующее влияние на базовое поведение панели поиска:

  • постукивание на месте не приводит к движению большого пальца (так же, как и другие предлагаемые решения);
  • выполнение жеста смахивания, который начинается вдали от большого пальца, по-прежнему может плавно перемещать большой палец (в отличие от других предлагаемых решений).

Код

/**
 * Translates every gesture to be centered at the current thumb location.
 */
public final class ThumbCentricSeekBar extends SeekBar {

    // Constructors go here...

    private Float offsetX;
    private Float offsetY;

    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        if (event.getAction() == ACTION_DOWN && offsetX == null) {
            offsetX = getThumb().getBounds().centerX() - event.getX();
            offsetY = getThumb().getBounds().centerY() - event.getY();
        }

        event.offsetLocation(offsetX, offsetY);

        if (event.getAction() == ACTION_UP) {
            offsetX = null;
            offsetY = null;
        }

        return super.onTouchEvent(event);
    }

}

Демо

Местоположение касания показано, чтобы проиллюстрировать поведение, которое я описал.

введите здесь описание изображения

person stkent    schedule 02.03.2017
comment
Обратите внимание, что это решение не защищает от переходов при введении второго указателя (но его можно без особого труда обновить для этого). - person stkent; 02.03.2017

Для отключения одиночного нажатия на SeebBar вы можете посмотреть время между событиями DOWN и UP. Обычно продолжительность одиночного нажатия менее 100 мс.

seekBar.setOnTouchListener(new View.OnTouchListener() {
      private long startTime = 0;
      private long endTime = 0;

      @Override
      public boolean onTouch(View view, MotionEvent motionEvent) {

        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
          startTime = motionEvent.getEventTime();
          return true;
        }
        if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
          endTime = motionEvent.getEventTime();
          if (Math.abs(startTime-endTime)<=100)
            return true;
          else
            return false;
        }
        Log.d(TAG, String.valueOf(motionEvent.getEventTime()));
        return false;
      }
    });
person Illya    schedule 22.09.2017

Ситуация, с которой я столкнулся, заключается в том, что SeekBar был создан другим фрагментом кода, поэтому я не мог создать подкласс с обработчиком onTouchEvent(). Вместо этого я переопределил OnTouchListener для экземпляра, с которым хотел повозиться...

SeekBar sb = (SeekBar) findViewById(R.id.seekbar);

sb.setOnTouchListener(new OnTouchListener()
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Drawable thumb = ((SeekBar) v).getThumb();

        if (   event.getX() < thumb.getBounds().left
            || event.getX() > thumb.getBounds().right
            || event.getY() < thumb.getBounds().top
            || event.getY() > thumb.getBounds().bottom) {
            // event occurred outside the thumb slider so ignore it
            return true;
        } else {
            // event occurred inside the thumb slider so pass it on
            return false;
        }
    }
});

Да, я мог бы просто вернуть содержимое выражения «если», но это не сделало бы код читабельным.

person kbro    schedule 21.11.2018

Смотрите ответ здесь. Это позволяет изменять прогресс только с помощью большого пальца, а не с помощью рисования прогресса. https://stackoverflow.com/a/62165116/1159930

person Markymark    schedule 03.06.2020