Android 中 spinner / AppCompatSpinner 文字颜色 和 显示样式 源码分析
Android 中 spinner / AppCompatSpinner 文字颜色 和 显示样式 源码分析
- 1 源码分析
- 1.1 AppCompatSpinner
- 1.2 Spinner/AbsSpinner
- 1.3 simple_spinner_item.xml
- 1.4 simple_spinner_dropdown_item.xml
- 1.4 自定义布局文件设置
- 2 Sample 工程
- 2.1 创建Sample工程
- 2.2 增加MainActivity
- 2.3 AndroidManifest.xml 配置缺省启动 MainActivity
- 2.4 在主布局中添加 AppCompatSpinner
- 3 自定义的 Spinner 项布局 (spinner_item.xml):
- 4 创建适配器并使用自定义布局
1 源码分析
我们想要自定义 spinner / AppCompatSpinner 的文字颜色和显示样式,我们先来看下spinner / AppCompatSpinner是如何来设置文字颜色 和 显示样式的。
1.1 AppCompatSpinner
我们看AppCompatSpinner 的源码,其父类是 android.widget.Spinner。
import android.widget.Spinner;[...此处省略部分代码]
public class AppCompatSpinner extends Spinner implements TintableBackgroundView {[...此处省略部分代码]
}
因此,我们可以统一自定义 spinner 的文字颜色和显示样式来达到目的。
1.2 Spinner/AbsSpinner
我们通过查找 R.layout 资源来看Spinner是如何设置的。在spinner 的父类 AbsSpinner 的源码中,发现如下代码:
final CharSequence[] entries = a.getTextArray(R.styleable.AbsSpinner_entries);if (entries != null) {final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context, R.layout.simple_spinner_item, entries);adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);setAdapter(adapter);}
适配器创建:
final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context, R.layout.simple_spinner_item, entries);
- 使用 ArrayAdapter 作为数据适配器
- 使用系统预定义的 simple_spinner_item 布局作为项视图
- 将 entries 数组作为数据源
下拉视图设置:
adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
适配器绑定:
setAdapter(adapter);
调用 setAdapter() 将适配器设置给 Spinner
代码中,使用了两个布局资源
- R.layout.simple_spinner_item: 系统预定义的简单文本项布局
- R.layout.simple_spinner_dropdown_item: 系统预定义的下拉项布局
1.3 simple_spinner_item.xml
我们来看 Android\Sdk\platforms\android-35\data\res\layout\simple_spinner_item.xml 源码,包含一个 TextView:
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml
**
** Copyright 2006, The Android Open Source Project
**
** 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 License.
*/
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1"style="?android:attr/spinnerItemStyle"android:singleLine="true"android:layout_width="match_parent"android:layout_height="wrap_content"android:ellipsize="marquee"android:textAlignment="inherit"/>
1.4 simple_spinner_dropdown_item.xml
Android\Sdk\platforms\android-35\data\res\layout\simple_spinner_dropdown_item.xml 源码,包含一个CheckedTextView
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml
**
** Copyright 2008, The Android Open Source Project
**
** 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 License.
*/
-->
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@android:id/text1"style="?android:attr/spinnerDropDownItemStyle"android:singleLine="true"android:layout_width="match_parent"android:layout_height="?android:attr/dropdownListPreferredItemHeight"android:ellipsize="marquee"/>
1.4 自定义布局文件设置
通过上面的分析,下面我们参考源码的方式通过自定义布局文件设置来实现目标。
2 Sample 工程
2.1 创建Sample工程
2.2 增加MainActivity
2.3 AndroidManifest.xml 配置缺省启动 MainActivity
<activityandroid:name=".MainActivity"android:exported="true" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>
2.4 在主布局中添加 AppCompatSpinner
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_vertical"android:orientation="horizontal"android:paddingVertical="8dp"android:id="@+id/main"android:spacing="16dp"><!-- 外层容器设置背景色 --><LinearLayoutandroid:layout_width="0dp"android:layout_height="32dp"android:layout_weight="2"android:gravity="center_vertical"><androidx.appcompat.widget.AppCompatSpinnerandroid:id="@+id/spinner"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_vertical"android:paddingStart="0dp"android:paddingEnd="0dp"android:spinnerMode="dropdown"android:textColor="?attr/colorOnSurface"android:textSize="14sp" /></LinearLayout></LinearLayout>
3 自定义的 Spinner 项布局 (spinner_item.xml):
在 res\layout 下创建布局:spinner_item.xml,为了演示,我使用了显眼的红色
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@android:id/text1"android:gravity="center"android:paddingTop="5dp"android:textStyle="bold"android:textColor="#FF0000"android:textSize="14dp"android:singleLine="true"android:layout_width="match_parent"android:layout_height="wrap_content"android:ellipsize="marquee"/>
4 创建适配器并使用自定义布局
在 MainActivity.java 的 onCreate 中源码如下:
package com.lzc.myapplication;import android.os.Bundle;
import android.widget.ArrayAdapter;import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatSpinner;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});// 创建模拟数据String[] planets = {"水星", "金星", "地球", "火星", "木星", "土星", "天王星", "海王星"};AppCompatSpinner spinner = findViewById(R.id.spinner);// 创建适配器并使用自定义布局ArrayAdapter<String> adapter = new ArrayAdapter<>(this,R.layout.custom_spinner,planets);// 设置下拉样式adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);spinner.setAdapter(adapter);}
}
如果想处理下拉样式,和上面同理,就不多说了。