当前位置: 首页 > 工具软件 > Clock Picker > 使用案例 >

从TimePicker中获取NumberPicker,从NumberPicker获取mSelectionDivider

祁通
2023-12-01

TimePicker源码很奇特,它并没有实现ondraw函数,而是直接用xml布局实现视图,我们可以从

TimePickerSpinnerDelegate

<span style="font-size:18px;">TimePickerClockDelegate</span>
两个类中找到相关代码


public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
        final int mode = a.getInt(R.styleable.TimePicker_timePickerMode, MODE_SPINNER);
        a.recycle();

        switch (mode) {
            case MODE_CLOCK:
                mDelegate = new TimePickerClockDelegate(//----->注意这个
                        this, context, attrs, defStyleAttr, defStyleRes);
                break;
            case MODE_SPINNER:
            default:
                mDelegate = new TimePickerSpinnerDelegate(//------>还有这个
                        this, context, attrs, defStyleAttr, defStyleRes);
                break;
        }
    }
然后可以发现正常的TimePicker是由三个NumberPicker和一个TextView,或者两个NumberPicker加上一个Button组成
,应该设置的模式不同而显示的样式不同。

然后问题来了,如果我们要自定义TimePicker怎么办,对于要在android机上实现类似ios那样的TimePicker效果,不重构基本是办不到的,但是如果仅仅是要改一些数字的大小,分割线的大小、颜色等,则不需要那么麻烦,直接上代码:

public class CustomTimePicker extends TimePicker
{
    public CustomTimePicker(Context context)
    {
        super(context);
        init();
    }

    public CustomTimePicker(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        init();
    }

    public CustomTimePicker(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        init();
    }

    public CustomTimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
    {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    public void init()
    {
        getNumberPicker(this);
    }


    public void getNumberPicker(TimePicker timePicker)
    {
        try
        {
            Class<?> clazz = Class.forName("com.android.internal.R$id");
            Field fieldHour = clazz.getField("hour");
            fieldHour.setAccessible(true);
            int hourId = fieldHour.getInt(null);
            NumberPicker hourNumberPicker = (NumberPicker) timePicker.findViewById(hourId);
            setDividerColor(hourNumberPicker);

            Field fieldminute = clazz.getField("minute");
            fieldminute.setAccessible(true);
            int minuteId = fieldminute.getInt(null);
            NumberPicker minuteNumberPicker = (NumberPicker) timePicker.findViewById(minuteId);
            setDividerColor(minuteNumberPicker);

            //更改冒号颜色
            //Field fieldDivider=clazz.getField("divider");
            //fieldDivider.setAccessible(true);
            //int dividerId=fieldDivider.getInt(null);
            //TextView textView=(TextView)timePicker.findViewById(dividerId);
            //textView.setTextColor(getResources().getColor(R.color.wheel_text_small));

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }


    public void setDividerColor(NumberPicker picker)
    {
        Field[] pickerFields = NumberPicker.class.getDeclaredFields();
        for (java.lang.reflect.Field pf : pickerFields)
        {
            Log.v("setDividerColor", "pf:" + pf.getName() + " type :" + pf.getGenericType());
            if (pf.getName().equals("mSelectionDivider"))//能找到这个域 (分割线视图)
            {
                Log.v("setDividerColor", "find......mSelectionDivider");
                pf.setAccessible(true);
                try
                {
                    ColorDrawable colorDrawable = new ColorDrawable();
                    colorDrawable.setColor(getResources().getColor(R.color.wheel_text_small));
                    pf.set(picker, colorDrawable);
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
                break;
            }
            if (pf.getName().equals("mSelectionDividerHeight"))//找不到这个私有域,(分割线的厚度)
            {
                Log.v("PowerSet", "find......mSelectionDividerHeight.");
            }
        }
    }
}


这里我们用了两次反射,第一次反射用来获取id,第二次放射用于获取私有域。

第一次反射:

Class<?> clazz = Class.forName("com.android.internal.R$id");
            Field fieldHour = clazz.getField("hour");
            fieldHour.setAccessible(true);
            int hourId = fieldHour.getInt(null);
            NumberPicker hourNumberPicker = (NumberPicker) timePicker.findViewById(hourId);
有人会问,为啥获取id还要放射,难道不能直接R.id.XXX来获取吗?但是这里有两个问题

1、注意这里的R是指一个包名,平常可能很多TX用eclipse时会注意到,但我们写了一个R.XX的时候系统就会提示我们导入哪一个R包,正常情况下我们会导入"工程名.R"这个包,但是如果粗心的话可能会导入类似android.R的包,这种情况下一般会提示找不到id,原因是我们导入的R包错误。而这里TimePicker的视图不是我们建的视图,而是系统视图,依赖的R包是

com.android.internal.R
,所以我们正常情况下是访问不到TimePicker视图中的id值。

2、那又有tx会说,那我们用

com.android.internal.R.id.XXX
来访问id不行吗?按原理来说是可以的,但是只要运行的tx就可以知道,编译阶段就错了,我们根本访问不到这个包。

所以,基于以上两个问题,反射就出来了,反射可以动态的访问我们正常情况下访问不到的一些值(有些值访问不到),代码就不说了,很容易读。

然后第二次反射是通过获取NumberPicker中的私有域,正常情况下私有域是我们无权访问的,但是反射就可以实现这一点,可能又有人会问,为什么要获取私有域而不是id,原因是TimePicker的视图是由xml布局而成,而NumberPicker则是直接onDraw绘画而成。

用以上两种方法,就可以动态改变分割线颜色,冒号有无、冒号颜色等等

 类似资料: