Created
June 23, 2019 16:07
-
-
Save joseprl89/1e131353e62cb40fa17f6e1ed53a89b3 to your computer and use it in GitHub Desktop.
5 star rating view Android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical" | |
android:padding="16dp" | |
tools:context=".MainActivity"> | |
<com.example.myapplication.views.RatingView | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content"/> | |
</LinearLayout> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.myapplication | |
import androidx.appcompat.app.AppCompatActivity | |
import android.os.Bundle | |
import android.view.MotionEvent | |
import android.view.View | |
import com.example.myapplication.views.StarView | |
import kotlinx.android.synthetic.main.activity_main.* | |
class MainActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<merge xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:orientation="horizontal" | |
tools:parentTag="android.widget.LinearLayout" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<com.example.myapplication.views.StarView | |
android:id="@+id/star_one" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_weight="1"/> | |
<com.example.myapplication.views.StarView | |
android:id="@+id/star_two" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_weight="1"/> | |
<com.example.myapplication.views.StarView | |
android:id="@+id/star_three" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_weight="1"/> | |
<com.example.myapplication.views.StarView | |
android:id="@+id/star_four" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_weight="1"/> | |
<com.example.myapplication.views.StarView | |
android:id="@+id/star_five" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_weight="1"/> | |
</merge> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.myapplication.views | |
import android.content.Context | |
import android.util.AttributeSet | |
import android.view.LayoutInflater | |
import android.widget.LinearLayout | |
import com.example.myapplication.R | |
import kotlinx.android.synthetic.main.rating_view_content.view.* | |
class RatingView: LinearLayout { | |
var selection: Selection = Selection.none() | |
val starViews: List<StarView> | |
get() = listOf(star_one, star_two, star_three, star_four, star_five) | |
constructor(context: Context?) : super(context) { | |
init(context) | |
} | |
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { | |
init(context) | |
} | |
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { | |
init(context) | |
} | |
private fun init(context: Context?) { | |
val inflater = LayoutInflater.from(context) | |
inflater.inflate(R.layout.rating_view_content, this) | |
for (i in 0 until starViews.count()) { | |
starViews[i].onStarClicked = onStarClickedForStar(i) | |
starViews[i].state = StarView.State.deselected | |
} | |
} | |
private fun onStarClickedForStar(i: Int): StarView.OnStarClicked { | |
return object:StarView.OnStarClicked{ | |
override fun starClicked(state: StarView.State) { | |
when (state) { | |
StarView.State.deselected -> selectUpTo(i) | |
StarView.State.selected -> selectUpTo(i) | |
else -> return | |
} | |
} | |
} | |
} | |
private fun selectUpTo(starIndex: Int) { | |
if (selection.shouldDeselectAll(starIndex)) { | |
selection = Selection.none() | |
starViews.forEach { it.state = StarView.State.deselected } | |
} else { | |
selection = Selection.selected(starIndex) | |
starViews.forEachIndexed { index, starView -> | |
starView.state = if (index <= starIndex) StarView.State.selected else StarView.State.deselected | |
} | |
} | |
} | |
@Suppress("DataClassPrivateConstructor") | |
data class Selection private constructor(val value: Int?) { | |
val isSelected: Boolean | |
get() = value != null | |
fun shouldDeselectAll(selectedIndex: Int): Boolean { | |
return isSelected && value == 0 && selectedIndex == 0 | |
} | |
companion object Factory { | |
fun none(): Selection { | |
return Selection(null) | |
} | |
fun selected(i: Int): Selection { | |
return Selection(i) | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.myapplication.views | |
import android.content.Context | |
import android.graphics.drawable.Drawable | |
import android.util.AttributeSet | |
import android.view.animation.AccelerateDecelerateInterpolator | |
import android.view.animation.Animation | |
import android.view.animation.Interpolator | |
import android.view.animation.ScaleAnimation | |
import android.widget.ImageSwitcher | |
import android.widget.ImageView | |
import com.example.myapplication.R | |
class StarView : ImageSwitcher { | |
enum class State { | |
selected, | |
deselected, | |
disabled | |
} | |
interface OnStarClicked { | |
fun starClicked(state: State) | |
} | |
var onStarClicked: OnStarClicked = object:OnStarClicked { | |
override fun starClicked(state: State) { | |
} | |
} | |
val State.drawable: Drawable? | |
get() = when (this) { | |
State.selected -> context.getDrawable(R.drawable.star_selected) | |
State.deselected -> context.getDrawable(R.drawable.star_deselected) | |
State.disabled -> context.getDrawable(R.drawable.star_disabled) | |
} | |
val State.scale: Float | |
get() = when (this) { | |
State.selected -> 1.0f | |
State.deselected -> 0.9f | |
State.disabled -> 0f | |
} | |
var state: State = State.deselected | |
set(value) { | |
val previousState = field | |
field = value | |
if (previousState != value) { | |
setImageDrawable(value.drawable) | |
// Recover 1.0f scale as the animation scale builds up on it, and it | |
// has been set in the init function already | |
scaleX = 1.0f | |
scaleY = 1.0f | |
val anim = ScaleAnimation( | |
previousState.scale, value.scale, | |
previousState.scale, value.scale, // Start and end values for the Y axis scaling | |
Animation.RELATIVE_TO_SELF, 0.5f, // Pivot point of X scaling | |
Animation.RELATIVE_TO_SELF, 0.5f // Pivot point of Y scaling | |
) | |
anim.fillAfter = true // Needed to keep the result of the animation | |
anim.duration = 300 | |
anim.interpolator = AccelerateDecelerateInterpolator() | |
startAnimation(anim) | |
} | |
} | |
constructor(context: Context?) : super(context) { init(context) } | |
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init(context) } | |
private fun init(context: Context?) { | |
state = State.deselected | |
setFactory { | |
ImageView(context).apply { | |
setImageDrawable(state.drawable) | |
} | |
} | |
setInAnimation(context, android.R.anim.fade_in) | |
setOutAnimation(context, android.R.anim.fade_out) | |
setOnClickListener { | |
onStarClicked.starClicked(state) | |
} | |
scaleX = state.scale | |
scaleY = state.scale | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment