TimePicker 判断开始时间大于结束时间----利用反射原理

一、 反射基本使用方法

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);

仅作笔记记录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值