[開源,學習,分享]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,缺少對於緩存的系統的管理,還有導航系統的不足,不同網絡的環境下的客戶端優化的問題.但是我認為僅僅作為一個研究學習的項目,已經足夠了.如果你喜歡或者支持這樣的項目,幫忙點個推薦吧.

 

以上,說完.謝謝.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM