Android实现多个倒计时优化与源码分析

来源:互联网 发布:安阳淘宝电商摄影 编辑:程序博客网 时间:2024/06/09 07:37


因为之前有个项目需求是需要时时刻去更新UI倒计时,之前想到的,这简单嘛,用计时或者Handler就可以搞定,而且性能也不错,但是需求要ListView,什么,?大量的View都需要,那Handle处理不好会挂的啊,那轮训呢,?太消耗内存和Cpu,突然之前只有想到用Handle去处理,但是Item太多如何管理呢.?带着这样的问题,思考着纠结着,今天无意中看到一个源码还不错,

这个类是Google原生提供的数字时钟,可以实现时时刻刻的更新,我想里面肯定封装了一些实现的逻辑就跟着开始研究学习,下面是该类的主要结构:

/** * Like AnalogClock, but digital.  Shows seconds. * * FIXME: implement separate views for hours/minutes/seconds, so * proportional fonts don't shake rendering */public class DigitalClock extends TextView {    Calendar mCalendar;    private final static String m12 = "h:mm:ss aa";    private final static String m24 = "k:mm:ss";    private FormatChangeObserver mFormatChangeObserver;    private Runnable mTicker;    private Handler mHandler;    private boolean mTickerStopped = false;    String mFormat;    public DigitalClock(Context context) {        super(context);        initClock(context);    }    public DigitalClock(Context context, AttributeSet attrs) {        super(context, attrs);        initClock(context);    }    private void initClock(Context context) {        Resources r = mContext.getResources();        if (mCalendar == null) {            mCalendar = Calendar.getInstance();        }        mFormatChangeObserver = new FormatChangeObserver(); //格式观察者,开始第一眼我看到这儿以为是通过观察者去实现的,结果只是一个格式的观察.        getContext().getContentResolver().registerContentObserver( //注册                Settings.System.CONTENT_URI, true, mFormatChangeObserver);        setFormat(); // 设置格式    }


/** *这个方法就是更新的核心,该方法是能正常的call onDraw或者onMeasure后便会回调. * */ @Override    protected void onAttachedToWindow() {        mTickerStopped = false;        super.onAttachedToWindow();        mHandler = new Handler(); // 用于Post一个runable.        /**         * requests a tick on the next hard-second boundary         */        mTicker = new Runnable() {                public void run() {                    if (mTickerStopped) return;                    mCalendar.setTimeInMillis(System.currentTimeMillis()); // 之前创建日历对象获取时间.                    setText(DateFormat.format(mFormat, mCalendar)); // 设置时间                    invalidate(); // 更新UI                    long now = SystemClock.uptimeMillis();                    long next = now + (1000 - now % 1000);// 这儿算法不错,保证一秒更新一次,                    mHandler.postAtTime(mTicker, next);                }            };        mTicker.run();    }    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        mTickerStopped = true;// 保证复用的时候,runable被系统回收.    }    /**     * Pulls 12/24 mode from system settings     */    private boolean get24HourMode() {        return android.text.format.DateFormat.is24HourFormat(getContext());    }    private void setFormat() {        if (get24HourMode()) {            mFormat = m24;        } else {            mFormat = m12;        }    }    private class FormatChangeObserver extends ContentObserver {        public FormatChangeObserver() {            super(new Handler());        }        @Override        public void onChange(boolean selfChange) {            setFormat();        }    }    @Override    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {        super.onInitializeAccessibilityEvent(event);        event.setClassName(DigitalClock.class.getName());    }    @Override    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {        super.onInitializeAccessibilityNodeInfo(info);        info.setClassName(DigitalClock.class.getName());    }}


看到OnAttachedToWindow方法后,可以借鉴出来一个自定义的好的写法.我就不用Calender去获取时间更新,把它封装出来给用户使用.我就暂时只贴出核心代码吧

@Overrideprotected void onAttachedToWindow() {mTickerStopped = false;super.onAttachedToWindow();mHandler = new Handler();/** * requests a tick on the next hard-second boundary */mTicker = new Runnable() {public void run() {if (mTickerStopped)return;long currentTime = System.currentTimeMillis();if (currentTime / 1000 == endTime / 1000 - 1 * 60) { // 判定是否到了指定时间mClockListener.remainOneMinutes(); // 指定时间的CallBack}long distanceTime = endTime - currentTime;// 计算差值distanceTime /= 1000; // 转为秒if (distanceTime == 0) {setText("00:00:00");onDetachedFromWindow(); // 保证该runnable不在被继续运行.mClockListener.timeEnd(); // 结束call back.} else if (distanceTime < 0) {setText("00:00:00");} else {setText(dealTime(distanceTime));// 设置倒计时.}invalidate();long now = SystemClock.uptimeMillis();long next = now + (1000 - now % 1000);// 够不够一秒,保证一秒更新一次mHandler.postAtTime(mTicker, next);}};mTicker.run();}



上面是核心展示UI工具类,真正的回收机制在下面处理,

在ListView中设置一个setRecyclerListener,该监听会根据手指滑动一个View移除屏幕外的时候会callback.

@Override    public void onMovedToScrapHeap(View view) {        try {            ((ClockView)((LinearLayout) view).getChildAt(0)).changeTicker();//寻找时钟,并启动.        }catch (Exception e){            e.printStackTrace();        }    }


回收的判断,

<pre name="code" class="java">/**     * 回收后启动     */    public void changeTicker() {        mTickerStopped = !mTickerStopped;        if (!mTickerStopped) {            mHandler.post(mTicker);        }else{            mHandler.removeCallbacks(mTicker);        }    }




在适配器中:

@Override    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder holder;        if (null == convertView) {            holder = new ViewHolder();            convertView = View.inflate(context, R.layout.item_list, null);            holder.cv = (ClockView) convertView.findViewById(R.id.cv);            convertView.setTag(holder);        } else {            holder = (ViewHolder) convertView.getTag();            holder.cv.changeTicker(); // 从回收中拿的时候启动一次.        }        holder.cv.setEndTime(mTimes.get(position));        return convertView;    }



github:https://github.com/q422013/ListClock

1 0
原创粉丝点击