Android Question Undo and Redo for EditText

Hedi

Member
Hello guys,

Is there is any way to Undo and Redo in EditText?

I search the forum and not find anything for android.


Thanks.
 
Solution
Here is an example of how Undo and Redo action works in EditText, step by step:

First add this class:
manager:
Sub Class_Globals
    Private stack As List
    Private ser As B4XSerializator
    Private index As Int
    Private const MAX_STACK_SIZE As Int = 100
End Sub

Public Sub Initialize (InitialState As Object)
    stack.Initialize
    index = -1
    AddState(InitialState)
End Sub

Public Sub AddState (state As Object)
    Dim b() As Byte = ser.ConvertObjectToBytes(state)
    If DifferentThanPrevState(b) Then
        If index < stack.Size - 1 Then
            'this happens when a new state is added after one or more undo actions.
            For i = stack.Size - 1 To index + 1 Step - 1
                stack.RemoveAt(i)
            Next...

Hedi

Member
When I run the app I get this error:

B4X:
manager_addstate (java line: 42)
java.lang.NullPointerException: Attempt to invoke virtual method "byte[] anywheresoftware.b4a.randomaccessfile.B4XSerializator.ConvertObjectToBytes(java.lang.Object)" on a null object reference
    at hedigrand.lucidzone.manager._addstate(manager.java:42)
    at hedigrand.lucidzone.main._timersettings_tick(main.java:16588)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:221)
    at anywheresoftware.b4a.objects.Timer$TickTack.run(Timer.java:105)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:210)
    at android.os.Looper.loop(Looper.java:299)
    at android.app.ActivityThread.main(ActivityThread.java:8250)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)
 
Last edited:
Upvote 0

Hedi

Member
When I run the app I get this error:

B4X:
manager_addstate (java line: 42)
java.lang.NullPointerException: Attempt to invoke virtual method "byte[] anywheresoftware.b4a.randomaccessfile.B4XSerializator.ConvertObjectToBytes(java.lang.Object)" on a null object reference
    at hedigrand.lucidzone.manager._addstate(manager.java:42)
    at hedigrand.lucidzone.main._timersettings_tick(main.java:16588)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:221)
    at anywheresoftware.b4a.objects.Timer$TickTack.run(Timer.java:105)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:210)
    at android.os.Looper.loop(Looper.java:299)
    at android.app.ActivityThread.main(ActivityThread.java:8250)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)

Fixed! I forgot to call:

manager1.Initialize(EditText1.Text)


Thank you Erel, I am proud of you.
 
Upvote 0

Hedi

Member
Here is an example of how Undo and Redo action works in EditText, step by step:

First add this class:
manager:
Sub Class_Globals
    Private stack As List
    Private ser As B4XSerializator
    Private index As Int
    Private const MAX_STACK_SIZE As Int = 100
End Sub

Public Sub Initialize (InitialState As Object)
    stack.Initialize
    index = -1
    AddState(InitialState)
End Sub

Public Sub AddState (state As Object)
    Dim b() As Byte = ser.ConvertObjectToBytes(state)
    If DifferentThanPrevState(b) Then
        If index < stack.Size - 1 Then
            'this happens when a new state is added after one or more undo actions.
            For i = stack.Size - 1 To index + 1 Step - 1
                stack.RemoveAt(i)
            Next
        End If
        stack.Add(b)
        If stack.Size >= MAX_STACK_SIZE Then
            stack.RemoveAt(1) 'keep the initial state
        End If
        index = stack.Size - 1
    End If
    'Log($"Stack size: ${stack.Size}"$)
End Sub

Public Sub Undo As Object
    If index > 0 Then index = index - 1
    Return ser.ConvertBytesToObject(stack.Get(index))
End Sub

'Will return Null if there is no redo state available.
Public Sub Redo As Object
    If index < stack.Size - 1 Then
        index = index + 1
        Return ser.ConvertBytesToObject(stack.Get(index))
    End If
    Return Null
End Sub

Private Sub DifferentThanPrevState(b() As Byte) As Boolean
    If index = -1 Then Return True
    Dim prev() As Byte = stack.Get(index)
    Return Not(CompareArrays(prev, b))
End Sub

Private Sub CompareArrays(a1() As Byte, a2() As Byte) As Boolean
    If a1.Length <> a2.Length Then Return False
    For i = 0 To a1.Length - 1
        If a1(i) <>  a2(i) Then Return False
    Next
    Return True
End Sub


Add this in Main Sub Globals:
Dim manager1 As manager


Initialize the manager:
manager1.Initialize(EditText1.Text)


In Main Sub Process_Globals, add this:
Type UndoData (text As String, selStart As Int)


Add this sub in Main:
Sub GetState As UndoData
    Dim ud As UndoData
    ud.Initialize
   
    ud.Text = EditText1.Text
    ud.selStart = EditText1.SelectionStart
   
    Return ud
End Sub


Add a Timer and set it to Enabled = True and Interval = 100:
Sub Timer1_Tick
   
    manager1.AddState(GetState) 'nothing will happen if the state is the same as the previous one
   
End Sub


Add this sub in Main:
Sub SetState (ud As UndoData)
   
    EditText1.Text = ud.text
    EditText1.SelectionStart = ud.selStart
   
End Sub


Undo Button:
Private Sub UndoBtn_Click
       
    Dim ud As UndoData = manager1.Undo
    SetState(ud)
       
End Sub


Redo Button:
Private Sub RedoBtn_Click
       
    Dim ud As UndoData = manager1.Redo
    SetState(ud)
       
End Sub
 
Last edited:
Upvote 0
Solution
Top