Xamarin. Android实现下拉刷新功能
PS:发现文章被其他网站或者博客抓取后发表为原创了,给图片加了个水印
下拉刷新功能在安卓和iOS中非常常见,一般实现这样的功能都是直接使用第三方的库,网上能找到很多这样的开源库。然而在Xamarin. Android中要实现一个好用的下拉刷新功能却不是很容易,在网上找了几个Xamarin.Android的下拉刷新控件,都不是很满意,所以想重新绑定一个java写的下拉刷新控件。在网上找了几个这样的开源库,通过对比发现android-pull-to-refresh实现的功能比较多,实现的效果也比较满意。
Android-Pull-To-Refresh项目地址:https://github.com/naver/android-pull-to-refresh
该库包含如下功能点:
- 支持顶部下拉刷新和顶部上拉刷新(可以同时启用这两个功能)
- Android2.3以上设备支持滚动
- 支持以下控件
- ListView
- ExpandableListView
- GridView
- WebView
- ScrollView
- HorizontalScrollView
- ViewPager
- 支持检测列表是否滚动到最末尾
- 支持ListFragment
- 支持很多自定义选项(1.自定义正在加载界面,可以修改图标和文字 2.支持多个提示图标,平滑滚动时间间隔设置等)
其他详细说明请到该项目的网站查看。
本文主要包含以下五个部分
一、Jar文件的生成
下面开始进行绑定操作,要能够进行绑定,首先需要将java项目编译为jar文件,我是通过fat-jar插件生成pulltorefresh.jar文件的,通过其他方式生成也是可以的。
只选中项目的output即可
二、PullToRefresh.dll的生成
接下来创建Android Binding项目,将生成的jar文件添加到项目中,生成类型选择embeddedjar,生成的版本选择2.3,由于该项目没有引用其他项目,所以不需要进行其他设置,项目的结构图如下:
接下来编译项目,编译时VS给出了如下的错误提示:
error CS0060: Inconsistent accessibility: base class 'Com.Handmark.Pulltorefresh.Library.PullToRefreshListView.InternalListView' is less accessible than class 'Com.Handmark.Pulltorefresh.Library.PullToRefreshListView.InternalListViewSDK9'
error CS0102: The type 'Com.Handmark.Pulltorefresh.Library.PullToRefreshBase' already contains a definition for 'Mode'
error CS0102: The type 'Com.Handmark.Pulltorefresh.Library.PullToRefreshBase' already contains a definition for 'State'
根据错误提示可以看出:
第一个错误是由于子类方法的可访问性比父类的高,双击错误提示可以看到InternalListView类是protected修饰的,而子类InternalListViewSDK9是public修饰,在c#是不允许的,那么我们可以通过metadata.xml配置类型修饰符,使他们的修饰符统一,我这里将InternalListView类的修饰符修改public,代码如下:
- <attr path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshListView.InternalListView']" name="visibility">public</attr>
第二个和第三个错误的提示为已经包含了Mode和State的定义,通过查看java的源代码发现是由以下原因引起的:
PullToRefreshBase类里面定义了两个枚举Mode和State,并且定义了getMode、getState、setMode、setState方法,java里的get和set方法会被转换为c#里的属性,生成的代码如下:
- public class PullToRefreshBase{
- public enum Mode{}
- public enum State{}
- public Mode Mode{get;set;}
- public State State{get;set;}
- }
在C#中,这样的代码是无法通过编译的,因为属性的名称和类型的名称一样,所以必须进行修改才行,我们可以将枚举的名称分别修改为PullToRefreshMode和PullToRefreshState.我们在metadata.xml里添加如下代码:
- <attr path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshBase.Mode']"
- name="managedName">
- PullToRefreshMode
- </attr>
- <attr path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshBase.State']"
- name="managedName">
- PullToRefreshState
- </attr>
再次编译,发现还是报错,一共报了10个错误:
第一个错误是由于访问修饰符不统一造成的,通过添加如下代码可以解决:
- <attr path="/api/package[@name='com.handmark.pulltorefresh.library.internal']/class[@name='RotateLoadingLayout']/method[@name='onLoadingDrawableSet' and count(parameter)=1 and parameter[1][@type='android.graphics.drawable.Drawable']]"
- name="visibility">protected</attr>
剩下的9个问题都是类似的,未实现某个接口或者抽象类的某个方法,双击其中的错误提示,发现有6个方法都是实现了,但编译的时候还是提示未实现方法。通过查看java源代码与生成的c#代码,找到了原因,java源代码里面有一个泛型类PullToRefreshBase<V entends View>,该类里面有一个抽象的泛型方法protected abstract T createRefreshableView(Context context, AttributeSet attrs);,转换为c#代码后泛型抽象方法的返回值变为了Java.Lang.Object,而实际上应该是生成一个泛型类,然后生成一个泛型抽象方法,可见转换程序还不是太完善。此处的修改方式为:将子类的对应方法的返回值修改为Java.Lang.Object,代码如下:
- <attr
- path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshScrollView']/method[@name='createRefreshableView']"
- name="managedReturn"
- >Java.Lang.Object</attr>
- <attr
- path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshExpandableListView']/method[@name='createRefreshableView']"
- name="managedReturn"
- >Java.Lang.Object</attr>
- <attr
- path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshGridView']/method[@name='createRefreshableView']"
- name="managedReturn"
- >Java.Lang.Object</attr>
- <attr
- path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshHorizontalScrollView']/method[@name='createRefreshableView']"
- name="managedReturn"
- >Java.Lang.Object</attr>
- <attr
- path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshListView']/method[@name='createRefreshableView']"
- name="managedReturn"
- >Java.Lang.Object</attr>
- <attr
- path="/api/package[@name='com.handmark.pulltorefresh.library']/class[@name='PullToRefreshWebView']/method[@name='createRefreshableView']"
- name="managedReturn"
- >Java.Lang.Object</attr>
剩下3个方法,在生成的C#代码里确实没找到,那就是没进行转换或者转换时报错了,此时我们需要查看对应的java源代码,分别找到报错的方法InternalListView.setEmptyView
InternalExpandableListView.setEmptyView
InternalGridView.setEmptyView
这3个方法里的代码都很简单,都只有一句代码
PullToRefreshListView.this.setEmptyView(emptyView);
PullToRefreshExpandableListView.this.setEmptyView(emptyView);
PullToRefreshGridView.this.setEmptyView(emptyView);
这3句代码都非常类似,都是”ClassName.this.MethodName”,在java里,只有内部类里可以这样写,该代码的作用是访问外部类的实例方法。 由于c#里面没有” ClassName.this.MethodName”的写法,所以猜想可能是这个原因导致了转换失败,那么我们就只有修改java源代码进行测试了。去掉this,换成同等效果的写法,修改方法如下,3个类的修改方法类似,这里就只写一个:
1. 在InternalListView类增加一个PullToRefreshListView类型的字段 _view
2. 在InternalListView类的构造函数添加一个PullToRefreshListView类型的参数view,并在构造函数内部给新增的字段赋值,使用view的值
3. 修改setEmptyView方法的代码,改为_view. setEmptyView(emptyView)
4. 增加一个get方法,返回刚才新增的_view字段
5. 修改引用的代码,传入对应的参数
修改完成后重新导出jar文件,替换为vs项目中的对应jar文件,然后重新编译,编译之后还是报同样的错误,这样就证明我们的猜想不正确,那么到底是什么原因导致转换失败内?我开始试了一些其他方法,都没有成功编译,最后发现InternalListView以及InternalListViewSDK9都是内部类(嵌套到PullToRefreshListView里面的,另外两个两个类也是内部类),就是这个内部类导致了错误,我们把对应的6个内部类(每个类里面2个内部类,一个InternalXXX一个InternalXXXSDK9)改为普通类,再次导出jar,再次编译,编译通过了,我们现在来测试一下绑定的库能否正常工作。
三、PullToRefresh的使用
新建一个测试项目PullToRefresh.Sample,添加相关资源及引用,并将原项目的java代码翻译为c#代码,翻译的时候有以下两个地方需要注意:
1. 实现java接口的类要继承自Java.Lang.Object,否则需要自己实现IJavaObject,而自己实现的IJavaObject很有可能无法正常工作,具体信息可以参考:http://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/working_with_jni/#Implementing_Interfaces
2. C#的内部类是无法直接访问外部类的实例成员的,所以需要对其中的内部类做调整
3. 向集合里添加数据使用mAdapter.Insert方法,直接向List集合添加界面不会显示数据
翻译后的C#代码如下:
- [Activity(Label = "PullToRefresh.Sample", MainLauncher = true, Icon = "@drawable/icon")]
- public class MainActivity : Activity, PullToRefreshBase.IOnRefreshListener, PullToRefreshBase.IOnLastItemVisibleListener
- {
- private List<string> mListItems;
- private PullToRefreshListView mPullRefreshListView;
- private ArrayAdapter<string> mAdapter;
- private string[] mStrings = { "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
- "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
- "Allgauer Emmentaler", "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
- "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
- "Allgauer Emmentaler" };
- protected override void OnCreate(Bundle bundle)
- {
- base.OnCreate(bundle);
- // Set our view from the "main" layout resource
- SetContentView(Resource.Layout.Main);
- mPullRefreshListView = FindViewById<PullToRefreshListView>(Resource.Id.pull_refresh_list);
- var actualListView = (ListView)mPullRefreshListView.RefreshableView;
- mListItems=new List<string>(mStrings);
- mAdapter=new ArrayAdapter<string>(this,Android.Resource.Layout.SimpleListItem1,mListItems);
- var soundListener = new SoundPullEventListener (this);
- soundListener.AddSoundEvent(PullToRefreshBase.PullToRefreshState.PullToRefresh, Resource.Raw.pull_event);
- soundListener.AddSoundEvent(PullToRefreshBase.PullToRefreshState.Reset, Resource.Raw.reset_sound);
- soundListener.AddSoundEvent(PullToRefreshBase.PullToRefreshState.Refreshing, Resource.Raw.refreshing_sound);
- mPullRefreshListView.SetOnPullEventListener(soundListener);
- mPullRefreshListView.SetOnRefreshListener(this);
- mPullRefreshListView.SetOnLastItemVisibleListener(this);
- actualListView.Adapter = mAdapter;
- }
- private class GetDataTask : AsyncTask<Java.Lang.Void, Java.Lang.Void, string[]>
- {
- private readonly MainActivity _mainActivity;
- public GetDataTask(MainActivity mainActivity)
- {
- _mainActivity = mainActivity;
- }
- protected override string[] RunInBackground(params Java.Lang.Void[] @params)
- {
- try
- {
- Thread.Sleep();
- }
- catch (InterruptedException)
- {
- }
- return _mainActivity.mStrings;
- }
- protected override void OnPostExecute(Object result)
- {
- _mainActivity. mAdapter.Insert("added after refresh:" + DateTime.Now.ToString("t"),);
- _mainActivity.mAdapter.NotifyDataSetChanged();
- _mainActivity.mPullRefreshListView.OnRefreshComplete();
- base.OnPostExecute(result);
- }
- }
- public void OnRefresh(PullToRefreshBase p0)
- {
- p0.GetLoadingLayoutProxy(true,true).SetLastUpdatedLabel(string.Format("上次更新:{0:t}",DateTime.Now));
- new GetDataTask(this).Execute();
- }
- public void OnLastItemVisible()
- {
- Toast.MakeText(this,"End of List", ToastLength.Short).Show();
- }
- }
代码调整完成后进行编译,发现编译的时候又报错了,报错信息里很多乱码,不过可以看到几个关键字“OnSmoothScrollFinishedListener”,所以猜想可能是“OnSmoothScrollFinishedListener”这个接口可能有问题,我们返回到java源代码查看。
在java源代码里的“PullToRefreshBase”类里搜索“OnSmoothScrollFinishedListener”,
找到接口的定义:
- static interface OnSmoothScrollFinishedListener {
- void onSmoothScrollFinished();
- }
我们发现接口是static且是默认的修饰符,我们试一试将修饰符改为public,重新导出jar,再次编译,发现能够通过了,现在我们来看看功能是否正常
运行的时候报错了,提示找不到资源,原来我们绑定java库时忘记打包资源了,我们将资源文件一起打包,然后重新编译.打包资源文件时需要注意以下问题:
- jar文件和资源文件都打包到一个zip文件中,zip压缩包的目录结构如下:
再次编译并运行,终于正常运行了。
四、MvvmCross中使用PullToRefresh
由于最近在使用Mvvmcross,所以也写一个Mvvmcross的例子,下面以ListView为例,实现一个MvxPullToRefreshListView。代码如下:
- public class MvxPullToRefreshListView:PullToRefreshListView,PullToRefreshBase.IOnRefreshListener
- {
- public MvxPullToRefreshListView(Context context, IAttributeSet attrs)
- : this(context, attrs, new MvxAdapter(context))
- {
- }
- public MvxPullToRefreshListView(Context context, IAttributeSet attrs, IMvxAdapter adapter)
- : base(context, attrs)
- {
- Mode = PullToRefreshMode.Both;
- if (adapter == null)
- return;
- var itemTemplateId = MvxAttributeHelpers.ReadListItemTemplateId(context, attrs);
- adapter.ItemTemplateId = itemTemplateId;
- var lv = (ListView) RefreshableView;
- lv.Adapter = adapter;
- base.SetOnRefreshListener(this);
- }
- public IMvxAdapter Adapter
- {
- get
- {
- var v = ((ListView)RefreshableView);
- var adapter=((HeaderViewListAdapter) v.Adapter).WrappedAdapter as IMvxAdapter;
- return adapter;
- }
- set
- {
- var existing = Adapter;
- if (existing == value)
- return;
- if (value != null && existing != null)
- {
- value.ItemsSource = existing.ItemsSource;
- value.ItemTemplateId = existing.ItemTemplateId;
- }
- var v = ((ListView)base.RefreshableView);
- v.Adapter = value;
- }
- }
- [MvxSetToNullAfterBinding]
- public IEnumerable ItemsSource
- {
- get { return Adapter.ItemsSource; }
- set { Adapter.ItemsSource = value; }
- }
- public bool IsLoading
- {
- get { return Refreshing; }
- set
- {
- if (!value)
- {
- OnRefreshComplete();
- }
- }
- }
- public int ItemTemplateId
- {
- get { return Adapter.ItemTemplateId; }
- set { Adapter.ItemTemplateId = value; }
- }
- private ICommand _itemClick;
- public new ICommand ItemClick
- {
- get { return _itemClick; }
- set { _itemClick = value; if (_itemClick != null) EnsureItemClickOverloaded(); }
- }
- private bool _itemClickOverloaded = false;
- private void EnsureItemClickOverloaded()
- {
- if (_itemClickOverloaded)
- return;
- _itemClickOverloaded = true;
- var v = ((ListView)base.RefreshableView);
- v.ItemClick += (sender, args) => ExecuteCommandOnItem(this.ItemClick, args.Position);
- }
- private ICommand _itemLongClick;
- public new ICommand ItemLongClick
- {
- get { return _itemLongClick; }
- set { _itemLongClick = value; if (_itemLongClick != null) EnsureItemLongClickOverloaded(); }
- }
- private bool _itemLongClickOverloaded = false;
- private void EnsureItemLongClickOverloaded()
- {
- if (_itemLongClickOverloaded)
- return;
- _itemLongClickOverloaded = true;
- var v = ((ListView)base.RefreshableView);
- v.ItemLongClick += (sender, args) => ExecuteCommandOnItem(this.ItemLongClick, args.Position);
- }
- protected virtual void ExecuteCommandOnItem(ICommand command, int position)
- {
- if (command == null)
- return;
- var item = Adapter.GetRawItem(position);
- if (item == null)
- return;
- if (!command.CanExecute(item))
- return;
- command.Execute(item);
- }
- public ICommand RefreshCommand { get; set; }
- #region IOnRefreshListener Members
- public void OnRefresh(PullToRefreshBase p0)
- {
- var lastUpdatedLabel = string.Format("上次更新:{0:T}", DateTime.Now);
- p0.LoadingLayoutProxy.SetLastUpdatedLabel(lastUpdatedLabel);
- if (RefreshCommand != null)
- {
- if (RefreshCommand.CanExecute(null))
- {
- RefreshCommand.Execute(null);
- }
- }
- }
- #endregion
- }
使用MvxPullToRefreshListView的关键代码如下:
FirstViewModel.cs
- /// <summary>
- /// The _refresh command
- /// </summary>
- private MvxCommand _refreshCommand;
- /// <summary>
- /// Gets the refresh command.
- /// </summary>
- /// <value>The refresh command.</value>
- public System.Windows.Input.ICommand RefreshCommand
- {
- get
- {
- _refreshCommand = _refreshCommand ?? new MvxCommand(async()=>await DoRefreshCommand());
- return _refreshCommand;
- }
- }
- /// <summary>
- /// Does the refresh command.
- /// </summary>
- /// <returns>Task.</returns>
- private async Task DoRefreshCommand()
- {
- Users = await LoadDataAsync();
- }
- /// <summary>
- /// load data as an asynchronous operation.
- /// </summary>
- /// <returns>Task{List{UserInfo}}.</returns>
- private async Task<List<UserInfo>> LoadDataAsync()
- {
- IsLoading = true;
- await Task.Delay(SleepMilliSeconds);
- var r = new Random();
- var count = r.Next(, );
- return await Task.Run(() =>
- {
- var list = new List<UserInfo>();
- for (int i = ; i < count; i++)
- {
- list.Insert(, new UserInfo
- {
- FirstName = "FirstName" + DateTime.Now.ToString("HH:mm:ss fff") + "__" + i,
- LastName = "LastName" + DateTime.Now.ToString("T") + "__" + i + r.Next(),
- });
- }
- IsLoading = false;
- return list;
- });
- }
FirstView.axml代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:local="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <pulltorefreshsample.droid.views.MvxPullToRefreshListView
- android:id="@+id/pull_refresh_list"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:cacheColorHint="#00000000"
- android:divider="#FF0C79E9"
- android:dividerHeight="1dp"
- android:fadingEdge="none"
- android:fastScrollEnabled="false"
- android:footerDividersEnabled="false"
- android:headerDividersEnabled="false"
- android:smoothScrollbar="true"
- local:MvxItemTemplate="@layout/user_item"
- local:MvxBind="ItemsSource Users;RefreshCommand RefreshCommand;IsLoading IsLoading"
- />
- </LinearLayout>
user_item.xaml的代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:local="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <TextView
- local:MvxBind="Text FirstName"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/textView1" />
- <TextView
- local:MvxBind="Text LastName"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/textView2" />
- </LinearLayout>
运行的效果和上面一样,就不上图了。
五、总结
C#要使用java的jar不容易啊,会出现各种各样的问题,感觉比直接在java里使用麻烦很多很多,需要耐心的解决这些问题,并且可能需要修改java代码,如果没有源代码,可能会比较麻烦。不过使用Xamarin的好处是,逻辑代码可以完全重用,并且编写代码的效率比直接用java要高。
Xamarin. Android实现下拉刷新功能的更多相关文章
- [转]Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能
版权声明:本文出自郭霖的博客,转载必须注明出处. 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最近项目中需要用到L ...
- Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最 近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在 ...
- android ListView上拉加载更多 下拉刷新功能实现(采用pull-to-refresh)
Android实现上拉加载更多功能以及下拉刷新功能, 采用了目前比较火的PullToRefresh,他是目前实现比较好的下拉刷新的类库. 目前他支持的控件有:ListView, ExpandableL ...
- Android StaggeredGrid 加下拉刷新功能 PullToRefresh
https://github.com/etsy/AndroidStaggeredGrid 用的github上面提供瀑布流,继承于abslistview,回收机制不错,并且提供了OnScrollLis ...
- Android 高仿微信(QQ)滑动弹出编辑、删除菜单效果,增加下拉刷新功能
不可否认,微信.QQ列表的滑动删除.编辑功能着实很经典(从IOS那边模仿过来的),然.Android这边,对列表的操作,其实大多还停留上下文菜单来实现. Android如何实现list item的滑动 ...
- Android自定义下拉刷新
网上的下拉刷新功能很多,不过基本上都是隐藏header的,而项目里面需要只隐藏部分的header,类似QQ好友动态的效果,修改了一些现有的,最后有很多问题,所以就自己自定义了一个,逻辑也很简单,首先就 ...
- Android智能下拉刷新加载框架—看这些就够了
一些值得学习的几个下拉刷新上拉加载开源库 Android智能下拉刷新框架-SmartRefreshLayout 支持所有的 View(AbsListView.RecyclerView.WebView. ...
- 利用Swiperefreshlayout实现下拉刷新功能的技术探讨
在常见的APP中通常有着下拉页面从而达到刷新页面的功能,这种看似简单的功能有着花样繁多的实现方式.而利用Swiperefreshlayout实现下拉刷新功能则是其中比较简明扼要的一种. 一般来说,在竖 ...
- 使用google自带包实现下拉刷新功能
android 实现下拉刷新有非常多开源的源代码能够用 比方 :PullToRefreshListView 使用起来也非常方便 如今还能够直接使用google libs以下的 android-sup ...
随机推荐
- [BOT] 一种android中实现“圆角矩形”的方法
内容简介 文章介绍ImageView(方法也可以应用到其它View)圆角矩形(包括圆形)的一种实现方式,四个角可以分别指定为圆角.思路是利用"Xfermode + Path"来进行 ...
- DBImport V3.7版本发布及软件稳定性(自动退出问题)解决过程分享
DBImport V3.7介绍: 1:先上图,再介绍亮点功能: 主要的升级功能为: 1:增加(Truncate Table)清表再插入功能: 清掉再插,可以保证两个库的数据一致,自己很喜欢这个功能. ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
- AngularJs之九(ending......)
今天继续angularJs,但也是最后一篇关于它的了,基础部分差不多也就这些,后续有机会再写它的提升部分. 今天要写的也是一个基础的选择列表: 一:使用ng-options,数组进行循环. <d ...
- AngularJS过滤器filter-保留小数,小数点-$filter
AngularJS 保留小数 默认是保留3位 固定的套路是 {{deom | number:4}} 意思就是保留小数点 的后四位 在渲染页面的时候 加入这儿个代码 用来精确浮点数,指定小数点 ...
- .Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整-控制反转和依赖注入的使用
再次调整项目架构是因为和群友dezhou的一次聊天,我原来的想法是项目尽量做简单点别搞太复杂了,仅使用了DbContext的注入,其他的也没有写接口耦合度很高.和dezhou聊过之后我仔细考虑了一下, ...
- angular2系列教程(八)In-memory web api、HTTP服务、依赖注入、Observable
大家好,今天我们要讲是angular2的http功能模块,这个功能模块的代码不在angular2里面,需要我们另外引入: index.html <script src="lib/htt ...
- 深入学习jQuery自定义插件
原文地址:jQuery自定义插件学习 1.定义插件的方法 对象级别的插件扩展,即为jQuery类的实例增加方法, 调用:$(选择器).函数名(参数); $(‘#id’).myPlugin(o ...
- git添加GitHub远程库
已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作 首先,登陆GitHub, ...
- 第12章 Linux系统管理
1. 进程管理 1.1 进程查看 (1)进程简介 进程是正在执行的一个程序或命令(如ls命令也是一个进程),每个进程都是一个运行的实体,都有自己的地址空间,并占用一定的系统资源. (2)进程管理的作用 ...