B4A Library LabelExtras - advanced click handling

Discussion in 'Additional libraries, classes and official updates' started by warwound, Jan 30, 2013.

  1. warwound

    warwound Expert Licensed User

    LabelExtras enables you to apply click listeners to sub-strings of a Label text.
    A click on a sub-string raises an event and you can take whatever action you desire on such a click.

    LabelExtras also exposes the Android Html class so you can use basic HTML formatting elements such as <b>, <i> and <u> in your Label text.

    Here's the library reference:

    LabelExtras
    Version:
    1.12
    • ClickableSpan
      Events:
      • Click (SelectionStart As Int, SelectionEnd As Int)
      • Click2 (SelectionStart As Int, SelectionEnd As Int, Tag As Object)
      Methods:
      • Initialize (EventName As String, Color As Int, UnderlineText As Boolean)
        Initialize the ClickableSpan.
        Color defines the color of the clickable text, pass -1 if you do not wish to change that text color.
        UnderlineText defines whether the clickable text shall be underlined.
        The Click(SelectionStart As Int, SelectionEnd As Int) event will be raised when this ClickableSpan is clicked.
      • Initialize2 (EventName As String, Color As Int, UnderlineText As Boolean, Tag As Object)
        Initialize the ClickableSpan.
        Color defines the color of the clickable text, pass -1 if you do not wish to change that text color.
        UnderlineText defines whether the clickable text shall be underlined.
        Pass any Object as the Tag and this Object will be returned to the Click2 event.
        The Click2(SelectionStart As Int, SelectionEnd As Int, Tag As Object) event will be raised when this ClickableSpan is clicked.
      • IsInitialized As Boolean
    • Html
      Events:
      • GetDrawable (Source As String) As Object
      • HandleTag (Opening As Boolean, Tag As String, SpannableStringBuilder1 As SpannableStringBuilder)
      Methods:
      • EscapeHtml (CharSequence1 As CharSequence) As String
        Returns an HTML escaped representation of the given plain text.
        Only available with Android AP version 16+.
      • FromHtml (HtmlString As String) As CharSequence
        Returns displayable styled text from the provided HTML string.
      • FromHtml2 (HtmlString As String, ImageGetterEventName As String, TagHandlerEventName As String) As CharSequence
        Returns displayable styled text from the provided HTML string.
        Using optional ImageGetter (to handle HTML IMG tags in the String) and TagHandler (to handle unrecognised tags in the String) callbacks.
        The drawable returned by the ImageGetter sub has to be sized with setBounds.
      • ToHtml (CharSequence1 As CharSequence) As CharSequence
        Returns an HTML representation of the provided Spanned text.
    • LabelExtras
      Methods:
      • SetDrawableBounds (Drawable1 As Drawable, Left As Int, Top As Int, Right As Int, Bottom As Int)
        Specifies a bounding rectangle for the drawable.
        Required for the Html Object ImageGetter callback.
      • SetMovementMethod (Label1 As TextView, MovementMethod1 As MovementMethod)
        Sets the movement method (arrow key handler) to be used for this TextView.
        To enable the LabelExtras click functionality you must set your Label movement method to an instance of LinkMovementMethod.
        The LinkMovementMethod.GetInstance method returns the instance required.
    • LinkMovementMethod
      Methods:
      • GetInstance As MovementMethod
        Returns an instance of the LinkMovementMethod which is required by the LabelExtras.SetMovementMethod method.
    • SpannableStringBuilder
      Methods:
      • Append (Text As CharSequence) As SpannableStringBuilder
      • Append2 (Text As CharSequence, Start As Int, End As Int) As SpannableStringBuilder
      • Append3 (Char1 As Char) As SpannableStringBuilder
      • CharAt (Where As Int) As Char
      • Clear
      • ClearSpans
      • Delete (Start As Int, End As Int) As SpannableStringBuilder
      • GetChars (Start As Int, End As Int, Dest() As Char, DestOffset As Int)
      • GetSpanEnd (Span1 As Object) As Int
      • GetSpanFlags (Span1 As Object) As Int
      • GetSpanStart (Span1 As Object) As Int
      • GetStyleSpans (QueryStart As Int, QueryEnd As Int) As StyleSpan[]
      • GetURLSpans (QueryStart As Int, QueryEnd As Int) As URLSpan[]
      • GetUnderlineSpans (QueryStart As Int, QueryEnd As Int) As UnderlineSpan[]
      • Initialize (CharSequence1 As CharSequence)
        Initialize the SpannableStringBuilder with a CharSequence.
        A CharSequence can be obtained using the Html.FromHtml method, a String can also be passed as a CharacterSequence.
      • IsInitialized As Boolean
      • NextStyleSpanTransition (Start As Int, Limit As Int) As Int
        Return the next offset after Start but less than or equal to Limit where a StyleSpan begins or ends.
      • NextURLSpanTransition (Start As Int, Limit As Int) As Int
        Return the next offset after Start but less than or equal to Limit where a URLSpan begins or ends.
      • NextUnderlineSpanTransition (Start As Int, Limit As Int) As Int
        Return the next offset after Start but less than or equal to Limit where an UnderlineSpan begins or ends.
      • RemoveSpan (Span1 As Object)
      • Replace (Start As Int, End As Int, CharSequence1 As CharSequence) As SpannableStringBuilder
      • Replace2 (Start As Int, End As Int, CharSequence1 As CharSequence, CharSequence1Start As Int, CharSequence1End As Int) As SpannableStringBuilder
      • SetSpan (Span1 As Object, Start As Int, End As Int, Flags As Int)
      Properties:
      • Length As Int [read only]
        Return the number of Chars in the buffer.
    • Spanned
      Fields:
      • SPAN_EXCLUSIVE_EXCLUSIVE As Int
        Constant which can be passed as the SpannableStringBuilder.SetSpan method Flags parameter.
        Spans of type SPAN_EXCLUSIVE_EXCLUSIVE do not expand to include text inserted at either their starting or ending point.
      • SPAN_EXCLUSIVE_INCLUSIVE As Int
        Constant which can be passed as the SpannableStringBuilder.SetSpan method Flags parameter.
        Non-0-length spans of type SPAN_EXCLUSIVE_INCLUSIVE expand to include text inserted at their ending point but not at their starting point.
      • SPAN_INCLUSIVE_EXCLUSIVE As Int
        Constant which can be passed as the SpannableStringBuilder.SetSpan method Flags parameter.
        Non-0-length spans of type SPAN_INCLUSIVE_EXCLUSIVE expand to include text inserted at their starting point but not at their ending point.
      • SPAN_INCLUSIVE_INCLUSIVE As Int
        Constant which can be passed as the SpannableStringBuilder.SetSpan method Flags parameter.
        Spans of type SPAN_INCLUSIVE_INCLUSIVE expand to include text inserted at either their starting or ending point.
    • StyleSpan
      Methods:
      • GetStyle As Int
        Returns the Typeface style of this StyleSpan.
        Typeface.STYLE_BOLD, Typeface.STYLE_ITALIC etc.
      • IsInitialized As Boolean
    • URLSpan
      Methods:
      • GetURL As String
      • IsInitialized As Boolean
    • UnderlineSpan
      Methods:
      • IsInitialized As Boolean

    Example code to follow.

    Martin.
     

    Attached Files:

    Last edited: Sep 23, 2013
  2. warwound

    warwound Expert Licensed User

    And here's a simple example of LabelExtras:

    Code:
    Sub Process_Globals

    End Sub

    Sub Globals
       
    Dim ClickableLabel As Label
       
    Dim LogLabel As Label
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       
    '   the Labels used in this demo are defined in the layout file "Main"
       Activity.LoadLayout("Main")
       
       
    '   Dim the objects from the LabelExtras library
       Dim Html1 As Html
       
    Dim LabelExtras1 As LabelExtras
       
    Dim LinkMovementMethod1 As LinkMovementMethod
       
    Dim SpannableStringBuilder1 As SpannableStringBuilder
       
    Dim StyleSpans() As StyleSpan
       
       
    '   this is the String which contains HTML elements that we want to make clickable
       '   note that the Html FromHtml method parses the String as HTML so if you try to use the CRLF constant in this String it will not create a new line, instead the HTML <br> element can be used
       Dim HtmlString As String="Any text that is within HTML <b>BOLD</b> or <i>ITALIC</i> tags will have a click listener added.<br><br>Click <b>here</b> to start ActivityTwo."
       
       SpannableStringBuilder1.Initialize(Html1.FromHtml(HtmlString))
       
       
    '   get the SpanStyles and iterate through them
       '   making each StyleSpan a ClickableSpan
       StyleSpans=SpannableStringBuilder1.GetStyleSpans(0, SpannableStringBuilder1.Length)
       
    For Each StyleSpan1 As StyleSpan In StyleSpans
          
    Dim SpanStart As Int=SpannableStringBuilder1.GetSpanStart(StyleSpan1)
          
    Dim SpanEnd As Int=SpannableStringBuilder1.GetSpanEnd(StyleSpan1)
          
    Dim SpanFlags As Int=SpannableStringBuilder1.GetSpanFlags(StyleSpan1)
          
          
    Dim ClickableSpan1 As ClickableSpan
          
    '   these clickable spans will have red text but not be underlined
          ClickableSpan1.Initialize("ClickableSpan1"Colors.Red, False)
          
          SpannableStringBuilder1.SetSpan(ClickableSpan1, SpanStart, SpanEnd, SpanFlags)
       
    Next
       
       
    '   now set the Label.Text property 
       ClickableLabel.Text=SpannableStringBuilder1
       
       
    '   finally call the LabelExtras.SetMovementMethod
       '   this is required so that the click functionality works as desired
       LabelExtras1.SetMovementMethod(ClickableLabel, LinkMovementMethod1.GetInstance)

    End Sub

    Sub Activity_Resume

    End Sub

    Sub Activity_Pause (UserClosed As Boolean)

    End Sub

    Sub ClickableSpan1_Click(SelectionStart As Int, SelectionEnd As Int)
       
    Dim LogText As StringBuilder
       LogText.Initialize
       
       LogText.Append(
    "ClickableSpan1_Click: "&DateTime.Now&CRLF)
       LogText.Append(
    "SelectionStart="&SelectionStart&CRLF)
       LogText.Append(
    "SelectionEnd="&SelectionEnd&CRLF)
       
       
    Dim SelectedText As String=ClickableLabel.Text.SubString2(SelectionStart, SelectionEnd)
       LogText.Append(
    "Selected (clicked) text='"&SelectedText&"'")
       
       LogLabel.Text=LogText.ToString
       
       
    If SelectedText="here" Then
          
    StartActivity(ActivityTwo)
       
    End If
    End Sub
    Clicks on the text within <b> and <i> HTML elements raise an event.
    The event is passed the SelectionStart and SelectionEnd indexes of the clicked text.
    In the Sub that handles the event you can take whatever action you desire!

    Martin
     

    Attached Files:

    Elcid1, fredo, MarcoRome and 2 others like this.
  3. Informatix

    Informatix Expert Licensed User

    Thank you.
     
  4. Inman

    Inman Well-Known Member Licensed User

    Great job. Thanks mate.
     
  5. warwound

    warwound Expert Licensed User

    Thanks for the replies.
    Here is another example, showing how to use the StyleSpan GetStyle method to only add click listeners to StyleSpans that have a style of Typeface.STYLE_BOLD:

    Code:
    Sub Process_Globals

    End Sub

    Sub Globals
       
    Dim ClickableLabel As Label
       
    Dim LogLabel As Label
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       
    Activity.LoadLayout("Main")
       
       
    '   Dim the objects from the LabelExtras library
       Dim Html1 As Html
       
    Dim LabelExtras1 As LabelExtras
       
    Dim LinkMovementMethod1 As LinkMovementMethod
       
    Dim SpannableStringBuilder1 As SpannableStringBuilder
       
    Dim StyleSpans() As StyleSpan
       
       
    '   this is the String which contains HTML elements that we want to make clickable
       '   note that the Html FromHtml method parses the String as HTML so if you try to use the CRLF constant in this String it will not create a new line, instead the HTML <br> element can be used
       Dim HtmlString As String="This String contains text that is within <b>BOLD</b> and <i>ITALIC</i> HTML tags, but a click listener will <i>only</i> be added to the text within the <b>BOLD</b> tags."
       
       SpannableStringBuilder1.Initialize(Html1.FromHtml(HtmlString))
       
       StyleSpans=SpannableStringBuilder1.GetStyleSpans(
    0, SpannableStringBuilder1.Length)
       
    For Each StyleSpan1 As StyleSpan In StyleSpans
          
    If StyleSpan1.GetStyle=Typeface.STYLE_BOLD Then
             
    Dim SpanStart As Int=SpannableStringBuilder1.GetSpanStart(StyleSpan1)
             
    Dim SpanEnd As Int=SpannableStringBuilder1.GetSpanEnd(StyleSpan1)
             
    Dim SpanFlags As Int=SpannableStringBuilder1.GetSpanFlags(StyleSpan1)
             
             
    Dim ClickableSpan1 As ClickableSpan
             ClickableSpan1.Initialize(
    "ClickableSpan1"Colors.Red, False)
             
             SpannableStringBuilder1.SetSpan(ClickableSpan1, SpanStart, SpanEnd, SpanFlags)
          
    End If
       
    Next
       
       ClickableLabel.Text=SpannableStringBuilder1
       LabelExtras1.SetMovementMethod(ClickableLabel, LinkMovementMethod1.GetInstance)

    End Sub

    Sub Activity_Resume

    End Sub

    Sub Activity_Pause (UserClosed As Boolean)

    End Sub

    Sub ClickableSpan1_Click(SelectionStart As Int, SelectionEnd As Int)
       
    Dim LogText As StringBuilder
       LogText.Initialize
       
       LogText.Append(
    "ClickableSpan1_Click: "&DateTime.Now&CRLF)
       LogText.Append(
    "SelectionStart="&SelectionStart&CRLF)
       LogText.Append(
    "SelectionEnd="&SelectionEnd&CRLF)
       
       
    Dim SelectedText As String=ClickableLabel.Text.SubString2(SelectionStart, SelectionEnd)
       LogText.Append(
    "Selected (clicked) text='"&SelectedText&"'")
       
       LogLabel.Text=LogText.ToString
    End Sub
    It could easily have added click listeners only to StyleSpans that had a style of Typreface.STYLE_ITALIC:

    Code:
    If StyleSpan1.GetStyle=Typeface.STYLE_ITALIC Then
    I also experimented using a String with both bold and italic tags:

    Code:
    Dim HtmlString As String="... but a click listener will <b><i>only</i></b> be added ..."
    The StyleSpans Array contains two StyleSpan objects for this String, one StyleSpan has a style of Typeface.STYLE_BOLD and the other has a style of Typeface.STYLE_ITALIC.

    Martin.
     

    Attached Files:

  6. warwound

    warwound Expert Licensed User

    The LabelExtras library also contains an UnderlineSpan object which can be used to add click listeners to text within an HTML <u> underline tag:

    Code:
    Sub Process_Globals

    End Sub

    Sub Globals
       
    Dim ClickableLabel As Label
       
    Dim LogLabel As Label
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       
    Activity.LoadLayout("Main")
       
       
    '   Dim the objects from the LabelExtras library
       Dim Html1 As Html
       
    Dim LabelExtras1 As LabelExtras
       
    Dim LinkMovementMethod1 As LinkMovementMethod
       
    Dim SpannableStringBuilder1 As SpannableStringBuilder
       
    Dim UnderlineSpans() As UnderlineSpan
       
       
    Dim HtmlString As String="The LabelExtras library also contains an <u>UnderlineSpan</u> object which can be used to add click listeners to text with an HTML &lt;u&gt; underline tag."
       
       SpannableStringBuilder1.Initialize(Html1.FromHtml(HtmlString))
       
       UnderlineSpans=SpannableStringBuilder1.GetUnderlineSpans(
    0, SpannableStringBuilder1.Length)
       
    For Each UnderlineSpan1 As UnderlineSpan In UnderlineSpans
          
    Dim SpanStart As Int=SpannableStringBuilder1.GetSpanStart(UnderlineSpan1)
          
    Dim SpanEnd As Int=SpannableStringBuilder1.GetSpanEnd(UnderlineSpan1)
          
    Dim SpanFlags As Int=SpannableStringBuilder1.GetSpanFlags(UnderlineSpan1)
          
          
    Dim ClickableSpan1 As ClickableSpan
          ClickableSpan1.Initialize(
    "ClickableSpan1"Colors.Blue, True)
          
          SpannableStringBuilder1.SetSpan(ClickableSpan1, SpanStart, SpanEnd, SpanFlags)
       
    Next
       
       ClickableLabel.Text=SpannableStringBuilder1
       LabelExtras1.SetMovementMethod(ClickableLabel, LinkMovementMethod1.GetInstance)

    End Sub

    Sub Activity_Resume

    End Sub

    Sub Activity_Pause (UserClosed As Boolean)

    End Sub

    Sub ClickableSpan1_Click(SelectionStart As Int, SelectionEnd As Int)
       
    Dim LogText As StringBuilder
       LogText.Initialize
       
       LogText.Append(
    "ClickableSpan1_Click: "&DateTime.Now&CRLF)
       LogText.Append(
    "SelectionStart="&SelectionStart&CRLF)
       LogText.Append(
    "SelectionEnd="&SelectionEnd&CRLF)
       
       
    Dim SelectedText As String=ClickableLabel.Text.SubString2(SelectionStart, SelectionEnd)
       LogText.Append(
    "Selected (clicked) text='"&SelectedText&"'")
       
       LogLabel.Text=LogText.ToString
    End Sub
    There are a few other Android classes that could be added to the library, take a look here: CharacterStyle | Android Developers.
    I'm not at all sure what many of these other classes do, URLSpan is presumably an HTML anchor tag.
    If there's any interest i'll look at adding more of these 'span' classes to the library.

    Martin.
     

    Attached Files:

  7. warwound

    warwound Expert Licensed User

    Here's (yet) another example, showing how to add a click listener to a String using character indexes to define the start and end points of the clickable text:

    Code:
    Sub Process_Globals

    End Sub

    Sub Globals
       
    Dim ClickableLabel As Label
       
    Dim LogLabel As Label
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       
    Activity.LoadLayout("Main")
       
       
    '   Dim the objects from the LabelExtras library
       Dim LabelExtras1 As LabelExtras
       
    Dim LinkMovementMethod1 As LinkMovementMethod
       
    Dim SpannableStringBuilder1 As SpannableStringBuilder
       
    Dim Spanned1 As Spanned
       
       SpannableStringBuilder1.Initialize(
    "This is a plain String."&CRLF&"It contains no HTML tags and has NOT been passed to the Html.FromHtml method."&CRLF&"A click listener will be added to the word 'plain' in the first sentence.")
       
       
    Dim ClickableSpan1 As ClickableSpan
       ClickableSpan1.Initialize(
    "ClickableSpan1"Colors.Magenta, True)
       SpannableStringBuilder1.SetSpan(ClickableSpan1, 
    1015, Spanned1.SPAN_EXCLUSIVE_EXCLUSIVE)
       
       ClickableLabel.Text=SpannableStringBuilder1
       LabelExtras1.SetMovementMethod(ClickableLabel, LinkMovementMethod1.GetInstance)

    End Sub

    Sub Activity_Resume

    End Sub

    Sub Activity_Pause (UserClosed As Boolean)

    End Sub

    Sub ClickableSpan1_Click(SelectionStart As Int, SelectionEnd As Int)
       
    Dim LogText As StringBuilder
       LogText.Initialize
       
       LogText.Append(
    "ClickableSpan1_Click: "&DateTime.Now&CRLF)
       LogText.Append(
    "SelectionStart="&SelectionStart&CRLF)
       LogText.Append(
    "SelectionEnd="&SelectionEnd&CRLF)
       
       
    Dim SelectedText As String=ClickableLabel.Text.SubString2(SelectionStart, SelectionEnd)
       LogText.Append(
    "Selected (clicked) text='"&SelectedText&"'")
       
       LogLabel.Text=LogText.ToString
    End Sub
    With previous examples, the value passed as the Flags parameter to the SpannableStringBuilder.SetSpan method has been the Flags value obtained from the SpannableStringBuilder1.GetSpanFlags method.
    Whatever the orginal Flags value was, it is preserved and set as the Flags of the new ClickableSpan.

    With this example we have no original flags to get and then set.
    LabelExtras' Spanned object has been created so that constants from the Android Spanned class can be used.
    I'm not sure exactly what the various constants do in practice - i've used the constant SPAN_EXCLUSIVE_EXCLUSIVE here and it works...

    Martin.
     

    Attached Files:

  8. susu

    susu Well-Known Member Licensed User

    Thank you Martin. It's very useful because you worked really hard. :sign0188:
     
  9. MLDev

    MLDev Active Member Licensed User

    Thanks Martin!
     
  10. Informatix

    Informatix Expert Licensed User

    Could you add the fromHTML function allowing to provide the image and to replace the unknown tags?
    Something like this (for handleTag, it's just a code skeleton):
    Code:
    private String evtImageGetter, evtTagHandler;
    private BA _ba;

       /**
        *Returns a displayable styled text from the provided HTML 
    string.
        *Any &lt;img&gt; tags 
    in the HTML will call the specified ImageGetter sub to request a representation of the image (a drawable or null for a generic replacement imageand the specified TagHandler sub to handle unknown tags.
        */
       
    public CharSequence FromHtmlWithEvents(final BA ba, String HtmlString, String EventNameForImageGetter, String EventNameForTagHandler)
       {
          evtImageGetter = EventNameForImageGetter.toLowerCase(BA.cul);
          evtTagHandler = EventNameForTagHandler.toLowerCase(BA.cul);
          _ba = ba;
          ImageGetter imageGetter = new ImageGetter() {
             @Override
             
    public Drawable getDrawable(String source) {
                
    if (_ba.subExists(evtImageGetter)) {
                   
    return (Drawable) _ba.raiseEvent(this, evtImageGetter, new Object[] {source});
                }
                return null;
             }
          };
          TagHandler tagHandler = new TagHandler() {
             @Override
             public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
                if (_ba.subExists(evtTagHandler)) {
                   Spanned result;
                   result = (Spanned) _ba.raiseEvent(this, evtTagHandler, new Object[] {opening, tag});
                   if (result != null) {

                   }
                }
             }
          };
          return android.text.Html.fromHtml(HtmlString, imageGetter, tagHandler);
       }
     
  11. warwound

    warwound Expert Licensed User

    Ok, i have added support for the ImageGetter and TagHandler interfaces.

    ImageGetter raises this event: GetDrawable(Source As String) As Drawable.

    There should be no problems returning an instance of a Drawable from the b4a event sub to the library i think?
    Is there a way to set a Drawable bounds in b4a or does that require me to create a helper object to set the Drawable bounds?

    ImageGetter
    Events:
    • GetDrawable (Source As String)
    Methods:
    • Initialize (EventName As String)
      ImageGetter raises the GetDrawable(Source As String) As Drawable event when the HTML parser encounters an IMG tag.
      The Source argument is the string from the "src" attribute.
      The event handling Sub should return a Drawable representation of the image or null for a generic replacement image.
      Make sure you call setBounds() on your Drawable if it doesn't already have its bounds set.
    • IsInitialized As Boolean


    TagHandler raises this event: HandleTag(Opening As Boolean, Tag As String, SpannableStringBuilder1 As SpannableStringBuilder).

    The android.text.Html.TagHandler interface actually passes an Editable to it's handleTag method.
    Editable is an interface and i'm hoping that casting the Editable to a wrapped SpannableStringBuilder will work.

    TagHandler
    Events:
    • HandleTag (Opening As Boolean, Tag As String, SpannableStringBuilder1 As SpannableStringBuilder)
    Methods:
    • Initialize (EventName As String)
      TagHandler raises the HandleTag(Opening As Boolean, Tag As String, SpannableStringBuilder1 As SpannableStringBuilder)
      event when he HTML parser encounters a tag that it does not know how to interpret.
    • IsInitialized As Boolean

    I've attached the update, if you can test it and report back with your findings.
    And if it works it'd be handy if you could upload any code examples to help other forum members use the new interfaces.

    The library also includeds some other unfinished updates - these other new objects have _TEST added to their ShortNames so if you ignore them and i'll decide what to do with them once you've tested the ImageGetter and TagHandler objects.

    Martin.
     
    Last edited: Mar 4, 2013
  12. Informatix

    Informatix Expert Licensed User

    I don't understand what you did. It cannot work because there's no redirection to the two handlers. And I find that creating new classes for the handlers is a bit cumbersome. I would prefer to declare only the HTML class with an event prefix as parameter.
    An event cannot return a Drawable. Change "drawable" by "object" in the event declaration.

    There's a bug with your script converting the XML file. The returned object for events is missing.

    And a big thank for considering my demand.
     
  13. warwound

    warwound Expert Licensed User

    That was a bit of an epic update then!
    I was busy and didn't have time to properly test it.

    So forget that update then and look at the update attached to this post.

    I've added a new method to the Html object as you requested:

    Html
    Events:
    • GetDrawable (Source As String) As Object
    • HandleTag (Opening As Boolean, Tag As String, SpannableStringBuilder1 As SpannableStringBuilder)
    Methods:
    • FromHtml2 (HtmlString As String, ImageGetterEventName As String, TagHandlerEventName As String) As CharSequence
      Returns displayable styled text from the provided HTML string.

    I took one of the previous examples and changed the HTML String to:

    Code:
    Dim HtmlString As String="<b>Any</b> text that you want to detect <b><i>clicks</i></b> on must be within an <spans>HTML</span> <b>BOLD</b> element, the <u>StyleSpanClick</u> event is passed SelectionStart and SelectionEnd parameters.<br><br>Click <b>HERE</b> to <martin>continue</martin>.<br><img src='http://localhost/myimage.jpg' />"
    So there is an IMG tag and an unknown tag (martin!).
    I added some Log statements and the event handler Subs get called, now we have a problem with the ImageGetter Sub.
    The events raised by the ImageGetter and TagHandler interfaces are raised from a different thread than the UI thread.
    How can the ImageGetter event Sub return a value?
    Here's the source for the new FromHtml2 method:

    Code:
    public static CharSequence FromHtml2(final BA pBA, String HtmlString, String ImageGetterEventName, String TagHandlerEventName) {

          ImageGetter imageGetter = 
    null;
          final 
    String imageGetterEventName = ImageGetterEventName.toLowerCase(BA.cul)
                + 
    "_getdrawable";
          
    if (pBA.subExists(imageGetterEventName)) {
             imageGetter = new ImageGetter() {
                @Override
                
    public Drawable getDrawable(String pSource) {
                   //   problem - cannot 
    return a value from the UI thread
                   
    Log.d("B4A""getDrawable callback: "+imageGetterEventName);
                   
    return (Drawable) pBA.raiseEventFromDifferentThread(nullnull, mTaskId++, imageGetterEventName, false, new Object[]{pSource});
                }
             };
          }

          TagHandler tagHandler = null;
          final String tagHandlerEventName = TagHandlerEventName.toLowerCase(BA.cul)
                + "_handletag";
          if (pBA.subExists(tagHandlerEventName)) {
             tagHandler = new TagHandler() {
                @Override
                public void handleTag(boolean pOpening, String pTag, Editable pOutput, XMLReader pXMLReader) {
                   Log.d("B4A", "handleTag callback: "+tagHandlerEventName);
                   //   pBA.raiseEvent(null, tagHandlerEventName, new Object[]{pOpening, pTag, new SpannableStringBuilder((android.text.SpannableStringBuilder) pOutput)});
                   pBA.raiseEventFromDifferentThread(null, null, mTaskId++, tagHandlerEventName, false, new Object[]{pOpening, pTag, new SpannableStringBuilder((android.text.SpannableStringBuilder) pOutput)});
                }
             };
          }

          return android.text.Html.fromHtml(HtmlString, imageGetter, tagHandler);
       }
    The b4a events do not get raised if i use the BA raiseEvent method, raiseEventFromDifferentThread is documented as having an Object return type but it is not useable.
    See here: http://www.basic4ppc.com/forum/basi...099-jsinterface-problem-msgbox.html#post62436
    The return type of raiseEventFromDifferentThread is always Null.

    Problem 2 is how to process an unknown tag?

    Code:
    Sub TagHandler_HandleTag(Opening As Boolean, Tag As String, SpannableStringBuilder1 As SpannableStringBuilder)
       
    Log("TagHandler1_HandleTag: "&Opening&", "&Tag)
       
    If Tag="martin" Then
          
    Log("Tag=martin")
          
    If Opening Then
             SpannableStringBuilder1.Append(
    "START")
          
    Else
             SpannableStringBuilder1.Append(
    "END")
          
    End If
       
    End If

       
    Log(SpannableStringBuilder1)
    End Sub
    This Sub gets called a for the unknown 'martin' tag opening and closing tag elements, as a quick attempt to 'do something' i've appended START and END to the SpannableStringBuilder thinking it might replace the unknow tag with that text - it didn't.
    I then added the Log(SpannableStringBuilder1) line and can see my appended text at the end of the String representation of the SpannableStringBuilder.

    I'll look at it more tomorrow - meanwhile if you want to try yourself then both my example code and the updated library files are attached.

    Martin.
     
    Last edited: Mar 10, 2013
  14. Informatix

    Informatix Expert Licensed User

    RaiseEventFromDifferentThread creates a runnable (a piece of code to execute) and put it in the main message queue, then exit with the null value. It cannot return anything from your B4A code because it does not call the event handler. It's just a message creator/sender. The runnable will be executed (=the event handler will be called) when the message is processed by the message handler.
    So we're a little bit stuck here.
     
    Last edited: Mar 4, 2013
  15. Informatix

    Informatix Expert Licensed User

    I looked at the code of the HTML class and it doesn't seem to call another thread, so we should succeed in running a B4A sub with RaiseEvent.
     
  16. warwound

    warwound Expert Licensed User

    Try the attached library version - all i've changed is the code that raises the events.

    The attached version is compiled from this:

    Code:
    public static CharSequence FromHtml2(final BA pBA, String HtmlString, String ImageGetterEventName, String TagHandlerEventName) {

          ImageGetter imageGetter = 
    null;
          final 
    String imageGetterEventName = ImageGetterEventName.toLowerCase(BA.cul)
                + 
    "_getdrawable";
          
    if (pBA.subExists(imageGetterEventName)) {
             imageGetter = new ImageGetter() {
                @Override
                
    public Drawable getDrawable(String pSource) {
                   //   problem - cannot 
    return a value from the UI thread
                   
    Log.d("B4A""getDrawable callback: "+imageGetterEventName);
                   
    return (Drawable) pBA.raiseEvent(null, imageGetterEventName, new Object[]{pSource});
                   //   return (Drawable) pBA.raiseEventFromDifferentThread(null, null, mTaskId++, imageGetterEventName, false, new Object[]{pSource});
                }
             };
          }

          TagHandler tagHandler = null;
          final String tagHandlerEventName = TagHandlerEventName.toLowerCase(BA.cul)
                + "_handletag";
          if (pBA.subExists(tagHandlerEventName)) {
             tagHandler = new TagHandler() {
                @Override
                public void handleTag(boolean pOpening, String pTag, Editable pOutput, XMLReader pXMLReader) {
                   Log.d("B4A", "handleTag callback: "+tagHandlerEventName);
                   pBA.raiseEvent(null, tagHandlerEventName, new Object[]{pOpening, pTag, new SpannableStringBuilder((android.text.SpannableStringBuilder) pOutput)});
                   //   pBA.raiseEventFromDifferentThread(null, null, mTaskId++, tagHandlerEventName, false, new Object[]{pOpening, pTag, new SpannableStringBuilder((android.text.SpannableStringBuilder) pOutput)});
                }
             };
          }

          return android.text.Html.fromHtml(HtmlString, imageGetter, tagHandler);
       }
    For me on a Jelly Bean tablet that fails to raise any b4a events, the logs created within the library callbacks appear but no events are raised.

    The version of the library in my previous post was compiled using raiseEventFromDifferentThread - you can see i've commented out these lines and replaced them with raiseEvent.

    I was planning to look at the various CallSub methods available to library code - i'm busy most of tomorrow so that might not be until Weds.
    If you'd like me to export the Eclipse project so you can experiment with it then let me know.

    Martin.
     
    Last edited: Mar 10, 2013
  17. Informatix

    Informatix Expert Licensed User

    In the meantime, I decompiled your code and tried to find a solution. I confirm that raiseEvent/rE2 does not fire anything. BUT I'VE FOUND A WAY ! I'm trying to do the things properly now (without my ugly hack)... I'll post when it's ready.
     
  18. Informatix

    Informatix Expert Licensed User

    Et voilà !
    With my hack, I saw that the activity was considered as paused by B4A so I changed the second parameter of RaiseEvent2 and that worked! We were very close to the solution.
    You'll find in the archive my B4A test project, the Java code for the html class (ready-made) and the recompiled library. I removed escapetoHTML because it is only available with API 16 and I don't think it's very useful.
     

    Attached Files:

  19. Informatix

    Informatix Expert Licensed User

    The recompiled library provided in the previous post does not include all the original documentation. The HTML java file should be merged with the existing library and recompiled properly.
     
  20. warwound

    warwound Expert Licensed User

    OK no problems.
    I plan to look at it all at the weekend and will then post again.

    Martin.
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice