Java Android TextWatcher为撤消/重做保存类似更改的批处理

Java Android TextWatcher为撤消/重做保存类似更改的批处理,java,android,android-edittext,undo,textwatcher,Java,Android,Android Edittext,Undo,Textwatcher,每次对EditText内容进行一批类似的更改后,我都会尝试对其进行快照 例如: 当我对连续/重叠文本执行几次连续删除时,这是一个批处理,我对由此产生的EditText内容进行快照 当我在连续/重叠的文本上进行几个连续添加时,这是一个批处理,我对由此产生的EditText内容进行快照 当我对连续/重叠的文本执行多个连续替换(=修改)时,这是一个批处理,我对由此产生的EditText内容进行快照 我希望你能明白 仅供参考:我需要一个撤销/重做机制,因为我不想/可以在每次文本更改时创建快照 我怎样才能

每次对EditText内容进行一批类似的更改后,我都会尝试对其进行快照

例如:

  • 当我对连续/重叠文本执行几次连续删除时,这是一个批处理,我对由此产生的EditText内容进行快照

  • 当我在连续/重叠的文本上进行几个连续添加时,这是一个批处理,我对由此产生的EditText内容进行快照

  • 当我对连续/重叠的文本执行多个连续替换(=修改)时,这是一个批处理,我对由此产生的EditText内容进行快照

  • 我希望你能明白

    仅供参考:我需要一个撤销/重做机制,因为我不想/可以在每次文本更改时创建快照

    我怎样才能实现我的目标

    编写失败的代码是因为(在尝试代码之前)我认为toplevel if语句的条件适用于删除文本/添加文本/修改文本

    那么,我如何才能改变这一点,使我有一个很酷的文本编辑器,如撤销/重做机制后端

    editBigField.addTextChangedListener(new TextWatcher() {
    
          private static final int NONE = -1;
          private static final int DELETE = 0;
          private static final int ADD = 1;
          private static final int MODIFY = 2;
    
          private int lastAction = NONE;
    
          private int delete;
    
          private int addstart;
          private int addend;
    
          private int modstart;
          private int modend;
    
          @Override
          public void afterTextChanged(Editable s) {
          }
    
          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            if (after == 0) // delete text
            {
              if (lastAction != DELETE || !(start <= delete && start + count - 1 >= delete)) {
                // save state here TODO
                lastAction = DELETE;
              }
              delete = start;
            }
            else if (count == 0 && after > 0) // add text
            {
              if (lastAction != ADD || !(start >= addstart - 1 && start <= addend)) {
                // save state here TODO
                lastAction = ADD;
              }
              addstart = start;
              addend = start + after;
            }
            else if (count != 0 && after > 0) // modify/replace text
            {
              if (lastAction != MODIFY || !(start + count - 1 >= modstart - 1 && start <= modend)) {
                // save state here TODO
                lastAction = MODIFY;
              }
              modstart = start;
              modend = start + after;
            }
    
          }
    
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
          }
    
        });
    
    editBigField.addTextChangedListener(新的TextWatcher(){
    私有静态final int NONE=-1;
    私有静态final int DELETE=0;
    专用静态最终整数相加=1;
    私有静态final int MODIFY=2;
    private int lastAction=NONE;
    私有int删除;
    私有int addstart;
    专用整数加数;
    私人int modstart;
    私有int模式;
    @凌驾
    公共无效后文本已更改(可编辑){
    }
    @凌驾
    更改前文本之前的公共void(字符序列s、int start、int count、int after){
    if(after==0)//删除文本
    {
    if(lastAction!=删除| |!(start=删除)){
    //在此处保存状态待办事项
    lastAction=删除;
    }
    删除=开始;
    }
    else if(count==0&&after>0)//添加文本
    {
    if(lastAction!=ADD | |!(start>=addstart-1&&start 0)//修改/替换文本
    {
    
    如果(lastAction!=MODIFY | | |!(start+count-1>=modstart-1&&start可能有人需要它。使用批撤消/重做实现:

    public class UndoRedoHelper {
        private static final String TAG = UndoRedoHelper.class.getCanonicalName();
        private boolean mIsUndoOrRedo = false;
    
        private EditHistory mEditHistory;
    
        private EditTextChangeListener mChangeListener;
    
        private TextView mTextView;
    
        // =================================================================== //
    
        public UndoRedoHelper(TextView textView) {
            mTextView = textView;
            mEditHistory = new EditHistory();
            mChangeListener = new EditTextChangeListener();
            mTextView.addTextChangedListener(mChangeListener);
        }
    
        // =================================================================== //
    
        public void disconnect() {
            mTextView.removeTextChangedListener(mChangeListener);
        }
    
        public void setMaxHistorySize(int maxHistorySize) {
            mEditHistory.setMaxHistorySize(maxHistorySize);
        }
    
        public void clearHistory() {
            mEditHistory.clear();
        }
    
        public boolean getCanUndo() {
            return (mEditHistory.mmPosition > 0);
        }
    
        public void undo() {
            EditItem edit = mEditHistory.getPrevious();
            if (edit == null) {
                return;
            }
    
            Editable text = mTextView.getEditableText();
            int start = edit.mmStart;
            int end = start + (edit.mmAfter != null ? edit.mmAfter.length() : 0);
    
            mIsUndoOrRedo = true;
            text.replace(start, end, edit.mmBefore);
            mIsUndoOrRedo = false;
    
            for (Object o : text.getSpans(0, text.length(), UnderlineSpan.class)) {
                text.removeSpan(o);
            }
    
            Selection.setSelection(text, edit.mmBefore == null ? start : (start + edit.mmBefore.length()));
        }
    
        public boolean getCanRedo() {
            return (mEditHistory.mmPosition < mEditHistory.mmHistory.size());
        }
    
        public void redo() {
            EditItem edit = mEditHistory.getNext();
            if (edit == null) {
                return;
            }
    
            Editable text = mTextView.getEditableText();
            int start = edit.mmStart;
            int end = start + (edit.mmBefore != null ? edit.mmBefore.length() : 0);
    
            mIsUndoOrRedo = true;
            text.replace(start, end, edit.mmAfter);
            mIsUndoOrRedo = false;
    
            // This will get rid of underlines inserted when editor tries to come
            // up with a suggestion.
            for (Object o : text.getSpans(0, text.length(), UnderlineSpan.class)) {
                text.removeSpan(o);
            }
    
            Selection.setSelection(text, edit.mmAfter == null ? start
                    : (start + edit.mmAfter.length()));
        }
    
        public void storePersistentState(Editor editor, String prefix) {
            // Store hash code of text in the editor so that we can check if the
            // editor contents has changed.
            editor.putString(prefix + ".hash",
                    String.valueOf(mTextView.getText().toString().hashCode()));
            editor.putInt(prefix + ".maxSize", mEditHistory.mmMaxHistorySize);
            editor.putInt(prefix + ".position", mEditHistory.mmPosition);
            editor.putInt(prefix + ".size", mEditHistory.mmHistory.size());
    
            int i = 0;
            for (EditItem ei : mEditHistory.mmHistory) {
                String pre = prefix + "." + i;
    
                editor.putInt(pre + ".start", ei.mmStart);
                editor.putString(pre + ".before", ei.mmBefore.toString());
                editor.putString(pre + ".after", ei.mmAfter.toString());
    
                i++;
            }
        }
    
        public boolean restorePersistentState(SharedPreferences sp, String prefix)
                throws IllegalStateException {
    
            boolean ok = doRestorePersistentState(sp, prefix);
            if (!ok) {
                mEditHistory.clear();
            }
    
            return ok;
        }
    
        private boolean doRestorePersistentState(SharedPreferences sp, String prefix) {
            String hash = sp.getString(prefix + ".hash", null);
            if (hash == null) {
                // No state to be restored.
                return true;
            }
    
            if (Integer.valueOf(hash) != mTextView.getText().toString().hashCode()) {
                return false;
            }
    
            mEditHistory.clear();
            mEditHistory.mmMaxHistorySize = sp.getInt(prefix + ".maxSize", -1);
    
            int count = sp.getInt(prefix + ".size", -1);
            if (count == -1) {
                return false;
            }
    
            for (int i = 0; i < count; i++) {
                String pre = prefix + "." + i;
    
                int start = sp.getInt(pre + ".start", -1);
                String before = sp.getString(pre + ".before", null);
                String after = sp.getString(pre + ".after", null);
    
                if (start == -1 || before == null || after == null) {
                    return false;
                }
                mEditHistory.add(new EditItem(start, before, after));
            }
    
            mEditHistory.mmPosition = sp.getInt(prefix + ".position", -1);
            if (mEditHistory.mmPosition == -1) {
                return false;
            }
    
            return true;
        }
    
        // =================================================================== //
    
        private final class EditHistory {
            private int mmPosition = 0;
            private int mmMaxHistorySize = -1;
    
            private final LinkedList<EditItem> mmHistory = new LinkedList<EditItem>();
    
            private void clear() {
                mmPosition = 0;
                mmHistory.clear();
            }
    
            private void add(EditItem item) {
                while (mmHistory.size() > mmPosition) {
                    mmHistory.removeLast();
                }
                mmHistory.add(item);
                mmPosition++;
    
                if (mmMaxHistorySize >= 0) {
                    trimHistory();
                }
            }
    
            private void setMaxHistorySize(int maxHistorySize) {
                mmMaxHistorySize = maxHistorySize;
                if (mmMaxHistorySize >= 0) {
                    trimHistory();
                }
            }
    
            private void trimHistory() {
                while (mmHistory.size() > mmMaxHistorySize) {
                    mmHistory.removeFirst();
                    mmPosition--;
                }
    
                if (mmPosition < 0) {
                    mmPosition = 0;
                }
            }
    
            private EditItem getCurrent() {
                if (mmPosition == 0) {
                    return null;
                }
                return mmHistory.get(mmPosition - 1);
            }
    
            private EditItem getPrevious() {
                if (mmPosition == 0) {
                    return null;
                }
                mmPosition--;
                return mmHistory.get(mmPosition);
            }
    
            private EditItem getNext() {
                if (mmPosition >= mmHistory.size()) {
                    return null;
                }
    
                EditItem item = mmHistory.get(mmPosition);
                mmPosition++;
                return item;
            }
        }
    
        private final class EditItem {
            private int mmStart;
            private CharSequence mmBefore;
            private CharSequence mmAfter;
    
            public EditItem(int start, CharSequence before, CharSequence after) {
                mmStart = start;
                mmBefore = before;
                mmAfter = after;
            }
    
            @Override
            public String toString() {
                return "EditItem{" +
                        "mmStart=" + mmStart +
                        ", mmBefore=" + mmBefore +
                        ", mmAfter=" + mmAfter +
                        '}';
            }
        }
    
        enum ActionType {
            INSERT, DELETE, PASTE, NOT_DEF;
        }
    
        private final class EditTextChangeListener implements TextWatcher {
            private CharSequence mBeforeChange;
            private CharSequence mAfterChange;
            private ActionType lastActionType = ActionType.NOT_DEF;
            private long lastActionTime = 0;
    
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                if (mIsUndoOrRedo) {
                    return;
                }
    
                mBeforeChange = s.subSequence(start, start + count);
            }
    
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if (mIsUndoOrRedo) {
                    return;
                }
    
                mAfterChange = s.subSequence(start, start + count);
                makeBatch(start);
            }
    
            private void makeBatch(int start) {
                ActionType at = getActionType();
                EditItem editItem = mEditHistory.getCurrent();
                if ((lastActionType != at || ActionType.PASTE == at || System.currentTimeMillis() - lastActionTime > 1000) || editItem == null) {
                    mEditHistory.add(new EditItem(start, mBeforeChange, mAfterChange));
                } else {
                    if (at == ActionType.DELETE) {
                        editItem.mmStart = start;
                        editItem.mmBefore = TextUtils.concat(mBeforeChange, editItem.mmBefore);
                    } else {
                        editItem.mmAfter = TextUtils.concat(editItem.mmAfter, mAfterChange);
                    }
                }
                lastActionType = at;
                lastActionTime = System.currentTimeMillis();
            }
    
            private ActionType getActionType() {
                if (!TextUtils.isEmpty(mBeforeChange) && TextUtils.isEmpty(mAfterChange)) {
                    return ActionType.DELETE;
                } else if (TextUtils.isEmpty(mBeforeChange) && !TextUtils.isEmpty(mAfterChange)) {
                    return ActionType.INSERT;
                } else {
                    return ActionType.PASTE;
                }
            }
    
            public void afterTextChanged(Editable s) {
            }
        }
    }
    
    公共类{
    私有静态最终字符串标记=UndoRedoHelper.class.getCanonicalName();
    私有布尔值misundorredo=false;
    私人编辑历史医学;
    私有EditTextChangeListener-McChangeListener;
    私有文本视图mTextView;
    // =================================================================== //
    公共撤消还原帮助程序(TextView TextView){
    mTextView=textView;
    mEditHistory=新编辑历史();
    McChangeListener=新的EditTextChangeListener();
    mTextView.addTextChangedListener(McChangeListener);
    }
    // =================================================================== //
    公共空间断开连接(){
    removeTextChangedListener(McChangeListener);
    }
    public void setMaxHistorySize(int maxHistorySize){
    mEditHistory.setMaxHistorySize(maxHistorySize);
    }
    公共历史{
    医学的;
    }
    公共布尔getCanUndo(){
    返回(mEditHistory.mm位置>0);
    }
    公共作废撤消(){
    EditItem edit=mEditHistory.getPrevious();
    如果(编辑==null){
    返回;
    }
    可编辑文本=mTextView.getEditableText();
    int start=edit.mmStart;
    int end=start+(edit.mmAfter!=null?edit.mmAfter.length():0);
    misundorredo=正确;
    text.replace(开始、结束、编辑.mmBefore);
    misundorredo=假;
    for(对象o:text.getSpans(0,text.length(),UnderlineSpan.class)){
    文本。删除span(o);
    }
    Selection.setSelection(text,edit.mmBefore==null?start:(start+edit.mmBefore.length());
    }
    公共布尔getCanRedo(){
    返回(mEditHistory.mmPosition