演示效果: 需求描述: 在发送礼物按钮上进行连击,界面上展示礼物且礼物数字随着连击事件增加。需求扩展:数字变动的间隔时间可通过参数设置。
思路简析: 这可以看作是一个生产-消费模型,用队列来存储连击事件。什么是状态机思想呢?简单地说,就是n个状态在不同条件下互相转化的过程。那么如何通过状态机思想来分析这个需求呢?
首先,罗列出所有状态:START、WORKING、WAITING、STOP 然后画草图,画出状态之间的转换条件: 按照状态机思想画上草图后,状态之间的关系链就会变得很清晰。
源码:
public class ComboUtil<T extends ComboUtil.TimeEvt> { //点击事件队列 private LinkedList<T> queue; private ComboHandler<T> mHandler; /** * @param noRspTime 无响应时间 两次点击间隔noRspTime时默认为新一轮连击 * @param takeEvtInterval 取队列数据的间隔时间,同一轮连击里点击事件的间隔时间 */ public ComboUtil(Callback<T> callback, long noRspTime, long takeEvtInterval) { queue = new LinkedList<>(); mHandler = new ComboHandler<T>(callback, noRspTime, takeEvtInterval) { @Override public T takeEvt() { return queue.isEmpty() ? null : queue.removeFirst(); } @Override public T getEvt() { return queue.isEmpty() ? null : queue.getFirst(); } @Override public boolean isEmptyQueue() { return queue.isEmpty(); } }; } public void onClick(T evt) { queue.add(evt); Log.d("连击测试", "enqueue"); mHandler.onModelEnqueue(); } public void onDestroy() { mHandler.onDestroy(); queue.clear(); } public static class TimeEvt { private long mills; public TimeEvt() { this.mills = System.currentTimeMillis(); } public long getMills() { return mills; } } public interface Callback<T> { //连击准备 void onStart(); //连击回调 void onCombo(T evt); //等待点击 void onWaiting(); //连击结束 void onStop(); } public static abstract class ComboHandler<T> extends Handler { public static final int START = 1; public static final int WORKING = 2; public static final int STOP = 4; public static final int WAITING = 3; private ComboUtil.Callback<T> callback; private long takeEvtInterval; private long noRspTime;//单位:毫秒 public ComboHandler(ComboUtil.Callback<T> callback, long noRspTime, long takeEvtInterval) { this.callback = callback; this.takeEvtInterval = takeEvtInterval; this.noRspTime = noRspTime; } @Override public void handleMessage(Message msg) { //用msg.arg1表示上一个状态 int from = msg.arg1; Message newMsg = Message.obtain(); switch (msg.what) { case START: if (from < STOP && from >= START) return;//不符合DFA状态机算法图 Log.d("连击测试", "receive START msg"); newMsg.arg1 = START; if (callback != null) callback.onStart(); if (isEmptyQueue()) { newMsg.what = WAITING;//队列为空进入等待状态 } else { newMsg.what = WORKING;//进入工作状态 } sendMessage(newMsg); break; case WORKING: if (from == WORKING || from == WAITING || from == START) { //符合DFA状态机算法图 Log.d("连击测试", "receive WORKING msg"); newMsg.arg1 = WORKING; //从队尾取出一个点击事件 T t = takeEvt(); ComboUtil.TimeEvt evt = (TimeEvt) t; if (evt == null) { //当前队列已空 if (from == WAITING) { //上一个状态已经是等待状态了 newMsg.what = STOP; sendMessage(newMsg);//直接结束本次连击 } else { //进入等待状态 newMsg.what = WAITING; sendMessage(newMsg); } } else { if (callback != null) callback.onCombo(t); //判断下一条数据时间 ComboUtil.TimeEvt nxtEvt = (TimeEvt) getEvt(); if (nxtEvt != null && nxtEvt.getMills() - evt.getMills() >= noRspTime) { //认为本次连击结束,并重新开始新一轮连击 newMsg.what = STOP; } else newMsg.what = WORKING; sendMessageDelayed(newMsg, takeEvtInterval); } } break; case WAITING: if (from == START || from == WORKING || from == WAITING) { Log.d("连击测试", "receive WAITING msg"); newMsg.arg1 = WAITING; boolean isEmpty = isEmptyQueue(); //符合DFA状态机算法图 if (callback != null) callback.onWaiting(); switch (from) { case START: case WORKING: //取数据时发现队列已空,进入等待状态,noRspTime时间后再次判断队列是否为空 newMsg.what = WAITING; sendMessageDelayed(newMsg, noRspTime); break; case WAITING: if (isEmpty) { //经过noRspTime后 数据队列仍然为空,直接发送stop msg 结束这次连击 newMsg.what = STOP; } else { //经过noRspTime后 数据队列已入队新的数据,发送take msg 继续取数据 newMsg.what = WORKING; } sendMessage(newMsg); break; } } break; case STOP: if (from == WORKING || from == WAITING) { Log.d("连击测试", "receive STOP msg"); newMsg.arg1 = STOP; if (callback != null) callback.onStop(); //发送stop msg到接收stop msg之间可能还有入队操作,需要再检查一次队列 if (!isEmptyQueue()) { newMsg.what = START; sendMessage(newMsg); } } break; } } //读写操作 public abstract T takeEvt(); //只读操作 public abstract T getEvt(); public abstract boolean isEmptyQueue(); //是否是空闲状态 public boolean isIDLE() { boolean hasMsg = hasMessages(START) || hasMessages(WORKING) || hasMessages(WAITING) || hasMessages(STOP); return !hasMsg; } /** * 当前空闲 则发送start msg 开始工作 * 开始工作后会自动按照逻辑从数据model队列取数据 */ public void onModelEnqueue() { if (isIDLE()) sendEmptyMessage(START); else if (hasMessages(WAITING)) { //如果是等待状态-唤醒 removeMessages(WAITING); Message newMsg = Message.obtain(); newMsg.arg1 = WAITING; newMsg.what = WORKING; sendMessage(newMsg); } } public void onDestroy() { removeCallbacksAndMessages(null); if (callback != null) callback.onStop(); } } }大家有什么好用的制图软件推荐吗?
有任何疑问或建议欢迎留言讨论!