Java tutorial
/* * 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.leeon.blank.widget; 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.support.v4.view.ViewConfigurationCompat; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.Scroller; import com.leeon.blank.R; import com.leeon.blank.util.LogUtil; /** * A seekbar contains two cursor(left and right). Multiple touch supported. */ public class RangeBarNew extends View { private boolean isInfinite = true; private float downY; private int mTouchSlop; private enum DIRECTION { LEFT, RIGHT } private double slopDistance = 2; private int mDuration = 100; /** * Scroller 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; // private int cursorHeight; // private Drawable indicatorDrawable; // private int indicatorHeight; /** * Represent states. */ private int[] mPressedEnableState = new int[] { android.R.attr.state_pressed, android.R.attr.state_enabled }; private int[] mUnPressedEanabledState = new int[] { -android.R.attr.state_pressed, android.R.attr.state_enabled }; /** * Colors of text and rangeBar in different states. */ private int mTextColorNormal; private int mRangeBarColorNormal; private int mRangeBarColorSelected; /** * Height of rangeBar */ private int mRangeBarHeight; /** * Size of text mark. */ private int mTextSize; /** * Space between the text and the rangeBar */ private int mMarginBetween; /** * Length of every part. As we divide some parts according to marks. */ private float mPartLength, mTinyPartLength; /** * Contents of text mark. */ private int[] mTextArray = { 0, 5, 15, 20 }; // private float[] mTextWidthArray; private Rect mPaddingRect; private Rect mLeftCursorRect; private Rect mRightCursorRect; private Rect mLeftIndicatorRect; private Rect mRightIndicatorRect; private RectF mRangeBarRect; private RectF mRangeBarRectSelected; private float mLeftCursorIndex = 0; private float mRightCursorIndex = 1.0f; private float mLeftCursorPreIndex = 0; private float mRightCursorPreIndex = 1; private float mLeftCursorNextIndex = 0; private float mRightCursorNextIndex = 1; private int mValueLeft = 0, mValueRight = -1; private Paint mPaint; private int mLeftPointerLastX; private int mRightPointerLastX; private int mLeftPointerID = -1; private int mRightPointerID = -1; private boolean mLeftHit; private boolean mRightHit; private float mLeftBoundary, mRightBoundary; private OnCursorChangeListener mListener; public RangeBarNew(Context context) { this(context, null, 0); } public RangeBarNew(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RangeBarNew(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); applyConfig(context, attrs); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(ViewConfiguration.get(context)); mPaddingRect = new Rect(); mLeftCursorRect = new Rect(); mRightCursorRect = new Rect(); mLeftIndicatorRect = new Rect(); mRightIndicatorRect = new Rect(); mPaddingRect.left = getPaddingLeft(); mPaddingRect.top = getPaddingTop(); mPaddingRect.right = getPaddingRight(); mPaddingRect.bottom = getPaddingBottom(); mRangeBarRect = new RectF(); mRangeBarRectSelected = new RectF(); if (mTextArray != null) { mTextWidthArray = new float[mTextArray.length]; } mLeftScroller = new Scroller(context); mRightScroller = new Scroller(context); initPaint(); initTextWidthArray(); setWillNotDraw(false); setFocusable(true); setClickable(true); } public void restView() { 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.RangeBar); indicatorDrawable = a.getDrawable(R.styleable.RangeBar_indicatorDrawable); mLeftCursorBG = a.getDrawable(R.styleable.RangeBar_leftCursorBackground); mRightCursorBG = a.getDrawable(R.styleable.RangeBar_rightCursorBackground); mTextColorNormal = a.getColor(R.styleable.RangeBar_textColorNormal, Color.GRAY); mRangeBarColorNormal = a.getColor(R.styleable.RangeBar_barColorNormal, Color.rgb(218, 215, 215)); mRangeBarColorSelected = a.getColor(R.styleable.RangeBar_barColorSelected, Color.rgb(197, 0, 0)); mRangeBarHeight = (int) a.getDimension(R.styleable.RangeBar_barHeight, 10); mTextSize = (int) a.getDimension(R.styleable.RangeBar_textSize, 14); mMarginBetween = (int) a.getDimension(R.styleable.RangeBar_spaceBetween, 16); isInfinite = a.getBoolean(R.styleable.RangeBar_isInfinite, true); CharSequence[] charArray = a.getTextArray(R.styleable.RangeBar_markTextArray); if (null == charArray || charArray.length <= 1) { throw new IllegalArgumentException("markTextArray should at least have 2 number!"); } mTextArray = new int[isInfinite ? charArray.length + 1 : charArray.length]; for (int i = 0; i < charArray.length; i++) { mTextArray[i] = Integer.parseInt(charArray[i].toString()); if (mTextArray[i] < 0) { throw new IllegalArgumentException("markTextArray must be a positive number array"); } } if (isInfinite) { mTextArray[charArray.length] = -1; } mLeftCursorIndex = 0; mRightCursorIndex = (mTextArray.length - 1) * 5; mRightCursorNextIndex = 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] == -1 ? "" : mTextArray[i] + ""); } } } @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 leftPointerH = mLeftCursorBG.getIntrinsicHeight(); final int rightPointerH = mRightCursorBG.getIntrinsicHeight(); // Get max height between left and right cursor. cursorHeight = Math.max(leftPointerH, rightPointerH); indicatorHeight = Math.max(indicatorDrawable.getIntrinsicHeight(), 2 * mTextSize); // Then get max height between seekbar and cursor. //final int maxOfCursorAndSeekbar = Math.max(mRangeBarHeight, maxOfCursor); // So we get the needed height. int heightNeeded = mRangeBarHeight + indicatorHeight + cursorHeight + 2 * mMarginBetween + mPaddingRect.top + mPaddingRect.bottom; heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightNeeded, MeasureSpec.EXACTLY); final int widthSize = MeasureSpec.getSize(widthMeasureSpec); mRangeBarRect.left = mPaddingRect.left + mLeftCursorBG.getIntrinsicWidth() / 2; mRangeBarRect.right = widthSize - mPaddingRect.right - mRightCursorBG.getIntrinsicWidth() / 2; mRangeBarRect.top = mPaddingRect.top + indicatorHeight + mMarginBetween; mRangeBarRect.bottom = mRangeBarRect.top + mRangeBarHeight; mRangeBarRectSelected.top = mRangeBarRect.top; mRangeBarRectSelected.bottom = mRangeBarRect.bottom; if (0 != mTextArray.length - 1) { mPartLength = (mRangeBarRect.right - mRangeBarRect.left) / (mTextArray.length - 1); mTinyPartLength = mPartLength / 5; } mLeftBoundary = mRangeBarRect.left - mLeftCursorBG.getIntrinsicWidth() / 2; mRightBoundary = mRangeBarRect.right + mRightCursorBG.getIntrinsicWidth() / 2; super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); final int length = mTextArray.length; /*** Draw text marks ***/ mPaint.setTextSize(mTextSize); mPaint.setColor(mTextColorNormal); for (int i = 0; i < length; i++) { final String text2draw = mTextArray[i] == -1 ? "" : mTextArray[i] + ""; final float textWidth = mTextWidthArray[i]; float textDrawLeft; // The last text mark's draw location should be adjust. if (i == length - 1) { textDrawLeft = mRangeBarRect.right - textWidth / 2; } else { textDrawLeft = mRangeBarRect.left + i * mPartLength - textWidth / 2; } canvas.drawText(text2draw, textDrawLeft, mRangeBarRect.top - 24 - mMarginBetween, mPaint); //draw lines float lineDrawLeft = mRangeBarRect.left + i * mPartLength; canvas.drawLine(lineDrawLeft, mRangeBarRect.top, lineDrawLeft, mRangeBarRect.top - 24, mPaint); //draw short lines if (i != length - 1) { for (int j = 1; j < 5; j++) { float lineDrawLeft_short = lineDrawLeft + j * mTinyPartLength; canvas.drawLine(lineDrawLeft_short, mRangeBarRect.top, lineDrawLeft_short, mRangeBarRect.top - 18, mPaint); } } } /*** Draw seekBar ***/ final float radius = mRangeBarHeight / 4f; mRangeBarRectSelected.left = mRangeBarRect.left + mTinyPartLength * mLeftCursorIndex; mRangeBarRectSelected.right = mRangeBarRect.left + mTinyPartLength * mRightCursorIndex; // If whole of seekbar is selected, just draw seekbar with selectedcolor. if (mLeftCursorIndex == 0 && mRightCursorIndex == (length - 1) * 5) { mPaint.setColor(mRangeBarColorSelected); canvas.drawRoundRect(mRangeBarRect, radius, radius, mPaint); } else { // Draw background first. mPaint.setColor(mRangeBarColorNormal); canvas.drawRoundRect(mRangeBarRect, radius, radius, mPaint); // Draw selected part. mPaint.setColor(mRangeBarColorSelected); // Can draw rounded rectangle, but original rectangle is enough. // Because edges of selected part will be covered by cursors. canvas.drawRect(mRangeBarRectSelected, mPaint); } /*** Draw cursors ***/ // left cursor first final int leftWidth = mLeftCursorBG.getIntrinsicWidth(); final int leftHeight = mLeftCursorBG.getIntrinsicHeight(); final int leftLeft = (int) (mRangeBarRectSelected.left - leftWidth / 2f); final int leftTop = (int) (mRangeBarRect.bottom + mMarginBetween); mLeftCursorRect.left = leftLeft; mLeftCursorRect.top = leftTop; mLeftCursorRect.right = leftLeft + leftWidth; mLeftCursorRect.bottom = leftTop + leftHeight; mLeftCursorBG.setBounds(mLeftCursorRect); mLeftCursorBG.draw(canvas); // right cursor second final int rightWidth = mRightCursorBG.getIntrinsicWidth(); final int rightHeight = mRightCursorBG.getIntrinsicHeight(); final int rightLeft = (int) (mRangeBarRectSelected.right - rightWidth / 2f); final int rightTop = (int) (mRangeBarRect.bottom + mMarginBetween); mRightCursorRect.left = rightLeft; mRightCursorRect.top = rightTop; mRightCursorRect.right = rightLeft + rightWidth; mRightCursorRect.bottom = rightTop + rightHeight; mRightCursorBG.setBounds(mRightCursorRect); mRightCursorBG.draw(canvas); //Draw indicators final int indicatorWidth = Math.max(indicatorDrawable.getIntrinsicWidth(), (int) mPaint.measureText("888")); if (mLeftHit && mLeftPointerID != -1) { final int indicatorLeft = (int) (mRangeBarRectSelected.left - (float) indicatorWidth / 2); final int indicatorTop = mPaddingRect.top; mLeftIndicatorRect.left = indicatorLeft; mLeftIndicatorRect.top = indicatorTop; mLeftIndicatorRect.right = indicatorLeft + indicatorWidth; mLeftIndicatorRect.bottom = indicatorTop + indicatorHeight; indicatorDrawable.setBounds(mLeftIndicatorRect); indicatorDrawable.draw(canvas); mPaint.setColor(Color.WHITE); mValueLeft = getValueByIndex(mLeftCursorIndex); canvas.drawText(mValueLeft + "", mLeftIndicatorRect.centerX() - mPaint.measureText(mValueLeft + "") / 2, mLeftIndicatorRect.centerY() + mTextSize / 4, mPaint); } if (mRightHit && mRightPointerID != -1) { final int indicatorRightLeft = (int) (mRangeBarRectSelected.right - (float) rightWidth / 2); final int indicatorRightTop = mPaddingRect.top; mRightIndicatorRect.left = indicatorRightLeft; mRightIndicatorRect.top = indicatorRightTop; mRightIndicatorRect.right = indicatorRightLeft + indicatorWidth; mRightIndicatorRect.bottom = indicatorRightTop + indicatorHeight; indicatorDrawable.setBounds(mRightIndicatorRect); indicatorDrawable.draw(canvas); mPaint.setColor(Color.WHITE); mValueRight = getValueByIndex(mRightCursorIndex); if (mValueRight == -1) { canvas.drawText("", mRightIndicatorRect.centerX() - mPaint.measureText("") / 2, mRightIndicatorRect.centerY() + mTextSize / 4, mPaint); } else { canvas.drawText(mValueRight + "", mRightIndicatorRect.centerX() - mPaint.measureText(mValueRight + "") / 2, mRightIndicatorRect.centerY() + mTextSize / 4, mPaint); } } triggerCallback(); } @Override public boolean onTouchEvent(MotionEvent event) { getParent().requestDisallowInterceptTouchEvent(false); // For multiple touch final int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: downY = event.getY(); handleTouchDown(event); break; case MotionEvent.ACTION_POINTER_DOWN: handleTouchDown(event); break; case MotionEvent.ACTION_MOVE: final float yDiff = Math.abs(event.getY() - downY); if (yDiff > mTouchSlop) { return false; } else { getParent().requestDisallowInterceptTouchEvent(true); handleTouchMove(event); } break; case MotionEvent.ACTION_POINTER_UP: handleTouchUp(event); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: handleTouchUp(event); 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 (mLeftHit) { return; } // If hit, change state of drawable, and record id of touch pointer. mLeftPointerLastX = downX; mLeftCursorBG.setState(mPressedEnableState); mLeftPointerID = event.getPointerId(actionIndex); mLeftHit = true; invalidate(); } else if (mRightCursorRect.contains(downX, downY)) { if (mRightHit) { return; } mRightPointerLastX = downX; mRightCursorBG.setState(mPressedEnableState); mRightPointerID = event.getPointerId(actionIndex); mRightHit = true; invalidate(); } } 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 (!mLeftHit) { return; } // If cursor between in tow mark locations, it should be located on the lower or higher one. if (getDiffByX(event.getX()) == 1) { // step 1:Calculate the offset with lower mark. final float offset = mLeftCursorIndex % 5; // step 2:Decide which mark will go to. if (offset < 2.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 = (int) (mLeftCursorIndex / 5) * 5; } else { mLeftCursorNextIndex = (int) (mLeftCursorIndex / 5) * 5 + 1; // 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) <= 5 && mLeftCursorNextIndex == mRightCursorNextIndex) { // Left can not go to the higher, just to the lower one. mLeftCursorNextIndex = (int) (mLeftCursorIndex / 5) * 5; } } // step 3: Move to. if (!mLeftScroller.computeScrollOffset()) { final int fromX = (int) (mLeftCursorIndex * mTinyPartLength); mLeftScroller.startScroll(fromX, 0, (int) (mLeftCursorNextIndex * mTinyPartLength - fromX), 0, mDuration); } if (mLeftCursorNextIndex != mLeftCursorPreIndex) { mLeftCursorPreIndex = mLeftCursorNextIndex; } } // Reset values of parameters mLeftPointerLastX = 0; mLeftCursorBG.setState(mUnPressedEanabledState); mLeftPointerID = -1; mLeftHit = false; invalidate(); } else if (actionID == mRightPointerID) { if (!mRightHit) { return; } if (getDiffByX(event.getX()) == 1) { final float offset = mRightCursorIndex % 5; if (offset > 2.5f) { mRightCursorNextIndex = (int) (mRightCursorIndex / 5) * 5 + 1; } else { mRightCursorNextIndex = (int) (mRightCursorIndex / 5) * 5; if (Math.abs(mLeftCursorIndex - mRightCursorIndex) <= 5 && mRightCursorNextIndex == mLeftCursorNextIndex) { mRightCursorNextIndex = (int) (mRightCursorIndex / 5) * 5 + 1; } } if (!mRightScroller.computeScrollOffset()) { final int fromX = (int) (mRightCursorIndex * mTinyPartLength); mRightScroller.startScroll(fromX, 0, (int) (mRightCursorNextIndex * mTinyPartLength - fromX), 0, mDuration); } if (mRightCursorNextIndex != mRightCursorPreIndex) { mRightCursorPreIndex = mRightCursorNextIndex; } } mRightPointerLastX = 0; mLeftCursorBG.setState(mUnPressedEanabledState); mRightPointerID = -1; mRightHit = false; invalidate(); } } private void handleTouchMove(MotionEvent event) { if (mLeftHit && 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; } if (mLeftCursorRect.left + deltaX < mLeftBoundary) { deltaX = mLeftBoundary - mLeftCursorRect.left; } if (isInfinite && (mLeftCursorRect.right + deltaX > mRightBoundary - mPartLength - slopDistance)) { deltaX = (float) (mRightBoundary - mPartLength - slopDistance - mLeftCursorRect.right); } // 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 (mRightHit || Math.round(mRightCursorIndex) == (mTextArray.length - 1) * 5 || 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. mRightCursorNextIndex = Math.min(mRightCursorIndex + 1, (mTextArray.length - 1) * 5); if (!mRightScroller.computeScrollOffset()) { final int fromX = (int) (mRightCursorIndex * mTinyPartLength); mRightScroller.startScroll(fromX, 0, (int) (mRightCursorNextIndex * mPartLength / 5 - fromX), 0, mDuration); } } } // After some calculate, if deltaX is still be zero, do quick return. if (deltaX == 0) { return; } // Calculate the movement. final float moveX = deltaX / mTinyPartLength; mLeftCursorIndex += moveX; invalidate(); } if (mRightHit && 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 + slopDistance) { deltaX = (float) (mRightBoundary + slopDistance - mRightCursorRect.right); } if (mRightCursorRect.left + deltaX < mLeftCursorRect.right) { if (mLeftHit || Math.round(mLeftCursorIndex) == 0 || mLeftScroller.computeScrollOffset()) { deltaX = mLeftCursorRect.right - mRightCursorRect.left; } else { mLeftCursorNextIndex = Math.max(0, mLeftCursorIndex - 1); if (!mLeftScroller.computeScrollOffset()) { final int fromX = (int) (mLeftCursorIndex * mPartLength / 5); mLeftScroller.startScroll(fromX, 0, (int) (mLeftCursorNextIndex * mPartLength / 5 - fromX), 0, mDuration); } } } if (deltaX == 0) { return; } final float moveX = deltaX / mTinyPartLength; mRightCursorIndex += moveX; invalidate(); } } public void rangeBarInit(int left, int right) { LogUtil.d("rangeBarInit,left:" + left + ",right:" + right); LogUtil.d("getIndexByValue left:" + getIndexByValue(left)); LogUtil.d("getIndexByValue right:" + getIndexByValue(right)); mLeftCursorIndex = getIndexByValue(left); mRightCursorIndex = getIndexByValue(right); invalidate(); } @Override public void computeScroll() { if (mLeftScroller.computeScrollOffset()) { final int deltaX = mLeftScroller.getCurrX(); mLeftCursorIndex = (float) deltaX / mTinyPartLength; invalidate(); } if (mRightScroller.computeScrollOffset()) { final int deltaX = mRightScroller.getCurrX(); mRightCursorIndex = (float) deltaX / mTinyPartLength; invalidate(); } } private void triggerCallback() { if (mListener == null) { return; } mListener.onCursorChanged(getValueByIndex(mLeftCursorIndex), getValueByIndex(mRightCursorIndex), !mLeftHit && !mRightHit);//touchup?? } 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 / 5); mLeftScroller.startScroll(leftFromX, 0, (int) (mLeftCursorNextIndex * mPartLength / 5 - leftFromX), 0, mDuration); if (mRightCursorIndex <= mLeftCursorNextIndex) { if (!mRightScroller.isFinished()) { mRightScroller.abortAnimation(); } mRightCursorNextIndex = mLeftCursorNextIndex + 1; final int rightFromX = (int) (mRightCursorIndex * mPartLength / 5); mRightScroller.startScroll(rightFromX, 0, (int) (mRightCursorNextIndex * mPartLength / 5 - rightFromX), 0, mDuration); } 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 / 5 * mRightCursorIndex); mRightScroller.startScroll(rightFromX, 0, (int) (mRightCursorNextIndex * mPartLength / 5 - rightFromX), 0, mDuration); if (mLeftCursorIndex >= mRightCursorNextIndex) { if (!mLeftScroller.isFinished()) { mLeftScroller.abortAnimation(); } mLeftCursorNextIndex = mRightCursorNextIndex - 1; final int leftFromX = (int) (mLeftCursorIndex * mPartLength / 5); mLeftScroller.startScroll(leftFromX, 0, (int) (mLeftCursorNextIndex * mPartLength / 5 - leftFromX), 0, mDuration); } 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 setSeekbarColorNormal(int color) { if (color == Color.TRANSPARENT) { throw new IllegalArgumentException("Do you want to make seekbar invisible?"); } mRangeBarColorNormal = color; invalidate(); } public void setSeekbarColorSelected(int color) { if (color <= Color.TRANSPARENT) { throw new IllegalArgumentException("Do you want to make seekbar invisible?"); } mRangeBarColorSelected = color; invalidate(); } /** * In pixels. Users should call this method before view is added to parent. */ public void setSeekbarHeight(int height) { if (height <= 0) { throw new IllegalArgumentException("Height of seekbar can not less than 0!"); } mRangeBarHeight = height; } /** * To set space between text mark and seekbar. */ 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(int... 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]; 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 onCursorChanged(int left, int right, boolean needRefresh); } public int getValueByIndex(float index) { if (index < 0) { return mTextArray[0]; } float a = index / 5; int floor = (int) Math.floor(a); int ceil = (int) Math.ceil(a); if (ceil > -1 && ceil < mTextArray.length - 1) { int delta = (int) (index % 5 * (mTextArray[ceil] - mTextArray[floor]) / 5); return mTextArray[floor] + delta; } else if (ceil == mTextArray.length - 1) { if (isInfinite) { return -1; } else { int delta = (int) (index % 5 * (mTextArray[ceil] - mTextArray[floor]) / 5); return mTextArray[floor] + delta; } } else { if (isInfinite) { return -1; } else { return mTextArray[mTextArray.length - 1]; } } } public float getIndexByValue(int value) { if (value == -1 || value > mTextArray[mTextArray.length - 2]) { return (mTextArray.length - 1) * 5; } int j = 1; float diff = 0f; for (int i = 1; i < mTextArray.length - 1; i++) { if (mTextArray[i] > value) { j = i; diff = value - mTextArray[j - 1]; break; } } return (j - 1) * 5 + diff / (mTextArray[j] - mTextArray[j - 1]) * 5; } private float getDiffByX(float x) { int a = (int) ((x - mPaddingRect.left) / mPartLength); if (a + 1 > -1 && a + 1 < mTextArray.length - 1) { return mTextArray[a + 1] - mTextArray[a]; } else { return 5.0f; } } }