package net.micode.notes.tool; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.Editable; import android.text.TextWatcher; import android.widget.EditText; import java.io.Serializable; import java.util.Collection; import java.util.LinkedList; public class EditTextManager implements TextWatcher { private static final String KEY_UNDO_OPTS = "KEY_UNDO_OPTS"; private static final String KEY_REDO_OPTS = "KEY_REDO_OPTS"; /*EditText对象*/ private final EditText editText; /*编辑记录栈*/ private final LinkedList undoOpts = new LinkedList<>(); private final LinkedList redoOpts = new LinkedList<>(); private EditOperation opt; private boolean enable = true; public EditTextManager(EditText editText) { this.editText = editText; } public static EditTextManager setup(EditText editText) { EditTextManager mgr = new EditTextManager(editText); editText.addTextChangedListener(mgr); return mgr; } public EditTextManager disable() { enable = false; return this; } public EditTextManager enable() { enable = true; return this; } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { if (count > 0) { int end = start + count; if (enable) { if (opt == null) { opt = new EditOperation(); } opt.setSrc(s.subSequence(start, end), start, end); } } } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (count > 0) { int end = start + count; if (enable) { if (opt == null) { opt = new EditOperation(); } opt.setDst(s.subSequence(start, end), start, end); } } } @Override public void afterTextChanged(Editable s) { if (enable && opt != null) { if (!redoOpts.isEmpty()) { redoOpts.clear(); } undoOpts.push(opt); } opt = null; } public boolean canUndo() { return !undoOpts.isEmpty(); } public boolean canRedo() { return !redoOpts.isEmpty(); } public boolean undo() { if (canUndo()) { EditOperation undoOpt = undoOpts.pop(); //屏蔽撤销产生的事件 disable(); undoOpt.undo(editText); enable(); //填入重做栈 redoOpts.push(undoOpt); return true; } return false; } /*保存/回复*/ public boolean redo() { if (canRedo()) { EditOperation redoOpt = redoOpts.pop(); //屏蔽重做产生的事件 disable(); redoOpt.redo(editText); enable(); //填入撤销 undoOpts.push(redoOpt); return true; } return false; } public Bundle exportState() { Bundle state = new Bundle(); state.putSerializable(KEY_UNDO_OPTS, undoOpts); state.putSerializable(KEY_REDO_OPTS, redoOpts); return state; } public void importState(Bundle state) { Collection savedUndoOpts = (Collection) state.getSerializable(KEY_UNDO_OPTS); undoOpts.clear(); undoOpts.addAll(savedUndoOpts); Collection savedRedoOpts = (Collection) state.getSerializable(KEY_REDO_OPTS); redoOpts.clear(); redoOpts.addAll(savedRedoOpts); } private static class EditOperation implements Parcelable, Serializable { public static final Creator CREATOR = new Creator() { @Override public EditOperation createFromParcel(Parcel source) { return new EditOperation(source); } @Override public EditOperation[] newArray(int size) { return new EditOperation[size]; } }; private String src; private int srcStart; private int srcEnd; private String dst; private int dstStart; private int dstEnd; EditOperation() { } EditOperation(Parcel in) { this.src = in.readString(); this.srcStart = in.readInt(); this.srcEnd = in.readInt(); this.dst = in.readString(); this.dstStart = in.readInt(); this.dstEnd = in.readInt(); } void setSrc(CharSequence src, int srcStart, int srcEnd) { this.src = src != null ? src.toString() : ""; this.srcStart = srcStart; this.srcEnd = srcEnd; } void setDst(CharSequence dst, int dstStart, int dstEnd) { this.dst = dst != null ? dst.toString() : ""; this.dstStart = dstStart; this.dstEnd = dstEnd; } /*撤销实现*/ void undo(EditText text) { Editable editable = text.getText(); int idx = -1; if (dstEnd > 0) { editable.delete(dstStart, dstEnd); if (src == null) { idx = dstStart; } } if (src != null) { editable.insert(srcStart, src); idx = srcStart + src.length(); } if (idx >= 0) { text.setSelection(idx); } } /*反撤销实现*/ void redo(EditText text) { Editable editable = text.getText(); int idx = -1; if (srcEnd > 0) { editable.delete(srcStart, srcEnd); if (dst == null) { idx = srcStart; } } if (dst != null) { editable.insert(dstStart, dst); idx = dstStart + dst.length(); } if (idx >= 0) { text.setSelection(idx); } } @Override public int describeContents() { return 0; } @Override /*序列化*/ public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.src); dest.writeInt(this.srcStart); dest.writeInt(this.srcEnd); dest.writeString(this.dst); dest.writeInt(this.dstStart); dest.writeInt(this.dstEnd); } } }