使用Telerik控件搭建Doubanfm频道部分
今天感觉好累啊。。还是坚持记录下。
收集的API:
https://github.com/HakurouKen/douban.fm-api
https://github.com/zonyitoo/doubanfm-qt/wiki/%E8%B1%86%E7%93%A3FM-API
https://github.com/ampm/douban-fm-sdk
都没有提到Cookie
利用这个API,本来想弄下COOKie的但是Post请求老是通过不过,索性作罢
记录下频道部分的搭建
根据下面的已知分组情况,想到用XML保存下来,再用分组的控件比较好。
想到像Toolkit的longlist(具体名字我忘了)在telerik的控件下,有一个更加方便使用的,叫RadDataBoundListBox,非常好用,分起组来很简单。
首先老样子,先绑定一个DataContext,新建VM类,Ioc注册等等。
一:频道表:
已知固定频道
channel=0 私人兆赫 type=e()
Region&Lang
channel=1 公共兆赫【地区 语言】:华语MHZ
channel=6 公共兆赫【地区 语言】:粤语MHZ
channel=2 公共兆赫【地区 语言】:欧美MHZ
channel=22 公共兆赫【地区 语言】:法语MHZ
channel=17 公共兆赫【地区 语言】:日语MHZ
channel=18 公共兆赫【地区 语言】:韩语MHZ
Ages
channel=3 公共兆赫【年代】:70年代MHZ
channel=4 公共兆赫【年代】:80年代MHZ
channel=5 公共兆赫【年代】: 90年代MHZ
Genre
channel=8 公共兆赫【流派】:民谣MHZ
channel=7 公共兆赫【流派】:摇滚MHZ
channel=13 公共兆赫【流派】:爵士MHZ
channel=27 公共兆赫【流派】:古典MHZ
channel=14 公共兆赫【流派】:电子MHZ
channel=16 公共兆赫【流派】:R&BMHZ
channel=15 公共兆赫【流派】:说唱MHZ
channel=10 公共兆赫【流派】:电影原声MHZ
Special
channel=20 公共兆赫【特辑】:女声MHZ
channel=28 公共兆赫【特辑】:动漫MHZ
channel=32 公共兆赫【特辑】:咖啡MHZ
channel=67 公共兆赫【特辑】:东京事变MHZ
Com
channel=52 公共兆赫【品牌】:乐混翻唱MHZ
channel=58 公共兆赫【品牌】:路虎揽胜运动MHZ
Artist
channel=26 公共兆赫:豆瓣音乐人MHZ
channel=dj DJ兆赫
其对应的XML
1 <?xml version="1.0" encoding="utf-8"?> 2 <AlreadyKnowChannels> 3 <Channel name="华语MHZ" channelid="1" group="Langurage"></Channel> 4 <Channel name="粤语MHZ" channelid="6" group="Langurage"></Channel> 5 <Channel name="欧美MHZ" channelid="2" group="Langurage"></Channel> 6 <Channel name="法语MHZ" channelid="22" group="Langurage"></Channel> 7 <Channel name="日语MHZ" channelid="17" group="Langurage"></Channel> 8 <Channel name="韩语MHZ" channelid="18" group="Langurage"></Channel> 9 <Channel name="70年代MHZ" channelid="3" group="Ages"></Channel> 10 <Channel name="80年代MHZ" channelid="4" group="Ages"></Channel> 11 <Channel name="90年代MHZ" channelid="5" group="Ages"></Channel> 12 <Channel name="民谣MHZ" channelid="8" group="Genre"></Channel> 13 <Channel name="摇滚MHZ" channelid="7" group="Genre"></Channel> 14 <Channel name="爵士MHZ" channelid="13" group="Genre"></Channel> 15 <Channel name="古典MHZ" channelid="27" group="Genre"></Channel> 16 <Channel name="电子MHZ" channelid="14" group="Genre"></Channel> 17 <Channel name="RBMHZ" channelid="16" group="Genre"></Channel> 18 <Channel name="说唱MHZ" channelid="15" group="Genre"></Channel> 19 <Channel name="电影原声MHZ" channelid="10" group="Genre"></Channel> 20 <Channel name="女声MHZ" channelid="20" group="Special"></Channel> 21 <Channel name="动漫MHZ" channelid="28" group="Special"></Channel> 22 <Channel name="咖啡MHZ" channelid="32" group="Special"></Channel> 23 <Channel name="东京事变MHZ" channelid="67" group="Special"></Channel> 24 <Channel name="乐混翻唱MHZ" channelid="52" group="Com"></Channel> 25 <Channel name="路虎揽胜运动MHZ" channelid="58" group="Com"></Channel> 26 <Channel name="豆瓣音乐人MHZ" channelid="26" group="Artist"></Channel> 27 <Channel name="DJ兆赫" channelid="dj" group="Artist"></Channel> 28 </AlreadyKnowChannels>
1 XDocument xdoc = XDocument.Load("XML/Channels.xml"); 2 XMLChannels = xdoc.Root.Descendants("Channel").Select( 3 (element) => 4 { 5 return new Channel 6 { 7 name = (string)element.Attribute("name"), 8 channelid = (string)element.Attribute("channelid"), 9 group = (string)element.Attribute("group") 10 }; 11 }).ToList();
再导入, XMLChannels是List<Channe>类型。
接下来创建Model
1 for (int i = 0; i < XMLChannels.Count; i++) 2 { 3 channels.Add(new 豆瓣电台.Model.Channel() { ChannelGroup = XMLChannels[i].group, ChannelName = XMLChannels[i].name, ChannelId = XMLChannels[i].channelid }); 4 }
下面是请求图片,由于异步关系,我们要把请求的顺序编号(就是那个i)也放到get类中去。
再在返回的string中放入末尾。最后就可以从订阅者里面取出来了。10,11行即是计算编号并去掉末尾这个编号,(这个编号是为了表明请求的频道ID与我们的频道表的索引(index)的一一对应的关系)
1 for (int i = 0; i < Channels.Count; i++) 2 { 3 new MyRxGetMethodService().Get("http://douban.fm/j/explore/get_channel_info?cid=" + channels[i].ChannelId, i).ObserveOn(Deployment.Current) 4 .Subscribe( 5 (result) => 6 { 7 8 try 9 { 10 int tempcount = int.Parse(result.Substring(result.LastIndexOf("}") + 1, 1)); 11 result = result.Substring(0, result.LastIndexOf("}") + 1); 12 JToken temptoken = JToken.Parse(result)["data"]["res"]; 13 // BitmapImage bitmap = new BitmapImage(); 14 // bitmap.UriSource = new Uri(temptoken["cover"].ToString(), UriKind.Absolute); 15 var dataitem = channels[tempcount]; 16 dataitem.ChannelImageSource = new Uri(temptoken["cover"].ToString(), UriKind.Absolute); 17 dataitem.ChannelIntro = temptoken["intro"].ToString(); 18 19 } 20 catch 21 { 22 } 23 24 }); 25 // Thread.Sleep(100); 26 }
背后逻辑基本好了,下面是界面
databoundlist的itemtemplate如下:用了一个SlideHubTile,使结果看起来生动一点,SlideHubTile有正面样式和背面样式。
1 <DataTemplate x:Key="JumpListItemTemplate"> 2 <Grid > 3 <Grid.Resources> 4 <DataTemplate x:Key="TitleHub"> 5 <Grid> 6 <TextBlock Text="{Binding ChannelName}" Foreground="White"/> 7 </Grid> 8 </DataTemplate> 9 <DataTemplate x:Key="BackHub"> 10 <Grid Background="#FF0072FF"> 11 <Image Source="{Binding ChannelImageSource}" 12 Stretch="UniformToFill"/> 13 <StackPanel Orientation="Vertical"> 14 <TextBlock Text="{Binding ChannelIntro}" Foreground="White" Height="85.5" Width="150" HorizontalAlignment="Left"/> 15 <TextBlock Height="85.5" Text="{Binding ChannelName}" Width="150" HorizontalAlignment="Left" Margin="0" Foreground="#FFE2901B"/> 16 </StackPanel> 17 </Grid> 18 </DataTemplate> 19 </Grid.Resources> 20 <telerikPrimitives:RadSlideHubTile Title="{Binding}" TitleTemplate="{StaticResource TitleHub}" BackContentTemplate="{StaticResource BackHub}" BackContent="{Binding}" Foreground="#FF1BA1E2"> 21 <telerikPrimitives:RadSlideHubTile.Picture> 22 <Image Source="{Binding ChannelImageSource}" 23 Stretch="UniformToFill"/> 24 </telerikPrimitives:RadSlideHubTile.Picture> 25 </telerikPrimitives:RadSlideHubTile> 26 </Grid> 27 </DataTemplate>
对于组的显示样式,我们可以用原先的模板
1 <helpers:JumpListFirstItemTemplateSelector x:Key="HeaderTemplateSelector"> 2 <helpers:JumpListFirstItemTemplateSelector.FirstItemTemplate> 3 <DataTemplate> 4 <Grid Margin="0,-8,0,12" Width="480"> 5 <TextBlock FontSize="{StaticResource PhoneFontSizeMedium}" Text="{Binding }" TextWrapping="Wrap"/> 6 </Grid> 7 </DataTemplate> 8 </helpers:JumpListFirstItemTemplateSelector.FirstItemTemplate> 9 <helpers:JumpListFirstItemTemplateSelector.StandardItemTemplate> 10 <DataTemplate> 11 <Grid Margin="0,20,0,12" Width="480"> 12 <TextBlock FontSize="{StaticResource PhoneFontSizeMedium}" Text="{Binding }" TextWrapping="Wrap"/> 13 </Grid> 14 </DataTemplate> 15 </helpers:JumpListFirstItemTemplateSelector.StandardItemTemplate> 16 </helpers:JumpListFirstItemTemplateSelector>
最后是DataBoundList的XAML
1 <telerikData:RadJumpList Margin="18,56,18,-44" 2 GroupDescriptorsSource="{Binding ChannelGroupDescriptors}" 3 ItemsSource="{Binding Channels}" 4 ItemTemplate="{StaticResource JumpListItemTemplate}" 5 GroupHeaderTemplateSelector="{StaticResource HeaderTemplateSelector}" 6 x:Name="jumplist" 7 > 8 <telerikData:RadJumpList.VirtualizationStrategyDefinition> 9 <telerikPrimitives:WrapVirtualizationStrategyDefinition Orientation="Horizontal"/> 10 </telerikData:RadJumpList.VirtualizationStrategyDefinition> 11 <i:Interaction.Triggers> 12 <i:EventTrigger EventName="ItemTap"> 13 <GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding ShowChannleDetials, Mode=OneWay}" PassEventArgsToCommand="True"/> 14 </i:EventTrigger> 15 </i:Interaction.Triggers> 16 </telerikData:RadJumpList>
用Command把点选的频道传到VM中去。
效果大致如下,(有些图片出不来,有点没弄明白)
二:具体频道信息
借用前段时间看DEEPDIVEMVVM中学到的,注入一个导航接口,
思路大致就是那个视频中提到的思路,也有源码下载。
1 public RelayCommand<Telerik.Windows.Controls.ListBoxItemTapEventArgs> ShowChannleDetials 2 { 3 get 4 { 5 return showchanneldetials 6 ?? (showchanneldetials = new RelayCommand<Telerik.Windows.Controls.ListBoxItemTapEventArgs>( 7 (args) => 8 { 9 Channel item = (args.Item.Content) as Channel; 10 string key = item.ChannelId; 11 new MyRxGetMethodService().Get("http://douban.fm/j/mine/playlist?type=n&channel=" + item.ChannelId + "&pb=64&from=mainsite").ObserveOn(Deployment.Current).Subscribe( 12 (result) => 13 { 14 var songs_string = JToken.Parse(result)["song"].ToString(); 15 var MySongs = JsonConvert.DeserializeObject<List<Song>>(songs_string); 16 if (!SimpleIoc.Default.IsRegistered<ChannelDetailViewModel>(key)) 17 { 18 SimpleIoc.Default.Register<ChannelDetailViewModel>( 19 () => 20 { 21 var mainvm = new ChannelDetailViewModel(MySongs,item.ChannelName,item.ChannelIntro,item.ChannelId,item.ChannelImageSource); 22 23 return mainvm; 24 }, key); 25 } 26 _navigate.NavigateTo( 27 new Uri( 28 "/ChannelDetail.xaml?" + key, 29 UriKind.Relative)); 30 31 }); 32 33 })); 34 } 35 }
注意new的ChannelDetailViewModel时候注入参数,还有在Ioc中注册
View部分直接使用Telerik的模板:最后效果如下:
要注意在View中也有部分代码:
别忘了在退出这个页面的时候取消掉VM的注册(再点击的过程中会重复注册)
1 public partial class ChannelDetail : PhoneApplicationPage 2 { 3 private string itemUrl; 4 public ChannelDetail() 5 { 6 InitializeComponent(); 7 } 8 protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) 9 { 10 if (DataContext == null) 11 { 12 var url = e.Uri.ToString(); 13 itemUrl = url.Substring(url.IndexOf("?") + 1); 14 15 if (!SimpleIoc.Default.IsRegistered<ChannelDetailViewModel>(itemUrl)) 16 { 17 MessageBox.Show("Item not found"); 18 return; 19 } 20 21 var vm = SimpleIoc.Default.GetInstance<ChannelDetailViewModel>(itemUrl); 22 23 DataContext = vm; 24 } 25 base.OnNavigatedTo(e); 26 } 27 protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e) 28 { 29 if (SimpleIoc.Default.IsRegistered<ChannelDetailViewModel>(itemUrl)) 30 { 31 SimpleIoc.Default.Unregister<ChannelDetailViewModel > (itemUrl); 32 } 33 base.OnNavigatingFrom(e); 34 } 35 private void Option1_Tap(object sender, System.Windows.Input.GestureEventArgs e) 36 { 37 this.Option1Viewer.Visibility = System.Windows.Visibility.Visible; 38 this.Option1Arrow.Visibility = System.Windows.Visibility.Visible; 39 this.Option2Viewer.Visibility = System.Windows.Visibility.Collapsed; 40 this.Option2Arrow.Visibility = System.Windows.Visibility.Collapsed; 41 } 42 43 private void Option2_Tap(object sender, System.Windows.Input.GestureEventArgs e) 44 { 45 this.Option1Viewer.Visibility = System.Windows.Visibility.Collapsed; 46 this.Option1Arrow.Visibility = System.Windows.Visibility.Collapsed; 47 this.Option2Viewer.Visibility = System.Windows.Visibility.Visible; 48 this.Option2Arrow.Visibility = System.Windows.Visibility.Visible; 49 } 50 }
还有点击图片更新歌曲:
使Songs重新指向一片区域,再raise属性改变,最后效果如下:
1 public RelayCommand RefreshCommand 2 { 3 get 4 { 5 return refreshcommand 6 ?? (refreshcommand = new RelayCommand( 7 () => 8 { 9 new MyRxGetMethodService().Get("http://douban.fm/j/mine/playlist?type=n&channel=" + ChannelId + "&pb=64&from=mainsite").ObserveOn(Deployment.Current).Subscribe( 10 (result) => 11 { 12 var songs_string = JToken.Parse(result)["song"].ToString(); 13 var Songs3 = JsonConvert.DeserializeObject<List<Song>>(songs_string); 14 Song[] tempsongs = new Song[songs.Count]; 15 songs.CopyTo(tempsongs); 16 List<Song> songstemp1 = new List<Song>(tempsongs); 17 songstemp1.AddRange(Songs3); 18 Songs = songstemp1; 19 }); 20 })); 21 } 22 }
三.结语
还有些想法还没实现,今天就到这里吧