一、 反射基本使用方法
1)实体类(普通内部类)
public class TestReflect {
public void show(Integer a) {
Log.i("dawn", a + "");
}
class TestFeflect {
private String name;
public String getName() {
return name;
}
public void printline() {
Log.v("dawn", "test Sucess!");
}
}
}
调用方式:
try {
Class<?> outterClass = Class.forName("com.example.test.TestReflect");
//获取JAVA私有内部类反射类型
Class innerclass = Class.forName("com.example.test.TestReflect$TestFeflect");
// 获取构造方法 是获取当前类和父类的所有方法
Constructor constructor = innerclass.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Object a = outterClass.newInstance();
Object f = constructor.newInstance(a);
Method g = f.getClass().getDeclaredMethod("printline");
g.setAccessible(true);
System.out.println(g);
System.out.println(g.invoke(f));
} catch (Exception e) {
e.printStackTrace();
Log.e("dawn", e.toString());
}
打印结果:test Sucess!
2)内部类改为静态内部类 static class TestFeflect
Class innerclass = Class.forName("com.example.test.TestReflect$TestFeflect");
// 获取构造方法 是获取当前类和父类的所有方法
Constructor constructor = innerclass.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Object f = constructor.newInstance();
Method g = f.getClass().getDeclaredMethod("printline");
g.setAccessible(true);
System.out.println(g);
System.out.println(g.invoke(f));
打印结果:test Sucess!
3)仿TimePicker创建类
public class TestReflect {
private TestDelegate mDelegate= null;
public TestReflect(Context context){
mDelegate = new TimePickerSpinnerDelegate(this,context);
}
interface TestDelegate{
View getHourView();
View getMinuteView();
}
abstract static class AbstractTimePickerDelegate implements TestDelegate{
protected final TestReflect testReflect;
protected final Context mContext;
public AbstractTimePickerDelegate(TestReflect delegator,Context context) {
testReflect = delegator;
mContext = context;
}
}
}
class TimePickerSpinnerDelegate extends TestReflect.AbstractTimePickerDelegate{
//反射回这个值就是我想要的
private final int mHourSpinner = 8;
public TimePickerSpinnerDelegate(TestReflect delegator, Context context) {
super(delegator, context);
}
@Override
public View getHourView() {
return null;
}
@Override
public View getMinuteView() {
return null;
}
}
反射相关代码:
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
TestReflect testReflect;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
binding.tvClick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onFClick();
}
});
binding.tvCreate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onFCreate();
}
});
}
private void onFCreate() {
testReflect = new TestReflect(this);
}
private void onFClick() {
try {
Class<?> outterClass = Class.forName("com.example.test.TestReflect");
Field usedFeild = outterClass.getDeclaredField("mDelegate");
usedFeild.setAccessible(true);
Object mDelegate = usedFeild.get(testReflect);
Log.v("dawn", mDelegate.toString());
Class innerclass = Class.forName("com.example.test.TimePickerSpinnerDelegate");
Field field = innerclass.getDeclaredField("mHourSpinner");
field.setAccessible(true);
int mHour = field.getInt(mDelegate);
Log.v("dawn","mHour = "+mHour);
} catch (Exception e) {
e.printStackTrace();
Log.e("dawn", e.toString());
}
}
}
========================================================================
二、解决TimePicker 判断开始时间大于结束时间
布局如图,PickerView在上下滚动的过程当中,需要判断开始时间不能大于结束时间。
public class TimeRangeView extends FrameLayout implements ITimePickerPlugin, TimePicker.OnTimeChangedListener {
private TimePicker mTimePickerStart;
private TimePicker mTimePickerEnd;
private String startTime;//开始时间
private String endTime;//结束时间
private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
public TimeRangeView(@NonNull Context context) {
super(context);
}
public TimeRangeView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TimeRangeView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
inflate(getContext(), R.layout.layout_time_frame, this);
mTimePickerStart = findViewById(R.id.time_picker_start);
mTimePickerEnd = findViewById(R.id.time_picker_end);
initData();
initEvent();
}
private void initData() {
mTimePickerStart.setIs24HourView(true);
mTimePickerEnd.setIs24HourView(true);
bindTimePicker(mTimePickerStart);
bindTimePicker(mTimePickerEnd);
startTime = sdf.format(System.currentTimeMillis());
endTime = sdf.format(System.currentTimeMillis() + 1000 * 60 * 10);
}
private void initEvent() {
mTimePickerStart.setOnTimeChangedListener(this);
mTimePickerEnd.setOnTimeChangedListener(this);
}
/**
* 设置时间
*
* @param date
* @param timeType 0:开始时间 1:结束时间
*/
public void setTime(Date date, @IntRange(from = 0, to = 1) int timeType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
if (timeType == 0) {
startTime = sdf.format(date);
mTimePickerStart.setHour(calendar.get(Calendar.HOUR_OF_DAY));
mTimePickerStart.setMinute(calendar.get(Calendar.MINUTE));
} else {
endTime = sdf.format(date);
mTimePickerEnd.setHour(calendar.get(Calendar.HOUR_OF_DAY));
mTimePickerEnd.setMinute(calendar.get(Calendar.MINUTE));
}
}
}
/**
* 获取开始时间
*
* @return
*/
public String getStartTime() {
return TextUtils.isEmpty(startTime.trim()) ? "" : startTime;
}
/**
* 获取结束时间
*
* @return
*/
public String getEndTime() {
return TextUtils.isEmpty(endTime.trim()) ? "" : endTime;
}
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
String time = (hourOfDay < 10 ? "0" + hourOfDay : hourOfDay) + ":" + (minute < 10 ? "0" + minute : minute);
if (view.getId() == R.id.time_picker_start) {
startTime = time;
} else {
endTime = time;
}
}
@Override
public void onScrollStateChange(NumberPicker view, int scrollState) {
if (scrollState == NumberPicker.OnScrollListener.SCROLL_STATE_IDLE) {
compareTime();
}
}
/**
* 校验时间范围
*/
private void compareTime() {
if (startTime == null || endTime == null) return;
try {
long startTimeMillions = sdf.parse(startTime).getTime();
long endTimeMillions = sdf.parse(endTime).getTime();
if (startTimeMillions > endTimeMillions) {
ToastUtil.showShortToast("开始时间不能大于结束时间");
}
} catch (ParseException e) {
e.printStackTrace();
ToastUtil.showShortToast("日期格式转化错误");
}
}
}
public interface ITimePickerPlugin extends NumberPicker.OnScrollListener {
/**
* 绑定TimePicker
* 注:解决【开始时间不能大于结束时间】不断弹出toast问题方案
*
* @param timePicker
*/
default void bindTimePicker(TimePicker timePicker){
try {
//一、最粗暴的方法
/* ViewGroup timeViewGroup = (ViewGroup) ((ViewGroup) ((ViewGroup) timePicker.getChildAt(0))).getChildAt(1);
NumberPicker startHour = (NumberPicker) timeViewGroup.getChildAt(0);
NumberPicker startMinute = (NumberPicker) timeViewGroup.getChildAt(2);*/
//二、通过反射找到 getHourView( 可惜是个TestAPI )
/* Class mTimePicker = Class.forName("android.widget.TimePicker");
Method getHourView = mTimePicker.getDeclaredMethod("getHourView");
// Method getMinuteView = mTimePicker.getDeclaredMethod("getMinuteView");
getHourView.setAccessible(true);
View hourHView = (View) getHourView.invoke(timePicker,null);
// hourHView.getParent() 就是对应的NumberPicker
((NumberPicker) hourHView.getParent()).setOnScrollListener(this);*/
//三、通过反射获取TimePicker源码里hour和minute的id
Resources systemResources = Resources.getSystem();
int hourNumberPickerId = systemResources.getIdentifier("hour", "id", "android");
int minuteNumberPickerId = systemResources.getIdentifier("minute", "id", "android");
NumberPicker startHour = (NumberPicker) timePicker.findViewById(hourNumberPickerId);
NumberPicker startMinute = (NumberPicker) timePicker.findViewById(minuteNumberPickerId);
if (startHour != null) {
startHour.setOnScrollListener(this);
}
if (startMinute != null) {
startMinute.setOnScrollListener(this);
}
}catch (Exception e){
e.printStackTrace();
}
}
@Override
void onScrollStateChange(NumberPicker view, int scrollState);
}
最初的时候,一直执迷于 private final TimePickerDelegate mDelegate; 这个对象去拿到实现类(TimePickerSpinnerDelegate)的引用。
源代码:
public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
/**
此处省去无关代码
*/
switch (mMode) {
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;
}
mDelegate.setAutoFillChangeListener((v, h, m) -> {
final AutofillManager afm = context.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
});
}
TimePickerSpinnerDelegate 源码:
class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
private static final boolean DEFAULT_ENABLED_STATE = true;
private static final int HOURS_IN_HALF_DAY = 12;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
private final EditText mHourSpinnerInput;
private final EditText mMinuteSpinnerInput;
private final EditText mAmPmSpinnerInput;
private final TextView mDivider;
mHourSpinner 正是我想要的对象。因为 @UnsupportedAppUsage 的注解,一直没有成功。而现在通过这种方式拿到NumberPicker以后,通过反射就多了更多的可能。
Resources systemResources = Resources.getSystem();
int hourNumberPickerId = systemResources.getIdentifier("hour", "id", "android");
int minuteNumberPickerId = systemResources.getIdentifier("minute", "id", "android");
NumberPicker startHour = (NumberPicker) timePicker.findViewById(hourNumberPickerId);
仅作笔记记录。