范围搜索栏带有辅助功能的自定义视图

时间:2019-01-29 07:53:01

标签: android accessibility android-custom-view

我已经创建了一个自定义范围搜索栏,我想为其实现可访问性。在默认搜索栏中,将球向右/向上移动的正常手势是“先右后左”。因此,当用户将注意力集中在左球上时,我想在对讲功能打开时检测“先右后左”手势,然后将球移动一步。

您可以激活对讲并使用音量作为我要达到的目标的示例。

对话手势信息: URL

My custom range seek bar

public class MyRangeSeekBar extends ConstraintLayout implements View.OnTouchListener {

    private static final String TAG = MyRangeSeekBar.class.getSimpleName();
    private static final long BALL_SPEED = 300;
    private static final int STEPS_LIMIT = 2;
    private static final int STEPS_MAX = 10;
    private final MyRangeSeekBarBinding binding;
    DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
    private int min = 1;
    private int max = 10;
    private int currentMin = 1;
    private int currentMax = 10;
    private float dX;
    private float posInitBar;
    private float posEndBar;
    private float halfBallWidth;
    private float barWidth;
    private float segmentWidth;
    private int rangeDescriptionLabel;
    private int rangeJoinDescriptionLabel;
    private int minimumDescriptionLabel;
    private int maximumDescriptionLabel;
    private DecimalFormat df = new DecimalFormat("#.##", symbols);

    public MyRangeSeekBar(Context context) {
        this(context, null);
    }

    public MyRangeSeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyRangeSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        binding = MyRangeSeekBarBinding.inflate(inflater, this, true);
        initAttrs(context, attrs, defStyleAttr);
    }

    private void initAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
        final TypedArray attributes =
                context.obtainStyledAttributes(
                        attrs,
                        R.styleable.MyRangeSeekBar,
                        defStyleAttr,
                        0);
        min = attributes.getInt(R.styleable.MyRangeSeekBar_min, 1);
        max = attributes.getInt(R.styleable.MyRangeSeekBar_max, 10);
        currentMin = attributes.getInt(R.styleable.MyRangeSeekBar_currentMin, 1);
        currentMax = attributes.getInt(R.styleable.MyRangeSeekBar_currentMax, 10);
        rangeDescriptionLabel = attributes.getResourceId(R.styleable.MyRangeSeekBar_rangeDescriptionLabel, R.string.mcaiucomps_range_label);
        rangeJoinDescriptionLabel = attributes.getResourceId(R.styleable.MyRangeSeekBar_rangeJoinDescriptionLabel, R.string.mcaiucomps_range_join_label);
        minimumDescriptionLabel = attributes.getResourceId(R.styleable.MyRangeSeekBar_minimumDescriptionLabel, R.string.mcaiucomps_min_label);
        maximumDescriptionLabel = attributes.getResourceId(R.styleable.MyRangeSeekBar_maximumDescriptionLabel, R.string.mcaiucomps_max_label);

        attributes.recycle();

        refresh();
    }

    private void checkValues() {
        if (max - min < STEPS_LIMIT) {
            throw new RuntimeException("Not enough steps, you need a minimum of 2 steps like from 1 to 3");
        }
        if (max - min >= STEPS_MAX) {
            throw new RuntimeException("Too many steps, you can't do more than 10 steps");
        }
        if (currentMin < min || currentMin > max) {
            throw new RuntimeException("The minimum value is not between the limits");
        }
        if (currentMax < min || currentMax > max) {
            throw new RuntimeException("The maximum value is not between the limits");
        }
        if (currentMin > currentMax) {
            throw new RuntimeException("The minimum is bigger than the maximum value");
        }
    }

    private void setBar() {

        binding.mcaiucompsBar.post(new Runnable() {
            @Override
            public void run() {
                halfBallWidth = binding.mcaiucompsBallLeft.getMeasuredWidth() / 2;
                posInitBar = binding.mcaiucompsBar.getX() + halfBallWidth;
                posEndBar = binding.mcaiucompsBar.getMeasuredWidth() - halfBallWidth;

                binding.mcaiucompsBallRight.setX(posEndBar + halfBallWidth);
                binding.mcaiucompsBallLeft.setX(posInitBar + halfBallWidth);

                barWidth = posEndBar - posInitBar;
                segmentWidth = barWidth / (max - min);

                setBalls();
            }
        });
    }

    private void setTitles() {
        binding.mcaiucompsMinValue.setText(getContext().getString(minimumDescriptionLabel, min));
        binding.mcaiucompsMinValue.setContentDescription(getContext().getString(minimumDescriptionLabel, min) + ", " + getContext().getString(R.string.caixabank_componente_rango_minimo));
        binding.mcaiucompsMaxValue.setText(getContext().getString(maximumDescriptionLabel, max));
        binding.mcaiucompsMaxValue.setContentDescription(getContext().getString(maximumDescriptionLabel, max) + ", " + getContext().getString(R.string.caixabank_componente_rango_maximo));
        binding.mcaiucompsTitle.setText(getContext().getString(rangeDescriptionLabel, currentMin, currentMax));
        binding.mcaiucompsTitle.setContentDescription(getContext().getString(rangeDescriptionLabel, currentMin, currentMax));
    }

    private void updateBallsPosition(View view) {
        //Check limits
        if (binding.mcaiucompsBallLeft.getId() == view.getId() && getLeftBallPos() < posInitBar) {
            animateBall(view, posInitBar);
        } else if (binding.mcaiucompsBallRight.getId() == view.getId() && getRightBallPos() > posEndBar) {
            animateBall(view, posEndBar);
        } else {
            //Move to the closest position
            if (view.getId() == binding.mcaiucompsBallLeft.getId()) {
                if (getLeftBallPos() >= posInitBar) {
                    currentMin = Math.round(getLeftBallPos() / segmentWidth);
                } else if (getLeftBallPos() >= getRightBallPos()) {
                    currentMin = currentMax;
                } else {
                    currentMin = min;
                }

                animateBall(binding.mcaiucompsBallLeft, currentMin * segmentWidth + posInitBar);
            } else {
                if (getRightBallPos() < barWidth) {
                    currentMax = Math.round(getRightBallPos() / segmentWidth);
                } else if (getRightBallPos() >= getLeftBallPos()) {
                    currentMax = currentMin;
                } else {
                    currentMax = max - min;
                }

                animateBall(binding.mcaiucompsBallRight, currentMax * segmentWidth + posInitBar);
            }
        }
    }

    private void animateBall(View view, float pos) {
        pos = Float.parseFloat(df.format(pos));
        // Leave some space between balls if they are in the same position
        if (view.getId() == binding.mcaiucompsBallLeft.getId()
                && (pos <= getRightBallPos() && pos >= getRightBallPos() - halfBallWidth || pos >= posEndBar)) {
            binding.mcaiucompsBallLeft.animate()
                    .x(getRightBallPos() - halfBallWidth * 1.7f)
                    .setDuration(BALL_SPEED)
                    .setInterpolator(new DecelerateInterpolator())
                    .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator valueAnimator) {
                            changeBarHighlightSize();
                            updateTitle();
                        }
                    })
                    .start();
        } else if (view.getId() == binding.mcaiucompsBallRight.getId()
                && (pos >= getLeftBallPos() && pos <= getLeftBallPos() + halfBallWidth || pos <= posInitBar)) {
            binding.mcaiucompsBallRight.animate()
                    .x(getLeftBallPos() - halfBallWidth * 0.3f)
                    .setDuration(BALL_SPEED)
                    .setInterpolator(new DecelerateInterpolator())
                    .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator valueAnimator) {
                            changeBarHighlightSize();
                            updateTitle();
                        }
                    })
                    .start();

        } else {
            view.animate()
                    .x(pos - halfBallWidth)
                    .setDuration(BALL_SPEED)
                    .setInterpolator(new DecelerateInterpolator())
                    .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator valueAnimator) {
                            changeBarHighlightSize();
                            updateTitle();
                        }
                    })
                    .start();
        }
    }

    private void updateTitle() {
        int minLabel = Math.round((getLeftBallPos() - posInitBar) / segmentWidth) + min;
        int maxLabel = Math.round((getRightBallPos() - posInitBar) / segmentWidth) + min;

        if (getRightBallPos() == posEndBar || maxLabel > max) {
            maxLabel = max;
        }
        if (getLeftBallPos() == posInitBar || minLabel < min) {
            minLabel = min;
        }
        String title = getContext().getString(rangeDescriptionLabel, minLabel, maxLabel);
        if (minLabel == maxLabel) {
            title = getContext().getString(rangeJoinDescriptionLabel, minLabel);
        }
        binding.mcaiucompsTitle.setText(title);
        binding.mcaiucompsTitle.setContentDescription(title);


    }

    private float getLeftBallPos() {
        return Float.parseFloat(df.format(binding.mcaiucompsBallLeft.getX() + halfBallWidth));
    }

    private float getRightBallPos() {
        return Float.parseFloat(df.format(binding.mcaiucompsBallRight.getX() + halfBallWidth));
    }

    private void setBalls() {
        binding.mcaiucompsBallLeft.setOnTouchListener(this);
        binding.mcaiucompsBallRight.setOnTouchListener(this);

        float posMin = (currentMin - min) * segmentWidth + posInitBar;
        float posMax = (currentMax - min) * segmentWidth + posInitBar;
        if (currentMax == currentMin) {
            posMin = posMax + halfBallWidth - halfBallWidth * 1.7f;
        }
        animateBall(binding.mcaiucompsBallLeft, posMin);
        animateBall(binding.mcaiucompsBallRight, posMax);

    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        if (binding.mcaiucompsBallLeft.getId() == view.getId()
                || binding.mcaiucompsBallRight.getId() == view.getId()) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    dX = view.getX() - event.getRawX();
                    view.bringToFront();
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (isValidMove(view, event.getRawX() + dX)) {
                        view.animate()
                                .x(event.getRawX() + dX)
                                .setDuration(0)
                                .start();
                        changeBarHighlightSize();
                        updateTitle();
                        Log.d(TAG, "onTouch: " + event.getRawX() + " dx: " + dX);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    updateBallsPosition(view);
                    break;

                default:
                    return false;
            }
            return true;
        }
        return false;
    }

    private boolean isValidMove(View view, float rawX) {
        if ((binding.mcaiucompsBallLeft.getId() == view.getId() && getLeftBallPos() >= posInitBar)
                || (binding.mcaiucompsBallRight.getId() == view.getId() && getRightBallPos() <= posEndBar)) {
            if ((binding.mcaiucompsBallLeft.getId() == view.getId() && rawX + halfBallWidth / 2 <= binding.mcaiucompsBallRight.getX())
                    || (binding.mcaiucompsBallRight.getId() == view.getId() && rawX + halfBallWidth / 2 >= getLeftBallPos())) {
                return true;
            }
        }

        return false;
    }

    public void changeBarHighlightSize() {
        ViewGroup.LayoutParams params = binding.mcaiucompsBarHighlight.getLayoutParams();
        params.width = (int) (getRightBallPos() - getLeftBallPos());
        binding.mcaiucompsBarHighlight.setLayoutParams(params);

        binding.mcaiucompsBarHighlight.setX(getLeftBallPos());


    }

    public int getMin() {
        return min;
    }

    public void setMin(int min) {
        this.min = min;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public int getCurrentMin() {
        return currentMin;
    }

    public void setCurrentMin(int currentMin) {
        this.currentMin = currentMin;
    }

    public int getCurrentMax() {
        return currentMax;
    }

    public void setCurrentMax(int currentMax) {
        this.currentMax = currentMax;
    }

    public void setRangeDescriptionLabel(@StringRes int rangeDescriptionLabel) {
        this.rangeDescriptionLabel = rangeDescriptionLabel;
        updateTitle();
    }

    public void setRangeJoinDescriptionLabel(@StringRes int rangeJoinDescriptionLabel) {
        this.rangeJoinDescriptionLabel = rangeJoinDescriptionLabel;
        updateTitle();
    }

    public void setMinimumDescriptionLabel(@StringRes int minimumDescriptionLabel) {
        this.minimumDescriptionLabel = minimumDescriptionLabel;
        setTitles();
    }

    public void setMaximumDescriptionLabel(@StringRes int maximumDescriptionLabel) {
        this.maximumDescriptionLabel = maximumDescriptionLabel;
        setTitles();
    }

    public void refresh() {
        checkValues();
        setBar();
        setTitles();
    }

    @Override
    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
        return super.onRequestSendAccessibilityEvent(child, event);
    }
}

谢谢!

0 个答案:

没有答案