使用SeekBar的Android - 在触摸SeekBar拖动/滚动屏幕时防止'拇指'移动

时间:2011-04-23 20:59:58

标签: android seekbar

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

无论如何都要让SeekBar以这种方式运行,这样触摸它来开始拖动不会导致栏改变位置而是滚动页面?

2 个答案:

答案 0 :(得分:3)

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

解决方案

在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

此方法将侦听器附加到arrayAdapter类

内的搜索条
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);

    }
});

答案 1 :(得分:0)

我通过子类化搜索条并检查运动角度是否大于45度来实现它。在这种情况下忽略touches并返回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>