您的位置:首页 > 移动开发 > Android开发

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

2015-08-12 10:59 537 查看


因为之前有个项目需求是需要时时刻去更新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;
}


github:https://github.com/q422013/ListClock
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: