0

My EditorFactory is crashing under Google Play testing. The class is embedded in a Renderer I have wrote to extend the Xamarin Forms Editor control.

Error:

    FATAL EXCEPTION: Thread-3
Process: com.MyApp.MyApp, PID: 23307
android.runtime.JavaProxyThrowable: System.InvalidCastException: Specified cast is not valid.
  at MyApp.EditorExRenderer+NoCopyEditableFactory.NewEditable (Java.Lang.ICharSequence source) [0x00000] in <0c33163fb4704d40bbc13286b4698089>:0 
  at Android.Text.EditableFactory.n_NewEditable_Ljava_lang_CharSequence_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_source) [0x0000f] in <0ce592b1e5104b27bf696797bee3407f>:0 
  at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPL_L (_JniMarshal_PPL_L callback, System.IntPtr jnienv, System.IntPtr klazz, System.IntPtr p0) [0x00005] in <0ce592b1e5104b27bf696797bee3407f>:0 
        at crc645beb7f91af2d2a49.EditorExRenderer_NoCopyEditableFactory.n_newEditable(Native Method)
        at crc645beb7f91af2d2a49.EditorExRenderer_NoCopyEditableFactory.newEditable(EditorExRenderer_NoCopyEditableFactory.java:29)
        at android.widget.TextView.setText(TextView.java:6219)
        at android.widget.TextView.setText(TextView.java:6156)
        at android.widget.EditText.setText(EditText.java:121)
        at android.widget.TextView.setText(TextView.java:6108)
        at androidx.test.espresso.action.ReplaceTextAction.perform(ReplaceTextAction.java:1)
        at androidx.test.tools.crawler.platform.hybrid.HybridInteractionController$1.perform(HybridInteractionController.java:2)
        at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:2)
        at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:21)
        at androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform(Unknown Source:0)
        at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:6)
        at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:1)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7664)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

A skeleton of my renderer code is below. I hope it's enough to give a rough idea of how I'm using the factory:

public class EditorExRenderer : EditorEx
{
  public EditorExRenderer(Android.Content.Context context) : base(context)
  {
  }

  private class NoCopyEditableFactory : EditableFactory // Editable.Factory in Java
  {
    public override IEditable NewEditable(ICharSequence source) => (IEditable)source;
  }

  void Load(string displayString)
  {
    ...
    ...
    SetSpannable(displayString);
    ...
    ...
  }

  void SetSpannable(string text)
  {
    _spannable = new SpannableStringBuilder(text);

    //set span
    Span span = new Span();
    ...
    ...
    _spannable.SetSpan(span, start, end, SpanTypes.InclusiveInclusive);
    ...
    ...

    Control.SetText(_spannable, TextView.BufferType.Spannable);

  }

  private void Control_TextChanged(object sender, Android.Text.TextChangedEventArgs e)
  {
    ...
    ...

    SetSpannable(newText);
  }

}

Any ideas of why it might be crashing?

jho
  • 199
  • 1
  • 8
  • Looks like exception is telling you that `public override IEditable NewEditable(ICharSequence source) => (IEditable)source;` is an `invalid cast`. Add debugging code or breakpoint to find out the class of `source`. It seems that it isn't a class that inherits from `IEditable`. – ToolmakerSteve Jul 10 '22 at 22:06
  • Thanks for you reply Steve, I have a solution, will post here. – jho Jul 13 '22 at 06:26

2 Answers2

1

ICharSequence and IEditable are two different kinds of interface. So we can't do a type cast between them.

In addition, you can try to create a instance of the EditableFactory and use it somewhere. Such as:

var NoCopyEditableFactory = new EditableFactory();
Control.SetEditableFactory(NoCopyEditableFactory);
  
Liyun Zhang - MSFT
  • 8,271
  • 1
  • 2
  • 14
  • Thanks for your reply Liyun, I've worked it out and will post soon. With regards to the constructor, the **Control** will be null until the `protected override void OnElementChanged(ElementChangedEventArgs e)` method is called. That's why I delay its usage. – jho Jul 13 '22 at 06:29
  • All right, sorry for my careless, I just want to show the way how to use EditableFactory. Can you mark it or your answer as an accepted answer?@jho – Liyun Zhang - MSFT Jul 13 '22 at 06:41
  • No worries, aside from that your solution is fine and was helpful. For the benefit of other readers I think both solutions can be accepted as answers. I can't vote for my own answer, so I'll vote for yours. Feel free to vote for mine as well if you think it is also an acceptable solution. Cheers – jho Jul 14 '22 at 03:54
  • After further testing, I discovered my new solution doesn't work. Please see an **Update** to my "Answer" above. – jho Jul 14 '22 at 11:26
  • Do you mean this line `textPaint.Color = Color` doesn't work? @jho – Liyun Zhang - MSFT Jul 15 '22 at 05:43
  • In the `SpanChange()` method, when I call `span.SetColor()` followed by `Invalidate()`, I believe the colour should change automatically. It doesn't so I re-Set the span to `_spannable`, and it worked. Now with my new code, even that doesn't work. – jho Jul 15 '22 at 07:56
  • I can't find the setColor method of the span. It just has the BackgroundColor, ForegroundColor and TextColor property.@jho – Liyun Zhang - MSFT Jul 15 '22 at 08:09
  • You can try to create the instance of the span by the construction method, such as `Span span = new ForegroundColorSpanEx(cb.Color)`.@jho – Liyun Zhang - MSFT Jul 15 '22 at 08:54
  • Please see my derived class in the code below: `public class ForegroundColorSpanEx : ForegroundColorSpan`. I corrected the code too to reflect it was a `ForegroundColorSpanEx`, not just a `Span`. My bad. – jho Jul 15 '22 at 08:55
  • Just set the correct `ForegroundColorSpanEx`, and same problem. In actual fact the error wasn't in my base code, it was in the code I edited for posting here at stackoverflow. The other odd thing is when I put a breakpoint on `UpdateDrawState`, it correctly reflects the change in color, but the new color is not updated on screen. – jho Jul 15 '22 at 08:57
  • But according to [this case](https://stackoverflow.com/questions/4897349/android-coloring-part-of-a-string-using-textview-settext), everything is right.@jho – Liyun Zhang - MSFT Jul 15 '22 at 09:15
  • Actually, I can't understand what you want clearly. Do you want to change the editor's text color in the xamarin.forms?@jho – Liyun Zhang - MSFT Jul 15 '22 at 09:17
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/246456/discussion-between-jho-and-liyun-zhang-msft). – jho Jul 15 '22 at 09:24
1

I had copied the NoCopyEditableFactory blindly from another StackOverflow post, so I removed it and everything works fine:

public class EditorExRenderer : EditorEx
{
  public EditorExRenderer(Android.Content.Context context) : base(context)
  {
  }

    protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {
            ...
            ... 
            //****************
            //create new SpannableFactory, rather than NoCopyEditableFactory
            Control.SetSpannableFactory(new SpannableFactory());
            ...
            ... 

        }

    }

  //private class NoCopyEditableFactory : EditableFactory // Editable.Factory in Java
  //{
    //public override IEditable NewEditable(ICharSequence source) => (IEditable)source;
  //}

  void Load(string displayString)
  {
    ...
    ...
    SetSpannable(displayString);
    ...
    ...
  }

  void SetSpannable(string text)
  {
    _spannable = new SpannableStringBuilder(text);

    //set span
    ForegroundColorSpanEx span = new Span();
    ...
    ...
    _spannable.SetSpan(span, start, end, SpanTypes.InclusiveInclusive);
    ...
    ...

    Control.SetText(_spannable, TextView.BufferType.Spannable);

  }

  private void Control_TextChanged(object sender, Android.Text.TextChangedEventArgs e)
  {
    ...
    ...

    SetSpannable(newText);
  }

}

UPDATE

I discovered my new code is creating errors. With the previous NoCopyEditableFactory, the following SpanChange method could dynamically change the color of the text font. With my new code above the dynamic coloring doesn't work. I tried using the default EditableFactory, instead of the NoCopyEditableFactory, and this also doesn't dynamically color the text.

So I am back to square one again. Any ideas?

void SpanChange(int index)
{
  Span span;
  int start, end;

  span = _spans[index];
  start = _spannable.GetSpanStart(span);
  end = _spannable.GetSpanEnd(span);

  span.SetColor(cb.Color);

  _spannable.SetSpan(span, start, end, SpanTypes.InclusiveInclusive);

  Invalidate();

}


public class ForegroundColorSpanEx : ForegroundColorSpan
{
  private Color Color;

    public ForegroundColorSpanEx(Color c)
      : base(c)
  {
    Color = c;
  }
  public override void UpdateDrawState(TextPaint textPaint)
  {
    base.UpdateDrawState(textPaint);
    textPaint.Color = Color;

  }
  public void SetColor(Color c)
  {
    Color = c;
  }
}
jho
  • 199
  • 1
  • 8
  • If you are just using the default spannable factory class, I don't think you need that line at all. TextView should already have what it needs. As a test, comment it out. Let me know if I am wrong. – ToolmakerSteve Jul 13 '22 at 18:30
  • I commented it out and you are right, it still works. Haven't run it through the more rigorous Google Play testing yet, but I suspect it will be ok there too. Cheers – jho Jul 14 '22 at 03:49
  • After further testing, I discovered my new solution doesn't work. Please see an **Update** to my "Answer" above. – jho Jul 14 '22 at 11:27
  • Please see my **UPDATE** section above. It explains what I regard as not working. Briefly here, yes, it is color related. – jho Jul 14 '22 at 23:26
  • I've re edited and clarified above. Though the code no longer crashes in Google Play closed testing, the dynamic colouring no longer works. – jho Jul 15 '22 at 04:40