2013-02-08 54 views
0

我試圖在對其進行一批類似更改後每次創建EditText內容的快照。Android TextWatcher保存撤銷/重做的類似更改的批次

例如像:

  1. 當我連續/重疊文本幾個連續的刪除,這是一個批次和我做的EditText的內容,從這個

  2. 結果的快照時,我做連續/重疊的文本連續幾次增加,這是一個批次,我做了一個快照的EditText內容,從這個

  3. 產生的,當我連續替換(= modifys)連續幾次VE /重疊的文本,這是一個批次和我做的EditText的內容,從這個

我希望你的想法導致的快照。

供參考:我需要這個撤消/重做機制,因爲我不想/可以使EVERYTIME文本更改快照。

我該如何實現目標?

失敗的代碼被寫入,因爲我雖然(在嘗試代碼之前)的頂級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) { 
     } 

    }); 

回答

0

也許有人需要這個呢。批量撤銷/重做實施:

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) { 
     } 
    } 
} 
+0

https://gist.github.com/kidinov/6900164中的新更改 – Webserveis 2016-06-26 06:31:11