diff --git a/Material.iml b/Material.iml index 35524d89..28df7fc4 100644 --- a/Material.iml +++ b/Material.iml @@ -1,5 +1,5 @@ - + @@ -8,7 +8,7 @@ - + diff --git a/README.md b/README.md index 8be8b7ac..a11a851c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,175 @@ Material -======== +===================== -A library to bring Material Design UI to pre-Lolipop Android. +[![Maven Central](https://img.shields.io/maven-central/v/com.github.rey5137/material.svg)](https://oss.sonatype.org/content/repositories/releases/com/github/rey5137/material/1.2.1/material-1.2.1.aar) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Material-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/1685) + +MaterialLibrary is an Open Source Android library that back-port Material Design components to pre-Lolipop Android. MaterialLibrary's original author is [Rey Pham](https://github.com/rey5137). + +* [Features](#features) +* [Demo](#demo) +* [Getting Started](#getting-started) +* [Donation](#donation) +* [Contributing](#contributing) + +##Features +- [Progress](https://github.com/rey5137/Material/wiki/Progress) + - Circular + + ![](https://github.com/rey5137/Material/raw/master/image/progress_circular_indeterminate.gif) ![](https://github.com/rey5137/Material/raw/master/image/progress_circular_determinate.gif) + - Linear + + ![](https://github.com/rey5137/Material/raw/master/image/progress_linear_indeterminate.gif) + ![](https://github.com/rey5137/Material/raw/master/image/progress_linear_determinate.gif) + ![](https://github.com/rey5137/Material/raw/master/image/progress_linear_query.gif) + ![](https://github.com/rey5137/Material/raw/master/image/progress_linear_buffer.gif) + +- [Button](https://github.com/rey5137/Material/wiki/Button) + + ![](https://github.com/rey5137/Material/raw/master/image/button_raise_touch.gif) ![](https://github.com/rey5137/Material/raw/master/image/button_raise_wave.gif) + + ![](https://github.com/rey5137/Material/raw/master/image/fab_image.gif) ![](https://github.com/rey5137/Material/raw/master/image/fab_line.gif) + +- [Switch](https://github.com/rey5137/Material/wiki/Switch) + + ![](https://github.com/rey5137/Material/raw/master/image/cb.gif) + + ![](https://github.com/rey5137/Material/raw/master/image/rb.gif) + + ![](https://github.com/rey5137/Material/raw/master/image/switch.gif) + +- [Slider](https://github.com/rey5137/Material/wiki/Slider) + + ![](https://github.com/rey5137/Material/raw/master/image/slider_continuous.gif) + + ![](https://github.com/rey5137/Material/raw/master/image/slider_discrete.gif) + +- [Spinner](https://github.com/rey5137/Material/wiki/Spinner) + + ![](https://github.com/rey5137/Material/raw/master/image/spn.gif) + +- [Text Field](https://github.com/rey5137/Material/wiki/Text-Field) + + ![](https://github.com/rey5137/Material/raw/master/image/textfield.gif) + +- [TabPageIndicator](https://github.com/rey5137/Material/wiki/TabPageIndicator) + + ![](https://github.com/rey5137/Material/raw/master/image/tpi.gif) + +- [SnackBar](https://github.com/rey5137/Material/wiki/SnackBar) + + ![](https://github.com/rey5137/Material/raw/master/image/snackbar.png) + +- [Dialog](https://github.com/rey5137/Material/wiki/Dialog) + + ![](https://github.com/rey5137/Material/raw/master/image/dialog_3.png) ![](https://github.com/rey5137/Material/raw/master/image/dialog_4.png) + +- [BottomSheetDialog](https://github.com/rey5137/Material/wiki/BottomSheet) + + ![](https://github.com/rey5137/Material/raw/master/image/bottomsheet.gif) + +- [Dynamic theme](https://github.com/rey5137/Material/wiki/Theme) + + ![](https://github.com/rey5137/Material/raw/master/image/theme.gif) + +## Demo + + + Get it on Google Play + + +Or try it [here](https://appetize.io/app/dxznk4bqkbzu6tr5qgrzpfhayc). + +## Getting Started + +Add Gradle dependency: + +```gradle +dependencies { + compile 'com.github.rey5137:material:1.2.1' +} +``` + +* Or +[Download from Maven](https://oss.sonatype.org/content/repositories/releases/com/github/rey5137/material/1.2.1/material-1.2.1.aar) + +You can try the SNAPSHOT version: + +```gradle +dependencies { + compile 'com.github.rey5137:material:1.2.1.6-SNAPSHOT' +} +``` +Make sure to add the snapshot repository: + +```gradle +repositories { + maven { + url "https://oss.sonatype.org/content/repositories/snapshots" + } +} +``` + +AppCompat and CardView library is required by Material library. + +```gradle +dependencies { + compile 'com.android.support:appcompat-v7:22.2.1' + compile 'com.android.support:cardview-v7:22.2.1' +} +``` +Now you can use any widget in **com.rey.material.widget** package as you wish. For styling, please view [Wiki](https://github.com/rey5137/Material/wiki). Note that default style of widgets depend on theme of AppCompat. Here is an example: + +```xml + +``` + +## Donation +You can support the project and thank the author for his hard work. + +Click here to lend your support to: Support Material Library project. and make a donation at pledgie.com ! + +**PayPal** +- [Donate] (https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=pea5137%40gmail%2ecom&lc=US&item_name=Rey%20Pham&no_note=0¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHostedGuest) + +## Contributing +Want to contribute? You are welcome! +Note that all pull request should go to `dev` branch. + +Developed By +------------ + +* Rey Pham - + + +License +-------- + + Copyright 2015 Rey Pham. + + 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. diff --git a/app/build.gradle b/app/build.gradle index 58edbcb1..f5af6461 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,10 +14,10 @@ android { signingConfigs { release { - storeFile file(MATERIAL_KEYSTORE_FILE) - storePassword MATERIAL_KEYSTORE_PASSWORD - keyAlias MATERIAL_KEYSTORE_ALIAS - keyPassword MATERIAL_KEYSTORE_PASSWORD +// storeFile file(MATERIAL_KEYSTORE_FILE) +// storePassword MATERIAL_KEYSTORE_PASSWORD +// keyAlias MATERIAL_KEYSTORE_ALIAS +// keyPassword MATERIAL_KEYSTORE_PASSWORD } } diff --git a/app/src/main/res/layout/fragment_slider.xml b/app/src/main/res/layout/fragment_slider.xml index 06313b03..86ec3844 100644 --- a/app/src/main/res/layout/fragment_slider.xml +++ b/app/src/main/res/layout/fragment_slider.xml @@ -26,7 +26,7 @@ android:gravity="center" android:padding="16dp" app:sl_value="50" - app:v_styleId="@array/slider"/> + app:v_styleId="@array/slider" /> - + @@ -63,7 +65,6 @@ - @@ -83,16 +84,14 @@ - - - - + + \ No newline at end of file diff --git a/lib/src/main/java/com/rey/material/widget/Slider.java b/lib/src/main/java/com/rey/material/widget/Slider.java index b34817fb..9c352e54 100644 --- a/lib/src/main/java/com/rey/material/widget/Slider.java +++ b/lib/src/main/java/com/rey/material/widget/Slider.java @@ -34,7 +34,7 @@ /** * Created by Ret on 3/18/2015. */ -public class Slider extends View implements ThemeManager.OnThemeChangedListener{ +public class Slider extends View implements ThemeManager.OnThemeChangedListener { private RippleManager mRippleManager; protected int mStyleId; @@ -75,6 +75,7 @@ public class Slider extends View implements ThemeManager.OnThemeChangedListener{ private float mThumbCurrentRadius; private float mThumbFillPercent; private boolean mAlwaysFillThumb = false; + private boolean mAlwaysShowValue; private int mTextHeight; private int mMemoValue; private String mValueText; @@ -88,14 +89,14 @@ public class Slider extends View implements ThemeManager.OnThemeChangedListener{ /** * Interface definition for a callback to be invoked when thumb's position changed. */ - public interface OnPositionChangeListener{ + public interface OnPositionChangeListener { /** * Called when thumb's position changed. * - * @param view The view fire this event. + * @param view The view fire this event. * @param fromUser Indicate the change is from user touch event or not. - * @param oldPos The old position of thumb. - * @param newPos The new position of thumb. + * @param oldPos The old position of thumb. + * @param newPos The new position of thumb. * @param oldValue The old value. * @param newValue The new value. */ @@ -128,7 +129,7 @@ public Slider(Context context, AttributeSet attrs, int defStyleAttr, int defStyl init(context, attrs, defStyleAttr, defStyleRes); } - private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ + private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //default color @@ -152,12 +153,12 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes); } - public void applyStyle(int resId){ + public void applyStyle(int resId) { ViewUtil.applyStyle(this, resId); applyStyle(getContext(), null, 0, resId); } - protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ + protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Slider, defStyleAttr, defStyleRes); @@ -169,108 +170,101 @@ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, String familyName = null; int style = Typeface.NORMAL; boolean textStyleDefined = false; - for(int i = 0, count = a.getIndexCount(); i < count; i++){ + for (int i = 0, count = a.getIndexCount(); i < count; i++) { int attr = a.getIndex(i); - if(attr == R.styleable.Slider_sl_discreteMode) + if (attr == R.styleable.Slider_sl_discreteMode) mDiscreteMode = a.getBoolean(attr, false); - else if(attr == R.styleable.Slider_sl_primaryColor) + else if (attr == R.styleable.Slider_sl_alwaysShowValue) + mAlwaysShowValue = a.getBoolean(attr, false); + else if (attr == R.styleable.Slider_sl_primaryColor) mPrimaryColor = a.getColor(attr, 0); - else if(attr == R.styleable.Slider_sl_secondaryColor) + else if (attr == R.styleable.Slider_sl_secondaryColor) mSecondaryColor = a.getColor(attr, 0); - else if(attr == R.styleable.Slider_sl_trackSize) + else if (attr == R.styleable.Slider_sl_trackSize) mTrackSize = a.getDimensionPixelSize(attr, 0); - else if(attr == R.styleable.Slider_sl_trackCap) { + else if (attr == R.styleable.Slider_sl_trackCap) { int cap = a.getInteger(attr, 0); - if(cap == 0) + if (cap == 0) mTrackCap = Paint.Cap.BUTT; - else if(cap == 1) + else if (cap == 1) mTrackCap = Paint.Cap.ROUND; else mTrackCap = Paint.Cap.SQUARE; - } - else if(attr == R.styleable.Slider_sl_thumbBorderSize) + } else if (attr == R.styleable.Slider_sl_thumbBorderSize) mThumbBorderSize = a.getDimensionPixelSize(attr, 0); - else if(attr == R.styleable.Slider_sl_thumbRadius) + else if (attr == R.styleable.Slider_sl_thumbRadius) mThumbRadius = a.getDimensionPixelSize(attr, 0); - else if(attr == R.styleable.Slider_sl_thumbFocusRadius) + else if (attr == R.styleable.Slider_sl_thumbFocusRadius) mThumbFocusRadius = a.getDimensionPixelSize(attr, 0); - else if(attr == R.styleable.Slider_sl_travelAnimDuration) { + else if (attr == R.styleable.Slider_sl_travelAnimDuration) { mTravelAnimationDuration = a.getInteger(attr, 0); mTransformAnimationDuration = mTravelAnimationDuration; - } - else if(attr == R.styleable.Slider_sl_alwaysFillThumb) { + } else if (attr == R.styleable.Slider_sl_alwaysFillThumb) { mAlwaysFillThumb = a.getBoolean(R.styleable.Slider_sl_alwaysFillThumb, false); - } - else if(attr == R.styleable.Slider_sl_interpolator){ + } else if (attr == R.styleable.Slider_sl_interpolator) { int resId = a.getResourceId(R.styleable.Slider_sl_interpolator, 0); mInterpolator = AnimationUtils.loadInterpolator(context, resId); - } - else if(attr == R.styleable.Slider_android_gravity) + } else if (attr == R.styleable.Slider_android_gravity) mGravity = a.getInteger(attr, 0); - else if(attr == R.styleable.Slider_sl_minValue) { + else if (attr == R.styleable.Slider_sl_minValue) { minValue = a.getInteger(attr, 0); valueRangeDefined = true; - } - else if(attr == R.styleable.Slider_sl_maxValue) { + } else if (attr == R.styleable.Slider_sl_maxValue) { maxValue = a.getInteger(attr, 0); valueRangeDefined = true; - } - else if(attr == R.styleable.Slider_sl_stepValue) + } else if (attr == R.styleable.Slider_sl_stepValue) mStepValue = a.getInteger(attr, 0); - else if(attr == R.styleable.Slider_sl_value) { + else if (attr == R.styleable.Slider_sl_value) { value = a.getInteger(attr, 0); valueDefined = true; - } - else if(attr == R.styleable.Slider_sl_fontFamily) { + } else if (attr == R.styleable.Slider_sl_fontFamily) { familyName = a.getString(attr); textStyleDefined = true; - } - else if(attr == R.styleable.Slider_sl_textStyle) { + } else if (attr == R.styleable.Slider_sl_textStyle) { style = a.getInteger(attr, 0); textStyleDefined = true; - } - else if(attr == R.styleable.Slider_sl_textColor) + } else if (attr == R.styleable.Slider_sl_textColor) mTextColor = a.getColor(attr, 0); - else if(attr == R.styleable.Slider_sl_textSize) + else if (attr == R.styleable.Slider_sl_textSize) mTextSize = a.getDimensionPixelSize(attr, 0); - else if(attr == R.styleable.Slider_android_enabled) + else if (attr == R.styleable.Slider_android_enabled) setEnabled(a.getBoolean(attr, true)); } a.recycle(); - if(mTrackSize < 0) + if (mTrackSize < 0) mTrackSize = ThemeUtil.dpToPx(context, 2); - if(mThumbBorderSize < 0) + if (mThumbBorderSize < 0) mThumbBorderSize = ThemeUtil.dpToPx(context, 2); - if(mThumbRadius < 0) + if (mThumbRadius < 0) mThumbRadius = ThemeUtil.dpToPx(context, 10); - if(mThumbFocusRadius < 0) + if (mThumbFocusRadius < 0) mThumbFocusRadius = ThemeUtil.dpToPx(context, 14); - if(mTravelAnimationDuration < 0){ + if (mTravelAnimationDuration < 0) { mTravelAnimationDuration = context.getResources().getInteger(android.R.integer.config_mediumAnimTime); mTransformAnimationDuration = mTravelAnimationDuration; } - if(mInterpolator == null) + if (mInterpolator == null) mInterpolator = new DecelerateInterpolator(); - if(valueRangeDefined) + if (valueRangeDefined) setValueRange(minValue, maxValue, false); - if(valueDefined) + if (valueDefined) setValue(value, false); - else if(mThumbPosition < 0) + else if (mThumbPosition < 0) setValue(mMinValue, false); - if(textStyleDefined) + if (textStyleDefined) mTypeface = TypefaceUtil.load(context, familyName, style); - if(mTextSize < 0) + if (mTextSize < 0) mTextSize = context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_small_material); mPaint.setTextSize(mTextSize); @@ -284,7 +278,7 @@ else if(mThumbPosition < 0) @Override public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { int style = ThemeManager.getInstance().getCurrentStyle(mStyleId); - if(mCurrentStyle != style){ + if (mCurrentStyle != style) { mCurrentStyle = style; applyStyle(mCurrentStyle); } @@ -293,7 +287,7 @@ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if(mStyleId != 0) { + if (mStyleId != 0) { ThemeManager.getInstance().registerOnThemeChangedListener(this); onThemeChanged(null); } @@ -303,17 +297,17 @@ protected void onAttachedToWindow() { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mRippleManager.cancelRipple(this); - if(mStyleId != 0) + if (mStyleId != 0) ThemeManager.getInstance().unregisterOnThemeChangedListener(this); } - private void measureText(){ + private void measureText() { Rect temp = new Rect(); String text = String.valueOf(mMaxValue); mPaint.setTextSize(mTextSize); float width = mPaint.measureText(text); - float maxWidth = (float)(mThumbRadius * Math.sqrt(2) * 2 - ThemeUtil.dpToPx(getContext(), 8)); - if(width > maxWidth){ + float maxWidth = (float) (mThumbRadius * Math.sqrt(2) * 2 - ThemeUtil.dpToPx(getContext(), 8)); + if (width > maxWidth) { float textSize = mTextSize * maxWidth / width; mPaint.setTextSize(textSize); } @@ -322,9 +316,9 @@ private void measureText(){ mTextHeight = temp.height(); } - private String getValueText(){ + private String getValueText() { int value = getValue(); - if(mValueText == null || mMemoValue != value){ + if (mValueText == null || mMemoValue != value) { mMemoValue = value; mValueText = String.valueOf(mMemoValue); } @@ -335,32 +329,33 @@ private String getValueText(){ /** * @return The minimum selectable value. */ - public int getMinValue(){ + public int getMinValue() { return mMinValue; } /** * @return The maximum selectable value. */ - public int getMaxValue(){ + public int getMaxValue() { return mMaxValue; } /** * @return The step value. */ - public int getStepValue(){ + public int getStepValue() { return mStepValue; } /** * Set the randge of selectable value. - * @param min The minimum selectable value. - * @param max The maximum selectable value. + * + * @param min The minimum selectable value. + * @param max The maximum selectable value. * @param animation Indicate that should show animation when thumb's current position changed. */ - public void setValueRange(int min, int max, boolean animation){ - if(max < min || (min == mMinValue && max == mMaxValue)) + public void setValueRange(int min, int max, boolean animation) { + if (max < min || (min == mMinValue && max == mMaxValue)) return; float oldValue = getExactValue(); @@ -369,54 +364,54 @@ public void setValueRange(int min, int max, boolean animation){ mMaxValue = max; setValue(oldValue, animation); - if(mOnPositionChangeListener != null && oldPosition == getPosition() && oldValue != getExactValue()) + if (mOnPositionChangeListener != null && oldPosition == getPosition() && oldValue != getExactValue()) mOnPositionChangeListener.onPositionChanged(this, false, oldPosition, oldPosition, Math.round(oldValue), getValue()); } /** * @return The selected value. */ - public int getValue(){ + public int getValue() { return Math.round(getExactValue()); } /** * @return The exact selected value. */ - public float getExactValue(){ + public float getExactValue() { return (mMaxValue - mMinValue) * getPosition() + mMinValue; } /** * @return The current position of thumb in [0..1] range. */ - public float getPosition(){ + public float getPosition() { return mThumbMoveAnimator.isRunning() ? mThumbMoveAnimator.getPosition() : mThumbPosition; } /** * Set current position of thumb. - * @param pos The position in [0..1] range. + * + * @param pos The position in [0..1] range. * @param animation Indicate that should show animation when change thumb's position. */ - public void setPosition(float pos, boolean animation){ + public void setPosition(float pos, boolean animation) { setPosition(pos, animation, animation, false); } - private void setPosition(float pos, boolean moveAnimation, boolean transformAnimation, boolean fromUser){ + private void setPosition(float pos, boolean moveAnimation, boolean transformAnimation, boolean fromUser) { boolean change = getPosition() != pos; int oldValue = getValue(); float oldPos = getPosition(); - if(!moveAnimation || !mThumbMoveAnimator.startAnimation(pos)){ + if (!moveAnimation || !mThumbMoveAnimator.startAnimation(pos)) { mThumbPosition = pos; - if(transformAnimation) { - if(!mIsDragging) + if (transformAnimation) { + if (!mIsDragging) mThumbRadiusAnimator.startAnimation(mThumbRadius); mThumbStrokeAnimator.startAnimation(pos == 0 ? 0 : 1); - } - else{ + } else { mThumbCurrentRadius = mThumbRadius; mThumbFillPercent = (mAlwaysFillThumb || mThumbPosition != 0) ? 1 : 0; invalidate(); @@ -426,12 +421,13 @@ private void setPosition(float pos, boolean moveAnimation, boolean transformAnim int newValue = getValue(); float newPos = getPosition(); - if(change && mOnPositionChangeListener != null) + if (change && mOnPositionChangeListener != null) mOnPositionChangeListener.onPositionChanged(this, fromUser, oldPos, newPos, oldValue, newValue); } /** * Changes the primary color and invalidates the view to force a redraw. + * * @param color New color to assign to mPrimaryColor. */ public void setPrimaryColor(int color) { @@ -441,6 +437,7 @@ public void setPrimaryColor(int color) { /** * Changes the secondary color and invalidates the view to force a redraw. + * * @param color New color to assign to mSecondaryColor. */ public void setSecondaryColor(int color) { @@ -450,6 +447,7 @@ public void setSecondaryColor(int color) { /** * Set if we want the thumb to always be filled. + * * @param alwaysFillThumb Do we want it to always be filled. */ public void setAlwaysFillThumb(boolean alwaysFillThumb) { @@ -458,35 +456,37 @@ public void setAlwaysFillThumb(boolean alwaysFillThumb) { /** * Set the selected value of this Slider. - * @param value The selected value. + * + * @param value The selected value. * @param animation Indicate that should show animation when change thumb's position. */ - public void setValue(float value, boolean animation){ + public void setValue(float value, boolean animation) { value = Math.min(mMaxValue, Math.max(value, mMinValue)); setPosition((value - mMinValue) / (mMaxValue - mMinValue), animation); } /** * Set a listener will be called when thumb's position changed. + * * @param listener The {@link Slider.OnPositionChangeListener} will be called. */ - public void setOnPositionChangeListener(OnPositionChangeListener listener){ + public void setOnPositionChangeListener(OnPositionChangeListener listener) { mOnPositionChangeListener = listener; } @Override public void setBackgroundDrawable(Drawable drawable) { Drawable background = getBackground(); - if(background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) + if (background instanceof RippleDrawable && !(drawable instanceof RippleDrawable)) ((RippleDrawable) background).setBackgroundDrawable(drawable); else super.setBackgroundDrawable(drawable); } - protected RippleManager getRippleManager(){ - if(mRippleManager == null){ - synchronized (RippleManager.class){ - if(mRippleManager == null) + protected RippleManager getRippleManager() { + if (mRippleManager == null) { + synchronized (RippleManager.class) { + if (mRippleManager == null) mRippleManager = new RippleManager(); } } @@ -536,18 +536,18 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override public int getSuggestedMinimumWidth() { - return (mDiscreteMode ? (int)(mThumbRadius * Math.sqrt(2)) : mThumbFocusRadius) * 4 + getPaddingLeft() + getPaddingRight(); + return (mDiscreteMode ? (int) (mThumbRadius * Math.sqrt(2)) : mThumbFocusRadius) * 4 + getPaddingLeft() + getPaddingRight(); } @Override public int getSuggestedMinimumHeight() { - return (mDiscreteMode ? (int)(mThumbRadius * (4 + Math.sqrt(2))) : mThumbFocusRadius * 2) + getPaddingTop() + getPaddingBottom(); + return (mDiscreteMode ? (int) (mThumbRadius * (4 + Math.sqrt(2))) : mThumbFocusRadius * 2) + getPaddingTop() + getPaddingBottom(); } @Override public void onRtlPropertiesChanged(int layoutDirection) { boolean rtl = layoutDirection == LAYOUT_DIRECTION_RTL; - if(mIsRtl != rtl) { + if (mIsRtl != rtl) { mIsRtl = rtl; invalidate(); } @@ -560,8 +560,8 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { int align = mGravity & Gravity.VERTICAL_GRAVITY_MASK; - if(mDiscreteMode){ - int fullHeight = (int)(mThumbRadius * (4 + Math.sqrt(2))); + if (mDiscreteMode) { + int fullHeight = (int) (mThumbRadius * (4 + Math.sqrt(2))); int height = mThumbRadius * 2; switch (align) { case Gravity.TOP: @@ -577,8 +577,7 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { mDrawRect.bottom = mDrawRect.top + height; break; } - } - else{ + } else { int height = mThumbFocusRadius * 2; switch (align) { case Gravity.TOP: @@ -597,19 +596,19 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { } } - private boolean isThumbHit(float x, float y, float radius){ + private boolean isThumbHit(float x, float y, float radius) { float cx = mDrawRect.width() * mThumbPosition + mDrawRect.left; float cy = mDrawRect.centerY(); return x >= cx - radius && x <= cx + radius && y >= cy - radius && y < cy + radius; } - private double distance(float x1, float y1, float x2, float y2){ + private double distance(float x1, float y1, float x2, float y2) { return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); } - private float correctPosition(float position){ - if(!mDiscreteMode) + private float correctPosition(float position) { + if (!mDiscreteMode) return position; int totalOffset = mMaxValue - mMinValue; @@ -618,10 +617,10 @@ private float correctPosition(float position){ int lowerValue = stepOffset * mStepValue; int higherValue = Math.min(totalOffset, (stepOffset + 1) * mStepValue); - if(valueOffset - lowerValue < higherValue - valueOffset) - position = lowerValue / (float)totalOffset; + if (valueOffset - lowerValue < higherValue - valueOffset) + position = lowerValue / (float) totalOffset; else - position = higherValue / (float)totalOffset; + position = higherValue / (float) totalOffset; return position; } @@ -631,28 +630,27 @@ public boolean onTouchEvent(@NonNull MotionEvent event) { super.onTouchEvent(event); getRippleManager().onTouchEvent(event); - if(!isEnabled()) + if (!isEnabled()) return false; float x = event.getX(); float y = event.getY(); - if(mIsRtl) + if (mIsRtl) x = 2 * mDrawRect.centerX() - x; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mIsDragging = isThumbHit(x, y, mThumbRadius) && !mThumbMoveAnimator.isRunning(); mMemoPoint.set(x, y); - if(mIsDragging) + if (mIsDragging) mThumbRadiusAnimator.startAnimation(mDiscreteMode ? 0 : mThumbFocusRadius); break; case MotionEvent.ACTION_MOVE: - if(mIsDragging) { - if(mDiscreteMode) { + if (mIsDragging) { + if (mDiscreteMode) { float position = correctPosition(Math.min(1f, Math.max(0f, (x - mDrawRect.left) / mDrawRect.width()))); setPosition(position, true, true, true); - } - else{ + } else { float offset = (x - mMemoPoint.x) / mDrawRect.width(); float position = Math.min(1f, Math.max(0f, mThumbPosition + offset)); setPosition(position, false, true, true); @@ -662,17 +660,16 @@ public boolean onTouchEvent(@NonNull MotionEvent event) { } break; case MotionEvent.ACTION_UP: - if(mIsDragging) { + if (mIsDragging) { mIsDragging = false; setPosition(getPosition(), true, true, true); - } - else if(distance(mMemoPoint.x, mMemoPoint.y, x, y) <= mTouchSlop){ + } else if (distance(mMemoPoint.x, mMemoPoint.y, x, y) <= mTouchSlop) { float position = correctPosition(Math.min(1f, Math.max(0f, (x - mDrawRect.left) / mDrawRect.width()))); setPosition(position, true, true, true); } break; case MotionEvent.ACTION_CANCEL: - if(mIsDragging) { + if (mIsDragging) { mIsDragging = false; setPosition(getPosition(), true, true, true); } @@ -682,15 +679,15 @@ else if(distance(mMemoPoint.x, mMemoPoint.y, x, y) <= mTouchSlop){ return true; } - private void getTrackPath(float x, float y, float radius){ + private void getTrackPath(float x, float y, float radius) { float halfStroke = mTrackSize / 2f; mLeftTrackPath.reset(); mRightTrackPath.reset(); - if(radius - 1f < halfStroke){ - if(mTrackCap != Paint.Cap.ROUND){ - if(x > mDrawRect.left){ + if (radius - 1f < halfStroke) { + if (mTrackCap != Paint.Cap.ROUND) { + if (x > mDrawRect.left) { mLeftTrackPath.moveTo(mDrawRect.left, y - halfStroke); mLeftTrackPath.lineTo(x, y - halfStroke); mLeftTrackPath.lineTo(x, y + halfStroke); @@ -698,16 +695,15 @@ private void getTrackPath(float x, float y, float radius){ mLeftTrackPath.close(); } - if(x < mDrawRect.right){ + if (x < mDrawRect.right) { mRightTrackPath.moveTo(mDrawRect.right, y + halfStroke); mRightTrackPath.lineTo(x, y + halfStroke); mRightTrackPath.lineTo(x, y - halfStroke); mRightTrackPath.lineTo(mDrawRect.right, y - halfStroke); mRightTrackPath.close(); } - } - else{ - if(x > mDrawRect.left){ + } else { + if (x > mDrawRect.left) { mTempRect.set(mDrawRect.left, y - halfStroke, mDrawRect.left + mTrackSize, y + halfStroke); mLeftTrackPath.arcTo(mTempRect, 90, 180); mLeftTrackPath.lineTo(x, y - halfStroke); @@ -715,7 +711,7 @@ private void getTrackPath(float x, float y, float radius){ mLeftTrackPath.close(); } - if(x < mDrawRect.right){ + if (x < mDrawRect.right) { mTempRect.set(mDrawRect.right - mTrackSize, y - halfStroke, mDrawRect.right, y + halfStroke); mRightTrackPath.arcTo(mTempRect, 270, 180); mRightTrackPath.lineTo(x, y + halfStroke); @@ -723,31 +719,29 @@ private void getTrackPath(float x, float y, float radius){ mRightTrackPath.close(); } } - } - else{ - if(mTrackCap != Paint.Cap.ROUND){ + } else { + if (mTrackCap != Paint.Cap.ROUND) { mTempRect.set(x - radius + 1f, y - radius + 1f, x + radius - 1f, y + radius - 1f); - float angle = (float)(Math.asin(halfStroke / (radius - 1f)) / Math.PI * 180); + float angle = (float) (Math.asin(halfStroke / (radius - 1f)) / Math.PI * 180); - if(x - radius > mDrawRect.left){ + if (x - radius > mDrawRect.left) { mLeftTrackPath.moveTo(mDrawRect.left, y - halfStroke); mLeftTrackPath.arcTo(mTempRect, 180 + angle, -angle * 2); mLeftTrackPath.lineTo(mDrawRect.left, y + halfStroke); mLeftTrackPath.close(); } - if(x + radius < mDrawRect.right){ + if (x + radius < mDrawRect.right) { mRightTrackPath.moveTo(mDrawRect.right, y - halfStroke); mRightTrackPath.arcTo(mTempRect, -angle, angle * 2); mRightTrackPath.lineTo(mDrawRect.right, y + halfStroke); mRightTrackPath.close(); } - } - else{ - float angle = (float)(Math.asin(halfStroke / (radius - 1f)) / Math.PI * 180); + } else { + float angle = (float) (Math.asin(halfStroke / (radius - 1f)) / Math.PI * 180); - if(x - radius > mDrawRect.left){ - float angle2 = (float)(Math.acos(Math.max(0f, (mDrawRect.left + halfStroke - x + radius) / halfStroke)) / Math.PI * 180); + if (x - radius > mDrawRect.left) { + float angle2 = (float) (Math.acos(Math.max(0f, (mDrawRect.left + halfStroke - x + radius) / halfStroke)) / Math.PI * 180); mTempRect.set(mDrawRect.left, y - halfStroke, mDrawRect.left + mTrackSize, y + halfStroke); mLeftTrackPath.arcTo(mTempRect, 180 - angle2, angle2 * 2); @@ -757,11 +751,11 @@ private void getTrackPath(float x, float y, float radius){ mLeftTrackPath.close(); } - if(x + radius < mDrawRect.right){ - float angle2 = (float)Math.acos(Math.max(0f, (x + radius - mDrawRect.right + halfStroke) / halfStroke)); + if (x + radius < mDrawRect.right) { + float angle2 = (float) Math.acos(Math.max(0f, (x + radius - mDrawRect.right + halfStroke) / halfStroke)); mRightTrackPath.moveTo((float) (mDrawRect.right - halfStroke + Math.cos(angle2) * halfStroke), (float) (y + Math.sin(angle2) * halfStroke)); - angle2 = (float)(angle2 / Math.PI * 180); + angle2 = (float) (angle2 / Math.PI * 180); mTempRect.set(mDrawRect.right - mTrackSize, y - halfStroke, mDrawRect.right, y + halfStroke); mRightTrackPath.arcTo(mTempRect, angle2, -angle2 * 2); @@ -773,8 +767,8 @@ private void getTrackPath(float x, float y, float radius){ } } - private Path getMarkPath(Path path, float cx, float cy, float radius, float factor){ - if(path == null) + private Path getMarkPath(Path path, float cx, float cy, float radius, float factor) { + if (path == null) path = new Path(); else path.reset(); @@ -790,15 +784,15 @@ private Path getMarkPath(Path path, float cx, float cy, float radius, float fact float nCy = cy - radius * factor; // calculate first arc - float angle = (float)(Math.atan2(y2 - nCy, x2 - nCx) * 180 / Math.PI); - float nRadius = (float)distance(nCx, nCy, x1, y1); + float angle = (float) (Math.atan2(y2 - nCy, x2 - nCx) * 180 / Math.PI); + float nRadius = (float) distance(nCx, nCy, x1, y1); mTempRect.set(nCx - nRadius, nCy - nRadius, nCx + nRadius, nCy + nRadius); path.moveTo(x1, y1); path.arcTo(mTempRect, 180 - angle, 180 + angle * 2); - if(factor > 0.9f) + if (factor > 0.9f) path.lineTo(x3, y3); - else{ + else { // find center point for second arc float x4 = (x2 + x3) / 2; float y4 = (y2 + y3) / 2; @@ -806,22 +800,22 @@ private Path getMarkPath(Path path, float cx, float cy, float radius, float fact double d1 = distance(x2, y2, x4, y4); double d2 = d1 / Math.tan(Math.PI * (1f - factor) / 4); - nCx = (float)(x4 - Math.cos(Math.PI / 4) * d2); - nCy = (float)(y4 - Math.sin(Math.PI / 4) * d2); + nCx = (float) (x4 - Math.cos(Math.PI / 4) * d2); + nCy = (float) (y4 - Math.sin(Math.PI / 4) * d2); // calculate second arc - angle = (float)(Math.atan2(y2 - nCy, x2 - nCx) * 180 / Math.PI); - float angle2 = (float)(Math.atan2(y3 - nCy, x3 - nCx) * 180 / Math.PI); - nRadius = (float)distance(nCx, nCy, x2, y2); + angle = (float) (Math.atan2(y2 - nCy, x2 - nCx) * 180 / Math.PI); + float angle2 = (float) (Math.atan2(y3 - nCy, x3 - nCx) * 180 / Math.PI); + nRadius = (float) distance(nCx, nCy, x2, y2); mTempRect.set(nCx - nRadius, nCy - nRadius, nCx + nRadius, nCy + nRadius); path.arcTo(mTempRect, angle, angle2 - angle); // calculate third arc nCx = cx * 2 - nCx; - angle = (float)(Math.atan2(y3 - nCy, x3 - nCx) * 180 / Math.PI); - angle2 = (float)(Math.atan2(y1 - nCy, x1 - nCx) * 180 / Math.PI); + angle = (float) (Math.atan2(y3 - nCy, x3 - nCx) * 180 / Math.PI); + angle2 = (float) (Math.atan2(y1 - nCy, x1 - nCx) * 180 / Math.PI); mTempRect.set(nCx - nRadius, nCy - nRadius, nCx + nRadius, nCy + nRadius); - path.arcTo(mTempRect, angle + (float)Math.PI / 4, angle2 - angle); + path.arcTo(mTempRect, angle + (float) Math.PI / 4, angle2 - angle); } path.close(); @@ -834,7 +828,7 @@ public void draw(@NonNull Canvas canvas) { super.draw(canvas); float x = mDrawRect.width() * mThumbPosition + mDrawRect.left; - if(mIsRtl) + if (mIsRtl) x = 2 * mDrawRect.centerX() - x; float y = mDrawRect.centerY(); int filledPrimaryColor = ColorUtil.getMiddleColor(mSecondaryColor, isEnabled() ? mPrimaryColor : mSecondaryColor, mThumbFillPercent); @@ -847,66 +841,82 @@ public void draw(@NonNull Canvas canvas) { canvas.drawPath(mLeftTrackPath, mPaint); mPaint.setColor(filledPrimaryColor); - if(mDiscreteMode){ + if (mDiscreteMode) { float factor = 1f - mThumbCurrentRadius / mThumbRadius; - if(factor > 0){ - mMarkPath = getMarkPath(mMarkPath, x, y, mThumbRadius, factor); - mPaint.setStyle(Paint.Style.FILL); - int saveCount = canvas.save(); - canvas.translate(0, -mThumbRadius * 2 * factor); - canvas.drawPath(mMarkPath, mPaint); - mPaint.setColor(ColorUtil.getColor(mTextColor, factor)); - canvas.drawText(getValueText(), x, y + mTextHeight / 2f - mThumbRadius * factor, mPaint); - canvas.restoreToCount(saveCount); - } + if (factor > 0 && !mAlwaysShowValue) + drawValue(canvas, factor, x, y, filledPrimaryColor); float radius = isEnabled() ? mThumbCurrentRadius : mThumbCurrentRadius - mThumbBorderSize; - if(radius > 0) { + if (radius > 0) { mPaint.setColor(filledPrimaryColor); canvas.drawCircle(x, y, radius, mPaint); } - } - else{ + + if(mAlwaysShowValue) + drawValue(canvas, factor, x, y, filledPrimaryColor); + + } else { float radius = isEnabled() ? mThumbCurrentRadius : mThumbCurrentRadius - mThumbBorderSize; - if(mThumbFillPercent == 1) + if (mThumbFillPercent == 1) mPaint.setStyle(Paint.Style.FILL); - else{ + else { float strokeWidth = (radius - mThumbBorderSize) * mThumbFillPercent + mThumbBorderSize; radius = radius - strokeWidth / 2f; mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(strokeWidth); } canvas.drawCircle(x, y, radius, mPaint); + } } - class ThumbRadiusAnimator implements Runnable{ + private void drawValue(@NonNull Canvas canvas, float factor, float x, float y, int filledPrimaryColor){ + mPaint.setColor(filledPrimaryColor); + mMarkPath = getMarkPath(mMarkPath, x, y, mThumbRadius, factor); + mPaint.setStyle(Paint.Style.FILL); + int saveCount = canvas.save(); + canvas.translate(0, -mThumbRadius * 2 * factor); + canvas.drawPath(mMarkPath, mPaint); + mPaint.setColor(mTextColor); + + float sizeMod = 1; + if(mAlwaysShowValue){ + sizeMod = (factor / 4) + 0.75f; + mPaint.setColor(mTextColor); + } else + mPaint.setColor(ColorUtil.getColor(mTextColor, factor)); + + mPaint.setTextSize(mTextSize * sizeMod); + canvas.drawText(getValueText(), x, y + (mTextHeight * sizeMod) / 2f - mThumbRadius * factor, mPaint); + canvas.restoreToCount(saveCount); + } + + class ThumbRadiusAnimator implements Runnable { boolean mRunning = false; long mStartTime; float mStartRadius; int mRadius; - public void resetAnimation(){ + public void resetAnimation() { mStartTime = SystemClock.uptimeMillis(); mStartRadius = mThumbCurrentRadius; } public boolean startAnimation(int radius) { - if(mThumbCurrentRadius == radius) + if (mThumbCurrentRadius == radius) return false; mRadius = radius; - if(getHandler() != null){ + if (getHandler() != null) { resetAnimation(); mRunning = true; getHandler().postAtTime(this, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidate(); return true; - } - else { + } else { mThumbCurrentRadius = mRadius; invalidate(); return false; @@ -916,7 +926,7 @@ public boolean startAnimation(int radius) { public void stopAnimation() { mRunning = false; mThumbCurrentRadius = mRadius; - if(getHandler() != null) + if (getHandler() != null) getHandler().removeCallbacks(this); invalidate(); } @@ -924,16 +934,16 @@ public void stopAnimation() { @Override public void run() { long curTime = SystemClock.uptimeMillis(); - float progress = Math.min(1f, (float)(curTime - mStartTime) / mTransformAnimationDuration); + float progress = Math.min(1f, (float) (curTime - mStartTime) / mTransformAnimationDuration); float value = mInterpolator.getInterpolation(progress); mThumbCurrentRadius = (mRadius - mStartRadius) * value + mStartRadius; - if(progress == 1f) + if (progress == 1f) stopAnimation(); - if(mRunning) { - if(getHandler() != null) + if (mRunning) { + if (getHandler() != null) getHandler().postAtTime(this, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); else stopAnimation(); @@ -944,32 +954,31 @@ public void run() { } - class ThumbStrokeAnimator implements Runnable{ + class ThumbStrokeAnimator implements Runnable { boolean mRunning = false; long mStartTime; float mStartFillPercent; int mFillPercent; - public void resetAnimation(){ + public void resetAnimation() { mStartTime = SystemClock.uptimeMillis(); mStartFillPercent = mThumbFillPercent; } public boolean startAnimation(int fillPercent) { - if(mThumbFillPercent == fillPercent) + if (mThumbFillPercent == fillPercent) return false; mFillPercent = fillPercent; - if(getHandler() != null){ + if (getHandler() != null) { resetAnimation(); mRunning = true; getHandler().postAtTime(this, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidate(); return true; - } - else { + } else { mThumbFillPercent = mAlwaysFillThumb ? 1 : mFillPercent; invalidate(); return false; @@ -979,7 +988,7 @@ public boolean startAnimation(int fillPercent) { public void stopAnimation() { mRunning = false; mThumbFillPercent = mAlwaysFillThumb ? 1 : mFillPercent; - if(getHandler() != null) + if (getHandler() != null) getHandler().removeCallbacks(this); invalidate(); } @@ -987,16 +996,16 @@ public void stopAnimation() { @Override public void run() { long curTime = SystemClock.uptimeMillis(); - float progress = Math.min(1f, (float)(curTime - mStartTime) / mTransformAnimationDuration); + float progress = Math.min(1f, (float) (curTime - mStartTime) / mTransformAnimationDuration); float value = mInterpolator.getInterpolation(progress); mThumbFillPercent = mAlwaysFillThumb ? 1 : ((mFillPercent - mStartFillPercent) * value + mStartFillPercent); - if(progress == 1f) + if (progress == 1f) stopAnimation(); - if(mRunning) { - if(getHandler() != null) + if (mRunning) { + if (getHandler() != null) getHandler().postAtTime(this, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); else stopAnimation(); @@ -1007,7 +1016,7 @@ public void run() { } - class ThumbMoveAnimator implements Runnable{ + class ThumbMoveAnimator implements Runnable { boolean mRunning = false; long mStartTime; @@ -1018,15 +1027,15 @@ class ThumbMoveAnimator implements Runnable{ float mFillPercent; int mDuration; - public boolean isRunning(){ + public boolean isRunning() { return mRunning; } - public float getPosition(){ + public float getPosition() { return mPosition; } - public void resetAnimation(){ + public void resetAnimation() { mStartTime = SystemClock.uptimeMillis(); mStartPosition = mThumbPosition; mStartFillPercent = mThumbFillPercent; @@ -1036,19 +1045,18 @@ public void resetAnimation(){ } public boolean startAnimation(float position) { - if(mThumbPosition == position) + if (mThumbPosition == position) return false; mPosition = position; - if(getHandler() != null){ + if (getHandler() != null) { resetAnimation(); mRunning = true; getHandler().postAtTime(this, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidate(); return true; - } - else { + } else { mThumbPosition = position; invalidate(); return false; @@ -1060,7 +1068,7 @@ public void stopAnimation() { mThumbCurrentRadius = mDiscreteMode && mIsDragging ? 0 : mThumbRadius; mThumbFillPercent = mAlwaysFillThumb ? 1 : mFillPercent; mThumbPosition = mPosition; - if(getHandler() != null) + if (getHandler() != null) getHandler().removeCallbacks(this); invalidate(); } @@ -1068,44 +1076,41 @@ public void stopAnimation() { @Override public void run() { long curTime = SystemClock.uptimeMillis(); - float progress = Math.min(1f, (float)(curTime - mStartTime) / mDuration); + float progress = Math.min(1f, (float) (curTime - mStartTime) / mDuration); float value = mInterpolator.getInterpolation(progress); - if(mDiscreteMode){ - if(mIsDragging) { + if (mDiscreteMode) { + if (mIsDragging) { mThumbPosition = (mPosition - mStartPosition) * value + mStartPosition; mThumbFillPercent = mAlwaysFillThumb ? 1 : ((mFillPercent - mStartFillPercent) * value + mStartFillPercent); - } - else{ - float p1 = (float)mTravelAnimationDuration / mDuration; - float p2 = (float)(mTravelAnimationDuration + mTransformAnimationDuration)/ mDuration; - if(progress < p1) { + } else { + float p1 = (float) mTravelAnimationDuration / mDuration; + float p2 = (float) (mTravelAnimationDuration + mTransformAnimationDuration) / mDuration; + if (progress < p1) { value = mInterpolator.getInterpolation(progress / p1); mThumbCurrentRadius = mStartRadius * (1f - value); mThumbPosition = (mPosition - mStartPosition) * value + mStartPosition; mThumbFillPercent = mAlwaysFillThumb ? 1 : ((mFillPercent - mStartFillPercent) * value + mStartFillPercent); - } - else if(progress > p2){ + } else if (progress > p2) { mThumbCurrentRadius = mThumbRadius * (progress - p2) / (1 - p2); } } - } - else{ + } else { mThumbPosition = (mPosition - mStartPosition) * value + mStartPosition; mThumbFillPercent = mAlwaysFillThumb ? 1 : ((mFillPercent - mStartFillPercent) * value + mStartFillPercent); - if(progress < 0.2) + if (progress < 0.2) mThumbCurrentRadius = Math.max(mThumbRadius + mThumbBorderSize * progress * 5, mThumbCurrentRadius); - else if(progress >= 0.8) + else if (progress >= 0.8) mThumbCurrentRadius = mThumbRadius + mThumbBorderSize * (5f - progress * 5); } - if(progress == 1f) + if (progress == 1f) stopAnimation(); - if(mRunning) { - if(getHandler() != null) + if (mRunning) { + if (getHandler() != null) getHandler().postAtTime(this, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); else stopAnimation(); diff --git a/lib/src/main/res/values/attrs.xml b/lib/src/main/res/values/attrs.xml index 61b28991..d6efe910 100644 --- a/lib/src/main/res/values/attrs.xml +++ b/lib/src/main/res/values/attrs.xml @@ -324,6 +324,7 @@ +