Xamarin.Form 实例: Discuz BBS 客户端 源码分享

感谢台风, 这个十一长假让我好好的休息了一回, 睡觉到腰酸背疼, 看电影看到眼发红. 今天最后一天, 不敢出去逛, 不知道哪会还会下暴雨...

嗯嗯..这个项目其实在十一之前就开始了, 工作无聊,没有新任务, 我就搞起它.

至于为什么选 Discuz 的 BBS , 因为我常上的几个网站, 都有一堆的 APP , 官方的, 第三方的 . BBS 虽然已经没落了, 但是官方的 APP 居然用不了!

写这个东西之前, 本来想拿来看 1024 的, 但是 1024 要么不是最新版本, 要么禁用了 API, 我就只能哈哈哈, 拿"以前" 最常上的 BBS 的 API 开始了.

源码:

https://github.com/gruan01/Discuz.Mobi

上几张图:

Andorid

WP

IOS : 没有, 只顾睡觉看电影了, 没有搞.

API

Discuz! X3.x 及已上版本内置 API , 低版本中, 用插件的形式提供.

这里是几个主要的 API :

版块列表 : http://xxx/api/mobile/index.php?mobile=no&version=1&module=forumindex

版块的主题列表 : http://xxx/api/mobile/index.php?mobile=no&version=1&module=forumdisplay&fid=11  其中的 fid 是上个 API 中返回值中的 fid , 即版块ID, 还可以有 page, tpp (pageSize)

主题详细: http://xxx/api/mobile/index.php?mobile=no&version=1&module=viewthread&tid=3287083 tid 即主题ID, 还可以有 page, ppp (pageSize) , 这名字取的好蛋疼啊.

登陆:  POST /api/mobile/index.php?mobile=no&version=1&module=login&loginsubmit=yes&loginfield=auto&submodule=checkpost

password=密码&username=用户名&formhash=&answer=安全问题答案&questionid=5

这个登陆一直没有成功过!不知道是啥原因..所以和登陆相关的东西都没办法做下去...

项目结构

基于

1, Xamarin.Form (以下简称 XF)

2, Caliburn.Micro (以下简称 CM)

Discuz 是主项目, 那个 Droid, WinPhone 是用于编译生成app 的.

ViewModels / Views 是 CM 的默认约定方式, 字面意思大家都理解.

Discuz.Api 的入口是 ApiClient, 关键是 Methods 目录下面的对 api 方法的封装.

具体用法可以参考 Test 项目.

讲解:

App.xaml

写过 WPF 的, 都知道该文件的重要性.

但是在 XF 中, 新建项目中,没有该文件, 需要手动添加一个, 然后在 app.cs 的构造函数中添加:

1 public App(SimpleContainer container) {
2        this.InitializeComponent();

InitializeComponent 会在你添加 App.xaml 之后, 自动生成, 在它里面会去加载 app.xaml

Caliburn.Micro

之前发过一篇, 不在赘述:

Xamarin 的 MVVM 之 Caliburn.Micro

TabbedPage 数据源绑定

之前写的项目没用 MVVM, 直接这样写:

 1     public partial class BusPage : TabbedPage {
 2
 3         public BusPage() {
 4             //InitializeComponent();
 5             //this.ItemsSource = this.Pages;
 6
 7             this.Title = "LBC 业务通";
 8
 9             this.BackgroundColor = Color.White;//在XAML中设置 Background 不起作用。
10
11             this.Children.Add(new OrderListPage());
12             this.Children.Add(new CustomerListPage());
13             this.Children.Add(new TemplatesListPage());
14             this.Children.Add(new SettingPage());
15         }
16     }
 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
 3              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
 4              x:Class="LBC.Mobi.Pages.BusPage"
 5             Padding="10"
 6             Title="LBC"
 7             >
 8
 9   <TabbedPage.ItemTemplate>
10     <DataTemplate>
11       <ContentPage
12           Title="{Binding Title}" Content="{Binding Content}"
13           />
14     </DataTemplate>
15   </TabbedPage.ItemTemplate>
16 </TabbedPage>

即: 根本就没有用到 ItemsSource, 直接添加到 Children 中的.

这样是很省事, 但是在 MVVM 中, 行不通了, 因为 Children 没有对应的依赖属性, 无法通过 MVVM 绑定.

为解决这个问题, 先扩展 ContentPage

 1     public class ViewLocatorPage : ContentPage {
 2
 3         public static readonly BindableProperty VMProperty = BindableProperty.Create<ViewLocatorPage, Screen>(p => p.VM, null, propertyChanged: VMChanged);
 4
 5         public Screen VM {
 6             get {
 7                 return (Screen)this.GetValue(VMProperty);
 8             }
 9             set {
10                 this.SetValue(VMProperty, value);
11             }
12         }
13
14
15         private static void VMChanged(BindableObject bindable, object oldValue, object newValue) {
16             var vm = (Screen)newValue;
17             //var view = vm.GetView();
18             var vmView = ViewLocator.LocateForModel(vm, null, null);
19             if (vmView == null)
20                 throw new Exception("没有找到视图");
21             ViewModelBinder.Bind(vm, vmView, null);
22
23             var activator = vm as IActivate;
24             if (activator != null)
25                 activator.Activate();
26
27             var page = (ViewLocatorPage)bindable;
28             if (null != (ContentPage)vmView) {
29                 var vp = (ContentPage)vmView;
30                 page.Content = vp.Content;
31                 if (vp.ToolbarItems != null)
32                     foreach (var t in vp.ToolbarItems)
33                         page.ToolbarItems.Add(t);
34             } else if (null != (Xamarin.Forms.View)vmView) {
35                 page.Content = (Xamarin.Forms.View)vmView;
36             }
37         }
38
39     }

VM做为依赖属性 (VMProperty), 在变化的时候会调用 VMChanged 方法.

在这个方法中, 会跟据 VM 去查询对应的视图, ViewLocator.LocateForModel(...)

将视图和模型绑定, ViewModelBinder.Bind(...)

并激活, activator.Activate()

然后做为 page 的 content 呈现在 TabbedPage 中. page.Content = vp.Content

需要注意的是 ViewLocator / ViewModelBinder 是 CM 中提供的, 所以用其它 MVVM 框架的, 请换成对应的 API 方法.

这样一来, 数据绑定就很简单了:

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
 3             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
 4             x:Class="Discuz.Views.TabView"
 5             xmlns:cal="clr-namespace:Caliburn.Micro.Xamarin.Forms;assembly=Caliburn.Micro.Platform.Xamarin.Forms"
 6             xmlns:local="clr-namespace:Discuz;assembly=Discuz"
 7             ItemsSource="{Binding Datas}"
 8             Title="蓝色理想"
 9             BackgroundColor="#1e5263"
10             >
11
12   <TabbedPage.ItemTemplate>
13     <DataTemplate>
14       <local:ViewLocatorPage Title="{Binding DisplayName}" VM="{Binding}" />
15     </DataTemplate>
16   </TabbedPage.ItemTemplate>
17
18 </TabbedPage>

Event & Command

WPF 中的 CM , 会跟据控件的类型自动选择默认的事件, 比如

<Button x:Name="Show" ... />

会在 Click 的时候, 自动去调用 VM中的 Show 方法,

或者

1 <Button cal:Message.Attach="Show" />
2 <xxx cal:Message.Attach="[Event click] = [Action Show()] ; [Event XXX] = [Action XXX()]" />

这样写多多少有点蛋疼.

在 CM For XF中, 无法通过 x:Name 获取到对应的控件, 所以第一种写法不能用, 第二种正如我说的, 蛋疼...

还好, XF 除了提供事件之外, 还会提供相应的 Command !

1     <ListView.Footer>
2       <StackLayout Padding="10">
3         <Button Text="加载更多" Command="{Binding LoadMoreCmd}"
4                 BindingContext="{Binding Source={x:Reference root}, Path=BindingContext}"
5                 />
6       </StackLayout>
7     </ListView.Footer>

ContentControl 的等价物 ContentView

WPF 中有 ContentControl, 它在 MVVM 中的意义重大,  使你不用关心它如何展示出来, 只用给它一个 ViewModel, 已达到功能分解的目的.

在 XF 中,没有 ContentControl, 但是有 ContentView , 一般自定义用户控件都是从它继承.

有了 ContentView , 就可以把臃肿的主页面分解成不同的子模型与视图了.

 1     <ListView.ItemTemplate>
 2       <DataTemplate>
 3         <ViewCell>
 4           <ViewCell.View>
 5             <StackLayout Padding="5,1">
 6               <ctrls:Border Style="{StaticResource BlockBorder}">
 7                 <ContentView cal:View.Model="{Binding }" />
 8               </ctrls:Border>
 9             </StackLayout>
10           </ViewCell.View>
11         </ViewCell>
12       </DataTemplate>
13     </ListView.ItemTemplate>

手势 GestureRecognizers

在 ListView 中,  可以通过 ItemTapped 来判断哪一行数据被点击.

用 ContentView 进行分解之后, 我想把 tap 事件也给分解到具体的子模型中, 这样可以更多的自主选择, 但是在子模型中怎么响应 ItemTapped 呢? 显然是不可能的!

XF 提供的手势功能可以很好的解决这个问题:

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <ContentView xmlns="http://xamarin.com/schemas/2014/forms"
 3              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
 4              x:Class="Discuz.Views.ForumDetailView"
 5              xmlns:ctrls="clr-namespace:Discuz.Controls;assembly=Discuz"
 6              xmlns:cal="clr-namespace:Caliburn.Micro.Xamarin.Forms;assembly=Caliburn.Micro.Platform.Xamarin.Forms"
 7               >
 8
 9   <ContentView.GestureRecognizers>
10     <TapGestureRecognizer Command="{Binding TapCommand}" />
11   </ContentView.GestureRecognizers>

意思就是说在这个ContentView 上 tap 的时候, 响应 TapCommand , 它和在 ListView 上响应 ItemTapped 是一样的效果!

消息传输中心 MessagingCenter

正如字面意思,它是 XF 中, 用来 在 不同的页面 之间 传输消息的, 是对观察者模式的封装.

消息传输, 需要两个元素, 消息标识符 和 消息内容

同时还要有消息的接收方法和发送方.

发送方:

1 public static void Send<TSender>(TSender sender, string message) where TSender : class;
2 public static void Send<TSender, TArgs>(TSender sender, string message, TArgs args) where TSender : class;

参数 message 其实是消息的标识符, 不要被字面意思给忽悠了, args 才是真正的消息内容.

1 MessagingCenter.Send(this, "AddFavorite", new Tuple<int, string>(this.Data.ID, this.Data.Name));

接收方:

1 public static void Subscribe<TSender, TArgs>(object subscriber, string message, Action<TSender, TArgs> callback, TSender source = null) where TSender : class;
2 public static void Subscribe<TSender>(object subscriber, string message, Action<TSender> callback, TSender source = null) where TSender : class;

同样, message 指的是消息标识符, args 才是真正的消息内容.

1 MessagingCenter.Subscribe<ForumDetailViewModel, Tuple<int, string>>(this, "AddFavorite", (sender, arg) => {
2    if (!this.Favorites.ContainsKey(arg.Item1)) {
3        this.ShowFavorite(arg.Item1, arg.Item2);
4        this.AddToFavorite(arg.Item1, arg.Item2);
5    }
6 });

这样一来, 就可以在两个不同的页面之前传递消息了, 只不过有一点缺点: 发送方不能得到消息的反馈.

拖拽排序

很遗憾, XF 的 ListView 没有相关的事件..

要完成这个功能, 要自己实现 Render, 找了几个拖拽的示例, 都不是我心中的理想状态, 暂时搁浅.

Android 样式生成器:

http://jgilfelt.github.io/android-actionbarstylegenerator/

-------------

OK 完

时间: 2024-10-10 02:27:33

Xamarin.Form 实例: Discuz BBS 客户端 源码分享的相关文章

C#中国象棋+游戏大厅 服务器 + 客户端源码

来源:www.ajerp.com/bbs C#中国象棋+游戏大厅 服务器 + 客户端源码 源码开源 C#版中国象棋(附游戏大厅) 基于前人大虾的修改版 主要用委托实现 服务器支持在线人数,大厅桌数的设置 游戏互不干扰 这个象棋大厅程序完全可以当做是C#委托事件的教程,而且游戏大厅功能也很多,服务器还可设置人数和桌数,大厅客户端也实时更新相关数据. 源码下载地址:http://www.ajerp.com/bbs/forum.php?mod=viewthread&tid=29&extra=pa

Eureka 系列(02)客户端源码分析

Eureka 系列(02)客户端源码分析 [TOC] 在上一篇 Eureka 系列(01)最简使用姿态 中对 Eureka 的简单用法做了一个讲解,本节分析一下 EurekaClient 的实现 DiscoveryClient.本文的源码是基于 Eureka-1.9.8. 1)服务注册(发送注册请求到注册中心) 2)服务发现(本质就是获取调用服务名所对应的服务提供者实例信息,包括IP.port等) 3)服务续约(本质就是发送当前应用的心跳请求到注册中心) 4)服务下线(本质就是发送取消注册的HT

Eureka 客户端源码分析

Eureka作为服务注册中心,主要的功能是服务注册和服务发现,是微服务框架的基础功能和核心功能. Eureka的使用可参考: Eureka服务端:Spring Cloud Eureka Server使用(注册中心), Eureka客户端:Eureka Client的使用, Eureka服务端:Eureka的高可用 Eureka分为客户端和服务端,这里主要介绍客户端源码 1.Eureka客户端主要使用EnableDiscoveryClient注解 1)@EnableDiscoveryClient源

开源中国安卓客户端源码之自定义控件---ScreenShotView

首先,感谢开源中国的开源精神.当初学者拿到客户端源码时,可能会对其中的项目结构和代码产生许多困惑,不知道该从何下手,当然我也是其中一员,接触安卓时间不长,也不是很精通,但是通过一段时间的琢磨,慢慢地领会到其中的一些编程方法,我只是想把我弄明白的这些知识通过博客的形式记录下来,以备以后查看,当然也可以帮助到更多的初学者.我的主要工作是给代码添加注释,理顺调用关系,让初学者更快.更深刻地理解代码的含义,领会其精神.首先大家从http://git.oschina.net/oschina/android

kbengine mmo源码(完整服务端源码+资源+完整客户端源码)

kbengine mmo源码(完整服务端源码+资源+完整客户端源码) PyConsole: display server information. PyConsole: Stop the server. Guiconsole: debug. Guiconsole: log. Demo: Ogre. Demo: Unity3d. demo视频:http://v.youku.com/v_show/id_XNjU5Nzc0MDQ4.html 下载地址: demo下载地址:http://sourcefo

多实例Mysql5.5.x源码编译安装

多实例Mysql5.5.x源码编译安装 1.  建立Mysql账号 [[email protected] ~]# groupadd mysql [[email protected] ~]# useradd -s /sbin/nologin -g mysql -M mysql useradd参数说明: -s/sbin/nologin 表示禁止该用户登录 -gmysql 指定属于mysql组 -M表示不创建用户家目录 检查创建的用户: [[email protected] ~]# tail -1 /

H264音视频直播系统 服务器端+客户端源码 可用于视频聊天、视频会议

H264音视频直播系统  服务器端+客户端源码 可用于视频聊天.视频会议 RTP协议实现音视频网络实时直播,采用H.264和AAC编码,码率极低,同时有较高的视频清晰度和音频音质,可用于视频聊天.视频会议.摄像头监控等多种应用场景. 发布端,选择视频和音频设备,指定服务器发布直播源 接收端,接收发布者的音视频并播放.占用带宽很低,如图所示.采用动态码率,平时在4~5KB/s左右,动作幅度较大时在8KB/s. C++代码,VS2010工程,包含全部代码,不用其它依赖项,可直接编译 接口调用简单

春秋旅行安卓客户端源码项目

春秋旅行安卓客户端源码,这是一款高度模仿春秋旅行app开发的一款应用,通过抓包工具获取到的接口,希望大家能够喜欢,并且对大家的学习能够有所帮助. 源码下载:http://code.662p.com/view/12837.html <ignore_js_op> <ignore_js_op> 详细说明:http://android.662p.com/thread-6460-1-1.html

Android应用源码仿暴风影音安卓客户端源码

Android应用源码仿暴风影音安卓客户端源码 本项目是一个模仿暴风影音的UI项目源码,仿照的界面有菜单页,主页,分类页等,项目内的所有数据都使用的本地模拟数据,仿照度一般在大分辨设备上布局显示会有问题,480x800的分辨率应该正合适,默认编译版本4.2.2编码GBK,注释不多,需要的朋友可以下载看一下. 下载地址:http://www.devstore.cn/code/info/133.html 运行截图:     版权声明:本文为博主原创文章,未经博主允许不得转载.