首页 > 解决方案 > 如何使用微调器解决奇怪的 S Pen 错误?

问题描述

在使用Spinner所有默认设置时,我在使用 S Pen 时发现了一些奇怪的错误:

  1. 如果您打开微调器并将 S Pen 悬停在列表底部,使其滚动到底部,然后向上提起 S Pen(使“悬停圆圈”消失),列表跳回顶部
  2. 如果您打开微调器并将 S Pen 悬停在列表底部,使其滚动到底部,然后选择一个选项,有时(间歇性 - 可能大约 4 次中的 1 次)它会忽略所选选项并跳回到顶部

我正在横向的平板设备上进行测试,该设备运行的是 Android 9,但我已经在其他设备上进行了一些测试,并且似乎是相同的。

为了比较,我尝试使用我经常使用的应用程序,它大量使用微调器 - dotnetideas 的“装箱单”。该应用程序最后一次更新是在 2019 年,目标 SDK 为 27。您可以通过进入设置并添加多件行李,然后尝试编辑任何装箱单项目上的行李,轻松测试应用程序中的微调器。我发现 S Pen 可以很好地配合这个应用程序,而且它没有上面描述的错误,所以必须有一些方法可以解决它。那不是一个开源应用程序,所以我无法从他们的源代码中获得想法。我尝试将我的目标 SDK 更改为 27,但没有任何区别。

这是我的代码,在我看来,这是一个具有所有默认设置的完全普通的微调器实现 - 并且仍然具有这些相同的“S Pen 错误”,这些错误在装箱单中无法重现。

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 部门:

    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.3.0'

标签: androidsamsung-mobilestylus-pen

解决方案


我在使用 S Pen 时也遇到了同样的问题。对于旧应用程序,API 25 Spinners 运行良好,但 API 30 不再适用。我可以解决它的唯一方法是将 s 替换SpinnerAutoCompleteTextViews。它生成的下拉菜单似乎可以在 S Pen 上正常工作。

需要进行一些修改以使其看起来像Spinner

1.AutoCompleteTextView不可编辑

从文档中,您可以通过添加android:inputType="none". 但是,正如MaterialAutoCompleteTextView代码中所说:

// Due to a framework bug, setting android:inputType="none" on xml has no effect. Therefore,
// we check it here in case the autoCompleteTextView should be non-editable.

所以你有两个选择:

  • 以编程方式设置输入类型autoComplete.setInputType(InputType.TYPE_NULL)
  • 使用MaterialAutoCompleteTextView解决方法

2. 处理点击显示下拉列表

默认行为AutoCompleteTextView是通过键入几个字符来显示建议。因此,当视图聚焦时,您必须强制下拉显示。

autoComplete.setOnFocusChangeListener(new View.OnFocusChangeListener() {
  @Override
  void onFocusChange(View v, boolean hasFocus) {
    if (hasFocus) {
      autoComplete.showDropDown();
    }
  }
});

autoComplete.setOnDismissListener(new AutoCompleteTextView.OnDismissListener() {
  @Override
  void onDismiss() {
    // Force focus change after selection
    autoComplete.clearFocus()
  }
});

3.样式下拉列表

Spinner调用 bothgetView()getDropDownView()from不同AdapterAutoCompleteTextView只调用getView()。因此,要提供正确的视图,您需要检查getView()调用中的父类型:

@Override
public @NonNull View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
  // Workaround for AutoCompleteTextView using only this method
  if (parent instanceof ListView) {
    return getDropDownView(position, convertView, parent);
  } else {
    return super.getView(position, convertView, parent);
  }
}

4.风格AutoCompleteTextView

最后一件事是在末尾添加小插入符号,AutoCompleteTextView使其看起来像Spinner. 您可以使用android:drawableEndXML 布局文件中的属性来处理此问题。

5. 其他提示

  • AutoCompleteTextView下拉列表选择触发AdapterView.OnItemClickListener而不是AdapterView.OnItemSelectedListenerSpinners 那样。方法签名几乎相同。
  • 没有适当的方法来触发此侦听器的初始值。作为一种解决方法,您可以设置初始值,然后附加侦听器并将您的第一个验证逻辑放入其构造函数中。

推荐阅读