diff --git a/Range-slider-ios.gif b/Range-slider-ios.gif
new file mode 100644
index 0000000..8a72cab
Binary files /dev/null and b/Range-slider-ios.gif differ
diff --git a/android/build.gradle b/android/build.gradle
index 0cc9ba5..4eb608c 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -9,6 +9,8 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+ classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
// noinspection DifferentKotlinGradleVersion
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
@@ -16,6 +18,7 @@ buildscript {
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
+apply plugin: 'com.github.dcendents.android-maven'
def getExtOrDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeRangeSlider_' + name]
@@ -127,5 +130,6 @@ dependencies {
// noinspection GradleDynamicVersion
api 'com.facebook.react:react-native:+'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- compile 'com.crystal:crystalrangeseekbar:1.1.3'
+ compile project(':rangeseekbar')
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}
diff --git a/android/crystalrangeseekbar/.gitignore b/android/crystalrangeseekbar/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/android/crystalrangeseekbar/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/android/crystalrangeseekbar/build.gradle b/android/crystalrangeseekbar/build.gradle
new file mode 100644
index 0000000..3722e15
--- /dev/null
+++ b/android/crystalrangeseekbar/build.gradle
@@ -0,0 +1,59 @@
+apply plugin: 'com.android.library'
+
+ext {
+ // Where you will see your artifact in Bintray's web interface
+ // The "bintrayName" should match the name of the Bintray repro.
+ bintrayRepo = 'maven'
+ bintrayName = 'crystalrangeseekbar'
+
+ // Maven metadata
+ publishedGroupId = 'com.crystal'
+ libraryName = 'CrystalRangeSeekbar'
+ // Save yourself a head ache, and set this equal to the name of the Android Studio library
+ // module. The artifact name needs to match the name of the library.
+ artifact = 'crystalrangeseekbar'
+
+ libraryDescription = 'An extended version of android range seekbar.'
+
+ gitUrl = 'https://github.com/syedowaisali/crystal-range-seekbar.git'
+
+ libraryVersion = '1.1.4'
+
+ developerId = 'syedowaisali'
+ developerName = 'Syed Owais Ali'
+ developerEmail = 'dp.owaisali@gmail.com'
+
+ licenseName = 'The Apache Software License, Version 2.0'
+ licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+ allLicenses = ["Apache-2.0"]
+}
+
+android {
+ compileSdkVersion 27
+ buildToolsVersion '27.0.3'
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:27.0.2'
+}
+
+repositories {
+ google()
+}
+
+apply from: 'https://raw.githubusercontent.com/attwellBrian/JCenter/master/installv1.gradle'
+apply from: 'https://raw.githubusercontent.com/attwellBrian/JCenter/master/bintrayv1.gradle'
diff --git a/android/crystalrangeseekbar/crystalrangeseekbar-rangeseekbar.iml b/android/crystalrangeseekbar/crystalrangeseekbar-rangeseekbar.iml
new file mode 100644
index 0000000..084762a
--- /dev/null
+++ b/android/crystalrangeseekbar/crystalrangeseekbar-rangeseekbar.iml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/crystalrangeseekbar/proguard-rules.pro b/android/crystalrangeseekbar/proguard-rules.pro
new file mode 100644
index 0000000..01eea0a
--- /dev/null
+++ b/android/crystalrangeseekbar/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in E:\Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/android/crystalrangeseekbar/rangeseekbar.iml b/android/crystalrangeseekbar/rangeseekbar.iml
new file mode 100644
index 0000000..4e846e8
--- /dev/null
+++ b/android/crystalrangeseekbar/rangeseekbar.iml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/crystalrangeseekbar/src/androidTest/java/com/example/crystalrangeseekbar/ApplicationTest.java b/android/crystalrangeseekbar/src/androidTest/java/com/example/crystalrangeseekbar/ApplicationTest.java
new file mode 100644
index 0000000..bd51c74
--- /dev/null
+++ b/android/crystalrangeseekbar/src/androidTest/java/com/example/crystalrangeseekbar/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.example.crystalrangeseekbar;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/android/crystalrangeseekbar/src/main/AndroidManifest.xml b/android/crystalrangeseekbar/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c877a02
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnRangeSeekbarChangeListener.java b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnRangeSeekbarChangeListener.java
new file mode 100644
index 0000000..f03d22b
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnRangeSeekbarChangeListener.java
@@ -0,0 +1,8 @@
+package com.crystal.crystalrangeseekbar.interfaces;
+
+/**
+ * Created by owais.ali on 7/14/2016.
+ */
+public interface OnRangeSeekbarChangeListener {
+ void valueChanged(Number minValue, Number maxValue);
+}
diff --git a/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnRangeSeekbarFinalValueListener.java b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnRangeSeekbarFinalValueListener.java
new file mode 100644
index 0000000..a22e850
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnRangeSeekbarFinalValueListener.java
@@ -0,0 +1,8 @@
+package com.crystal.crystalrangeseekbar.interfaces;
+
+/**
+ * Created by owais.ali on 7/14/2016.
+ */
+public interface OnRangeSeekbarFinalValueListener {
+ void finalValue(Number minValue, Number maxValue);
+}
diff --git a/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnSeekbarChangeListener.java b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnSeekbarChangeListener.java
new file mode 100644
index 0000000..8ac23f8
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnSeekbarChangeListener.java
@@ -0,0 +1,8 @@
+package com.crystal.crystalrangeseekbar.interfaces;
+
+/**
+ * Created by owais.ali on 7/14/2016.
+ */
+public interface OnSeekbarChangeListener {
+ void valueChanged(Number value);
+}
diff --git a/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnSeekbarFinalValueListener.java b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnSeekbarFinalValueListener.java
new file mode 100644
index 0000000..b598fce
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/interfaces/OnSeekbarFinalValueListener.java
@@ -0,0 +1,8 @@
+package com.crystal.crystalrangeseekbar.interfaces;
+
+/**
+ * Created by owais.ali on 7/14/2016.
+ */
+public interface OnSeekbarFinalValueListener {
+ void finalValue(Number value);
+}
diff --git a/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/BubbleThumbRangeSeekbar.java b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/BubbleThumbRangeSeekbar.java
new file mode 100644
index 0000000..56c1098
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/BubbleThumbRangeSeekbar.java
@@ -0,0 +1,352 @@
+package com.crystal.crystalrangeseekbar.widgets;
+
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.animation.OvershootInterpolator;
+
+import com.example.crystalrangeseekbar.R;
+
+/**
+ * Created by owais.ali on 7/12/2016.
+ */
+public class BubbleThumbRangeSeekbar extends CrystalRangeSeekbar {
+
+ //////////////////////////////////////////
+ // PRIVATE CONSTANTS
+ //////////////////////////////////////////
+
+ //private static final float BUBBLE_WITH = 200f;
+ //private static final float BUBBLE_HEIGHT = 200f;
+
+ //////////////////////////////////////////
+ // PRIVATE VAR
+ //////////////////////////////////////////
+
+ private boolean animate;
+ private boolean isPressedLeftThumb;
+ private boolean isPressedRightThumb;
+ private BubbleRect thumbPressedRect;
+
+ //////////////////////////////////////////
+ // CONSTRUCTOR
+ //////////////////////////////////////////
+
+ public BubbleThumbRangeSeekbar(Context context) {
+ super(context);
+ }
+
+ public BubbleThumbRangeSeekbar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public BubbleThumbRangeSeekbar(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ //////////////////////////////////////////
+ // INITIALIZATION
+ //////////////////////////////////////////
+
+ @Override
+ protected void init(){
+ thumbPressedRect = new BubbleRect();
+ super.init();
+ }
+
+ //////////////////////////////////////////
+ // OVERRIDE METHODS
+ //////////////////////////////////////////
+
+ @Override
+ protected void touchDown(float x, float y) {
+ super.touchDown(x, y);
+
+ animate = true;
+ if(Thumb.MIN.equals(getPressedThumb())){
+ isPressedLeftThumb = true;
+ startAnimationUp(Thumb.MIN);
+ }
+ else if(Thumb.MAX.equals(getPressedThumb())){
+ isPressedRightThumb = true;
+ startAnimationUp(Thumb.MAX);
+ }
+ }
+
+ @Override
+ protected void touchUp(float x, float y) {
+ super.touchUp(x, y);
+
+ animate = true;
+ if(Thumb.MIN.equals(getPressedThumb())){
+ startAnimationDown(Thumb.MIN);
+ }
+ else{
+ startAnimationDown(Thumb.MAX);
+ }
+ }
+
+ @Override
+ protected void drawLeftThumbWithColor(Canvas canvas, Paint paint, RectF rect) {
+ if(isPressedLeftThumb){
+
+ if(! animate){
+ rect.left = rect.left - ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ rect.right = rect.left + getBubbleWith();
+ rect.top = getLeftThumbRect().top - ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ rect.bottom = getLeftThumbRect().bottom + ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ }
+ else{
+
+ rect.left = thumbPressedRect.left;
+ rect.right = thumbPressedRect.right;
+ rect.top = thumbPressedRect.top;
+ rect.bottom = thumbPressedRect.bottom;
+ }
+
+ canvas.drawOval(rect, paint);
+ }
+ else {
+ canvas.drawOval(rect, paint);
+ }
+ }
+
+ @Override
+ protected void drawRightThumbWithColor(Canvas canvas, Paint paint, RectF rect) {
+ if(isPressedRightThumb){
+
+ if(! animate){
+ rect.left = rect.left - ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ rect.right = rect.left + getBubbleWith();
+ rect.top = getRightThumbRect().top - ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ rect.bottom = getRightThumbRect().bottom + ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ }
+ else{
+
+ rect.left = thumbPressedRect.left;
+ rect.right = thumbPressedRect.right;
+ rect.top = thumbPressedRect.top;
+ rect.bottom = thumbPressedRect.bottom;
+ }
+
+ canvas.drawOval(rect, paint);
+ }
+ else {
+ canvas.drawOval(rect, paint);
+ }
+ }
+
+ @Override
+ protected void drawLeftThumbWithImage(Canvas canvas, Paint paint, RectF rect, Bitmap image) {
+ if(isPressedLeftThumb){
+
+ if(! animate){
+ image = resizeImage((int) getBubbleWith(), (int) getBubbleHeight(), image);
+ rect.top = getLeftThumbRect().top - ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ rect.left = rect.left - ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ }
+ else{
+ image = resizeImage((int) thumbPressedRect.imageWith, (int) thumbPressedRect.imageHeight, image);
+ rect.top = thumbPressedRect.top;
+ rect.left = thumbPressedRect.left;
+ }
+
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+ else{
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+ }
+
+ @Override
+ protected void drawRightThumbWithImage(Canvas canvas, Paint paint, RectF rect, Bitmap image) {
+ if(isPressedRightThumb){
+
+ if(! animate){
+ image = resizeImage((int) getBubbleWith(), (int) getBubbleHeight(), image);
+ rect.top = getRightThumbRect().top - ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ rect.left = rect.left - ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ }
+ else{
+ image = resizeImage((int) thumbPressedRect.imageWith, (int) thumbPressedRect.imageHeight, image);
+ rect.top = thumbPressedRect.top;
+ rect.left = thumbPressedRect.left;
+ }
+
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+ else{
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+ }
+
+ //////////////////////////////////////////
+ // PROTECTED METHODS
+ //////////////////////////////////////////
+
+ protected float getBubbleWith(){
+ return getResources().getDimension(R.dimen.bubble_thumb_width);
+ }
+
+ protected float getBubbleHeight(){
+ return getResources().getDimension(R.dimen.bubble_thumb_height);
+ }
+
+ protected void startAnimationUp(final Thumb thumb){
+
+ BubbleRect toRect = new BubbleRect();
+ RectF fromRect;
+
+ // left thumb
+ if(Thumb.MIN.equals(thumb)){
+ fromRect = getLeftThumbRect();
+ }
+ else{
+ fromRect = getRightThumbRect();
+ }
+
+ toRect.left = fromRect.left - ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ toRect.right = toRect.left + getBubbleWith();
+ toRect.top = fromRect.top - ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ toRect.bottom = fromRect.bottom + ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+
+ PropertyValuesHolder leftValueHolder = PropertyValuesHolder.ofFloat("left", fromRect.left, toRect.left);
+ PropertyValuesHolder rightValueHolder = PropertyValuesHolder.ofFloat("right", fromRect.right, toRect.right);
+ PropertyValuesHolder topValueHolder = PropertyValuesHolder.ofFloat("top", fromRect.top, toRect.top);
+ PropertyValuesHolder bottomValueHolder = PropertyValuesHolder.ofFloat("bottom", fromRect.bottom, toRect.bottom);
+ PropertyValuesHolder imageWithValueHolder = PropertyValuesHolder.ofFloat("width", getThumbWidth(), getBubbleWith());
+ PropertyValuesHolder imageHeightValueHolder = PropertyValuesHolder.ofFloat("height", getThumbHeight(), getBubbleHeight());
+
+ ValueAnimator animation = ValueAnimator.ofPropertyValuesHolder(leftValueHolder, rightValueHolder, topValueHolder, bottomValueHolder, imageWithValueHolder, imageHeightValueHolder);
+ animation.setDuration(200);
+ animation.setInterpolator(new OvershootInterpolator(5f));
+ animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ thumbPressedRect.left = (float)animation.getAnimatedValue("left");
+ thumbPressedRect.right = (float)animation.getAnimatedValue("right");
+ thumbPressedRect.top = (float)animation.getAnimatedValue("top");
+ thumbPressedRect.bottom = (float)animation.getAnimatedValue("bottom");
+ thumbPressedRect.imageWith = (float)animation.getAnimatedValue("width");
+ thumbPressedRect.imageHeight = (float)animation.getAnimatedValue("height");
+ invalidate();
+ }
+ });
+ animation.start();
+
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ animate = false;
+ }
+ }, 200);
+ }
+
+ protected void startAnimationDown(Thumb thumb){
+
+ RectF toRect = new RectF();
+ RectF fromRect;
+
+ // left thumb
+ if(Thumb.MIN.equals(thumb)){
+ fromRect = getLeftThumbRect();
+ }
+ else{
+ fromRect = getRightThumbRect();
+ }
+
+ toRect.left = fromRect.left + ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ toRect.right = toRect.left + getThumbWidth();
+ toRect.top = 0f;
+ toRect.bottom = getThumbHeight();
+
+ PropertyValuesHolder leftValueHolder = PropertyValuesHolder.ofFloat("left", fromRect.left, toRect.left);
+ PropertyValuesHolder rightValueHolder = PropertyValuesHolder.ofFloat("right", fromRect.right, toRect.right);
+ PropertyValuesHolder topValueHolder = PropertyValuesHolder.ofFloat("top", fromRect.top, toRect.top);
+ PropertyValuesHolder bottomValueHolder = PropertyValuesHolder.ofFloat("bottom", fromRect.bottom, toRect.bottom);
+ PropertyValuesHolder imageWithValueHolder = PropertyValuesHolder.ofFloat("width", getBubbleWith(), getThumbWidth());
+ PropertyValuesHolder imageHeightValueHolder = PropertyValuesHolder.ofFloat("height", getBubbleHeight(), getThumbHeight());
+
+ ValueAnimator animation = ValueAnimator.ofPropertyValuesHolder(leftValueHolder, rightValueHolder, topValueHolder, bottomValueHolder, imageWithValueHolder, imageHeightValueHolder);
+ animation.setDuration(300);
+ animation.setInterpolator(new OvershootInterpolator(3f));
+ animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ thumbPressedRect.left = (float)animation.getAnimatedValue("left");
+ thumbPressedRect.right = (float)animation.getAnimatedValue("right");
+ thumbPressedRect.top = (float)animation.getAnimatedValue("top");
+ thumbPressedRect.bottom = (float)animation.getAnimatedValue("bottom");
+ thumbPressedRect.imageWith = (float)animation.getAnimatedValue("width");
+ thumbPressedRect.imageHeight = (float)animation.getAnimatedValue("height");
+ invalidate();
+ }
+ });
+ animation.start();
+
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ animate = false;
+ isPressedLeftThumb = false;
+ isPressedRightThumb = false;
+ }
+ }, 300);
+ }
+
+ //////////////////////////////////////////
+ // PUBLIC METHODS
+ //////////////////////////////////////////
+
+
+ //////////////////////////////////////////
+ // PRIVATE METHODS
+ //////////////////////////////////////////
+
+ private Bitmap resizeImage( int iconWidth, int iconHeight, Bitmap bmp) {
+
+
+ int width = bmp.getWidth();
+ int height = bmp.getHeight();
+
+ // calculate the scale
+ float scaleWidth = ((float) iconWidth) / width;
+ float scaleHeight = ((float) iconHeight) / height;
+
+ // create a matrix for the manipulation
+ Matrix matrix = new Matrix();
+ // resize the Bitmap
+ matrix.postScale(scaleWidth, scaleHeight);
+
+ // if you want to rotate the Bitmap
+ // matrix.postRotate(45);
+
+ // recreate the new Bitmap
+
+ // make a Drawable from Bitmap to allow to set the Bitmap
+ // to the ImageView, ImageButton or what ever
+ return Bitmap.createBitmap(bmp, 0, 0, width, height, matrix, true);
+
+ }
+
+ //////////////////////////////////////////
+ // PRIVATE CLASS
+ //////////////////////////////////////////
+
+ private class BubbleRect{
+ public float left;
+ public float right;
+ public float top;
+ public float bottom;
+ public float imageWith;
+ public float imageHeight;
+ }
+
+}
diff --git a/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/BubbleThumbSeekbar.java b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/BubbleThumbSeekbar.java
new file mode 100644
index 0000000..012e090
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/BubbleThumbSeekbar.java
@@ -0,0 +1,280 @@
+package com.crystal.crystalrangeseekbar.widgets;
+
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.animation.OvershootInterpolator;
+
+import com.example.crystalrangeseekbar.R;
+
+/**
+ * Created by owais.ali on 7/12/2016.
+ */
+public class BubbleThumbSeekbar extends CrystalSeekbar {
+
+ //////////////////////////////////////////
+ // PRIVATE CONSTANTS
+ //////////////////////////////////////////
+
+ //private static final float BUBBLE_WITH = 200f;
+ //private static final float BUBBLE_HEIGHT = 200f;
+
+ //////////////////////////////////////////
+ // PRIVATE VAR
+ //////////////////////////////////////////
+
+ private boolean animate;
+ private boolean isPressedLeftThumb;
+ private BubbleRect thumbPressedRect;
+
+ //////////////////////////////////////////
+ // CONSTRUCTOR
+ //////////////////////////////////////////
+
+ public BubbleThumbSeekbar(Context context) {
+ super(context);
+ }
+
+ public BubbleThumbSeekbar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public BubbleThumbSeekbar(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ //////////////////////////////////////////
+ // INITIALIZATION
+ //////////////////////////////////////////
+
+ @Override
+ protected void init(){
+ thumbPressedRect = new BubbleRect();
+ super.init();
+ }
+
+ //////////////////////////////////////////
+ // OVERRIDE METHODS
+ //////////////////////////////////////////
+
+ @Override
+ protected void touchDown(float x, float y) {
+ super.touchDown(x, y);
+
+ animate = true;
+ if(Thumb.MIN.equals(getPressedThumb())){
+ isPressedLeftThumb = true;
+ startAnimationUp();
+ }
+ }
+
+ @Override
+ protected void touchUp(float x, float y) {
+ super.touchUp(x, y);
+
+ animate = true;
+ if(Thumb.MIN.equals(getPressedThumb())){
+ startAnimationDown();
+ }
+ }
+
+ @Override
+ protected void drawLeftThumbWithColor(Canvas canvas, Paint paint, RectF rect) {
+ if(isPressedLeftThumb){
+
+ if(! animate){
+ rect.left = rect.left - ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ rect.right = rect.left + getBubbleWith();
+ rect.top = getThumbRect().top - ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ rect.bottom = getThumbRect().bottom + ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ }
+ else{
+
+ rect.left = thumbPressedRect.left;
+ rect.right = thumbPressedRect.right;
+ rect.top = thumbPressedRect.top;
+ rect.bottom = thumbPressedRect.bottom;
+ }
+
+ canvas.drawOval(rect, paint);
+ }
+ else {
+ canvas.drawOval(rect, paint);
+ }
+ }
+
+ @Override
+ protected void drawLeftThumbWithImage(Canvas canvas, Paint paint, RectF rect, Bitmap image) {
+ if(isPressedLeftThumb){
+
+ if(! animate){
+ image = resizeImage((int) getBubbleWith(), (int) getBubbleHeight(), image);
+ rect.top = getThumbRect().top - ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ rect.left = rect.left - ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ }
+ else{
+ image = resizeImage((int) thumbPressedRect.imageWith, (int) thumbPressedRect.imageHeight, image);
+ rect.top = thumbPressedRect.top;
+ rect.left = thumbPressedRect.left;
+ }
+
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+ else{
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+ }
+
+ //////////////////////////////////////////
+ // PROTECTED METHODS
+ //////////////////////////////////////////
+
+ protected float getBubbleWith(){
+ return getResources().getDimension(R.dimen.bubble_thumb_width);
+ }
+
+ protected float getBubbleHeight(){
+ return getResources().getDimension(R.dimen.bubble_thumb_height);
+ }
+
+ protected void startAnimationUp(){
+
+ BubbleRect toRect = new BubbleRect();
+ RectF fromRect = getThumbRect();
+
+ toRect.left = fromRect.left - ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ toRect.right = toRect.left + getBubbleWith();
+ toRect.top = fromRect.top - ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+ toRect.bottom = fromRect.bottom + ((getBubbleHeight() / 2) - (getThumbHeight() / 2));
+
+ PropertyValuesHolder leftValueHolder = PropertyValuesHolder.ofFloat("left", fromRect.left, toRect.left);
+ PropertyValuesHolder rightValueHolder = PropertyValuesHolder.ofFloat("right", fromRect.right, toRect.right);
+ PropertyValuesHolder topValueHolder = PropertyValuesHolder.ofFloat("top", fromRect.top, toRect.top);
+ PropertyValuesHolder bottomValueHolder = PropertyValuesHolder.ofFloat("bottom", fromRect.bottom, toRect.bottom);
+ PropertyValuesHolder imageWithValueHolder = PropertyValuesHolder.ofFloat("width", getThumbWidth(), getBubbleWith());
+ PropertyValuesHolder imageHeightValueHolder = PropertyValuesHolder.ofFloat("height", getThumbHeight(), getBubbleHeight());
+
+ ValueAnimator animation = ValueAnimator.ofPropertyValuesHolder(leftValueHolder, rightValueHolder, topValueHolder, bottomValueHolder, imageWithValueHolder, imageHeightValueHolder);
+ animation.setDuration(200);
+ animation.setInterpolator(new OvershootInterpolator(5f));
+ animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ thumbPressedRect.left = (float)animation.getAnimatedValue("left");
+ thumbPressedRect.right = (float)animation.getAnimatedValue("right");
+ thumbPressedRect.top = (float)animation.getAnimatedValue("top");
+ thumbPressedRect.bottom = (float)animation.getAnimatedValue("bottom");
+ thumbPressedRect.imageWith = (float)animation.getAnimatedValue("width");
+ thumbPressedRect.imageHeight = (float)animation.getAnimatedValue("height");
+ invalidate();
+ }
+ });
+ animation.start();
+
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ animate = false;
+ }
+ }, 200);
+ }
+
+ protected void startAnimationDown(){
+
+ RectF toRect = new RectF();
+ RectF fromRect = getThumbRect();
+
+ toRect.left = fromRect.left + ((getBubbleWith() / 2) - (getThumbWidth() / 2));
+ toRect.right = toRect.left + getThumbWidth();
+ toRect.top = 0f;
+ toRect.bottom = getThumbHeight();
+
+ PropertyValuesHolder leftValueHolder = PropertyValuesHolder.ofFloat("left", fromRect.left, toRect.left);
+ PropertyValuesHolder rightValueHolder = PropertyValuesHolder.ofFloat("right", fromRect.right, toRect.right);
+ PropertyValuesHolder topValueHolder = PropertyValuesHolder.ofFloat("top", fromRect.top, toRect.top);
+ PropertyValuesHolder bottomValueHolder = PropertyValuesHolder.ofFloat("bottom", fromRect.bottom, toRect.bottom);
+ PropertyValuesHolder imageWithValueHolder = PropertyValuesHolder.ofFloat("width", getBubbleWith(), getThumbWidth());
+ PropertyValuesHolder imageHeightValueHolder = PropertyValuesHolder.ofFloat("height", getBubbleHeight(), getThumbHeight());
+
+ ValueAnimator animation = ValueAnimator.ofPropertyValuesHolder(leftValueHolder, rightValueHolder, topValueHolder, bottomValueHolder, imageWithValueHolder, imageHeightValueHolder);
+ animation.setDuration(300);
+ animation.setInterpolator(new OvershootInterpolator(3f));
+ animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ thumbPressedRect.left = (float)animation.getAnimatedValue("left");
+ thumbPressedRect.right = (float)animation.getAnimatedValue("right");
+ thumbPressedRect.top = (float)animation.getAnimatedValue("top");
+ thumbPressedRect.bottom = (float)animation.getAnimatedValue("bottom");
+ thumbPressedRect.imageWith = (float)animation.getAnimatedValue("width");
+ thumbPressedRect.imageHeight = (float)animation.getAnimatedValue("height");
+ invalidate();
+ }
+ });
+ animation.start();
+
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ animate = false;
+ isPressedLeftThumb = false;
+ }
+ }, 300);
+ }
+
+ //////////////////////////////////////////
+ // PUBLIC METHODS
+ //////////////////////////////////////////
+
+
+ //////////////////////////////////////////
+ // PRIVATE METHODS
+ //////////////////////////////////////////
+
+ private Bitmap resizeImage( int iconWidth, int iconHeight, Bitmap bmp) {
+
+
+ int width = bmp.getWidth();
+ int height = bmp.getHeight();
+
+ // calculate the scale
+ float scaleWidth = ((float) iconWidth) / width;
+ float scaleHeight = ((float) iconHeight) / height;
+
+ // create a matrix for the manipulation
+ Matrix matrix = new Matrix();
+ // resize the Bitmap
+ matrix.postScale(scaleWidth, scaleHeight);
+
+ // if you want to rotate the Bitmap
+ // matrix.postRotate(45);
+
+ // recreate the new Bitmap
+
+ // make a Drawable from Bitmap to allow to set the Bitmap
+ // to the ImageView, ImageButton or what ever
+ return Bitmap.createBitmap(bmp, 0, 0, width, height, matrix, true);
+
+ }
+
+ //////////////////////////////////////////
+ // PRIVATE CLASS
+ //////////////////////////////////////////
+
+ private class BubbleRect{
+ public float left;
+ public float right;
+ public float top;
+ public float bottom;
+ public float imageWith;
+ public float imageHeight;
+ }
+
+}
diff --git a/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/CrystalRangeSeekbar.java b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/CrystalRangeSeekbar.java
new file mode 100644
index 0000000..57ec5c4
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/CrystalRangeSeekbar.java
@@ -0,0 +1,1169 @@
+package com.crystal.crystalrangeseekbar.widgets;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.core.content.ContextCompat;
+
+import com.crystal.crystalrangeseekbar.interfaces.OnRangeSeekbarChangeListener;
+import com.crystal.crystalrangeseekbar.interfaces.OnRangeSeekbarFinalValueListener;
+import com.example.crystalrangeseekbar.R;
+
+
+/**
+ * Created by owais.ali on 6/20/2016.
+ */
+public class CrystalRangeSeekbar extends View {
+
+ //////////////////////////////////////////
+ // PRIVATE CONSTANTS
+ //////////////////////////////////////////
+
+ private static final int INVALID_POINTER_ID = 255;
+ //private static final int DEFAULT_THUMB_WIDTH = 80;
+ //private static final int DEFAULT_THUMB_HEIGHT = 80;
+
+ private final float NO_STEP = -1f;
+ private final float NO_FIXED_GAP = -1f;
+
+ //////////////////////////////////////////
+ // PUBLIC CONSTANTS CLASS
+ //////////////////////////////////////////
+
+ public static final class DataType{
+ public static final int LONG = 0;
+ public static final int DOUBLE = 1;
+ public static final int INTEGER = 2;
+ public static final int FLOAT = 3;
+ public static final int SHORT = 4;
+ public static final int BYTE = 5;
+ }
+
+ public static final class ColorMode {
+ public static final int SOLID = 0;
+ public static final int GRADIENT = 1;
+ }
+
+ //////////////////////////////////////////
+ // PRIVATE VAR
+ //////////////////////////////////////////
+
+ private OnRangeSeekbarChangeListener onRangeSeekbarChangeListener;
+ private OnRangeSeekbarFinalValueListener onRangeSeekbarFinalValueListener;
+
+ private float absoluteMinValue;
+ private float absoluteMaxValue;
+ private float absoluteMinStartValue;
+ private float absoluteMaxStartValue;
+ private float minValue;
+ private float maxValue;
+ private float minStartValue;
+ private float maxStartValue;
+ private float steps;
+ private float gap;
+ private float fixGap;
+
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ private int dataType;
+ private float cornerRadius;
+ private int barColorMode;
+ private int barColor;
+ private int barGradientStart;
+ private int barGradientEnd;
+ private int barHighlightColorMode;
+ private int barHighlightColor;
+ private int barHighlightGradientStart;
+ private int barHighlightGradientEnd;
+ private int leftThumbColor;
+ private int rightThumbColor;
+ private int leftThumbColorNormal;
+ private int leftThumbColorPressed;
+ private int rightThumbColorNormal;
+ private int rightThumbColorPressed;
+ private boolean seekBarTouchEnabled;
+ private float barPadding;
+ private float barHeight;
+ private float _barHeight;
+ private float thumbWidth = getResources().getDimension(R.dimen.thumb_width);
+ private float thumbDiameter;
+
+ //private float thumbHalfWidth;
+ //private float thumbHalfHeight;
+ private float thumbHeight = getResources().getDimension(R.dimen.thumb_height);
+ private Drawable leftDrawable;
+ private Drawable rightDrawable;
+ private Drawable leftDrawablePressed;
+ private Drawable rightDrawablePressed;
+ private Bitmap leftThumb;
+ private Bitmap leftThumbPressed;
+ private Bitmap rightThumb;
+ private Bitmap rightThumbPressed;
+ private Thumb pressedThumb;
+ private double normalizedMinValue = 0d;
+ private double normalizedMaxValue = 100d;
+ private int pointerIndex;
+ private RectF _rect;
+ private Paint _paint;
+
+ private RectF rectLeftThumb, rectRightThumb;
+
+ private boolean mIsDragging;
+
+ //////////////////////////////////////////
+ // ENUMERATION
+ //////////////////////////////////////////
+
+ protected enum Thumb{ MIN, MAX }
+
+ //////////////////////////////////////////
+ // CONSTRUCTOR
+ //////////////////////////////////////////
+
+ public CrystalRangeSeekbar(Context context) {
+ this(context, null);
+ }
+
+ public CrystalRangeSeekbar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CrystalRangeSeekbar(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ // prevent render is in edit mode
+ if(isInEditMode()) return;
+
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CrystalRangeSeekbar);
+ try{
+ cornerRadius = getCornerRadius(array);
+ minValue = getMinValue(array);
+ maxValue = getMaxValue(array);
+ minStartValue = getMinStartValue(array);
+ maxStartValue = getMaxStartValue(array);
+ steps = getSteps(array);
+ gap = getGap(array);
+ fixGap = getFixedGap(array);
+ _barHeight = getBarHeight(array);
+ barColorMode = getBarColorMode(array);
+ barColor = getBarColor(array);
+ barGradientStart = getBarGradientStart(array);
+ barGradientEnd = getBarGradientEnd(array);
+ barHighlightColorMode = getBarHighlightColorMode(array);
+ barHighlightColor = getBarHighlightColor(array);
+ barHighlightGradientStart = getBarHighlightGradientStart(array);
+ barHighlightGradientEnd = getBarHighlightGradientEnd(array);
+ leftThumbColorNormal = getLeftThumbColor(array);
+ rightThumbColorNormal = getRightThumbColor(array);
+ leftThumbColorPressed = getLeftThumbColorPressed(array);
+ rightThumbColorPressed = getRightThumbColorPressed(array);
+ leftDrawable = getLeftDrawable(array);
+ rightDrawable = getRightDrawable(array);
+ leftDrawablePressed = getLeftDrawablePressed(array);
+ rightDrawablePressed = getRightDrawablePressed(array);
+ thumbDiameter = getDiameter(array);
+ dataType = getDataType(array);
+ seekBarTouchEnabled = isSeekBarTouchEnabled(array);
+ }
+ finally {
+ array.recycle();
+ }
+
+ init();
+ }
+
+ //////////////////////////////////////////
+ // INITIALIZING
+ //////////////////////////////////////////
+
+ protected void init(){
+ absoluteMinValue = minValue;
+ absoluteMaxValue = maxValue;
+ leftThumbColor = leftThumbColorNormal;
+ rightThumbColor = rightThumbColorNormal;
+ leftThumb = getBitmap(leftDrawable);
+ rightThumb = getBitmap(rightDrawable);
+ leftThumbPressed = getBitmap(leftDrawablePressed);
+ rightThumbPressed = getBitmap(rightDrawablePressed);
+ leftThumbPressed = (leftThumbPressed == null) ? leftThumb : leftThumbPressed;
+ rightThumbPressed = (rightThumbPressed == null) ? rightThumb : rightThumbPressed;
+
+ gap = Math.max(0, Math.min(gap, absoluteMaxValue - absoluteMinValue));
+ gap = gap / (absoluteMaxValue - absoluteMinValue) * 100;
+ if(fixGap != NO_FIXED_GAP){
+ fixGap = Math.min(fixGap, absoluteMaxValue);
+ fixGap = fixGap / (absoluteMaxValue - absoluteMinValue) * 100;
+ addFixGap(true);
+ }
+
+ thumbWidth = getThumbWidth();
+ thumbHeight = getThumbHeight();
+
+ //thumbHalfWidth = thumbWidth / 2;
+ //thumbHalfHeight = thumbHeight / 2;
+
+ barHeight = getBarHeight();
+ barPadding = getBarPadding();
+
+ _paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ _rect = new RectF();
+ rectLeftThumb = new RectF();
+ rectRightThumb = new RectF();
+
+ pressedThumb = null;
+
+ setMinStartValue();
+ setMaxStartValue();
+
+ setWillNotDraw(false);
+ }
+
+ //////////////////////////////////////////
+ // PUBLIC METHODS
+ //////////////////////////////////////////
+
+ public CrystalRangeSeekbar setCornerRadius(float cornerRadius){
+ this.cornerRadius = cornerRadius;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setMinValue(float minValue){
+ this.minValue = minValue;
+ this.absoluteMinValue = minValue;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setMaxValue(float maxValue){
+ this.maxValue = maxValue;
+ this.absoluteMaxValue = maxValue;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setMinStartValue(float minStartValue){
+ this.minStartValue = minStartValue;
+ this.absoluteMinStartValue = minStartValue;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setMaxStartValue(float maxStartValue){
+ this.maxStartValue = maxStartValue;
+ this.absoluteMaxStartValue = maxStartValue;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setSteps(float steps){
+ this.steps = steps;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setGap(float gap){
+ this.gap = gap;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setFixGap(float fixGap){
+ this.fixGap = fixGap;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarHeight(float height) {
+ _barHeight = height;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarColorMode(int barColorMode) {
+ this.barColorMode = barColorMode;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarColor(int barColor) {
+ this.barColor = barColor;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarGradientStart(int barGradientStart) {
+ this.barGradientStart = barGradientStart;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarGradientEnd(int barGradientEnd) {
+ this.barGradientEnd = barGradientEnd;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarHighlightColorMode(int barHighlightColorMode) {
+ this.barHighlightColorMode = barHighlightColorMode;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarHighlightColor(int barHighlightColor) {
+ this.barHighlightColor = barHighlightColor;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarHighlightGradientStart(int barHighlightGradientStart) {
+ this.barHighlightGradientStart = barHighlightGradientStart;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setBarHighlightGradientEnd(int barHighlightGradientEnd) {
+ this.barHighlightGradientEnd = barHighlightGradientEnd;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setLeftThumbColor(int leftThumbColorNormal){
+ this.leftThumbColorNormal = leftThumbColorNormal;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setLeftThumbHighlightColor(int leftThumbColorPressed){
+ this.leftThumbColorPressed = leftThumbColorPressed;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setLeftThumbDrawable(int resId){
+ setLeftThumbDrawable(ContextCompat.getDrawable(getContext(), resId));
+ return this;
+ }
+
+ public CrystalRangeSeekbar setLeftThumbDrawable(Drawable drawable){
+ setLeftThumbBitmap(getBitmap(drawable));
+ return this;
+ }
+
+ public CrystalRangeSeekbar setLeftThumbBitmap(Bitmap bitmap){
+ leftThumb = bitmap;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setLeftThumbHighlightDrawable(int resId){
+ setLeftThumbHighlightDrawable(ContextCompat.getDrawable(getContext(), resId));
+ return this;
+ }
+
+ public CrystalRangeSeekbar setLeftThumbHighlightDrawable(Drawable drawable){
+ setLeftThumbHighlightBitmap(getBitmap(drawable));
+ return this;
+ }
+
+ public CrystalRangeSeekbar setLeftThumbHighlightBitmap(Bitmap bitmap){
+ leftThumbPressed = bitmap;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setRightThumbColor(int rightThumbColorNormal){
+ this.rightThumbColorNormal = rightThumbColorNormal;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setRightThumbHighlightColor(int rightThumbColorPressed){
+ this.rightThumbColorPressed = rightThumbColorPressed;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setRightThumbDrawable(int resId){
+ setRightThumbDrawable(ContextCompat.getDrawable(getContext(), resId));
+ return this;
+ }
+
+ public CrystalRangeSeekbar setRightThumbDrawable(Drawable drawable){
+ setRightThumbBitmap(getBitmap(drawable));
+ return this;
+ }
+
+ public CrystalRangeSeekbar setRightThumbBitmap(Bitmap bitmap){
+ rightThumb = bitmap;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setRightThumbHighlightDrawable(int resId){
+ setRightThumbHighlightDrawable(ContextCompat.getDrawable(getContext(), resId));
+ return this;
+ }
+
+ public CrystalRangeSeekbar setRightThumbHighlightDrawable(Drawable drawable){
+ setRightThumbHighlightBitmap(getBitmap(drawable));
+ return this;
+ }
+
+ public CrystalRangeSeekbar setRightThumbHighlightBitmap(Bitmap bitmap){
+ rightThumbPressed = bitmap;
+ return this;
+ }
+
+ public CrystalRangeSeekbar setDataType(int dataType){
+ this.dataType = dataType;
+ return this;
+ }
+
+
+ /* add support to customize thumb size */
+
+ public CrystalRangeSeekbar setThumbSize(float thumbSize) {
+ final float scale = getContext().getResources().getDisplayMetrics().density;
+ this.thumbHeight = thumbSize * scale + 0.5f;
+ this.thumbWidth = thumbSize * scale + 0.5f;
+ this.thumbDiameter = (thumbSize * scale + 0.5f) / 2;
+ return this;
+ }
+
+ public void setOnRangeSeekbarChangeListener(OnRangeSeekbarChangeListener onRangeSeekbarChangeListener){
+ this.onRangeSeekbarChangeListener = onRangeSeekbarChangeListener;
+ if(this.onRangeSeekbarChangeListener != null){
+ this.onRangeSeekbarChangeListener.valueChanged(getSelectedMinValue(), getSelectedMaxValue());
+ }
+ }
+
+ public void setOnRangeSeekbarFinalValueListener(OnRangeSeekbarFinalValueListener onRangeSeekbarFinalValueListener){
+ this.onRangeSeekbarFinalValueListener = onRangeSeekbarFinalValueListener;
+ }
+
+ public Number getSelectedMinValue(){
+ double nv = normalizedMinValue;
+ if(steps > 0 && steps <= ((Math.abs(absoluteMaxValue)) / 2)){
+ float stp = steps / (absoluteMaxValue - absoluteMinValue) * 100;
+ double half_step = stp / 2;
+ double mod = nv % stp;
+ if(mod > half_step){
+ nv = nv - mod;
+ nv = nv + stp;
+ }
+ else{
+ nv = nv - mod;
+ }
+ }
+ else{
+ if(steps != NO_STEP)
+ throw new IllegalStateException("steps out of range " + steps);
+ }
+
+ return formatValue(normalizedToValue(nv));
+ }
+
+ public Number getSelectedMaxValue(){
+
+ double nv = normalizedMaxValue;
+ if(steps > 0 && steps <= (Math.abs(absoluteMaxValue) / 2)){
+ float stp = steps / (absoluteMaxValue - absoluteMinValue) * 100;
+ double half_step = stp / 2;
+ double mod = nv % stp;
+ if(mod > half_step){
+ nv = nv - mod;
+ nv = nv + stp;
+ }
+ else{
+ nv = nv - mod;
+ }
+ }
+ else{
+ if(steps != NO_STEP)
+ throw new IllegalStateException("steps out of range " + steps);
+ }
+
+ return formatValue(normalizedToValue(nv));
+ }
+
+ public void apply(){
+
+ // reset normalize min and max value
+ normalizedMinValue = 0d;
+ normalizedMaxValue = 100d;
+
+ gap = Math.max(0, Math.min(gap, absoluteMaxValue - absoluteMinValue));
+ gap = gap / (absoluteMaxValue - absoluteMinValue) * 100;
+ if(fixGap != NO_FIXED_GAP){
+ fixGap = Math.min(fixGap, absoluteMaxValue);
+ fixGap = fixGap / (absoluteMaxValue - absoluteMinValue) * 100;
+ addFixGap(true);
+ }
+
+ thumbWidth = getThumbWidth();
+ thumbHeight = getThumbHeight();
+
+ //thumbHalfWidth = thumbWidth / 2;
+ //thumbHalfHeight = thumbHeight / 2;
+
+ barHeight = getBarHeight();
+ barPadding = thumbWidth * 0.5f;
+
+ // set min start value
+ if(minStartValue <= absoluteMinValue){
+ minStartValue = 0;
+ setNormalizedMinValue(minStartValue);
+ }
+ else if(minStartValue >= absoluteMaxValue){
+ minStartValue = absoluteMaxValue;
+ setMinStartValue();
+ }
+ else{
+ setMinStartValue();
+ }
+
+ // set max start value
+ if (maxStartValue < absoluteMinStartValue || maxStartValue <= absoluteMinValue) {
+ maxStartValue = 0;
+ setNormalizedMaxValue(maxStartValue);
+ }
+ else if(maxStartValue >= absoluteMaxValue){
+ maxStartValue = absoluteMaxValue;
+ setMaxStartValue();
+ }
+ else{
+ setMaxStartValue();
+ }
+ invalidate();
+
+ if (onRangeSeekbarChangeListener != null) {
+ onRangeSeekbarChangeListener.valueChanged(getSelectedMinValue(), getSelectedMaxValue());
+ }
+ }
+
+ //////////////////////////////////////////
+ // PROTECTED METHODS
+ //////////////////////////////////////////
+
+ protected Thumb getPressedThumb(){
+ return pressedThumb;
+ }
+
+
+ protected float getThumbWidth() {
+ return (leftThumb != null) ? leftThumb.getWidth() : thumbWidth;
+ }
+
+ protected float getThumbHeight() {
+ return (leftThumb != null) ? leftThumb.getHeight() : thumbHeight;
+ }
+
+ protected float getThumbDiameter() {
+ return (thumbDiameter > 0) ? thumbDiameter : thumbWidth;
+ }
+
+ protected float getBarHeight(){
+ return _barHeight > 0 ? _barHeight : (thumbHeight * 0.5f) * 0.3f;
+ }
+
+ protected float getBarPadding(){
+ return thumbWidth * 0.5f;
+ }
+
+ protected Bitmap getBitmap(Drawable drawable){
+ return (drawable != null) ? ((BitmapDrawable) drawable).getBitmap() : null;
+ }
+
+ protected float getCornerRadius(final TypedArray typedArray){
+ return typedArray.getFloat(R.styleable.CrystalRangeSeekbar_corner_radius, 0f);
+ }
+
+ protected float getMinValue(final TypedArray typedArray){
+ return typedArray.getFloat(R.styleable.CrystalRangeSeekbar_min_value, 0f);
+ }
+
+ protected float getMaxValue(final TypedArray typedArray){
+ return typedArray.getFloat(R.styleable.CrystalRangeSeekbar_max_value, 100f);
+ }
+
+ protected float getMinStartValue(final TypedArray typedArray){
+ return typedArray.getFloat(R.styleable.CrystalRangeSeekbar_min_start_value, minValue);
+ }
+
+ protected float getMaxStartValue(final TypedArray typedArray){
+ return typedArray.getFloat(R.styleable.CrystalRangeSeekbar_max_start_value, maxValue);
+ }
+
+ protected float getSteps(final TypedArray typedArray){
+ return typedArray.getFloat(R.styleable.CrystalRangeSeekbar_steps, NO_STEP);
+ }
+
+ protected float getGap(final TypedArray typedArray){
+ return typedArray.getFloat(R.styleable.CrystalRangeSeekbar_gap, 0f);
+ }
+
+ protected float getFixedGap(final TypedArray typedArray){
+ return typedArray.getFloat(R.styleable.CrystalRangeSeekbar_fix_gap, NO_FIXED_GAP);
+ }
+
+ protected int getBarColorMode(final TypedArray typedArray) {
+ return typedArray.getInt(R.styleable.CrystalRangeSeekbar_bar_color_mode, CrystalSeekbar.ColorMode.SOLID);
+ }
+
+ protected float getBarHeight(final TypedArray typedArray){
+ return typedArray.getDimensionPixelSize(R.styleable.CrystalRangeSeekbar_bar_height, 0);
+ }
+
+ protected int getBarColor(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_bar_color, Color.GRAY);
+ }
+
+ protected int getBarGradientStart(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_bar_gradient_start, Color.GRAY);
+ }
+
+ protected int getBarGradientEnd(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_bar_gradient_end, Color.DKGRAY);
+ }
+
+ protected int getBarHighlightColorMode(final TypedArray typedArray) {
+ return typedArray.getInt(R.styleable.CrystalRangeSeekbar_bar_highlight_color_mode, CrystalSeekbar.ColorMode.SOLID);
+ }
+
+ protected int getBarHighlightColor(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_bar_highlight_color, Color.BLACK);
+ }
+
+ protected int getBarHighlightGradientStart(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_bar_highlight_gradient_start, Color.DKGRAY);
+ }
+
+ protected int getBarHighlightGradientEnd(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_bar_highlight_gradient_end, Color.BLACK);
+ }
+
+ protected int getLeftThumbColor(final TypedArray typedArray){
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_left_thumb_color, Color.BLACK);
+ }
+
+ protected int getRightThumbColor(final TypedArray typedArray){
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_right_thumb_color, Color.BLACK);
+ }
+
+ protected int getLeftThumbColorPressed(final TypedArray typedArray){
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_left_thumb_color_pressed, Color.DKGRAY);
+ }
+
+ protected int getRightThumbColorPressed(final TypedArray typedArray){
+ return typedArray.getColor(R.styleable.CrystalRangeSeekbar_right_thumb_color_pressed, Color.DKGRAY);
+ }
+
+ protected Drawable getLeftDrawable(final TypedArray typedArray){
+ return typedArray.getDrawable(R.styleable.CrystalRangeSeekbar_left_thumb_image);
+ }
+
+ protected Drawable getRightDrawable(final TypedArray typedArray){
+ return typedArray.getDrawable(R.styleable.CrystalRangeSeekbar_right_thumb_image);
+ }
+
+ protected Drawable getLeftDrawablePressed(final TypedArray typedArray){
+ return typedArray.getDrawable(R.styleable.CrystalRangeSeekbar_left_thumb_image_pressed);
+ }
+
+ protected Drawable getRightDrawablePressed(final TypedArray typedArray){
+ return typedArray.getDrawable(R.styleable.CrystalRangeSeekbar_right_thumb_image_pressed);
+ }
+
+ protected int getDataType(final TypedArray typedArray){
+ return typedArray.getInt(R.styleable.CrystalRangeSeekbar_data_type, DataType.INTEGER);
+ }
+
+ protected boolean isSeekBarTouchEnabled(final TypedArray typedArray){
+ return typedArray.getBoolean(R.styleable.CrystalRangeSeekbar_seek_bar_touch_enabled, false);
+ }
+
+ protected float getDiameter(final TypedArray typedArray){
+ return typedArray.getDimensionPixelSize(R.styleable.CrystalRangeSeekbar_thumb_diameter, getResources().getDimensionPixelSize(R.dimen.thumb_height));
+ }
+
+ protected RectF getLeftThumbRect(){
+ return rectLeftThumb;
+ }
+
+ protected RectF getRightThumbRect(){
+ return rectRightThumb;
+ }
+
+ protected void setupBar(final Canvas canvas, final Paint paint, final RectF rect){
+ rect.left = barPadding;
+ rect.top = 0.5f * (getHeight() - barHeight);
+ rect.right = getWidth() - barPadding;
+ rect.bottom = 0.5f * (getHeight() + barHeight);
+
+ paint.setStyle(Paint.Style.FILL);
+ paint.setAntiAlias(true);
+
+ if (barColorMode == CrystalSeekbar.ColorMode.SOLID) {
+ paint.setColor(barColor);
+ drawBar(canvas, paint, rect);
+
+ } else {
+ paint.setShader(
+ new LinearGradient(rect.left, rect.bottom, rect.right, rect.top,
+ barGradientStart,
+ barGradientEnd,
+ Shader.TileMode.MIRROR)
+ );
+
+ drawBar(canvas, paint, rect);
+
+ paint.setShader(null);
+ }
+ }
+
+ protected void drawBar(final Canvas canvas, final Paint paint, final RectF rect){
+ canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
+ }
+
+ protected void setupHighlightBar(final Canvas canvas, final Paint paint, final RectF rect){
+ rect.left = normalizedToScreen(normalizedMinValue) + (getThumbWidth() / 2);
+ rect.right = normalizedToScreen(normalizedMaxValue) + (getThumbWidth() / 2);
+
+ paint.setStyle(Paint.Style.FILL);
+ paint.setAntiAlias(true);
+
+ if (barHighlightColorMode == CrystalSeekbar.ColorMode.SOLID) {
+ paint.setColor(barHighlightColor);
+ drawHighlightBar(canvas, paint, rect);
+
+ } else {
+ paint.setShader(
+ new LinearGradient(rect.left, rect.bottom, rect.right, rect.top,
+ barHighlightGradientStart,
+ barHighlightGradientEnd,
+ Shader.TileMode.MIRROR)
+ );
+
+ drawHighlightBar(canvas, paint, rect);
+
+ paint.setShader(null);
+ }
+ }
+
+ protected void drawHighlightBar(final Canvas canvas, final Paint paint, final RectF rect){
+ canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
+ }
+
+ protected void setupLeftThumb(final Canvas canvas, final Paint paint, final RectF rect){
+ leftThumbColor = (Thumb.MIN.equals(pressedThumb)) ? leftThumbColorPressed : leftThumbColorNormal;
+ paint.setColor(leftThumbColor);
+
+ //float leftL = normalizedToScreen(normalizedMinValue);
+ //float rightL = Math.min(leftL + thumbHalfWidth + barPadding, getWidth());
+ rectLeftThumb.left = normalizedToScreen(normalizedMinValue);
+ rectLeftThumb.right = Math.min(rectLeftThumb.left + (getThumbWidth() / 2) + barPadding, getWidth());
+ rectLeftThumb.top = 0f;
+ rectLeftThumb.bottom = thumbHeight;
+
+ if(leftThumb != null){
+ Bitmap lThumb = (Thumb.MIN.equals(pressedThumb)) ? leftThumbPressed : leftThumb;
+ drawLeftThumbWithImage(canvas, paint, rectLeftThumb, lThumb);
+ }
+ else{
+ drawLeftThumbWithColor(canvas, paint, rectLeftThumb);
+ }
+ }
+
+ protected void drawLeftThumbWithColor(final Canvas canvas, final Paint paint, final RectF rect){
+ canvas.drawOval(rect, paint);
+ }
+
+ protected void drawLeftThumbWithImage(final Canvas canvas, final Paint paint, final RectF rect, final Bitmap image){
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+
+ protected void setupRightThumb(final Canvas canvas, final Paint paint, final RectF rect){
+
+ rightThumbColor = (Thumb.MAX.equals(pressedThumb)) ? rightThumbColorPressed : rightThumbColorNormal;
+ paint.setColor(rightThumbColor);
+
+ //float leftR = normalizedToScreen(normalizedMaxValue);
+ //float rightR = Math.min(leftR + thumbHalfWidth + barPadding, getWidth());
+ rectRightThumb.left = normalizedToScreen(normalizedMaxValue);
+ rectRightThumb.right = Math.min(rectRightThumb.left + (getThumbWidth() / 2) + barPadding, getWidth());
+ rectRightThumb.top = 0f;
+ rectRightThumb.bottom = thumbHeight;
+
+ if(rightThumb != null){
+ Bitmap rThumb = (Thumb.MAX.equals(pressedThumb)) ? rightThumbPressed : rightThumb;
+ drawRightThumbWithImage(canvas, paint, rectRightThumb, rThumb);
+ }
+ else{
+ drawRightThumbWithColor(canvas, paint, rectRightThumb);
+ }
+ }
+
+ protected void drawRightThumbWithColor(final Canvas canvas, final Paint paint, final RectF rect){
+ canvas.drawOval(rect, paint);
+ }
+
+ protected void drawRightThumbWithImage(final Canvas canvas, final Paint paint, final RectF rect, final Bitmap image){
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+
+ protected void trackTouchEvent(MotionEvent event){
+ final int pointerIndex = event.findPointerIndex(mActivePointerId);
+ try{
+ final float x = event.getX(pointerIndex);
+
+ if (Thumb.MIN.equals(pressedThumb)) {
+ setNormalizedMinValue(screenToNormalized(x));
+ } else if (Thumb.MAX.equals(pressedThumb)) {
+ setNormalizedMaxValue(screenToNormalized(x));
+ }
+ }
+ catch (Exception ignored){}
+ }
+
+ protected void touchDown(final float x, final float y){
+
+ }
+
+ protected void touchMove(final float x, final float y){
+
+ }
+
+ protected void touchUp(final float x, final float y){
+
+ }
+
+ protected int getMeasureSpecWith(int widthMeasureSpec){
+ int width = 200;
+ if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
+ width = MeasureSpec.getSize(widthMeasureSpec);
+ }
+ return width;
+ }
+
+ protected int getMeasureSpecHeight(int heightMeasureSpec){
+ int height = Math.round(thumbHeight);
+ if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
+ height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
+ }
+ return height;
+ }
+
+ protected final void log(Object object){
+ Log.d("CRS=>", String.valueOf(object));
+ }
+
+ //////////////////////////////////////////
+ // PRIVATE METHODS
+ //////////////////////////////////////////
+
+ private void setMinStartValue() {
+ if (minStartValue > minValue && minStartValue <= maxValue) {
+ minStartValue = Math.min(minStartValue, absoluteMaxValue);
+ minStartValue -= absoluteMinValue;
+ minStartValue = minStartValue / (absoluteMaxValue - absoluteMinValue) * 100;
+ setNormalizedMinValue(minStartValue);
+ }
+ }
+
+ private void setMaxStartValue() {
+ if (maxStartValue <= absoluteMaxValue && maxStartValue > absoluteMinValue && maxStartValue >= absoluteMinStartValue) {
+ maxStartValue = Math.max(absoluteMaxStartValue, absoluteMinValue);
+ maxStartValue -= absoluteMinValue;
+ maxStartValue = maxStartValue / (absoluteMaxValue - absoluteMinValue) * 100;
+ setNormalizedMaxValue(maxStartValue);
+ }
+ }
+
+ private Thumb evalPressedThumb(float touchX){
+ Thumb result = null;
+
+ boolean minThumbPressed = isInThumbRange(touchX, normalizedMinValue);
+ boolean maxThumbPressed = isInThumbRange(touchX, normalizedMaxValue);
+ if (minThumbPressed && maxThumbPressed) {
+ // if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids "stalling" the thumbs in a corner, not being able to drag them apart anymore.
+ result = (touchX / getWidth() > 0.5f) ? Thumb.MIN : Thumb.MAX;
+ }
+ else if(minThumbPressed){
+ result = Thumb.MIN;
+ }
+ else if(maxThumbPressed){
+ result = Thumb.MAX;
+ }
+
+ if (seekBarTouchEnabled && result == null) {
+ result = findClosestThumb(touchX);
+ }
+ return result;
+ }
+
+ private boolean isInThumbRange(float touchX, double normalizedThumbValue){
+ float thumbPos = normalizedToScreen(normalizedThumbValue);
+ float left = thumbPos - (getThumbWidth() / 2);
+ float right = thumbPos + (getThumbWidth() / 2);
+ float x = touchX - (getThumbWidth() / 2);
+ if(thumbPos > (getWidth() - thumbWidth)) x = touchX;
+ return (x >= left && x <= right);
+ //return Math.abs(touchX - normalizedToScreen(normalizedThumbValue)) <= thumbWidth;
+ }
+
+ private Thumb findClosestThumb(float touchX) {
+ float screenMinX = normalizedToScreen(normalizedMinValue);
+ float screenMaxX = normalizedToScreen(normalizedMaxValue);
+ if (touchX >= screenMaxX) {
+ return Thumb.MAX;
+ } else if (touchX <= screenMinX) {
+ return Thumb.MIN;
+ }
+
+ double minDiff = Math.abs(screenMinX - touchX);
+ double maxDiff = Math.abs(screenMaxX - touchX);
+ return minDiff < maxDiff ? Thumb.MIN : Thumb.MAX;
+ }
+
+ private void onStartTrackingTouch(){
+ mIsDragging = true;
+ }
+
+ private void onStopTrackingTouch(){
+ mIsDragging = false;
+ }
+
+ private float normalizedToScreen(double normalizedCoord){
+ float width = getWidth() - (barPadding * 2);
+ return (float) normalizedCoord / 100f * width;
+ }
+
+ private double screenToNormalized(float screenCoord){
+ double width = getWidth();
+
+ if (width <= 2 * barPadding) {
+ // prevent division by zero, simply return 0.
+ return 0d;
+ } else {
+ width = width - (barPadding * 2);
+ double result = screenCoord / width * 100d;
+ result = result - (barPadding / width * 100d);
+ result = Math.min(100d, Math.max(0d, result));
+ return result;
+
+ }
+ }
+
+ private void setNormalizedMinValue(double value) {
+ normalizedMinValue = Math.max(0d, Math.min(100d, Math.min(value, normalizedMaxValue)));
+ if(fixGap != NO_FIXED_GAP && fixGap > 0){
+ addFixGap(true);
+ }
+ else{
+ addMinGap();
+ }
+ invalidate();
+ }
+
+ private void setNormalizedMaxValue(double value) {
+ normalizedMaxValue = Math.max(0d, Math.min(100d, Math.max(value, normalizedMinValue)));
+ if(fixGap != NO_FIXED_GAP && fixGap > 0){
+ addFixGap(false);
+ }
+ else{
+ addMaxGap();
+ }
+ invalidate();
+ }
+
+ private void addFixGap(boolean leftThumb){
+ if(leftThumb){
+ normalizedMaxValue = normalizedMinValue + fixGap;
+ if(normalizedMaxValue >= 100){
+ normalizedMaxValue = 100;
+ normalizedMinValue = normalizedMaxValue - fixGap;
+ }
+ }
+ else{
+ normalizedMinValue = normalizedMaxValue - fixGap;
+ if(normalizedMinValue <= 0){
+ normalizedMinValue = 0;
+ normalizedMaxValue = normalizedMinValue + fixGap;
+ }
+ }
+ }
+
+ private void addMinGap(){
+ if((normalizedMinValue + gap) > normalizedMaxValue){
+ double g = normalizedMinValue + gap;
+ normalizedMaxValue = g;
+ normalizedMaxValue = Math.max(0d, Math.min(100d, Math.max(g, normalizedMinValue)));
+
+ if(normalizedMinValue >= (normalizedMaxValue - gap)){
+ normalizedMinValue = normalizedMaxValue - gap;
+ }
+ }
+ }
+
+ private void addMaxGap(){
+ if((normalizedMaxValue - gap) < normalizedMinValue){
+ double g = normalizedMaxValue - gap;
+ normalizedMinValue = g;
+ normalizedMinValue = Math.max(0d, Math.min(100d, Math.min(g, normalizedMaxValue)));
+ if(normalizedMaxValue <= (normalizedMinValue + gap)){
+ normalizedMaxValue = normalizedMinValue + gap;
+ }
+ }
+ }
+
+ private double normalizedToValue(double normalized) {
+ double val = normalized / 100 * (maxValue - minValue);
+ val = val + minValue;
+ return val;
+ }
+
+ private void attemptClaimDrag() {
+ if (getParent() != null) {
+ getParent().requestDisallowInterceptTouchEvent(true);
+ }
+ }
+
+ private Number formatValue(T value) throws IllegalArgumentException{
+ final Double v = (Double) value;
+ if (dataType == DataType.LONG) {
+ return v.longValue();
+ }
+ if (dataType == DataType.DOUBLE) {
+ return v;
+ }
+ if (dataType == DataType.INTEGER) {
+ return Math.round(v);
+ }
+ if (dataType == DataType.FLOAT) {
+ return v.floatValue();
+ }
+ if (dataType == DataType.SHORT) {
+ return v.shortValue();
+ }
+ if (dataType == DataType.BYTE) {
+ return v.byteValue();
+ }
+ throw new IllegalArgumentException("Number class '" + value.getClass().getName() + "' is not supported");
+ }
+
+ //////////////////////////////////////////
+ // OVERRIDE METHODS
+ //////////////////////////////////////////
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // prevent render is in edit mode
+ if(isInEditMode()) return;
+
+ // setup bar
+ setupBar(canvas, _paint, _rect);
+
+ // setup seek bar active range line
+ setupHighlightBar(canvas, _paint ,_rect);
+
+ // draw left thumb
+ setupLeftThumb(canvas, _paint, _rect);
+
+ // draw right thumb
+ setupRightThumb(canvas, _paint, _rect);
+
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(getMeasureSpecWith(widthMeasureSpec), getMeasureSpecHeight(heightMeasureSpec));
+ }
+
+ /**
+ * Handles thumb selection and movement. Notifies listener callback on certain events.
+ */
+ @Override
+ public synchronized boolean onTouchEvent(MotionEvent event) {
+
+ if (!isEnabled())
+ return false;
+
+
+
+ final int action = event.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = event.getPointerId(event.getPointerCount() - 1);
+ pointerIndex = event.findPointerIndex(mActivePointerId);
+ float mDownMotionX = event.getX(pointerIndex);
+
+ pressedThumb = evalPressedThumb(mDownMotionX);
+
+ if(pressedThumb == null) return super.onTouchEvent(event);
+
+ touchDown(event.getX(pointerIndex), event.getY(pointerIndex));
+ setPressed(true);
+ invalidate();
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ attemptClaimDrag();
+
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (pressedThumb != null) {
+
+ if (mIsDragging) {
+ touchMove(event.getX(pointerIndex), event.getY(pointerIndex));
+ trackTouchEvent(event);
+ }
+
+ if (onRangeSeekbarChangeListener != null) {
+ onRangeSeekbarChangeListener.valueChanged(getSelectedMinValue(), getSelectedMaxValue());
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mIsDragging) {
+ trackTouchEvent(event);
+ onStopTrackingTouch();
+ setPressed(false);
+ touchUp(event.getX(pointerIndex), event.getY(pointerIndex));
+ if(onRangeSeekbarFinalValueListener != null){
+ onRangeSeekbarFinalValueListener.finalValue(getSelectedMinValue(), getSelectedMaxValue());
+ }
+ } else {
+ // Touch up when we never crossed the touch slop threshold
+ // should be interpreted as a tap-seek to that location.
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ onStopTrackingTouch();
+ }
+
+ pressedThumb = null;
+ invalidate();
+ if (onRangeSeekbarChangeListener != null) {
+ onRangeSeekbarChangeListener.valueChanged(getSelectedMinValue(), getSelectedMaxValue());
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ //final int index = event.getPointerCount() - 1;
+ // final int index = ev.getActionIndex();
+ /*mDownMotionX = event.getX(index);
+ mActivePointerId = event.getPointerId(index);
+ invalidate();*/
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP:
+ /*onSecondaryPointerUp(event);*/
+ invalidate();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsDragging) {
+ onStopTrackingTouch();
+ setPressed(false);
+ touchUp(event.getX(pointerIndex), event.getY(pointerIndex));
+ }
+ invalidate(); // see above explanation
+ break;
+ }
+
+ return true;
+
+ }
+}
+
diff --git a/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/CrystalSeekbar.java b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/CrystalSeekbar.java
new file mode 100644
index 0000000..3aec70d
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/java/com/crystal/crystalrangeseekbar/widgets/CrystalSeekbar.java
@@ -0,0 +1,978 @@
+package com.crystal.crystalrangeseekbar.widgets;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.core.content.ContextCompat;
+
+import com.crystal.crystalrangeseekbar.interfaces.OnSeekbarChangeListener;
+import com.crystal.crystalrangeseekbar.interfaces.OnSeekbarFinalValueListener;
+import com.example.crystalrangeseekbar.R;
+
+
+/**
+ * Created by owais.ali on 6/20/2016.
+ */
+public class CrystalSeekbar extends View {
+
+ //////////////////////////////////////////
+ // PRIVATE CONSTANTS
+ //////////////////////////////////////////
+
+ private static final int INVALID_POINTER_ID = 255;
+ //private static int DEFAULT_THUMB_WIDTH ;
+ //private static int DEFAULT_THUMB_HEIGHT;
+
+ private final float NO_STEP = -1f;
+
+ //////////////////////////////////////////
+ // PUBLIC CONSTANTS CLASS
+ //////////////////////////////////////////
+
+ public static final class DataType {
+ public static final int LONG = 0;
+ public static final int DOUBLE = 1;
+ public static final int INTEGER = 2;
+ public static final int FLOAT = 3;
+ public static final int SHORT = 4;
+ public static final int BYTE = 5;
+ }
+
+ public static final class Position {
+ public static final int LEFT = 0;
+ public static final int RIGHT = 1;
+ }
+
+ public static final class ColorMode {
+ public static final int SOLID = 0;
+ public static final int GRADIENT = 1;
+ }
+
+ //////////////////////////////////////////
+ // PRIVATE VAR
+ //////////////////////////////////////////
+
+ private OnSeekbarChangeListener onSeekbarChangeListener;
+ private OnSeekbarFinalValueListener onSeekbarFinalValueListener;
+
+ private float absoluteMinValue;
+ private float absoluteMaxValue;
+ private float minValue;
+ private float maxValue;
+ private float minStartValue;
+ private float steps;
+
+
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ private int position;
+ private int nextPosition;
+ private int dataType;
+ private float cornerRadius;
+ private int barColorMode;
+ private int barColor;
+ private int barGradientStart;
+ private int barGradientEnd;
+ private int barHighlightColorMode;
+ private int barHighlightColor;
+ private int barHighlightGradientStart;
+ private int barHighlightGradientEnd;
+ private int thumbColor;
+ private int thumbColorNormal;
+ private int thumbColorPressed;
+ private boolean seekBarTouchEnabled;
+ private float barPadding;
+ private float _barHeight;
+ private float barHeight;
+ private float thumbWidth = 30f;
+ private float thumbHeight = 30f;
+ private float thumbDiameter;
+ private Drawable thumbDrawable;
+ private Drawable thumbDrawablePressed;
+ private Bitmap thumb;
+ private Bitmap thumbPressed;
+ private Thumb pressedThumb;
+ private double normalizedMinValue = 0d;
+ private double normalizedMaxValue = 100d;
+ private int pointerIndex;
+
+ private RectF _rect;
+ private Paint _paint;
+
+ private RectF rectThumb;
+
+ private boolean mIsDragging;
+
+ //////////////////////////////////////////
+ // ENUMERATION
+ //////////////////////////////////////////
+
+ protected enum Thumb {MIN}
+
+ //////////////////////////////////////////
+ // CONSTRUCTOR
+ //////////////////////////////////////////
+
+ public CrystalSeekbar(Context context) {
+ this(context, null);
+ }
+
+ public CrystalSeekbar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CrystalSeekbar(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ // prevent render is in edit mode
+ if (isInEditMode()) return;
+
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CrystalSeekbar);
+ try {
+ cornerRadius = getCornerRadius(array);
+ minValue = getMinValue(array);
+ maxValue = getMaxValue(array);
+ minStartValue = getMinStartValue(array);
+ steps = getSteps(array);
+ _barHeight = getBarHeight(array);
+ barColorMode = getBarColorMode(array);
+ barColor = getBarColor(array);
+ barGradientStart = getBarGradientStart(array);
+ barGradientEnd = getBarGradientEnd(array);
+ barHighlightColorMode = getBarHighlightColorMode(array);
+ barHighlightColor = getBarHighlightColor(array);
+ barHighlightGradientStart = getBarHighlightGradientStart(array);
+ barHighlightGradientEnd = getBarHighlightGradientEnd(array);
+ thumbColorNormal = getThumbColor(array);
+ thumbColorPressed = getThumbColorPressed(array);
+ thumbDrawable = getThumbDrawable(array);
+ thumbDrawablePressed = getThumbDrawablePressed(array);
+ dataType = getDataType(array);
+ position = getPosition(array);
+ nextPosition = position;
+ thumbDiameter = getDiameter(array);
+ seekBarTouchEnabled = isSeekBarTouchEnabled(array);
+ } finally {
+ array.recycle();
+ }
+
+ init();
+ }
+
+ //////////////////////////////////////////
+ // INITIALIZING
+ //////////////////////////////////////////
+
+ protected void init() {
+ absoluteMinValue = minValue;
+ absoluteMaxValue = maxValue;
+ thumbColor = thumbColorNormal;
+ thumb = getBitmap(thumbDrawable);
+ thumbPressed = getBitmap(thumbDrawablePressed);
+ thumbPressed = (thumbPressed == null) ? thumb : thumbPressed;
+
+ thumbWidth = getThumbWidth();
+ thumbWidth = getThumbWidth();
+ thumbHeight = getThumbHeight();
+
+ barHeight = getBarHeight();
+ barPadding = getBarPadding();
+
+ _paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ _rect = new RectF();
+ rectThumb = new RectF();
+
+ pressedThumb = null;
+
+ setMinStartValue();
+
+ setWillNotDraw(false);
+ }
+
+ //////////////////////////////////////////
+ // PUBLIC METHODS
+ //////////////////////////////////////////
+
+ public CrystalSeekbar setCornerRadius(float cornerRadius) {
+ this.cornerRadius = cornerRadius;
+ return this;
+ }
+
+ public CrystalSeekbar setMinValue(float minValue) {
+ this.minValue = minValue;
+ this.absoluteMinValue = minValue;
+ return this;
+ }
+
+ public CrystalSeekbar setMaxValue(float maxValue) {
+ this.maxValue = maxValue;
+ this.absoluteMaxValue = maxValue;
+ return this;
+ }
+
+ public CrystalSeekbar setMinStartValue(float minStartValue) {
+ this.minStartValue = minStartValue;
+ return this;
+ }
+
+ public CrystalSeekbar setSteps(float steps) {
+ this.steps = steps;
+ return this;
+ }
+
+ public CrystalSeekbar setBarHeight(float barHeight) {
+ this._barHeight = barHeight;
+ return this;
+ }
+
+ public CrystalSeekbar setBarColorMode(int barColorMode) {
+ this.barColorMode = barColorMode;
+ return this;
+ }
+
+ public CrystalSeekbar setBarColor(int barColor) {
+ this.barColor = barColor;
+ return this;
+ }
+
+ public CrystalSeekbar setBarGradientStart(int barGradientStart) {
+ this.barGradientStart = barGradientStart;
+ return this;
+ }
+
+ public CrystalSeekbar setBarGradientEnd(int barGradientEnd) {
+ this.barGradientEnd = barGradientEnd;
+ return this;
+ }
+
+ public CrystalSeekbar setBarHighlightColorMode(int barHighlightColorMode) {
+ this.barHighlightColorMode = barHighlightColorMode;
+ return this;
+ }
+
+ public CrystalSeekbar setBarHighlightColor(int barHighlightColor) {
+ this.barHighlightColor = barHighlightColor;
+ return this;
+ }
+
+ public CrystalSeekbar setBarHighlightGradientStart(int barHighlightGradientStart) {
+ this.barHighlightGradientStart = barHighlightGradientStart;
+ return this;
+ }
+
+ public CrystalSeekbar setBarHighlightGradientEnd(int barHighlightGradientEnd) {
+ this.barHighlightGradientEnd = barHighlightGradientEnd;
+ return this;
+ }
+
+ public CrystalSeekbar setThumbColor(int leftThumbColorNormal) {
+ this.thumbColorNormal = leftThumbColorNormal;
+ return this;
+ }
+
+ public CrystalSeekbar setThumbHighlightColor(int leftThumbColorPressed) {
+ this.thumbColorPressed = leftThumbColorPressed;
+ return this;
+ }
+
+ public CrystalSeekbar setThumbDrawable(int resId) {
+ setThumbDrawable(ContextCompat.getDrawable(getContext(), resId));
+ return this;
+ }
+
+ public CrystalSeekbar setThumbDrawable(Drawable drawable) {
+ setThumbBitmap(getBitmap(drawable));
+ return this;
+ }
+
+ public CrystalSeekbar setThumbBitmap(Bitmap bitmap) {
+ thumb = bitmap;
+ return this;
+ }
+
+ public CrystalSeekbar setThumbHighlightDrawable(int resId) {
+ setThumbHighlightDrawable(ContextCompat.getDrawable(getContext(), resId));
+ return this;
+ }
+
+ public CrystalSeekbar setThumbHighlightDrawable(Drawable drawable) {
+ setThumbHighlightBitmap(getBitmap(drawable));
+ return this;
+ }
+
+ public CrystalSeekbar setThumbHighlightBitmap(Bitmap bitmap) {
+ thumbPressed = bitmap;
+ return this;
+ }
+
+ /* add support to customize thumb size */
+
+ public CrystalSeekbar setThumbSize(float thumbSize) {
+ final float scale = getContext().getResources().getDisplayMetrics().density;
+ this.thumbHeight = thumbSize * scale + 0.5f;
+ this.thumbWidth = thumbSize * scale + 0.5f;
+ this.thumbDiameter = (thumbSize * scale + 0.5f) / 2;
+ return this;
+ }
+
+ public CrystalSeekbar setDataType(int dataType) {
+ this.dataType = dataType;
+ return this;
+ }
+
+ public CrystalSeekbar setPosition(int pos) {
+ this.nextPosition = pos;
+ return this;
+ }
+
+ public void setOnSeekbarChangeListener(OnSeekbarChangeListener onSeekbarChangeListener) {
+ this.onSeekbarChangeListener = onSeekbarChangeListener;
+ if (this.onSeekbarChangeListener != null) {
+ this.onSeekbarChangeListener.valueChanged(getSelectedMinValue());
+ }
+ }
+
+ public void setOnSeekbarFinalValueListener(OnSeekbarFinalValueListener onSeekbarFinalValueListener) {
+ this.onSeekbarFinalValueListener = onSeekbarFinalValueListener;
+ }
+
+ public Thumb getPressedThumb() {
+ return pressedThumb;
+ }
+
+ public RectF getThumbRect() {
+ return rectThumb;
+ }
+
+ public float getCornerRadius() {
+ return cornerRadius;
+ }
+
+ public float getMinValue() {
+ return minValue;
+ }
+
+ public float getMaxValue() {
+ return maxValue;
+ }
+
+ public float getMinStartValue() {
+ return minStartValue;
+ }
+
+ public float getSteps() {
+ return steps;
+ }
+
+ public int getBarColor() {
+ return barColor;
+ }
+
+ public int getBarHighlightColor() {
+ return barHighlightColor;
+ }
+
+ public int getLeftThumbColor() {
+ return thumbColor;
+ }
+
+ public int getLeftThumbColorPressed() {
+ return thumbColorPressed;
+ }
+
+ public Drawable getLeftDrawable() {
+ return thumbDrawable;
+ }
+
+ public Drawable getLeftDrawablePressed() {
+ return thumbDrawablePressed;
+ }
+
+ public int getDataType() {
+ return dataType;
+ }
+
+ public int getPosition() {
+ return this.position;
+ }
+
+ public float getThumbWidth() {
+ return (thumb != null) ? thumb.getWidth() : thumbWidth;
+ }
+
+ public float getThumbHeight() {
+ return (thumb != null) ? thumb.getHeight() : thumbHeight;
+ }
+
+ protected float getThumbDiameter() {
+ return (thumbDiameter > 0) ? thumbDiameter : getResources().getDimension(R.dimen.thumb_width);
+ }
+
+ public float getBarHeight() {
+ return _barHeight > 0 ? _barHeight : (thumbHeight * 0.5f) * 0.3f;
+ }
+
+ public float getDiameter(final TypedArray typedArray) {
+ return typedArray.getDimensionPixelSize(R.styleable.CrystalSeekbar_thumb_diameter, getResources().getDimensionPixelSize(R.dimen.thumb_height));
+ }
+
+ protected boolean isSeekBarTouchEnabled(final TypedArray typedArray){
+ return typedArray.getBoolean(R.styleable.CrystalSeekbar_seek_bar_touch_enabled, false);
+ }
+
+ public float getBarPadding() {
+ return thumbWidth * 0.5f;
+ }
+
+ public Number getSelectedMinValue() {
+ double nv = normalizedMinValue;
+ if (steps > 0 && steps <= ((absoluteMaxValue) / 2)) {
+ float stp = steps / (absoluteMaxValue - absoluteMinValue) * 100;
+ double half_step = stp / 2;
+ double mod = nv % stp;
+ if (mod > half_step) {
+ nv = nv - mod;
+ nv = nv + stp;
+ } else {
+ nv = nv - mod;
+ }
+ } else {
+ if (steps != NO_STEP)
+ throw new IllegalStateException("steps out of range " + steps);
+ }
+
+ nv = (position == Position.LEFT) ? nv : Math.abs(nv - maxValue);
+ return formatValue(normalizedToValue(nv));
+ }
+
+ public Number getSelectedMaxValue() {
+
+ double nv = normalizedMaxValue;
+ if (steps > 0 && steps <= (absoluteMaxValue / 2)) {
+ float stp = steps / (absoluteMaxValue - absoluteMinValue) * 100;
+ double half_step = stp / 2;
+ double mod = nv % stp;
+ if (mod > half_step) {
+ nv = nv - mod;
+ nv = nv + stp;
+ } else {
+ nv = nv - mod;
+ }
+ } else {
+ if (steps != NO_STEP)
+ throw new IllegalStateException("steps out of range " + steps);
+ }
+
+ return formatValue(normalizedToValue(nv));
+ }
+
+ public void apply() {
+
+ // reset normalize min and max value
+ //normalizedMinValue = 0d;
+ //normalizedMaxValue = 100d;
+
+ thumbWidth = (thumb != null) ? thumb.getWidth() : getResources().getDimension(R.dimen.thumb_width);
+ thumbHeight = (thumb != null) ? thumb.getHeight() : getResources().getDimension(R.dimen.thumb_height);
+
+ barHeight = (thumbHeight * 0.5f) * 0.3f;
+ barPadding = thumbWidth * 0.5f;
+
+ // set min start value
+ if (minStartValue <= minValue) {
+ minStartValue = 0;
+ setNormalizedMinValue(minStartValue);
+ } else if (minStartValue > maxValue) {
+ minStartValue = maxValue;
+ setNormalizedMinValue(minStartValue);
+ } else {
+ //minStartValue = (long)getSelectedMinValue();
+ if (nextPosition != position) {
+ minStartValue = (float) Math.abs(normalizedMaxValue - normalizedMinValue);
+ }
+ if (minStartValue > minValue) {
+ minStartValue = Math.min(minStartValue, absoluteMaxValue);
+ minStartValue -= absoluteMinValue;
+ minStartValue = minStartValue / (absoluteMaxValue - absoluteMinValue) * 100;
+ }
+
+ setNormalizedMinValue(minStartValue);
+ position = nextPosition;
+
+ }
+
+ invalidate();
+ if (onSeekbarChangeListener != null) {
+ onSeekbarChangeListener.valueChanged(getSelectedMinValue());
+ }
+ }
+
+ //////////////////////////////////////////
+ // PROTECTED METHODS
+ //////////////////////////////////////////
+
+ protected Bitmap getBitmap(Drawable drawable) {
+ return (drawable != null) ? ((BitmapDrawable) drawable).getBitmap() : null;
+ }
+
+ protected float getCornerRadius(final TypedArray typedArray) {
+ return typedArray.getFloat(R.styleable.CrystalSeekbar_corner_radius, 0f);
+ }
+
+ protected float getMinValue(final TypedArray typedArray) {
+ return typedArray.getFloat(R.styleable.CrystalSeekbar_min_value, 0f);
+ }
+
+ protected float getMaxValue(final TypedArray typedArray) {
+ return typedArray.getFloat(R.styleable.CrystalSeekbar_max_value, 100f);
+ }
+
+ protected float getMinStartValue(final TypedArray typedArray) {
+ return typedArray.getFloat(R.styleable.CrystalSeekbar_min_start_value, minValue);
+ }
+
+ protected float getSteps(final TypedArray typedArray) {
+ return typedArray.getFloat(R.styleable.CrystalSeekbar_steps, NO_STEP);
+ }
+
+ protected float getBarHeight(final TypedArray typedArray){
+ return typedArray.getDimensionPixelSize(R.styleable.CrystalSeekbar_bar_height, 0);
+ }
+
+ protected int getBarColorMode(final TypedArray typedArray) {
+ return typedArray.getInt(R.styleable.CrystalSeekbar_bar_color_mode, ColorMode.SOLID);
+ }
+
+ protected int getBarColor(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalSeekbar_bar_color, Color.GRAY);
+ }
+
+ protected int getBarGradientStart(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalSeekbar_bar_gradient_start, Color.GRAY);
+ }
+
+ protected int getBarGradientEnd(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalSeekbar_bar_gradient_end, Color.DKGRAY);
+ }
+
+ protected int getBarHighlightColorMode(final TypedArray typedArray) {
+ return typedArray.getInt(R.styleable.CrystalSeekbar_bar_highlight_color_mode, ColorMode.SOLID);
+ }
+
+ protected int getBarHighlightColor(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalSeekbar_bar_highlight_color, Color.BLACK);
+ }
+
+ protected int getBarHighlightGradientStart(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalSeekbar_bar_highlight_gradient_start, Color.DKGRAY);
+ }
+
+ protected int getBarHighlightGradientEnd(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalSeekbar_bar_highlight_gradient_end, Color.BLACK);
+ }
+
+ protected int getThumbColor(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalSeekbar_thumb_color, Color.BLACK);
+ }
+
+ protected int getThumbColorPressed(final TypedArray typedArray) {
+ return typedArray.getColor(R.styleable.CrystalSeekbar_thumb_color_pressed, Color.DKGRAY);
+ }
+
+ protected Drawable getThumbDrawable(final TypedArray typedArray) {
+ return typedArray.getDrawable(R.styleable.CrystalSeekbar_thumb_image);
+ }
+
+ protected Drawable getThumbDrawablePressed(final TypedArray typedArray) {
+ return typedArray.getDrawable(R.styleable.CrystalSeekbar_thumb_image_pressed);
+ }
+
+ protected int getDataType(final TypedArray typedArray) {
+ return typedArray.getInt(R.styleable.CrystalSeekbar_data_type, DataType.INTEGER);
+ }
+
+ protected final int getPosition(final TypedArray typedArray) {
+ final int pos = typedArray.getInt(R.styleable.CrystalSeekbar_position, Position.LEFT);
+
+ normalizedMinValue = (pos == Position.LEFT) ? normalizedMinValue : normalizedMaxValue;
+ return pos;
+ }
+
+ protected void setupBar(final Canvas canvas, final Paint paint, final RectF rect) {
+ rect.left = barPadding;
+ rect.top = 0.5f * (getHeight() - barHeight);
+ rect.right = getWidth() - barPadding;
+ rect.bottom = 0.5f * (getHeight() + barHeight);
+
+ paint.setStyle(Paint.Style.FILL);
+ paint.setAntiAlias(true);
+
+ if (barColorMode == ColorMode.SOLID) {
+ paint.setColor(barColor);
+ drawBar(canvas, paint, rect);
+
+ } else {
+ paint.setShader(
+ new LinearGradient(rect.left, rect.bottom, rect.right, rect.top,
+ barGradientStart,
+ barGradientEnd,
+ Shader.TileMode.MIRROR)
+ );
+
+ drawBar(canvas, paint, rect);
+
+ paint.setShader(null);
+ }
+ }
+
+ protected void drawBar(final Canvas canvas, final Paint paint, final RectF rect) {
+ canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
+ }
+
+ protected void setupHighlightBar(final Canvas canvas, final Paint paint, final RectF rect) {
+ if (position == Position.RIGHT) {
+ rect.left = normalizedToScreen(normalizedMinValue) + (getThumbWidth() / 2);
+ rect.right = getWidth() - (getThumbWidth() / 2);
+ } else {
+ rect.left = getThumbWidth() / 2;
+ rect.right = normalizedToScreen(normalizedMinValue) + (getThumbWidth() / 2);
+ }
+
+ paint.setStyle(Paint.Style.FILL);
+ paint.setAntiAlias(true);
+
+ if (barHighlightColorMode == ColorMode.SOLID) {
+ paint.setColor(barHighlightColor);
+ drawHighlightBar(canvas, paint, rect);
+
+ } else {
+ paint.setShader(
+ new LinearGradient(rect.left, rect.bottom, rect.right, rect.top,
+ barHighlightGradientStart,
+ barHighlightGradientEnd,
+ Shader.TileMode.MIRROR)
+ );
+
+ drawHighlightBar(canvas, paint, rect);
+
+ paint.setShader(null);
+ }
+ }
+
+ protected void drawHighlightBar(final Canvas canvas, final Paint paint, final RectF rect) {
+ canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
+ }
+
+ protected void setupLeftThumb(final Canvas canvas, final Paint paint, final RectF rect) {
+
+ thumbColor = (Thumb.MIN.equals(pressedThumb)) ? thumbColorPressed : thumbColorNormal;
+ paint.setColor(thumbColor);
+
+ rectThumb.left = normalizedToScreen(normalizedMinValue);
+ rectThumb.right = Math.min(rectThumb.left + (getThumbWidth() / 2) + barPadding, getWidth());
+
+ rectThumb.top = 0f;
+ rectThumb.bottom = thumbHeight;
+
+ if (thumb != null) {
+ Bitmap lThumb = (Thumb.MIN.equals(pressedThumb)) ? thumbPressed : thumb;
+ drawLeftThumbWithImage(canvas, paint, rectThumb, lThumb);
+ } else {
+ drawLeftThumbWithColor(canvas, paint, rectThumb);
+ }
+ }
+
+ protected void drawLeftThumbWithColor(final Canvas canvas, final Paint paint, final RectF rect) {
+ canvas.drawOval(rect, paint);
+ }
+
+ protected void drawLeftThumbWithImage(final Canvas canvas, final Paint paint, final RectF rect, final Bitmap image) {
+ canvas.drawBitmap(image, rect.left, rect.top, paint);
+ }
+
+ protected void trackTouchEvent(MotionEvent event) {
+ final int pointerIndex = event.findPointerIndex(mActivePointerId);
+ try {
+ final float x = event.getX(pointerIndex);
+
+ if (Thumb.MIN.equals(pressedThumb)) {
+ setNormalizedMinValue(screenToNormalized(x));
+ }
+ } catch (Exception ignored) {
+ }
+ }
+
+ protected void touchDown(final float x, final float y) {
+
+ }
+
+ protected void touchMove(final float x, final float y) {
+
+ }
+
+ protected void touchUp(final float x, final float y) {
+
+ }
+
+ protected int getMeasureSpecWith(int widthMeasureSpec) {
+ int width = 200;
+ if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
+ width = MeasureSpec.getSize(widthMeasureSpec);
+ }
+ return width;
+ }
+
+ protected int getMeasureSpecHeight(int heightMeasureSpec) {
+ int height = Math.round(thumbHeight);
+ if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
+ height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
+ }
+ return height;
+ }
+
+ protected final void log(Object object) {
+ Log.d("CRS=>", String.valueOf(object));
+ }
+
+ //////////////////////////////////////////
+ // PRIVATE METHODS
+ //////////////////////////////////////////
+
+ private void setMinStartValue() {
+ if (minStartValue > minValue && minStartValue < maxValue) {
+ minStartValue = Math.min(minStartValue, absoluteMaxValue);
+ minStartValue -= absoluteMinValue;
+ minStartValue = minStartValue / (absoluteMaxValue - absoluteMinValue) * 100;
+ setNormalizedMinValue(minStartValue);
+ }
+ }
+
+ private Thumb evalPressedThumb(float touchX) {
+ Thumb result = null;
+
+ boolean minThumbPressed = isInThumbRange(touchX, normalizedMinValue);
+ if (seekBarTouchEnabled || minThumbPressed) {
+ // if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids "stalling" the thumbs in a corner, not being able to drag them apart anymore.
+ result = Thumb.MIN;
+ }
+ return result;
+ }
+
+ private boolean isInThumbRange(float touchX, double normalizedThumbValue) {
+ float thumbPos = normalizedToScreen(normalizedThumbValue);
+ float left = thumbPos - (getThumbWidth() / 2);
+ float right = thumbPos + (getThumbWidth() / 2);
+ float x = touchX - (getThumbWidth() / 2);
+ if (thumbPos > (getWidth() - thumbWidth)) x = touchX;
+ return (x >= left && x <= right);
+ }
+
+ private void onStartTrackingTouch() {
+ mIsDragging = true;
+ }
+
+ private void onStopTrackingTouch() {
+ mIsDragging = false;
+ }
+
+ private float normalizedToScreen(double normalizedCoord) {
+
+ float width = getWidth() - (barPadding * 2);
+ return (float) normalizedCoord / 100f * width;
+ }
+
+ private double screenToNormalized(float screenCoord) {
+ double width = getWidth();
+
+ if (width <= 2 * barPadding) {
+ // prevent division by zero, simply return 0.
+ return 0d;
+ } else {
+ width = width - (barPadding * 2);
+ double result = screenCoord / width * 100d;
+ result = result - (barPadding / width * 100d);
+ result = Math.min(100d, Math.max(0d, result));
+ return result;
+
+ }
+ }
+
+ private void setNormalizedMinValue(double value) {
+ normalizedMinValue = Math.max(0d, Math.min(100d, Math.min(value, normalizedMaxValue)));
+ invalidate();
+ }
+
+ private void setNormalizedMaxValue(double value) {
+ normalizedMaxValue = Math.max(0d, Math.min(100d, Math.max(value, normalizedMinValue)));
+ invalidate();
+ }
+
+ private double normalizedToValue(double normalized) {
+ double val = normalized / 100 * (maxValue - minValue);
+ val = (position == Position.LEFT) ? val + minValue : val;
+ return val;
+ }
+
+ private void attemptClaimDrag() {
+ if (getParent() != null) {
+ getParent().requestDisallowInterceptTouchEvent(true);
+ }
+ }
+
+ private Number formatValue(T value) throws IllegalArgumentException {
+ final Double v = (Double) value;
+ if (dataType == DataType.LONG) {
+ return v.longValue();
+ }
+ if (dataType == DataType.DOUBLE) {
+ return v;
+ }
+ if (dataType == DataType.INTEGER) {
+ return Math.round(v);
+ }
+ if (dataType == DataType.FLOAT) {
+ return v.floatValue();
+ }
+ if (dataType == DataType.SHORT) {
+ return v.shortValue();
+ }
+ if (dataType == DataType.BYTE) {
+ return v.byteValue();
+ }
+ throw new IllegalArgumentException("Number class '" + value.getClass().getName() + "' is not supported");
+ }
+
+ //////////////////////////////////////////
+ // OVERRIDE METHODS
+ //////////////////////////////////////////
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // prevent render is in edit mode
+ if (isInEditMode()) return;
+
+ // setup bar
+ setupBar(canvas, _paint, _rect);
+
+ // setup seek bar active range line
+ setupHighlightBar(canvas, _paint, _rect);
+
+ // draw left thumb
+ setupLeftThumb(canvas, _paint, _rect);
+
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(getMeasureSpecWith(widthMeasureSpec), getMeasureSpecHeight(heightMeasureSpec));
+ }
+
+ /**
+ * Handles thumb selection and movement. Notifies listener callback on certain events.
+ */
+ @Override
+ public synchronized boolean onTouchEvent(MotionEvent event) {
+
+ if (!isEnabled())
+ return false;
+
+
+ final int action = event.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = event.getPointerId(event.getPointerCount() - 1);
+ pointerIndex = event.findPointerIndex(mActivePointerId);
+ float mDownMotionX = event.getX(pointerIndex);
+
+ pressedThumb = evalPressedThumb(mDownMotionX);
+
+ if (pressedThumb == null) return super.onTouchEvent(event);
+
+ touchDown(event.getX(pointerIndex), event.getY(pointerIndex));
+ setPressed(true);
+ invalidate();
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ attemptClaimDrag();
+
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (pressedThumb != null) {
+
+ if (mIsDragging) {
+ touchMove(event.getX(pointerIndex), event.getY(pointerIndex));
+ trackTouchEvent(event);
+ }
+
+ if (onSeekbarChangeListener != null) {
+ onSeekbarChangeListener.valueChanged(getSelectedMinValue());
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mIsDragging) {
+ trackTouchEvent(event);
+ onStopTrackingTouch();
+ setPressed(false);
+ touchUp(event.getX(pointerIndex), event.getY(pointerIndex));
+ if (onSeekbarFinalValueListener != null) {
+ onSeekbarFinalValueListener.finalValue(getSelectedMinValue());
+ }
+ } else {
+ // Touch up when we never crossed the touch slop threshold
+ // should be interpreted as a tap-seek to that location.
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ onStopTrackingTouch();
+ }
+
+ pressedThumb = null;
+ invalidate();
+ if (onSeekbarChangeListener != null) {
+ onSeekbarChangeListener.valueChanged(getSelectedMinValue());
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ //final int index = event.getPointerCount() - 1;
+ // final int index = ev.getActionIndex();
+ /*mDownMotionX = event.getX(index);
+ mActivePointerId = event.getPointerId(index);
+ invalidate();*/
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP:
+ /*onSecondaryPointerUp(event);*/
+ invalidate();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsDragging) {
+ onStopTrackingTouch();
+ setPressed(false);
+ touchUp(event.getX(pointerIndex), event.getY(pointerIndex));
+ }
+ invalidate(); // see above explanation
+ break;
+ }
+
+ return true;
+
+ }
+}
diff --git a/android/crystalrangeseekbar/src/main/res/values/attr.xml b/android/crystalrangeseekbar/src/main/res/values/attr.xml
new file mode 100644
index 0000000..302e7c5
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/res/values/attr.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/crystalrangeseekbar/src/main/res/values/dimens.xml b/android/crystalrangeseekbar/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..3d9eaa5
--- /dev/null
+++ b/android/crystalrangeseekbar/src/main/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+ 15dp
+ 15dp
+
+ 70dp
+ 70dp
+
diff --git a/android/crystalrangeseekbar/src/test/java/com/example/crystalrangeseekbar/ExampleUnitTest.java b/android/crystalrangeseekbar/src/test/java/com/example/crystalrangeseekbar/ExampleUnitTest.java
new file mode 100644
index 0000000..a173384
--- /dev/null
+++ b/android/crystalrangeseekbar/src/test/java/com/example/crystalrangeseekbar/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package com.example.crystalrangeseekbar;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/android/jesster2k10reactnativerangeslider.iml b/android/jesster2k10reactnativerangeslider.iml
index b5da8a7..5600d1b 100644
--- a/android/jesster2k10reactnativerangeslider.iml
+++ b/android/jesster2k10reactnativerangeslider.iml
@@ -32,7 +32,7 @@
-
+
@@ -128,8 +128,8 @@
+
-
@@ -165,5 +165,7 @@
+
+
\ No newline at end of file
diff --git a/android/settings.gradle b/android/settings.gradle
index 8b13789..fa706c4 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -1 +1,2 @@
-
+include ':rangeseekbar'
+project(':rangeseekbar').projectDir = new File('crystalrangeseekbar')
diff --git a/android/src/main/java/com/jesster2k10reactnativerangeslider/RNRangeSliderManager.kt b/android/src/main/java/com/jesster2k10reactnativerangeslider/RNRangeSliderManager.kt
index d969586..eadd97a 100644
--- a/android/src/main/java/com/jesster2k10reactnativerangeslider/RNRangeSliderManager.kt
+++ b/android/src/main/java/com/jesster2k10reactnativerangeslider/RNRangeSliderManager.kt
@@ -6,114 +6,114 @@ import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
-class RangeSliderManager: SimpleViewManager() {
+class RNRangeSliderManager: SimpleViewManager() {
override fun getName(): String {
return "RNRangeSlider"
}
- override fun createViewInstance(reactContext: ThemedReactContext): RangeSliderView {
- return RangeSliderView(reactContext as Context)
+ override fun createViewInstance(reactContext: ThemedReactContext): RNRangeSliderView {
+ return RNRangeSliderView(reactContext as Context)
}
@ReactProp(name = "tintColorBetweenHandles")
- fun setTintColorBetweenHandles(view: RangeSliderView, color: String?) {
+ fun setTintColorBetweenHandles(view: RNRangeSliderView, color: String?) {
if (color == null) return
view.run { setTintColorBetweenHandles(color) }
}
@ReactProp(name = "tintColor")
- fun setTintColor(view: RangeSliderView, color: String?) {
+ fun setTintColor(view: RNRangeSliderView, color: String?) {
if (color == null) return
view.run { setTintColor(color) }
}
@ReactProp(name = "handleColor")
- fun setHandleColor(view: RangeSliderView, color: String?) {
+ fun setHandleColor(view: RNRangeSliderView, color: String?) {
if (color == null) return
view.run { setHandleColor(color) }
}
@ReactProp(name = "handlePressedColor")
- fun setHandlePressedColor(view: RangeSliderView, color: String?) {
+ fun setHandlePressedColor(view: RNRangeSliderView, color: String?) {
if (color == null) return
view.run { setHandlePressedColor(color) }
}
@ReactProp(name = "step")
- fun setStep(view: RangeSliderView, step: Int?) {
+ fun setStep(view: RNRangeSliderView, step: Int?) {
if (step == null) return
view.run { setStep(step.toFloat()) }
}
@ReactProp(name = "leftHandlePressedColor")
- fun setLeftHandlePressedColor(view: RangeSliderView, color: String?) {
+ fun setLeftHandlePressedColor(view: RNRangeSliderView, color: String?) {
if (color == null) return
view.run { setLeftHandlePressedColor(color) }
}
@ReactProp(name = "rightHandlePressedColor")
- fun setRightHandlePressedColor(view: RangeSliderView, color: String?) {
+ fun setRightHandlePressedColor(view: RNRangeSliderView, color: String?) {
if (color == null) return
view.run { setRightHandlePressedColor(color) }
}
@ReactProp(name = "leftHandleColor")
- fun setLeftHandleColor(view: RangeSliderView, color: String?) {
+ fun setLeftHandleColor(view: RNRangeSliderView, color: String?) {
if (color == null) return
view.run { setLeftHandleColor(color) }
}
@ReactProp(name = "rightHandleColor")
- fun setRightHandleColor(view: RangeSliderView, color: String?) {
+ fun setRightHandleColor(view: RNRangeSliderView, color: String?) {
if (color == null) return
view.run { setRightHandleColor(color) }
}
@ReactProp(name = "handleDiameter")
- fun setCornerRadius(view: RangeSliderView, radius: Int?) {
+ fun setCornerRadius(view: RNRangeSliderView, radius: Int?) {
if (radius == null) return
view.run { setCornerRadius(radius.toFloat()) }
}
@ReactProp(name = "min")
- fun setMin(view: RangeSliderView, min: Int?) {
+ fun setMin(view: RNRangeSliderView, min: Int?) {
if (min == null) return
view.run { setMinValue(min.toFloat()) }
}
@ReactProp(name = "max")
- fun setMax(view: RangeSliderView, max: Int?) {
+ fun setMax(view: RNRangeSliderView, max: Int?) {
if (max == null) return
view.run { setMaxValue(max.toFloat()) }
}
@ReactProp(name = "minStart")
- fun setMinStart(view: RangeSliderView, min: Int?) {
+ fun setMinStart(view: RNRangeSliderView, min: Int?) {
if (min == null) return
view.run { setMinStartValue(min.toFloat()) }
}
@ReactProp(name = "maxStart")
- fun maxStart(view: RangeSliderView, max: Int?) {
+ fun maxStart(view: RNRangeSliderView, max: Int?) {
if (max == null) return
view.run { setMaxStartValue(max.toFloat()) }
}
@ReactProp(name = "fixGap")
- fun setFixGap(view: RangeSliderView, gap: Int?) {
+ fun setFixGap(view: RNRangeSliderView, gap: Int?) {
if (gap == null) return
view.run { setFixGap(gap.toFloat()) }
}
@ReactProp(name = "suffix")
- fun setSuffix(view: RangeSliderView, suffix: String?) {
+ fun setSuffix(view: RNRangeSliderView, suffix: String?) {
if (suffix == null) return
view.suffix = suffix
}
@ReactProp(name = "prefix")
- fun setPrefix(view: RangeSliderView, prefix: String?) {
+ fun setPrefix(view: RNRangeSliderView, prefix: String?) {
if (prefix == null) return
view.prefix = prefix
}
diff --git a/android/src/main/java/com/jesster2k10reactnativerangeslider/RNRangeSliderView.kt b/android/src/main/java/com/jesster2k10reactnativerangeslider/RNRangeSliderView.kt
index 0f05116..690055e 100644
--- a/android/src/main/java/com/jesster2k10reactnativerangeslider/RNRangeSliderView.kt
+++ b/android/src/main/java/com/jesster2k10reactnativerangeslider/RNRangeSliderView.kt
@@ -10,7 +10,7 @@ import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerModule
import java.lang.StringBuilder
-class RangeSliderView(context: Context): LinearLayout(context), OnRangeSeekbarChangeListener {
+class RNRangeSliderView(context: Context): LinearLayout(context), OnRangeSeekbarChangeListener {
private var rangeSeekBar: CrystalRangeSeekbar
private var minTextView: TextView?
private var maxTextView: TextView?
@@ -18,13 +18,13 @@ class RangeSliderView(context: Context): LinearLayout(context), OnRangeSeekbarCh
private var minValue: Float = 0f
private var maxValue: Float = 100f
- var suffix: String? = null
+ var suffix: String? = ""
set(value) {
field = value
updateText()
}
- var prefix: String? = null
+ var prefix: String? = ""
set(value) {
field = value
updateText()
@@ -34,6 +34,8 @@ class RangeSliderView(context: Context): LinearLayout(context), OnRangeSeekbarCh
inflate(context, R.layout.range_slider, this)
rangeSeekBar = findViewById(R.id.range_seek_bar)
rangeSeekBar.setOnRangeSeekbarChangeListener(this)
+ rangeSeekBar.setMinStartValue(minValue)
+ rangeSeekBar.setMaxStartValue(maxValue)
minTextView = findViewById(R.id.range_seek_bar_min)
maxTextView = findViewById(R.id.range_seek_bar_max)
diff --git a/android/src/main/java/com/jesster2k10reactnativerangeslider/ReactNativeRangeSliderPackage.kt b/android/src/main/java/com/jesster2k10reactnativerangeslider/ReactNativeRangeSliderPackage.kt
index 7ba004b..01cb9a9 100644
--- a/android/src/main/java/com/jesster2k10reactnativerangeslider/ReactNativeRangeSliderPackage.kt
+++ b/android/src/main/java/com/jesster2k10reactnativerangeslider/ReactNativeRangeSliderPackage.kt
@@ -16,7 +16,7 @@ class ReactNativeRangeSliderPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List> {
return mutableListOf(
- RangeSliderManager()
+ RNRangeSliderManager()
)
}
}
diff --git a/android/src/main/res/layout/range_slider.xml b/android/src/main/res/layout/range_slider.xml
index f1173ae..3202639 100644
--- a/android/src/main/res/layout/range_slider.xml
+++ b/android/src/main/res/layout/range_slider.xml
@@ -1,36 +1,45 @@
-
-
+
+
-
-
-
+ android:textAlignment="viewStart"
+ android:gravity="start"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+
+
diff --git a/example/android/ReactNativeRangeSliderExample.iml b/example/android/ReactNativeRangeSliderExample.iml
index 809c50d..798e385 100644
--- a/example/android/ReactNativeRangeSliderExample.iml
+++ b/example/android/ReactNativeRangeSliderExample.iml
@@ -8,7 +8,7 @@
-
+
diff --git a/example/android/app/app.iml b/example/android/app/app.iml
index 3214e18..dc0a85a 100644
--- a/example/android/app/app.iml
+++ b/example/android/app/app.iml
@@ -102,7 +102,6 @@
-
@@ -140,5 +139,6 @@
+
\ No newline at end of file
diff --git a/example/android/build.gradle b/example/android/build.gradle
index b762df8..bf54708 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -13,6 +13,8 @@ buildscript {
}
dependencies {
classpath("com.android.tools.build:gradle:3.4.2")
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+ classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
index e4e1cca..475262b 100644
--- a/example/android/settings.gradle
+++ b/example/android/settings.gradle
@@ -4,3 +4,6 @@ include ':app'
include ':jesster2k10reactnativerangeslider'
project(':jesster2k10reactnativerangeslider').projectDir = new File(rootProject.projectDir, '../../android')
+
+include ':rangeseekbar'
+project(':rangeseekbar').projectDir = new File('../../android/crystalrangeseekbar')
diff --git a/example/src/AndroidApp.tsx b/example/src/AndroidApp.tsx
index 4b35885..0fbe7b7 100644
--- a/example/src/AndroidApp.tsx
+++ b/example/src/AndroidApp.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { StyleSheet, View, Text, SafeAreaView } from 'react-native';
+import { StyleSheet, View, Text, SafeAreaView, ScrollView } from 'react-native';
import RangeSlider from '@jesster2k10/react-native-range-slider';
const AndroidApp = () => {
@@ -10,23 +10,82 @@ const AndroidApp = () => {
return (
-
- Basic Slider (no customization)
-
-
-
- Customized Colors
-
-
+
+
+ Basic Slider
+
+
+
+ Custom Range & Step
+
+
+
+ Customized Colors
+
+
+
+ Prefixed Slider
+
+
+
+ Suffixed Slider
+
+
+
+ Customized Handles
+
+
+
);
};
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 2a95605..68c756f 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -40,6 +40,18 @@ export default function App() {
style={styles.slider}
/>
+
+ Prefixed Slider
+
+
Suffixed Slider
= ({
min,
@@ -14,9 +13,7 @@ const RangeSlider: React.FC = ({
step,
handleBorderColor,
handleColor,
- handleDiameter = Platform.select({
- android: 10,
- }),
+ handleDiameter,
handleBorderWidth,
type = 'range',
selectedMaximum,
@@ -44,7 +41,7 @@ const RangeSlider: React.FC = ({
minStartValue,
maxStartValue,
fixGap,
- style,
+ style = {},
cornerRadius,
}: RangeSliderProps) => {
const defaultStyle = {
@@ -72,8 +69,10 @@ const RangeSlider: React.FC = ({
fixGap={fixGap}
leftHandlePressedColor={leftHandlePressedColor}
rightHandlePressedColor={rightHandlePressedColor}
- handlePressedColor={handlePressedColor}
+ handlePressedColor={handlePressedColor || handleColor}
cornerRadius={cornerRadius}
+ prefix={prefix}
+ suffix={suffix}
style={[defaultStyle, style]}
/>
);
@@ -114,4 +113,14 @@ const RangeSlider: React.FC = ({
}
};
+RangeSlider.defaultProps = {
+ min: 0,
+ max: 100,
+ step: 1,
+ type: 'range',
+ selectedMinimum: 0,
+ selectedMaximum: 100,
+ tintColor: '#DCDCDC', // extra light gray
+};
+
export default RangeSlider;
diff --git a/src/types.ts b/src/types.ts
index 49615a0..3b0a115 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -48,8 +48,8 @@ export interface AndroidOnlyRangeSliderProps {
leftHandlePressedColor?: string;
rightHandlePressedColor?: string;
handlePressedColor?: string;
- minStartValue?: string;
- maxStartValue?: string;
+ minStartValue?: number;
+ maxStartValue?: number;
fixGap?: number;
cornerRadius?: number;
}