Call requires API level 29 (current min is 21): `android.widget.NumberPicker#setTextColor`

我与影子孤独终老i 提交于 2021-01-04 05:31:24

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!