• [开源,学习,分享]UWP第三方简书客户端分享


    简介

    Windows10正式版发布到现在,我利用零零碎碎的一些时间对UWP进行一些学习,也基于这门技术开发了一个第三方的简书App.

     

    基本界面

    优酷视频:

    http://v.youku.com/v_show/id_XMTM2MjU4MjI4NA==.html

    基本功能

    • 客户端采用了UWP的技术,所以支持x86,x64,ARM平台,采用了响应式的布局.对手机进行了部分的优化.
    • 对SQLite和本地存储进行了封装,支持缓存.

    缓存支持同步和异步的两种方式,分别实现了两个接口:

    internal interface IStorage
    {
        void AddItem(string key, object value);
        void UpdateItem(string key, object value);
        bool ContainItem(string key);
        void FlushAll();
        T GetItem<T>(string key) where T : class;
        List<T> GetItems<T>(string key) where T : class;
        bool RemoveItem(string key);
        long GetSize();
    }
    interface IStorageAsync
    {
        Task AddItemAsync(string key, object value);
        Task UpdateItemAsync(string key, object value);
        Task<bool> ContainItemAsync(string key);
        Task FlushAllAsync();
        Task<T> GetItemAsync<T>(string key) where T : class;
        Task<List<T>> GetItemsAsync<T>(string key) where T : class;
        Task<bool> RemoveItemAsync(string key);
        long GetSize();
    }

    通过Key和Value的方式实现增减删改.

    • 简单的封装了新浪微博的分享图片和文字的功能,可以在Config.cs文件中加入你自己的key和secret.
    public async Task<WeiboResult> ShareTextAsync(string text)
    {
        if (text == null)
        {
            throw new ArgumentNullException(nameof(text));
        }
    
        if (string.IsNullOrWhiteSpace(text))
        {
            throw new ArgumentException("Text could not be empty", nameof(text));
        }
    
        if (!UserInfo.CheckUseable())
        {
            string authorizeCode = await this.GetAuthorizeCodeAsync();
            await this.Authorize(authorizeCode);
        }
    
        Uri uri = new Uri("https://api.weibo.com/2/statuses/update.json");
    
        Dictionary<string, string> pairs = new Dictionary<string, string>();
        pairs.Add("access_token", UserInfo.Token);
        pairs.Add("status", text);
    
        HttpFormUrlEncodedContent content = new HttpFormUrlEncodedContent(pairs);
    
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response;
            try
            {
                response = await client.PostAsync(uri, content);
            }
            catch (Exception ex)
            {
                throw new Exception("Network error", ex);
            }
            return await response.Content.ReadAsJsonAsync<WeiboResult>();
        }
    }
    
    public async Task<WeiboResult> ShareImageAsync(byte[] image, string text)
    {
        if (image == null)
        {
            throw new ArgumentNullException(nameof(image));
        }
    
        if (text == null)
        {
            throw new ArgumentNullException(nameof(text));
        }
    
        if (string.IsNullOrWhiteSpace(text))
        {
            throw new ArgumentException("Text could not be empty", nameof(text));
        }
    
        if (!UserInfo.CheckUseable())
        {
            string authorizeCode = await this.GetAuthorizeCodeAsync();
            await this.Authorize(authorizeCode);
        }
    
        Uri uri = new Uri("https://upload.api.weibo.com/2/statuses/upload.json");
    
        HttpBufferContent bufferContent = new HttpBufferContent(image.AsBuffer());
    
        HttpMultipartFormDataContent content = new HttpMultipartFormDataContent();
    
        content.Add(new HttpStringContent(UserInfo.Token), "access_token");
        content.Add(new HttpStringContent(text), "status");
        content.Add(bufferContent, "pic", "pic.jpg");
    
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response;
            try
            {
                response = await client.PostAsync(uri, content);
            }
            catch (Exception ex)
            {
                throw new Exception("Network error", ex);
            }
            return await response.Content.ReadAsJsonAsync<WeiboResult>();
        }
    }
    • 添加了对异步线程错误的处理和对异步Command的支持.

    异步线程的处理我在上一篇《讲讲我在Windows10(uwp)开发中遇到的一些坑》已经说过了.这里说下贴一下异步Command的代码:

    public class AsyncCommand : AsyncCommandBase
    {
        private readonly Func<Task> command;
        public AsyncCommand(Func<Task> command)
        {
            this.command = command;
        }
        public override bool CanExecute(object parameter)
        {
            return true;
        }
        public override Task ExecuteAsync(object parameter)
        {
            return command();
        }
    }
    • 支持下拉刷新和加载更多(但还有Bug).

    对于UWP的下拉刷新,我在博客园里看到了几种实现方式:

    UWP的一种下拉刷新实现

    只贴了一种,因为目前实现下拉刷新的方式都是ListView外部套一个ScrollViewer来实现,这种实现方式有个严重的问题就是:ListView内部也是有一个ScrollViewer,当滑动的时候,会出现ListView内部的ScrollViewer被压缩,这样直接导致了下拉刷新的失败.

    我这里思考了另一种方式,就是对ListView内部的ScrollViewer进行操作.

    <ScrollViewer Margin="0,0,0,-100" RenderTransformOrigin="0.5,0.5" x:Name="ScrollViewer" AutomationProperties.AccessibilityView="Raw" BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}" IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" TabNavigation="{TemplateBinding TabNavigation}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
        <ScrollViewer.RenderTransform>
            <CompositeTransform TranslateY="-100" />
        </ScrollViewer.RenderTransform>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Border Height="{TemplateBinding RefreshHeaderHeight}" x:Name="PullToRefreshIndicator" Background="Transparent">
                <Grid HorizontalAlignment="Center">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="auto" />
                        <ColumnDefinition Width="auto" />
                    </Grid.ColumnDefinitions>
                    <Grid Width="40">
                        <Viewbox x:Name="Arrow" Height="15" VerticalAlignment="Top" Margin="0,4,0,0" RenderTransformOrigin="0.5,0.5">
                            <Viewbox.RenderTransform>
                                <CompositeTransform Rotation="180" />
                            </Viewbox.RenderTransform>
                            <Image Source="ms-appx:///Assets/arrow.png" Width="12" Height="12.9999"/>
                            <!--<Path
                Width="12"
                Height="12.9999"
                Stretch="Fill"
                Fill="{TemplateBinding ArrowColor}"
                Data="M 20.4289,10.4376L 25,15.0087L 23.571,16.4376L 20.0291,12.8957L 20.0291,21.9999L 18.0083,21.9999L 18.0083,12.8583L 14.4289,16.4377L 13,15.0087L 17.5624,10.429L 19.0087,9" />-->
                        </Viewbox>
                    </Grid>
                    <TextBlock Grid.Column="1" x:Name="PullPart" Text="{TemplateBinding PullPartTemplate}"/>
                    <TextBlock Grid.Column="1" Visibility="Collapsed" x:Name="ReleasePart" Text="{TemplateBinding ReleasePartTemplate}"/>
                </Grid>
            </Border>
            <ItemsPresenter FooterTransitions="{TemplateBinding FooterTransitions}" FooterTemplate="{TemplateBinding FooterTemplate}" 
                Footer="{TemplateBinding Footer}" HeaderTemplate="{TemplateBinding HeaderTemplate}" Header="{TemplateBinding Header}" 
                HeaderTransitions="{TemplateBinding HeaderTransitions}" Padding="{TemplateBinding Padding}" Grid.Row="1"/>
        </Grid>
    </ScrollViewer>

    对内部的ScrollViewer的压缩进行操作,这样能够比较精准的获取用户的下拉.

    同时我已经把这个代码封装成一个单独的控件,你可以从下面的链接获取到源码:

    https://github.com/youngytj/uwp_PullToRefreshListview

    使用方式只要在xaml里添加如下代码即可使用:

    <local:PullToRefreshListView x:Name="lv" 
            PullPartTemplate="Pull" ReleasePartTemplate="Release" 
            RefreshContent="lv_RefreshContent" MoreContent="lv_MoreContent"/>
    PullPartTemplate和ReleasePartTemplate分别是下拉和释放时候的文字.然后后面是更新和加载更多时候的事件.
    • 使用了MVVMLight,所有的界面都以嵌入的方式放入MainPage中.

    MainPage里面通过ContentControl的来绑定内容

    <ContentControl Content="{Binding CurrentViewModel}" ContentTemplate="{Binding Path=CurrentTemplate}" 
                                HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>

    View和ViewModel的绑定放在了App.xaml

    <Application.Resources>
        <ResourceDictionary>
            <vm:ViewModelLocator x:Key="Locator"/>
            <DataTemplate x:Key="HomeViewModel">
                <view:HomeView/>
            </DataTemplate>
            ...
    </Application.Resources>

    这里有个注意的地方就是View只是作为一个资源的方式存在,当MainPage中发生页面转换时,会将ViewModel的名字作为一个Key(所以类名必须是Key)来寻找View.

    public static class DataTemplateSelector
    {
        public static DataTemplate GetTemplate(ViewModelBase param)
        {
            Type t = param.GetType();
            if(!views.ContainsKey(t.Name))
            {
                views.Add(t.Name, App.Current.Resources[t.Name] as DataTemplate);
            }
    
            return views[t.Name];
        }
    }

    开源地址

    没有上传到市场,因为我这边网络一直上载不了包,如果网络环境好了以后再考虑上传,下面是Github的源码地址:

    Github

    写在最后

    并不是很看好微软这种实现跨平台.除了目前UWP这门技术的不成熟,包括很多的缺失,诸如异步线程的处理问题,缺少对移动端的滑动的支持(不像android一样可以从底层开始实现一个行云流水般的手势操作,还有就是ScrollViewer依然存在之前的问题.),还有就是对于微软的这种跨平台的方式,我支持这种看法--因为硬件设备和运行环境的不同带来的用户体验的不同,才是跨平台最大的障碍!这一障碍,不是任何一个“技术”或“技术提供商”可以解决的!.总体来说,因为是全新的平台,相应的开源组件比较少,上手还需要一些时间来熟悉这个平台,平台的不足之处也需要自己从无到有.

    这个客户端其实还有很多不好的地方,比如对于异步线程启动关闭的控制不足,缺少log,缺少对于缓存的系统的管理,还有导航系统的不足,不同网络的环境下的客户端优化的问题.但是我认为仅仅作为一个研究学习的项目,已经足够了.如果你喜欢或者支持这样的项目,帮忙点个推荐吧.

    以上,说完.谢谢.

  • 相关阅读:
    数据移除的循环
    C#窗体多语言切换(简繁)
    C# 文字转换最简单的方法
    使用CodeDOM动态编译一个字符串表达式
    C#设置WebBrowser默认浏览器
    Image和字节数组互转
    苹果手机的SB系列(8)为什么没有短信全部删除?
    循环删除外键约束
    腾讯云COS自定义域名,支持https,CDN,私有访问
    腾讯云COS请求签名C#版
  • 原文地址:https://www.cnblogs.com/youngytj/p/4889010.html
Copyright © 2020-2023  润新知