首先感谢林政老师的博客,给了我很大的指导。
准备工作
我的开发环境:
- Visual Studio 2013(With Update 4)
- Windows Phone 8.1
- Windows 8.1
我使用的是百度天气的api,所以你需要一个百度天气的ak,戳这里申请。记住你ak的值,像这样拼接uri:
http://api.map.baidu.com/telematics/v3/weather?location=城市
&output=xml&ak=你的ak
可以获取一个xml格式的返回数据:
具体思路
实现
ForecastPeriod类,用来记录每天的天气和数据的绑定:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace WeatherForecastDIY
{
/// <summary>
/// 天气预报数据类
/// </summary>
class ForecastPeriod:INotifyPropertyChanged
{
private string weather;//天气
private string wind;//风力
private string maxTemperature;//最高温
private string minTemperature;//最低温
private string date;//日期
private string dayPictureUrl;//天气图片
private string temperature;//温度
public string Weather
{
set
{
if (value != weather)
{
weather = value;
OnpropertyChanged("Weather");
}
}
get
{
return weather;
}
}
public string Wind
{
set
{
if (value != wind)
{
wind = value;
OnpropertyChanged("Wind");
}
}
get
{
return wind;
}
}
public string MaxTemperature
{
set
{
if (value != maxTemperature)
{
maxTemperature = value;
OnpropertyChanged("MaxTemperature");
}
}
get
{
analysisTemperature();
return maxTemperature;
}
}
public string MinTemperature
{
set
{
if (value != minTemperature)
{
minTemperature = value;
OnpropertyChanged("MinTemperature");
}
}
get
{
analysisTemperature();
return minTemperature;
}
}
public string Date
{
set
{
if (value != date)
{
date = value;
OnpropertyChanged("Date");
}
}
get
{
return date;
}
}
public string DayPictureUrl
{
set
{
if (dayPictureUrl != value)
{
dayPictureUrl = value;
OnpropertyChanged("DayPictureUrl");
}
}
get
{
return dayPictureUrl;
}
}
public string Temperature
{
set
{
if (value != temperature)
{
temperature = value;
OnpropertyChanged("Temperature");
}
}
get
{
return temperature;
}
}
/// <summary>
/// 分析温度字符串,得到最高最低温
/// </summary>
private void analysisTemperature()
{
if (Temperature == null)
return;
int p = -1;//波浪号的位置
for (int i = 0; i < Temperature.Length; i++)
if (Temperature[i] == '~') { p = i; break; }
if(p==-1)//没有波浪号
{
maxTemperature = Temperature;
minTemperature = Temperature;
}
else
{
maxTemperature = Temperature.Substring(0, p) + "℃";
minTemperature = Temperature.Substring(p + 1, Temperature.Length - p - 1);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnpropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler!=null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
Forecast类,这个的主要功能是异步获取数据和解析Xml:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.ComponentModel;
using Windows.UI.Xaml;
using Windows.Storage;
namespace WeatherForecastDIY
{
/// <summary>
/// 天气预报类
/// </summary>
class Forecast:INotifyPropertyChanged
{
private string city;//城市名
private string currentTemperature;//实时的温度
private Visibility vis;//控制进度条是否可见
public string City
{
set
{
if (city != value)
{
city = value;
OnpropertyChanged("City");
}
}
get
{
return city;
}
}
public string CurrentTemperature//实时气温
{
set
{
if (currentTemperature != value)
{
currentTemperature = value;
OnpropertyChanged("CurrentTemperature");
}
}
get
{
return currentTemperature;
}
}
public ForecastPeriod TodayForecast//当天的天气
{
set;
get;
}
public Visibility Vis
{
set
{
if(value!=vis)
{
vis = value;
OnpropertyChanged("Vis");
}
}
get
{
return vis;
}
}
public ObservableCollection<ForecastPeriod> ForecastList { set; get; }//不知道多少天的天气集合
/// <summary>
/// 构造函数,初始化
/// </summary>
/// <param name="CityName"></param>
public Forecast(string CityName)
{
City = CityName;
ForecastList = new ObservableCollection<ForecastPeriod>();
TodayForecast=new ForecastPeriod();
}
public Forecast()
{
City = "成都";//默认是成都萌萌哒
ForecastList = new ObservableCollection<ForecastPeriod>();
TodayForecast = new ForecastPeriod();
}
/// <summary>
/// 异步获取天气预报
/// </summary>
/// <returns></returns>
private async void getForecastAsync()
{
//异步一开始设置进度条可见
Vis = Visibility.Visible;
//局部变量
ObservableCollection<ForecastPeriod> newForecastList = new ObservableCollection<ForecastPeriod>();
//异步获取数据,很多童鞋问为啥uri要加DateTime,那是因为如果uri是一样的,不知道哪里的缓存就不让你更新数据
Uri uri = new Uri("http://api.map.baidu.com/telematics/v3/weather?location=" + City + "&output=xml&ak=HyXsPscHkQOfR0nxyUmYrV8l&date=" + System.DateTime.Now.ToString());
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync(uri);
response.EnsureSuccessStatusCode();
Stream stream = await response.Content.ReadAsStreamAsync();
//搞到Xlm
XElement now = XElement.Load(stream);
//用完释放好习惯
httpClient.Dispose();
//解析Xml,请对照着百度返回的Xml看
//判断是否成功
if(now.Element("status").Value.Equals("success"))
{
//往下走啊往下走
now = now.Element("results").Element("weather_data").Element("date");
//这天的天气
ForecastPeriod newForecastPeriod = new ForecastPeriod();
//那一排Xml天气数据居然是并列的,解析解析
while (now != null)
{
//获取当前节点的名字
string nowName=now.Name.LocalName;
if (nowName.Equals("date"))
{
string tmp = now.Value;
//当日期的长度大于2,就是当天的日期,那个有实时天气的(这个半夜会迷之失效,为啥问度娘)
if (tmp.Length > 2)
{
newForecastPeriod.Date = tmp.Substring(0, 2);
//简单的说就是在提取实时气温
int p = -1;
for (int i = 0; i < tmp.Length; i++) if (tmp[i] == ':') { p = i; break; }
CurrentTemperature = tmp.Substring(p + 1, tmp.Length - p - 3);
}
else
newForecastPeriod.Date = now.Value;
}
else if (nowName.Equals("dayPictureUrl"))
newForecastPeriod.DayPictureUrl = now.Value;
else if (nowName.Equals("weather"))
newForecastPeriod.Weather = now.Value;
else if (nowName.Equals("wind"))
newForecastPeriod.Wind = now.Value;
else if (nowName.Equals("temperature"))
{
//每次到气温的时候就结束了一天的预报,把这一天的情况加入集合
newForecastPeriod.Temperature = now.Value;
newForecastList.Add(newForecastPeriod);
newForecastPeriod = new ForecastPeriod();
}
now = (XElement)now.NextNode;
}
//终于解析完了(长吁一口气)
//接下来就是赋值了
TodayForecast.Weather = newForecastList[0].Weather;
TodayForecast.Wind = newForecastList[0].Wind;
TodayForecast.MaxTemperature = newForecastList[0].MaxTemperature;
TodayForecast.MinTemperature = newForecastList[0].MinTemperature;
foreach (ForecastPeriod forecastPeriod in newForecastList)
ForecastList.Add(forecastPeriod);
}
else
{
//熊孩子一定乱输的城市,然后被度娘给了Error
//所以要纠正过来,还是默认成都萌萌哒
City = "成都";
//改应用数据
ApplicationDataContainer localsetting = Windows.Storage.ApplicationData.Current.LocalSettings;
localsetting.Values["City"] = City;
//这叫不叫递归调用呢?反正再来一次
getForecastAsync();
}
//终于异步完了,关掉进度条
Vis = Visibility.Collapsed;
}
/// <summary>
/// 没什么用的函数,懒得改了,大家明白那个意思就好
/// </summary>
public void getForecast()
{
getForecastAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 绑定
/// </summary>
/// <param name="name"></param>
protected void OnpropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
主界面,设计得太丑,还望美工好的童鞋给予指导和建议:
<Page
x:Class="WeatherForecastDIY.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WeatherForecastDIY"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton x:Name="Setting" Icon="Setting" Label="设置" Click="Setting_Click"/>
<AppBarButton x:Name="Refresh" Icon="Refresh" Label="刷新" Click="Refresh_Click"/>
<AppBarButton x:Name="SolidStar" Icon="SolidStar" Label="为我打分"/>
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Pivot Title="{Binding City}">
<PivotItem Header="今日">
<StackPanel x:Name="TodayStackPanel" Orientation="Horizontal">
<StackPanel x:Name="TodayWeatherStackPanel" Height="Auto" Width="15" Orientation="Vertical">
<TextBlock Text="天气 " TextWrapping="Wrap" Height="60" Width="Auto" FontSize="15"></TextBlock>
<TextBlock Text="{Binding Path=TodayForecast.Weather}" TextWrapping="Wrap" Height="Auto" Width="Auto" FontSize="15"></TextBlock>
</StackPanel>
<StackPanel x:Name="TodayWindStackPanel" Height="Auto" Width="15" Orientation="Vertical">
<TextBlock Text="风力 " TextWrapping="Wrap" Height="60" Width="Auto" FontSize="15"></TextBlock>
<TextBlock Text="{Binding Path=TodayForecast.Wind}" TextWrapping="Wrap" Height="Auto" Width="Auto" FontSize="15"></TextBlock>
</StackPanel>
<StackPanel x:Name="TodayMaxTemStackPanel" Height="Auto" Width="15" Orientation="Vertical">
<TextBlock Text="最高温 " TextWrapping="Wrap" Height="60" Width="Auto" FontSize="15"></TextBlock>
<TextBlock Text="{Binding Path=TodayForecast.MaxTemperature}" TextWrapping="Wrap" Height="Auto" Width="Auto" FontSize="15"></TextBlock>
</StackPanel>
<StackPanel x:Name="TodayMinTemStackPanel" Height="Auto" Width="15" Orientation="Vertical">
<TextBlock Text="最低温 " TextWrapping="Wrap" Height="60" Width="Auto" FontSize="15"></TextBlock>
<TextBlock Text="{Binding Path=TodayForecast.MinTemperature}" TextWrapping="Wrap" Height="Auto" Width="Auto" FontSize="15"></TextBlock>
</StackPanel>
<TextBlock x:Name="CurrentTemperature" Height="240" Width="100" Text="{Binding CurrentTemperature}" VerticalAlignment="Top" FontSize="100" FontStyle="Normal"></TextBlock>
<TextBlock Text="℃" Height="90" Width="90" VerticalAlignment="Top" FontSize="50"></TextBlock>
</StackPanel>
</PivotItem>
<PivotItem Header="每天">
<Grid x:Name="EverydayWeatherGrid">
<ListBox x:Name="EverydayWeatherListBox" Background="White">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Height="60" Width="Auto" Margin="0,0,0,15" Orientation="Horizontal">
<TextBlock Text="{Binding Date}" Width="60" FontSize="30"></TextBlock>
<Image Source="{Binding DayPictureUrl}" Width="60" Height="Auto"></Image>
<TextBlock Text="{Binding Temperature}" Width="60" Height="Auto"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</PivotItem>
</Pivot>
<ProgressBar x:Name="ProgressBar" HorizontalAlignment="Left" Height="10" VerticalAlignment="Top" Visibility="{Binding Vis}" IsIndeterminate="True" Width="399.999969482422"/>
</Grid>
</Page>
接下来是MainPage.xaml.cs,注释写得很详细:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Storage;
// “空白页”项模板在 http://go.microsoft.com/fwlink/?LinkId=391641 上有介绍
namespace WeatherForecastDIY
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
private string city;//城市(我不是字幕君。。。)
public string City
{
set
{
if(value!=city)
{
city = value;
}
}
get
{
return city;
}
}
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
//第一次进来时就更新
getCity();
ReportForecast();
}
/// <summary>
/// 报道天气
/// </summary>
private void ReportForecast()
{
Forecast forecast = new Forecast(City);
forecast.getForecast();
//数据绑定
DataContext = forecast;
EverydayWeatherListBox.ItemsSource = forecast.ForecastList;
}
/// <summary>
/// 读应用数据的函数(突然意识到应该说方法,而不是函数)
/// </summary>
private void getCity()
{
ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
if (!localSettings.Values.ContainsKey("City"))
localSettings.Values["City"] = "成都";//怎样都是成都萌萌哒
City = localSettings.Values["City"].ToString();
}
/// <summary>
/// 在此页将要在 Frame 中显示时进行调用。
/// </summary>
/// <param name="e">描述如何访问此页的事件数据。
/// 此参数通常用于配置页。</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// TODO: 准备此处显示的页面。
// TODO: 如果您的应用程序包含多个页面,请确保
// 通过注册以下事件来处理硬件“后退”按钮:
// Windows.Phone.UI.Input.HardwareButtons.BackPressed 事件。
// 如果使用由某些模板提供的 NavigationHelper,
// 则系统会为您处理该事件。
//上面一堆堆完全不知所以然,简单说就是这个界面从不见了到出现时调用这个方法
getCity();
ReportForecast();
}
/// <summary>
/// 更新按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Refresh_Click(object sender, RoutedEventArgs e)
{
getCity();
ReportForecast();
}
/// <summary>
/// 设置按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Setting_Click(object sender, RoutedEventArgs e)
{
//调到SetCirt界面
Frame.Navigate(typeof(SetCity), City);
}
}
}
接下来是SetCity的页面,也就是设置城市的页面,这个难点在于换页面。
设计:
<Page
x:Class="WeatherForecastDIY.SetCity"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WeatherForecastDIY"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton x:Name="Accept" Icon="Accept" Label="确定" Click="Accept_Click"></AppBarButton>
<AppBarButton x:Name="Cancel" Icon="Cancel" Label="取消" Click="Cancel_Click"></AppBarButton>
</CommandBar>
</Page.BottomAppBar>
<Grid>
<StackPanel x:Name="SetCityStackPanel">
<TextBlock Margin="0,0,0,0" Height="15" FontSize="15" Text="输入城市"></TextBlock>
<TextBox x:Name="TextBox" Height="15" Width="Auto" FontSize="15"></TextBox>
</StackPanel>
</Grid>
</Page>
SetCity.xaml.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Storage;
// “空白页”项模板在 http://go.microsoft.com/fwlink/?LinkID=390556 上有介绍
namespace WeatherForecastDIY
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class SetCity : Page
{
public SetCity()
{
this.InitializeComponent();
}
/// <summary>
/// 在此页将要在 Frame 中显示时进行调用。
/// </summary>
/// <param name="e">描述如何访问此页的事件数据。
/// 此参数通常用于配置页。</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
//想必大家都看见了上方微软君的迷之解释了吧。。。
TextBox.Text = (string)e.Parameter;
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
TextBox.Text = "";
}
private void Accept_Click(object sender, RoutedEventArgs e)
{
//改改改,应用数据什么的
ApplicationDataContainer localsetting = Windows.Storage.ApplicationData.Current.LocalSettings;
localsetting.Values["City"] = TextBox.Text;
//回到主界面
Frame.GoBack();
}
}
}