diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..11de0b8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,34 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: GuilhE
+
+---
+
+## Describe the bug
+A clear and concise description of what the bug is.
+
+## To Reproduce
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+## Expected behavior
+A clear and concise description of what you expected to happen.
+
+## Screenshots
+If applicable, add screenshots to help explain your problem.
+
+## Details (please complete the following information):
+- Library version
+- Android version
+- Emulator/Device specs
+- Logs/Crash Reports/Stacktraces
+- Example Code/Link to repositories
+
+## Additional context
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..aa1ea7b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: enhancement
+assignees: ''
+
+---
+
+## Is your feature request related to a problem? Please describe
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+## Describe the solution you'd like
+A clear and concise description of what you want to happen.
+
+## Describe alternatives you've considered
+A clear and concise description of any alternative solutions or features you've considered.
+
+## Additional context
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/pull_request.md b/.github/ISSUE_TEMPLATE/pull_request.md
new file mode 100644
index 0000000..0f6a964
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/pull_request.md
@@ -0,0 +1,19 @@
+---
+name: Pull Request
+about: Contribute to this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+## Description
+A few sentences describing the overall goals of the pull request's commits.
+
+## Related PRs/Issues
+List related PRs and Issues for this Pull Request.
+
+## Todos
+- [ ] Tests
+- [ ] Documentation
+- [ ] Screenshots
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..ad14d8e
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,25 @@
+name: Publish to Bintray
+on:
+ release:
+ types: [published]
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+
+ - name: Grant Permission to Execute
+ run: chmod +x gradlew
+
+ - name: Publish Library
+ env:
+ bintrayUser: ${{ secrets.BINTRAY_USER }}
+ bintrayApiKey: ${{ secrets.BINTRAY_API_KEY }}
+ run: ./gradlew bintrayUpload
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..ad779e2
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,32 @@
+# Changelog
+
+---
+
+## [2.0.0]
+
+- Added thumb size configurations support
+- Kotlin port
+- Global repository update
+- Changed: `setProgressAnimationCallback` to `setActionCallback`
+- Changed: `setBackgroundColor` to `setProgressBackgroundColor`
+- Changed: `setProgressThumbSizeRate` to `setProgressMaxThumbSizeRate`
+- Changed: `` to ``
+- Changed: `` to ``
+
+---
+
+## [1.4.1]
+
+- Changed LIBRARY_PACKAGE_NAME
+
+---
+
+## [1.3.1]
+
+- Added reverse progress
+
+---
+
+## [1.3.0]
+
+- Added multiple-arc-progress feature
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..09827eb
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at . All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/README.md b/README.md
index b800a38..55ff49d 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,19 @@
# CircularProgressView
-
+
[](https://android-arsenal.com/details/1/6152) [](https://appetize.io/app/jeftbchvbfuafwpeaf00fba8bm)
+
A fancy CircularProgressView.
-#### Version 1.x
-- **Jul, 2019** - Added thumb size configurations support
-- **Nov, 2019** - Added "gradient color" (SweepGradient) for progress
-- **Apr, 2019** - Added rounded progress
-- **Jun, 2018** - Added reverse progress
-- **May, 2018** - Added _"multiple-arc-progress"_
-- **February, 2018** - Background alpha enable/disable
-- **November, 2017** - Progress thumb and animation callback
-- **September, 2017** - CircularProgressView
-
-
## Getting started
Include it into your project, for example, as a Gradle dependency:
```groovy
implementation 'com.github.guilhe:circular-progress-view:${LATEST_VERSION}'
```
- [](https://search.maven.org/search?q=g:com.github.guilhe%20AND%20circular-progress-view) [](https://bintray.com/gdelgado/android/circular-progress-view/_latestVersion)
-
+[](https://search.maven.org/search?q=g:com.github.guilhe%20AND%20circular-progress-view) [](https://bintray.com/gdelgado/android/circular-progress-view/_latestVersion)
+
## Usage
Check out the __sample__ module where you can find a few examples of how to create it by `xml` or `java`.
@@ -43,8 +33,8 @@ Attributes accepted in xml:
-
-
+
+
@@ -89,7 +79,7 @@ For the given array of colors:
```
The default result will be (left):
-
+
To achieve the result on the right side you have two options: either copy the first color and add it as last, or use the helper attribute/method that does that for you:
```xml
@@ -110,7 +100,7 @@ Finally, you may also use the attribute `progressBarColorArrayPositions` to pass
There are many methods to help you customize this `View` by code. For more details checkout the __sample app__, _javadocs_ or the code itself.
### "Multiple Progress" for PieChart
-
+
```java
setProgress(@NonNull List progressList, @NonNull List progressColorList)
@@ -119,7 +109,7 @@ This mode can be used to display a simple pie chart. It will disable the progres
## Sample
-
+
_Animation last update on April, 2019_
diff --git a/build.gradle b/build.gradle
index 2909677..626a13d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.kotlin_version = '1.4.0'
repositories {
jcenter()
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.3'
+ classpath 'com.android.tools.build:gradle:4.0.1'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@@ -28,9 +30,9 @@ allprojects {
ext {
minSdkVersion = 19
- targetSdkVersion = 29
- compileSdkVersion = 29
- buildToolsVersion = '29.0.3'
+ targetSdkVersion = 30
+ compileSdkVersion = 30
+ buildToolsVersion = '30.0.1'
supportLibraryVersion = '28.0.0'
}
\ No newline at end of file
diff --git a/circular-progress-view/build.gradle b/circular-progress-view/build.gradle
index aa4e8e6..0677169 100644
--- a/circular-progress-view/build.gradle
+++ b/circular-progress-view/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -23,8 +24,12 @@ android {
dependencies {
implementation "com.android.support:support-annotations:$rootProject.supportLibraryVersion"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
if (project.rootProject.file('local.properties').exists()) {
apply from: 'deploy.gradle'
+}
+repositories {
+ mavenCentral()
}
\ No newline at end of file
diff --git a/circular-progress-view/src/main/java/com/github/guilhe/views/CircularProgressView.java b/circular-progress-view/src/main/java/com/github/guilhe/views/CircularProgressView.java
deleted file mode 100644
index d1c0258..0000000
--- a/circular-progress-view/src/main/java/com/github/guilhe/views/CircularProgressView.java
+++ /dev/null
@@ -1,823 +0,0 @@
-package com.github.guilhe.views;
-
-import android.animation.Animator;
-import android.animation.FloatEvaluator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.graphics.SweepGradient;
-import android.os.Build;
-import android.support.annotation.ColorInt;
-import android.support.annotation.ColorRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.animation.DecelerateInterpolator;
-
-import com.github.guilhe.views.circularprogress.R;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.github.guilhe.views.ProgressThumbScaleType.AUTO;
-import static com.github.guilhe.views.ProgressThumbScaleType.POINT;
-import static com.github.guilhe.views.ProgressThumbScaleType.RATE;
-import static com.github.guilhe.views.ProgressThumbScaleType.values;
-
-@SuppressWarnings("unused")
-public class CircularProgressView extends View {
-
- private static final String TAG = CircularProgressView.class.getSimpleName();
-
- private static final float ANGLE_OFFSET_FOR_MULTIPLE_ARC_PROGRESS = 6;
- private static final float DEFAULT_VIEW_PADDING_DP = 10;
- private static final float DEFAULT_SHADOW_PADDING_DP = 5;
- private static final float DEFAULT_STROKE_THICKNESS_DP = 10;
- private static final float DEFAULT_THUMB_SIZE_DP = 10;
- private static final float DEFAULT_MAXIMUM_THUMB_SIZE_RATE = 2;
- private static final int DEFAULT_MAX_WIDTH_DP = 100;
- private static final int DEFAULT_MAX = 100;
- private static final int DEFAULT_STARTING_ANGLE = 270;
- private static final int DEFAULT_ANIMATION_MILLIS = 1000;
- private static final int DEFAULT_PROGRESS_COLOR = Color.BLACK;
- private static final float DEFAULT_BACKGROUND_ALPHA = 0.3f;
- private static final TimeInterpolator DEFAULT_INTERPOLATOR = new DecelerateInterpolator();
-
- private final float mDefaultViewPadding = dpToPx(DEFAULT_VIEW_PADDING_DP);
- private final float mDefaultShadowPadding = dpToPx(DEFAULT_SHADOW_PADDING_DP);
- private final float mDefaultStrokeThickness = dpToPx(DEFAULT_STROKE_THICKNESS_DP);
- private final float mDefaultThumbSize = dpToPx(DEFAULT_THUMB_SIZE_DP);
- private final int mDefaultMaxWidth = dpToPx(DEFAULT_MAX_WIDTH_DP);
-
- private int mMax;
- private boolean mShadowEnabled;
- private boolean mProgressThumbEnabled;
- private ProgressThumbScaleType mProgressThumbScaleType;
- private float mMaxThumbSizeRate;
- private int mStartingAngle;
- private boolean mMultipleArcsEnabled;
- private float mProgressListTotal;
- private ArrayList mProgressList = new ArrayList<>();
- private ArrayList mProgressPaintList = new ArrayList<>();
- private float mProgress;
- private float mProgressStrokeThickness;
- private float mProgressThumbSize;
- private float mProgressThumbSizeRate;
- private float mProgressIconThickness;
- private int mProgressColor;
- private int mBackgroundColor;
- private boolean mBackgroundAlphaEnabled;
- private boolean mReverseEnabled;
- private boolean mProgressRounded;
- private List mValuesToDrawList = new ArrayList<>();
-
- private RectF mProgressRectF;
- private RectF mShadowRectF;
- private Paint mBackgroundPaint;
- private Paint mProgressPaint;
- private Paint mThumbPaint;
- private Paint mShadowPaint;
- private Paint mShadowThumbPaint;
- private float mLastValidRawMeasuredDim;
- private float mLastValidStrokeThickness;
- private float mLastValidThumbSize;
- private float mLastValidThumbSizeRate;
-
- private TimeInterpolator mInterpolator;
- private Animator mProgressAnimator;
- private OnProgressChangeAnimationCallback mCallback;
- private Shader mShader;
- private int[] mShaderColors;
- private float[] mShaderPositions;
- private boolean mInitShader;
- private boolean mSizeChanged = false;
-
- public interface OnProgressChangeAnimationCallback {
- void onProgressChanged(float progress);
-
- void onAnimationFinished(float progress);
- }
-
- public CircularProgressView(Context context) {
- super(context);
- init(context, null);
- }
-
- public CircularProgressView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
-
- public CircularProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context, attrs);
- }
-
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- public CircularProgressView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- init(context, attrs);
- }
-
- private void init(Context context, AttributeSet attrs) {
- mLastValidStrokeThickness = mDefaultStrokeThickness;
- mLastValidThumbSize = mDefaultThumbSize;
- mLastValidThumbSizeRate = DEFAULT_MAXIMUM_THUMB_SIZE_RATE;
- mInterpolator = DEFAULT_INTERPOLATOR;
- mProgressRectF = new RectF();
- mShadowRectF = new RectF();
-
- mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBackgroundPaint.setStyle(Paint.Style.STROKE);
- mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mProgressPaint.setStyle(Paint.Style.STROKE);
- mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mShadowPaint.setStyle(Paint.Style.STROKE);
- mShadowThumbPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mShadowThumbPaint.setStyle(Paint.Style.FILL);
- mThumbPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mThumbPaint.setStyle(Paint.Style.FILL);
-
- if (attrs != null) {
- TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircularProgressView, 0, 0);
- try {
- mMax = typedArray.getInt(R.styleable.CircularProgressView_max, DEFAULT_MAX);
- mShadowEnabled = typedArray.getBoolean(R.styleable.CircularProgressView_shadow, true);
- mProgressThumbEnabled = typedArray.getBoolean(R.styleable.CircularProgressView_progressThumb, false);
- mProgressThumbScaleType = values()[typedArray.getInteger(R.styleable.CircularProgressView_progressThumbScaleType, 0)];
- mMaxThumbSizeRate = typedArray.getFloat(R.styleable.CircularProgressView_progressThumbSizeMaxRate, DEFAULT_MAXIMUM_THUMB_SIZE_RATE);
- mStartingAngle = typedArray.getInteger(R.styleable.CircularProgressView_startingAngle, DEFAULT_STARTING_ANGLE);
- mProgress = typedArray.getFloat(R.styleable.CircularProgressView_progress, 0);
- mProgressStrokeThickness = typedArray.getDimension(R.styleable.CircularProgressView_progressBarThickness, mDefaultStrokeThickness);
- mProgressThumbSize = typedArray.getDimension(R.styleable.CircularProgressView_progressThumbSize, mDefaultThumbSize);
- setProgressThumbSizeRate(typedArray.getFloat(R.styleable.CircularProgressView_progressThumbSizeRate, DEFAULT_MAXIMUM_THUMB_SIZE_RATE));
- mProgressColor = typedArray.getInt(R.styleable.CircularProgressView_progressBarColor, DEFAULT_PROGRESS_COLOR);
- mProgressRounded = typedArray.getBoolean(R.styleable.CircularProgressView_progressBarRounded, false);
- mBackgroundColor = typedArray.getInt(R.styleable.CircularProgressView_backgroundColor, mProgressColor);
- mBackgroundAlphaEnabled = typedArray.getBoolean(R.styleable.CircularProgressView_backgroundAlphaEnabled, true);
- mReverseEnabled = typedArray.getBoolean(R.styleable.CircularProgressView_reverse, false);
-
- int colorsId = typedArray.getResourceId(R.styleable.CircularProgressView_progressBarColorArray, -1);
- boolean duplicate = typedArray.getBoolean(R.styleable.CircularProgressView_duplicateFirstColorInArray, false);
- if (colorsId != -1) {
- mShaderColors = typedArray.getResources().getIntArray(colorsId);
- if (duplicate) {
- mShaderColors = duplicateFirstColor(mShaderColors);
- }
- mInitShader = true;
- }
- int positionsId = typedArray.getResourceId(R.styleable.CircularProgressView_progressBarColorArrayPositions, -1);
- if (positionsId != -1) {
- TypedArray floats = typedArray.getResources().obtainTypedArray(positionsId);
- mShaderPositions = new float[floats.length()];
- for (int i = 0; i < floats.length(); i++) {
- mShaderPositions[i] = floats.getFloat(i, 0f);
- }
- floats.recycle();
- }
- } finally {
- typedArray.recycle();
- }
- } else {
- mProgressStrokeThickness = mDefaultStrokeThickness;
- mProgressThumbSize = mDefaultThumbSize;
- mProgressThumbSizeRate = DEFAULT_MAXIMUM_THUMB_SIZE_RATE;
- mProgressThumbScaleType = AUTO;
- mMaxThumbSizeRate = DEFAULT_MAXIMUM_THUMB_SIZE_RATE;
- mShadowEnabled = true;
- mMax = DEFAULT_MAX;
- mStartingAngle = DEFAULT_STARTING_ANGLE;
- mProgressColor = DEFAULT_PROGRESS_COLOR;
- mBackgroundColor = mProgressColor;
- mBackgroundAlphaEnabled = true;
- mReverseEnabled = false;
- mProgressRounded = false;
- mShader = null;
- mShaderColors = null;
- mShaderPositions = null;
- }
-
- resetBackgroundPaint();
- mProgressPaint.setColor(mProgressColor);
- setShader(mShader);
- mProgressPaint.setStrokeCap(mProgressRounded ? Paint.Cap.ROUND : Paint.Cap.SQUARE);
- mShadowPaint.setColor(adjustAlpha(Color.BLACK, 0.2f));
- mShadowPaint.setStrokeCap(mProgressPaint.getStrokeCap());
- setThickness(mProgressStrokeThickness, false);
- }
-
- /**
- * Either width or height, this view will use Math.min(width, height) value.
- * If an invalid size is set it won't take effect and a last valid size will be used.
- * Check {@link #onMeasure(int, int)}
- *
- * @param size in pixels
- */
- public void setSize(int size) {
- getLayoutParams().height = size;
- mSizeChanged = true;
- requestLayout();
- }
-
- /**
- * This method changes the progress bar starting angle.
- * The default value is 270 and it's equivalent to 12 o'clock.
- *
- * @param angle where the progress bar starts.
- */
- public void setStartingAngle(int angle) {
- mStartingAngle = angle;
- invalidate();
- }
-
- public int getStartingAngle() {
- return mStartingAngle;
- }
-
- /**
- * Sets progress bar max value (100%)
- *
- * @param max value
- */
- public void setMax(int max) {
- mMax = max;
- invalidate();
- }
-
- public int getMax() {
- return mMax;
- }
-
- /**
- * Changes progress and background color
- *
- * @param color - Color
- */
- public void setColor(int color) {
- setProgressColor(color);
- setBackgroundColor(color);
- }
-
- /**
- * You can simulate the use of this method with by calling {@link #setColor(int)} with ContextCompat:
- * setBackgroundColor(ContextCompat.getColor(resId));
- */
- @RequiresApi(api = Build.VERSION_CODES.M)
- public void setColorResource(@ColorRes int resId) {
- setColor(getContext().getColor(resId));
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- public void setColor(Color color) {
- setColor(color.toArgb());
- }
-
- public void setProgressColor(int color) {
- mProgressColor = color;
- if (mBackgroundColor == -1) {
- setBackgroundColor(color);
- }
- mProgressPaint.setColor(color);
- setShader(null);
- invalidate();
- }
-
- /**
- * You can simulate the use of this method with by calling {@link #setProgressColor(int)} with ContextCompat:
- * setProgressColor(ContextCompat.getColor(resId));
- */
- @RequiresApi(api = Build.VERSION_CODES.M)
- public void setProgressColorResource(@ColorRes int resId) {
- setProgressColor(getContext().getColor(resId));
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- public void setProgressColor(Color color) {
- setProgressColor(color.toArgb());
- }
-
- /**
- * This will create a SweepGradient and use it as progress color. Rainboooowwwww!
- *
- * @param colors The colors to be distributed between around the center.
- * There must be at least 2 colors in the array.
- * @param positions May be NULL. The relative position of
- * each corresponding color in the colors array, beginning
- * with 0 and ending with 1.0. If the values are not
- * monotonic, the drawing may produce unexpected results.
- * If positions is NULL, then the colors are automatically
- * spaced evenly.
- * @param duplicateFirst to create a perfect stitch the last color from the array must be equal to the first. If true it will do it for you.
- */
- public void setProgressColors(@NonNull @ColorInt int[] colors, @Nullable float[] positions, boolean duplicateFirst) {
- if (duplicateFirst) {
- colors = duplicateFirstColor(colors);
- }
- mShaderColors = colors;
- mShaderPositions = positions;
- setShader(new SweepGradient(mProgressRectF.centerX(), mProgressRectF.centerY(), colors, positions));
- invalidate();
- }
-
- public void setProgressColors(@NonNull @ColorInt int[] colors, @Nullable float[] positions) {
- setProgressColors(colors, positions, false);
- }
-
- private int[] duplicateFirstColor(@ColorInt @NonNull int[] colors) {
- int[] aux = Arrays.copyOf(colors, colors.length + 1);
- aux[colors.length] = colors[0];
- colors = aux;
- return colors;
- }
-
- private void setShader(Shader shader) {
- mShader = shader;
- mProgressPaint.setShader(shader);
- }
-
- public int getProgressColor() {
- return mProgressColor;
- }
-
- public void setBackgroundColor(int color) {
- mBackgroundColor = color;
- resetBackgroundPaint();
- invalidate();
- }
-
- public void setBackgroundAlphaEnabled(boolean enabled) {
- mBackgroundAlphaEnabled = enabled;
- resetBackgroundPaint();
- invalidate();
- }
-
- public boolean isBackgroundAlphaEnabled() {
- return mBackgroundAlphaEnabled;
- }
-
- public void setReverseEnabled(boolean enabled) {
- mReverseEnabled = enabled;
- invalidate();
- }
-
- public boolean isReverseEnabled() {
- return mReverseEnabled;
- }
-
- public boolean isProgressRounded() {
- return mProgressRounded;
- }
-
- public void setProgressRounded(boolean enabled) {
- mProgressRounded = enabled;
- mProgressPaint.setStrokeCap(mProgressRounded ? Paint.Cap.ROUND : Paint.Cap.SQUARE);
- mShadowPaint.setStrokeCap(mProgressPaint.getStrokeCap());
- invalidate();
- }
-
- /**
- * You can simulate the use of this method with by calling {@link #setBackgroundColor(int)} with ContextCompat:
- * setBackgroundColor(ContextCompat.getColor(resId));
- */
- @RequiresApi(api = Build.VERSION_CODES.M)
- public void setShadowColorResource(@ColorRes int resId) {
- setBackgroundColor(getContext().getColor(resId));
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- public void setBackgroundColor(Color color) {
- setBackgroundColor(color.toArgb());
- }
-
- public int getBackgroundColor() {
- return mBackgroundColor;
- }
-
- public void setShadowEnabled(boolean enable) {
- mShadowEnabled = enable;
- invalidate();
- }
-
- public boolean isShadowEnabled() {
- return mShadowEnabled;
- }
-
- public void setProgressThumbEnabled(boolean enable) {
- mProgressThumbEnabled = enable;
- invalidate();
- requestLayout();
- }
-
- public boolean isProgressThumbEnabled() {
- return mProgressThumbEnabled;
- }
-
- /**
- * Changes progressBar & progressIcon, background and shadow line width.
- *
- * @param thickness in pixels
- */
- public void setProgressStrokeThickness(float thickness) {
- setThickness(thickness, true);
- }
-
- private void setThickness(float thickness, boolean requestLayout) {
- mProgressStrokeThickness = thickness;
- mProgressIconThickness = mProgressStrokeThickness / 2;
- mBackgroundPaint.setStrokeWidth(mProgressStrokeThickness);
- mProgressPaint.setStrokeWidth(mProgressStrokeThickness);
- if (mProgressPaintList != null) {
- for (Paint paint : mProgressPaintList) {
- paint.setStrokeWidth(mProgressStrokeThickness);
- }
- }
- mShadowPaint.setStrokeWidth(mProgressStrokeThickness);
- if (requestLayout) {
- requestLayout();
- }
- }
-
- public float getProgressStrokeThickness() {
- return mProgressStrokeThickness;
- }
-
- public void setProgressThumbSize(float size) {
- setThumbSize(size, true);
- }
-
- private void setThumbSize(float size, boolean requestLayout) {
- mProgressThumbSize = size;
-
- if (requestLayout) {
- requestLayout();
- }
- }
-
- public float getProgressThumbSize() {
- return mProgressThumbSize;
- }
-
- public void setProgressThumbSizeRate(float rate) {
- setThumbSizeRate(rate, true);
- }
-
- private void setThumbSizeRate(float size, boolean requestLayout) {
- mProgressThumbSizeRate = Math.max(Math.min(size, mMaxThumbSizeRate), 0); // To prevent the Thumb size too big
-
- if (requestLayout) {
- requestLayout();
- }
- }
-
- public float getProgressThumbSizeRate() {
- return mProgressThumbSizeRate;
- }
-
- public void setProgressMaxThumbSizeRate(float maxRate) {
- mMaxThumbSizeRate = maxRate;
- }
-
- public float getProgressMaxThumbSizeRate() {
- return mMaxThumbSizeRate;
- }
-
- public void setProgressThumbScaleType(int index) {
- mProgressThumbScaleType = ProgressThumbScaleType.values()[Math.max(Math.min(index, 0), values().length-1)];
- }
-
- public void setProgressThumbScaleType(ProgressThumbScaleType scaleType) {
- mProgressThumbScaleType = scaleType;
- }
-
- public ProgressThumbScaleType getProgressThumbScaleType() {
- return mProgressThumbScaleType;
- }
-
- public void setProgress(float progress) {
- setProgress(progress, false);
- }
-
- public void setProgress(float progress, boolean animate) {
- setProgress(progress, animate, DEFAULT_ANIMATION_MILLIS);
- }
-
- public void setProgress(float progress, boolean animate, long duration) {
- setProgress(progress, animate, duration, true);
- }
-
- /**
- * This method will activate the "multiple-arc-progress" and disable the progress thumb, progress round and background.
- * This method disables the "single-arc-progress".
- *
- * @param progressList - list containing all the progress "step-per-arc". Their sum most be less or equal to {@link #getMax()}.
- * @param progressColorList - list containing the progress "step-per-arc" color. If progressColorList.size() is less than progressList.size(), Color.TRANSPARENT will be used for the missing colors.
- * @throws RuntimeException - will be thrown if progress entities sum is greater than max value.
- */
- public void setProgress(@NonNull List progressList, @NonNull List progressColorList) throws RuntimeException {
- setProgressRounded(false);
- mProgress = mProgressListTotal = 0;
- for (float value : progressList) {
- mProgressListTotal += value;
- if (mProgressListTotal > mMax) {
- throw new RuntimeException(String.format("Progress entities sum (%s) is greater than max value (%s)", mProgressListTotal, mMax));
- }
- }
-
- mMultipleArcsEnabled = true;
- mProgressList = new ArrayList<>(progressList);
- mProgressPaintList = new ArrayList<>();
- for (int i = 0; i < mProgressList.size(); i++) {
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setStyle(Paint.Style.STROKE);
- paint.setColor(i < progressColorList.size() ? progressColorList.get(i) : Color.TRANSPARENT);
- mProgressPaintList.add(paint);
- }
- setThickness(mProgressStrokeThickness, false);
- invalidate();
- }
-
- public float getProgress() {
- return mProgress;
- }
-
- public void resetProgress() {
- setProgress(0);
- }
-
- public void resetProgress(boolean animate) {
- resetProgress(animate, DEFAULT_ANIMATION_MILLIS);
- }
-
- public void resetProgress(boolean animate, long duration) {
- setProgress(0, animate, duration, false);
- }
-
- public void setAnimationInterpolator(TimeInterpolator interpolator) {
- mInterpolator = interpolator == null ? DEFAULT_INTERPOLATOR : interpolator;
- }
-
- public void setProgressAnimationCallback(OnProgressChangeAnimationCallback callback) {
- mCallback = callback;
- }
-
- private void resetBackgroundPaint() {
- mBackgroundPaint.setColor(mBackgroundAlphaEnabled ? adjustAlpha(mBackgroundColor, DEFAULT_BACKGROUND_ALPHA) : mBackgroundColor);
- }
-
- /**
- * This method will activate the "single-arc-progress" and enable the progress thumb and background.
- * This method disables the "multiple-arc-progress".
- */
- private void setProgress(float progress, boolean animate, long duration, boolean clockwise) {
- mMultipleArcsEnabled = false;
- if (animate) {
- if (mProgressAnimator != null) {
- mProgressAnimator.cancel();
- }
- mProgressAnimator = getAnimator(getProgress(), clockwise ? progress : 0, duration, new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- setProgressValue(((Float) valueAnimator.getAnimatedValue()));
- if (mCallback != null) {
- mCallback.onProgressChanged(mProgress);
- }
- }
- });
- mProgressAnimator.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCallback != null) {
- mCallback.onAnimationFinished(mProgress);
- }
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- //not in use
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- //not in use
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- //not in use
- }
- });
- mProgressAnimator.start();
- } else {
- setProgressValue(progress);
- }
- }
-
- private void setProgressValue(float value) {
- mProgress = value;
- invalidate();
- }
-
- private ValueAnimator getAnimator(double current, double next, long duration, ValueAnimator.AnimatorUpdateListener updateListener) {
- ValueAnimator animator = new ValueAnimator();
- animator.setInterpolator(mInterpolator);
- animator.setDuration(duration);
- animator.setObjectValues(current, next);
- animator.setEvaluator(new FloatEvaluator() {
- public Integer evaluate(float fraction, float startValue, float endValue) {
- return Math.round(startValue + (endValue - startValue) * fraction);
- }
- });
- animator.addUpdateListener(updateListener);
- return animator;
- }
-
- /**
- * Changes color's alpha by the factor
- *
- * @param color The color to change alpha
- * @param factor 1.0f (solid) to 0.0f (transparent)
- * @return int - A color with modified alpha
- */
- private int adjustAlpha(int color, float factor) {
- int alpha = Math.round(Color.alpha(color) * factor);
- int red = Color.red(color);
- int green = Color.green(color);
- int blue = Color.blue(color);
- return Color.argb(alpha, red, green, blue);
- }
-
- @Override
- protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED ? MeasureSpec.getSize(heightMeasureSpec) : mDefaultMaxWidth;
-
- int rawMeasuredDim = Math.max(Math.min(width, height), 0);
- float progressWidth = mProgressStrokeThickness;
-
- float thumbSize = 0;
- if (mProgressThumbScaleType == POINT) {
- thumbSize = mProgressThumbSize;
- } else if (mProgressThumbScaleType == RATE) {
- thumbSize = (mProgressStrokeThickness / 2) * mProgressThumbSizeRate;
- } else {
- thumbSize = mProgressStrokeThickness;
- }
-
- // if ThumbSize diameter is thicker than Stroke
- if (mProgressThumbEnabled && mProgressThumbScaleType != AUTO) {
- if (thumbSize * 2 > mProgressStrokeThickness) {
- // increase progressWidth by thumbSize
- progressWidth += thumbSize - mProgressStrokeThickness;
- } else {
- progressWidth = mProgressStrokeThickness / 2;
- }
- }
-
- float arcDim = Math.max(progressWidth, 0) + mDefaultViewPadding;
- mProgressRectF.set(arcDim, arcDim, rawMeasuredDim - arcDim, rawMeasuredDim - arcDim);
-
- //To avoid creating a messy composition
- if (mProgressRectF.width() <= (Math.max(progressWidth, thumbSize))) {
- arcDim = mLastValidRawMeasuredDim;
- mProgressRectF.set(arcDim, arcDim, rawMeasuredDim - arcDim, rawMeasuredDim - arcDim);
- setThickness(mLastValidStrokeThickness, false);
- setThumbSize(mLastValidThumbSize, false);
- setThumbSizeRate(mLastValidThumbSizeRate, false);
- } else {
- mLastValidRawMeasuredDim = arcDim;
- mLastValidStrokeThickness = mProgressStrokeThickness;
- mLastValidThumbSize = mProgressThumbSize;
- mLastValidThumbSizeRate = mProgressThumbSizeRate;
- }
-
- mShadowRectF.set(mProgressRectF.left, mDefaultShadowPadding + mProgressRectF.top, mProgressRectF.right, mDefaultShadowPadding + mProgressRectF.bottom);
- setMeasuredDimension(rawMeasuredDim, rawMeasuredDim);
- }
-
- @Override
- protected synchronized void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- //Either we are using "single-arc-progress" or "multiple-arc-progress".
- mValuesToDrawList.clear();
- if (!mMultipleArcsEnabled) {
- mValuesToDrawList.add(mProgress);
- mProgressPaintList.clear();
- mProgressPaintList.add(mProgressPaint);
- } else {
- mValuesToDrawList.addAll(mProgressList);
- }
-
- float angle;
- float previousAngle = mStartingAngle;
- float radius = (float) getWidth() / 2 - mDefaultViewPadding;
-
- float thumbSize = 0;
- if (mProgressThumbScaleType == AUTO) {
- thumbSize = mProgressIconThickness;
- radius -= (mProgressIconThickness + mProgressStrokeThickness / 2);
- } else {
- boolean isThicker = false;
- if (mProgressThumbScaleType == POINT) {
- thumbSize = mProgressThumbSize;
- isThicker = mProgressThumbSize * 2 > mProgressStrokeThickness;
- } else if (mProgressThumbScaleType == RATE) {
- thumbSize = (mProgressStrokeThickness / 2) * mProgressThumbSizeRate;
- isThicker = mProgressThumbSizeRate > 1;
- }
-
- if (isThicker) {
- radius -= thumbSize;
- } else {
- radius -= mProgressStrokeThickness / 2;
- }
- }
- double endX, endY;
-
- //Shadow logic
- if (mShadowEnabled) {
- angle = 360 * (mMultipleArcsEnabled ? mProgressListTotal : mProgress) / mMax;
- if (mReverseEnabled) {
- angle *= -1;
- }
- if (!mMultipleArcsEnabled && mProgressThumbEnabled) {
- //Only in "single-arc-progress", otherwise we'll end up with N thumbs
-
- //Who doesn't love a bit of math? :)
- //cos(a) = adj / hyp <>cos(angle) = x / radius <>x = cos(angle) * radius
- //sin(a) = opp / hyp <>sin(angle) = y / radius <>y = sin(angle) * radius
- //x = cos(startingAngle + progressAngle) * radius + originX(center)
- //y = sin(startingAngle + progressAngle) * radius + originY(center)
- endX = (Math.cos(Math.toRadians(previousAngle + angle)) * radius);
- endY = (Math.sin(Math.toRadians(previousAngle + angle)) * radius);
- mShadowThumbPaint.set(mShadowPaint); // shadow stroke style copy
- switch(mProgressThumbScaleType) {
- case POINT:
- case RATE:
- mShadowThumbPaint.setStyle(Paint.Style.FILL);
- break;
- case AUTO:
- default:
- mShadowThumbPaint.setStyle(Paint.Style.STROKE);
- }
- canvas.drawCircle((float) endX + mShadowRectF.centerX(), (float) endY + mShadowRectF.centerY(), thumbSize, mShadowThumbPaint);
- }
- canvas.drawArc(mShadowRectF, previousAngle, angle, false, mShadowPaint);
- }
-
- //Progress logic
- if (mInitShader) {
- mInitShader = false;
- setShader(new SweepGradient(mProgressRectF.centerX(), mProgressRectF.centerY(), mShaderColors, mShaderPositions));
- } else if (mSizeChanged) {
- mSizeChanged = false;
- setShader(new SweepGradient(mProgressRectF.centerX(), mProgressRectF.centerY(), mShaderColors, mShaderPositions));
- }
-
- for (int i = 0; i < mValuesToDrawList.size(); i++) {
- if (!mMultipleArcsEnabled) {
- //No background will be used when "multiple-arc-progress" is enable because it will be mixed with the "progress-colors"
- canvas.drawOval(mProgressRectF, mBackgroundPaint);
- }
-
- angle = 360 * mValuesToDrawList.get(i) / mMax;
- if (mReverseEnabled) {
- angle *= -1;
- }
- float offset = !mReverseEnabled && mMultipleArcsEnabled ? ANGLE_OFFSET_FOR_MULTIPLE_ARC_PROGRESS : 0; //to better glue all the "pieces"
- canvas.drawArc(mProgressRectF, previousAngle - offset, angle + offset, false, mProgressPaintList.get(i));
- if (!mMultipleArcsEnabled && mProgressThumbEnabled) {
- //Only in "single-arc-progress", otherwise we'll end up with N thumbs
- endX = (Math.cos(Math.toRadians(previousAngle + angle)) * radius);
- endY = (Math.sin(Math.toRadians(previousAngle + angle)) * radius);
-
- mThumbPaint.set(mProgressPaintList.get(i)); // stroke style copy
- switch(mProgressThumbScaleType) {
- case POINT:
- case RATE:
- mThumbPaint.setStyle(Paint.Style.FILL);
- break;
- case AUTO:
- default:
- mThumbPaint.setStyle(Paint.Style.STROKE);
- }
- canvas.drawCircle((float) endX + mProgressRectF.centerX(), (float) endY + mProgressRectF.centerY(), thumbSize, mThumbPaint);
- }
- previousAngle += angle;
- }
- }
-
- public int dpToPx(float dp) {
- return (int) Math.ceil(dp * Resources.getSystem().getDisplayMetrics().density);
- }
-}
\ No newline at end of file
diff --git a/circular-progress-view/src/main/java/com/github/guilhe/views/CircularProgressView.kt b/circular-progress-view/src/main/java/com/github/guilhe/views/CircularProgressView.kt
new file mode 100644
index 0000000..8342ecf
--- /dev/null
+++ b/circular-progress-view/src/main/java/com/github/guilhe/views/CircularProgressView.kt
@@ -0,0 +1,619 @@
+package com.github.guilhe.views
+
+import android.animation.Animator
+import android.animation.FloatEvaluator
+import android.animation.TimeInterpolator
+import android.animation.ValueAnimator
+import android.animation.ValueAnimator.AnimatorUpdateListener
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.*
+import android.os.Build
+import android.support.annotation.ColorInt
+import android.support.annotation.ColorRes
+import android.support.annotation.RequiresApi
+import android.util.AttributeSet
+import android.view.View
+import android.view.animation.DecelerateInterpolator
+import com.github.guilhe.views.ProgressThumbScaleType.*
+import com.github.guilhe.views.circularprogress.R
+import java.util.*
+import kotlin.jvm.Throws
+import kotlin.math.*
+
+@Suppress("unused", "MemberVisibilityCanBePrivate")
+class CircularProgressView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+) : View(context, attrs, defStyleAttr) {
+
+ private val defaultViewPadding = dpToPx(DEFAULT_VIEW_PADDING_DP).toFloat()
+ private val defaultShadowPadding = dpToPx(DEFAULT_SHADOW_PADDING_DP).toFloat()
+ private val defaultStrokeThickness = dpToPx(DEFAULT_STROKE_THICKNESS_DP).toFloat()
+ private val defaultThumbSize = dpToPx(DEFAULT_THUMB_SIZE_DP).toFloat()
+ private val defaultMaxWidth = dpToPx(DEFAULT_MAX_WIDTH_DP.toFloat())
+ private val valuesToDrawList: MutableList = ArrayList()
+
+ private lateinit var progressRectF: RectF
+ private lateinit var shadowRectF: RectF
+ private lateinit var backgroundPaint: Paint
+ private lateinit var progressPaint: Paint
+ private lateinit var shadowPaint: Paint
+ private lateinit var shadowThumbPaint: Paint
+ private lateinit var thumbPaint: Paint
+ private lateinit var progressAnimator: ValueAnimator
+
+ private var multipleArcsEnabled = false
+ private var progressListTotal = 0f
+ private var progressList = ArrayList()
+ private var progressPaintList: ArrayList = ArrayList()
+ private var progress = 0f
+ private var progressThumbSizeRate = DEFAULT_MAXIMUM_THUMB_SIZE_RATE
+ private var progressIconThickness = 0f
+ private var progressBackgroundAlphaEnabled = true
+ private var reverseEnabled = false
+ private var progressRounded = false
+ private var lastValidRawMeasuredDim = 0f
+ private var lastValidStrokeThickness = defaultStrokeThickness
+ private var lastValidThumbSize = defaultThumbSize
+ private var lastValidThumbSizeRate = DEFAULT_MAXIMUM_THUMB_SIZE_RATE
+ private var interpolator: TimeInterpolator = DEFAULT_INTERPOLATOR
+ private var shader: Shader? = null
+ private var shaderColors: IntArray = intArrayOf()
+ private var shaderPositions: FloatArray = floatArrayOf()
+ private var initShader = false
+ private var sizeChanged = false
+
+ var progressMaxThumbSizeRate = DEFAULT_MAXIMUM_THUMB_SIZE_RATE
+ var progressThumbSize = defaultThumbSize
+ var actionCallback: CircularProgressViewActionCallback? = null
+
+ var isBackgroundAlphaEnabled: Boolean = false
+ set(enabled) {
+ field = enabled
+ resetBackgroundPaint()
+ invalidate()
+ }
+
+ var isReverseEnabled: Boolean = false
+ set(enabled) {
+ field = enabled
+ invalidate()
+ }
+
+ var isProgressRounded: Boolean = false
+ set(enabled) {
+ field = enabled
+ progressPaint.strokeCap = if (progressRounded) Paint.Cap.ROUND else Paint.Cap.SQUARE
+ shadowPaint.strokeCap = progressPaint.strokeCap
+ invalidate()
+ }
+
+ var isShadowEnabled: Boolean = true
+ set(enable) {
+ field = enable
+ invalidate()
+ }
+
+ var isProgressThumbEnabled: Boolean = false
+ set(enable) {
+ field = enable
+ invalidate()
+ requestLayout()
+ }
+
+ var progressColor: Int = DEFAULT_PROGRESS_COLOR
+ set(color) {
+ field = color
+ if (color == -1) {
+ progressBackgroundColor = color
+ }
+ progressPaint.color = color
+ setShader(null)
+ invalidate()
+ }
+
+ var progressBackgroundColor: Int = DEFAULT_PROGRESS_COLOR
+ set(color) {
+ field = color
+ resetBackgroundPaint()
+ invalidate()
+ }
+
+ /**
+ * Changes progressBar & progressIcon, background and shadow line width. Thickness in pixels.
+ */
+ var progressStrokeThickness: Float = defaultStrokeThickness
+ set(thickness) {
+ setThickness(thickness, true)
+ }
+
+ var progressThumbScaleType: ProgressThumbScaleType = AUTO
+ set(type) {
+ field = values()[max(min(type.ordinal, 0), values().size - 1)]
+ }
+
+ /**
+ * This method changes the progress bar starting angle.
+ * The default value is 270 and it's equivalent to 12 o'clock.
+ */
+ var startingAngle: Int = DEFAULT_STARTING_ANGLE
+ set(angle) {
+ field = angle
+ invalidate()
+ }
+
+ /**
+ * Sets progress bar max value (100%)
+ */
+ var max: Int = DEFAULT_MAX
+ set(value) {
+ field = value
+ invalidate()
+ }
+
+ interface CircularProgressViewActionCallback {
+ fun onProgressChanged(progress: Float)
+ fun onAnimationFinished(progress: Float)
+ }
+
+ init {
+ setupAttr(context, attrs)
+ }
+
+ private fun setupAttr(context: Context, attrs: AttributeSet?) {
+ progressRectF = RectF()
+ shadowRectF = RectF()
+ backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ backgroundPaint.style = Paint.Style.STROKE
+ progressPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ progressPaint.style = Paint.Style.STROKE
+ shadowPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ shadowPaint.style = Paint.Style.STROKE
+ shadowThumbPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ shadowThumbPaint.style = Paint.Style.FILL
+ thumbPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ thumbPaint.style = Paint.Style.FILL
+
+ if (attrs != null) {
+ val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.CircularProgressView, 0, 0)
+ try {
+ max = typedArray.getInt(R.styleable.CircularProgressView_max, DEFAULT_MAX)
+ isShadowEnabled = typedArray.getBoolean(R.styleable.CircularProgressView_shadow, true)
+ isProgressThumbEnabled = typedArray.getBoolean(R.styleable.CircularProgressView_progressThumb, false)
+ progressThumbScaleType = values()[typedArray.getInteger(R.styleable.CircularProgressView_progressThumbScaleType, 0)]
+ progressMaxThumbSizeRate = typedArray.getFloat(R.styleable.CircularProgressView_progressThumbSizeMaxRate, DEFAULT_MAXIMUM_THUMB_SIZE_RATE)
+ startingAngle = typedArray.getInteger(R.styleable.CircularProgressView_startingAngle, DEFAULT_STARTING_ANGLE)
+ progress = typedArray.getFloat(R.styleable.CircularProgressView_progress, 0f)
+ progressStrokeThickness = typedArray.getDimension(R.styleable.CircularProgressView_progressBarThickness, defaultStrokeThickness)
+ progressThumbSize = typedArray.getDimension(R.styleable.CircularProgressView_progressThumbSize, defaultThumbSize)
+ progressThumbSizeRate = typedArray.getFloat(R.styleable.CircularProgressView_progressThumbSizeRate, DEFAULT_MAXIMUM_THUMB_SIZE_RATE)
+ progressColor = typedArray.getInt(R.styleable.CircularProgressView_progressBarColor, DEFAULT_PROGRESS_COLOR)
+ progressRounded = typedArray.getBoolean(R.styleable.CircularProgressView_progressBarRounded, false)
+ progressBackgroundColor = typedArray.getInt(R.styleable.CircularProgressView_progressBackgroundColor, progressColor)
+ progressBackgroundAlphaEnabled = typedArray.getBoolean(R.styleable.CircularProgressView_progressBackgroundAlphaEnabled, true)
+ reverseEnabled = typedArray.getBoolean(R.styleable.CircularProgressView_reverse, false)
+ val colorsId = typedArray.getResourceId(R.styleable.CircularProgressView_progressBarColorArray, -1)
+ val duplicate = typedArray.getBoolean(R.styleable.CircularProgressView_duplicateFirstColorInArray, false)
+ if (colorsId != -1) {
+ shaderColors = typedArray.resources.getIntArray(colorsId)
+ if (duplicate) {
+ shaderColors = duplicateFirstColor(shaderColors)
+ }
+ initShader = true
+ }
+ val positionsId = typedArray.getResourceId(R.styleable.CircularProgressView_progressBarColorArrayPositions, -1)
+ if (positionsId != -1) {
+ val floats = typedArray.resources.obtainTypedArray(positionsId)
+ shaderPositions = FloatArray(floats.length())
+ for (i in 0 until floats.length()) {
+ shaderPositions[i] = floats.getFloat(i, 0f)
+ }
+ floats.recycle()
+ }
+ } finally {
+ typedArray.recycle()
+ }
+ }
+
+ resetBackgroundPaint()
+ progressPaint.color = progressColor
+ setShader(shader)
+ progressPaint.strokeCap = if (progressRounded) Paint.Cap.ROUND else Paint.Cap.SQUARE
+ shadowPaint.color = adjustAlpha(Color.BLACK, 0.2f)
+ shadowPaint.strokeCap = progressPaint.strokeCap
+ setThickness(progressStrokeThickness, false)
+ }
+
+ /**
+ * Either width or height, this view will use Math.min(width, height) value.
+ * If an invalid size is set it won't take effect and a last valid size will be used.
+ * Check [.onMeasure]
+ *
+ * @param size in pixels
+ */
+ fun setSize(size: Int) {
+ layoutParams.height = size
+ sizeChanged = true
+ requestLayout()
+ }
+
+ /**
+ * Changes progress and background color
+ *
+ * @param color - Color
+ */
+ fun setColor(color: Int) {
+ progressColor = color
+ progressBackgroundColor = color
+ }
+
+ /**
+ * You can simulate the use of this method with by calling [.setColor] with ContextCompat:
+ * setBackgroundColor(ContextCompat.getColor(resId));
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ fun setColorResource(@ColorRes resId: Int) {
+ setColor(context.getColor(resId))
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ fun setColor(color: Color) {
+ setColor(color.toArgb())
+ }
+
+ /**
+ * You can simulate the use of this method with by calling [.setProgressColor] with ContextCompat:
+ * setProgressColor(ContextCompat.getColor(resId));
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ fun setProgressColorResource(@ColorRes resId: Int) {
+ progressColor = context.getColor(resId)
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ fun setProgressColor(color: Color) {
+ progressColor = color.toArgb()
+ }
+
+ /**
+ * This will create a SweepGradient and use it as progress color. Rainboooowwwww!
+ *
+ * @param colors - The colors to be distributed between around the center. There must be at least 2 colors in the array.
+ * @param positions - May be NULL. The relative position of each corresponding color in the colors array, beginning with 0 and ending with 1.0.
+ * If the values are not monotonic, the drawing may produce unexpected results. If positions is NULL, then the colors are automatically spaced evenly.
+ * @param duplicateFirst to create a perfect stitch the last color from the array must be equal to the first. If true it will do it for you.
+ */
+ @JvmOverloads
+ fun setProgressColors(@ColorInt colors: IntArray, positions: FloatArray, duplicateFirst: Boolean = false) {
+ shaderColors = if (duplicateFirst) duplicateFirstColor(colors) else colors
+ shaderPositions = positions
+ setShader(SweepGradient(progressRectF.centerX(), progressRectF.centerY(), colors, positions))
+ invalidate()
+ }
+
+ private fun duplicateFirstColor(@ColorInt colors: IntArray): IntArray {
+ return colors.copyOf(colors.size + 1).also {
+ it[colors.size] = colors[0]
+ }
+ }
+
+ private fun setShader(shader: Shader?) {
+ progressPaint.shader = shader
+ }
+
+ /**
+ * You can simulate the use of this method with by calling [.setBackgroundColor] with ContextCompat:
+ * setBackgroundColor(ContextCompat.getColor(resId));
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ fun setShadowColorResource(@ColorRes resId: Int) = setBackgroundColor(context.getColor(resId))
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ fun setBackgroundColor(color: Color) = setBackgroundColor(color.toArgb())
+
+ private fun setThickness(thickness: Float, requestLayout: Boolean) {
+ progressStrokeThickness = thickness
+ progressIconThickness = progressStrokeThickness / 2
+ backgroundPaint.strokeWidth = progressStrokeThickness
+ progressPaint.strokeWidth = progressStrokeThickness
+ for (paint in progressPaintList) {
+ paint.strokeWidth = progressStrokeThickness
+ }
+ shadowPaint.strokeWidth = progressStrokeThickness
+ if (requestLayout) {
+ requestLayout()
+ }
+ }
+
+ @JvmOverloads
+ fun setProgress(progress: Float, animate: Boolean = false, duration: Long = DEFAULT_ANIMATION_MILLIS.toLong()) {
+ setProgress(progress, animate, duration, true)
+ }
+
+ /**
+ * This method will activate the "multiple-arc-progress" and disable the progress thumb, progress round and background.
+ * This method disables the "single-arc-progress".
+ *
+ * @param progressList - list containing all the progress "step-per-arc". Their sum most be less or equal to [.getMax].
+ * @param progressColorList - list containing the progress "step-per-arc" color. If progressColorList.size() is less than progressList.size(), Color.TRANSPARENT will be used for the missing colors.
+ * @throws RuntimeException - will be thrown if progress entities sum is greater than max value.
+ */
+ @Throws(RuntimeException::class)
+ fun setProgress(progressList: List, progressColorList: List) {
+ progressRounded = false
+ progressListTotal = 0f
+ progress = progressListTotal
+ for (value in progressList) {
+ progressListTotal += value
+ if (progressListTotal > max) {
+ throw RuntimeException(String.format("Progress entities sum (%s) is greater than max value (%s)", progressListTotal, max))
+ }
+ }
+ multipleArcsEnabled = true
+ this.progressList = ArrayList(progressList)
+ progressPaintList = ArrayList()
+ for (i in progressList.indices) {
+ val paint = Paint(Paint.ANTI_ALIAS_FLAG)
+ paint.style = Paint.Style.STROKE
+ paint.color = if (i < progressColorList.size) progressColorList[i] else Color.TRANSPARENT
+ progressPaintList.add(paint)
+ }
+ setThickness(progressStrokeThickness, false)
+ invalidate()
+ }
+
+ @JvmOverloads
+ fun resetProgress(animate: Boolean = false, duration: Long = DEFAULT_ANIMATION_MILLIS.toLong()) {
+ setProgress(0f, animate, duration, false)
+ }
+
+ fun setAnimationInterpolator(interpolator: TimeInterpolator?) {
+ this.interpolator = interpolator ?: DEFAULT_INTERPOLATOR
+ }
+
+ private fun resetBackgroundPaint() {
+ backgroundPaint.color = if (progressBackgroundAlphaEnabled) adjustAlpha(progressBackgroundColor, DEFAULT_BACKGROUND_ALPHA) else progressBackgroundColor
+ }
+
+ /**
+ * This method will activate the "single-arc-progress" and enable the progress thumb and background.
+ * This method disables the "multiple-arc-progress".
+ */
+ private fun setProgress(progress: Float, animate: Boolean, duration: Long, clockwise: Boolean) {
+ multipleArcsEnabled = false
+ if (animate) {
+ if (::progressAnimator.isInitialized) {
+ progressAnimator.cancel()
+ }
+ progressAnimator = getAnimator(progress.toDouble(), if (clockwise) progress.toDouble() else 0.toDouble(), duration, AnimatorUpdateListener { valueAnimator ->
+ setProgressValue(valueAnimator.animatedValue as Float)
+ actionCallback?.onProgressChanged(progress)
+ })
+ progressAnimator.addListener(object : Animator.AnimatorListener {
+ override fun onAnimationEnd(animation: Animator) {
+ actionCallback?.onAnimationFinished(progress)
+ }
+
+ override fun onAnimationStart(animation: Animator) {}
+
+ override fun onAnimationCancel(animation: Animator) {}
+
+ override fun onAnimationRepeat(animation: Animator) {}
+ })
+ progressAnimator.start()
+ } else {
+ setProgressValue(progress)
+ }
+ }
+
+ private fun setProgressValue(value: Float) {
+ progress = value
+ invalidate()
+ }
+
+ private fun getAnimator(current: Double, next: Double, duration: Long, updateListener: AnimatorUpdateListener) = ValueAnimator().apply {
+ this.interpolator = interpolator
+ this.duration = duration
+ this.setObjectValues(current, next)
+ this.setEvaluator(object : FloatEvaluator() {
+ fun evaluate(fraction: Float, startValue: Float, endValue: Float): Int {
+ return (startValue + (endValue - startValue) * fraction).roundToInt()
+ }
+ })
+ this.addUpdateListener(updateListener)
+ }
+
+ /**
+ * Changes color's alpha by the factor
+ *
+ * @param color The color to change alpha
+ * @param factor 1.0f (solid) to 0.0f (transparent)
+ * @return int - A color with modified alpha
+ */
+ private fun adjustAlpha(color: Int, factor: Float): Int {
+ val alpha = (Color.alpha(color) * factor).roundToInt()
+ val red = Color.red(color)
+ val green = Color.green(color)
+ val blue = Color.blue(color)
+ return Color.argb(alpha, red, green, blue)
+ }
+
+ @Synchronized
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ val width = MeasureSpec.getSize(widthMeasureSpec)
+ val height = if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED) MeasureSpec.getSize(heightMeasureSpec) else defaultMaxWidth
+ val rawMeasuredDim = max(min(width, height), 0)
+ var progressWidth = progressStrokeThickness
+ val thumbSize = when (progressThumbScaleType) {
+ POINT -> progressThumbSize
+ RATE -> progressStrokeThickness / 2 * progressThumbSizeRate
+ else -> progressStrokeThickness
+ }
+
+ // if ThumbSize diameter is thicker than Stroke
+ if (isProgressThumbEnabled && progressThumbScaleType != AUTO) {
+ if (thumbSize * 2 > progressStrokeThickness) {
+ // increase progressWidth by thumbSize
+ progressWidth += thumbSize - progressStrokeThickness
+ } else {
+ progressWidth = progressStrokeThickness / 2
+ }
+ }
+ var arcDim = max(progressWidth, 0f) + defaultViewPadding
+ progressRectF[arcDim, arcDim, rawMeasuredDim - arcDim] = rawMeasuredDim - arcDim
+
+ //To avoid creating a messy composition
+ if (progressRectF.width() <= max(progressWidth, thumbSize)) {
+ arcDim = lastValidRawMeasuredDim
+ progressRectF[arcDim, arcDim, rawMeasuredDim - arcDim] = rawMeasuredDim - arcDim
+ setThickness(lastValidStrokeThickness, false)
+ progressThumbSize = lastValidThumbSize
+ progressThumbSizeRate = max(min(lastValidThumbSizeRate, progressMaxThumbSizeRate), 0f) // To prevent the Thumb size too big
+ } else {
+ lastValidRawMeasuredDim = arcDim
+ lastValidStrokeThickness = progressStrokeThickness
+ lastValidThumbSize = progressThumbSize
+ lastValidThumbSizeRate = progressThumbSizeRate
+ }
+ shadowRectF[progressRectF.left, defaultShadowPadding + progressRectF.top, progressRectF.right] = defaultShadowPadding + progressRectF.bottom
+ setMeasuredDimension(rawMeasuredDim, rawMeasuredDim)
+ }
+
+ @Synchronized
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ //Either we are using "single-arc-progress" or "multiple-arc-progress".
+ valuesToDrawList.clear()
+ if (!multipleArcsEnabled) {
+ valuesToDrawList.add(progress)
+ progressPaintList.clear()
+ progressPaintList.add(progressPaint)
+ } else {
+ valuesToDrawList.addAll(progressList)
+ }
+ var angle: Float
+ var previousAngle = startingAngle.toFloat()
+ var radius = width.toFloat() / 2 - defaultViewPadding
+ var thumbSize = 0f
+ if (progressThumbScaleType == AUTO) {
+ thumbSize = progressIconThickness
+ radius -= progressIconThickness + progressStrokeThickness / 2
+ } else {
+ var isThicker = false
+ if (progressThumbScaleType == POINT) {
+ thumbSize = progressThumbSize
+ isThicker = progressThumbSize * 2 > progressStrokeThickness
+ } else if (progressThumbScaleType == RATE) {
+ thumbSize = progressStrokeThickness / 2 * progressThumbSizeRate
+ isThicker = progressThumbSizeRate > 1
+ }
+ radius -= if (isThicker) {
+ thumbSize
+ } else {
+ progressStrokeThickness / 2
+ }
+ }
+ var endX: Double
+ var endY: Double
+
+ //Shadow logic
+ if (isShadowEnabled) {
+ angle = 360 * (if (multipleArcsEnabled) progressListTotal else progress) / max
+ if (reverseEnabled) {
+ angle *= -1f
+ }
+ if (!multipleArcsEnabled && isProgressThumbEnabled) {
+ //Only in "single-arc-progress", otherwise we'll end up with N thumbs
+
+ //Who doesn't love a bit of math? :)
+ //cos(a) = adj / hyp <>cos(angle) = x / radius <>x = cos(angle) * radius
+ //sin(a) = opp / hyp <>sin(angle) = y / radius <>y = sin(angle) * radius
+ //x = cos(startingAngle + progressAngle) * radius + originX(center)
+ //y = sin(startingAngle + progressAngle) * radius + originY(center)
+ endX = cos(Math.toRadians(previousAngle + angle.toDouble())) * radius
+ endY = sin(Math.toRadians(previousAngle + angle.toDouble())) * radius
+ shadowThumbPaint.set(shadowPaint) // shadow stroke style copy
+ when (progressThumbScaleType) {
+ POINT, RATE -> shadowThumbPaint.style = Paint.Style.FILL
+ else -> shadowThumbPaint.style = Paint.Style.STROKE
+ }
+ canvas.drawCircle(endX.toFloat() + shadowRectF.centerX(), endY.toFloat() + shadowRectF.centerY(), thumbSize, shadowThumbPaint)
+ }
+ canvas.drawArc(shadowRectF, previousAngle, angle, false, shadowPaint)
+ }
+
+ //Progress logic
+ if (initShader) {
+ initShader = false
+ setShader(SweepGradient(progressRectF.centerX(), progressRectF.centerY(), shaderColors, shaderPositions))
+ } else if (sizeChanged) {
+ sizeChanged = false
+ setShader(SweepGradient(progressRectF.centerX(), progressRectF.centerY(), shaderColors, shaderPositions))
+ }
+ for (i in valuesToDrawList.indices) {
+ if (!multipleArcsEnabled) {
+ //No background will be used when "multiple-arc-progress" is enable because it will be mixed with the "progress-colors"
+ canvas.drawOval(progressRectF, backgroundPaint)
+ }
+ angle = 360 * valuesToDrawList[i] / max
+ if (reverseEnabled) {
+ angle *= -1f
+ }
+ val offset: Float = if (!reverseEnabled && multipleArcsEnabled) ANGLE_OFFSET_FOR_MULTIPLE_ARC_PROGRESS else 0f //to better glue all the "pieces"
+ canvas.drawArc(progressRectF, previousAngle - offset, angle + offset, false, progressPaintList[i])
+ if (!multipleArcsEnabled && isProgressThumbEnabled) {
+ //Only in "single-arc-progress", otherwise we'll end up with N thumbs
+ endX = cos(Math.toRadians(previousAngle + angle.toDouble())) * radius
+ endY = sin(Math.toRadians(previousAngle + angle.toDouble())) * radius
+ thumbPaint.set(progressPaintList[i]) // stroke style copy
+ when (progressThumbScaleType) {
+ POINT, RATE -> thumbPaint.style = Paint.Style.FILL
+ else -> thumbPaint.style = Paint.Style.STROKE
+ }
+ canvas.drawCircle(endX.toFloat() + progressRectF.centerX(), endY.toFloat() + progressRectF.centerY(), thumbSize, thumbPaint)
+ }
+ previousAngle += angle
+ }
+ }
+
+ private fun dpToPx(dp: Float) = ceil(dp * Resources.getSystem().displayMetrics.density.toDouble()).toInt()
+
+ companion object {
+ private const val ANGLE_OFFSET_FOR_MULTIPLE_ARC_PROGRESS = 6f
+ private const val DEFAULT_VIEW_PADDING_DP = 10f
+ private const val DEFAULT_SHADOW_PADDING_DP = 5f
+ private const val DEFAULT_STROKE_THICKNESS_DP = 10f
+ private const val DEFAULT_THUMB_SIZE_DP = 10f
+ private const val DEFAULT_MAXIMUM_THUMB_SIZE_RATE = 2f
+ private const val DEFAULT_MAX_WIDTH_DP = 100
+ private const val DEFAULT_MAX = 100
+ private const val DEFAULT_STARTING_ANGLE = 270
+ private const val DEFAULT_ANIMATION_MILLIS = 1000
+ private const val DEFAULT_PROGRESS_COLOR = Color.BLACK
+ private const val DEFAULT_BACKGROUND_ALPHA = 0.3f
+ private val DEFAULT_INTERPOLATOR: TimeInterpolator = DecelerateInterpolator()
+ }
+}
+
+enum class ProgressThumbScaleType { AUTO, POINT, RATE }
+
+@Suppress("unused")
+inline fun CircularProgressView.addActionListener(
+ crossinline onProgressChanged: (progress: Float) -> Unit = { _ -> },
+ crossinline onAnimationFinished: (progress: Float) -> Unit = { _ -> },
+): CircularProgressView.CircularProgressViewActionCallback {
+ val callback = object : CircularProgressView.CircularProgressViewActionCallback {
+ override fun onProgressChanged(progress: Float) {
+ onProgressChanged.invoke(progress)
+ }
+
+ override fun onAnimationFinished(progress: Float) {
+ onAnimationFinished.invoke(progress)
+ }
+ }
+ actionCallback = callback
+ return callback
+}
\ No newline at end of file
diff --git a/circular-progress-view/src/main/java/com/github/guilhe/views/ProgressThumbScaleType.java b/circular-progress-view/src/main/java/com/github/guilhe/views/ProgressThumbScaleType.java
deleted file mode 100644
index 9e44812..0000000
--- a/circular-progress-view/src/main/java/com/github/guilhe/views/ProgressThumbScaleType.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.github.guilhe.views;
-
-public enum ProgressThumbScaleType {
- AUTO, POINT, RATE
-}
diff --git a/circular-progress-view/src/main/res/values/attrs.xml b/circular-progress-view/src/main/res/values/attrs.xml
index abb07fd..34399df 100644
--- a/circular-progress-view/src/main/res/values/attrs.xml
+++ b/circular-progress-view/src/main/res/values/attrs.xml
@@ -14,8 +14,8 @@
-
-
+
+
@@ -39,8 +39,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index b03cfb1..ddca840 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -23,7 +23,7 @@ bintrayName = circular-progress-view
libraryName = CircularProgressView
libraryDescription = A fancy CircularProgressView
-libraryVersion = 1.4.2
+libraryVersion = 2.0.0
siteUrl = https://github.com/GuilhE/android-circular-progress-view
gitUrl = https://github.com/GuilhE/android-circular-progress-view.git
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index c56a31a..11f57fd 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Apr 24 22:10:51 WEST 2020
+#Mon Aug 24 13:26:38 WEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/.imgs/banner.png b/media/banner.png
similarity index 100%
rename from .imgs/banner.png
rename to media/banner.png
diff --git a/.imgs/piechart.png b/media/piechart.png
similarity index 100%
rename from .imgs/piechart.png
rename to media/piechart.png
diff --git a/.imgs/rainbow.png b/media/rainbow.png
similarity index 100%
rename from .imgs/rainbow.png
rename to media/rainbow.png
diff --git a/.imgs/sample.gif b/media/sample.gif
similarity index 100%
rename from .imgs/sample.gif
rename to media/sample.gif
diff --git a/sample/build.gradle b/sample/build.gradle
index 1ca3581..876bd5e 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -1,4 +1,6 @@
apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -8,8 +10,8 @@ android {
applicationId "com.github.guilhe.cicularprogressview.sample"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 7
- versionName "1.4.1"
+ versionCode 8
+ versionName "2.0.0"
}
dataBinding {
@@ -37,5 +39,9 @@ dependencies {
implementation project(':circular-progress-view')
implementation "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
implementation "com.android.support:design:$rootProject.supportLibraryVersion"
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+ implementation 'com.android.support.constraint:constraint-layout:2.0.0'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
}
\ No newline at end of file
diff --git a/sample/src/main/java/sample/SampleActivity.java b/sample/src/main/java/sample/SampleActivity.java
index 6ac876d..9ae8640 100644
--- a/sample/src/main/java/sample/SampleActivity.java
+++ b/sample/src/main/java/sample/SampleActivity.java
@@ -18,76 +18,72 @@
import java.util.List;
import java.util.Random;
-/**
- * Created by gdelgado on 30/08/2017.
- */
-
public class SampleActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener, RadioGroup.OnCheckedChangeListener {
- private ActivitySampleEditorBinding mBinding;
- private boolean mTransparent;
- private Toast mToast;
+ private ActivitySampleEditorBinding binding;
+ private boolean transparent;
+ private Toast toast;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mBinding = DataBindingUtil.setContentView(this, R.layout.activity_sample_editor);
-
- mBinding.sizeSeekBar.setOnSeekBarChangeListener(this);
- mBinding.thicknessSeekBar.setOnSeekBarChangeListener(this);
- mBinding.thumbsizeSeekBar.setOnSeekBarChangeListener(this);
- mBinding.progressSeekBar.setOnSeekBarChangeListener(this);
- mBinding.angleSeekBar.setOnSeekBarChangeListener(this);
- mBinding.colorRSeekBar.setOnSeekBarChangeListener(this);
- mBinding.colorGSeekBar.setOnSeekBarChangeListener(this);
- mBinding.colorBSeekBar.setOnSeekBarChangeListener(this);
- mBinding.bgRSeekBar.setOnSeekBarChangeListener(this);
- mBinding.bgGSeekBar.setOnSeekBarChangeListener(this);
- mBinding.bgBSeekBar.setOnSeekBarChangeListener(this);
- mBinding.thumbScaleGroup.setOnCheckedChangeListener(this);
-
- mBinding.roundedSwitch.setOnCheckedChangeListener((compoundButton, checked) -> mBinding.sampleCircularProgressView.setProgressRounded(checked));
- mBinding.shadowSwitch.setOnCheckedChangeListener((compoundButton, checked) -> mBinding.sampleCircularProgressView.setShadowEnabled(checked));
- mBinding.thumbSwitch.setOnCheckedChangeListener((compoundButton, checked) -> {
- mBinding.sampleCircularProgressView.setProgressThumbEnabled(checked);
- mBinding.thumbScaleAuto.setEnabled(checked);
- mBinding.thumbScalePoint.setEnabled(checked);
- mBinding.thumbScaleRate.setEnabled(checked);
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_sample_editor);
+
+ binding.sizeSeekBar.setOnSeekBarChangeListener(this);
+ binding.thicknessSeekBar.setOnSeekBarChangeListener(this);
+ binding.thumbsizeSeekBar.setOnSeekBarChangeListener(this);
+ binding.progressSeekBar.setOnSeekBarChangeListener(this);
+ binding.angleSeekBar.setOnSeekBarChangeListener(this);
+ binding.colorRSeekBar.setOnSeekBarChangeListener(this);
+ binding.colorGSeekBar.setOnSeekBarChangeListener(this);
+ binding.colorBSeekBar.setOnSeekBarChangeListener(this);
+ binding.bgRSeekBar.setOnSeekBarChangeListener(this);
+ binding.bgGSeekBar.setOnSeekBarChangeListener(this);
+ binding.bgBSeekBar.setOnSeekBarChangeListener(this);
+ binding.thumbScaleGroup.setOnCheckedChangeListener(this);
+
+ binding.roundedSwitch.setOnCheckedChangeListener((compoundButton, checked) -> binding.sampleCircularProgressView.setProgressRounded(checked));
+ binding.shadowSwitch.setOnCheckedChangeListener((compoundButton, checked) -> binding.sampleCircularProgressView.setShadowEnabled(checked));
+ binding.thumbSwitch.setOnCheckedChangeListener((compoundButton, checked) -> {
+ binding.sampleCircularProgressView.setProgressThumbEnabled(checked);
+ binding.thumbScaleAuto.setEnabled(checked);
+ binding.thumbScalePoint.setEnabled(checked);
+ binding.thumbScaleRate.setEnabled(checked);
});
- mBinding.reverseSwitch.setOnCheckedChangeListener((compoundButton, checked) -> mBinding.sampleCircularProgressView.setReverseEnabled(checked));
- mBinding.alphaSwitch.setOnCheckedChangeListener(((compoundButton, checked) -> mBinding.sampleCircularProgressView.setBackgroundAlphaEnabled(checked)));
- mBinding.colorsSwitch.setOnCheckedChangeListener((compoundButton, checked) -> {
- if (!mTransparent) {
- mBinding.sampleCircularProgressView.setBackgroundColor(mBinding.sampleCircularProgressView.getProgressColor());
+ binding.reverseSwitch.setOnCheckedChangeListener((compoundButton, checked) -> binding.sampleCircularProgressView.setReverseEnabled(checked));
+ binding.alphaSwitch.setOnCheckedChangeListener(((compoundButton, checked) -> binding.sampleCircularProgressView.setBackgroundAlphaEnabled(checked)));
+ binding.colorsSwitch.setOnCheckedChangeListener((compoundButton, checked) -> {
+ if (!transparent) {
+ binding.sampleCircularProgressView.setProgressBackgroundColor(binding.sampleCircularProgressView.getProgressColor());
}
});
- mBinding.transparentSwitch.setOnCheckedChangeListener((compoundButton, checked) -> {
- mTransparent = checked;
- mBinding.sampleCircularProgressView.setBackgroundColor(Color.TRANSPARENT);
+ binding.transparentSwitch.setOnCheckedChangeListener((compoundButton, checked) -> {
+ transparent = checked;
+ binding.sampleCircularProgressView.setProgressBackgroundColor(Color.TRANSPARENT);
});
- mBinding.sampleCircularProgressView.setProgressAnimationCallback(new CircularProgressView.OnProgressChangeAnimationCallback() {
+ binding.sampleCircularProgressView.setActionCallback(new CircularProgressView.CircularProgressViewActionCallback() {
@Override
public void onProgressChanged(float progress) {
}
@Override
public void onAnimationFinished(float progress) {
- if (mToast != null) {
- mToast.cancel(); //Prevent toasts from overlapping.
+ if (toast != null) {
+ toast.cancel(); //Prevent toasts from overlapping.
}
- mToast = Toast.makeText(SampleActivity.this, String.valueOf(progress) + "%", Toast.LENGTH_SHORT);
- mToast.show();
+ toast = Toast.makeText(SampleActivity.this, progress + "%", Toast.LENGTH_SHORT);
+ toast.show();
}
});
- mBinding.sampleFloatingActionButton.setOnClickListener(v -> {
+ binding.sampleFloatingActionButton.setOnClickListener(v -> {
List values = new ArrayList() {{
add(12.5f);
// add(12.5f);
add(25f);
add(50f);
}};
- mBinding.sampleCircularProgressView.setProgress(values, new ArrayList() {{
+ binding.sampleCircularProgressView.setProgress(values, new ArrayList() {{
for (Float ignored : values) {
add(Color.rgb(new Random().nextInt(), new Random().nextInt(), new Random().nextInt()));
}
@@ -99,68 +95,68 @@ public void onAnimationFinished(float progress) {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
switch (seekBar.getId()) {
case R.id.size_SeekBar:
- mBinding.sampleCircularProgressView.setSize((int) (progress * getResources().getDisplayMetrics().density + 0.5f));
+ binding.sampleCircularProgressView.setSize((int) (progress * getResources().getDisplayMetrics().density + 0.5f));
break;
case R.id.thickness_SeekBar:
- mBinding.sampleCircularProgressView.setProgressStrokeThickness((int) (progress * getResources().getDisplayMetrics().density + 0.5f));
+ binding.sampleCircularProgressView.setProgressStrokeThickness((int) (progress * getResources().getDisplayMetrics().density + 0.5f));
break;
case R.id.thumbsize_SeekBar:
float size;
- if (mBinding.sampleCircularProgressView.getProgressThumbScaleType() == ProgressThumbScaleType.RATE) {
+ if (binding.sampleCircularProgressView.getProgressThumbScaleType() == ProgressThumbScaleType.RATE) {
size = progress / ((float) seekBar.getMax() / 2);
- mBinding.sampleCircularProgressView.setProgressThumbSizeRate(size);
+ binding.sampleCircularProgressView.setProgressMaxThumbSizeRate(size);
} else {
size = (progress * getResources().getDisplayMetrics().density + 0.5f);
- mBinding.sampleCircularProgressView.setProgressThumbSize(size);
+ binding.sampleCircularProgressView.setProgressThumbSize(size);
}
break;
case R.id.progress_SeekBar:
- mBinding.sampleCircularProgressView.setProgress(progress, mBinding.animatedSwitch.isChecked());
+ binding.sampleCircularProgressView.setProgress(progress, binding.animatedSwitch.isChecked());
break;
case R.id.angle_SeekBar:
- mBinding.sampleCircularProgressView.setStartingAngle(progress);
+ binding.sampleCircularProgressView.setStartingAngle(progress);
break;
case R.id.color_r_SeekBar:
- if (mBinding.colorsSwitch.isChecked()) {
- mBinding.bgRSeekBar.setProgress(progress);
+ if (binding.colorsSwitch.isChecked()) {
+ binding.bgRSeekBar.setProgress(progress);
}
- mBinding.sampleCircularProgressView.setProgressColor(Color.rgb(progress, mBinding.colorGSeekBar.getProgress(), mBinding.colorBSeekBar.getProgress()));
+ binding.sampleCircularProgressView.setProgressColor(Color.rgb(progress, binding.colorGSeekBar.getProgress(), binding.colorBSeekBar.getProgress()));
break;
case R.id.color_g_SeekBar:
- if (mBinding.colorsSwitch.isChecked()) {
- mBinding.bgGSeekBar.setProgress(progress);
+ if (binding.colorsSwitch.isChecked()) {
+ binding.bgGSeekBar.setProgress(progress);
}
- mBinding.sampleCircularProgressView.setProgressColor(Color.rgb(mBinding.colorRSeekBar.getProgress(), progress, mBinding.colorBSeekBar.getProgress()));
+ binding.sampleCircularProgressView.setProgressColor(Color.rgb(binding.colorRSeekBar.getProgress(), progress, binding.colorBSeekBar.getProgress()));
break;
case R.id.color_b_SeekBar:
- if (mBinding.colorsSwitch.isChecked()) {
- mBinding.bgBSeekBar.setProgress(progress);
+ if (binding.colorsSwitch.isChecked()) {
+ binding.bgBSeekBar.setProgress(progress);
}
- mBinding.sampleCircularProgressView.setProgressColor(Color.rgb(mBinding.colorRSeekBar.getProgress(), mBinding.colorGSeekBar.getProgress(), progress));
+ binding.sampleCircularProgressView.setProgressColor(Color.rgb(binding.colorRSeekBar.getProgress(), binding.colorGSeekBar.getProgress(), progress));
break;
case R.id.bg_r_SeekBar:
- if (mBinding.colorsSwitch.isChecked()) {
- mBinding.colorRSeekBar.setProgress(progress);
+ if (binding.colorsSwitch.isChecked()) {
+ binding.colorRSeekBar.setProgress(progress);
}
- if (!mTransparent) {
- mBinding.sampleCircularProgressView.setBackgroundColor(Color.rgb(progress, mBinding.bgGSeekBar.getProgress(), mBinding.bgBSeekBar.getProgress()));
+ if (!transparent) {
+ binding.sampleCircularProgressView.setProgressBackgroundColor(Color.rgb(progress, binding.bgGSeekBar.getProgress(), binding.bgBSeekBar.getProgress()));
}
break;
case R.id.bg_g_SeekBar:
- if (mBinding.colorsSwitch.isChecked()) {
- mBinding.colorGSeekBar.setProgress(progress);
+ if (binding.colorsSwitch.isChecked()) {
+ binding.colorGSeekBar.setProgress(progress);
}
- if (!mTransparent) {
- mBinding.sampleCircularProgressView.setBackgroundColor(Color.rgb(mBinding.bgRSeekBar.getProgress(), progress, mBinding.bgBSeekBar.getProgress()));
+ if (!transparent) {
+ binding.sampleCircularProgressView.setProgressBackgroundColor(Color.rgb(binding.bgRSeekBar.getProgress(), progress, binding.bgBSeekBar.getProgress()));
}
break;
case R.id.bg_b_SeekBar:
- if (mBinding.colorsSwitch.isChecked()) {
- mBinding.colorBSeekBar.setProgress(progress);
+ if (binding.colorsSwitch.isChecked()) {
+ binding.colorBSeekBar.setProgress(progress);
}
- if (!mTransparent) {
- mBinding.sampleCircularProgressView.setBackgroundColor(Color.rgb(mBinding.bgRSeekBar.getProgress(), mBinding.bgGSeekBar.getProgress(), progress));
+ if (!transparent) {
+ binding.sampleCircularProgressView.setProgressBackgroundColor(Color.rgb(binding.bgRSeekBar.getProgress(), binding.bgGSeekBar.getProgress(), progress));
}
break;
}
@@ -178,18 +174,18 @@ public void onStopTrackingTouch(SeekBar seekBar) {
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.thumb_scale_point:
- mBinding.sampleCircularProgressView.setProgressThumbScaleType(ProgressThumbScaleType.POINT);
- mBinding.sampleCircularProgressView.setProgressThumbSize((mBinding.thumbsizeSeekBar.getProgress() * getResources().getDisplayMetrics().density + 0.5f));
+ binding.sampleCircularProgressView.setProgressThumbScaleType(ProgressThumbScaleType.POINT);
+ binding.sampleCircularProgressView.setProgressThumbSize((binding.thumbsizeSeekBar.getProgress() * getResources().getDisplayMetrics().density + 0.5f));
break;
case R.id.thumb_scale_rate:
- mBinding.sampleCircularProgressView.setProgressThumbScaleType(ProgressThumbScaleType.RATE);
- float rate = (mBinding.thumbsizeSeekBar.getProgress() / (mBinding.thumbsizeSeekBar.getMax() / mBinding.sampleCircularProgressView.getProgressMaxThumbSizeRate()));
- mBinding.sampleCircularProgressView.setProgressThumbSizeRate(rate);
+ binding.sampleCircularProgressView.setProgressThumbScaleType(ProgressThumbScaleType.RATE);
+ float rate = (binding.thumbsizeSeekBar.getProgress() / (binding.thumbsizeSeekBar.getMax() / binding.sampleCircularProgressView.getProgressMaxThumbSizeRate()));
+ binding.sampleCircularProgressView.setProgressMaxThumbSizeRate(rate);
break;
case R.id.thumb_scale_auto:
default:
- mBinding.sampleCircularProgressView.setProgressThumbScaleType(ProgressThumbScaleType.AUTO);
+ binding.sampleCircularProgressView.setProgressThumbScaleType(ProgressThumbScaleType.AUTO);
}
- mBinding.sampleCircularProgressView.requestLayout();
+ binding.sampleCircularProgressView.requestLayout();
}
}
\ No newline at end of file
diff --git a/sample/src/main/res/layout/activity_sample_grid.xml b/sample/src/main/res/layout/activity_sample_grid.xml
index 4c03204..fd55b06 100644
--- a/sample/src/main/res/layout/activity_sample_grid.xml
+++ b/sample/src/main/res/layout/activity_sample_grid.xml
@@ -179,7 +179,7 @@
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
- app:backgroundColor="@android:color/transparent"
+ app:progressBackgroundColor="@android:color/transparent"
app:layout_constraintEnd_toStartOf="@+id/circularProgressView15"
app:layout_constraintStart_toEndOf="@+id/circularProgressView13"
app:layout_constraintTop_toBottomOf="@+id/circularProgressView11"
@@ -193,7 +193,7 @@
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
- app:backgroundColor="@android:color/transparent"
+ app:progressBackgroundColor="@android:color/transparent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/circularProgressView14"
app:layout_constraintTop_toBottomOf="@+id/circularProgressView12"