Skip to content

Commit

Permalink
- Fixed issue where onclick wouldn't fire on generic types
Browse files Browse the repository at this point in the history
  • Loading branch information
MattJAshworth committed Jul 1, 2024
1 parent 8103ef1 commit bed4c82
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 69 deletions.
118 changes: 116 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,116 @@
# <img src ="https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Kotlin_logo_2021.svg/1920px-Kotlin_logo_2021.svg.png" width=48> SpinnerTools
Advanced spinner views for Android including bottomsheets & searching
# <img src ="https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Kotlin_logo_2021.svg/1920px-Kotlin_logo_2021.svg.png" width=48> SpinnerTools [![](https://jitpack.io/v/MattJAshworth/SpinnerTools.svg)](https://jitpack.io/#MattJAshworth/SpinnerTools)
Spinner View for Android with bottomsheet and searching built in

# Screenshots
<img src="/screenshots/spinnertools.gif" width="25%" alt="Spinner Tools Demo">

# Download
## build.gradle (Groovy)
Add to your project level `build.gradle`
```Java
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
```
Add a dependency to your module `build.gradle`:
```Java
dependencies {
implementation 'com.github.MattJAshworth:SpinnerTools:1.2'
}
```

## build.gradle.kts (Kotlin DSL)

Add to `settings.gradle.kts`
```Kotlin
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://www.jitpack.io" ) }

}
}
```

Add a dependency to your `build.gradle.kts`:
```Kotlin
dependencies {
implementation("com.github.MattJAshworth:SpinnerTools:1.2")
}
```
# Implementation
Add the `xyz.mattjashworth.spinnertools.sheet.Spinner` to your layout XML file.

Below are all the YesNoButton's xml attributes. You cannot currently set these programmatically.
```XML
<xyz.mattjashworth.spinnertools.sheet.Spinner
android:id="@+id/app_spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
app:DismissWhenSelected="true"
app:Title="Select Person"
app:Searchable="true"
app:DisplayMember="name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
```

When binding to the view element specify the type. For example `ExampleObject`
```Kotlin
data class ExampleObject(
val name: String,
val age: String
)
```
```Kotlin
val searchSpinner = findViewById<Spinner<ExampleObject>>(R.id.app_spinner)
```

Set the spinner items by calling `setItems()`
```Kotlin
searchSpinner.setItems(data)
```

Attach an item selected listener to return the item selected by the user. The listener returns the type specified. In this case `ExampleObject`.
```Kotlin
searchSpinner.setOnItemSelectedListener(object : Spinner.OnItemSelectedListener<ExampleObject> {
override fun onItemSelected(model: ExampleObject) {
Snackbar.make(rootView, model.name, Snackbar.LENGTH_LONG).show()
}

})
```

# License
```
MIT License
Copyright (c) 2024 Matt J Ashworth
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,46 @@ package xyz.mattjashworth.spinnertools.sheet

import android.content.Context
import android.content.res.TypedArray
import android.graphics.Color
import android.os.Build
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.LinearLayout
import androidx.annotation.AttrRes
import android.widget.PopupWindow
import androidx.annotation.RequiresApi
import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.view.marginLeft
import androidx.core.view.marginRight
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.textfield.TextInputLayout
import com.google.gson.Gson
import com.google.gson.JsonParser
import xyz.mattjashworth.spinnertools.R
import xyz.mattjashworth.spinnertools.sheet.adapters.SearchSpinnerAdapter



@RequiresApi(Build.VERSION_CODES.N_MR1)
class Spinner<T>(context: Context, attributeSet: AttributeSet) : LinearLayout(context, attributeSet) {

private lateinit var selectedItem: EditText
private var card: CardView
private var items = ArrayList<T>()

private var SelectedObject: T? = null
private var selectedObject: T? = null

private var DisplayMember: String? = null
private var Title = "Select Item"
private var SelectedMode: Mode = Mode.Sheet
private var Searchable = false
private var DismissWhenSelected = false
private var displayMember: String? = null
private var title = "Select Item"
private var searchable = false
private var dismissWhenSelected = false

private lateinit var textInputLayout: TextInputLayout

private enum class Mode {Sheet, Dropdown}
private var type: Any? = null

private var onItemSelectedListener: OnItemSelectedListener<T>? = null
Expand All @@ -45,25 +51,27 @@ class Spinner<T>(context: Context, attributeSet: AttributeSet) : LinearLayout(co
val root = inflate(context, R.layout.spinner, this)

val ta: TypedArray = getContext().obtainStyledAttributes(attributeSet, R.styleable.Spinner)
DisplayMember = ta.getString(R.styleable.Spinner_DisplayMember)!!
SelectedMode = Mode.values()[ta.getInt(R.styleable.Spinner_Mode, 0)]
Title = ta.getString(R.styleable.Spinner_Title)!!
Searchable = ta.getBoolean(R.styleable.Spinner_Searchable, false)
DismissWhenSelected = ta.getBoolean(R.styleable.Spinner_DismissWhenSelected, false)
displayMember = ta.getString(R.styleable.Spinner_DisplayMember) ?: ""
title = ta.getString(R.styleable.Spinner_Title) ?: ""
searchable = ta.getBoolean(R.styleable.Spinner_Searchable, false)
dismissWhenSelected = ta.getBoolean(R.styleable.Spinner_DismissWhenSelected, false)
ta.recycle()



selectedItem = findViewById<EditText>(R.id.tv_spinner_selected)
val layout = findViewById<TextInputLayout>(R.id.tIL)
textInputLayout = findViewById<TextInputLayout>(R.id.tIL)
card = findViewById(R.id.card_spinner)
layout.hint = Title
textInputLayout.hint = title

setChildListener(rootView, OnClickListener {
val s = SpinnerSheet<T>(context, items, Title, DisplayMember)
if (SelectedObject != null)
s.setSelectedObject(SelectedObject!!)
val s = SpinnerSheet<T>(context, items, title, displayMember, searchable)
if (selectedObject != null)
s.setSelectedObject(selectedObject!!)
s.setOnItemClickListener(object : SpinnerSheet.OnSearchSpinnerClickListener<T> {
override fun onClick(position: Int, model: T) {

if (DismissWhenSelected)
if (dismissWhenSelected)
s.dismiss()

val gson = Gson()
Expand All @@ -72,6 +80,7 @@ class Spinner<T>(context: Context, attributeSet: AttributeSet) : LinearLayout(co
if (model is String) {

selectedItem.setText(model)

} else {

val obj = JsonParser.parseString(jsonStr).asJsonObject
Expand All @@ -81,14 +90,16 @@ class Spinner<T>(context: Context, attributeSet: AttributeSet) : LinearLayout(co

var res = ""

if (!DisplayMember.isNullOrEmpty()) res = obj.get(DisplayMember).asString
if (!displayMember.isNullOrEmpty()) res =
obj.get(displayMember).asString
else res = obj.get(keys.max()).asString

SelectedObject = model
selectedObject = model
selectedItem.setText(res)

onItemSelectedListener?.onItemSelected(model)
}

onItemSelectedListener?.onItemSelected(model)
}

})
Expand Down Expand Up @@ -122,11 +133,11 @@ class Spinner<T>(context: Context, attributeSet: AttributeSet) : LinearLayout(co
}

fun setDisplayMember(id: String) {
DisplayMember = id
displayMember = id
}

fun setTitle(title: String) {
Title = title
this.title = title
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package xyz.mattjashworth.spinnertools.sheet

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import android.widget.TextView
import androidx.core.widget.addTextChangedListener
Expand All @@ -16,7 +17,7 @@ import xyz.mattjashworth.spinnertools.R
import xyz.mattjashworth.spinnertools.sheet.adapters.SearchSpinnerAdapter
import java.lang.reflect.Modifier

internal class SpinnerSheet<T>(context: Context, items: ArrayList<T>, title: String, displayMember: String?) {
internal class SpinnerSheet<T>(context: Context, items: ArrayList<T>, title: String, displayMember: String?, searchable: Boolean) {


private var onSearchSpinnerClickListener: OnSearchSpinnerClickListener<T>? = null
Expand All @@ -35,6 +36,7 @@ internal class SpinnerSheet<T>(context: Context, items: ArrayList<T>, title: Str
val spinnerSheetView = layoutInflater.inflate(R.layout.bottomsheet_spinner, null)

val search = spinnerSheetView.findViewById<EditText>(R.id.et_bottomsheet_search)
if (!searchable) search.visibility = View.GONE
val sheetTitle = spinnerSheetView.findViewById<TextView>(R.id.tv_sheet_title)
sheetTitle.text = title

Expand Down
27 changes: 0 additions & 27 deletions SpinnerTools/src/main/res/layout/dropdown_spinner.xml

This file was deleted.

1 change: 0 additions & 1 deletion SpinnerTools/src/main/res/layout/spinner.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
android:id="@+id/card_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="10dp"
android:background="@android:color/transparent"
app:contentPadding="10dp"
app:cardUseCompatPadding="true"
Expand Down
4 changes: 0 additions & 4 deletions SpinnerTools/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
<declare-styleable name="Spinner">
<attr name="DisplayMember" format="string" />
<attr name="Searchable" format="boolean"/>
<attr name="Mode" format="enum">
<enum name="Sheet" value="0"/>
<enum name="Dropdown" value="1"/>
</attr>
<attr name="Title" format="string"/>
<attr name="DismissWhenSelected" format="boolean"/>
</declare-styleable>
Expand Down
6 changes: 3 additions & 3 deletions sample/src/main/java/xyz/mattjashworth/sample/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ class MainActivity : AppCompatActivity() {
)


val s = findViewById<Spinner<ExampleObject>>(R.id.app_spinner)
s.setItems(data)
s.setOnItemSelectedListener(object : Spinner.OnItemSelectedListener<ExampleObject> {
val searchSpinner = findViewById<Spinner<ExampleObject>>(R.id.app_spinner)
searchSpinner.setItems(data)
searchSpinner.setOnItemSelectedListener(object : Spinner.OnItemSelectedListener<ExampleObject> {
override fun onItemSelected(model: ExampleObject) {
Snackbar.make(rootView, model.name, Snackbar.LENGTH_LONG).show()
}
Expand Down
9 changes: 6 additions & 3 deletions sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
android:id="@+id/app_spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:DisplayMember="ID"
app:Mode="Sheet"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
app:DismissWhenSelected="true"
app:Title="Select Remedy Code"
app:Title="Select Person"
app:Searchable="true"
app:DisplayMember="name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Expand Down
Binary file added screenshots/spinnertools.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit bed4c82

Please sign in to comment.