【问题标题】:Prevent single tap from changing SeekBar progress防止单击更改 SeekBar 进度
【发布时间】:2011-07-04 19:24:32
【问题描述】:

我在我的 Android 应用中使用SeekBar。当用户单击SeekBar 上的任意位置时,其进度值会更改。我只希望在用户滑动 SeekBar 拇指时更改进度值(就像 iOS 中的 UISlider 一样)。

我尝试将SeekBarclickable 属性设置为false,但没有成功。如何实现所需的行为?

【问题讨论】:

    标签: android android-seekbar android-touch-event


    【解决方案1】:

    我想出了一个解决方案,它不是很漂亮,但它应该可以解决问题......

    这就是想法:

    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);
    

    【讨论】:

    • 如何检测拇指何时被按下?
    • 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);
    • 谢谢,这完全是一个 hack。为我工作
    【解决方案2】:

    查看其他线程:

    User can not interact with the Seekbar

    我尝试了以下方法,效果很好。请注意,我的 seekbar 的 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();
               }
            });
        }
    }
    

    【讨论】:

    • 为什么这个值是 +/- 24?
    【解决方案3】:
        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;
                    }
                }
            }
    

    您可以在停止跟踪时比较进度。

    【讨论】:

      【解决方案4】:

      查看@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:这是我的第一篇文章,如有错误请见谅。

      【讨论】:

        【解决方案5】:

        本周我遇到了同样的问题,我使用自定义 SeekBar 解决了它: 按照我的代码:

        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;
        }}
        

        希望对你有帮助

        【讨论】:

        • FTW: thumb.getBounds().contains((int)event.getX(), (int)event.getY())
        • 试过这个解决方案,这个解决了标题信息,但它会导致拇指大小发生意外变化,例如拇指大小变大
        【解决方案6】:

        为时已晚,但也许有人需要解决方案。我扩展自定义 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);
            }
        }
        }
        

        【讨论】:

          【解决方案7】:

          这与 SeekBar 中的事件侦听器完美配合。

          PS。我从 Slim 改进了这段代码,使其更通用。

          /**
           * 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);
              }
          }
          

          【讨论】:

          • SeekBar 公开了一个getThumb 方法;这是否消除了自己存储拇指可绘制对象的需要?
          • @stkent SeekBar.getThumb 在 API >= 16 中公开。所以是的,如果您不需要向后兼容性,可以删除该方法。
          【解决方案8】:

          通过此代码,您可以禁用拇指按用户移动

               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);
                      }
          
                  }
              });
          

          【讨论】:

            【解决方案9】:
            private class UpdateListener implements OnSeekBarChangeListener {
            
                // max step user can change at once
                private static final int THUMB_MAX_MOVE = 5;
            
                // current seekbar value (50 is initial seekbar value as defined in xml)
                private int currentProgress = 50;
            
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
            
                    // save current value before user jumps around
                    currentProgress = seekBar.getProgress();
                }
            
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {}
            
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            
                    // check if we've jumped too far
                    if (Math.abs(currentProgress - progress) > THUMB_MAX_MOVE) {
            
                        // if so, revert to last progress and return
                        seekBar.setProgress(currentProgress);
                        return;
                    }
            
                    // if not, move was valid and update current progress
                    currentProgress = progress;
            
                    // do anything more here
                }
            }
            

            【讨论】:

              【解决方案10】:

              我还没有看到建议的另一个选项:转换接收到的运动事件的子类,以使每个手势看起来都从拇指的中心开始。这对底层的搜索栏行为有以下影响:

              • 在适当位置轻敲不会导致拇指移动(与其他建议的解决方案相同);
              • 从拇指开始离开的滑动手势仍然可以平滑地平移拇指(不同于其他建议的解决方案)。

              代码

              /**
               * 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);
                  }
              
              }
              

              演示

              显示触摸位置是为了说明我描述的行为。

              【讨论】:

              • 请注意,当引入第二个指针时,此解决方案不能防止跳转(但可以毫不费力地进行更新)。
              【解决方案11】:

              要禁用 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;
                    }
                  });
              

              【讨论】:

                【解决方案12】:

                我面临的情况是 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;
                        }
                    }
                });
                

                是的,我可以简单地返回“if”表达式的内容,但这不会使代码可读。

                【讨论】:

                  【解决方案13】:

                  在此处查看答案。它只允许通过拇指而不是进度可绘制来更改进度。 https://stackoverflow.com/a/62165116/1159930

                  【讨论】:

                    猜你喜欢
                    • 2014-01-15
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-05-01
                    • 2011-09-28
                    • 1970-01-01
                    相关资源
                    最近更新 更多