When using a Spinner with all default settings, I found some weird bugs when using the S Pen:
- If you open the spinner and hover the S Pen at the bottom of the list, so it scrolls to the bottom, and then lift the S Pen up (so that the "hover circle" disappears), the list jumps back to the top
- If you open the spinner and hover the S Pen at the bottom of the list, so it scrolls to the bottom, and then select an option, sometimes (intermittently - maybe about 1 in 4 times) it ignores the selected option and just jumps back to the top
I am testing on a tablet device in landscape orientation, the device is running Android 9 but I have done some tests on other devices and seems to be the same.
For comparison I tried using an app I use regularly which makes heavy use of spinners - "Packing List" by dotnetideas. This app was last updated in 2019 and has target SDK 27. You can easily test the spinners in the app by going into the settings and adding multiple items of luggage, then trying to edit the luggage on any packing list item. I found the S Pen works fine with this app, and it doesn't have the bugs described above, so there must be some way to work around it. That's not an open source app so I can't get ideas from their source code. I tried changing my target SDK to 27, but didn't make any difference.
Here is my code for what seems to me to be a completely vanilla implementation of spinner with all default settings - and still has these same "S Pen bugs" which are not reproducible in Packing List.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.penpoc">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SPenControlExperiment">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow();
}
}
}
main_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />
MainFragment.java:
public class MainFragment extends Fragment {
public static MainFragment newInstance() {
return new MainFragment();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.main_fragment, container, false);
Spinner spDefault = root.findViewById(R.id.spDefault);
ArrayAdapter<String> defaultSpinnerAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item);
defaultSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spDefault.setAdapter(defaultSpinnerAdapter);
defaultSpinnerAdapter.addAll(getExampleList());
defaultSpinnerAdapter.notifyDataSetChanged();
return root;
}
private List<String> getExampleList() {
List<String> list = new ArrayList<>();
list.add("Alpha");
list.add("Bravo");
list.add("Charlie");
list.add("Delta");
list.add("Echo");
list.add("Foxtrot");
list.add("Golf");
list.add("Hotel");
list.add("India");
list.add("Juliet");
list.add("Kilo");
list.add("Lima");
list.add("Mike");
list.add("November");
list.add("Oscar");
list.add("Papa");
list.add("Quebec");
list.add("Romeo");
return list;
}
}
main_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainFragment">
<!-- Add some text views to push the spinner further down the page, this is not strictly necessary -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="one" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="two" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="three" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="four" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="five" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="six" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="seven" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="eight" />
<Spinner
android:layout_width="350dp"
android:layout_height="wrap_content"
android:id="@+id/spDefault" />
</LinearLayout>
Gradle deps:
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
I exactly faced the same problem with the S Pen. For an old app, API 25
Spinners were working great, but not any more with API 30. The only way I can resolve it was to replaceSpinners byAutoCompleteTextViews. The drop down it generates seems to work correctly with the S Pen.A few adaptations are needed to make it look like a
Spinner:1. Make
AutoCompleteTextViewnot editableFrom the documentation, you can make it by adding
android:inputType="none". But, as said inMaterialAutoCompleteTextViewcode:So you have two options:
autoComplete.setInputType(InputType.TYPE_NULL)MaterialAutoCompleteTextViewhaving a workaround for this2. Handle clicks to show the drop down list
The default behavior with
AutoCompleteTextViewis to show suggestions by typing a few characters. So you have to force the drop down show when view gets focused.3. Style the drop down list
Contrary to
Spinnercalling bothgetView()andgetDropDownView()fromAdapter,AutoCompleteTextViewonly callsgetView(). So, to provide the correct view, you need to check the parent type ingetView()call:4. Style the
AutoCompleteTextViewOne last thing is to add the little caret at the end of the
AutoCompleteTextViewto make it look like aSpinner. You can use theandroid:drawableEndattribute in your XML layout file to handle this.5. Other tips
AutoCompleteTextViewdrop down list selection triggersAdapterView.OnItemClickListenerinstead ofAdapterView.OnItemSelectedListenerasSpinners do. Methods signatures are almost the same.