Android实现多个倒计时优化与源代码分析
由于之前有个项目需求是须要时时刻去更新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去获取时间更新,把它封装出来给用户使用.我就临时仅仅贴出核心代码吧
@Override protected 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; }