如何更新及替换ViewPager中的Fragment

如何更新及替换ViewPager中的Fragment,第1张

ListView的工作原理

在了解ViewPager的工作原理之前,先回顾ListView的工作原理:

ListView只有在需要显示某些列表项时,它才会去申请可用的视图对象;如果为所有的列表项数据创建视图对象,会浪费内存;

ListView找谁去申请视图对象呢? 答案是adapter。adapter是一个控制器对象,负责从模型层获取数据,创建并填充必要的视图对象,将准备好的视图对象返回给ListView;

首先,通过调用adapter的getCount()方法,ListView询问数组列表中包含多少个对象(为避免出现数组越界的错误);紧接着ListView就调用adapter的getView(int, View, ViewGroup)方法。

ViewPager某种程度上类似于ListView,区别在于:ListView通过ArrayAdaptergetView(int position, View convertView, ViewGroup parent)填充视图;ViewPager通过FragmentPagerAdaptergetItem(int position)生成指定位置的fragment

而我们需要关注的是:

ViewPager和它的adapter是如何配合工作的?

声明:本文内容针对androidsupportv4app

ViewPager有两个adapter:FragmentPagerAdapter和FragmentStatePagerAdapter:

androidsupportv4appFragmentPagerAdapter

继承自androidsupportv4viewPagerAdapter,每页都是一个Fragment,并且所有的Fragment实例一直保存在Fragment

manager中。所以它适用于少量固定的fragment,比如一组用于分页显示的标签。除了当Fragment不可见时,它的视图层(view

hierarchy)有可能被销毁外,每页的Fragment都会被保存在内存中。(翻译自代码文件的注释部分)

androidsupportv4appFragmentStatePagerAdapter

继承自androidsupportv4viewPagerAdapter,每页都是一个Fragment,当Fragment不被需要时(比如不可见),整个Fragment都会被销毁,除了saved

state被保存外(保存下来的bundle用于恢复Fragment实例)。所以它适用于很多页的情况。(翻译自代码文件的注释部分)

它俩的子类,需要实现getItem(int) 和 androidsupportv4viewPagerAdaptergetCount()

先通过一段代码了解ViewPager和FragmentPagerAdapter的典型用法

稍后做详细分析:

// Set a PagerAdapter to supply views for this pager

ViewPager viewPager = (ViewPager) findViewById(Ridmy_viewpager_id);

viewPagersetAdapter(mMyFragmentPagerAdapter);

private FragmentPagerAdapter mMyFragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {

@Override

public int getCount() {

return 2; // Return the number of views available

}

@Override

public Fragment getItem(int position) {

return new MyFragment(); // Return the Fragment associated with a specified position

}

// Called when the host view is attempting to determine if an item's position has changed

@Override

public int getItemPosition(Object object) {

if (object instanceof MyFragment) {

((MyFragment)object)updateView();

}

return supergetItemPosition(object);

}

};

private class MyFragment extends Fragment {

@Override

public void onCreate(Bundle savedInstanceState) {

superonCreate(savedInstanceState);

// do something such as init data

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View view = inflaterinflate(Rlayoutfragment_my, container, false);

// init view in the fragment

return view;

}

public void updateView() {

// do something to update the fragment

}

}

FragmentPagerAdapter和FragmentStatePagerAdapter对Fragment的管理略有不同,在详细考察二者区别之前,我们通过两种较为直观的方式先感受下:

通过两张直观的对比FragmentPagerAdapter和FragmentStatePagerAdapter的区别

说明:这两张来自于《Android权威编程指南》,原图有3个Fragment,我增加了1个Fragment,以及被调到的方法。

FragmentPagerAdapter的Fragment管理:

FragmentStatePageAdapter的Fragment管理:

详细分析 adapter method和fragment lifecycle method 的调用情况

好啦,感受完毕,我们需要探究其详情,梳理adapter创建、销毁Fragment的过程,过程中adapter method和fragment lifecycle method哪些被调到,有哪些一样,有哪些不一样。

最开始处于第0页时,adapter不仅为第0页创建Fragment实例,还为相邻的第1页创建了Fragment实例:

// 刚开始处在page0

D/Adapter (25946): getItem(0)

D/Fragment0(25946): newInstance(2015-09-10) // 注释:newInstance()调用了Fragment的构造器方法,下同。

D/Adapter (25946): getItem(1)

D/Fragment1(25946): newInstance(Hello World, I'm li2)

D/Fragment0(25946): onAttach()

D/Fragment0(25946): onCreate()

D/Fragment0(25946): onCreateView()

D/Fragment1(25946): onAttach()

D/Fragment1(25946): onCreate()

D/Fragment1(25946): onCreateView()

第1次从第0页滑到第1页,adapter同样会为相邻的第2页创建Fragment实例;

// 第1次滑到page1

D/Adapter (25946): onPageSelected(1)

D/Adapter (25946): getItem(2)

D/Fragment2(25946): newInstance(true)

D/Fragment2(25946): onAttach()

D/Fragment2(25946): onCreate()

D/Fragment2(25946): onCreateView()

FragmentPagerAdapter和FragmentStatePagerAdapter齐声说:呐,请主公贰放心,属下定会为您准备好相邻的下一页视图哒!么么哒!

它俩对待下一页的态度是相同的,但对于上上页,它俩做出了不一样的事情:

FragmentPagerAdapter说:上上页的实例还保留着,只是销毁了它的视图:

// 第N次(N不等于1)向右滑动选中page2

D/Adapter (25946): onPageSelected(2)

D/Adapter (25946): destroyItem(0) // 销毁page0的视图

D/Fragment0(25946): onDestroyView()

D/Fragment3(25946): onCreateView() // page3的Fragment实例仍保存在FragmentManager中,所以只需创建它的视图

FragmentStatePagerAdapter说:上上页的实例和视图都被俺销毁啦:

// 第N次(N不等于1)向右滑选中page2

D/Adapter (27880): onPageSelected(2)

D/Adapter (27880): destroyItem(0) // 销毁page0的实例和视图

D/Adapter (27880): getItem(3) // 创建page3的Fragment

D/Fragment3(27880): newInstance()

D/Fragment0(27880): onDestroyView()

D/Fragment0(27880): onDestroy()

D/Fragment0(27880): onDetach()

D/Fragment3(27880): onAttach()

D/Fragment3(27880): onCreate()

D/Fragment3(27880): onCreateView()

使用 MVPArms 开发也有一段时间了,首先感谢 作者 的无私奉献和分享!在此记录一下 Fragment 使用过程中遇到的问题和解决方案。

Activity Fragment 的生命周期如下:

<div align=center>

<div align=center>

</div>

<div align=center>

</div>

MVPArms 框架对 Activity Fragment 的生命周期进行了很好的封装。

通过 ActivityDelegate 代理 Activity 的生命周期(具体实现为 ActivityDelegateImpl ),通过 FragmentDelegate 代理 Fragment 的生命周期(具体实现为 FragmentDelegateImpl );

然后在 ActivityLifecycle 实现了 ApplicationActivityLifecycleCallbacks 接口,内部类 FragmentLifecycle 实现了 FragmentManagerFragmentLifecycleCallbacks 抽象类;

并将 ActivityLifecycle 注入到 BaseApplication 中,注入过程是通过 AppDelegate 来代理 Application 的生命周期完成的。

为此作者还专门通过 一篇文章 介绍思想,收获颇多。

ApplicationActivityLifecycleCallbacks 接口定义如下:

FragmentManagerFragmentLifecycleCallbacks 抽象类定义如下:

MainActivity 有三个 Fragment,分别是 HomeFragment DashboardFragment NotificationsFragment

MainActivity 控制 Fragment 的切换,其中 HomeFragment 是主页。

使用 ARouter 控制 Fragment 的切换。

设置 MainActivity 的启动模式为 singleTask ,在 AndroidManifestxml 中为 MainActivity 添加以下属性:

MainActivity 的布局文件包含了一个 FramLayout,用来动态添加 Fragment;还包含了一个 BottomNavigationView,在 Activity 中控制 Fragment 的切换。

MainActivity 中有一个 List 用来存储 Fragment,根据每个 Fragment 在 List 中的索引切换 Fragment。此处的切换方式使用的是 hide/show 的方式,当 Fragment 需要频繁切换的时候,这样做比 replace 的方式更有效率。

每个 Fragment 的布局文件都有两个 Button,用来在一个 Fragment 切换至其他的 Fragment。

具体实现方式是由 ARouter 先跳转到 Activity,然后由 Activity 控制 Fragment 的切换。

在 Fragment 通过 ARouter 跳转到 Activity,会触发 Activity 的 onNewIntent(Intent intent) 方法回调,所以在 onNewIntent 处理ARouter 携带的要切换的目标 Fragment 的索引,然后通过 BottomNavigationView 的 OnNavigationItemSelectedListener 控制切换 Fragment,同时设置 Toolbar 的 Title。从而完成从一个 Fragment 切换至其他的 Fragment。

由于同一个 Activity 与三个 Fragment 的生命周期同步,所以当 Activity 在 onResume 状态下,三个 Fragment 也在同时 onResume,使用 hide/show 的方式切换 Fragment 无法刷新 Fragment 的状态。这时候 onHiddenChanged(boolean hidden) 方法就派上用场了,可以在 Fragment 中重写此方法来处理刷新等逻辑。

当设备旋转或者 Activity 长期处于后台而被系统回收,Activity 的会经历销毁->重建的过程。但是我们可以保存 Fragment,当 Activity 重建时继续使用已经存在的 Fragment 实例,避免浪费系统资源。

利用系统 API 提供的 Fragment#setRetainInstance(boolean retain) 方法来保存 Fragment 实例,在 GlobalConfiguration 的 FragmentLifecycleCallbacks 回调方法里设为 true。

因为 FargmentManager 在 Activity 重建时会自动恢复,所以可以在添加 Fragment 时设置 tag,然后通过 FragmentManager#findFragmentByTag(String tag) 获取 FragmentManager 中已存在的 Fragment 实例。

这里使用了 FragmentUtils 工具类处理 Fragment。

在 Activity 的 onSaveInstanceState(Bundle outState) 保存当前 Fragment 索引:

在 Activity 的 initData(Bundle savedInstanceState) 恢复原来 Fragment 索引:

在 Activity 中持有 各个 Fragment 实例,MVPArms 的 IFragment 接口提供一个 setData(Object data) 方法,可以将通信数据传递给目标 Fragment:

然后在 Fragment 中重写 setData() 方法接收消息,根据消息做一些事情:

使用 ARouter 将 通信数据携带发送给 Activity,然后在 Activity 的 onNewIntent(Intent intent) 接收处理。

Fragment 之间的通信可以通过 Fragment 先与 Activity 通信,然后由 Activity 传递给目标 Fragment。例如上面的通过按钮切换 Fragment 就是一个例子。通过 ARouter 来实现。

Activity 也可以在布局文件里直接使用 <fragment> 标签来静态加载 Fragment。

<fragment> 中的 android:name 属性指定要在布局中实例化的 Fragment 类。

当系统创建此 Activity 布局时,会实例化在布局中指定的每个片段,并为每个片段调用 onCreateView() 方法,以检索每个片段的布局。系统会直接插入片段返回的 View 来替代 <fragment> 元素。

用法如下:

欢迎 star 和 issue:

我是 xiaobailong24 ,您可以通过以下平台找到我:

ViewModel:是以感知生命周期的形式来存储和管理视图相关的数据。

ViewModel主要有以下的特点:

继承ViewMode,实现自定义ViewModel。

在Activity中使用LoginViewModel

首先查看ViewModel源码

可以看到,我们并没有手动调用 ViewModel 的构造函数来创建 ViewModel 实例,而是由 ViewModelProvider 来获取,其实ViewModel 初始化是在ViewModelProvider内部自己通过反射来构建出 ViewModel 实例。

ViewModelProvider 一共包含三个构造函数,可以看到,不管是哪种方式,最终都是要拿到两个构造参数:ViewModelStore 和 Factory,且都不能为 null。

Activity和Fragment实现了ViewModelStoreOwner和HasDefaultViewModelProviderFactory接口,这两个接口分别提供了ViewModelStore 和 Factory;

既然 Factory 实例也有了,下一步就是来调用 ViewModelProvider(this)get() 方法了。get() 方法需要我们传入 Class 对象,ViewModelProvider 需要拿到 Class 才能完成反射 *** 作。在此方法里主要是通过 modelClass 来自动生成一个字符串 Key,并将参数转发给另外一个 get() 方法。

该方法会通过 key 从 ViewModelStore 里取 ViewModel 实例,如果取不到值或者是取出来的值类型不符,则会通过 mFactorycreate(modelClass) 方法来反射初始化 ViewModel,并在返回初始化结果前将它存到 mViewModelStore 中,这样就完成了 ViewModel 的初始化流程了。

结论:ViewModel保持不变是因为ViewModelStore没有变化。

原因:Activity 每次获取 ViewModel 实例都会先尝试从 mViewModelStore 中取值,只有在取不到值的时候才会去重新构建一个新的 ViewModel 实例,且构建后的 ViewModel 实例也会被保存在mViewModelStore 中。那既然 Activity 可以在页面销毁重建的情况下获取到之前的 ViewModel 实例,那么不也就间接说明了在这种情况下 ViewModelStore 也是一直被保留着而没有被回收。

ComponentActivity 的 getViewModelStore() 方法获取 ViewModelStore 实例的来源有两种:

这里只要看第一种情况

NonConfigurationInstances 是 ComponentActivity 的一个静态内部类,其内部就包含了一个 ViewModelStore 成员变量,在 Activity 被重建时,其对应的 ViewModelStore 就被保存在了这。

通过查找引用,可以找到 ComponentActivity 就是在 onRetainNonConfigurationInstance() 方法里来完成 NonConfigurationInstancesviewModelStore 变量的赋值。从该方法名可以猜出,该方法就用于获取非配置项实例,以便在后续重建 Activity 时恢复数据。

通过查找方法引用,可以知道 onRetainNonConfigurationInstance() 又是被父类 androidappActivity 的以下方法所调用,由父类去负责保留 NonConfigurationInstances 对象。

在 ActivityThread 类的以下方法存在调用,该方法用于回调 Activity 的 onDestroy 方法,在回调前会先将数据保存到 ActivityClientRecord 的 lastNonConfigurationInstances 字段中。

在重新启动 Activity 时,又会将数据 attach 到新的 Activity 实例上,将其作为 getLastNonConfigurationInstance() 方法的返回值。通过这种数据交接,重建前的 ViewModelStore 实例就会被重建后的 Activity 拿到,当中就保留了重建前 Activity 初始化的所有 ViewModel 实例,从而保障了 ViewModel 实例的不变性。

ViewModelProvider 提供的 Factory 接口实现类有两个:

如果想要通过其它类型的构造函数来初始化 ViewModel 的话,就需要我们自己来实现 ViewModelProviderFactory 接口声明初始化逻辑了:

如果想使用同一个ViewModel类对应不同的实例对象,那么就需要在初始化的时候主动为它们指定不同的 Key,这样它们就可以一起被存到 ViewModelStore 的 HashMap 中了。

ViewModel回收是由于ViewModelStore清空 HashMap。

在 ComponentActivity 中通过监听Lifecycle状态,在Activity 在收到 ON_DESTROY 事件时,如果判断到是由于配置项更改导致了 Activity 被销毁,那么就不会调用 getViewModelStore()clear() 。如果是正常退出 Activity,那就会调用 getViewModelStore()clear() 方法,这样就会清空掉所有缓存的 ViewModel 实例了,ViewModel 的 clear() 方法也同时会被调用。

参考: 从源码看Jetpack

LiveData是一种具有生命周期感知能力的可观察数据持有类。

LiveData可以保证屏幕上的显示内容和数据一直保持同步。

在项目中,LiveData一般是存放在ViewModel中,以保证app配置变更时,数据不会丢失。

使用流程其实很简单,就是自定义实现一个Observer观察者,然后在Activity或者Fragment中获取到ViewModel,通过ViewModel获取到对应的LiveData,然后给LiveData添加观察者监听,用来监听LiveData中的数据变化,在Observer的onChanged中使用监听回调数据。

在使用LiveData的时候需要注意,LiveData有两个设置数据的方法,一个是setValue,一个是postValue,setValue只能是在主线程使用,而postValue只能在子线程中使用。

LiveData添加观察者监听,可以看到LiveData的observe方法,使用了@MainThread注释,表明该观察者监听添加的方法,只能是在主线程中使用,如果不是在主线程中使用,则会抛出异常。

在LiveData添加观察者的时候,因为LifecycleBoundObserver实际上也是实现了LifecycleEventObserver接口的,所以在LifecyclinglifecycleEventObserver对观察者对象做封装的时候,也是直接返回传入的观察者对象,不做任何的处理

LifecycleBoundObserver中封装了LifecycleOwner对象和Observer对象,并且实现了LifecycleEventObserver接口,根据Lifecycle的原理,其实我们可以知道,LifecycleRegistryaddObserver方法,添加的就是LifecycleEventObserver实现了对象。

所以在Activity使用LiveData,添加观察者,其实其内部最终还是给Activity的LifecycleRegistry添加观察者,然后根据Activity的生命周期的变化对LiveData进行通知。

postValue的通知更新,其实就是调动任务栈分发任务,而被分发执行的任务实现如下:

从这里可以看到,其实postValue在分发的任务中,其内部实现的依然是setValue()方法,只不过是从子线程切换到了主线程进行执行。做了一次线程的切换。

在postValue方法中,其内部调用的是ArchTaskExecutor的postToMainThread方法。

在这里可以看到mDelegate其实就是DefaultTaskExecutor对象

所以mDelegatepostToMainThread(runnable)其实就是调用了DefaultTaskExecutorpostToMainThread方法。

在这里可以看到,mMainHandler其实就是通过主线程的Looper实例创建的Handler对象,所以这里Handler发送消息执行任务,就是在主线程中执行该任务。

LiveData在分发消息的时候,会调用dispatchingValue方法循环分发,当消息分发完成之后,其实并不会退出do-while循环,还会在调用considerNotify方法的内部调用observeractiveStateChanged(false);继续执行第二次dispatchingValue方法,也就是说递归执行,在第二次执行的时候,mDispatchingValue = true,就会执行将mDispatchInvalidated = true,那么就会完成dispatchingValue方法的第二次执行,被直接return,那么considerNotify()方法的执行也就完成,此时就会执行considerNotify之后的if条件,因为在dispatchingValue第二次执行的时候将mDispatchInvalidated设置为了true,就直接break跳出了循环,结束了消息的分发。

但是这样的情况,一般是在存在观察者处于ON_STOP或者已经是ON_DESTROY状态的时候。

如果观察者都是处于onResume,那么这个时候会因为mDispatchInvalidated=false而退出了循环,结束分发。

但是如果是先setValue,然后再设置Observer的话。

因为此时设置Observer的时候,当生命周期发生变化的时候,又会调用回调onStateChanged方法,进而调用activeStateChanged方法

因为在添加Observer之前,已经针对该LiveData设置了一个value,此时添加了观察者,那么又因为生命周期发生了变化,那么该观察者在调用dispatchingValue(this);传入的就不是null,则在do-while循环的if判断中,就会执行if条件,进而调用considerNotify()方法给传入的ObserverWrapper实现类分发消息,那么就会把之前设置的消息分发给了该观察者。

这样的情况就是LiveData的粘性事件。即后注册的观察者接收到了之前LiveData设置的value消息。

那么问题又一次来了,什么时候会触发调用LifecycleBoundObserver的onStateChanged方法呢?

通过LiveData的observe方法进行分析,我们可以知道给LiveData添加观察者的时候,其实就是通过给实现了LifecycleOwner接口的Activity的getLifecycle()方法获取到的LifecycleRegistry对象添加观察者,而LifecycleRegistry中的addObserver方法,就会先满足while条件,然后执行了ObserverWithStatedispatchEvent方法,此时就会调用到了LifecycleBoundObserveronStateChanged方法

这里为什么会满足while条件呢?calculateTargetState会获取当前Activity生命周期状态的前一个和后一个状态,然后取更小的那个状态,在addObserver的时候,calculateTargetState这里如果activity是onStart的状态,那么calculateTargetState取出的就是CREATED状态,如果activity是onResume的状态,那么这里取出的就是STARTED,不管怎么样都会大于INITIALIZED状态,那么就会满足while条件,此时第二个activity是在onCreate生命周期调用observe方法注册Observer

其实这里个人感觉,应该是在addObserver之后,因为第二个Activity(也就是添加addObserver的)发生了生命周期变化,从onCreate变成了onStart,从onStart变成onResume,此时就会调用moveToState,然后就会调用forwardPass(),然后就会分发消息,因为之前已经postValue或者setValue了,那么在这个LiveData里的mData就不会为null,有消息了,就可以优先分发一次。

满足while条件后,就会调用statefulObserverdispatchEvent(lifecycleOwner, upEvent(statefulObservermState));,这里最终就会调用LifecycleBoundObserver的

这里shouldBeActive(),在Activity的最后的生命周期是onResume的时候,就会满足true,那么此时activeStateChanged()传入的参数就是true,而初始的时候,mActive为false

此时mActive就会重新赋值为true,那么就会调用dispatchingValue()方法,此时dispatchingValue()的参数传入this,那么就不会为false。

一般常用的粘性事件解决方案,其实就是hook修改mLastVersion的值,让这个值变成与mVersion的值一致,但是如果是在onResume或者onStart的生命周期去添加注册观察者,那么常见的粘性事件解决方案中,因为会调用superobserve(),那么就会因为在LifecycleRegistryaddObserver方法中,满足while条件,从而又会进行LifecycleBoundObserver的onStateChanged方法的回调,这样又会出现粘性事件。这样的情况的解决方案,其实可以hook修改mVersion的值,在注册观察者之前,改成-1

给你说个思路

1 按行读文件, 每一行split('\\s'),然后只要id 这个String

2 然后取id后的十六进制数

3 将此行和这一整行(value)和它对应的十六进制数(key)写入一个map

4 对十六进制树进行排序

5 get by key

6 输出

Xamarin iOS使用按钮接接收用户输入

按钮是用户交互的最基础控件。即使是在iPhone或者iPad中,用户使用最多 *** 作也是通过触摸实现点击。而点击 *** 作最多的控件往往是按钮控件。一般使用UIButton类来实现按钮。本节将主要讲解按钮相关的内容。

Xamarin iOS使用代码添加按钮

由于按钮拖放的方式比较简单,所以不再介绍。这里直接讲解代码中如何添加按钮。使用代码为主视图添加一个按钮的方式和在222节中讲解的步骤是一样的。首先需要使用UIButton类实例化一个按钮对象,然后是设置位置和大小,最后是使用AddSubview()方法将按钮对象添加到主视图中。(由于视图的添加方式都一样,后面将省略使用代码添加视图这块内容。)。

示例2-5以下将使用代码为主视图添加一个青色的按钮。代码如下:

using System;

using SystemDrawing;

using MonoTouchFoundation;

using MonoTouchUIKit;

namespace Application

{

public partial class __16ViewController : UIViewController

{

…… //这里省略了视图控制器的构造方法和析构方法

#region View lifecycle

public override void ViewDidLoad ()

{

baseViewDidLoad ();

// Perform any additional setup after loading the view, typically from a nib

UIButton button = new UIButton (); //实例化按钮对象

buttonFrame = new RectangleF (120, 261, 80, 30); //设置按钮对象的位置和大小

buttonBackgroundColor = UIColorCyan; //设置按钮对象的背景颜色

thisViewAddSubview (button); //将按钮对象添加到主视图中

}

…… //这里省略了视图加载和卸载前后的一些方法

#endregion

}

}

运行效果如图213所示。

图213 运行效果

注意:由于按钮视图继承了UIView类,所以它继承了UIView类中的属性和方法。

Xamarin iOS按钮的格式化设置

在图213中可以看到,明明是添加了一个按钮,但是就和添加了一个空白视图一样,为了让按钮和空白视图区别开,需要对按钮进行一些设置。

1设置按钮的外观

外观是直接区别按钮和其他视图的手段。如果是使用Interface Builder添加的按钮,它的外观设置方式有两种,一种是直接打开属性界面,对按钮外观的进行设置,如图214所示。

图214 按钮的设置

另一种就是使用代码对按钮的外观进行设置。这一种方式适用于使用代码添加的按钮中。表2-2列出了常用的一些外观设置属性。

表2-2 常用属性

示例2-6下面将在主视图中添加一个按钮。此按钮的标题为I am button,标题的颜色为黑色。代码如下:

using System;

using SystemDrawing;

using MonoTouchFoundation;

using MonoTouchUIKit;

namespace Application

{

public partial class __18ViewController : UIViewController

{

…… //这里省略了视图控制器的构造方法和析构方法

#region View lifecycle

public override void ViewDidLoad ()

{

baseViewDidLoad ();

// Perform any additional setup after loading the view, typically from a nib

UIButton button = new UIButton ();

buttonFrame = new RectangleF (107, 269, 120, 30);

buttonSetTitle ("I am button", UIControlStateNormal); //设置按钮的标题

buttonSetTitleColor (UIColorBlack, UIControlStateNormal); //设置按钮的标题颜色

thisViewAddSubview (button);

}

…… //这里省略了视图加载和卸载前后的一些方法

#endregion

}

}

运行效果如图215所示。

图215 运行效果

2设置按钮的状态

在示例2-6中,设置按钮的标题和颜色时,需要对按钮的状态进行设置,表示按钮在某一状态下的标题和标题颜色是什么样子。例如,UIControlStateNormal就表示按钮的一种状态。对于像按钮的这类视图,即可以接受用户输入的视图也被称为控件。这些控件都有自己的状态。表2-3就为开发者详细介绍了控件的状态。

表2-3 控件的状态

3设置按钮的类型

按钮的形式是多种多样的。例如,在通讯录中,添加新联系人的按钮是一个加号;查看来电的详细信息时是一个感叹号等。这些按钮的实现,可以在实例化按钮对象时使用UIButtonType来实现。UIButtonType中的内容如表2-4所示。

表2-4 UIButtonType的内容

示例2-7以下代码将设置两个不同风格的按钮。代码如下:

using System;

using SystemDrawing;

using MonoTouchFoundation;

using MonoTouchUIKit;

namespace Application

{

public partial class __19ViewController : UIViewController

{

…… //这里省略了视图控制器的构造方法和析构方法

#region View lifecycle

public override void ViewDidLoad ()

{

baseViewDidLoad ();

// Perform any additional setup after loading the view, typically from a nib

//实例化按钮对象并设置按钮的类型

UIButton button1 = new UIButton (UIButtonTypeDetailDisclosure);

button1Center = new PointF (160, 150); //设置按钮的中心位置

thisViewAddSubview (button1);

//实例化按钮对象并设置按钮的类型

UIButton button2 = new UIButton (UIButtonTypeContactAdd);

button2Center = new PointF (160, 350); //设置按钮的中心位置

thisViewAddSubview (button2);

}

…… //这里省略了视图加载和卸载前后的一些方法

#endregion

}

}

剂要交替喷施~次。防治蚜虫可选用%吡

由于ios系统对用户隐私的控制,第三方应用程序只能通过苹果官方接口调用系统通讯录,不能像android那样直接 *** 作通讯录数据库。

一般地,使用系统自带通讯录的方法有两种,一种是直接将整个通讯录引入到应用程序,另一种是逐条读取通讯录中的每一条联系人信息。下面我们就一一详解。

1 直接引用整个通讯录

使用的类:ABPeoplePickerNavigationController

方法:

在LocalAddressBookControllerh文件中

#import <UIKit/UIKith>

#import <AddressBook/AddressBookh>

#import <AddressBookUI/AddressBookUIh>

@interface LocalAddressBookController : UIViewController<ABPersonViewControllerDelegate,ABPeoplePickerNavigationControllerDelegate,ABNewPersonViewControllerDelegate>

{

ABPeoplePickerNavigationController picker;

ABNewPersonViewController personViewController;

}

@end

在LocalAddressBookControllerm文件中

#import "LocalAddressBookControllerh"

#import "PublicHeaderh"

@implementation LocalAddressBookController

- (id)initWithNibName:(NSString )nibNameOrNil bundle:(NSBundle )nibBundleOrNil

{

self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

if (self) {

// Custom initialization

}

return self;

}

- (void)didReceiveMemoryWarning

{

// Releases the view if it doesn't have a superview

[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use

}

#pragma mark - View lifecycle

- (void)viewDidLoad

{

[super viewDidLoad];

picker = [[ABPeoplePickerNavigationController alloc]init];

pickerviewframe = CGRectMake(0, 0, Screen_width, Screen_height-48);

pickerpeoplePickerDelegate = self;

pickerdelegate = self; //为了隐藏右上角的“取消”按钮

[picker setHidesBottomBarWhenPushed:YES];

[picker setNavigationBarHidden:NO animated:NO];//显示上方的NavigationBar和搜索框

[selfview addSubview:pickerview];

}

#pragma mark UINavigationControllerDelegate methods

//隐藏“取消”按钮

- (void)navigationController:(UINavigationController )navigationController willShowViewController:(UIViewController )viewController animated:(BOOL)animated

{

UIView custom = [[UIView alloc] initWithFrame:CGRectMake(00f,00f,00f,00f)];

UIBarButtonItem btn = [[UIBarButtonItem alloc] initWithCustomView:custom];

[viewControllernavigationItem setRightBarButtonItem:btn animated:NO];

[btn release];

[custom release];

}

#pragma mark - peoplePickerDelegate Methods

-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController )peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person

{

// [picker setNavigationBarHidden:NO animated:NO];

NSLog(@"%@", (NSString)ABRecordCopyCompositeName(person));

return YES;

}

-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController )peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier

{

return YES;

}

-(BOOL)personViewController:(ABPersonViewController )personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{

return YES;

}

//“取消”按钮的委托响应方法

-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController )peoplePicker

{

//assigning control back to the main controller

[picker dismissModalViewControllerAnimated:YES];

}

//……

@end

以上就是关于如何更新及替换ViewPager中的Fragment全部的内容,包括:如何更新及替换ViewPager中的Fragment、MVPArms 系列 -- Fragment 的正确使用、Android Jetpack架构组件(三)—ViewModel等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/10088056.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-05
下一篇2023-05-05

发表评论

登录后才能评论

评论列表(0条)

    保存