diff --git a/app/build.gradle b/app/build.gradle index 3bf43b0..ae97862 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,11 @@ apply plugin: 'com.android.application' - apply plugin: 'kotlin-android' - apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 28 defaultConfig { - applicationId "com.github.saran2020.sliderating" + applicationId "com.github.saran2020.app" minSdkVersion 21 targetSdkVersion 28 versionCode 1 @@ -22,8 +20,7 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(':sliderating') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 808fbe2..da782c8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools" package="com.github.saran2020.app"> (R.id.slide_rating) + val ratingTextView = findViewById(R.id.rating_text) + + ratingView.callback = object : SlideRatingView.RatingChangeCallback { + override fun onRatingChanged(previous: Float, new: Float) { + ratingTextView.text = "$new" + } + } + } +} diff --git a/app/src/main/java/com/github/saran2020/sliderating/MainActivity.kt b/app/src/main/java/com/github/saran2020/sliderating/MainActivity.kt deleted file mode 100644 index 39b4f87..0000000 --- a/app/src/main/java/com/github/saran2020/sliderating/MainActivity.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.saran2020.sliderating - -import android.support.v7.app.AppCompatActivity -import android.os.Bundle - -class MainActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - } -} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 980638c..7913900 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,19 +1,26 @@ - + + + android:text="@integer/initial_rating"/> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9fadc60..8864d5d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ SlideRating + 2.5 + 5 diff --git a/build.gradle b/build.gradle index fbbb51a..c5b77c0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.21' + ext.kotlin_version = '1.3.30' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e735bb9..2713932 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Apr 07 02:07:40 IST 2019 +#Sun Apr 21 03:00:25 IST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/settings.gradle b/settings.gradle index e7b4def..40b4b54 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app' +include ':app', ':sliderating' diff --git a/sliderating/.gitignore b/sliderating/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/sliderating/.gitignore @@ -0,0 +1 @@ +/build diff --git a/sliderating/build.gradle b/sliderating/build.gradle new file mode 100644 index 0000000..debd019 --- /dev/null +++ b/sliderating/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 28 + + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/sliderating/proguard-rules.pro b/sliderating/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/sliderating/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/sliderating/src/main/AndroidManifest.xml b/sliderating/src/main/AndroidManifest.xml new file mode 100644 index 0000000..659c80d --- /dev/null +++ b/sliderating/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/sliderating/src/main/java/com/github/saran2020/sliderating/SlideRatingView.kt b/sliderating/src/main/java/com/github/saran2020/sliderating/SlideRatingView.kt new file mode 100644 index 0000000..fd456e8 --- /dev/null +++ b/sliderating/src/main/java/com/github/saran2020/sliderating/SlideRatingView.kt @@ -0,0 +1,237 @@ +package com.github.saran2020.sliderating + +import android.content.Context +import android.support.v7.widget.LinearLayoutCompat +import android.util.AttributeSet +import android.util.Log +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import android.view.ViewGroup +import android.widget.ImageView +import java.util.* +import kotlin.math.ceil +import kotlin.math.floor + + +open class SlideRatingView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : LinearLayoutCompat(context, attrs, defStyleAttr) { + + private var mIsDragging = false + private var mScaledTouchSlop = 0 + private var mTouchDownX = 0f + private var ratingSpace = 0.0f + private var maxRating = 5 + private var currentRating = 0f + set(value) { + if (value > maxRating) { + throw IllegalArgumentException("Rating cannot be more than max rating") + } else if (value < 0) { + throw IllegalArgumentException("Rating cannot be less than 0") + } + + val previousRating = field + val newRating = roundOffRating(value) + + if (previousRating == newRating) { + return + } + + field = newRating + callback?.onRatingChanged(previousRating, field) + + Log.d("buggy_bug", "current rating $currentRating") + + for (i in 0..childCount) { + val childAt = getChildAt(i) + if (childAt != null) { + setRatingResource(childAt as ImageView, i) + } + } + } + + var callback: RatingChangeCallback? = null + + private fun roundOffRating(value: Float): Float { + val decimal = value - floor(value) + for (mutableEntry in assetMap) { + if (mutableEntry.key >= decimal) { + return floor(value) + mutableEntry.key + } + } + + return value + } + + // Todo: Make this drawable based instead of resource Id based + // (Will make it more convenient for the developer) + private var assetMap: SortedMap = sortedMapOf( + 0f to R.drawable.ic_star_empty, + 0.5f to R.drawable.ic_star_half, + 1f to R.drawable.ic_star_full + ) + + init { + attrs?.let { + val typedArray = context.obtainStyledAttributes( + it, + R.styleable.SlideRatingView, 0, 0 + ) + + ratingSpace = typedArray.getDimension( + R.styleable.SlideRatingView_rating_space, 0f + ) + maxRating = typedArray.getInt( + R.styleable.SlideRatingView_max_rating, 5 + ) + currentRating = typedArray.getFloat( + R.styleable.SlideRatingView_initial_rating, 0f + ) + + typedArray.recycle() + } + + isClickable = true + isFocusable = true + mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop + addViews() + } + + + override fun onTouchEvent(event: MotionEvent?): Boolean { + if (!isEnabled) { + return false + } + + when (event?.action) { + MotionEvent.ACTION_DOWN -> { + if (isInScrollingContainer()) { + mTouchDownX = event.x + } else { + startDrag(event) + } + } + + MotionEvent.ACTION_MOVE -> { + if (mIsDragging) { + trackTouchEvent(event) + } else { + val x = event.x + if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) { + startDrag(event) + } + } + } + + MotionEvent.ACTION_UP -> { + if (mIsDragging) { + trackTouchEvent(event) + onStopTrackingTouch() + isPressed = false + } 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() + } + } + + MotionEvent.ACTION_CANCEL -> { + if (mIsDragging) { + onStopTrackingTouch() + isPressed = false + } + } + } + + return true + } + + private fun startDrag(event: MotionEvent) { + isPressed = true + onStartTrackingTouch() + trackTouchEvent(event) + } + + + private fun isInScrollingContainer(): Boolean { + var p = parent + while (p != null && p is ViewGroup) { + if (p.shouldDelayChildPressedState()) { + return true + } + p = p.parent + } + + return false + } + + protected fun trackTouchEvent(event: MotionEvent) { + val x = Math.round(event.x) + + for (i in 0..childCount) { + val child: View = getChildAt(i) ?: return + + if (x > child.left && x < (child.left + child.width)) { + + val dragOnView = x - child.left + val ratioCross = dragOnView / child.width.toFloat() + + currentRating = i + ratioCross + } + } + } + + private fun onStartTrackingTouch() { + mIsDragging = true + } + + private fun onStopTrackingTouch() { + mIsDragging = false + } + + + private fun addViews() { + for (i in 0 until maxRating) { + addRatingViews(i) + } + } + + private fun addRatingViews(pos: Int) { + + val imageView = getImageView(pos) + addView(imageView) + } + + private fun getImageView(pos: Int): ImageView { + val imageView = ImageView(context) + val layoutParams = LayoutParams(0, LayoutParams.MATCH_PARENT, 1f) + layoutParams.marginEnd = if (pos != (maxRating - 1)) ratingSpace.toInt() else 0 + + imageView.layoutParams = layoutParams + setRatingResource(imageView, pos) + + return imageView + } + + private fun setRatingResource(imageView: ImageView, index: Int) { + val pos = index + 1 + + imageView.setImageResource( + when { + pos <= floor(currentRating) -> assetMap[1f]!! + pos == ceil(currentRating).toInt() -> { + val decimal = currentRating - floor(currentRating) + assetMap[decimal]!! + } + else -> assetMap[0f]!! + } + ) + } + + interface RatingChangeCallback { + fun onRatingChanged(previous: Float, new: Float) + } +} \ No newline at end of file diff --git a/sliderating/src/main/res/drawable/ic_star_empty.xml b/sliderating/src/main/res/drawable/ic_star_empty.xml new file mode 100644 index 0000000..42a8c38 --- /dev/null +++ b/sliderating/src/main/res/drawable/ic_star_empty.xml @@ -0,0 +1,5 @@ + + + diff --git a/sliderating/src/main/res/drawable/ic_star_full.xml b/sliderating/src/main/res/drawable/ic_star_full.xml new file mode 100644 index 0000000..1f36bd3 --- /dev/null +++ b/sliderating/src/main/res/drawable/ic_star_full.xml @@ -0,0 +1,5 @@ + + + diff --git a/sliderating/src/main/res/drawable/ic_star_half.xml b/sliderating/src/main/res/drawable/ic_star_half.xml new file mode 100644 index 0000000..daf7570 --- /dev/null +++ b/sliderating/src/main/res/drawable/ic_star_half.xml @@ -0,0 +1,5 @@ + + + diff --git a/sliderating/src/main/res/values/values.xml b/sliderating/src/main/res/values/values.xml new file mode 100644 index 0000000..5568bd8 --- /dev/null +++ b/sliderating/src/main/res/values/values.xml @@ -0,0 +1,10 @@ + + SlideRating + + + + + + + +