Anand
Anand

Reputation: 1959

Highlight URL using label span - xamarin.forms

I am creating a chat application in xamarin.forms.What I am trying to achieve is whenever user typed message contains a URL, that should be highlighted and provide click to it.For this feature I found Span in Label text.When user click on send button of chat , I will check for URL and make it as another span.I got this idea from Lucas Zhang - MSFT form this question here.

The problem is I am trying to do the spanning in view model and the individual chat bubble is in another view cell which will call as ItemTemplate in my chat listview. Anyway the spanning is not working as I intended ie; it doesn't highlight .

My view Model.

public Queue<Message> DelayedMessages { get; set; } = new Queue<Message>();
public ObservableCollection<Message> Messages { get; set; } = new  ObservableCollection<Message>();                                         
public string TextToSend { get; set; }

public ChatPageViewModel()
            {                        
                OnSendCommand = new Command(() =>
                {

                    if (!string.IsNullOrEmpty(TextToSend))
                    {

                        var urlStr = TextToSend;

                        int startIndex = 0, endIndex = 0;

                        if (urlStr.Contains("www."))
                        {
                            startIndex = urlStr.IndexOf("www.");
                        }

                        if (urlStr.Contains(".com"))
                        {
                            endIndex = urlStr.IndexOf(".com") + 3;
                        }

                        if (startIndex != 0 || endIndex != 0)
                        {
                            var formattedString = new FormattedString();

                            Span span1 = new Span() { Text = urlStr.Substring(0, startIndex), TextColor = Color.Black };

                            formattedString.Spans.Add(span1);

                            Span span2 = new Span() { Text = urlStr.Substring(startIndex, endIndex - startIndex + 1), TextColor = Color.LightBlue };
                            span2.GestureRecognizers.Add(new TapGestureRecognizer()
                            {
                                NumberOfTapsRequired = 1,
                                Command = new Command(() => {


                                })
                            });

                            formattedString.Spans.Add(span2);

                            Span span3 = new Span() { Text = urlStr.Substring(endIndex, urlStr.Length - 1 - endIndex), TextColor = Color.Black };
                            formattedString.Spans.Add(span3);
                            var message = new Message
                            {
                                Text = formattedString.ToString(),
                                IsIncoming = false,
                                MessageDateTime = DateTime.Now
                            };
                            Messages.Add(message);
                            TextToSend = string.Empty;
                        }

                        else
                        {                  
                            var message = new Message
                            {
                                Text = urlStr.ToString(),
                                IsIncoming = false,
                                MessageDateTime = DateTime.Now
                            };
                            Messages.Add(message);
                            TextToSend = string.Empty;
                        }               
                    }                                  
                });        
            }

Single chat Bubble XAML

<Label  x:Name="OutgoingMessage" TextColor="White"  FormattedText="{Binding Text}" HorizontalOptions="End" >              
</Label>

My Chat page XAML

<Grid RowSpacing="0" Margin="0,20,0,0"
       ColumnSpacing="0">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="1" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <ListView Grid.Row="0" 
             ItemTemplate="{StaticResource MessageTemplateSelector}" 
             ItemsSource="{Binding Messages,Mode=OneWay}" 
             Margin="0"       
             SelectionMode="None"                                  
             FlowDirection="RightToLeft"                               
             HasUnevenRows="True" x:Name="ChatList"
             VerticalOptions="FillAndExpand" 
             SeparatorColor="Transparent"
             >
     </ListView>
    <BoxView HorizontalOptions="FillAndExpand"
             HeightRequest="1"
             BackgroundColor="#F2F3F5"
             Grid.Row="1"/>
    <partials:ChatInputBarView Grid.Row="2"
                               Margin="0,0,0,0"
                               x:Name="chatInput"/>
</Grid>

ChatPage.xaml.cs

 public partial class ChatPage : ContentPage
    {
        ChatPageViewModel vm;
        public ChatPage()
        {
            InitializeComponent();
            this.BindingContext = vm= new ChatPageViewModel();                      
        }     
    }

Messages class

 public class Message : ObservableObject
    {
        string text;
        public string Text
        {
            get { return text; }
            set { SetProperty(ref text, value); }
        }

        DateTime messageDateTime;

        public DateTime MessageDateTime
        {
            get { return messageDateTime; }
            set { SetProperty(ref messageDateTime, value); }
        }

        public string MessageTimeDisplay => MessageDateTime.Humanize();

        bool isIncoming;

        public bool IsIncoming
        {
            get { return isIncoming; }
            set { SetProperty(ref isIncoming, value); }
        }

    }

Any Help is appreciated.

EDIT: This question was actually continuation of question. Previously I used AwesomeHyperLinkLabel fromlink. The problem was I cant manage the click event of that label.Thats why I moved with label span.Thanks to Leo Zhu - MSFT For the render changes.

Upvotes: 0

Views: 353

Answers (2)

Anand
Anand

Reputation: 1959

The mistake was with my model.Changed string to FormattedString and also changed in the viewmodel

 public class Message : ObservableObject
    {
        FormattedString text;
        public FormattedString Text
        {
            get { return text; }
            set { SetProperty(ref text, value); }
        }

        DateTime messageDateTime;

        public DateTime MessageDateTime
        {
            get { return messageDateTime; }
            set { SetProperty(ref messageDateTime, value); }
        }

        public string MessageTimeDisplay => MessageDateTime.Humanize();

        bool isIncoming;

        public bool IsIncoming
        {
            get { return isIncoming; }
            set { SetProperty(ref isIncoming, value); }
        }

    }

Upvotes: 0

Leo Zhu
Leo Zhu

Reputation: 15001

For Android:

[assembly: ExportRenderer(typeof(AwesomeHyperLinkLabel), typeof(AwesomeHyperLinkLabelRenderer))]
namespace App18.Droid
{
public class AwesomeHyperLinkLabelRenderer : LabelRenderer
{

    public AwesomeHyperLinkLabelRenderer(Context context) : base(context)
    {
    }
    protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            var view = (AwesomeHyperLinkLabel)Element;
            if (view == null) return;

            TextView textView = new TextView(Forms.Context);
            textView.LayoutParameters = new LayoutParams(LayoutParams.WrapContent, LayoutParams.WrapContent);
            textView.SetTextColor(view.TextColor.ToAndroid());

            // Setting the auto link mask to capture all types of link-able data
            textView.AutoLinkMask = MatchOptions.All;
            // Make sure to set text after setting the mask
            textView.Text = view.Text;
            AddHyperlinksManually(textView);
            //textView.SetTextSize(ComplexUnitType.Dip, (float)view.FontSize);
            // overriding Xamarin Forms Label and replace with our native control
            SetNativeControl(textView);
        }
    public static void AddHyperlinksManually(TextView _tv)
    {
        SpannableStringBuilder currentSpan = new SpannableStringBuilder(_tv.Text);
        Linkify.AddLinks(currentSpan, MatchOptions.WebUrls);

        var objects = currentSpan.GetSpans(0, currentSpan.Length(), Java.Lang.Class.FromType(typeof(URLSpan)));
        var urlSpans = new URLSpan[objects.Length];
        for (var i = 0; i < urlSpans.Length; i++)
        {
            urlSpans[i] = objects[i] as URLSpan;
        }

        foreach (URLSpan _url in urlSpans)
        {
            int iStart = currentSpan.GetSpanStart(_url);
            int iEnd = currentSpan.GetSpanEnd(_url);

            currentSpan.RemoveSpan(_url);
            currentSpan.SetSpan(new CustomURLSpan(_url.URL), iStart, iEnd, SpanTypes.InclusiveInclusive);
            _tv.SetText(currentSpan, TextView.BufferType.Normal);

            _tv.MovementMethod = LinkMovementMethod.Instance;
        }
    }
    public class CustomURLSpan : ClickableSpan
    {
        string mTargetURL;

        public CustomURLSpan(string _url) {
            mTargetURL =_url;
    }

        public override void OnClick(Android.Views.View widget)
        {
            //here you could handle the click event,and you could use MessagingCenter to send mTargetURL to your Page.
            Console.WriteLine("Click");
        }
    }
}  

Upvotes: 1

Related Questions