问题
I want to change selected field of text color using setTextColor. But Android Studio gives me this error. What should I do? Min SDK is 21. This is code of my CustomNumberPicker class:
import android.annotation.TargetApi
import android.content.Context
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.NumberPicker
import android.widget.NumberPicker.OnScrollListener
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.widget.TextViewCompat
import ir.partsoftware.cup.R
import timber.log.Timber
class CustomNumberPicker : NumberPicker {
constructor(context: Context?) : super(context) {
init()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
init()
}
private fun init() {
setDividerColor(ContextCompat.getColor(context, R.color.color_secondary))
setNumberPickerTextColor(this, ContextCompat.getColor(context, R.color.color_secondary))
this.setOnValueChangedListener { picker, oldVal, newVal ->
setNumberPickerTextColor(this, ContextCompat.getColor(context, R.color.color_secondary))
}
this.setOnScrollListener { numberPicker, scrollState ->
setNumberPickerTextColor(this, ContextCompat.getColor(context, R.color.color_secondary))
}
}
private fun setNumberPickerTextColor(numberPicker: NumberPicker, color: Int) {
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
numberPicker.textColor = color
} else {
val count = numberPicker.childCount
for (i in 0 until count) {
val child = numberPicker.getChildAt(i)
if (child is EditText) {
try {
child.setTextColor(color)
val fieldSelectorWheelPaint = numberPicker.javaClass.getDeclaredField("mSelectorWheelPaint")
val paint = fieldSelectorWheelPaint[numberPicker] as Paint
paint.color = color
fieldSelectorWheelPaint.isAccessible = true
numberPicker.invalidate()
} catch (ex: java.lang.Exception) {
// Ignore
}
}
}
}
}
private fun setDividerColor(@ColorInt color: Int) {
try {
val fDividerDrawable =
NumberPicker::class.java.getDeclaredField("mSelectionDivider")
fDividerDrawable.isAccessible = true
val d = fDividerDrawable[this] as Drawable
DrawableCompat.setTint(d, color)
d.invalidateSelf()
postInvalidate()
} catch (e: Exception) {
Timber.d(e)
}
}
override fun addView(
child: View,
index: Int,
params: ViewGroup.LayoutParams
) {
super.addView(child, index, params)
updateView(child)
}
private fun updateView(view: View) {
if (view is EditText) {
try {
TextViewCompat.setTextAppearance(view, R.style.TextAppearance_PartPay_NumPicker)
val customFont: Typeface? = ResourcesCompat.getFont(context, R.font.iran_yekan)
view.typeface = customFont
// setNumberPickerTextColor(ContextCompat.getColor(context, R.color.color_secondary))
} catch (e: Exception) {
Timber.d(e)
}
}
}
}
回答1:
Try the next code. Will use reflection when the API is not accesible:
public void setNumberPickerTextColor(final NumberPicker numberPicker, final int color){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
numberPicker.setTextColor(color);
}
else {
final int count = numberPicker.getChildCount();
for (int i = 0; i < count; i++) {
final View child = numberPicker.getChildAt(i);
if (child instanceof EditText) {
try {
((EditText)child).setTextColor(color);
numberPicker.invalidate();
final Field fieldSelectorWheelPaint = numberPicker.getClass().getDeclaredField("mSelectorWheelPaint");
boolean isAccessible = fieldSelectorWheelPaint.isAccessible();
fieldSelectorWheelPaint.setAccessible(true);
final Paint paint = (Paint)fieldSelectorWheelPaint.get(numberPicker);
if (paint != null){
paint.setColor(color);
fieldSelectorWheelPaint.setAccessible(isAccessible);
numberPicker.invalidate();
}
final Field fieldSelectionDivider = numberPicker.getClass().getDeclaredField("mSelectionDivider");
isAccessible = fieldSelectionDivider.isAccessible();
fieldSelectionDivider.setAccessible(true);
fieldSelectionDivider.set(numberPicker, null);
fieldSelectionDivider.setAccessible(isAccessible);
numberPicker.invalidate();
}
catch (Exception ex) {
// Ignore
}
}
}
}
}
You may call this method the first time you get a reference to the control, and in addition if the color doesn't persist after scrolling, then hook listener such as next:
numberPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(final NumberPicker picker, final int oldVal, final int newVal) {
setNumberPickerTextColor(numberPicker, Color.RED);
}
});
Or alternatively you can also hook a scroll listener, although the above setOnValueChangedListener example is more optimal, as it will only perform the update when the value is changed. To improve the next scroll method you could check if the scrollState is in an idle state, so it is only called when scrolling ends:
numberPicker.setOnScrollListener(new NumberPicker.OnScrollListener() {
@Override
public void onScrollStateChange(final NumberPicker numberPicker, final int scrollState) {
setNumberPickerTextColor(numberPicker, Color.RED);
}
});
UPDATE: The next section is specific only to your updated question code.
The problem is that you are extending a NumberPicker class, in such case you need to use the getDeclaredField on the super class. My above answer can be used only when not extending the NumberPicker class. In addition you've placed the isAccesible in the wrong line, it needs to be a bit before to make it accesible.
Next is the correction to your code which can be used perfectly when extending a NumberPicker class. You can see that getDeclaredField is preceded by superclass, and isAccessible is at the correct position:
private fun setNumberPickerTextColor(numberPicker: NumberPicker, color: Int) {
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
numberPicker.textColor = color
} else {
val count = numberPicker.childCount
for (i in 0 until count) {
val child = numberPicker.getChildAt(i)
if (child is EditText) {
try {
child.setTextColor(color)
val fieldSelectorWheelPaint = numberPicker.javaClass.superclass.getDeclaredField("mSelectorWheelPaint")
fieldSelectorWheelPaint.isAccessible = true
val paint = fieldSelectorWheelPaint[numberPicker] as Paint
paint.color = color
numberPicker.invalidate()
} catch (ex: java.lang.Exception) {
// Ignore
}
}
}
}
}
来源:https://stackoverflow.com/questions/64086987/call-requires-api-level-29-current-min-is-21-android-widget-numberpickerset