您的位置:首页 > 其它

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

2015-12-03 20:20 477 查看
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绘画而成。

用以上两种方法,就可以动态改变分割线颜色,冒号有无、冒号颜色等等
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: