Giter Club home page Giter Club logo

rangeseekbar's Introduction

RangeSeekbar

A seekbar contains two cursor and support multi-touch.

Opps! Screen shot has missed

RangeSeekbar have left and right cursors, user can move cursor to make fliter.

How to use

RangeSeekbar support user to set difference parameters.

	1. seekbarHeight:        Height of seekbar.
	2. textSize: 	         Size of text mark.
	3. spaceBetween:         Space between seekbar and text mark.
	4. leftCursorBackground: Background drawable of left cursor, press state supported.
	5. rightCursorBackground:Similar with leftCursorBackground.
	6. markTextArray:        Text of marks. The most important parameter, must be set.
	7. textColorNormal:      Color of text mark in normal state.
	8. textColorSelected:    Color of text mark in selected state.
	9. seekbarColorNormal:   Similar with textColorNormal.
	10.seekbarColorSelected: Similar with textColorSelected.
	11.autoMoveDuration:     Time when a cursor move to a mark index without touch.
	
	Users can also set these in java code.

Other supported:

	1. setLeftSelection(int index): Set left cursor to any of text mark(besides the last one).
	2. setRightSelection(int index):Set right cursor to any of text mark(besides the first one).
	3. setOnCursorChangeListener:   Set it to listen when left cursor or right cursor is located on new index.

Developed by:

Roy Wang ([email protected])

If you use this lib. Please let me know.

License:

Copyright 2014 Roy Wang

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

rangeseekbar's People

Contributors

bryant1410 avatar dolphinwang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rangeseekbar's Issues

setLeftSelection and setRightSelection not working as expected

I write a code something like this : rangeFriday.setLeftSelection(listTimesOriginal.indexOf(timeSplit[1])); rangeFriday.setRightSelection(listTimesOriginal.indexOf(timeSplit[2]));
I have debugged the code and the proper integer value is going in but the left and right cursors both move to the start of the range bar.
I think this variable mPartLength in the functions setLeftSelection(int partIndex) and setRightSelection(int partIndex) is creating issues. Cant understand what to do with it....

Kindly modify the code to get it working or guide me to the solution..
The library is good and really usefull except this one feature...

I just want to only one point.

If I left point selected 1, and right point I also need selected 1. left 1, right 2. left 2, right 3?
I fixed some code . But something is not right .
Can you help me ?

/*

  • Copyright (C) 2014 Roy Wang
    *
  • Licensed under the Apache License, Version 2.0 (the "License");
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
    *
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an "AS IS" BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */
    package com.gridy.main.view.comboseekbar;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;

import com.gridy.main.R;

/**

  • A seekbar contains two cursor(left and right). Multiple touch supported.
    *

  • @author dolphinWang

  • @time 2014/05/07
    */
    @TargetApi(Build.VERSION_CODES.FROYO)
    public class RangeSeekbar extends View {

    private static final String DEBUG_TAG = "RangeSeekbar.java";

    private static final int DEFAULT_DURATION = 100;

    private enum DIRECTION {
    LEFT, RIGHT;
    }

    private int mDuration;

    /**

    • Scrollers for left and right cursor
      */
      private Scroller mLeftScroller;
      private Scroller mRightScroller;

    /**

    • Background drawables for left and right cursor. State list supported.
      */
      private Drawable mLeftCursorBG;
      private Drawable mRightCursorBG;

    /**

    • Represent states.
      */
      private int[] mPressedEnableState = new int[]{
      android.R.attr.state_pressed, android.R.attr.state_enabled};
      private int[] mUnPresseEanabledState = new int[]{
      -android.R.attr.state_pressed, android.R.attr.state_enabled};

    /**

    • Colors of text and seekbar in different states.
      */
      private int mTextColorNormal;
      private int mTextColorSelected;
      private int mSeekbarColorNormal;
      private int mSeekbarColorSelected;

    /**

    • Height of seekbar
      */
      private int mSeekbarHeight;

    /**

    • Size of text mark.
      */
      private int mTextSize;

    /**

    • Space between the text and the seekbar
      */
      private int mMarginBetween;

    /**

    • Length of every part. As we divide some parts according to marks.
      */
      private int mPartLength;

    /**

    • Contents of text mark.
      */
      private CharSequence[] mTextArray;

    /**
    *
    */
    private float[] mTextWidthArray;

    private Rect mPaddingRect;
    private Rect mLeftCursorRect;
    private Rect mRightCursorRect;

    private RectF mSeekbarRect;
    private RectF mSeekbarRectSelected;

    private float mLeftCursorIndex = 0;
    private float mRightCursorIndex = 1.0f;
    private int mLeftCursorNextIndex = 0;
    private int mRightCursorNextIndex = 1;

    private Paint mPaint;

    private int mLeftPointerLastX;
    private int mRightPointerLastX;

    private int mLeftPointerID = -1;
    private int mRightPointerID = -1;

    private boolean mLeftHited;
    private boolean mRightHited;
    private boolean isAutoMove;

    private int mRightBoundary;

    private OnCursorChangeListener mListener;

    private Rect[] mClickRectArray;
    private int mClickIndex = -1;
    private int mClickDownLastX = -1;
    private int mClickDownLastY = -1;

    public RangeSeekbar(Context context) {
    this(context, null, 0);
    }

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

    public RangeSeekbar(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    applyConfig(context, attrs);
    
    if (mPaddingRect == null) {
        mPaddingRect = new Rect();
    }
    mPaddingRect.left = getPaddingLeft();
    mPaddingRect.top = getPaddingTop();
    mPaddingRect.right = getPaddingRight();
    mPaddingRect.bottom = getPaddingBottom();
    
    mLeftCursorRect = new Rect();
    mRightCursorRect = new Rect();
    
    mSeekbarRect = new RectF();
    mSeekbarRectSelected = new RectF();
    
    if (mTextArray != null) {
        mTextWidthArray = new float[mTextArray.length];
        mClickRectArray = new Rect[mTextArray.length];
    }
    
    mLeftScroller = new Scroller(context, new DecelerateInterpolator());
    mRightScroller = new Scroller(context, new DecelerateInterpolator());
    
    initPaint();
    initTextWidthArray();
    
    setWillNotDraw(false);
    setFocusable(true);
    setClickable(true);
    

    }

    private void applyConfig(Context context, AttributeSet attrs) {
    if (attrs == null) {
    return;
    }

    TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.RangeSeekbar);
    
    mDuration = a.getInteger(R.styleable.RangeSeekbar_autoMoveDuration,
            DEFAULT_DURATION);
    
    mLeftCursorBG = a
            .getDrawable(R.styleable.RangeSeekbar_leftCursorBackground);
    mRightCursorBG = a
            .getDrawable(R.styleable.RangeSeekbar_rightCursorBackground);
    
    mTextColorNormal = a.getColor(R.styleable.RangeSeekbar_textColorNormal,
            Color.BLACK);
    mTextColorSelected = a.getColor(
            R.styleable.RangeSeekbar_textColorSelected,
            Color.rgb(242, 79, 115));
    
    mSeekbarColorNormal = a.getColor(
            R.styleable.RangeSeekbar_seekbarColorNormal,
            Color.rgb(218, 215, 215));
    mSeekbarColorSelected = a.getColor(
            R.styleable.RangeSeekbar_seekbarColorSelected,
            Color.rgb(242, 79, 115));
    isAutoMove = a.getBoolean(R.styleable.RangeSeekbar_isAutoMove, true);
    
    mSeekbarHeight = (int) a.getDimension(
            R.styleable.RangeSeekbar_seekbarHeight, 10);
    mTextSize = (int) a.getDimension(R.styleable.RangeSeekbar_seekbar_textSize, 15);
    mMarginBetween = (int) a.getDimension(
            R.styleable.RangeSeekbar_spaceBetween, 15);
    
    mTextArray = a.getTextArray(R.styleable.RangeSeekbar_markTextArray);
    if (mTextArray != null && mTextArray.length > 0) {
        mLeftCursorIndex = 0;
        mRightCursorIndex = mTextArray.length - 1;
        mRightCursorNextIndex = (int) mRightCursorIndex;
    }
    
    a.recycle();
    

    }

    private void initPaint() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    mPaint.setAntiAlias(true);
    mPaint.setStyle(Style.FILL);
    mPaint.setTextSize(mTextSize);
    

    }

    private void initTextWidthArray() {
    if (mTextArray != null && mTextArray.length > 0) {
    final int length = mTextArray.length;
    for (int i = 0; i < length; i++) {
    mTextWidthArray[i] = mPaint.measureText(mTextArray[i]
    .toString());
    }
    }
    }

    @OverRide
    public void setPadding(int left, int top, int right, int bottom) {
    super.setPadding(left, top, right, bottom);

    if (mPaddingRect == null) {
        mPaddingRect = new Rect();
    }
    mPaddingRect.left = left;
    mPaddingRect.top = top;
    mPaddingRect.right = right;
    mPaddingRect.bottom = bottom;
    

    }

    @OverRide
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    final int leftPointerH = mLeftCursorBG.getIntrinsicHeight();
    final int rightPointerH = mRightCursorBG.getIntrinsicHeight();
    
    // Get max height between left and right cursor.
    final int maxOfCursor = Math.max(leftPointerH, rightPointerH);
    // Then get max height between seekbar and cursor.
    final int maxOfCursorAndSeekbar = Math.max(mSeekbarHeight, maxOfCursor);
    // So we get the needed height.
    int heightNeeded = maxOfCursorAndSeekbar + mMarginBetween + mTextSize
            + mPaddingRect.top + mPaddingRect.bottom;
    
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(
            Math.max(heightSize, heightNeeded), MeasureSpec.EXACTLY);
    
    final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    
    mSeekbarRect.left = mPaddingRect.left
            + mLeftCursorBG.getIntrinsicWidth() / 2;
    mSeekbarRect.right = widthSize - mPaddingRect.right
            - mRightCursorBG.getIntrinsicWidth() / 2;
    mSeekbarRect.top = mPaddingRect.top + mTextSize + mMarginBetween;
    mSeekbarRect.bottom = mSeekbarRect.top + mSeekbarHeight;
    
    mSeekbarRectSelected.top = mSeekbarRect.top;
    mSeekbarRectSelected.bottom = mSeekbarRect.bottom;
    
    mPartLength = ((int) (mSeekbarRect.right - mSeekbarRect.left))
            / (mTextArray.length - 1);
    
    mRightBoundary = (int) (mSeekbarRect.right + mRightCursorBG
            .getIntrinsicWidth() / 2);
    
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    

    }

    @OverRide
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    /*** Draw text marks ***/
    final int length = mTextArray.length;
    mPaint.setTextSize(mTextSize);
    for (int i = 0; i < length; i++) {
        if ((i > mLeftCursorIndex && i < mRightCursorIndex)
                || (i == mLeftCursorIndex || i == mRightCursorIndex)) {
            mPaint.setColor(mTextColorSelected);
        } else {
            mPaint.setColor(mTextColorNormal);
        }
    
        final String text2draw = mTextArray[i].toString();
        final float textWidth = mTextWidthArray[i];
    
        float textDrawLeft = 0;
        // The last text mark's draw location should be adjust.
        if (i == length - 1) {
            textDrawLeft = mSeekbarRect.right
                    + (mRightCursorBG.getIntrinsicWidth() / 2) - textWidth;
        } else {
            textDrawLeft = mSeekbarRect.left + i * mPartLength - textWidth
                    / 2;
        }
    
        canvas.drawText(text2draw, textDrawLeft, mPaddingRect.top
                + mTextSize, mPaint);
    
        Rect rect = mClickRectArray[i];
        if (rect == null) {
            rect = new Rect();
            rect.top = mPaddingRect.top;
            rect.bottom = rect.top + mTextSize + mMarginBetween
                    + mSeekbarHeight;
            rect.left = (int) textDrawLeft;
            rect.right = (int) (rect.left + textWidth);
    
            mClickRectArray[i] = rect;
        }
    }
    
    /*** Draw seekbar ***/
    final float radius = (float) mSeekbarHeight / 2;
    mSeekbarRectSelected.left = mSeekbarRect.left + mPartLength
            * mLeftCursorIndex;
    mSeekbarRectSelected.right = mSeekbarRect.left + mPartLength
            * mRightCursorIndex;
    // If whole of seekbar is selected, just draw seekbar with selected
    // color.
    if (mLeftCursorIndex == 0 && mRightCursorIndex == length - 1) {
        mPaint.setColor(mSeekbarColorSelected);
        canvas.drawRoundRect(mSeekbarRect, radius, radius, mPaint);
    } else {
        // Draw background first.
        mPaint.setColor(mSeekbarColorNormal);
        canvas.drawRoundRect(mSeekbarRect, radius, radius, mPaint);
    
        // Draw selected part.
        mPaint.setColor(mSeekbarColorSelected);
        // Can draw rounded rectangle, but original rectangle is enough.
        // Because edges of selected part will be covered by cursors.
        canvas.drawRect(mSeekbarRectSelected, mPaint);
    }
    
    /*** Draw cursors ***/
    // left cursor first
    final int leftWidth = mLeftCursorBG.getIntrinsicWidth();
    final int leftHieght = mLeftCursorBG.getIntrinsicHeight();
    final int leftLeft = (int) (mSeekbarRectSelected.left - (float) leftWidth / 2);
    final int leftTop = (int) ((mSeekbarRect.top + mSeekbarHeight / 2) - (leftHieght / 2));
    mLeftCursorRect.left = leftLeft;
    mLeftCursorRect.top = leftTop;
    mLeftCursorRect.right = leftLeft + leftWidth;
    mLeftCursorRect.bottom = leftTop + leftHieght;
    mLeftCursorBG.setBounds(mLeftCursorRect);
    mLeftCursorBG.draw(canvas);
    
    // right cursor second
    final int rightWidth = mRightCursorBG.getIntrinsicWidth();
    final int rightHeight = mRightCursorBG.getIntrinsicHeight();
    final int rightLeft = (int) (mSeekbarRectSelected.right - (float) rightWidth / 2);
    final int rightTop = (int) ((mSeekbarRectSelected.top + mSeekbarHeight / 2) - (rightHeight / 2));
    mRightCursorRect.left = rightLeft;
    mRightCursorRect.top = rightTop;
    mRightCursorRect.right = rightLeft + rightWidth;
    mRightCursorRect.bottom = rightTop + rightHeight;
    mRightCursorBG.setBounds(mRightCursorRect);
    mRightCursorBG.draw(canvas);
    

    }

    @OverRide
    public boolean onTouchEvent(MotionEvent event) {
    if (getParent() != null) {
    getParent().requestDisallowInterceptTouchEvent(true);
    }

    // For multiple touch
    final int action = event.getActionMasked();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
    
            handleTouchDown(event);
    
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
    
            handleTouchDown(event);
    
            break;
        case MotionEvent.ACTION_MOVE:
    
            handleTouchMove(event);
    
            break;
        case MotionEvent.ACTION_POINTER_UP:
    
            handleTouchUp(event);
    
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
    
            handleTouchUp(event);
            mClickIndex = -1;
            mClickDownLastX = -1;
            mClickDownLastY = -1;
    
            break;
    }
    
    return super.onTouchEvent(event);
    

    }

    private void handleTouchDown(MotionEvent event) {
    final int actionIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    final int downX = (int) event.getX(actionIndex);
    final int downY = (int) event.getY(actionIndex);

    if (mLeftCursorRect.contains(downX, downY)) {
        if (mLeftHited) {
            return;
        }
    
        // If hit, change state of drawable, and record id of touch pointer.
        mLeftPointerLastX = downX;
        mLeftCursorBG.setState(mPressedEnableState);
        mLeftPointerID = event.getPointerId(actionIndex);
        mLeftHited = true;
    
        invalidate();
    } else if (mRightCursorRect.contains(downX, downY)) {
        if (mRightHited) {
            return;
        }
    
        mRightPointerLastX = downX;
        mRightCursorBG.setState(mPressedEnableState);
        mRightPointerID = event.getPointerId(actionIndex);
        mRightHited = true;
    
        invalidate();
    } else {
        // If touch x-y not be contained in cursor,
        // then we check if it in click areas
        final int clickBoundaryTop = mClickRectArray[0].top;
        final int clickBoundaryBottom = mClickRectArray[0].bottom;
        mClickDownLastX = downX;
        mClickDownLastY = downY;
    
        // Step one : if in boundary of total Y.
        if (downY < clickBoundaryTop || downY > clickBoundaryBottom) {
            mClickIndex = -1;
            return;
        }
    
        // Step two: find nearest mark in x-axis
        final int partIndex = (int) ((downX - mSeekbarRect.left) / mPartLength);
        final int partDelta = (int) ((downX - mSeekbarRect.left) % mPartLength);
        if (partDelta < mPartLength / 2) {
            mClickIndex = partIndex;
        } else if (partDelta > mPartLength / 2) {
            mClickIndex = partIndex + 1;
        }
    
        if (mClickIndex == mLeftCursorIndex
                || mClickIndex == mRightCursorIndex) {
            mClickIndex = -1;
            return;
        }
    
        // Step three: check contain
        if (!mClickRectArray[mClickIndex].contains(downX, downY)) {
            mClickIndex = -1;
        }
    }
    

    }

    private void handleTouchUp(MotionEvent event) {
    final int actionIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    final int actionID = event.getPointerId(actionIndex);

    if (actionID == mLeftPointerID) {
        if (!mLeftHited) {
            return;
        }
    
        // If cursor between in tow mark locations, it should be located on
        // the lower or higher one.
    
        // step 1:Calculate the offset with lower mark.
        final int lower = (int) Math.floor(mLeftCursorIndex);
        final int higher = (int) Math.ceil(mLeftCursorIndex);
    
        final float offset = mLeftCursorIndex - lower;
        if (offset != 0) {
    
            // step 2:Decide which mark will go to.
            if (offset < 0.5f) {
                // If left cursor want to be located on lower mark, go ahead
                // guys.
                // Because right cursor will never appear lower than the
                // left one.
                mLeftCursorNextIndex = lower;
            } else if (offset > 0.5f) {
                mLeftCursorNextIndex = higher;
                // If left cursor want to be located on higher mark,
                // situation becomes a little complicated.
                // We should check that whether distance between left and
                // right cursor is less than 1, and next index of left
                // cursor is difference with current
                // of right one.
                if (Math.abs(mLeftCursorIndex - mRightCursorIndex) <= 1
                        && mLeftCursorNextIndex == mRightCursorNextIndex) {
                    // Left can not go to the higher, just to the lower one.
                    mLeftCursorNextIndex = lower;
                }
            }
    
            // step 3: Move to.
            if (!mLeftScroller.computeScrollOffset()) {
                final int fromX = (int) (mLeftCursorIndex * mPartLength);
    
                mLeftScroller.startScroll(fromX, 0, mLeftCursorNextIndex
                        * mPartLength - fromX, 0, mDuration);
    
                triggleCallback(true, mLeftCursorNextIndex);
            }
        }
    
        // Reset values of parameters
        mLeftPointerLastX = 0;
        mLeftCursorBG.setState(mUnPresseEanabledState);
        mLeftPointerID = -1;
        mLeftHited = false;
    
        invalidate();
    } else if (actionID == mRightPointerID) {
        if (!mRightHited) {
            return;
        }
    
        final int lower = (int) Math.floor(mRightCursorIndex);
        final int higher = (int) Math.ceil(mRightCursorIndex);
    
        final float offset = mRightCursorIndex - lower;
        if (offset != 0) {
    
            if (offset > 0.5f) {
                mRightCursorNextIndex = higher;
            } else if (offset < 0.5f) {
                mRightCursorNextIndex = lower;
                if (Math.abs(mLeftCursorIndex - mRightCursorIndex) <= 1
                        && mRightCursorNextIndex == mLeftCursorNextIndex) {
                    mRightCursorNextIndex = higher;
                }
            }
    
            if (!mRightScroller.computeScrollOffset()) {
                final int fromX = (int) (mRightCursorIndex * mPartLength);
    
                mRightScroller.startScroll(fromX, 0, mRightCursorNextIndex
                        * mPartLength - fromX, 0, mDuration);
    
                triggleCallback(false, mRightCursorNextIndex);
            }
        }
    
        mRightPointerLastX = 0;
        mLeftCursorBG.setState(mUnPresseEanabledState);
        mRightPointerID = -1;
        mRightHited = false;
    
        invalidate();
    } else {
        final int pointerIndex = event.findPointerIndex(actionID);
        final int upX = (int) event.getX(pointerIndex);
        final int upY = (int) event.getY(pointerIndex);
    
        if (mClickIndex != -1
                && mClickRectArray[mClickIndex].contains(upX, upY)) {
            // Find nearest cursor
            final float distance2LeftCursor = Math.abs(mLeftCursorIndex
                    - mClickIndex);
            final float distance2Right = Math.abs(mRightCursorIndex
                    - mClickIndex);
    
            final boolean moveLeft = distance2LeftCursor <= distance2Right;
            int fromX = 0;
            if (moveLeft) {
                if (!mLeftScroller.computeScrollOffset()) {
                    mLeftCursorNextIndex = mClickIndex;
                    fromX = (int) (mLeftCursorIndex * mPartLength);
                    mLeftScroller.startScroll(fromX, 0,
                            mLeftCursorNextIndex * mPartLength - fromX, 0,
                            mDuration);
    
                    triggleCallback(true, mLeftCursorNextIndex);
    
                    invalidate();
                }
            } else {
                if (!mRightScroller.computeScrollOffset()) {
                    mRightCursorNextIndex = mClickIndex;
                    fromX = (int) (mRightCursorIndex * mPartLength);
                    mRightScroller.startScroll(fromX, 0,
                            mRightCursorNextIndex * mPartLength - fromX, 0,
                            mDuration);
    
                    triggleCallback(false, mRightCursorNextIndex);
    
                    invalidate();
                }
            }
        }
    }
    

    }

    private void handleTouchMove(MotionEvent event) {
    if (mClickIndex != -1) {
    final int actionIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    final int x = (int) event.getX(actionIndex);
    final int y = (int) event.getY(actionIndex);

        if (!mClickRectArray[mClickIndex].contains(x, y)) {
            mClickIndex = -1;
        }
    }
    
    if (mLeftHited && mLeftPointerID != -1) {
    
        final int index = event.findPointerIndex(mLeftPointerID);
        final float x = event.getX(index);
    
        float deltaX = x - mLeftPointerLastX;
        mLeftPointerLastX = (int) x;
    
        DIRECTION direction = (deltaX < 0 ? DIRECTION.LEFT
                : DIRECTION.RIGHT);
    
        if (direction == DIRECTION.LEFT && mLeftCursorIndex == 0) {
            return;
        }
    
        // Check whether cursor will move out of boundary
        if (mLeftCursorRect.left + deltaX < mPaddingRect.left) {
            mLeftCursorIndex = 0;
            invalidate();
            return;
        }
    
        // Check whether left and right cursor will collision.
        if (mLeftCursorRect.right + deltaX >= mRightCursorRect.left) {
            // Check whether right cursor is in "Touch" mode( if in touch
            // mode, represent that we can not move it at all), or right
            // cursor reach the boundary.
            if (mRightHited || mRightCursorIndex == mTextArray.length - 1
                    || mRightScroller.computeScrollOffset()) {
                // Just move left cursor to the left side of right one.
                deltaX = mRightCursorRect.left - mLeftCursorRect.right;
            } else {
                // Move right cursor to higher location.
                final int maxMarkIndex = mTextArray.length - 1;
    
                if (mRightCursorIndex <= maxMarkIndex - 1 && isAutoMove) {
                    mRightCursorNextIndex = (int) (mRightCursorIndex + 1);
    
                    if (!mRightScroller.computeScrollOffset()) {
                        final int fromX = (int) (mRightCursorIndex * mPartLength);
    
                        mRightScroller
                                .startScroll(fromX, 0,
                                        mRightCursorNextIndex * mPartLength
                                                - fromX, 0, mDuration);
                        triggleCallback(false, mRightCursorNextIndex);
                    }
                }
            }
        }
    
        // After some calculate, if deltaX is still be zero, do quick
        // return.
        if (isAutoMove && deltaX == 0) {
            return;
        }
    
        // Calculate the movement.
        final float moveX = deltaX / mPartLength;
        mLeftCursorIndex += moveX;
        if (mLeftCursorIndex > mRightCursorIndex) {
            mRightCursorIndex = mLeftCursorIndex;
        }
        invalidate();
    }
    
    if (mRightHited && mRightPointerID != -1) {
    
        final int index = event.findPointerIndex(mRightPointerID);
        final float x = event.getX(index);
    
        float deltaX = x - mRightPointerLastX;
        mRightPointerLastX = (int) x;
    
        DIRECTION direction = (deltaX < 0 ? DIRECTION.LEFT
                : DIRECTION.RIGHT);
    
        final int maxIndex = mTextArray.length - 1;
        if (direction == DIRECTION.RIGHT && mRightCursorIndex == maxIndex) {
            return;
        }
    
        if (mRightCursorRect.right + deltaX > mRightBoundary) {
            deltaX = mRightBoundary - mRightCursorRect.right;
        }
    
        final int maxMarkIndex = mTextArray.length - 1;
        if (direction == DIRECTION.RIGHT
                && mRightCursorIndex == maxMarkIndex) {
            return;
        }
    
        if (mRightCursorRect.left + deltaX < mLeftCursorRect.right) {
            if (mLeftHited || mLeftCursorIndex == 0
                    || mLeftScroller.computeScrollOffset()) {
                deltaX = mLeftCursorRect.right - mRightCursorRect.left;
            } else {
                if (mLeftCursorIndex >= 1 && isAutoMove) {
                    mLeftCursorNextIndex = (int) (mLeftCursorIndex - 1);
    
                    if (!mLeftScroller.computeScrollOffset()) {
                        final int fromX = (int) (mLeftCursorIndex * mPartLength);
                        mLeftScroller.startScroll(fromX, 0,
                                mLeftCursorNextIndex * mPartLength - fromX,
                                0, mDuration);
                        triggleCallback(true, mLeftCursorNextIndex);
                    }
                }
            }
        }
    
        if (isAutoMove && deltaX == 0) {
            return;
        }
    
        final float moveX = deltaX / mPartLength;
        mRightCursorIndex += moveX;
        if (mRightCursorIndex < mLeftCursorIndex) {
            mLeftCursorIndex = mRightCursorIndex;
    
        }
        invalidate();
    }
    

    }

    @OverRide
    public void computeScroll() {
    if (mLeftScroller.computeScrollOffset()) {
    final int deltaX = mLeftScroller.getCurrX();

        mLeftCursorIndex = (float) deltaX / mPartLength;
    
        invalidate();
    }
    
    if (mRightScroller.computeScrollOffset()) {
        final int deltaX = mRightScroller.getCurrX();
    
        mRightCursorIndex = (float) deltaX / mPartLength;
    
        invalidate();
    }
    

    }

    private void triggleCallback(boolean isLeft, int location) {

    if (mListener == null) {
        return;
    }
    try {
    
    
        if (isLeft) {
            mListener.onLeftCursorChanged(location,
                    mTextArray[location].toString());
        } else {
            mListener.onRightCursorChanged(location,
                    mTextArray[location].toString());
        }
    }catch (Exception e){
        mLeftCursorIndex = 0;
        mRightCursorIndex = mTextArray.length - 1;
        mRightCursorNextIndex = (int) mRightCursorIndex;
        invalidate();
    }
    

    }

    public void setLeftSelection(int partIndex) {
    if (partIndex >= mTextArray.length - 1 || partIndex < 0) {
    throw new IllegalArgumentException(
    "Index should from 0 to size of text array minus 2!");
    }

    if (partIndex != mLeftCursorIndex) {
        if (!mLeftScroller.isFinished()) {
            mLeftScroller.abortAnimation();
        }
        mLeftCursorNextIndex = partIndex;
        final int leftFromX = (int) (mLeftCursorIndex * mPartLength);
        mLeftScroller.startScroll(leftFromX, 0, mLeftCursorNextIndex
                * mPartLength - leftFromX, 0, mDuration);
        triggleCallback(true, mLeftCursorNextIndex);
    
        if (mRightCursorIndex <= mLeftCursorNextIndex) {
            if (!mRightScroller.isFinished()) {
                mRightScroller.abortAnimation();
            }
            mRightCursorNextIndex = mLeftCursorNextIndex + 1;
            final int rightFromX = (int) (mRightCursorIndex * mPartLength);
            mRightScroller.startScroll(rightFromX, 0, mRightCursorNextIndex
                    * mPartLength - rightFromX, 0, mDuration);
            triggleCallback(false, mRightCursorNextIndex);
        }
    
        invalidate();
    }
    

    }

    public void setRightSelection(int partIndex) {
    if (partIndex >= mTextArray.length || partIndex < 0) {
    throw new IllegalArgumentException(
    "Index should from 1 to size of text array minus 1!");
    }

    if (partIndex != mRightCursorIndex) {
        if (!mRightScroller.isFinished()) {
            mRightScroller.abortAnimation();
        }
    
        mRightCursorNextIndex = partIndex;
        final int rightFromX = (int) (mPartLength * mRightCursorIndex);
        mRightScroller.startScroll(rightFromX, 0, mRightCursorNextIndex
                * mPartLength - rightFromX, 0, mDuration);
        triggleCallback(false, mRightCursorNextIndex);
    
        if (mLeftCursorIndex >= mRightCursorNextIndex) {
            if (!mLeftScroller.isFinished()) {
                mLeftScroller.abortAnimation();
            }
    
            mLeftCursorNextIndex = mRightCursorNextIndex - 1;
            final int leftFromX = (int) (mLeftCursorIndex * mPartLength);
            mLeftScroller.startScroll(leftFromX, 0, mLeftCursorNextIndex
                    * mPartLength - leftFromX, 0, mDuration);
            triggleCallback(true, mLeftCursorNextIndex);
        }
        invalidate();
    }
    

    }

    public void setLeftCursorBackground(Drawable drawable) {
    if (drawable == null) {
    throw new IllegalArgumentException(
    "Do you want to make left cursor invisible?");
    }

    mLeftCursorBG = drawable;
    
    requestLayout();
    invalidate();
    

    }

    public void setLeftCursorBackground(int resID) {
    if (resID < 0) {
    throw new IllegalArgumentException(
    "Do you want to make left cursor invisible?");
    }

    mLeftCursorBG = getResources().getDrawable(resID);
    
    requestLayout();
    invalidate();
    

    }

    public void setRightCursorBackground(Drawable drawable) {
    if (drawable == null) {
    throw new IllegalArgumentException(
    "Do you want to make right cursor invisible?");
    }

    mRightCursorBG = drawable;
    
    requestLayout();
    invalidate();
    

    }

    public void setRightCursorBackground(int resID) {
    if (resID < 0) {
    throw new IllegalArgumentException(
    "Do you want to make right cursor invisible?");
    }

    mRightCursorBG = getResources().getDrawable(resID);
    
    requestLayout();
    invalidate();
    

    }

    public void setTextMarkColorNormal(int color) {
    if (color == Color.TRANSPARENT) {
    throw new IllegalArgumentException(
    "Do you want to make text mark invisible?");
    }

    mTextColorNormal = color;
    
    invalidate();
    

    }

    public void setTextMarkColorSelected(int color) {
    if (color == Color.TRANSPARENT) {
    throw new IllegalArgumentException(
    "Do you want to make text mark invisible?");
    }

    mTextColorSelected = color;
    
    invalidate();
    

    }

    public void setSeekbarColorNormal(int color) {
    if (color == Color.TRANSPARENT) {
    throw new IllegalArgumentException(
    "Do you want to make seekbar invisible?");
    }

    mSeekbarColorNormal = color;
    
    invalidate();
    

    }

    public void setSeekbarColorSelected(int color) {
    if (color <= 0 || color == Color.TRANSPARENT) {
    throw new IllegalArgumentException(
    "Do you want to make seekbar invisible?");
    }

    mSeekbarColorSelected = color;
    
    invalidate();
    

    }

    /**

    • In pixels. Users should call this method before view is added to parent.
      *

    • @param height
      */
      public void setSeekbarHeight(int height) {
      if (height <= 0) {
      throw new IllegalArgumentException(
      "Height of seekbar can not less than 0!");
      }

      mSeekbarHeight = height;
      }

    /**

    • To set space between text mark and seekbar.
      *

    • @param space
      */
      public void setSpaceBetween(int space) {
      if (space < 0) {
      throw new IllegalArgumentException(
      "Space between text mark and seekbar can not less than 0!");
      }

      mMarginBetween = space;

      requestLayout();
      invalidate();
      }

    /**

    • This method should be called after {@link #setTextMarkSize(int)}, because

    • view will measure size of text mark by paint.
      */
      public void setTextMarks(CharSequence... marks) {
      if (marks == null || marks.length == 0) {
      throw new IllegalArgumentException(
      "Text array is null, how can i do...");
      }

      mTextArray = marks;
      mLeftCursorIndex = 0;
      mRightCursorIndex = mTextArray.length - 1;
      mRightCursorNextIndex = (int) mRightCursorIndex;
      mTextWidthArray = new float[marks.length];
      mClickRectArray = new Rect[mTextArray.length];
      initTextWidthArray();

      requestLayout();
      invalidate();
      }

    /**

    • Users should call this method before view is added to parent.
      *

    • @param size in pixels
      */
      public void setTextMarkSize(int size) {
      if (size < 0) {
      return;
      }

      mTextSize = size;
      mPaint.setTextSize(size);
      }

    public int getLeftCursorIndex() {
    return (int) mLeftCursorIndex;
    }

    public int getRightCursorIndex() {
    return (int) mRightCursorIndex;
    }

    public void setOnCursorChangeListener(OnCursorChangeListener l) {
    mListener = l;
    }

    public interface OnCursorChangeListener {
    void onLeftCursorChanged(int location, String textMark);

    void onRightCursorChanged(int location, String textMark);
    

    }
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.