Discuz BBS客户端源码 [复制链接]
感谢台风, 这个十一长假让我好好的休息了一回, 睡觉到腰酸背疼, 看电影看到眼发红. 今天最后一天, 不敢出去逛, 不知道哪会还会下暴雨... 嗯嗯..这个项目其实在十一之前就开始了, 工作无聊,没有新任务, 我就搞起它. 至于为什么选 Discuz 的 BBS , 因为我常上的几个网站, 都有一堆的 APP , 官方的, 第三方的 . BBS 虽然已经没落了, 但是官方的 APP 居然用不了! 写这个东西之前, 本来想拿来看 1024 的, 但是 1024 要么不是最新版本, 要么禁用了 API, 我就只能哈哈哈, 拿"以前" 最常上的 BBS 的 API 开始了. 上几张图: Andorid
WP IOS : 没有, 只顾睡觉看电影了, 没有搞. APIDiscuz! X3.x 及已上版本内置 API , 低版本中, 用插件的形式提供. 这里是几个主要的 API : 版块列表 : http://www.moke8.com/api/mobile/index.php?mobile=no&version=1&module=forumindex 版块的主题列表 : http://www.moke8.com/api/mobile/index.php?mobile=no&version=1&module=forumdisplay&fid=11 其中的 fid 是上个 API 中返回值中的 fid , 即版块ID, 还可以有 page, tpp (pageSize) 主题详细: http://www.moke8.com/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 <ContentPage12 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 & CommandWPF 中的 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 的等价物 ContentViewWPF 中有 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, 找了几个拖拽的示例, 都不是我心中的理想状态, 暂时搁浅. |
历史资源提醒--必看
该页面资源/教程来自原魔趣吧历史资源转移,因发布历史久远,部分资源/教程可能已失效或无法在最新版程序中安装使用!DZ资源建议在Discuz3.4及以下版本使用,PHP版本建议5.6。资源仅提供做代码研究学习使用!
因改版,部分贴内链接将无法正常跳转,如链接失效或未正常跳转,请利用站内搜索功能搜索资源名称获取对应资源!