Skip to content

Commit

Permalink
Support remove states programmatically #6
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmigloz committed Mar 22, 2021
1 parent 97074f4 commit 8a9e2f2
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 156 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

## `2.0.0` (22/03/21)

- Update dependencies
- Support remove states programmatically #6
- Support nested scroll when used inside a RecyclerView or ViewPager #8
- Migrate to Kotlin #9
- Update dependencies

**Breaking change:**
- Minimum API requirements: API >= 21 (Android 5.0 - LOLLIPOP)
Expand Down
76 changes: 69 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Android library that provides a multi state switch view.

## Usage

Take a look at [the sample app](https://github.com/davidmigloz/multi-state-switch/tree/master/sample) to see a live example of the capabilities of the library.

#### Step 1

Add the JitPack repository to your `build.gradle ` file:
Expand Down Expand Up @@ -41,19 +43,79 @@ Use `MultiStateSwitch` view in your layout:
... />
```

TODO
Add states:

#### Attributes
```kotlin
switch.addStatesFromStrings(listOf("One", "Two", "Three"))
```

TODO
Use `StateStyle` to customize the style of the states:

#### API
```kotlin
switch.addStateFromString(
"Cold",
StateStyle.Builder()
.withSelectedBackgroundColor(Color.BLUE)
.withDisabledTextColor(Color.WHITE)
.build()
)
```

TODO
Use `State` object if you need to display different text depending on the state's state:

#### Callback
```kotlin
switch.addState(
State(
text = "ON",
selectedText = "OFF",
disabledText = "OFF"
)
)
```

TODO
#### Attributes

- `app:multistateswitch_background_color=[color|reference]`
- `app:multistateswitch_text_color=[color|reference]`
- `app:multistateswitch_text_size=[dimension|reference]`
- `app:multistateswitch_selected_state_index=[integer|reference]`
- `app:multistateswitch_selected_background_color=[color|reference]`
- `app:multistateswitch_selected_text_color=[color|reference]`
- `app:multistateswitch_selected_text_size=[dimension|reference]`
- `app:multistateswitch_disabled_state_enabled=[boolean|reference]`
- `app:multistateswitch_disabled_state_index=[integer|reference]`
- `app:multistateswitch_disabled_background_color=[color|reference]`
- `app:multistateswitch_disabled_text_color=[color|reference]`
- `app:multistateswitch_disabled_text_size=[dimension|reference]`
- `app:multistateswitch_max_number_states=[integer|reference]`

#### API

- `addState(state: State, stateStyle: StateStyle? = null)`: adds state to the switch.
- `addStates(states: List<State>, stateStyles: List<StateStyle>? = null)`: adds states to the switch and the displaying styles. If you provide styles, you have to provide them for every state.
- `addStateFromString(stateText: String, stateStyle: StateStyle? = null)`: adds state to the switch directly from a string. The text will be used for normal, selected and disabled state.
- `addStatesFromStrings(statesTexts: List<String>, stateStyles: List<StateStyle>? = null)`: adds states to the switch directly from a string and the displaying styles. If you provide styles, you have to provide them for every state. The texts will be used for normal, selected and disabled states.
- `replaceState(stateIndex: Int, state: State, stateStyle: StateStyle? = null)`: replaces state.
- `replaceStateFromString(stateIndex: Int, stateText: String)`: replaces state directly from a string. The text will be used for normal, selected and disabled states.
- `removeState(stateIndex: Int)`: removes an state.
- `selectState(index: Int, notifyStateListeners: Boolean = true)`: selects state in given index. If `notifyStateListeners` is `true` all the listeners will be notified about the new selected state.
- `getNumberStates(): Int`: returns number of states of the switch.
- `setMaxNumberStates(maxNumberStates: Int)`: Sets the max number of states. If you try to add a new state but the number of states is already `maxNumberStates` the state will be ignored. By default is `-1` which means that there is no restriction. This parameter is also used to determine how many states to show in the editor preview. If it is set to no limit, `3` will be rendered by default, if not the number of states drawn will match `maxNumberStates`.
- `getMaxNumberStates(): Int`: returns max number of states. By default is -1 which means that there is no restriction.
- `hasMaxNumberStates(): Boolean`: checks whether there is a limit in the number of states or not.
- `setTextTypeface(textTypeface: Typeface)`: sets typeface.
- `setPadding(left: Int, top: Int, right: Int, bottom: Int)`


#### Listener

To listen to state changes, you have to register a `StateListener`:

```kotlin
binding.defaultSwitch.addStateListener { stateIndex, state ->
// ...
}
```

## Contributing

Expand Down
Binary file modified docs/multi-state-switch.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ class MultiStateSwitch @JvmOverloads constructor(
if (hasMaxNumberStates() && getNumberStates() >= getMaxNumberStates()) return
states.add(state)
stateStyle?.run { statesStyles.put(getNumberStates() - 1, stateStyle) }
if(isAttachedToWindow){
populateView()
invalidate()
}
}

/**
Expand All @@ -164,7 +168,7 @@ class MultiStateSwitch @JvmOverloads constructor(
}

/**
* Adds state to the switch and the displaying style.
* Adds state to the switch directly from a string and the displaying style.
* The text will be used for normal, selected and disabled states.
*/
@JvmOverloads
Expand All @@ -173,7 +177,7 @@ class MultiStateSwitch @JvmOverloads constructor(
}

/**
* Adds states to the switch and the displaying styles.
* Adds states to the switch directly from a string and the displaying styles.
* If you provide styles, you have to provide them for every state.
* The texts will be used for normal, selected and disabled states.
*/
Expand All @@ -199,13 +203,29 @@ class MultiStateSwitch @JvmOverloads constructor(
}

/**
* Replaces state.
* Replaces state directly from a string.
* The text will be used for normal, selected and disabled states.
*/
fun replaceState(stateIndex: Int, stateText: String) {
fun replaceStateFromString(stateIndex: Int, stateText: String) {
replaceState(stateIndex, State(stateText))
}

/**
* Removes an state.
*/
fun removeState(stateIndex: Int) {
require(stateIndex < getNumberStates()) { "State index doesn't exist" }
states.removeAt(stateIndex)
statesStyles.remove(stateIndex)
if(stateIndex == currentStateIndex) {
currentStateIndex = if(stateIndex == 0) 0 else stateIndex - 1
}
if(isAttachedToWindow){
populateView()
invalidate()
}
}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
// Called when the view is first assigned a size, and again if the size changes for any reason
// All calculations related to positions, dimensions, and any other values must be done here (not in onDraw)
Expand Down Expand Up @@ -268,6 +288,7 @@ class MultiStateSwitch @JvmOverloads constructor(
* Populates the states that have to be drawn.
*/
private fun populateStates() {
statesSelectors.clear()
for (i in states.indices) {
try {
val selector = createStateSelector(i)
Expand Down Expand Up @@ -363,7 +384,7 @@ class MultiStateSwitch @JvmOverloads constructor(
/**
* Creates a bitmap from a view.
*/
fun createBitmapFromView(view: View): Bitmap {
private fun createBitmapFromView(view: View): Bitmap {
val returnedBitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(returnedBitmap)
view.background?.draw(canvas)
Expand Down Expand Up @@ -449,6 +470,7 @@ class MultiStateSwitch @JvmOverloads constructor(
* Calculate the bounds of the view and the centers of the states.
*/
private fun calculateBounds() {
statesCenters.clear()
// Calculate background bounds
val backgroundBounds = Rect().apply {
left = drawingArea.left + shadowStartEndOverflowPx
Expand Down Expand Up @@ -486,7 +508,7 @@ class MultiStateSwitch @JvmOverloads constructor(

@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
if (!isEnabled) return false
if (!isEnabled || states.isEmpty()) return false
val rawX = event.x
val normalizedX = getNormalizedX(event)
currentStateCenter.x = when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.davidmiguel.multistateswitch.StateListener
import com.davidmiguel.multistateswitch.StateStyle
import com.davidmiguel.multistateswitch.sample.databinding.ActivityMainBinding
import com.davidmiguel.multistateswitch.sample.viewpager.ViewPagerActivity
import kotlin.random.Random

class MainActivity : AppCompatActivity(), StateListener {

Expand All @@ -22,6 +23,7 @@ class MainActivity : AppCompatActivity(), StateListener {
setupDefaultSwitch()
setupDisabledSwitch()
setupCustomizedSwitch()
setupAddRemoveSwitch()
setupViewPagerBtn()
}

Expand All @@ -40,14 +42,24 @@ class MainActivity : AppCompatActivity(), StateListener {
}

private fun setupCustomizedSwitch() {
binding.customizedSwitch.addStateFromString("Cold", StateStyle.Builder()
.withSelectedBackgroundColor(Color.BLUE)
.build())
binding.customizedSwitch.addState(State("ON", "OFF", "OFF"), StateStyle.Builder()
.withTextColor(ContextCompat.getColor(this, R.color.colorPrimary))
.withDisabledBackgroundColor(Color.BLACK)
.withDisabledTextColor(Color.WHITE)
.build())
binding.customizedSwitch.addStateFromString(
"Cold",
StateStyle.Builder()
.withSelectedBackgroundColor(Color.BLUE)
.build()
)
binding.customizedSwitch.addState(
State(
text = "ON",
selectedText = "OFF",
disabledText = "OFF"
),
StateStyle.Builder()
.withTextColor(ContextCompat.getColor(this, R.color.colorPrimary))
.withDisabledBackgroundColor(Color.BLACK)
.withDisabledTextColor(Color.WHITE)
.build()
)
binding.customizedSwitch.addStateFromString("Hot", StateStyle.Builder()
.withSelectedBackgroundColor(Color.RED)
.build())
Expand All @@ -61,6 +73,19 @@ class MainActivity : AppCompatActivity(), StateListener {
binding.select3Btn.setOnClickListener { binding.customizedSwitch.selectState(2) }
}

private fun setupAddRemoveSwitch() {
binding.addRemoveSwitch.addStateListener(this)
binding.addBtn.setOnClickListener {
binding.addRemoveSwitch.addStateFromString("R${Random.nextInt(100)}")
}
binding.removeBtn.setOnClickListener {
val numStates = binding.addRemoveSwitch.getNumberStates()
if (numStates > 0) {
binding.addRemoveSwitch.removeState(numStates - 1)
}
}
}

private fun setupViewPagerBtn() {
binding.viewPagerBtn.setOnClickListener {
startActivity(Intent(this, ViewPagerActivity::class.java))
Expand Down
Loading

0 comments on commit 8a9e2f2

Please sign in to comment.