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

Related tags

SeekBar RangeSeekbar
Overview

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.

You might also like...
enable users to slide card to the left or right smoothly and continuously
enable users to slide card to the left or right smoothly and continuously

有图有真相 模仿探探首页的卡片滑动效果: 不得不说,探探的ui效果真的很赞。在着手这个project之前,我没有参考过github上其它类似的开源项目。所以,如果这个project重复造了轮子,请不要打我。 在这个仓库竣工之时,有一个小伙伴发了我另一个开源工程,颇有相似之处。我下载了源码,导入了st

A Tinder-like Android library to create the swipe cards effect. You can swipe left or right to like or dislike the content.

Swipecards Travis master: A Tinder-like cards effect as of August 2014. You can swipe left or right to like or dislike the content. The library create

Custom ViewPager that allows to block left or right swipe gestures.
Custom ViewPager that allows to block left or right swipe gestures.

SwipeDirectionViewPager Introduction Custom ViewPager that allows to block swiping right or left where the ViewPager child fragments set the scroll di

This is a repo for implementing Map pan or drag (up, down, right ,left) to load more places on the Google Map for Android

Challenge Display restaurants around the user’s current location on a map ○ Use the FourSquare Search API to query for restaurants: https://developer.

RTL marquee text view android right to left moving text - persian - farsi - arabic - urdo
RTL marquee text view android right to left moving text - persian - farsi - arabic - urdo

RTL marquee text view android right to left moving text - persian - farsi - arabic - urdo

Esp touch flutter plugin - Client-side (mobile) Android Flutter implementation for ESP-Touch protocol

esp_touch_flutter_plugin Client-side (mobile) Android Flutter implementation for

A flexible view for providing a limited rect window into a large data set,just like a two-dimensional RecyclerView.  It different from RecyclerView is that it's two-dimensional(just like a Panel) and it pin the itemView of first row and first column in their original location. Dubins path refers to the shortest curve that connects two points in the two-dimensional Euclidean plane
Dubins path refers to the shortest curve that connects two points in the two-dimensional Euclidean plane

Dubins Dubins path refers to the shortest curve that connects two points in the two-dimensional Euclidean plane (i.e. x-y plane) with a constraint on

Android Spinner Dialog Library supported on both Java and Kotlin, Use for single or multi selection of choice
Android Spinner Dialog Library supported on both Java and Kotlin, Use for single or multi selection of choice

SpinnerDialog Android Spinner Dialog Library, Use for single or multi selection of choice Android UI Download To include SpinnerDialog in your project

Convert audio files inside your Android app easily. Supported formats: AAC, MP3, M4A, WMA, WAV and FLAC.

AndroidAudioConverter Convert audio files inside your Android app easily. This is a wrapper of FFmpeg-Android-Java lib. Supported formats: AAC MP3 M4A

An Animated Scrolling View for React Native applications, supported on both iOS and Android

react-native-focused-scroll An Animated Scrolling View for React Native applications, supported on both iOS and Android Preview react-native-focus-scr

A custom SwipeRefreshLayout to support the pull-to-refresh featrue.RecyclerView,ListView,GridView,NestedScrollView,ScrollView are supported.
A custom SwipeRefreshLayout to support the pull-to-refresh featrue.RecyclerView,ListView,GridView,NestedScrollView,ScrollView are supported.

SuperSwipeRefreshLayout A custom SwipeRefreshLayout to support the pull-to-refresh featrue.You can custom your header view and footer view. RecyclerVi

Load the online comic content supported by any plug-in project to provide a convenient online comic viewing experience
Load the online comic content supported by any plug-in project to provide a convenient online comic viewing experience

kinoko Awesome manga reader. Download Description Load the online comic content supported by any plug-in project to provide a convenient online comic

This directory contains the model files (protos) for the Bar ServiceThis directory contains the model files (protos) for the Bar Service
This directory contains the model files (protos) for the Bar ServiceThis directory contains the model files (protos) for the Bar Service

This directory contains the model files (protos) for the Bar ServiceThis directory contains the model files (protos) for the Bar Service

 🌏 Android/IDEA localization plugin. supports multiple languages and multiple translators.
🌏 Android/IDEA localization plugin. supports multiple languages and multiple translators.

English | 简体中文 AndroidLocalizePlugin 🌏 Android/IDEA localization plugin. supports multiple languages and multiple translators. Features Multiple tran

 A MaterialChipSetWidget is used to hold multiple chipsets 🤩 and each chipset has multiple values. 🔖
A MaterialChipSetWidget is used to hold multiple chipsets 🤩 and each chipset has multiple values. 🔖

A MaterialChipSetWidget is used to hold multiple chipsets 🤩 and each chipset has multiple values. 🔖

High level parsing to ensure your input is in the right shape and satisfies all constraints that business logic requires.

Parsix High level parsing to ensure your input is in the right shape and satisfies all constraints that business logic requires. It is highly inspired

A FloatingActionButton subclass that shows a counter badge on right top corner
A FloatingActionButton subclass that shows a counter badge on right top corner

CounterFab A FloatingActionButton subclass that shows a counter badge on right top corner It's also used by Louvre library. Installation Include the l

Comments
  • setLeftSelection and setRightSelection not working as expected

    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...

    opened by siddharthKarnik 2
  • Fix broken headings in Markdown files

    Fix broken headings in Markdown files

    GitHub changed the way Markdown headings are parsed, so this change fixes it.

    See bryant1410/readmesfix for more information.

    Tackles bryant1410/readmesfix#1

    opened by bryant1410 0
  • I just want to only one point.

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

      } }

    opened by ohshi000 3
  • Seekbar like whatsapp such that when i slide between the two thumbs then both the thumbs gets dragged

    Seekbar like whatsapp such that when i slide between the two thumbs then both the thumbs gets dragged

    I want to create a seekbar like whatsapp such that when i slide between the two thumbs then both the thumbs gets dragged and the range changes.I have seen many two thumb seekbar libraries but in all of them i can individually move both the thumbs but dragging between them doesn't result in both the thumbs getting dragged equally.Is this library support this?

    opened by bkjbkjbnkj687698698 1
Owner
dolphinWang
a funny guy
dolphinWang
Custom circular SeekBar (Circle, Semi-circle, and Ellipse) View/Widget for Android

CircularSeekBar Android CircularSeekBar Custom View/Widget This is a custom circular SeekBar. It can be used to create SeekBars that are: -Full Circle

Matt Joseph 462 Dec 19, 2022
A SeekBar suited for showing a preview of something. As seen in Google Play Movies.

PreviewSeekBar A SeekBar suited for showing a video preview. As seen in Google Play Movies Google Play Movies PreviewSeekBar's sample Build Add the fo

Rúben Sousa 3.3k Jan 3, 2023
Circular SeekBar view for Android

SeekArc What is a SeekArc? So what the heck is a SeekArc? Essentially it’s a SeekBar that has been wrapped around a circle. It acts like a SeekBar and

Neil Davies 870 Dec 10, 2022
Android circle seekbar widget inspired from: https://github.com/LarsWerkman/HoloColorPicker

Android HoloCircleSeekBar A Circle SeekBar inspired by Android Holo ColorPicker designed by Marie Schweiz and developed by Lars Werkman. How to integr

Jesus 232 Nov 10, 2022
A colorful SeekBar for picking color

ScreenShot: Attrs attr format default colorSeeds references colorBarPosition integer 0 alphaBarPosition integer 0 maxPosition integer 100 bgColor colo

Jack Fu 324 Dec 26, 2022
StartPointSeekBar is a custom view for the Android platform that makes it possible to have a SeekBar to have custom start point.

Forked/Inspired from https://code.google.com/p/range-seek-bar/ by [email protected] This solves the problem as described in http://

Gaurav Vashisth 142 Dec 29, 2022
A color picker seekbar for android.

ColorSeekBar A color picker seekbar for android. Download Use Gradle compile 'com.divyanshu.colorseekbar:colorseekbar:1.0.2' or Maven <dependency>

Divyanshu Bhargava 129 Nov 29, 2022
Ranger is custom view which able to act like android seekbar.

Ranger is custom view which able to act like android seekbar.

Enes Zor 3 Oct 17, 2021
A simple material-based support library to bring consistent SeekBars on Android 14 and above

SeekBarCompat A support library for the material design SeekBar in Android for API 14 and above. Screenshot On APIs 14 and above - Seekbars would look

Ahmed Rizwan 157 Dec 27, 2022
A RatingBar library for android, you can customize size, spacing, color and image easily, and support right to left.

A RatingBar library for android, you can customize size, spacing, color and image easily, and support right to left.

dqq 300 Aug 29, 2021