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) , 这名字取的好蛋疼啊.
项目结构

基于
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 的构造函数中添加:
public App(SimpleContainer container) {
this.InitializeComponent();
InitializeComponent 会在你添加 App.xaml 之后, 自动生成, 在它里面会去加载 app.xaml
Caliburn.Micro
之前发过一篇, 不在赘述:
Xamarin 的 MVVM 之 Caliburn.Micro
TabbedPage 数据源绑定
之前写的项目没用 MVVM, 直接这样写:
public partial class BusPage : TabbedPage {
public BusPage() {
//InitializeComponent();
//this.ItemsSource = this.Pages;
this.Title = "LBC 业务通";
this.BackgroundColor = Color.White;//在XAML中设置 Background 不起作用。
this.Children.Add(new OrderListPage());
this.Children.Add(new CustomerListPage());
this.Children.Add(new TemplatesListPage());
this.Children.Add(new SettingPage());
}
}
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LBC.Mobi.Pages.BusPage"
Padding="10"
Title="LBC"
>
<TabbedPage.ItemTemplate>
<DataTemplate>
<ContentPage
Title="{Binding Title}" Content="{Binding Content}"
/>
</DataTemplate>
</TabbedPage.ItemTemplate>
</TabbedPage>
即: 根本就没有用到 ItemsSource, 直接添加到 Children 中的.
这样是很省事, 但是在 MVVM 中, 行不通了, 因为 Children 没有对应的依赖属性, 无法通过 MVVM 绑定.
为解决这个问题, 先扩展 ContentPage
public class ViewLocatorPage : ContentPage {
public static readonly BindableProperty VMProperty = BindableProperty.Create<ViewLocatorPage, Screen>(p => p.VM, null, propertyChanged: VMChanged);
public Screen VM {
get {
return (Screen)this.GetValue(VMProperty);
}
set {
this.SetValue(VMProperty, value);
}
}
private static void VMChanged(BindableObject bindable, object oldValue, object newValue) {
var vm = (Screen)newValue;
//var view = vm.GetView();
var vmView = ViewLocator.LocateForModel(vm, null, null);
if (vmView == null)
throw new Exception("没有找到视图");
ViewModelBinder.Bind(vm, vmView, null);
var activator = vm as IActivate;
if (activator != null)
activator.Activate();
var page = (ViewLocatorPage)bindable;
if (null != (ContentPage)vmView) {
var vp = (ContentPage)vmView;
page.Content = vp.Content;
if (vp.ToolbarItems != null)
foreach (var t in vp.ToolbarItems)
page.ToolbarItems.Add(t);
} else if (null != (Xamarin.Forms.View)vmView) {
page.Content = (Xamarin.Forms.View)vmView;
}
}
}
VM做为依赖属性 (VMProperty), 在变化的时候会调用 VMChanged 方法.
在这个方法中, 会跟据 VM 去查询对应的视图, ViewLocator.LocateForModel(...)
将视图和模型绑定, ViewModelBinder.Bind(...)
并激活, activator.Activate()
然后做为 page 的 content 呈现在 TabbedPage 中. page.Content = vp.Content
需要注意的是 ViewLocator / ViewModelBinder 是 CM 中提供的, 所以用其它 MVVM 框架的, 请换成对应的 API 方法.
这样一来, 数据绑定就很简单了:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Discuz.Views.TabView"
xmlns:cal="clr-namespace:Caliburn.Micro.Xamarin.Forms;assembly=Caliburn.Micro.Platform.Xamarin.Forms"
xmlns:local="clr-namespace:Discuz;assembly=Discuz"
ItemsSource="{Binding Datas}"
Title="蓝色理想"
BackgroundColor="#1e5263"
>
<TabbedPage.ItemTemplate>
<DataTemplate>
<local:ViewLocatorPage Title="{Binding DisplayName}" VM="{Binding}" />
</DataTemplate>
</TabbedPage.ItemTemplate>
</TabbedPage>
Event & Command
WPF 中的 CM , 会跟据控件的类型自动选择默认的事件, 比如
<Button x:Name="Show" ... />
会在 Click 的时候, 自动去调用 VM中的 Show 方法,
或者
<Button cal:Message.Attach="Show" /> <xxx cal:Message.Attach="[Event click] = [Action Show()] ; [Event XXX] = [Action XXX()]" />
这样写多多少有点蛋疼.
在 CM For XF中, 无法通过 x:Name 获取到对应的控件, 所以第一种写法不能用, 第二种正如我说的, 蛋疼...
还好, XF 除了提供事件之外, 还会提供相应的 Command !
<ListView.Footer>
<StackLayout Padding="10">
<Button Text="加载更多" Command="{Binding LoadMoreCmd}"
BindingContext="{Binding Source={x:Reference root}, Path=BindingContext}"
/>
</StackLayout>
</ListView.Footer>
ContentControl 的等价物 ContentView
WPF 中有 ContentControl, 它在 MVVM 中的意义重大, 使你不用关心它如何展示出来, 只用给它一个 ViewModel, 已达到功能分解的目的.
在 XF 中,没有 ContentControl, 但是有 ContentView , 一般自定义用户控件都是从它继承.
有了 ContentView , 就可以把臃肿的主页面分解成不同的子模型与视图了.
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Padding="5,1">
<ctrls:Border Style="{StaticResource BlockBorder}">
<ContentView cal:View.Model="{Binding }" />
</ctrls:Border>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
手势 GestureRecognizers
在 ListView 中, 可以通过 ItemTapped 来判断哪一行数据被点击.
用 ContentView 进行分解之后, 我想把 tap 事件也给分解到具体的子模型中, 这样可以更多的自主选择, 但是在子模型中怎么响应 ItemTapped 呢? 显然是不可能的!
XF 提供的手势功能可以很好的解决这个问题:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Discuz.Views.ForumDetailView"
xmlns:ctrls="clr-namespace:Discuz.Controls;assembly=Discuz"
xmlns:cal="clr-namespace:Caliburn.Micro.Xamarin.Forms;assembly=Caliburn.Micro.Platform.Xamarin.Forms"
>
<ContentView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}" />
</ContentView.GestureRecognizers>
意思就是说在这个ContentView 上 tap 的时候, 响应 TapCommand , 它和在 ListView 上响应 ItemTapped 是一样的效果!
消息传输中心 MessagingCenter
正如字面意思,它是 XF 中, 用来 在 不同的页面 之间 传输消息的, 是对观察者模式的封装.
消息传输, 需要两个元素, 消息标识符 和 消息内容
同时还要有消息的接收方法和发送方.
发送方:
public static void Send<TSender>(TSender sender, string message) where TSender : class; public static void Send<TSender, TArgs>(TSender sender, string message, TArgs args) where TSender : class;
参数 message 其实是消息的标识符, 不要被字面意思给忽悠了, args 才是真正的消息内容.
MessagingCenter.Send(this, "AddFavorite", new Tuple<int, string>(this.Data.ID, this.Data.Name));
接收方:
public static void Subscribe<TSender, TArgs>(object subscriber, string message, Action<TSender, TArgs> callback, TSender source = null) where TSender : class; public static void Subscribe<TSender>(object subscriber, string message, Action<TSender> callback, TSender source = null) where TSender : class;
同样, message 指的是消息标识符, args 才是真正的消息内容.
MessagingCenter.Subscribe<ForumDetailViewModel, Tuple<int, string>>(this, "AddFavorite", (sender, arg) => {
if (!this.Favorites.ContainsKey(arg.Item1)) {
this.ShowFavorite(arg.Item1, arg.Item2);
this.AddToFavorite(arg.Item1, arg.Item2);
}
});
这样一来, 就可以在两个不同的页面之前传递消息了, 只不过有一点缺点: 发送方不能得到消息的反馈.
拖拽排序
很遗憾, XF 的 ListView 没有相关的事件..
要完成这个功能, 要自己实现 Render, 找了几个拖拽的示例, 都不是我心中的理想状态, 暂时搁浅.
Android 样式生成器:
http://jgilfelt.github.io/android-actionbarstylegenerator/
-------------
OK 完
Xamarin.Form 实例: Discuz BBS 客户端 源码分享的更多相关文章
- C#中国象棋+游戏大厅 服务器 + 客户端源码
来源:www.ajerp.com/bbs C#中国象棋+游戏大厅 服务器 + 客户端源码 源码开源 C#版中国象棋(附游戏大厅) 基于前人大虾的修改版 主要用委托实现 服务器支持在线人数,大厅桌数的设 ...
- 业务类接口在TCP,HTTP,BLL模式下的实例 设计模式混搭 附源码一份
业务类接口在TCP,HTTP,BLL模式下的实例 设计模式混搭 附源码一份 WinForm酒店管理软件--框架这篇随笔可以说是我写的最被大家争议的随笔,一度是支持和反对是一样的多.大家对我做的这个行业 ...
- FileZilla客户端源码解析
FileZilla客户端源码解析 FTP是TCP/IP协议组的协议,有指令通路和数据通路两条通道.一般来说,FTP标准命令TCP端口号是21,Port方式数据传输端口是20. FileZilla作为p ...
- Zookeeper 源码(三)Zookeeper 客户端源码
Zookeeper 源码(三)Zookeeper 客户端源码 Zookeeper 客户端主要有以下几个重要的组件.客户端会话创建可以分为三个阶段:一是初始化阶段.二是会话创建阶段.三是响应处理阶段. ...
- Plupload上传实例《模仿微云上传实例》,带源码
Plupload上传实例<模仿微云上传实例>,带源码,作者:鱼塘总裁 如有疑问,加群交流:646104701 一.实例截图 1.上传过程 2.上传成功 3.上传失败 4.最小化 二.所需文 ...
- Eureka 系列(04)客户端源码分析
Eureka 系列(04)客户端源码分析 [TOC] 0. Spring Cloud 系列目录 - Eureka 篇 在上一篇 Eureka 系列(01)最简使用姿态 中对 Eureka 的简单用法做 ...
- swift实现饭否应用客户端源码
swift 版 iOS 饭否客户端 源码下载:http://code.662p.com/view/13318.html 饭否是中国大陆地区第一家提供微博服务的网站,被称为中国版Twitter.用户可通 ...
- android版高仿淘宝客户端源码V2.3
android版高仿淘宝客户端源码V2.3,这个版本我已经更新到2.3了,源码也上传到源码天堂那里了,大家可以看一下吧,该应用实现了我们常用的购物功能了,也就是在手机上进行网购的流程的,如查看产品(浏 ...
- 控件真的很好用,突然感觉自己以前研究Discuz!NT366源码的方式很2了
控件真的很好用,突然感觉自己以前研究Discuz!NT366源码的方式很2了,就是按钮上的或其他控件上的图片哪里去了?
随机推荐
- C# PDF打印
C#中使用iTextSharp生成并下载PDF很方便. 首先要将iTextSharp的dll下载并引入项目 主要分成两部分,一部分是PDF的Document生成,另一部分就是将Document输出到页 ...
- javascript特效实现(4)——当前时间和倒计时效果
这个效果的实现关键是对Date对象和setTimeout的使用. 一共有三个例子,HTML结构如下,就不添加CSS样式了. <body> 当前时间:<p id="p1&qu ...
- Andorid 内存溢出与内存泄露,几种常见导致内存泄露的写法
内存泄露,大部分是因为程序的逻辑不严谨,但是又可以跑通顺,然后导致的,内存溢出不会报错,如果不看日志信息是并不知道有泄露的.但是如果一直泄露,然后最终导致的内存溢出,仍然会使程序挂掉.内存溢出大部分是 ...
- mac 免密码登陆服务器
由于mac os 是基于unix的操作系统终端和linux非常类似,所以不用借助类似于windows下的putty 和CRT工具即可远程登陆linux服务器,只需简单地3步即可免密码ssh远程. 1 ...
- HADOOP namenode HA
参考的文章:http://www.cnblogs.com/smartloli/p/4298430.html 当然,在操作的过程中,发现与上述文章中描述的还是有一些小小的区别. 配置好后,start-d ...
- HFSS学习(一)计划
2015-11-28 21:05:33 基本概念 边界条件 激励源 建模 网格划分 变量设置与调谐优化 仿真结果 实例 微带线仿真 Ku波段微带线发夹线滤波器仿真 介质滤波器 腔体滤波器 微带一分四功 ...
- MIT jos 6.828 Fall 2014 训练记录(lab 5)
源代码参见我的github: https://github.com/YaoZengzeng/jos File system perliminaries 我们开发的是一个单用户的操作系统,只提供了足够的 ...
- java 21 - 11 IO流的标准输入流和标准输出流
标准输入输出流 System类中的两个成员变量: public static final InputStream in "标准"输入流. public static final P ...
- sublime配置全攻略
大家好,今天给大家分享一款编辑器:sublime text2 我用过很多编辑器, EditPlus.EmEditor.Notepad++.Notepad2.UltraEdit.Editra.V ...
- 应用python编写简单新浪微博应用(一)
转载至:http://blog.sina.com.cn/s/blog_6c39196501016o7n.html 首先,你要有一个新浪微博账号. 申请页面:http://weibo.com 其次,你要 ...