diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index cadde47..ea6ca70 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -8,6 +8,9 @@ * naweinberger * Added `onItemClicked` functionality. + +* Sviatoslav Kostenko sviatoslav.kostenko@gmail.com + * Added ability to insert values programmatically. In chronological order: diff --git a/HorizontalPicker/src/main/java/com/wefika/horizontalpicker/HorizontalPicker.java b/HorizontalPicker/src/main/java/com/wefika/horizontalpicker/HorizontalPicker.java index 4154c1d..a0c8d44 100644 --- a/HorizontalPicker/src/main/java/com/wefika/horizontalpicker/HorizontalPicker.java +++ b/HorizontalPicker/src/main/java/com/wefika/horizontalpicker/HorizontalPicker.java @@ -171,7 +171,7 @@ public HorizontalPicker(Context context, AttributeSet attrs, int defStyle) { sideItems = a.getInt(R.styleable.HorizontalPicker_sideItems, sideItems); float textSize = a.getDimension(R.styleable.HorizontalPicker_android_textSize, -1); - if(textSize > -1) { + if (textSize > -1) { setTextSize(textSize); } } finally { @@ -217,9 +217,10 @@ public HorizontalPicker(Context context, AttributeSet attrs, int defStyle) { previousScrollerX = Integer.MIN_VALUE; - setValues(values); + if (values != null && values.length > 0) { + setValues(values); + } setSideItems(sideItems); - touchHelper = new PickerTouchHelper(this); ViewCompat.setAccessibilityDelegate(this, touchHelper); @@ -233,7 +234,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int heightSize = MeasureSpec.getSize(heightMeasureSpec); int height; - if(heightMode == MeasureSpec.EXACTLY) { + if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); @@ -336,6 +337,7 @@ public void onRtlPropertiesChanged(int layoutDirection) { /** * TODO cache values + * * @param text * @return */ @@ -377,7 +379,7 @@ private TextDirectionHeuristicCompat getTextDirectionHeuristic() { private void remakeLayout() { - if (layouts != null && layouts.length > 0 && getWidth() > 0) { + if (layouts != null && layouts.length > 0 && getWidth() > 0) { for (int i = 0; i < layouts.length; i++) { layouts[i].replaceOrMake(values[i], textPaint, itemWidth, Layout.Alignment.ALIGN_CENTER, 1f, 1f, boringMetrics, false, ellipsize, @@ -393,7 +395,7 @@ private void drawEdgeEffect(Canvas canvas, EdgeEffect edgeEffect, int degrees) { return; } - if(!edgeEffect.isFinished()) { + if (!edgeEffect.isFinished()) { final int restoreCount = canvas.getSaveCount(); final int width = getWidth(); final int height = getHeight(); @@ -407,7 +409,7 @@ private void drawEdgeEffect(Canvas canvas, EdgeEffect edgeEffect, int degrees) { } edgeEffect.setSize(height, width); - if(edgeEffect.draw(canvas)) { + if (edgeEffect.draw(canvas)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { postInvalidateOnAnimation(); } else { @@ -437,8 +439,8 @@ private int getTextColor(int item) { scrollX < itemWithPadding * (item + 1) - itemWithPadding / 2) { int position = scrollX - itemWithPadding / 2; color = getColor(position, item); - } else if(item == pressedItem) { - color = textColor.getColorForState(new int[] { android.R.attr.state_pressed }, color); + } else if (item == pressedItem) { + color = textColor.getColorForState(new int[]{android.R.attr.state_pressed}, color); } return color; @@ -455,7 +457,7 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { @Override public boolean onTouchEvent(MotionEvent event) { - if(!isEnabled()) { + if (!isEnabled()) { return false; } @@ -472,10 +474,10 @@ public boolean onTouchEvent(MotionEvent event) { int deltaMoveX = (int) (lastDownEventX - currentMoveX); - if(scrollingX || - (Math.abs(deltaMoveX) > touchSlop) && values != null && values.length > 0) { + if (scrollingX || + (Math.abs(deltaMoveX) > touchSlop) && valuesArePresent()) { - if(!scrollingX) { + if (!scrollingX) { deltaMoveX = 0; pressedItem = -1; scrollingX = true; @@ -485,20 +487,20 @@ public boolean onTouchEvent(MotionEvent event) { final int range = getScrollRange(); - if(overScrollBy(deltaMoveX, 0, getScrollX(), 0, range, 0, + if (overScrollBy(deltaMoveX, 0, getScrollX(), 0, range, 0, overscrollDistance, 0, true)) { mVelocityTracker.clear(); } final float pulledToX = getScrollX() + deltaMoveX; - if(pulledToX < 0) { + if (pulledToX < 0) { leftEdgeEffect.onPull((float) deltaMoveX / getWidth()); - if(!rightEdgeEffect.isFinished()) { + if (!rightEdgeEffect.isFinished()) { rightEdgeEffect.onRelease(); } - } else if(pulledToX > range) { + } else if (pulledToX > range) { rightEdgeEffect.onPull((float) deltaMoveX / getWidth()); - if(!leftEdgeEffect.isFinished()) { + if (!leftEdgeEffect.isFinished()) { leftEdgeEffect.onRelease(); } } @@ -511,9 +513,9 @@ public boolean onTouchEvent(MotionEvent event) { break; case MotionEvent.ACTION_DOWN: - if(!adjustScrollerX.isFinished()) { + if (!adjustScrollerX.isFinished()) { adjustScrollerX.forceFinished(true); - } else if(!flingScrollerX.isFinished()) { + } else if (!flingScrollerX.isFinished()) { flingScrollerX.forceFinished(true); } else { scrollingX = false; @@ -521,7 +523,7 @@ public boolean onTouchEvent(MotionEvent event) { lastDownEventX = event.getX(); - if(!scrollingX) { + if (!scrollingX) { pressedItem = getPositionFromTouch(event.getX()); } invalidate(); @@ -533,11 +535,11 @@ public boolean onTouchEvent(MotionEvent event) { velocityTracker.computeCurrentVelocity(1000, maximumFlingVelocity); int initialVelocityX = (int) velocityTracker.getXVelocity(); - if(scrollingX && Math.abs(initialVelocityX) > mMinimumFlingVelocity) { + if (scrollingX && Math.abs(initialVelocityX) > mMinimumFlingVelocity) { flingX(initialVelocityX); } else if (values != null) { float positionX = event.getX(); - if(!scrollingX) { + if (!scrollingX) { int itemPos = getPositionOnScreen(positionX); int relativePos = itemPos - sideItems; @@ -548,7 +550,7 @@ public boolean onTouchEvent(MotionEvent event) { smoothScrollBy(relativePos); } - } else if(scrollingX) { + } else if (scrollingX) { finishScrolling(); } } @@ -556,7 +558,7 @@ public boolean onTouchEvent(MotionEvent event) { mVelocityTracker.recycle(); mVelocityTracker = null; - if(leftEdgeEffect != null) { + if (leftEdgeEffect != null) { leftEdgeEffect.onRelease(); rightEdgeEffect.onRelease(); } @@ -565,7 +567,7 @@ public boolean onTouchEvent(MotionEvent event) { pressedItem = -1; invalidate(); - if(leftEdgeEffect != null) { + if (leftEdgeEffect != null) { leftEdgeEffect.onRelease(); rightEdgeEffect.onRelease(); } @@ -578,7 +580,7 @@ public boolean onTouchEvent(MotionEvent event) { private void selectItem() { // post to the UI Thread to avoid potential interference with the OpenGL Thread - if (onItemClicked != null) { + if (onItemClicked != null && valuesArePresent()) { post(new Runnable() { @Override public void run() { @@ -586,8 +588,8 @@ public void run() { } }); } - - adjustToNearestItemX(); + if (valuesArePresent()) + adjustToNearestItemX(); } @Override @@ -672,7 +674,6 @@ public void setSideItems(int sideItems) { throw new IllegalArgumentException("Number of items on each side must be grater or equal to 0."); } else if (this.sideItems != sideItems) { this.sideItems = sideItems; - calculateItemSize(getWidth(), getHeight()); } } @@ -685,6 +686,7 @@ public CharSequence[] getValues() { /** * Sets values to choose from + * * @param values New values to choose from */ public void setValues(CharSequence[] values) { @@ -695,17 +697,14 @@ public void setValues(CharSequence[] values) { if (this.values != null) { layouts = new BoringLayout[this.values.length]; for (int i = 0; i < layouts.length; i++) { - layouts[i] = new BoringLayout(this.values[i], textPaint, itemWidth, Layout.Alignment.ALIGN_CENTER, + layouts[i] = new BoringLayout(this.values[i], textPaint, itemWidth > 0 ? itemWidth : 0, Layout.Alignment.ALIGN_CENTER, 1f, 1f, boringMetrics, false, ellipsize, itemWidth); } } else { layouts = new BoringLayout[0]; } - // start marque only if has already been measured - if (getWidth() > 0) { - startMarqueeIfNeeded(); - } + calculateItemSize(getWidth(), getHeight()); requestLayout(); invalidate(); @@ -742,7 +741,7 @@ protected Parcelable onSaveInstanceState() { @Override public void setOverScrollMode(int overScrollMode) { - if(overScrollMode != OVER_SCROLL_NEVER) { + if (overScrollMode != OVER_SCROLL_NEVER) { Context context = getContext(); leftEdgeEffect = new EdgeEffect(context); rightEdgeEffect = new EdgeEffect(context); @@ -771,7 +770,7 @@ public void setEllipsize(TextUtils.TruncateAt ellipsize) { protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { super.scrollTo(scrollX, scrollY); - if(!flingScrollerX.isFinished() && clampedX) { + if (!flingScrollerX.isFinished() && clampedX) { flingScrollerX.springBack(scrollX, scrollY, 0, getScrollRange(), 0, 0); } } @@ -787,24 +786,24 @@ private int getPositionFromTouch(float x) { private void computeScrollX() { OverScroller scroller = flingScrollerX; - if(scroller.isFinished()) { + if (scroller.isFinished()) { scroller = adjustScrollerX; - if(scroller.isFinished()) { + if (scroller.isFinished()) { return; } } - if(scroller.computeScrollOffset()) { + if (scroller.computeScrollOffset()) { int currentScrollerX = scroller.getCurrX(); - if(previousScrollerX == Integer.MIN_VALUE) { + if (previousScrollerX == Integer.MIN_VALUE) { previousScrollerX = scroller.getStartX(); } int range = getScrollRange(); - if(previousScrollerX >= 0 && currentScrollerX < 0) { + if (previousScrollerX >= 0 && currentScrollerX < 0) { leftEdgeEffect.onAbsorb((int) scroller.getCurrVelocity()); - } else if(previousScrollerX <= range && currentScrollerX > range) { + } else if (previousScrollerX <= range && currentScrollerX > range) { rightEdgeEffect.onAbsorb((int) scroller.getCurrVelocity()); } @@ -812,7 +811,7 @@ private void computeScrollX() { getScrollRange(), 0, overscrollDistance, 0, false); previousScrollerX = currentScrollerX; - if(scroller.isFinished()) { + if (scroller.isFinished()) { onScrollerFinishedX(scroller); } @@ -835,9 +834,9 @@ private void adjustToNearestItemX() { int x = getScrollX(); int item = Math.round(x / (itemWidth + dividerSize * 1f)); - if(item < 0) { + if (item < 0) { item = 0; - } else if(item > values.length) { + } else if (item > values.length) { item = values.length; } @@ -864,12 +863,15 @@ private void calculateItemSize(int w, int h) { scrollToItem(selectedItem); remakeLayout(); - startMarqueeIfNeeded(); + // start marque only if has already been measured + if (getWidth() > 0) { + startMarqueeIfNeeded(); + } } private void onScrollerFinishedX(OverScroller scroller) { - if(scroller == flingScrollerX) { + if (scroller == flingScrollerX) { finishScrolling(); } } @@ -921,24 +923,27 @@ private int getPositionOnScreen(float x) { } private void smoothScrollBy(int i) { - int deltaMoveX = (itemWidth + (int) dividerSize) * i; - deltaMoveX = getRelativeInBound(deltaMoveX); + if (valuesArePresent()) { + int deltaMoveX = (itemWidth + (int) dividerSize) * i; + deltaMoveX = getRelativeInBound(deltaMoveX); - previousScrollerX = Integer.MIN_VALUE; - flingScrollerX.startScroll(getScrollX(), 0, deltaMoveX, 0); - stopMarqueeIfNeeded(); - invalidate(); + previousScrollerX = Integer.MIN_VALUE; + flingScrollerX.startScroll(getScrollX(), 0, deltaMoveX, 0); + stopMarqueeIfNeeded(); + invalidate(); + } } /** * Calculates color for specific position on time picker + * * @param scrollX * @return */ private int getColor(int scrollX, int position) { int itemWithPadding = (int) (itemWidth + dividerSize); float proportion = Math.abs(((1f * scrollX % itemWithPadding) / 2) / (itemWithPadding / 2f)); - if(proportion > .5) { + if (proportion > .5) { proportion = (proportion - .5f); } else { proportion = .5f - proportion; @@ -948,22 +953,23 @@ private int getColor(int scrollX, int position) { int defaultColor; int selectedColor; - if(pressedItem == position) { - defaultColor = textColor.getColorForState(new int[] { android.R.attr.state_pressed }, textColor.getDefaultColor()); - selectedColor = textColor.getColorForState(new int[] { android.R.attr.state_pressed, android.R.attr.state_selected }, defaultColor); + if (pressedItem == position) { + defaultColor = textColor.getColorForState(new int[]{android.R.attr.state_pressed}, textColor.getDefaultColor()); + selectedColor = textColor.getColorForState(new int[]{android.R.attr.state_pressed, android.R.attr.state_selected}, defaultColor); } else { defaultColor = textColor.getDefaultColor(); - selectedColor = textColor.getColorForState(new int[] { android.R.attr.state_selected }, defaultColor); + selectedColor = textColor.getColorForState(new int[]{android.R.attr.state_selected}, defaultColor); } return (Integer) new ArgbEvaluator().evaluate(proportion, selectedColor, defaultColor); } /** * Sets text size for items + * * @param size New item text size in px. */ private void setTextSize(float size) { - if(size != textPaint.getTextSize()) { + if (size != textPaint.getTextSize()) { textPaint.setTextSize(size); requestLayout(); @@ -973,6 +979,7 @@ private void setTextSize(float size) { /** * Calculates item from x coordinate position. + * * @param x Scroll position to calculate. * @return Selected item from scrolling position in {param x} */ @@ -982,6 +989,7 @@ private int getPositionFromCoordinates(int x) { /** * Scrolls to specified item. + * * @param index Index of an item to scroll to */ private void scrollToItem(int index) { @@ -992,6 +1000,7 @@ private void scrollToItem(int index) { /** * Calculates relative horizontal scroll position to be within our scroll bounds. * {@link com.wefika.horizontalpicker.HorizontalPicker#getInBoundsX(int)} + * * @param x Relative scroll position to calculate * @return Current scroll position + {param x} if is within our scroll bounds, otherwise it * will return min/max scroll position. @@ -1003,15 +1012,16 @@ private int getRelativeInBound(int x) { /** * Calculates x scroll position that is still in range of view scroller + * * @param x Scroll position to calculate. * @return {param x} if is within bounds of over scroller, otherwise it will return min/max * value of scoll position. */ private int getInBoundsX(int x) { - if(x < 0) { + if (x < 0) { x = 0; - } else if(x > ((itemWidth + (int) dividerSize) * (values.length - 1))) { + } else if (x > ((itemWidth + (int) dividerSize) * (values.length - 1))) { x = ((itemWidth + (int) dividerSize) * (values.length - 1)); } return x; @@ -1019,12 +1029,16 @@ private int getInBoundsX(int x) { private int getScrollRange() { int scrollRange = 0; - if(values != null && values.length != 0) { + if (values != null && values.length != 0) { scrollRange = Math.max(0, ((itemWidth + (int) dividerSize) * (values.length - 1))); } return scrollRange; } + private boolean valuesArePresent() { + return values != null && values.length > 0; + } + public interface OnItemSelected { public void onItemSelected(int index); @@ -1198,6 +1212,7 @@ boolean isRunning() { boolean isStopped() { return mStatus == MARQUEE_STOPPED; } + } public static class SavedState extends BaseSavedState { @@ -1222,7 +1237,7 @@ public void writeToParcel(Parcel dest, int flags) { @Override public String toString() { - return "HorizontalPicker.SavedState{" + return "HorizontalPicker.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " selItem=" + mSelItem + "}"; diff --git a/build.gradle b/build.gradle index 14f2362..70a2778 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-alpha7' + classpath 'com.android.tools.build:gradle:2.1.0-alpha1' } }