しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Xamarin.Forms で RSS フィードのサムネイル画像を ListView に表示したい

RSS フィードの情報を ListView に表示することはできたものの、若干読みにくさを感じる画面でした

f:id:matatabi_ux:20150814221302g:plain

というわけで、RSS の各記事内に埋め込まれている画像を拾い上げて画面に表示するなどの修正をしてみます


といっても画像抽出に必要なことは下記のように description タグ内の img タグを拾い上げて src 属性から Uri を抽出するだけです

/// <summary>
/// Top page
/// </summary>
public partial class TopPage : ContentPage
{
    /// <summary>
    /// Remove html tag regex
    /// </summary>
    private static readonly Regex HtmlTagRegex = new Regex(@"<.*?>", RegexOptions.Singleline);

    /// <summary>
    /// Remove last read more paragraph regex
    /// </summary>
    private static readonly Regex ReadMoreRegex = new Regex(@"Read\s*?More[\r\n\s][\r\n\s]*", RegexOptions.Singleline);

    /// <summary>
    /// Get image html tag src attribute regex
    /// </summary>
    private static readonly Regex ImgTagRegex = new Regex(@"<img.*?src\s*=\s*[""'](?<uri>.+?)[""'].*?>", RegexOptions.Singleline);

    /// <summary>
    /// News feed retrieve service
    /// </summary>
    private INewsFeedService newsFeed = null;

    /// <summary>
    /// ViewModel
    /// </summary>
    public TopPageViewModel ViewModel { get; private set; }

    /// <summary>
    /// Constructir
    /// </summary>
    public TopPage()
    {
        this.ViewModel = new TopPageViewModel();
        this.newsFeed = ((App)App.Current).Container.Resolve<INewsFeedService>();

        InitializeComponent();
    }

    /// <summary>
    /// Appearing event handler
    /// </summary>
    protected override async void OnAppearing()
    {
        base.OnAppearing();

        await this.newsFeed.Update();

        // Add items on UI thread
        foreach (var item in this.newsFeed.Feed.Channel.Items)
        {
            var vm = new NewsItemViewModel
            {
                UniqueId = item.Guid,
                Categories = new ObservableCollection<string>(item.Categories),
                Title = item.Title,
                Link = item.Link,
                LastUpdated = item.PubDate,
                Description = ReadMoreRegex.Replace(
                                WebUtility.HtmlDecode(
                                HtmlTagRegex.Replace(item.Description, string.Empty)),
                                string.Empty),
            };
            var imgMatch = ImgTagRegex.Match(item.Description);
            if (imgMatch.Success)
            {
                vm.Thumbnail = ImageSource.FromUri(new Uri(imgMatch.Groups["uri"].Value));
            }
            this.ViewModel.Items.Add(vm);
        }
    }
}

img タグの src 属性は次のような正規表現を利用して

    /// <summary>
    /// Get image html tag src attribute regex
    /// </summary>
    private static readonly Regex ImgTagRegex = new Regex(@"<img.*?src\s*=\s*[""'](?<uri>.+?)[""'].*?>", RegexOptions.Singleline);

ヒットした場合は Thmbnail というプロパティに uri を設定するようにしています

            var imgMatch = ImgTagRegex.Match(item.Description);
            if (imgMatch.Success)
            {
                vm.Thumbnail = ImageSource.FromUri(new Uri(imgMatch.Groups["uri"].Value));
            }

ちなみに前回末尾についていた「Read More」など余分な文字列も次のような正規表現で取り除くようにしています

    /// <summary>
    /// Remove last read more paragraph regex
    /// </summary>
    private static readonly Regex ReadMoreRegex = new Regex(@"Read\s*?More[\r\n\s][\r\n\s]*", RegexOptions.Singleline);

バインディングするデータ側の修正ができたので、次は XAML を修正

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinReader.Views.TopPage"
             x:Name="root"
             BindingContext="{Binding Path=ViewModel, Source={x:Reference Name=root}}">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0,20,0,0"/>
    </ContentPage.Padding>
    <StackLayout Orientation="Vertical"
                 Spacing="10">
        <StackLayout Orientation="Horizontal"
                     Padding="15,5"
                     BackgroundColor="#33ffffff"
                     Spacing="5">
            <Label Text="TechCrunch"
                   TextColor="Lime"
                   FontSize="Medium"/>
            <Label Text="Reader"
                   FontSize="Medium"/>
        </StackLayout>
        <ListView ItemsSource="{Binding Items}"
                  SeparatorColor="Transparent"
                  HasUnevenRows="True">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Vertical"
                                     Spacing="0"
                                     Padding="15,10">
                            <Image Source="{Binding Thumbnail}"
                                   HeightRequest="150"
                                   VerticalOptions="StartAndExpand"
                                   Aspect="AspectFill"/>
                            <StackLayout Orientation="Vertical"
                                         Padding="10"
                                         BackgroundColor="#66000000"
                                         Spacing="10">
                                <Label Text="{Binding Title}"
                                       TextColor="Accent"
                                       FontSize="Medium"/>
                                <Label Text="{Binding Description}"
                                       TextColor="Default"
                                       FontSize="Small"/>
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

ListView の上にヘッダ部分を追加し、各リストにはサムネイル画像をつけました

これを実行すると・・・

f:id:matatabi_ux:20150815190128g:plain

だいぶそれっぽくなってきました