【问题标题】:How can I get a working vertical SeekBar in Android?如何在 Android 中获得工作的垂直 SeekBar?
【发布时间】:2011-02-03 21:56:08
【问题描述】:

我已经实现了常用的 VerticalSeekBar 帖子 here。事实上,SeekBar 的操作有点古怪。这是我从那个例子中稍微改编的onTouchEvent()

public boolean onTouchEvent(MotionEvent event)
    {
            xPos = event.getX();
            yPos = event.getY();
            oOffset = this.getThumbOffset();
            oProgress = this.getProgress();

            //Code from example - Not working
            //this.setThumbOffset( progress * (this.getBottom()-this.getTop()) );

            this.setProgress((int)(29*yPos/this.getBottom()));
            return true;
    }

我已经成功实现了一个 VerticalSeekBar,其中的进度按预期更新并且功能齐全,但拇指没有效仿。这只是一个图形故障,所以我现在忽略它。但是, 要是能正常工作就好了。这个 SeekBar 有max = 20

但是,我尝试使用max = 1000 实现另一个 VerticalSeekBar。显然,它使用相同的代码,所以你会假设相同的行为。即使我的手指滑过 SeekBar 并最终离开屏幕,我也只能取得 0~35 的进度。如果我只是在进度条的末尾附近点击(应该progress ~ 900),它会返回大约 35 的进度,黄色进度条通过停留在顶部附近来反映该值。

我的问题是:有没有人有一个可以工作的垂直 SeekBar 的链接,知道如何调整这个特定的例子?

【问题讨论】:

    标签: android seekbar


    【解决方案1】:

    这是一个有效的 VerticalSeekBar 实现:

    package android.widget;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    
    public class VerticalSeekBar extends SeekBar {
    
        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());
        }
    
        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:
                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_UP:
                    setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
                    onSizeChanged(getWidth(), getHeight(), 0, 0);
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                    break;
            }
            return true;
        }
    }
    

    要实现它,请在您的项目中创建一个新类,选择正确的包:

    在那里,粘贴代码并保存。现在在您的 XML 布局中使用它:

    <android.widget.VerticalSeekBar
      android:id="@+id/seekBar1"
      android:layout_width="wrap_content"
      android:layout_height="200dp"
      />
    

    【讨论】:

      【解决方案2】:

      接受的答案中给出的代码没有拦截 onStartTrackingTouch 和 onStopTrackingTouch 事件,因此我对其进行了修改以更好地控制这两个事件。

      这是我的代码:

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

      【讨论】:

        【解决方案3】:

        将此代码与 setProgress 方法一起使用时遇到问题。为了解决这些问题,我建议覆盖 setProgress 并向其添加 onSizeChanged 调用。

        【讨论】:

          【解决方案4】:

          将此代码与 setProgress 方法一起使用时遇到问题。为了解决这些问题,我建议覆盖 setProgress 并向其添加 onSizeChanged 调用。在此处添加代码..

             private int x,y,z,w;
             protected void onSizeChanged(int w, int h, int oldw, int oldh) {
                  super.onSizeChanged(h, w, oldh, oldw);
                  this.x=w;
                  this.y=h;
                  this.z=oldw;
                  this.w=oldh;
              }
                  @Override
                  public synchronized void setProgress(int progress) {
          
                      super.setProgress(progress);
          
                          onSizeChanged(x, y, z, w);
          
                  }
          

          通过添加以下代码来执行选定的悬停动作:

          1.setPressed(true);setSelected(true);//在ACTION_DOWN中添加这个

          2.setPressed(false);setSelected(false);//在ACTION_UP中添加这个

          并在您的 xml 中为选定的悬停选项编写代码。

          <selector xmlns:android="http://schemas.android.com/apk/res/android">
              <item android:state_pressed="true"
                    android:state_window_focused="true"
                    android:drawable="@drawable/thumb_h" />
              <item android:state_selected="true"
                    android:state_window_focused="true"
                    android:drawable="@drawable/thumb_h" />
              <item android:drawable="@drawable/thumb" />
          </selector>
          

          这对我有用...

          【讨论】:

            【解决方案5】:

            感谢 Paul Tsupikoff、Fatal1ty2787 和 Ramesh 的出色代码。

            就我个人而言,我想要一个与给定代码相比上下颠倒的垂直滑块。换句话说,拇指越低,该值会增加,而不是减少。更改四行似乎已经解决了这个问题。

            首先,我更改了 Paul 最初给出的 onDraw() 方法。 rotate()translate() 调用现在具有以下参数:

            c.rotate(90);
            c.translate(0, -getWidth());
            

            然后我对 Fatal1ty2787 给出的 onTouchEvent() 中的 ACTION_MOVE 案例进行了两项更改。对setProgress() 的调用现在如下所示:

            setProgress((int) (getMax() * event.getY() / getHeight()));
            

            最后,对onProgressChanged() 的调用如下所示:

            myListener.onProgressChanged(this, (int) (getMax() * event.getY() / getHeight()), true);
            

            现在,如果 Google 和我们一样对这个功能感兴趣的话....

            【讨论】:

              【解决方案6】:

              对于API 11 and later,可以使用seekbar's XML attributes(android:rotation="270") 进行垂直效果。

              <SeekBar
              android:id="@+id/seekBar1"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:rotation="270"/>
              

              对于较旧的 API 级别(例如 API10),请使用: https://github.com/AndroSelva/Vertical-SeekBar-Android

              【讨论】:

                【解决方案7】:

                另一个想法可能是改变 MotionEvent 的 X 和 Y 坐标并将它们传递给超级实现:

                public class VerticalSeekBar extends SeekBar {
                
                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);
                }
                
                @Override
                public boolean onTouchEvent(MotionEvent event) {
                    if (!isEnabled()) {
                        return false;
                    }
                    float x = (getHeight() - event.getY()) * getWidth() / getHeight();
                    float y = event.getX();
                    MotionEvent verticalEvent = MotionEvent
                            .obtain(event.getDownTime(), event.getEventTime(), event.getAction(), x, y,
                                    event.getPressure(), event.getSize(), event.getMetaState(),
                                    event.getYPrecision(), event.getXPrecision(), event.getDeviceId(),
                                    event.getEdgeFlags());
                    return super.onTouchEvent(verticalEvent);
                }
                
                protected void onDraw(Canvas c) {
                    c.rotate(-90);
                    c.translate(-getHeight(), 0);
                    super.onDraw(c);
                }
                
                @Override
                protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                    super.onMeasure(heightMeasureSpec, widthMeasureSpec);
                    setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
                }
                
                protected void onSizeChanged(int w, int h, int oldw, int oldh) {
                    super.onSizeChanged(h, w, oldh, oldw);
                }
                }
                

                在这种情况下,不需要调用 setProgress(int) 方法,因此您可以使用 OnSeekBarChangeListener.onProgressChanged() 中的布尔标志“fromUser”来确定搜索是否由用户交互产生。

                【讨论】:

                  【解决方案8】:

                  目标平台

                  来自 Android 2.3.x(姜饼) 到 Android 7.x(牛轧糖)

                  开始

                  这个库发布在 jCenter 上。只需将这些行添加到 build.gradle。

                  dependencies {
                      compile 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:0.7.2'
                  }
                  

                  用法

                  布局 XML

                  <!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes -->
                  <com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
                      android:layout_width="wrap_content"
                      android:layout_height="150dp">
                      <com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
                          android:id="@+id/mySeekBar"
                          android:layout_width="0dp"
                          android:layout_height="0dp"
                          android:max="100"
                          android:progress="0"
                          android:splitTrack="false"
                          app:seekBarRotation="CW90" /> <!-- Rotation: CW90 or CW270 -->
                  </com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>
                  

                  注意:Android N+ 需要android:splitTrack="false"

                  Java 代码

                  public class TestVerticalSeekbar extends AppCompatActivity {
                      private SeekBar volumeControl = null;
                  
                  
                      @Override
                      protected void onCreate(Bundle savedInstanceState) {
                          super.onCreate(savedInstanceState);
                          setContentView(R.layout.activity_test_vertical_seekbar);
                  
                          volumeControl = (SeekBar) findViewById(R.id.mySeekBar);
                  
                          volumeControl.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                              int progressChanged = 0;
                  
                              public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                                  progressChanged = progress;
                              }
                  
                              public void onStartTrackingTouch(SeekBar seekBar) {
                                  // TODO Auto-generated method stub
                              }
                  
                              public void onStopTrackingTouch(SeekBar seekBar) {
                                  Toast.makeText(getApplicationContext(), "seek bar progress:" + progressChanged,
                                          Toast.LENGTH_SHORT).show();
                              }
                          });
                      }
                  
                  }
                  

                  【讨论】:

                    【解决方案9】:

                    根据 Paul Tsupikoff 的回答,这里是 AppCompatVerticalSeekBar

                    package com.my.apppackage;
                    
                    import android.annotation.SuppressLint;
                    import android.content.Context;
                    import android.graphics.Canvas;
                    import android.util.AttributeSet;
                    import android.view.MotionEvent;
                    
                    import androidx.appcompat.widget.AppCompatSeekBar;
                    
                    public class AppCompatVerticalSeekBar extends AppCompatSeekBar {
                        public AppCompatVerticalSeekBar(Context context) {
                            super(context);
                        }
                    
                        public AppCompatVerticalSeekBar(Context context, AttributeSet attrs) {
                            super(context, attrs);
                        }
                    
                        public AppCompatVerticalSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
                            super(context, attrs, defStyleAttr);
                        }
                    
                        @Override
                        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
                        protected synchronized void onDraw(Canvas canvas) {
                            canvas.rotate(-90);
                            canvas.translate(-getHeight(), 0);
                            super.onDraw(canvas);
                        }
                    
                        @SuppressLint("ClickableViewAccessibility")
                        @Override
                        public boolean onTouchEvent(MotionEvent event) {
                            if (!isEnabled()) {
                                return false;
                            }
                    
                            switch (event.getAction()) {
                                case MotionEvent.ACTION_DOWN:
                                case MotionEvent.ACTION_MOVE:
                                case MotionEvent.ACTION_UP: {
                                    setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
                                    onSizeChanged(getWidth(), getHeight(), 0, 0);
                                    break;
                                }
                                case MotionEvent.ACTION_CANCEL: {
                                    break;
                                }
                            }
                            return true;
                        }
                    }
                    

                    在你的布局文件中使用它:

                    <com.my.apppackage.AppCompatVerticalSeekBar
                        android:id="@+id/verticalSeekBar1"
                        android:layout_width="wrap_content"
                        android:layout_height="200dp" />
                    

                    【讨论】:

                      猜你喜欢
                      • 2017-04-29
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2019-07-12
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多