三种XML元素定义Menu,下面简单介绍一下它们:
- “
- 是菜单项,用于定义MenuItem,可以嵌套元素,以便创建子菜单。
- 是
- 元素的不可见容器(可选)。可以使用它对菜单项进行分组,使一组菜单项共享可用性和可见性等属性。
其中,- 是我们主要需要关注的元素,它的常见属性如下:
- android:id:菜单项(MenuItem)的唯一标识
- android:icon:菜单项的图标(可选)
- android:title:菜单项的标题(必选)
- android:showAsAction:指定菜单项的显示方式。常用的有ifRoom、never、always、withText,多个属性值之间可以使用|隔开。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/option_normal_1" android:icon="@mipmap/ic_vpn_key_white_24dp" android:title="普通菜单1" app:showAsAction="ifRoom"/>
<item android:id="@+id/option_normal_2" android:icon="@mipmap/ic_email_white_24dp" android:title="普通菜单2" app:showAsAction="always"/>
<item android:id="@+id/option_normal_3" android:icon="@mipmap/ic_vpn_key_white_24dp" android:title="普通菜单3" app:showAsAction="withText|always"/>
<item android:id="@+id/option_normal_4" android:title="普通菜单4" app:showAsAction="never"/> </menu>
|
菜单栏中的菜单项会分为两个部分。一部分可以直接在菜单栏中看见,我们可以称之为常驻菜单;另一部分会被集中收纳到溢出菜单中(就是菜单栏右侧的小点状图标)。一般情况下,常驻菜单项以图标形式显示(需要定义icon属性),而溢出菜单项则以文字形式显示(通过title属性定义)。showAsAction的差异如下所示:
- always:菜单项永远不会被收纳到溢出菜单中,因此在菜单项过多的情况下可能超出菜单栏的显示范围。
- ifRoom:在空间足够时,菜单项会显示在菜单栏中,否则收纳入溢出菜单中。
- withText:无论菜单项是否定义了icon属性,都只会显示它的标题,而不会显示图标。使用这种方式的菜单项默认会被收纳入溢出菜单中。
- never:菜单项永远只会出现在溢出菜单中。
java加载代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater=getMenuInflater(); inflater.inflate(R.menu.option_menu_normal,menu); return true; }
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.option_normal_1: return true; case R.id.option_normal_2: return true; case R.id.option_normal_3: return true; case R.id.option_normal_4: return true; default: return super.onOptionsItemSelected(item); } }
|
可以看见,我们在Activity中重写了onCreateOptionsMenu方法,在这个方法中完成加载Menu资源的操作,关键代码如下:
1 2 3 4
| MenuInflater inflater=getMenuInflater();
inflater.inflate(R.menu.option_menu_normal,menu);
|
需要注意的是,这个方法必须返回true,否则Menu将不会显示。
LayoutInflater是用来实例化整个布局文件,而 MenuInflater是用来实例化Menu目录下的Menu布局文件的。
传统意义上的菜单定义需要Override Activity的onCreateOptionsMenu,然后在里面调用Menu.add把Menu的一个个item加进来,比较复杂。而通过使用MenuInflater可以把Menu的构造直接放在Menu布局文件中,真现模型(Model)与视图(View)的分离,程序也看着清爽多了。
几点说明:
与LayoutInflater相比,MenuInflater的用法简单多了。首先,MenuInflater获取方法只有一种:Activity.getMenuInflater();其次,MenuInflater.inflater(int menuRes,Menu menu)(这里不代表inflater就是static方法,可以这样调用,只是为了描述方便)的返回值是void型,这就决定了MenuInflater.inflater后就没有后续操作了。这说明通过这种方式把Menu布局文件写好后就不能在程序中动态修改了,而不像LayoutInflater.inflater那样,返回值是View型,可以进行后续的进一步操作。另外,MenuInflater只有一个void inflater(int menuRes,Menu menu)非构造方法。
包含多级子菜单的选项菜单
- 是可以嵌套的,而又是
- 的容器。因此,我们可以在应用中实现具有层级结构的子菜单。下面给出一个实际的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/option_sub_file" android:title="文件" app:showAsAction="ifRoom"> <menu> <item android:id="@+id/file_new" android:title="新建"/> <item android:id="@+id/file_save" android:title="保存"/>
<item android:id="@+id/file_more" android:title="更多"> <menu> <item android:id="@+id/file_more_1" android:title="更多1"/> <item android:id="@+id/file_more_2" android:title="更多2"/>
<item android:id="@+id/file_more_more" android:title="更多更多"> <menu> <item android:id="@+id/file_more_more_1" android:title="更多更多1"/> <item android:id="@+id/file_more_more_2" android:title="更多更多2"/> </menu> </item> </menu> </item> </menu> </item> </menu>
|
intent
Intent中文意思指”意图”,按照Android的设计理念,Android使用Intent来封装程序的”调用意图”,不管启动Activity、Service、BroadcastReceiver,Android都使用统一的Intent对象来封装这一”启动意图”。此外,Intent也是应用程序组件之间通信的重要媒介。在Android中指定的了具体是某个组件,那么就是显性意图;如果只是提出要求没有指定具体的某个人,在Android中即没有指定某个具体的组件,那么就是隐式意图;所有Intent页面跳转的方式又分为显示跳转和隐式跳转。
Intent和三大组件
Android应用程序包含三种重要组件:Activity、Service、BroadcastReceiver,应用程序采用一致的方式启动它们,都是依靠Intent来进行启动的,Intent中封装了程序要启动的意图。
下面是Intent启动不同组件的部分方法:
Activity组件:
startActivity(Intent intent);startActivityForResult(Intent intent,int requestCode);
Service组件:
startService(Intent intent);bindService(Intent intent,ServiceConnection conn,int flags);
BroadcastReceiver组件:
sendBroadcast(Intent intent);sendOrderedBroadcast(Intent intent,String receiverPermission);
显式(Explicit intent)和隐式(Implicit intent)。
一、显式(设置Component(组件))
显式,即直接指定需要打开的activity对应的类。
以下多种方式都是一样的,实际上都是设置Component直接指定Activity类的显式Intent,由MainActivity跳转到SecondActivity:
1、构造方法传入Component,最常用的方式
1 2
| Intent intent = new Intent(this, SecondActivity.class); startActivity(intent);
|
2、setComponent方法
1 2 3 4 5 6 7
| ComponentName componentName = new ComponentName(this, SecondActivity.class);
Intent intent = new Intent(); intent.setComponent(componentName); startActivity(intent);
|
3、setClass/setClassName方法
1 2 3 4 5 6
| Intent intent = new Intent(); intent.setClass(this, SecondActivity.class);
startActivity(intent);
|
显式Intent通过Component可以直接设置需要调用的Activity类,可以唯一确定一个Activity,意图特别明确,所以是显式的。设置这个类的方式可以是Class对象(如SecondActivity.class),
也可以是包名加类名的字符串(如”com.example.app016.SecondActivity”)。这个很好理解,在应用程序内部跳转界面常用这种方式。
二、隐式
隐式,即不是像显式的那样直接指定需要调用的Activity,隐式不明确指定启动哪个Activity,而是设置Action、Data、Category,让系统来筛选出合适的Activity。
筛选是根据所有的来筛选。
下面以Action为例:
AndroidManifest.xml文件中,首先被调用的Activity要有一个带有并且包含的Activity,设定它能处理的Intent,并且category设为”android.intent.category.DEFAULT”。
action的name是一个字符串,可以自定义,例如我在这里设成”abcdefg”:
1 2 3 4 5 6 7
| <activity android:name="com.example.app016.SecondActivity"> <intent-filter> <action android:name="abcdefg"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
|
然后,在MainActivity,才可以通过这个action name找到上面的Activity。
下面两种方式分别通过setAction和构造方法方法设置Action,两种方式效果相同。
1、setAction方法
1 2 3
| Intent intent = new Intent(); intent.setAction("abcdefg"); startActivity(intent);
|
2、构造方法直接设置Action
1 2
| Intent intent = new Intent("abcdefg"); startActivity(intent);
|
通过设置Action字符串,表明自己的意图,即我想干嘛,需要由系统解析,找到能够处理这个Intent的Activity并启动。
比如我想打电话,则可以设置Action为”android.intent.action.DIAL”字符串,表示打电话的意图,系统会找到能处理这个意图的Activity,例如调出拨号面板。
有几点需要注意:
1、 这个Activity其他应用程序也可以调用,只要使用这个Action字符串。这样应用程序之间交互就很容易了,例如手机QQ可以调用QQ空间,可以调用腾讯微博等。
因为如此,为了防止应用程序之间互相影响,一般命名方式是包名+Action名,例如这里命名”abcdefg”就很不合理了,就应该改成”com.example.app016.MyTest”。
2、 当然,你可以在自己的程序中调用其他程序的Action。
例如可以在自己的应用程序中调用拨号面板:
1 2 3 4
| Intent intent = new Intent(Intent.ACTION_DIAL);
startActivity(intent);
|
3、一个Activity可以处理多种Action 只要你的应用程序够牛逼,一个Activity可以看网页,打电话,发短信,发邮件。。。当然可以。 Intent的Action只要是其中之一,就可以打开这个Activity。
1 2 3 4 5 6 7 8 9 10
| activity android:name="com.example.app016.SecondActivity"> <intent-filter> <action android:name="com.example.app016.SEND_EMAIL"/> <action android:name="com.example.app016.SEND_MESSAGE"/> <action android:name="com.example.app016.DAIL"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
|
对于一个Action字符串,系统有可能会找到一个Activity能处理这个Action,也有可能找到多个Activity,也可能一个都找不到。
1、找到一个Activity
很简单,直接打开这个Activity。这个不需要解释。
2、找到多个Acyivity
系统会提示从多个activity中选择一个打开。
例如我们自己开发一个拨号面板应用程序,可以设置activity的中Action name为”android.intent.action.DIAL”,这样别的程序调用拨号器时,
用户可以从Android自带的拨号器和我们自己开发的拨号器中选择。
1 2 3 4 5 6 7 8
| <activity android:name="com.example.app016.SecondActivity"> <intent-filter> <action android:name="android.intent.action.DIAL"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
|
这也就是当Android手机装上UC浏览器后,打开网页时会弹出选择Android自带浏览器还是UC浏览器,可能都会遇到过。
3、一个Activity都没找到
一个都没找到的话,程序就会出错,会抛出ActivityNotFoundException。比如随便写一个action字符串:
1 2
| Intent intent = new Intent("asasasas"); startActivity(intent);
|
所以应该注意try catch异常。
1 2 3 4 5 6 7 8 9
| Intent intent = new Intent("asasasas"); try { startActivity(intent); } catch(ActivityNotFoundException e) { Toast.makeText(this, "找不到对应的Activity", Toast.LENGTH_SHORT).show(); }
|
或者也可以使用Intent的resolveActivity方法判断这个Intent是否能找到合适的Activity,如果没有,则不再startActivity,或者可以直接禁用用户操作的控件。
1 2 3 4 5
| Intent intent = new Intent(Intent.ACTION_DIAL); if(intent.resolveActivity(getPackageManager()) == null) { }
|
注意resolveActivity方法返回值就是显式Intent上面讲到的ComponentName对象,一般情况下也就是系统找到的那个Activity。
但是如果有多个Activity可供选择的话,则返回的Component是com.android.internal.app.ResolverActivity,也就是用户选择Activity的那个界面对应的Activity,这里不再深究。
1 2 3 4 5 6 7 8
| Intent intent = new Intent(Intent.ACTION_DIAL); ComponentName componentName = intent.resolveActivity(getPackageManager()); if(componentName != null) { String className = componentName.getClassName(); Toast.makeText(this, className, Toast.LENGTH_SHORT).show(); }
|
四大属性
Intent在Android中的核心作用就是“跳转”(Android中的跳转机制),同时可以携带必要的信息,将Intent作为一个信息桥梁。最熟悉的莫过于从一个活动跳转到另一个活动,然后返回到上一个活动。不过Intent的“跳转”作用不仅于此,Intent还可以在其他地方使用,比如在碎片中跳转,接受到一个广播后自动跳转。
Intent主要有以下四个重要属性,它们分别为:
- Action:Action属性的值为一个字符串,它代表了系统中已经定义了一系列常用的动作。通过setAction()方法或在清单文件AndroidManifest.xml中设置。默认为:DEFAULT。
- Data:Data通常是URI格式定义的操作数据。例如:tel:// 。通过setData()方法设置。
- Category:Category属性用于指定当前动作(Action)被执行的环境。通过addCategory()方法或在清单文件AndroidManifest.xml中设置。默认为:CATEGORY_DEFAULT。
- Extras:Extras属性主要用于传递目标组件所需要的额外的数据。通过
putExtras()
方法设置。
Action:action表示该activity可以执行的动作。
- 自定义action,匹配Activity的功能:
a.menifest中注册的Activity
1 2 3 4 5 6
| <activity android:name=".StudyActivity"> <intent-filter> <action android:name="com.czh.study" /> </intent-filter> </activity>
|
b.启动activity
1 2
| Intent intent = new Intent("com.czh.study"); startActivity(intent)
|
当intent启动Activity的时候会拿着action到menifest中查找是否有activity的acitoin和我相同,如果匹配成功(即intent中的action和中的action相同)就跳转到该actvitiy.
(如果匹配的2个activity的话,系统会给你一个选择框让你选择启动那么activity)
- 利用系统action,调用UI
Action常用的值如下:
- ACTION_MAIN:Android Application的入口,每个Android应用必须且只能包含一个此类型的Action声明。
- ACTION_VIEW:系统根据不同的Data类型,通过已注册的对应Application显示数据。
- ACTION_EDIT:系统根据不同的Data类型,通过已注册的对应Application编辑示数据。
- ACTION_DIAL:打开系统默认的拨号程序,如果Data中设置了电话号码,则自动在拨号程序中输入此号码。
- ACTION_CALL:直接呼叫Data中所带的号码。
- ACTION_ANSWER:接听来电。
- ACTION_SEND:由用户指定发送方式进行数据发送操作。
- ACTION_SENDTO:系统根据不同的Data类型,通过已注册的对应Application进行数据发送操作。
- ACTION_BOOT_COMPLETED:Android系统在启动完毕后发出带有此Action的广播(Broadcast)。
- ACTION_TIME_CHANGED:Android系统的时间发生改变后发出带有此Action的广播(Broadcast)。
- ACTION_PACKAGE_ADDED:Android系统安装了新的Application之后发出带有此Action的广播(Broadcast)。
- ACTION_PACKAGE_CHANGED:Android系统中已存在的Application发生改变之后(如应用更新操作)发出带有此Action的广播
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
| 1 从google搜索内容 Intent intent = new Intent(); intent.setAction(Intent.ACTION_WEB_SEARCH); intent.putExtra(SearchManager.QUERY,"searchString") startActivity(intent);
2 浏览网页 Uri uri = Uri.parse("http://www.google.com"); Intent it = new Intent(Intent.ACTION_VIEW,uri); startActivity(it);
3 显示地图 Uri uri = Uri.parse("geo:38.899533,-77.036476"); Intent it = new Intent(Intent.Action_VIEW,uri); startActivity(it);
4 路径规划 Uri uri = Uri.parse("http://maps.google.com/maps?f=dsaddr=startLat%20startLng&daddr=endLat%20endLng&hl=en"); Intent it = new Intent(Intent.ACTION_VIEW,URI); startActivity(it);
5 拨打电话 Uri uri = Uri.parse("tel:xxxxxx"); Intent it = new Intent(Intent.ACTION_DIAL, uri); startActivity(it);
6 调用发短信的程序 Intent it = new Intent(Intent.ACTION_VIEW); it.putExtra("sms_body", "The SMS text"); it.setType("vnd.android-dir/mms-sms"); startActivity(it);
7 发送短信 Uri uri = Uri.parse("smsto:0800000123"); Intent it = new Intent(Intent.ACTION_SENDTO, uri); it.putExtra("sms_body", "The SMS text"); startActivity(it); String body="this is sms demo"; Intent mmsintent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("smsto", number, null)); mmsintent.putExtra(Messaging.KEY_ACTION_SENDTO_MESSAGE_BODY, body); mmsintent.putExtra(Messaging.KEY_ACTION_SENDTO_COMPOSE_MODE, true); mmsintent.putExtra(Messaging.KEY_ACTION_SENDTO_EXIT_ON_SENT, true); startActivity(mmsintent);
8 播放多媒体 Intent it = new Intent(Intent.ACTION_VIEW); Uri uri = Uri.parse("file:///sdcard/song.mp3"); it.setDataAndType(uri, "audio/mp3"); startActivity(it); Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1"); Intent it = new Intent(Intent.ACTION_VIEW, uri); startActivity(it);
9 uninstall apk Uri uri = Uri.fromParts("package", strPackageName, null); Intent it = new Intent(Intent.ACTION_DELETE, uri); startActivity(it);
10 install apk Uri installUri = Uri.fromParts("package", "xxx", null); returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);
11 打开照相机 <1>Intent i = new Intent(Intent.ACTION_CAMERA_BUTTON, null); this.sendBroadcast(i); <2>long dateTaken = System.currentTimeMillis(); String name = createName(dateTaken) + ".jpg"; fileName = folder + name; ContentValues values = new ContentValues(); values.put(Images.Media.TITLE, fileName); values.put("_data", fileName); values.put(Images.Media.PICASA_ID, fileName); values.put(Images.Media.DISPLAY_NAME, fileName); values.put(Images.Media.DESCRIPTION, fileName); values.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, fileName); Uri photoUri = getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Intent inttPhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); inttPhoto.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); startActivityForResult(inttPhoto, 10);
12 从gallery选取图片 Intent i = new Intent(); i.setType("image/*"); i.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(i, 11);
13 打开录音机 Intent mi = new Intent(Media.RECORD_SOUND_ACTION); startActivity(mi);
14 显示应用详细列表 Uri uri = Uri.parse("market://details?id=app_id"); Intent it = new Intent(Intent.ACTION_VIEW, uri); startActivity(it);
刚才找app id未果,结果发现用package name也可以 Uri uri = Uri.parse("market://details?id=<packagename>"); 这个简单多了
15 寻找应用 Uri uri = Uri.parse("market://search?q=pname:pkg_name"); Intent it = new Intent(Intent.ACTION_VIEW, uri); startActivity(it);
16 打开联系人列表 <1> Intent i = new Intent(); i.setAction(Intent.ACTION_GET_CONTENT); i.setType("vnd.android.cursor.item/phone"); startActivityForResult(i, REQUEST_TEXT);
<2> Uri uri = Uri.parse("content://contacts/people"); Intent it = new Intent(Intent.ACTION_PICK, uri); startActivityForResult(it, REQUEST_TEXT);
17 打开另一程序 Intent i = new Intent(); ComponentName cn = new ComponentName("com.yellowbook.android2", "com.yellowbook.android2.AndroidSearch"); i.setComponent(cn); i.setAction("android.intent.action.MAIN"); startActivityForResult(i, RESULT_OK);
18 调用系统编辑添加联系人(高版本SDK有效): Intent it = newIntent(Intent.ACTION_INSERT_OR_EDIT); it.setType("vnd.android.cursor.item/contact"); it.putExtra("name","myName"); it.putExtra(android.provider.Contacts.Intents.Insert.COMPANY, "organization"); it.putExtra(android.provider.Contacts.Intents.Insert.EMAIL,"email"); it.putExtra(android.provider.Contacts.Intents.Insert.PHONE,"homePhone"); it.putExtra(android.provider.Contacts.Intents.Insert.SECONDARY_PHONE, "mobilePhone"); it.putExtra( android.provider.Contacts.Intents.Insert.TERTIARY_PHONE, "workPhone"); it.putExtra(android.provider.Contacts.Intents.Insert.JOB_TITLE,"title"); startActivity(it);
19 调用系统编辑添加联系人(全有效): Intent intent = newIntent(Intent.ACTION_INSERT_OR_EDIT); intent.setType(People.CONTENT_ITEM_TYPE); intent.putExtra(Contacts.Intents.Insert.NAME, "My Name"); intent.putExtra(Contacts.Intents.Insert.PHONE, "+1234567890"); intent.putExtra(Contacts.Intents.Insert.PHONE_TYPE,Contacts.PhonesColumns.TYPE_MOBILE); intent.putExtra(Contacts.Intents.Insert.EMAIL, "com@com.com"); intent.putExtra(Contacts.Intents.Insert.EMAIL_TYPE, Contacts.ContactMethodsColumns.TYPE_WORK); startActivity(intent);
|
category
Category属性用于指定当前动作(Action)被执行的环境。
- 自定义category ,匹配Activity的功能:
如果2个activity有相同的action,那么我们可以用不同的category来区分。
a.menifest中注册的Activity
1 2 3 4 5 6 7
| <activity android:name=".StudyActivity"> <intent-filter> <action android:name="com.czh.study" /> <category android:name="com.czh.category.study" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
|
b.启动activity
1 2 3
| Intent intent = new Intent("com.czh.study"); intent.addCategory("com.czh.category.study") startActivity(intent);
|
解释:
1.这里的category起到了匹配的作用,系统先匹配action,如果action,再匹配cagegory,如果有相同就跳转。
2.有一点大家需要注意:
隐式启动中你必须要加上这一条。
但是也有例外:
这一个就不需要要。
- 利用系统category
cagegory常用的值如下:
- CATEGORY_DEFAULT:Android系统中默认的执行方式,按照普通Activity的执行方式执行。
- CATEGORY_BROWSABLE:设置该组件可以使用浏览器启动。
- CATEGORY_TAB:指定该Activity作为TabActivity的Tab页
- CATEGORY_LAUNCHER:设置该组件为在当前应用程序启动器中优先级最高的Activity,通常为入口ACTION_MAIN配合使用。
- CATEGORY_HOME:设置该组件为Home Activity。
- CATEGORY_PREFERENCE:设置该组件为Preference。
- CATEGORY_GADGET:设置该组件可以内嵌到另外的Activity中。
- CATEGORY_TEST:该Activity是一个测试
- CATEGORY_CAR_MODE:设置该Activity可在车载环境下使用
- CATEGORY_CAR_DOCK:指定手机被插入汽车底座(硬件)时运行该Activity
- CATEGORY_DESK_DOCK:指定手机被插入桌面底座(硬件)时运行该Activity
**extras属性主要用于传递目标组件所需要的额外的数据。通过putExtras()方法设置。 **
常用的系统extras
- EXTRA_BCC:存放邮件密送人地址的字符串数组。
- EXTRA_CC:存放邮件抄送人地址的字符串数组。
- EXTRA_EMAIL:存放邮件地址的字符串数组。
- EXTRA_SUBJECT:存放邮件主题字符串。
- EXTRA_TEXT:存放邮件内容。
- EXTRA_KEY_EVENT:以KeyEvent对象方式存放触发Intent的按键。
- EXTRA_PHONE_NUMBER:存放调用ACTION_CALL时的电话号码。
例如:
1 2 3 4 5
| Intent intent = new Intent("com.czh.study"); intent.putExtra("key", "value"); startActivity(intent);
String value = getIntent().getStringExtra("key");
|
data
- 什么是uri:
我们先得弄明白uri是什么,才能向下讲。
通用资源标志符(Universal Resource Identifier, 简称”URI”)。
Uri代表要操作的数据,Android上可用的每种资源 - 图像、视频片段等都可以用Uri来表示。换句话说:android系统中任何可用的资源(图像、视频、文件)都可以用uri表示。
- uri讲解:
- uri属性有以下4部分组成:android:scheme、android:host、android:port、android:path
其中host和port2个统称为authority。
- 要使authority(host和port)有意义,必须指定scheme;要使path有意义,必须使scheme和authority(host和port)有意义
举例说明:
URI为: file://com.android.jony.test:520/mnt/sdcard,我们拆分如下:
scheme–>file:
host–>com.android.jony.test
port–>520
path–>mnt/sdcard
authority–>com.android.jony.test:520
图片uri:
content://media/external/images/media/62026
tel://:号码数据格式,后跟电话号码。
mailto://:邮件数据格式,后跟邮件收件人地址。
smsto://:短息数据格式,后跟短信接收号码。
content://:内容数据格式,后跟需要读取的内容。
file://:文件数据格式,后跟文件路径。
market://search?q=pname:pkgname:市场数据格式,在Google Market里搜索包名为pkgname的应用。
geo://latitude,longitude:经纬数据格式,在地图上显示经纬度指定的位置。
在intentfilter中指定data属性的实际目的是:要求接收的Intent中的data必须符合intent-filter中指定的data属性,这样达到反向限定Intent的作用。
在AndroidManifest.xml 中进行如下设置:
1 2 3 4 5 6
| <activity android:name=".TestActivity"> <intent-filter> <action android:name="com.jony.test"/> <data android:scheme="file"/> </intent-filter> </activity>
|
启动该Activity的Intent必须进行如下设置:
1 2 3
| Intent intent = new Intent(); Uri uri = Uri.parse("file://com.android.test:520/mnt/sdcard"); intent.setData(uri)
|
data讲解:
1.data属性有以下5部分组成:android:scheme、android:host、android:port、android:path、android:mimeType
data的前四个属性构成了URI的组成部分,
mimeType设置了数据的类型
2.data元素组成的URI模型如下:
scheme://host:port/path
URI和intent-filter匹配:
Intent中URI和intent-filter进行比较的时候只会进行部分的比较:
(1)当intent-filter中只设置了scheme,只会比较URI的scheme部分;
(2)当intent-filter中只设置了scheme和authority,那么只会匹配URI中的scheme和authority;
(3)当intent-filter中设置了scheme、authority和path,那么只会匹配URI中的scheme、authority、path;(path可以使用通配符进行匹配)
(4)当intent-filter中设置了mimeType,还会进行数据类型的匹配。
intent和intentfilter
如果一个 Intent 请求在一片数据上执行一个动作, Android 如何知道哪个应用程序(和组件)能用来响应这个请求呢?
Intent Filter就是 用来注册 Activity 、 Service 和 Broadcast Receiver 具有能在某种数据上执行一个动作的能力。
使用 Intent Filter ,应用程序组件告诉 Android ,它们能为其它程序的组件的动作请求提供服务,包括同一个程序的组件、本地的或第三方的应用程序。
(1)IntentFilter的解释:
IntentFilter就是用于描述intent的各种属性, 比如action, category等
一些属性设置的例子:
<data android:mimeType=”video/mpeg” android:scheme=”http” . . . />
(2)使用场景
activity的隐式启动和广播的匹配
(3)IntentFilter的匹配规则
IntentFilter的过滤信息有action,category,data.一个组件可以包含多个intent-filter,一个intent只要能完全匹配一组intent-filter即可成功的启动对应的组件。
1.action的匹配规则
intent-filter中必须包含一个action,intent信息中也必须指定一个action。intent-filter中可以有一个或者多个action,只要intent匹配其中的一个action即可。
2.category的匹配规则
intent中可以不指定category,在receiver的intent-filter中也可以不指定category,但是当要隐式启动一个activity是就必须指定android.intent.category.DEFAULT的category。否则系统也会报找不到指定的activity.当intent中指定了一个category时,就会在具有category的intent-filter中去找匹配组件。
3.data的匹配规则
data由两部分组成mimeType和URI.mimeType指媒体类型,比如image/jpeg、audio/*等。
URI的结构为
//:/[ | |]
scheme :URI的模式,比如http、file、content等,如果URI中没有指定scheme,那么整个URI的其他参数无效。
host:URI的主机名,比如www.baidu.com,如果host么有指定那么真个URI的其他参数无效。
port:URI的端口号。
path,pathPattern,pathPrefix:这三个参数指定路径信息
data的规则是如果inent-filter中描述了data,那么必须要在intent中描述匹配的data信息,如果intent-filter中没有描述data信息,那么intent中可以不指定data信息。data的规则中如果intent-filter只指定了mimeType,那么其默认的URI的scheme是file或者content。在intent中就必须同时设置mimeType和URI。如果intent-filter中有多组data,那么intent中只需要匹配一组即可。
启动活动
Intent启动Activity
1 2
| Intent intent = new Intent("com.example.android.myapplication.ACTION_START"); intent.addCategory("com.example.android.myapplication.MY_CATEGORY"; startActivity(intent);
|
如果没有在manifest文件中添加
1
| <category android:name="com.example.android.myapplication.MY_CATEGORY"></category>
|
就会出现如下异常信息
1
| android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.example.android.myapplication.ACTION_START cat=[com.example.android.myapplication.MY_CATEGORY] }
|
如果不添加默认的
1
| <category android:name="android.intent.category.DEFAULT"></category>
|
一样会遇到上面这个Error
*Intent.ACTION_VIEW 系统内置的动作
* Uri.parse()将一个网址字符串解析成一个 Uri 对象
* setData()方法将这个 Uri 对象传递进去,指定当前 Intent正在操作的数据
* */
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
Activity使用Intent传递数据
向SecondActivity传递数据
1 2 3 4 5 6 7 8
| String data = "This data come from FirstActivity"; Intent intent = new Intent(FirstActivity.this,SecondActivity.class); intent.putExtra("howy",data); startActivity(intent);
Intent intent = getIntent(); String dataFromFirstActivity = intent.getStringExtra("howy"); Log.d(TAG, dataFromFirstActivity);
|
数据往返传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| Intent intent = new Intent(FirstActivity.this,SecondActivity.class); startActivityForResult(intent, 1);
Intent intent = new Intent(); String data = "This data from SecondActivity come to FirstActivity"; intent.putExtra("howy", data); setResult(RESULT_OK,intent); finish();
protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case 1: if (resultCode == RESULT_OK) { String dataComeFromSecondActivity = data.getStringExtra("howy"); Log.d(TAG, dataComeFromSecondActivity); } if (resultCode == RESULT_CANCELED) { String content = "Data come form SecondActivity had been CANCELED"; String dataComeFromSecondActivity = data.getStringExtra("howy"); Log.d(TAG, " " + dataComeFromSecondActivity); Log.d(TAG, content); } break; default: String content = "No data come form SecondActivity"; Log.d(TAG, content); } }
|
把startActivityForResult方法中的requestCode设置为2
就会打印No data come form SecondActivity,
在onActivityResult方法会先判断requestCode,再判断resultCode。
即使把resultCode设为RESULT_CANCELED也是可以回传数据的。
onBackPressed()方法按返回键回传数据。
1 2 3 4 5 6
| public void onBackPressed() { Intent intent = new Intent(); intent.putExtra("data_return", "Hello FirstActivity"); setResult(RESULT_OK, intent); finish(); }
|
Activity生命周期
Android 使用任务(Task)来管理活动,一个任务就是一组存放在栈里的活动的集合,后进先出。
活动状态
每个活动在其生命周期中最多可能会有四种状态。
- 运行状态
当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。
- 暂停状态
当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态,比如对话框形式的活动只会占用屏幕中间的部分区域,处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。
- 停止状态
当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
- 销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。
活动的生存期
Activity 类定义了下面的回调。你可以不用实现所有的回调方法。当了解其中的每一个非常的重要,实现这些可以保证你的应用行为如用户所期望的那样。
- onCreate()
它会在活动第一次被创建的时候调用。应该在这个方法中完成活动的初始化操作,比如说加载布局、绑定事件等。
- onStart()
这个方法在活动由不可见变为可见的时候调用。
- onResume()
这个方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。
- onPause()
这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗 CPU 的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
- onStop()
这个方法在活动完全不可见的时候调用。它和 onPause() 方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么 onPause() 方法会得到执行,而 onStop() 方法并不会执行。
- onDestroy()
这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。
- onRestart()
这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。
以上七个方法中除了onRestart()方法,其他都是两两相对的,从而又可以将活动分为三种生存期。
- 完整生存期
活动在 onCreate() 方法和 onDestroy() 方法之间所经历的,就是完整生存期。一般情况下,一个活动会在 onCreate() 方法中完成各种初始化操作,而在 onDestroy() 方法中完成释放内存的操作。
- 可见生存期
活动在 onStart() 方法和 onStop() 方法之间所经历的,就是可见生存期。在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们可以通过这两个方法,合理地管理那些对用户可见的资源。比如在 onStart() 方法中对资源进行加载,而在 onStop() 方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
- 前台生存期
活动在 onResume() 方法和 onPause() 方法之间所经历的,就是前台生存期。在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行相互的,我们平时看到和接触最多的也这个状态下的活动。
保存临时数据
当一个活动进入到了停止状态,是有可能被系统回收的。Activity 中还提供了一个 onSaveInstanceState() 回调方法,这个方法会保证一定在活动被回收之前调用,因此我们可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。
- 保存临时数据
onSaveInstanceState() 方法会携带一个Bundle 类型的参数,Bundle 提供了一系列的方法用于保存数据,键值对方式。如 putString()、putInt() 等。
1 2 3 4 5 6
| @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); String tempData = "Something you just typed"; outState.putString("data_key", tempData); }
|
- 恢复临时数据
onCreate()方法其实也有一个 Bundle 类型的参数。这个参数在一般情况下都是 null,但是当活动被系统回收之前有通过 onSaveInstanceState() 方法来保存数据的话,这个参数就会带有之前所保存的全部数据,只需要再通过相应的取值方法将数据取出即可。
1 2 3 4 5 6 7 8 9 10 11
| @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
if (savedInstanceState != null) { String tempData = savedInstanceState.getString("data_key"); Log.d(TAG, tempData); } ...... }
|
Intent 还可以结合 Bundle 一起用于传递数据的,首先可以把需要传递的数据都保存在 Bundle 对象中,然后再将 Bundle 对象存放在 Intent 里。到了目标活动之后先从 Intent 中取出 Bundle,再从Bundle中一一取出数据。
活动的四种启动模式
安卓中的活动(Activity)有四种不同的启动模式,这四种模式分别是:standard,SingleTop,SingleTask,SingleInstance。这四种模式中,standard模式是默认的模式,其他三个想要使用的话,要在AndroidMainFest中进行修改。(例如:)
standard模式:
系统默认的启动模式。
Android是使用返回栈来管理活动的,在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。
对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,而是每次启动活动都会创建该活动的一个新的实例。
standard模式原理示意图:
这是最基础的模式,在这种模式中,当你进入一个活动,这个活动就会创造一个实例,出现在返回栈的最顶层,上一个你离开的活动就会被压在下面,(注:返回栈是Android管理活动的地方,出现在界面上的活动就在最上面,再出现新的就一层层往下压。)当你无限的点击进入下一个界面,你的每一个活动都会在返回栈中往下压。如果你点击返回上一个的活动,最顶层的活动就会消失,第二层的就会上来,如果你要退出这个应用,要把返回栈中的所有活动都取消掉,才能退出。如果你用一个极端的例子,不停地从这个活动进入同一个活动,你点击了十次,就要返回十次才能退出程序,因为你在返回栈中创造了十个相同的实例,尽管活动是一样的。
SingleTop模式:
1
| android:launchMode="singleTop"
|
在SingleTop模式中, 会检查在返回栈栈顶是不是你要启动的活动,如果是的话,他就不会启动,直接使用。
所以SingleTop不会出现standard中的情况需要点击多次才能退出程序,它只需要点击一次就可以了。
singleTop模式原理示意图:
SingleTask模式:
当活动的启动模式指定为singleTask,每次启动该活动时,首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在就直接使用该实例,并把这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
1
| android:launchMode="singleTask"
|
singleTask原理示意图:
singleInstance
这种模式是最特殊的模式,这种模式是为了让不同的app之间可以共享同一个活动,如果你的app想让别的app调用你的某一个界面,就可以用这种模式,这种模式会为你想共享的界面单独创造出一个单独使用的返回栈,不会与别的返回栈共同使用。
1
| android:launchMode="singleInstance"
|
singleInstance模式示意图:
数据适配器
什么是数据适配器?
下图展示了数据源、适配器、ListView等数据展示控件之间的关系。我们知道,数据源是各种各样的,而ListView所展示数据的格式则是有一定的要求的。数据适配器正是建立了数据源与ListView之间的适配关系,将数据源转换为ListView能够显示的数据格式,从而将数据的来源与数据的显示进行解耦,降低程序的耦合性。这也体现了Android的适配器模式的使用。对于ListView、GridView等数据展示控件有多种数据适配器,本文讲解最通用的数据适配器——BaseAdapter。
ListView的显示与缓存机制
我们知道,ListView、GridView等控件可以展示大量的数据信息。假如下图中的ListView可以展示100条信息,但是屏幕的尺寸是有限的,一屏幕只能显示下图中的7条。当向上滑动ListView的时候,item1被滑出了屏幕区域,那么系统就会将item1回收到Recycler中,即View缓冲池中,而将要显示的item8则会从缓存池中取出布局文件,并重新设置好item8需要显示的数据,并放入需要显示的位置。这就是ListView的缓冲机制,总结起来就是一句话:需要时才显示,显示完就被会收到缓存。ListView,GridView等数据显示控件通过这种缓存机制可以极大的节省系统资源。
BaseAdapter
使用BaseAdapter比较简单,主要是通过继承此类来实现BaseAdapter的四个方法:
public int getCount(): 适配器中数据集的数据个数;
public Object getItem(int position): 获取数据集中与索引对应的数据项;
public long getItemId(int position): 获取指定行对应的ID;
public View getView(int position,View convertView,ViewGroup parent): 获取没一行Item的显示内容。
下面通过一个简单示例演示如何使用BaseAdapter。
1.创建布局文件
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.cbt.learnbaseadapter.MainActivity">
<ListView android:id="@+id/lv_main" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
|
item.xml (ListView中每条信息的显示布局)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_image" android:src="@mipmap/ic_launcher" android:layout_width="60dp" android:layout_height="60dp"/> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="30dp" android:layout_toEndOf="@id/iv_image" android:text="Title" android:gravity="center" android:textSize="25sp"/>
<TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toEndOf="@id/iv_image" android:layout_below="@id/tv_title" android:text="Content" android:textSize="20sp"/> </RelativeLayout>
|
2.创建数据源
ItemBean.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.cbt.learnbaseadapter;
/** * Created by caobotao on 15/12/20. */ public class ItemBean { public int itemImageResId;//图像资源ID public String itemTitle;//标题 public String itemContent;//内容
public ItemBean(int itemImageResId, String itemTitle, String itemContent) { this.itemImageResId = itemImageResId; this.itemTitle = itemTitle; this.itemContent = itemContent; } }
|
通过此Bean类,我们就将要显示的数据与ListView的布局内容一一对应了,每个Bean对象对应ListView的一条数据。这种方法在ListView中使用的非常广泛。
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.cbt.learnbaseadapter; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ListView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { ListView mListView ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<ItemBean> itemBeanList = new ArrayList<>(); for (int i = 0;i < 20; i ++){ itemBeanList.add(new ItemBean(R.mipmap.ic_launcher, "标题" + i, "内容" + i)); } mListView = (ListView) findViewById(R.id.lv_main); mListView.setAdapter(new MyAdapter(this,itemBeanList)); } }
|
3.创建BaseAdapter
通过上面的讲解,我们知道继承BaseAdapter需要重新四个方法:getCount、getItem、getItemId、getView。其中前三个都比较简单,而getView稍微比较复杂。通常重写getView有三种方式,这三种方法性能方面有很大的不同。接下来我们使用此三种方式分别实现MyAdapter。
第一种:逗比式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| package com.cbt.learnbaseadapter; import android.content.Context; import android.view.*; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView;
import java.util.List;
public class MyAdapter extends BaseAdapter{ private List<ItemBean> mList; private LayoutInflater mInflater;
public MyAdapter(Context context, List<ItemBean> list) { mList = list; mInflater = LayoutInflater.from(context); }
@Override public int getCount() { return mList.size(); }
@Override public Object getItem(int position) { return mList.get(position); }
@Override public long getItemId(int position) { return position; }
@Override public View getView(int position, View convertView, ViewGroup parent) { View view = mInflater.inflate(R.layout.item,null);
ImageView imageView = (ImageView) view.findViewById(R.id.iv_image); TextView titleTextView = (TextView) view.findViewById(R.id.tv_title); TextView contentTextView = (TextView) view.findViewById(R.id.tv_content);
ItemBean bean = mList.get(position);
imageView.setImageResource(bean.itemImageResId); titleTextView.setText(bean.itemTitle); contentTextView.setText(bean.itemContent);
return view; } }
|
为什么称这种getView的方式是逗比式呢?
通过上面讲解,我们知道ListView、GridView等数据展示控件有缓存机制,而这种方式每次调用getView时都是通过inflate创建一个新的View对象,然后在此view中通过findViewById找到对应的控件,完全没有利用到ListView的缓存机制。这种方式没有经过优化处理,对资源造成了极大的浪费,效率是很低的。
第二种:普通式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.item,null); }
ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_image); TextView titleTextView = (TextView) convertView.findViewById(R.id.tv_title); TextView contentTextView = (TextView) convertView.findViewById(R.id.tv_content);
ItemBean bean = mList.get(position);
imageView.setImageResource(bean.itemImageResId); titleTextView.setText(bean.itemTitle); contentTextView.setText(bean.itemContent); return convertView; }
|
此方式充分使用了ListView的缓存机制,如果view没有缓存才创建新的view,效率相比于逗比式提升了很多。但是,当ListView很复杂时,每次调用findViewById都会去遍历视图树,所以findViewById是很消耗时间的,我们应该尽量避免使用findViewById来达到进一步优化的目的。
第三种:文艺式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { viewHolder = new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null);
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.iv_image); viewHolder.title = (TextView) convertView.findViewById(R.id.tv_title); viewHolder.content = (TextView) convertView.findViewById(R.id.tv_content);
convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } ItemBean bean = mList.get(position);
viewHolder.imageView.setImageResource(bean.itemImageResId); viewHolder.title.setText(bean.itemTitle); viewHolder.content.setText(bean.itemContent);
return convertView; }
class ViewHolder{ public ImageView imageView; public TextView title; public TextView content; }
|
此方式不仅利用了ListView的缓存机制,而且使用ViewHolder类来实现显示数据视图的缓存,避免多次调用findViewById来寻找控件,以达到优化程序的目的。所以,大家在平时的开发中应当尽量使用这种方式进行getView的实现。
总结一下用ViewHolder优化BaseAdapter的整体步骤:
1 创建bean对象,用于封装数据;
2 在构造方法中初始化的数据List;
3 创建ViewHolder类,创建布局映射关系;
4 判断convertView,为空则创建,并设置tag,不为空则通过tag取出ViewHolder;
5 给ViewHolder的控件设置数据。