【问题标题】:Android with SeekBar - Prevent 'thumb' from moving when touching SeekBar to drag/scroll screen带有 SeekBar 的 Android - 在触摸 SeekBar 以拖动/滚动屏幕时防止“拇指”移动
【发布时间】:2011-04-23 20:59:57
【问题描述】:

我有一个带有 TableLayout 的 android 应用程序,每行都有一个 SeekBar 和一个 ToggleButton。这些行超出了屏幕的可见范围,因此可以滚动。当我触摸并拖动以向上滚动页面并在此过程中触摸 SeekBar 时,它会立即更改“拇指”的位置,而不是滚动页面。但是,ToggleButton 的行为并非如此;相反,我可以开始拖动按钮并滚动然后释放,而不会改变 ToggleButton 的状态。

有没有办法让 SeekBar 以这种方式运行,这样触摸它开始拖动不会导致栏改变位置而是滚动页面?

【问题讨论】:

    标签: android seekbar


    【解决方案1】:

    我也遇到过类似的问题。我能找到的唯一解决方案(当搜索栏在列表视图中时)是禁用搜索栏,直到单击该项目。

    解决方案

    在 ArrayAdapter 中,我将 enabled 和 focusable 都设置为 false 并添加了 SeekBar 侦听器,将属性设置为 false 允许我使用列表项 onItemClicked 侦听器。在 onItemCLickListener 中,我检索了搜索栏,将属性设置为 true,这意味着它可以向上或向下滑动。然后我在调整后禁用它。代码如下

    ArrayAdapter 片段

    此代码在列表项的创建中,其中包含搜索栏

        seekBar.setClickable(false);
        seekBar.setFocusable(false);
        seekBar.setEnabled(false);
    
        /* attach listener */
        attachProgressUpdatedListener(seekBar, positionOfItemInList);
    

    AttachProgressUpdatedListener

    此方法将侦听器附加到数组适配器类中的搜索栏

    private void attachProgressUpdatedListener(SeekBar seekBar,
        final int position) {
    
    seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    
        public void onStopTrackingTouch(SeekBar seekBar) {
        int progress = seekBar.getProgress();
        int task_id = (Integer) seekBar.getTag();
    
        TaskHandler taskHandler = new TaskHandler(DBAdapter
            .getDBAdapterInstance(getContext()));
    
        taskHandler.updateTaskProgress(task_id, progress);
    
        mList.get(position).setProgress(progress);
    
        //need to fire an update to the activity
        notifyDataSetChanged();
        seekBar.setEnabled(false);
    
        }
    
        public void onStartTrackingTouch(SeekBar seekBar) {
        // empty as onStartTrackingTouch listener not being used in
        // current implementation
    
        }
    
        public void onProgressChanged(SeekBar seekBar, int progress,
            boolean fromUser) {
        // empty as onProgressChanged listener not being used in
        // current implementation
    
        }
    });
    
    }
    

    OnItemCLickListener

    此片段来自包含列表视图的活动。

    taskListView.setOnItemClickListener(new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
        SeekBar sb = (SeekBar) view.findViewById(R.id.seek);
        sb.setFocusable(true);
        sb.setEnabled(true);
    
        }
    });
    

    【讨论】:

      【解决方案2】:

      我通过对搜索栏进行子类化并检查运动角度是否大于 45 度来使其工作。在这种情况下,忽略触摸并返回 false,以便滚动视图执行其操作。

      编辑:

      这是一个扩展的 SeekBar,它允许您在浮点数而不是整数中设置最小值/最大值。

      public class SeekBarEx extends SeekBar implements
              SeekBar.OnSeekBarChangeListener {
          final int SEEK_POINTS = 0x10000;
          final String TAG = "SeekBarEx";
          public float mMax;
          public float mMin;
          public OnSeekBarExChangeListener delegate = null;
      
          public interface OnSeekBarExChangeListener {
              public void onSeekChanged(SeekBarEx seekBarEx, float value,
                      boolean fromUser);
      
              public void onStartTrackingTouch(SeekBarEx seekBar);
      
              public void onStopTrackingTouch(SeekBarEx seekBar);
          }
      
          public SeekBarEx(Context ctx, AttributeSet attr) {
              super(ctx, attr);
      
              super.setMax(SEEK_POINTS);
      
              mMin = 0f;
              mMax = 1.0f;
              initAttributes(attr);
              this.setOnSeekBarChangeListener(this);
          }
      
          public void setDelegate(OnSeekBarExChangeListener d) {
              delegate = d;
          }
      
      
          public void initAttributes(AttributeSet attrSet) {
              TypedArray a;
              a = getContext().obtainStyledAttributes(attrSet, R.styleable.SeekBarEx);
      
              final int N = a.getIndexCount();
      
              int i;
              for (i = 0; i < N; i++) {
                  int attr = a.getIndex(i);
                  switch (attr) {
                  case R.styleable.SeekBarEx_max:
                      mMax = a.getFloat(i, 1.0f);
                      Log.d(TAG, "maxSet " + mMax);
                      break;
                  case R.styleable.SeekBarEx_min:
                      mMin = a.getFloat(i, 0f);
                      Log.d(TAG, "minSet" + mMin);
                      break;
                  case R.styleable.SeekBarEx_value:
                      this.setValue(a.getFloat(i, 0));
                      break;
                  }
              }
      
              a.recycle();
      
          }
      
          @Override
          public int getProgress() {
              return super.getProgress();
          }
      
          public float getValue() {
              float r;
              float run;
              r = (float) super.getProgress();
              r = r / (float) SEEK_POINTS;
              run = mMax - mMin;
              r = r * run + mMin;
              return r;
          }
      
          public void setValue(float v) {
              if (Float.isNaN(v) || Float.isInfinite(v))
                  return;
              if (v > mMax)
                  v = mMax;
              if (v < mMin)
                  v = mMin;
              float run;
              int setv;
              run = mMax - mMin;
              v -= mMin;
              setv = Math.round(v * (float) SEEK_POINTS / run);
              super.setProgress(setv);
          }
      
          public boolean valueChanged = false;
      
          public void cancelTracking() {
              if (oldValue != Float.NaN) {
                  this.setValue(oldValue);
                  oldValue = Float.NaN;
                  valueChanged = false;
                  acceptTouches = false;
                  acceptChange = false;
              }
          }
      
          // we override these methods so that when we forcully cancel
          // on ontouches moved. We can revert back to the old value
          @Override
          public void onProgressChanged(SeekBar seekBar, int progress,
                  boolean fromUser) {
              Log.d(TAG, "SeekBar changed to " + progress);
              if (delegate != null && acceptTouches) {
                  valueChanged = true;
                  delegate.onSeekChanged(this, this.getValue(), fromUser);
              } else
                  cancelTracking();
          }
      
          @Override
          public void onStartTrackingTouch(SeekBar seekBar) {
              if (delegate != null)
                  delegate.onStartTrackingTouch(this);
          }
      
          @Override
          public void onStopTrackingTouch(SeekBar seekBar) {
              if (delegate != null && valueChanged)
                  delegate.onStopTrackingTouch(this);
              else
                  cancelTracking();
              acceptChange = false;
              valueChanged = false;
          }
      
          public float mY, mX;
          public boolean acceptTouches = true;
          // acceptChange never read todo: delete
          public boolean acceptChange = false;
          public float oldValue = Float.NaN;
      
          public ScrollView getScrollView() {
              View view;
              view = this;
              int maxUp;
              maxUp = 5;
              while (view != null && maxUp > 0) {
                  view = (View) view.getParent();
                  ScrollView scroller;
                  if (view instanceof ScrollView) {
                      scroller = (ScrollView) view;
                      return scroller;
                  }
                  maxUp--;
              }
              return null;
          }
          // **************************************
          // This is the important part in achieving the effect in scroll
          // view to be nice
          @Override
          public boolean onTouchEvent(MotionEvent event) {
              int action;
              action = event.getAction() & MotionEvent.ACTION_MASK;
              ScrollView scroller = this.getScrollView();
              boolean mayScroll;
      
              mayScroll = true;
              if (scroller == null)
                  mayScroll = false;
              else {
                  int scrollAmount = scroller.getMaxScrollAmount();
                  if (scrollAmount == 0)
                      mayScroll = false;
              }
              switch (action) {
              case MotionEvent.ACTION_CANCEL:
                  Log.d(TAG, "got cancel touches");
                  cancelTracking();
                  super.onTouchEvent(event);
                  return true;
              case MotionEvent.ACTION_DOWN:
                  mX = event.getX();
                  mY = event.getY();
                  acceptTouches = true;
                  acceptChange = false;
                  oldValue = this.getValue();
                  valueChanged = false;
                  break;
              case MotionEvent.ACTION_MOVE:
                  float x;
                  float y;
                  x = event.getX();
                  y = event.getY();
                  float dx;
                  float dy;
                  dx = x - mX;
                  dy = y - mY;
                  if (dx < 0)
                      dx = -dx;
                  if (dy < 0)
                      dy = -dy;
      
                  y = this.getHeight() / 2 - y;
                  float angle;
                  float distance;
                  distance = dx * dx + dy * dy;
                  // I just realized this is wrong it should be
                  // angle = (float)Math.acos(Math.abs(dx)/Math.sqrt(distance))
                  // I'm leaving it until tested or someone can confirm
                  angle = (float) Math.atan(dy / dx);
                  int distanceLimit;
                  distanceLimit = this.getHeight() / 3;
                  distanceLimit *= distanceLimit;
                  // if we move at an angle of atleast 45degrees
                  // cancel
                  if (mayScroll && angle > Math.PI / 4.0) {
                      cancelTracking();
                  }
      
                  mX += 100000;
                  if (y < 0)
                      y = -y;
                  // if we moved finger too far just cancel
                  // cause the person may have wanted to scroll but
                  // failed so we revert back to the old value
                  if (y > this.getHeight() * 2) {
                      cancelTracking();
                  } else if (acceptTouches)
                      acceptChange = true;
                  break;
              default:
                  break;
              }
              // if we accept touches do the usual otherwise
              // return false so scrollView can do it's thing
              if (acceptTouches)
                  return super.onTouchEvent(event);
              return false;
          }
      
      }
      

      seekbarex.xml 如下所示。这只是将 min/max/value 添加为浮点数。

      <declare-styleable name="SeekBarEx">
      
      <attr name="min" format="float"/>
      <attr name="max" format="float"/>
      <attr name="value" format="float"/>
      </declare-styleable>
      
      </resources>
      

      【讨论】:

      • 好吧,实际上它比我想象的要好。我只是有一个错误,但提出的问题是旧的。如果有人有兴趣以某种方式给我发消息。但它本质上就是我上面所说的,我只需要禁用我通常在滑块更改后执行的长时间处理。
      • 你能解释一下答案吗?我无法理解你是如何做到的。
      • @Gogu 我修改了答案并包含了代码。这是相当直截了当的。我们检查触摸角度是否大于 45 度,如果是则取消并恢复到旧值,然后让 ScrollView 做它的事情。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多