基於metro風格的Windows phone 8 應用提到了圖塊的概念,它就是指啟動菜單中的快速啟動圖標。一般一個應用必須有一個默認圖塊,還可以有若干個次要圖塊。另外,通知與圖塊的關系比較密切,我們可以通過在接受到消息時動態更新圖塊來達到適時的效果。我們本節把圖塊和通知放在一起講。
快速導航:
一、圖塊
二、圖塊更新計划
三、本地通知
四、推送通知
一、圖塊
1)定義默認圖塊
默認圖塊只能在清單文件中定義它,並且選定的圖塊模板后就不能再改變,除非重新發布應用,但是我們可以更新同類型的模板。應用安裝后默認狀態不會出現在開始菜單,需要在程序清單中右鍵選擇固定到開始屏幕項。
圖塊模板類型:
1.圖標模版:它可以包含大小兩種圖標,三種圖塊都可以用。必須為包含透明背景的png格式metro風格圖片。
2.翻轉模版:在中型、大型圖塊中可以實現翻轉效果。
3.循環模板:在中型、大型圖塊中實現多張背景圖輪流切換。
2)創建更新圖塊
圖塊的創建和更新可以通過兩種方式,分別是定義模版xml或通過代碼構建,下面的代碼演示了如何創建和更新圖塊。
[C#] public partial class MainPage : PhoneApplicationPage
{
// 構造函數
public MainPage()
{
InitializeComponent();
// 用於本地化 ApplicationBar 的示例代碼
//BuildLocalizedApplicationBar();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
CreateTitle();
}
private void ShowTitle()
{
var tilte = ShellTile.ActiveTiles.FirstOrDefault();
if (tilte != null)
{
MessageBox.Show(tilte.NavigationUri.ToString());
}
}
//圖標圖塊
IconicTileData iconicTileData = new IconicTileData()
{
Title = "標題",
Count = 5,
WideContent1 = "第一行文本",
WideContent2 = "第二行文本",
WideContent3 = "第三行文本",
SmallIconImage = new Uri("/Assets/Tiles/IconLarge.png", UriKind.Relative),
IconImage = new Uri("/Assets/Tiles/IconSamall.png", UriKind.Relative),
//透明度設置為255才會顯示自定義顏色背景,否則顯示為系統背景
BackgroundColor = new Color { A = 255, R = 0, G = 148, B = 255 }
};
//圖標圖塊模板
string iconicTileXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
<wp:Notification xmlns:wp=""WPNotification"" Version=""2.0"">
<wp:Tile Id=""titleid1"" Template=""IconicTile"">
<wp:SmallIconImage>/Assets/Tiles/IconLarge.png</wp:SmallIconImage>
<wp:IconImage>/Assets/Tiles/IconSamall.png</wp:IconImage>
<wp:WideContent1>第一行文本</wp:WideContent1>
<wp:WideContent2 Action=""Clear"">第二行文本</wp:WideContent2>
<wp:WideContent3>第三行文本</wp:WideContent3>
<wp:Count>6</wp:Count>
<wp:Title>標題</wp:Title>
<wp:BackgroundColor>#FF524742</wp:BackgroundColor>
</wp:Tile>
</wp:Notification>";
//可用於清除Count(如果加了Action="Clear",則清除該項的顯示)
string iconicTileXml2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<wp:Notification xmlns:wp=""WPNotification"" Version=""2.0"">
<wp:Tile Id=""titleid1"" Template=""IconicTile"">
<wp:Count Action=""Clear"">0</wp:Count>
</wp:Tile>
</wp:Notification>";
//翻轉圖塊
FlipTileData flipTileData = new FlipTileData()
{
Title = "標題",
BackTitle = "背面標題",
BackContent = "背面的文本內容部分",
WideBackContent = "在寬圖塊背面的文本內容",
Count = 5,
SmallBackgroundImage = new Uri("/Assets/Tiles/Samall.png", UriKind.Relative),
BackgroundImage = new Uri("/Assets/Tiles/Medium.png", UriKind.Relative),
//不設置背景圖像則顯示為系統背景色
//BackBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileMedium.png", UriKind.Relative),
WideBackgroundImage = new Uri("/Assets/Tiles/Large.png", UriKind.Relative),
//WideBackBackgroundImage = new Uri("/Assets/Tiles/IconicTileMediumLarge.png", UriKind.Relative)
};
//翻轉圖塊模板
string flipTileXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
<wp:Notification xmlns:wp=""WPNotification"" Version=""2.0"">
<wp:Tile Id=""titleid2"" Template=""FlipTile"">
<wp:SmallBackgroundImage>/Assets/Tiles/Samall.png</wp:SmallBackgroundImage>
<wp:WideBackgroundImage>/Assets/Tiles/Large.png</wp:WideBackgroundImage>
<wp:WideBackBackgroundImage>/Assets/Tiles/IconicTileMediumLarge.png</wp:WideBackBackgroundImage>
<wp:WideBackContent>在寬圖塊背面的文本內容</wp:WideBackContent>
<wp:BackgroundImage>/Assets/Tiles/Medium.png</wp:BackgroundImage>
<wp:Count>6</wp:Count>
<wp:Title>標題</wp:Title>
<wp:BackBackgroundImage>Assets/Tiles/FlipCycleTileMedium.png</wp:BackBackgroundImage>
<wp:BackTitle>背面標題</wp:BackTitle>
<wp:BackContent>背面的文本內容部分</wp:BackContent>
</wp:Tile>
</wp:Notification>";
//循環圖塊
CycleTileData cycleTileData = new CycleTileData()
{
Title = "標題",
Count = 10,
SmallBackgroundImage = new Uri("/Assets/Tiles/Samall.png", UriKind.Relative),
CycleImages = new Uri[]
{
new Uri("/Assets/Tiles/Title1.png", UriKind.Relative),
new Uri("/Assets/Tiles/Title2.png", UriKind.Relative),
new Uri("/Assets/Tiles/Title3.png", UriKind.Relative),
}
};
//循環圖塊模板
string cycleTileXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
<wp:Notification xmlns:wp=""WPNotification"" Version=""2.0"">
<wp:Tile Id=""titleid3"" Template=""CycleTile"">
<wp:SmallBackgroundImage>/Assets/Tiles/Samall.png</wp:SmallBackgroundImage>
<wp:CycleImage1>/Assets/Tiles/Title1.png</wp:CycleImage1>
<wp:CycleImage2>/Assets/Tiles/Title2.png</wp:CycleImage2>
<wp:CycleImage3>/Assets/Tiles/Title3.png</wp:CycleImage3>
<wp:Count>6</wp:Count>
<wp:Title>標題</wp:Title>
</wp:Tile>
</wp:Notification>";
private void CreateTitle()
{
//添加一個次要圖塊
ShellTile.Create(new Uri("/Page1.xaml", UriKind.Relative), iconicTileData, true);
//通過xml模板添加
ShellTile.Create(new Uri("/Page1.xaml", UriKind.Relative), new IconicTileData(iconicTileXml), true);
}
private void ClearCount()
{
//清除Count
ShellTile.ActiveTiles.ElementAt(1).Update(new IconicTileData(iconicTileXml2));
}
private void UpdateTitle()
{
//更新默認圖塊的內容,我們定義的翻轉模版,這里不能修改模版類型
ShellTile.ActiveTiles.FirstOrDefault().Update(flipTileData);
}
}
二、圖塊更新計划
我們可以定義一個更新計划,定期的更新圖塊的背景圖像。當應用退出以后,這個更新計划依然能夠在后台運行。它的實現代碼如下:
[C#] ShellTileSchedule SampleTileSchedule = new ShellTileSchedule();
//計划是否已經執行
bool TileScheduleRunning = false;
//開始執行計划
private void Button_Click_1(object sender, RoutedEventArgs e)
{
//指定計划執行一次還是多次
SampleTileSchedule.Recurrence = UpdateRecurrence.Interval;
//指定計划的更新間隔時間
SampleTileSchedule.Interval = UpdateInterval.EveryHour;
//指定計划的執行次數,如果未設置,則為不確定次數
SampleTileSchedule.MaxUpdateCount = 50;
//指定計划的開始時間
SampleTileSchedule.StartTime = DateTime.Now;
//獲取背景圖像的網絡URI
SampleTileSchedule.RemoteImageUri = new Uri(@"http://images.cnblogs.com/cnblogs_com/lipan/319399/o_Large.png");
SampleTileSchedule.Start();
}
//停止計划
private void Button_Click_2(object sender, RoutedEventArgs e)
{
if (TileScheduleRunning) SampleTileSchedule.Stop();
}
三、本地通知
通知分為本地通知和推送通知。我們可以通過本地通知實現本地消息和提醒功能。
1)本地消息和提醒
我們可以定義提醒和鬧鍾的功能,在應用退出以后,當計划的提醒時間達到時,提醒或者鬧鍾功能將自動別觸發,其中鬧鍾的功能我們還可以自定義鈴聲。下面看看代碼實現的過程。
[XAML] <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="listbox1" Width="440" MaxHeight="420" Margin="10,44,0,150">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal" Background="Blue" Width="440">
<TextBlock Width="120" Text="{Binding Title}" />
<TextBlock Text="{Binding BeginTime}" />
<TextBlock Text="{Binding RecurrenceType}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding IsScheduled}" />
<TextBlock Text=" " />
<Button Margin="0" Tag="{Binding Name}" Click="deleteButton_Click" Content="X" BorderBrush="Red" Background="Red" Foreground="{StaticResource PhoneBackgroundBrush}" VerticalAlignment="Top" BorderThickness="0" Width="50" Padding="0,0,0,0"></Button>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Margin" Value="5"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<TextBlock HorizontalAlignment="Left" Margin="10,12,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Text="已注冊的本地消息提醒:">
</TextBlock>
<TextBox x:Name="textbox1" HorizontalAlignment="Left" Height="72" Margin="10,555,0,0" TextWrapping="Wrap" Text="標題" VerticalAlignment="Top" Width="125"/>
<RadioButton x:Name="radioButton1" IsChecked="True" GroupName="radioButtonGroup" Content="Reminder" HorizontalAlignment="Left" Margin="135,532,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="radioButton2" GroupName="radioButtonGroup" Content="Alarm" HorizontalAlignment="Left" Margin="134,604,0,0" VerticalAlignment="Top"/>
<Button Content="注冊" HorizontalAlignment="Left" Margin="328,569,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
</Grid>
public partial class Page1 : PhoneApplicationPage
{
public Page1()
{
InitializeComponent();
}
IEnumerable<ScheduledNotification> notifications;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ListboxInit();
base.OnNavigatedTo(e);
}
private void ListboxInit()
{
//返回系統所有已注冊的通知
notifications = ScheduledActionService.GetActions<ScheduledNotification>();
listbox1.ItemsSource = notifications;
}
//刪除一個通知
private void deleteButton_Click(object sender, RoutedEventArgs e)
{
string name = (string)((Button)sender).Tag;
ScheduledActionService.Remove(name);
ListboxInit();
}
//新增一個通知
private void Button_Click_1(object sender, RoutedEventArgs e)
{
String name = System.Guid.NewGuid().ToString();
if (radioButton1.IsChecked == true)
{
//名稱,唯一標識
Reminder reminder = new Reminder(name);
//消息標題
reminder.Title = textbox1.Text;
reminder.Content = "這里是提醒的正文部分。";
//消息重現類型
reminder.RecurrenceType = RecurrenceInterval.Daily;
//開始時間
reminder.BeginTime = DateTime.Now + new TimeSpan(0, 0, 30);
//結束時間
reminder.ExpirationTime = DateTime.Now + new TimeSpan(0, 0, 45);
//從提醒啟動應用程序時的啟動URI
reminder.NavigationUri = new Uri("/Page2.xaml?a=test", UriKind.Relative);
//注冊
ScheduledActionService.Add(reminder);
}
else
{
//可以自定義鈴聲的通知
Alarm alarm = new Alarm(name);
alarm.Content = "這里是鬧鍾的正文部分。";
//提醒時播放的文件
alarm.Sound = new Uri("/1.mp3", UriKind.Relative);
//消息重現類型
alarm.RecurrenceType = RecurrenceInterval.Daily;
//開始時間
alarm.BeginTime = DateTime.Now + new TimeSpan(0, 0, 30);
//結束時間
alarm.ExpirationTime = DateTime.Now + new TimeSpan(0, 1, 30);
//注冊
ScheduledActionService.Add(alarm);
}
ListboxInit();
}
}
2)本地Toast
通過本地Toast可以在實現Toast消息彈出,但是當應用運行時則不會彈出,所以一般在后台計划中被調用。詳細情況請見《Windows phone 8 學習筆記 多任務 后台代理》 。
四、推送通知
推送通知都需要借助於微軟推送雲服務器,因為一般來講,應用退出以后是不會保留后台服務去等待接受消息的,這種做法比較費電。推送通知的做法是,當有消息推送過來的時候,由系統去統一完成消息的接收,用戶選擇性的去啟動應用。
1. 推送通知的類型
推送通知主要有三種類型,如下:
1.磁貼通知:消息到達時,將會更新應用的默認圖塊,這樣直觀的現實當前應用有更新內容。
2.Toast推送通知:消息到達時,將會在屏幕上方彈出一個Toast提示,用戶單擊即可啟動應用。
3.raw通知:這個通知在應用運行的前提下,提供靈活的消息處理,但是非允許狀態下將無法接受消息。
2. 推送通知的實現
要實現推送通知,首先我們需要建立推送通道。在Windows phone 中建立推送通道,並且得到通道的URI。代碼如下:
[C# Windows phone] //磁貼通知
private void TileInit()
{
//推送服務通道
HttpNotificationChannel pushChannel;
//通道名稱
string channelName = "TileSampleChannel";
InitializeComponent();
//嘗試發現是否已經創建
pushChannel = HttpNotificationChannel.Find(channelName);
var newChannel = false;
//沒有發現,新建一個
if (pushChannel == null)
{
pushChannel = new HttpNotificationChannel(channelName);
newChannel = true;
}
//通知通道關聯URI改變時:
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
//出錯時:
pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);
if (newChannel)
{
pushChannel.Open();
//將通知訂閱綁定到默認圖塊
pushChannel.BindToShellTile();
}
else
{
MessageBox.Show(String.Format("通道URI: {0}", pushChannel.ChannelUri.ToString()));
}
}
//Toast通知
private void ToastInit()
{
//推送服務通道
HttpNotificationChannel pushChannel;
//通道名稱
string channelName = "ToastSampleChannel";
InitializeComponent();
//嘗試發現是否已經創建
pushChannel = HttpNotificationChannel.Find(channelName);
var newChannel = false;
//沒有發現,新建一個
if (pushChannel == null)
{
pushChannel = new HttpNotificationChannel(channelName);
newChannel = true;
}
//通知通道關聯URI改變時:
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
//出錯時:
pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);
//收到Toast消息時:(如果程序未啟動則彈出Toast,否則觸發該事件)
pushChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(PushChannel_ShellToastNotificationReceived);
if (newChannel)
{
pushChannel.Open();
//將通知訂閱綁定到ShellToast
pushChannel.BindToShellToast();
}
else
{
MessageBox.Show(String.Format("通道URI: {0}", pushChannel.ChannelUri.ToString()));
}
}
//Row通知
private void RawInit()
{
//推送服務通道
HttpNotificationChannel pushChannel;
//通道名稱
string channelName = "RawSampleChannel";
InitializeComponent();
//嘗試發現是否已經創建
pushChannel = HttpNotificationChannel.Find(channelName);
var newChannel = false;
//沒有發現,新建一個
if (pushChannel == null)
{
pushChannel = new HttpNotificationChannel(channelName);
newChannel = true;
}
//通知通道關聯URI改變時:
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
//出錯時:
pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);
//收到Raw通知時:(只有應用運行時才觸發本事件)
pushChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(PushChannel_HttpNotificationReceived);
if (newChannel)
{
pushChannel.Open();
//這里並沒有綁定操作
}
else
{
MessageBox.Show(String.Format("通道URI: {0}", pushChannel.ChannelUri.ToString()));
}
}
//URI更新時
void PushChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(String.Format("通道URI: {0}", e.ChannelUri.ToString()));
});
}
//遇到錯誤時
void PushChannel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)
{
//處理錯誤
}
//收到Toast通知時
void PushChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
{
StringBuilder message = new StringBuilder();
string relativeUri = string.Empty;
message.AppendFormat("收到 Toast {0}:\n", DateTime.Now.ToShortTimeString());
foreach (string key in e.Collection.Keys)
{
message.AppendFormat("{0}: {1}\n", key, e.Collection[key]);
if (key.ToLower() == "wp:param") relativeUri = e.Collection[key];
}
Dispatcher.BeginInvoke(() => MessageBox.Show(message.ToString()));
}
//收到Raw通知時:
void PushChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
{
string message;
using (System.IO.StreamReader reader = new System.IO.StreamReader(e.Notification.Body))
{
message = reader.ReadToEnd();
}
Dispatcher.BeginInvoke(() =>
MessageBox.Show(String.Format("收到 Row {0}:\n{1}",
DateTime.Now.ToShortTimeString(), message))
);
}
得到推送URI后,我們需要一個web服務端,這個服務端就是我們用來向自己的應用發送推送消息的地方,如果web端用.net實現,那么實現方式如下:
[C# .Net] //發送磁貼消息
private void SendTile()
{
try
{
string subscriptionUri = TextBoxUri.Text.ToString();
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);
httpWebRequest.Method = "POST";
string tileMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Tile>" +
"<wp:BackgroundImage>/Assets/Tiles/FlipCycleTileMedium.png</wp:BackgroundImage>" +
"<wp:Count>5</wp:Count>" +
"<wp:Title>標題</wp:Title>" +
"<wp:BackBackgroundImage></wp:BackBackgroundImage>" +
"<wp:BackTitle>背面標題</wp:BackTitle>" +
"<wp:BackContent>背面文本內容</wp:BackContent>" +
"</wp:Tile> " +
"</wp:Notification>";
byte[] notificationMessage = Encoding.Default.GetBytes(tileMessage);
httpWebRequest.ContentLength = notificationMessage.Length;
httpWebRequest.ContentType = "text/xml";
//X-WindowsPhone-Target設置為token
httpWebRequest.Headers.Add("X-WindowsPhone-Target", "token");
//Tile消息類型為 1
httpWebRequest.Headers.Add("X-NotificationClass", "1");
using (Stream requestStream = httpWebRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
string notificationStatus = response.Headers["X-NotificationStatus"];
string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
}
catch (Exception ex)
{
}
}
//發送Toast消息
private void SendToast()
{
try
{
//這個URI就是通道創建時由WP客戶端獲取到的,需要提交到服務端
string uri = TextBoxUri.Text.ToString();
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(uri);
httpWebRequest.Method = "POST";
string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>標題</wp:Text1>" +
"<wp:Text2>內容部分</wp:Text2>" +
"<wp:Param>/Page2.xaml?NavigatedFrom=ToastNotification</wp:Param>" +
"</wp:Toast> " +
"</wp:Notification>";
byte[] notificationMessage = Encoding.Default.GetBytes(toastMessage);
//設置請求頭
httpWebRequest.ContentLength = notificationMessage.Length;
httpWebRequest.ContentType = "text/xml";
//X-WindowsPhone-Target設置為toast
httpWebRequest.Headers.Add("X-WindowsPhone-Target", "toast");
//Toast消息類型為 2
httpWebRequest.Headers.Add("X-NotificationClass", "2");
using (Stream requestStream = httpWebRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
//獲取相應頭包含的狀態信息
string notificationStatus = response.Headers["X-NotificationStatus"];
string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
}
catch (Exception ex)
{
}
}
//發送Raw消息
private void SendRaw()
{
try
{
string subscriptionUri = TextBoxUri.Text.ToString();
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);
httpWebRequest.Method = "POST";
//這里的消息內容完全自定義,也可以為非xml
string rawMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<root>" +
"<Value1>a<Value1>" +
"<Value2>b<Value2>" +
"</root>";
byte[] notificationMessage = Encoding.Default.GetBytes(rawMessage);
httpWebRequest.ContentLength = notificationMessage.Length;
httpWebRequest.ContentType = "text/xml";
//沒有 X-WindowsPhone-Target 頭
//Raw消息類型為 3
httpWebRequest.Headers.Add("X-NotificationClass", "3");
using (Stream requestStream = httpWebRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
string notificationStatus = response.Headers["X-NotificationStatus"];
string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
}
catch (Exception ex)
{
}
}
出處:[ http://www.cnblogs.com/lipan/]
版權聲明:本文的版權歸作者與博客園共有。轉載時須注明原文出處以及作者,並保留原文指向型鏈接,不得更改原文內容。否則作者將保留追究其法律責任。
