π A lightweight dropdown popup spinner with fully customizable arrow and animations.
Add below codes to your root build.gradle
file (not your module build.gradle file).
allprojects {
repositories {
mavenCentral()
}
}
And add a dependency code to your module's build.gradle
file.
dependencies {
implementation "com.github.skydoves:powerspinner:1.1.7"
}
Snapshots of the current development version of PowerSpinner are available, which track the latest versions.
repositories {
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
Add following XML namespace inside your XML layout file.
xmlns:app="http://schemas.android.com/apk/res-auto"
Here is a basic example of implementing PowerSpinnerView
in XML layout.
Basically We can use PowerSpinnerView
like a TextView
.
We can set the text using the hint
and textColorHint
attributes when the item is not selected.
<com.skydoves.powerspinner.PowerSpinnerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/md_blue_200"
android:gravity="center"
android:hint="Question 1"
android:padding="10dp"
android:textColor="@color/white_93"
android:textColorHint="@color/white_70"
android:textSize="14.5sp"
app:spinner_arrow_gravity="end"
app:spinner_arrow_padding="8dp"
app:spinner_divider_color="@color/white_70"
app:spinner_divider_show="true"
app:spinner_divider_size="0.4dp"
app:spinner_item_array="@array/questions"
app:spinner_popup_animation="dropdown"
app:spinner_popup_background="@color/background800"
app:spinner_popup_elevation="14dp" />
We can create an instance of PowerSpinnerView
using the builder class.
val mySpinnerView = createPowerSpinnerView(this) {
setSpinnerPopupWidth(300)
setSpinnerPopupHeight(350)
setArrowPadding(6)
setArrowAnimate(true)
setArrowAnimationDuration(200L)
setArrowGravity(SpinnerGravity.START)
setArrowTint(ContextCompat.getColor(this@MainActivity, R.color.md_blue_200))
setSpinnerPopupAnimation(SpinnerAnimation.BOUNCE)
setShowDivider(true)
setDividerColor(Color.WHITE)
setDividerSize(2)
setLifecycleOwner(this@MainActivity)
}
If the PowerSpinnerView
is clicked, the spinner popup will be shown and when an item is selected,
the spinner popup will be dismissed. We can also show and dismiss manually using below methods.
powerSpinnerView.show() // show the spinner popup
powerSpinnerView.dismiss() // dismiss the spinner popup
// If the popup is not showing, shows the spinner popup menu.
// If the popup is already showing, dismiss the spinner popup menu.
powerSpinnerView.showOrDismiss()
And we can customize the basic actions of the spinner popup using the following ways.
// the spinner popup will not be shown when clicked.
powerSpinnerView.setOnClickListener { }
// the spinner popup will not be dismissed when item selected.
powerSpinnerView.dismissWhenNotifiedItemSelected = false
Interface definition for a callback to be invoked when selected item on the spinner popup.
setOnSpinnerItemSelectedListener<String> { oldIndex, oldItem, newIndex, newText ->
toast("$text selected!")
}
Here is the Java way.
powerSpinnerView.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<String>() {
@Override public void onItemSelected(int oldIndex, @Nullable String oldItem, int newIndex, String newItem) {
toast(item + " selected!");
}
});
We can select an item manually or initially using the below method.
This method must be invoked after the setItems
is called.
powerSpinnerView.selectItemByIndex(4)
We can store and restore the selected position in the past automatically.
If we select an item, the position that we selected will be selected again automatically on the next inflation.
Just use the below method or attribute.
powerSpinnerView.preferenceName = "country"
Or we can set it using attribute in XML layout.
app:spinner_preference_name="country"
We can remove the stored position data on an item or clear all of the data on your application.
spinnerView.removePersistedData("country")
spinnerView.clearAllPersistedData()
We can customize the showing and dimsmiss animation.
SpinnerAnimation.NORMAL
SpinnerAnimation.DROPDOWN
SpinnerAnimation.FADE
SpinnerAnimation.BOUNCE
NORMAL | DROPDOWN | FADE | BOUNCE |
---|---|---|---|
We can use our customized adapter and binds to the PowerSpinnerView
.
The PowerSpinnerView
provides the spinner popup's recyclerview via getSpinnerRecyclerView
method.
Here is a sample of the customized adapter.
val adapter = IconSpinnerAdapter(spinnerView)
spinnerView.setSpinnerAdapter(adapter)
spinnerView.getSpinnerRecyclerView().layoutManager = GridLayoutManager(context, 2)
Basically, this library provides a customized adapter.
We should create an instance of the IconSpinnerAdapter
and call setItems
using a list of IconSpinnerItem
.
spinnerView.apply {
setSpinnerAdapter(IconSpinnerAdapter(this))
setItems(
arrayListOf(
IconSpinnerItem(text = "Item1", iconRes = R.drawable.unitedstates),
IconSpinnerItem(text = "Item2", iconRes = R.drawable.southkorea)))
getSpinnerRecyclerView().layoutManager = GridLayoutManager(context, 2)
selectItemByIndex(0) // select an item initially.
lifecycleOwner = this@MainActivity
}
Here is the java way.
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>();
iconSpinnerItems.add(new IconSpinnerItem("item1", contextDrawable(R.drawable.unitedstates)));
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(spinnerView);
spinnerView.setSpinnerAdapter(iconSpinnerAdapter);
spinnerView.setItems(iconSpinnerItems);
spinnerView.selectItemByIndex(0);
spinnerView.setLifecycleOwner(this);
Here is a way to customize your adapter for binding the PowerSpinnerView
.
Firstly, create a new adapter and viewHolder extending RecyclerView.Adapter
and PowerSpinnerInterface<T>
.
You shoud override spinnerView
, onSpinnerItemSelectedListener
fields and setItems
, notifyItemSelected
methods.
class MySpinnerAdapter(
powerSpinnerView: PowerSpinnerView
) : RecyclerView.Adapter<MySpinnerAdapter.MySpinnerViewHolder>(),
PowerSpinnerInterface<MySpinnerItem> {
override var index: Int = powerSpinnerView.selectedIndex
override val spinnerView: PowerSpinnerView = powerSpinnerView
override var onSpinnerItemSelectedListener: OnSpinnerItemSelectedListener<MySpinnerItem>? = null
On the customized adapter, you must call spinnerView.notifyItemSelected
method when your item is clicked or the spinner item should be changed.
override fun onBindViewHolder(holder: MySpinnerViewHolder, position: Int) {
holder.itemView.setOnClickListener {
notifyItemSelected(position)
}
}
// we must call the spinnerView.notifyItemSelected method to let PowerSpinnerView know about changed information.
override fun notifyItemSelected(index: Int) {
if (index == NO_SELECTED_INDEX) return
val oldIndex = this.index
this.index = index
this.spinnerView.notifyItemSelected(index, this.spinnerItems[index].text)
this.onSpinnerItemSelectedListener?.onItemSelected(
oldIndex = oldIndex,
oldItem = oldIndex.takeIf { it != NO_SELECTED_INDEX }?.let { spinnerItems[oldIndex] },
newIndex = index,
newItem = item
)
}
And we can listen to the selected item's information.
spinnerView.setOnSpinnerItemSelectedListener<MySpinnerItem> {
oldIndex, oldItem, newIndex, newItem -> toast(newItem.text)
}
We can use PowerSpinner on the PreferenceScreen
xml for implementing setting screens.
And add a dependency code to your module's build.gradle
file.
dependencies {
implementation "androidx.preference:preference-ktx:1.1.1"
}
And create your preference xml file like below.
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.preference.Preference
android:title="Account preferences"
app:iconSpaceReserved="false" />
<com.skydoves.powerspinner.PowerSpinnerPreference
android:key="question1"
android:title="Question1"
app:spinner_arrow_gravity="end"
app:spinner_arrow_padding="8dp"
app:spinner_divider_color="@color/white_70"
app:spinner_divider_show="true"
app:spinner_divider_size="0.2dp"
app:spinner_item_array="@array/questions1"
app:spinner_popup_animation="dropdown"
app:spinner_popup_background="@color/background900"
app:spinner_popup_elevation="14dp" />
You don't need to set preferenceName
attribute, and OnSpinnerItemSelectedListener
should be set on PowerSpinnerPreference
. You can reference this sample codes.
val countySpinnerPreference = findPreference<PowerSpinnerPreference>("country")
countySpinnerPreference?.setOnSpinnerItemSelectedListener<IconSpinnerItem> { oldIndex, oldItem, newIndex, newItem ->
Toast.makeText(requireContext(), newItem.text, Toast.LENGTH_SHORT).show()
}
Dialog, PopupWindow and etc.. have memory leak issue if not dismissed before activity or fragment are destroyed.
But Lifecycles are now integrated with the Support Library since Architecture Components 1.0 Stable released.
So we can solve the memory leak issue so easily.
Just use setLifecycleOwner
method. Then dismiss
method will be called automatically before activity or fragment would be destroyed.
.setLifecycleOwner(lifecycleOwner)
Attributes | Type | Default | Description |
---|---|---|---|
spinner_arrow_drawable | Drawable | arrow | arrow drawable. |
spinner_arrow_show | Boolean | true | sets the visibility of the arrow. |
spinner_arrow_gravity | SpinnerGravity | end | the gravity of the arrow. |
spinner_arrow_padding | Dimension | 2dp | padding of the arrow. |
spinner_arrow_tint | Color | None | tint color of the arrow. |
spinner_arrow_animate | Boolean | true | show arrow rotation animation when showing. |
spinner_arrow_animate_duration | Integer | 250 | the duration of the arrow animation. |
spinner_divider_show | Boolean | true | show the divider of the popup items. |
spinner_divider_size | Dimension | 0.5dp | sets the height of the divider. |
spinner_divider_color | Color | White | sets the color of the divider. |
spinner_popup_width | Dimension | spinnerView's width | the width of the popup. |
spinner_popup_height | Dimension | WRAP_CONTENT | the height of the popup. |
spinner_popup_background | Color | spinnerView's background | the background color of the popup. |
spinner_popup_animation | SpinnerAnimation | NORMAL | the spinner animation when showing. |
spinner_popup_animation_style | Style Resource | -1 | sets the customized animation style. |
spinner_popup_elevation | Dimension | 4dp | the elevation size of the popup. |
spinner_item_array | String Array Resource | null | sets the items of the popup. |
spinner_dismiss_notified_select | Boolean | true | sets dismiss when the popup item is selected. |
spinner_debounce_duration | Integer | 150 | A duration of the debounce for showOrDismiss. |
spinner_preference_name | String | null | saves and restores automatically the selected position. |
Support it by joining stargazers for this repository. β
And follow me for my next creations! π€©
Copyright 2019 skydoves (Jaewoong Eum)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the L