最近刚使用Xamarin技术开发完一个跨平台APP。怎么说,总的感觉吧,xamarin.forms 坑太多了。记得在最初研究Xamarin.forms技术时,掉进过不少坑。也是啥都不懂。各种刨资料。不得不吐槽下,国内资料真的是少的可怜。没得办法只有翻墙去找资料了。从开始研究这个技术到现在APP上线,大概花了4~5个月。
也有很多研究xamarin.forms技术的大神们发表着自己不同的意见。只能说仁者见仁智者见智。但是,不得不承认的是,跨平台技术给了我们开发移动端的一个很大的便捷。准确来说,是给C#在移动端世界里开疆扩土的一个很大便捷和机会。自从微软收购Xamarin后,这几年的动静可不小哇。今年发布VS2017的正式版,这给开发人员节省了不少的环境搭配时间。不知道大家有没有体会到使用VS2015开发APP配环境配的连砸电脑的心都有了。接着VS For Mac的出现,这是要逆天的节奏啊,之前在Mac上安装了VS玩了玩,可惜没得中文版的,自我感觉还是没得Windows版的VS好用。毕竟使用Windows版的VS用习惯了。总体来说,使用Xamarin跨平台开发还是很不错的。可以共享UI 和后台代码呀。一套代码可以在Android/iOS/WindowsPhone等不同平台运行。
今天更大家分享Forms的一个控件Image的相关使用方法。
我们从Xamrin的官方API可以了解到Image控件就是显示图片的。前面也有很多大神有做专题博客介绍,我这就不做详细介绍了。大致介绍下Image基本常用的属性:
属性 | 值 |
Aspect | 获取或设置图像的缩放模式。这是一个枚举 |
IsLoading | 获取图像的加载状态。(这是一个只读属性) |
IsOpaque | 获取或设置图像的不透明度标志。 |
Source | 获取或设置图像的源。图片源有很多种,可以来自于文件,图片流,或者URL |
·下面来介绍下如何实现圆形图片
实现圆形图片需要引用到第三方包。
第一步: 右键你的解决方案选择管理Nuget程序包
在浏览框中搜索 Xlabs.Forms 选中并添加包引用到项目中
注意 PCL 、Android、iOS 都得添加引用。
在包的下面有此发布的github地址 https://github.com/XLabs/Xamarin-Forms-Labs
点击去对应有控件包如何使用有详细说明。但是其中有个坑。下面我会提到的。
第二步:
根据git提到此包使用的文档。我们需要分别在IOS、Android、WindowsPone 上做些小修改
iOS 修改
如图要将之前的默认继承的基类修改成 XFormsApplicationDelegate
WindowsPhone 、Android 也是如此做法。但是在Android的中修改会有如下的问题:
我翻墙在网上找到了对应的解决方法。需要在Android中新建一个类XFormsAppCompatDroid ,继承FormsAppCompatActivity
/// <summary>
/// Class XFormsAppCompatDroid
/// </summary>
public class XFormsAppCompatDroid : FormsAppCompatActivity
{
/// <summary>
/// Gets or sets the destory
/// </summary>
/// <value>The destroy.</value>
public EventHandler<EventArgs> Destroy { get; set; }
/// <summary>
/// Gets or sets the pause.
/// </summary>
/// <value>The pause.</value>
public EventHandler<EventArgs> Pause { get; set; }
/// <summary>
/// Gets or sets the restart.
/// </summary>
/// <value>The restart.</value>
public EventHandler<EventArgs> Restart { get; set; }
/// <summary>
/// Gets or sets the resume.
/// </summary>
/// <value>The resume.</value>
public EventHandler<EventArgs> Resume { get; set; }
/// <summary>
/// Gets or sets the start.
/// </summary>
/// <value>The start.</value>
public EventHandler<EventArgs> Start { get; set; }
/// <summary>
/// Gets or sets the stop.
/// </summary>
/// <value>The stop event handler.</value>
public EventHandler<EventArgs> Stop { get; set; }
/// <summary>
/// Called when [destroy].
/// </summary>
protected override void OnDestroy()
{
var handler = this.Destroy;
if (handler != null)
{
handler(this, new EventArgs());
}
base.OnDestroy();
}
/// <summary>
/// Called as part of the activity lifecycle when an activity is going into
/// the background, but has not (yet) been killed.
/// </summary>
/// <since version="Added in API level 1" />
/// <altmember cref="M:Android.App.Activity.OnResume" />
/// <altmember cref="M:Android.App.Activity.OnSaveInstanceState(Android.OS.Bundle)" />
/// <altmember cref="M:Android.App.Activity.OnStop" />
/// <remarks><para tool="javadoc-to-mdoc">Called as part of the activity lifecycle when an activity is going into
/// the background, but has not (yet) been killed. The counterpart to
/// <c><see cref="M:Android.App.Activity.OnResume" /></c>.
/// </para>
/// <para tool="javadoc-to-mdoc">When activity B is launched in front of activity A, this callback will
/// be invoked on A. B will not be created until A's <c><see cref="M:Android.App.Activity.OnPause" /></c> returns,
/// so be sure to not do anything lengthy here.
/// </para>
/// <para tool="javadoc-to-mdoc">This callback is mostly used for saving any persistent state the
/// activity is editing, to present a "edit in place" model to the user and
/// making sure nothing is lost if there are not enough resources to start
/// the new activity without first killing this one. This is also a good
/// place to do things like stop animations and other things that consume a
/// noticeable amount of CPU in order to make the switch to the next activity
/// as fast as possible, or to close resources that are exclusive access
/// such as the camera.
/// </para>
/// <para tool="javadoc-to-mdoc">In situations where the system needs more memory it may kill paused
/// processes to reclaim resources. Because of this, you should be sure
/// that all of your state is saved by the time you return from
/// this function. In general <c><see cref="M:Android.App.Activity.OnSaveInstanceState(Android.OS.Bundle)" /></c> is used to save
/// per-instance state in the activity and this method is used to store
/// global persistent data (in content providers, files, etc.)
/// </para>
/// <para tool="javadoc-to-mdoc">After receiving this call you will usually receive a following call
/// to <c><see cref="M:Android.App.Activity.OnStop" /></c> (after the next activity has been resumed and
/// displayed), however in some cases there will be a direct call back to
/// <c><see cref="M:Android.App.Activity.OnResume" /></c> without going through the stopped state.
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <i>Derived classes must call through to the super class's
/// implementation of this method. If they do not, an exception will be
/// thrown.</i>
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <format type="text/html">
/// <a href="http://developer.android.com/reference/android/app/Activity.html#onPause()" target="_blank">[Android Documentation]</a>
/// </format>
/// </para></remarks>
protected override void OnPause()
{
var handler = this.Pause;
if (handler != null)
{
handler(this, new EventArgs());
}
base.OnPause();
}
/// <summary>
/// Called after <c><see cref="M:Android.App.Activity.OnStop" /></c> when the current activity is being
/// re-displayed to the user (the user has navigated back to it).
/// </summary>
/// <since version="Added in API level 1" />
/// <altmember cref="M:Android.App.Activity.OnStop" />
/// <altmember cref="M:Android.App.Activity.OnStart" />
/// <altmember cref="M:Android.App.Activity.OnResume" />
/// <remarks><para tool="javadoc-to-mdoc">Called after <c><see cref="M:Android.App.Activity.OnStop" /></c> when the current activity is being
/// re-displayed to the user (the user has navigated back to it). It will
/// be followed by <c><see cref="M:Android.App.Activity.OnStart" /></c> and then <c><see cref="M:Android.App.Activity.OnResume" /></c>.
/// </para>
/// <para tool="javadoc-to-mdoc">For activities that are using raw <c><see cref="T:Android.Database.ICursor" /></c> objects (instead of
/// creating them through
/// <c><see cref="M:Android.App.Activity.ManagedQuery(Android.Net.Uri, System.String[], System.String[], System.String[], System.String[])" /></c>,
/// this is usually the place
/// where the cursor should be required (because you had deactivated it in
/// <c><see cref="M:Android.App.Activity.OnStop" /></c>.
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <i>Derived classes must call through to the super class's
/// implementation of this method. If they do not, an exception will be
/// thrown.</i>
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <format type="text/html">
/// <a href="http://developer.android.com/reference/android/app/Activity.html#onRestart()" target="_blank">[Android Documentation]</a>
/// </format>
/// </para></remarks>
protected override void OnRestart()
{
var handler = this.Restart;
if (handler != null)
{
handler(this, new EventArgs());
}
base.OnRestart();
}
/// <summary>
/// Called after <c><see cref="M:Android.App.Activity.OnRestoreInstanceState(Android.OS.Bundle)" /></c>, <c><see cref="M:Android.App.Activity.OnRestart" /></c>, or
/// <c><see cref="M:Android.App.Activity.OnPause" /></c>, for your activity to start interacting with the user.
/// </summary>
/// <since version="Added in API level 1" />
/// <altmember cref="M:Android.App.Activity.OnRestoreInstanceState(Android.OS.Bundle)" />
/// <altmember cref="M:Android.App.Activity.OnRestart" />
/// <altmember cref="M:Android.App.Activity.OnPostResume" />
/// <altmember cref="M:Android.App.Activity.OnPause" />
/// <remarks><para tool="javadoc-to-mdoc">Called after <c><see cref="M:Android.App.Activity.OnRestoreInstanceState(Android.OS.Bundle)" /></c>, <c><see cref="M:Android.App.Activity.OnRestart" /></c>, or
/// <c><see cref="M:Android.App.Activity.OnPause" /></c>, for your activity to start interacting with the user.
/// This is a good place to begin animations, open exclusive-access devices
/// (such as the camera), etc.
/// </para>
/// <para tool="javadoc-to-mdoc">Keep in mind that onResume is not the best indicator that your activity
/// is visible to the user; a system window such as the key guard may be in
/// front. Use <c><see cref="M:Android.App.Activity.OnWindowFocusChanged(System.Boolean)" /></c> to know for certain that your
/// activity is visible to the user (for example, to resume a game).
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <i>Derived classes must call through to the super class's
/// implementation of this method. If they do not, an exception will be
/// thrown.</i>
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <format type="text/html">
/// <a href="http://developer.android.com/reference/android/app/Activity.html#onResume()" target="_blank">[Android Documentation]</a>
/// </format>
/// </para></remarks>
protected override void OnResume()
{
var handler = this.Resume;
if (handler != null)
{
handler(this, new EventArgs());
}
base.OnResume();
}
/// <summary>
/// Called after <c><see cref="M:Android.App.Activity.OnCreate(Android.OS.Bundle)" /></c> or after <c><see cref="M:Android.App.Activity.OnRestart" /></c> when
/// the activity had been stopped, but is now again being displayed to the
/// user.
/// </summary>
/// <since version="Added in API level 1" />
/// <altmember cref="M:Android.App.Activity.OnCreate(Android.OS.Bundle)" />
/// <altmember cref="M:Android.App.Activity.OnStop" />
/// <altmember cref="M:Android.App.Activity.OnResume" />
/// <remarks><para tool="javadoc-to-mdoc">Called after <c><see cref="M:Android.App.Activity.OnCreate(Android.OS.Bundle)" /></c> or after <c><see cref="M:Android.App.Activity.OnRestart" /></c> when
/// the activity had been stopped, but is now again being displayed to the
/// user. It will be followed by <c><see cref="M:Android.App.Activity.OnResume" /></c>.
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <i>Derived classes must call through to the super class's
/// implementation of this method. If they do not, an exception will be
/// thrown.</i>
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <format type="text/html">
/// <a href="http://developer.android.com/reference/android/app/Activity.html#onStart()" target="_blank">[Android Documentation]</a>
/// </format>
/// </para></remarks>
protected override void OnStart()
{
var handler = this.Start;
if (handler != null)
{
handler(this, new EventArgs());
}
base.OnStart();
}
/// <summary>
/// Called when you are no longer visible to the user.
/// </summary>
/// <since version="Added in API level 1" />
/// <altmember cref="M:Android.App.Activity.OnRestart" />
/// <altmember cref="M:Android.App.Activity.OnResume" />
/// <altmember cref="M:Android.App.Activity.OnSaveInstanceState(Android.OS.Bundle)" />
/// <altmember cref="M:Android.App.Activity.OnDestroy" />
/// <remarks><para tool="javadoc-to-mdoc">Called when you are no longer visible to the user. You will next
/// receive either <c><see cref="M:Android.App.Activity.OnRestart" /></c>, <c><see cref="M:Android.App.Activity.OnDestroy" /></c>, or nothing,
/// depending on later user activity.
/// </para>
/// <para tool="javadoc-to-mdoc">Note that this method may never be called, in low memory situations
/// where the system does not have enough memory to keep your activity's
/// process running after its <c><see cref="M:Android.App.Activity.OnPause" /></c> method is called.
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <i>Derived classes must call through to the super class's
/// implementation of this method. If they do not, an exception will be
/// thrown.</i>
/// </para>
/// <para tool="javadoc-to-mdoc">
/// <format type="text/html">
/// <a href="http://developer.android.com/reference/android/app/Activity.html#onStop()" target="_blank">[Android Documentation]</a>
/// </format>
/// </para></remarks>
protected override void OnStop()
{
var handler = this.Stop;
if (handler != null)
{
handler(this, new EventArgs());
}
base.OnStop();
}
}
/// <summary>
/// Class XFormsAppDroid.
/// </summary>
public class XFormsAppDroid : XFormsApp<XFormsAppCompatDroid>
{
/// <summary>
/// Initializes a new instance of the <see cref="XFormsAppDroid"/> class.
/// </summary>
public XFormsAppDroid() { }
/// <summary>
/// Initializes a new instance of the <see cref="XFormsAppDroid"/> class.
/// </summary>
/// <param name="app">The application.</param>
public XFormsAppDroid(XFormsAppCompatDroid app) : base(app) { }
/// <summary>
/// Raises the back press.
/// </summary>
public void RaiseBackPress()
{
this.OnBackPress();
}
/// <summary>
/// Called when [initialize].
/// </summary>
/// <param name="app">The application.</param>
/// <param name="initServices">Should initialize services.</param>
protected override void OnInit(XFormsAppCompatDroid app, bool initServices = true)
{
this.AppContext.Start += (o, e) => this.OnStartup();
this.AppContext.Stop += (o, e) => this.OnClosing();
this.AppContext.Pause += (o, e) => this.OnSuspended();
this.AppContext.Resume += (o, e) => this.OnResumed();
this.AppDataDirectory = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
if (initServices)
{
DependencyService.Register<TextToSpeechService>();
DependencyService.Register<Geolocator>();
DependencyService.Register<MediaPicker>();
DependencyService.Register<SoundService>();
DependencyService.Register<EmailService>();
DependencyService.Register<FileManager>();
DependencyService.Register<AndroidDevice>();
}
base.OnInit(app);
}
}
上面是新建的一个类。大家可以直接Copy过去用即可。然后在MainActivity中继承该类即可。
OK 引用此包的大坑问题已经解决。接下来就是使用了。
第三步:使用此第三方自定义控件
我们需要现在Xaml文件中引用
xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
就可以使用此控件进行显示设置圆形图片了。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
x:Class="ImageSize.ImageDemo">
<controls:CircleImage Source="index_1.png"
WidthRequest="300"
HeightRequest="300"
Aspect="AspectFill"/>
</ContentPage>
OK。大功告成。接下来就只用测试调试即可。
以上是我做的实现圆形图片功能的分享。