/* * Copyright 2016. SHENQINCI(沈钦赐)<946736079@qq.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ren.qinc.edit; import android.support.annotation.NonNull; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.widget.EditText; import java.util.Stack; /** * 撤销和恢复撤销 * Created by 沈钦赐 on 16/6/23. */ public class PerformEdit { //操作序号(一次编辑可能对应多个操作,如替换文字,就是删除+插入) int index; //撤销栈 Stack<Action> history = new Stack<>(); //恢复栈 Stack<Action> historyBack = new Stack<>(); private Editable editable; private EditText editText; //自动操作标志,防止重复回调,导致无限撤销 private boolean flag = false; public PerformEdit(@NonNull EditText editText) { CheckNull(editText, "EditText不能为空"); this.editable = editText.getText(); this.editText = editText; editText.addTextChangedListener(new Watcher()); } protected void onEditableChanged(Editable s) { } protected void onTextChanged(Editable s) { } /** * 清理记录 * Clear history. */ public final void clearHistory() { history.clear(); historyBack.clear(); } /** * 撤销 * Undo. */ public final void undo() { if (history.empty()) return; //锁定操作 flag = true; Action action = history.pop(); historyBack.push(action); if (action.isAdd) { //撤销添加 editable.delete(action.startCursor, action.startCursor + action.actionTarget.length()); editText.setSelection(action.startCursor, action.startCursor); } else { //插销删除 editable.insert(action.startCursor, action.actionTarget); if (action.endCursor == action.startCursor) { editText.setSelection(action.startCursor + action.actionTarget.length()); } else { editText.setSelection(action.startCursor, action.endCursor); } } //释放操作 flag = false; //判断是否是下一个动作是否和本动作是同一个操作,直到不同为止 if (!history.empty() && history.peek().index == action.index) { undo(); } } /** * 恢复 * Redo. */ public final void redo() { if (historyBack.empty()) return; flag = true; Action action = historyBack.pop(); history.push(action); if (action.isAdd) { //恢复添加 editable.insert(action.startCursor, action.actionTarget); if (action.endCursor == action.startCursor) { editText.setSelection(action.startCursor + action.actionTarget.length()); } else { editText.setSelection(action.startCursor, action.endCursor); } } else { //恢复删除 editable.delete(action.startCursor, action.startCursor + action.actionTarget.length()); editText.setSelection(action.startCursor, action.startCursor); } flag = false; //判断是否是下一个动作是否和本动作是同一个操作 if (!historyBack.empty() && historyBack.peek().index == action.index) redo(); } /** * 首次设置文本 * Set default text. */ public final void setDefaultText(CharSequence text) { clearHistory(); flag = true; editable.replace(0, editable.length(), text); flag = false; } private class Watcher implements TextWatcher { /** * Before text changed. * * @param s the s * @param start the start 起始光标 * @param count the endCursor 选择数量 * @param after the after 替换增加的文字数 */ @Override public final void beforeTextChanged(CharSequence s, int start, int count, int after) { if (flag) return; int end = start + count; if (end > start && end <= s.length()) { CharSequence charSequence = s.subSequence(start, end); //删除了文字 if (charSequence.length() > 0) { Action action = new Action(charSequence, start, false); if (count > 1) { //如果一次超过一个字符,说名用户选择了,然后替换或者删除操作 action.setSelectCount(count); } else if (count == 1 && count == after) { //一个字符替换 action.setSelectCount(count); } //还有一种情况:选择一个字符,然后删除(暂时没有考虑这种情况) history.push(action); historyBack.clear(); action.setIndex(++index); } } } /** * On text changed. * * @param s the s * @param start the start 起始光标 * @param before the before 选择数量 * @param count the endCursor 添加的数量 */ @Override public final void onTextChanged(CharSequence s, int start, int before, int count) { if (flag) return; int end = start + count; if (end > start) { CharSequence charSequence = s.subSequence(start, end); //添加文字 if (charSequence.length() > 0) { Action action = new Action(charSequence, start, true); history.push(action); historyBack.clear(); if (before > 0) { //文字替换(先删除再增加),删除和增加是同一个操作,所以不需要增加序号 action.setIndex(index); } else { action.setIndex(++index); } } } } @Override public final void afterTextChanged(Editable s) { if (flag) return; if (s != editable) { editable = s; onEditableChanged(s); } PerformEdit.this.onTextChanged(s); } } private class Action { /** 改变字符. */ CharSequence actionTarget; /** 光标位置. */ int startCursor; int endCursor; /** 标志增加操作. */ boolean isAdd; /** 操作序号. */ int index; public Action(CharSequence actionTag, int startCursor, boolean add) { this.actionTarget = actionTag; this.startCursor = startCursor; this.endCursor = startCursor; this.isAdd = add; } public void setSelectCount(int count) { this.endCursor = endCursor + count; } public void setIndex(int index) { this.index = index; } } private static void CheckNull(Object o, String message) { if (o == null) throw new IllegalStateException(message); } }