如何为Spinner模式的TimePicker自定义布局并修改选中文字颜色
Hey there! Let's tackle your two requirements for the Spinner-mode TimePicker: customizing its layout and changing the selected text color to orange or green. I'll break this down into easy-to-follow steps.
There are two practical approaches here—one using styles (no code needed) and another using code for dynamic control:
Approach 1: Using Custom Styles (Clean & Static)
This is the best option if you want to apply the same style across multiple TimePickers or your entire app.
First, add these styles to your res/values/styles.xml file:
<!-- Base style for the Spinner-mode TimePicker --> <style name="CustomSpinnerTimePicker" parent="Widget.MaterialComponents.TimePicker.Spinner"> <item name="android:spinnerStyle">@style/CustomTimeSpinner</item> </style> <!-- Style for the inner Spinners inside the TimePicker --> <style name="CustomTimeSpinner" parent="Widget.MaterialComponents.Spinner"> <item name="spinnerItemStyle">@style/CustomSpinnerItemText</item> </style> <!-- Style to define default and selected text colors --> <style name="CustomSpinnerItemText" parent="TextAppearance.MaterialComponents.Spinner.Item"> <item name="android:textColor">@color/default_text</item> <!-- Set your desired selected color (orange/green) --> <item name="android:textColorSelected">@color/orange</item> </style>
Don't forget to define the colors in res/values/colors.xml:
<color name="orange">#FFA500</color> <color name="green">#00C851</color> <color name="default_text">#333333</color>
Then apply the custom style to your TimePicker in the layout:
<TimePicker android:id="@+id/timePicker" android:layout_width="match_parent" android:layout_height="wrap_content" android:timePickerMode="spinner" style="@style/CustomSpinnerTimePicker" <!-- Add this line --> app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/constraintLayout2" tools:ignore="UnusedAttribute" />
Approach 2: Using Code (Dynamic Color Changes)
If you need to switch colors on the fly (e.g., based on user settings), use reflection to access the TimePicker's inner Spinners and customize their adapters:
Add this code to your Activity/Fragment:
val timePicker = findViewById<TimePicker>(R.id.timePicker) // Access the hour and minute Spinners using their internal tags val hourSpinner = timePicker.findViewWithTag<Spinner>("hour") val minuteSpinner = timePicker.findViewWithTag<Spinner>("minute") // Create a custom adapter to handle selected text color fun createColoredAdapter(spinner: Spinner): ArrayAdapter<*> { val originalAdapter = spinner.adapter as ArrayAdapter<*> return object : ArrayAdapter<Any?>(this, android.R.layout.simple_spinner_item, originalAdapter.items) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val view = super.getView(position, convertView, parent) // Set color based on selection state view.setTextColor( if (spinner.selectedItemPosition == position) ContextCompat.getColor(this@YourActivity, R.color.green) else ContextCompat.getColor(this@YourActivity, R.color.default_text) ) return view } override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { val view = super.getDropDownView(position, convertView, parent) view.setTextColor( if (spinner.selectedItemPosition == position) ContextCompat.getColor(this@YourActivity, R.color.green) else ContextCompat.getColor(this@YourActivity, R.color.default_text) ) return view } } } // Apply the custom adapters hourSpinner.adapter = createColoredAdapter(hourSpinner) minuteSpinner.adapter = createColoredAdapter(minuteSpinner) // Update color when selection changes hourSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { (parent?.adapter as ArrayAdapter<*>).notifyDataSetChanged() } override fun onNothingSelected(parent: AdapterView<*>?) {} } minuteSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { (parent?.adapter as ArrayAdapter<*>).notifyDataSetChanged() } override fun onNothingSelected(parent: AdapterView<*>?) {} }
Replace YourActivity with your actual Activity class name.
If you want full control over the layout (e.g., rearrange spinners, add labels, or remove separators), build a custom layout instead of using the default TimePicker:
Step 1: Create the Custom Layout (res/layout/custom_time_picker.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:padding="16dp"> <!-- Hour Spinner --> <Spinner android:id="@+id/hourSpinner" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=":" android:textSize="24sp" android:paddingHorizontal="12dp" /> <!-- Minute Spinner --> <Spinner android:id="@+id/minuteSpinner" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> <!-- Add AM/PM spinner here if using 12-hour format --> </LinearLayout>
Step 2: Create Hour/Minute Arrays (res/values/arrays.xml)
<resources> <!-- 24-hour format hours --> <string-array name="hours_24h"> <item>00</item><item>01</item><item>02</item><item>03</item><item>04</item><item>05</item> <item>06</item><item>07</item><item>08</item><item>09</item><item>10</item><item>11</item> <item>12</item><item>13</item><item>14</item><item>15</item><item>16</item><item>17</item> <item>18</item><item>19</item><item>20</item><item>21</item><item>22</item><item>23</item> </string-array> <!-- 5-minute interval minutes --> <string-array name="minutes_interval"> <item>00</item><item>05</item><item>10</item><item>15</item><item>20</item><item>25</item> <item>30</item><item>35</item><item>40</item><item>45</item><item>50</item><item>55</item> </string-array> </resources>
Step 3: Implement the Custom TimePicker in Your Activity/Fragment
// Inflate the custom layout val customTimePicker = layoutInflater.inflate(R.layout.custom_time_picker, null) val hourSpinner = customTimePicker.findViewById<Spinner>(R.id.hourSpinner) val minuteSpinner = customTimePicker.findViewById<Spinner>(R.id.minuteSpinner) // Reuse the colored adapter logic from Approach 2 fun createColoredAdapter(entries: Array<String>): ArrayAdapter<String> { return object : ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, entries) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val view = super.getView(position, convertView, parent) view.setTextColor( if ((parent as Spinner).selectedItemPosition == position) ContextCompat.getColor(this@YourActivity, R.color.orange) else ContextCompat.getColor(this@YourActivity, R.color.default_text) ) return view } override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { val view = super.getDropDownView(position, convertView, parent) view.setTextColor( if ((parent as Spinner).selectedItemPosition == position) ContextCompat.getColor(this@YourActivity, R.color.orange) else ContextCompat.getColor(this@YourActivity, R.color.default_text) ) return view } } } // Set adapters with our arrays hourSpinner.adapter = createColoredAdapter(resources.getStringArray(R.array.hours_24h)) minuteSpinner.adapter = createColoredAdapter(resources.getStringArray(R.array.minutes_interval)) // Add selection listeners to capture the selected time hourSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { val selectedHour = parent?.selectedItem.toString() // Handle selected hour } override fun onNothingSelected(parent: AdapterView<*>?) {} } minuteSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { val selectedMinute = parent?.selectedItem.toString() // Handle selected minute } override fun onNothingSelected(parent: AdapterView<*>?) {} } // Add the custom time picker to your main layout val container = findViewById<ConstraintLayout>(R.id.your_main_container) container.addView(customTimePicker)
内容的提问来源于stack exchange,提问作者MasterInAndroid

