展会信息港展会大全

Android在资源中定义数组 在Java代码中获取数组
来源:互联网   发布日期:2016-01-19 12:11:12   浏览:5999次  

导读:8 1 1 概述除了在Java代码中定义数组,Android还提供了在资源中定义数组,然后在Java代码中解析资源,从而获取数组的方法。实际开发中,推荐将数据存放在资源文件中,以实现程序的逻辑代码与数据分离,便于项目的 ...

8.1.1.概述

除了在Java代码中定义数组,Android还提供了在资源中定义数组,然后在Java代码中解析资源,从而获取数组的方法。

实际开发中,推荐将数据存放在资源文件中,以实现程序的逻辑代码与数据分离,便于项目的管理,尽量减少对Java代码的修改。

8.1.2.在资源中定义数组

步骤1、在res/values文件夹下创建arrays.xml文件;

步骤2、在arrays.xml文件中创建一个数组,如下代码所示:

<?xml version="1.0" encoding="UTF-8"?>

<resources>

<string-array name="citys">

<item>北京</item>

<item>天津</item>

<item>上海</item>

<item>重庆</item>

</string-array>

</resources>

说明:

Android规定存放数组的文件必须在res/values文件夹下创建,推荐存放资源数组的文件名为arrays.xml。

以上定义了一个含有四个直辖市名称的字符串数组,数组名是citys,数组元素在<item>标签中存放。

8.1.3.Resource类

8.1.3.1.概述

Android提供了Resource类,通过该类提供的方法可以很方便地获取资源中的数据,如资源中定义的数组。

8.1.3.2.创建Resources对象

getResource();

作用:该方法是ContextWrapper类的静态方法,用于创建Resources对象。

示例:以下代码创建一个Resources对象:

Resources res=Resources.getResource();

说明:该方法必须在Context类及其子类中才能使用。

8.1.3.4.常用方法

getStringArray(int resId);

作用:获取资源索引值为resId的字符串类型的数组。

示例:以下代码将8.1.2.中创建的citys数组获取并存放在数组citys中:

Resources res=getResources();

String[] citys=res.getStringArray(R.array.citys);

提示:Resources还提供了获取int、boolean等类型的数组的方法,本章仅介绍本章代码中用到的获取字符串数组的方法。

8.2.ListView控件

8.2.1.概述

ListView是android应用程序中使用频率最高的控件。该控件使用垂直列表的方式显示多个选项,特别适合于手机这种屏幕相对较小的设备。

ListView控件的特点:每个列表项独占一行,每行的布局都相同,数据和布局分离,

数据来自适配器,ListView只负责显示,图-1是ListView的一个应用:

图-1

8.2.2.常用属性

XML属性

说明

choiceMode

设置ListView的选择方式,有三个值:

(1)none:默认方式

(1) SingleChoice:单选

(2) multipleChoice:复选

divider

设置列表项的分隔条,分隔条可以是颜色值也可以是图片

entries

指定一个字符串数组资源,用于显示在ListView中

8.2.3.常用方法

1、Object getItemAtPosition(int position)

作用:获得当前列表项

参数-position:当前列表项的索引值。

示例:

//获取索引值是position的列表项,转换为字符串

String text=(String)listView.getItemAtPosition(position);

2、void setChoiceMode(int choiceMode)

作用:设置列表的选择方式

参数-choiceMode有以下三个可选值:

进程的优先级

12.1.1.概述

Android规定:进程的优先级分为以下五个级别,如图-1所示:

图-1

1、 前台进程 -Activte process

Active (前台) process是包含(与用户交互的)控件的那种应用程序。这些是Android通过回收资源来极力保护的进程。Active process包括:

(1)处于 active 状态的Activity,它们运行在前台来响应用户的事件。

(2)Activity Service或者正在执行onReceive事件处理函数的Broadcast Receiver。

(3)正在执行onStart,onCreate,OnDestroy事件处理函数的Service。

2、 可见进程-Visible Process

可见但不活动的进程是那些拥有 可见 Activity的进程。 可见 Activity是那些在屏幕上可见,但不是在前台或不响应用户事件的 Activity。这种情况发生在当一个Activity被部分遮盖的时候(被一个非全屏或者透明的Activity)。可见进程只在极端的情况下,才会 被杀死来保证Active Process的运行。包括以下情况:

(1)可见的Activity,但处于暂停(onPause()) 状态;

(2)被可见Activity绑定的Service

3、 服务进程 Service process

进程中包含已经启动的Service。Service以动态的方式持续运行但没有可见的界面。因为Service不直接和用户交互,它们拥有比 visible Process较低的优先级。它们还是可以被认为是前台进程,不会被杀死,直到资源被active/visible Process需求。

4、 背景进程 Background process

进程中的Activity不可见和进程中没有任何启动的Service,这些进程都可以看作是后台进程。在系统中,拥有大量的后台进程,并且 Android按照后看见先杀掉的原则来杀掉后台进程以获取资源给前台进程。

5、 空进程-Empty process

为了改善整个系统的性能,Android经常在内存中保留那些已经走完生命周期的应用程序。Android维护这些缓存来改善应用程序重新启动的时间。这 些进程在资源需要的时候常常被杀掉。

当一个进程被杀掉,进程保留,变成空进程。

12.1.2.设置/取消Service为前台进程的方法

由上所述,Service排在进程的第三优先级,通常耗时的操作是放在线程中,那么将这样的线程放在Service中将会有较高的优先级,降低被 Android系统杀掉的几率。

若是将线程放在Activity中,当Activity被完全遮盖,处于onStop状态时,其进程的优先级别降为第四级。明显不如放在处于第三级别的 Service中更保险。

应用场景,如音乐播放器,通过在前台做其它操作时,音乐播放器在后台播放音乐,这种情况将播放音乐的线程放在Service中是适宜的。

Service类中有两个方法,分别用来设置Service为前台进程和取消前台进程。被设置为前台进程的Service拥有最高的优先级别,被 Android系统杀掉的几率降至最低。

1、startForeground(int id,Notification noti);

作用:设置Service对象为前台进程。

说明:

第一个参数是通知的id值。

第二个参数是通知对象。

startForegroud方法的参数与通知管理器相同,使用上也类似,都是发送一个通知,并指定该通知对象的id值。

2、stopForeGround(int id);

作用:取消(指定id值所通知的Service对象)前台进程。

12.1.3.设置Service为前台进程的步骤

步骤1、在Service类的onStartCommand方法中(通常在该方法中)创建Intent对象,并指定与其绑定的Activity,示例代码 如下:

Intent foreIntent=new Intent(this, MainActivity.class);

步骤2、创建PendingIntetn对象

PendingIntent pintent=PendingIntent.getActivity(

this, 0, foreIntent, PendingIntent.FLAG_UPDATE_CURRENT);

说明:第四个参数指明在通知栏随时刷新通知。

步骤3、创建通知对象,示例代码如下:

Notification noti=new Notification(

R.drawable.icon,"notification",System.currentTimeMillis());

说明:

第一个参数是通知栏中显示的本通知的图标。

第二个参数是通知栏中显示的本通知的标题。

第三个参数是本通知发出的时间。

步骤4、将此通知放到通知栏的(Ongoing)正在运行组中,示例代码如下:

noti.flags=Notification.FLAG_ONGOING_EVENT;

步骤5、设置通知的点击事件,示例代码如下:

noti.setLatestEventInfo(this, "title","content", pintent);

步骤6、向指定的Activity发送通知,并设置当前的Service对象为前台进程,示例代码如下:

startForeground(97789, noti);

12.1.4.示例

运行图-1所示的窗口:

图-2

1、单击图-1中的start foreground按钮,将启动一个Service对象,并设置改Service为前台进程,在该在日志窗口中出现图-2中红框内的第一行信息。

2、单击图-2中的stop foreground按钮,将取消Service的当前进程,并在日志窗口中显示图-2中红框内的第二行信息。

以下列出关键代码:

步骤1、创建项目exer12_01,包名为com.tarena.exer12_01,项目入口:MainActivity类,该中关键代码如下所示:

@Override

public void onClick(View v) {

//创建Intent对象,并设置目标组件为MyService

Intent intent=new Intent();

intent.setClass(this, MyService.class);

switch(v.getId()){

case R.id.btnStartFore:

//设置intent.action的值为Constant.ACTION_FORE

intent.setAction(Constant.ACTION_FORE);

startService(intent);//启动服务

break;

case R.id.btnStopFore:

//设置intent.action的值为Constant.ACTION_STOP_FORE

intent.setAction(Constant.ACTION_STOP_FORE);

startService(intent);

break;

case R.id.btnStopService:

stopService(intent);//停止服务

break;

}

}

步骤2、在src/com.tarena.exer12_01包下创建MyService.java该类继承自Service类。关键代码如下所示:

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

String action=intent.getAction();

if(Constant.ACTION_FORE.equals(action)){

Log.i(tag,"startForeground");

Intent foreIntent=new Intent();

foreIntent.setClass(this, MainActivity.class);

PendingIntent pintent=PendingIntent.getActivity(

this, 0, foreIntent, PendingIntent.FLAG_UPDATE_CURRENT);

Notification noti=new Notification(

R.drawable.icon,"notification",System.currentTimeMillis());

//将此通知放到通知栏的"Ongoing"即"正在运行"组中

noti.flags=Notification.FLAG_ONGOING_EVENT;

noti.setLatestEventInfo(

this, "改变Service优先级", "设置service为foreground级别", pintent);

startForeground(97789, noti);

}else if(Constant.ACTION_STOP_FORE.equals(action)){

Log.i(tag,"stopForeground");

stopForeground(true);//取消当前服务为前台服务

}

return super.onStartCommand(intent, flags, startId);

}

步骤3、打开项目清单文件,注册该服务,如下代码中红框中代码所示:

<application android:icon="@drawable/icon"

android:label="@string/app_name">

<service android:name="MyService"></service>

</application>

12.2.UI与线程

12.2.1.概述

UI是英文User Interface单词的简称。

当应用程序启动时,系统会为应用程序创建一个主线程(main)或者叫UI线程,它负责分发事件到不同的控件(例如绘画事件)以完成应用程序与 Android UI孔庙件的交互。

例如,当触摸屏幕上的一个按钮时,UI线程会把触摸事件分发到控件上,更改状态并加入事件队列,UI线程会分发请求和通知到各个控件,完成相应的动作。

单线程模型的性能是非常差的,除非应用程序相当简单,特别是当所有的操作都在主线程中执行,比如访问网络或数据库之类的耗时操作将会导致用户界面锁定,所 有的事件将不能分发,应用程序就像死了一样,更严重的是当超过5秒时,系统就会弹出 应用程序无响应 的对话框。

12.2.2.main线程

主线程也叫UI线程,主线程负责UI的创建,UI的刷新以及处理用户的输入事件。

提示:Android规定,Activity中的控件的刷新由主线程负责,其它线程不能直接刷新。

12.2.3.ANR术语

ANR的全称:Activity or Application is not responding,当用户操作超过系统规定的响应时间时,会弹出ANR对话框,如图-3所示:

图-3

若选择Force close按钮将强制关闭当前的Activity;

若选择Wait按钮将保留当前的Activity继续等待。

出现ANR的条件:

1. 在main线程(或称主线程)中有一个耗时操作正在执行,此时用户输入事件并且这个事件在5秒内没有得到响应,就会弹出ANR。

2. 广播接收者的onReceive()方法在10秒内没有执行完成,也会弹出ANR。

提示:在广播接收者的onReceive方法中要避免执行耗时的操作。

12.2.4.示例-测试ANR发生的两种情况

创建项目exer12_02,在该类中创建MyReceiver类,该类是BroadcastRecevier的子类。

图-4

1、单击download按钮,在主线程中执行一个循环模拟从网络下载数据的操作,该循环耗时10秒,在该循环执行过程中,在图-4的标注所指的编辑框中 连续试图输入字符串,5秒后,将弹出图-3所示的ANR对话框。

2、单击send broadcast按钮,发送广播,然后在图-4的标注所指的编辑框内连续试图输入字符串,10秒后将弹出ANR对话框。

以下是关键代码:

步骤1、创建工具类-CommonUtils.java,该类中定义了一个模拟耗时操作的循环,代码如下所示:

public final class CommonUtils {

public static final String ACTION_RECEIVER="com.tarena.ACTION_RECEIVER";

public static void timeConsuming(int n){

for(int i=0;i<n;i++){

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

步骤2、创建MyReceiver.java类,该类继承自BroadcastReceiver类,代码如下所示:

public class MyReceiver extends BroadcastReceiver {

private static final String tag="MyReceiver";

@Override

public void onReceive(Context context, Intent intent) {

Log.i(tag,"onReceiver");

CommonUtils.timeConsuming(15);

}

}

步骤3、以下是MainActivity.java类的代码,该代码负责发送广播,模拟下载的耗时操作,代码如下所示:

public class MainActivity extends Activity implements OnClickListener{

TextView mTextView;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mTextView=(TextView)findViewById(R.id.tv);

//实例化按钮对象

Button btnDownload=(Button)findViewById(R.id.btnDownload);

Button btnSendBraodcast=(Button)findViewById(R.id.btnSendBroadcast);

//注册按钮对象的单击事件

btnDownload.setOnClickListener(this);

btnSendBraodcast.setOnClickListener(this);

}

//实现按钮的单击事件

@Override

public void onClick(View v) {

switch(v.getId()){

case R.id.btnDownload://下载按钮

CommonUtils.timeConsuming(10);//模拟(耗时10秒的)下载

mTextView.setText("finished");

break;

case R.id.btnSendBroadcast://发送广播按钮

//发送广播

Intent intent=new Intent(CommonUtils.ACTION_RECEIVER);

sendBroadcast(intent);

mTextView.setText("finished");

break;

}

}

}

12.3.Message对象

12.3.1.概述

Message类用于存放消息,该类通常与Handler类配合使用。

12.3.2.常用属性

1、int arg1:存放一个int类型的数据。

2、int arg2:存放一个int类型的数据。

3、int what:存放一个int类型的数据。

4、Object obj:存放任意类型的对象。

12.3.3.示例代码

Message msg=new Message();

msg.what=1;

msg.arg1=100;

msg.obj= hello ;

msg.obj=new Runnable();

12.4.用Handler更新UI

12.4.1.概述

由以上所述,在主线程中不宜执行耗时操作,因此,通常的耗时操作都放在其它线程中,Androidghi称这样的线程为work thread(工作线程)。但Android还规定:只有主线程才能修改Activity中的控件,其它线程不能修改。

解决以上问题有多种方法,本节介绍如何通过Handler类提供的方法解决工作线程不能直接修改UI的问题。

Handler修改主线程UI的思路:Handler对象通过在工作线程中发送消息,该消息发送至消息对列中,等待处理。

在主线程中从消息对列中接收消息,根据消息中的信息决定如何更新主线程中UI.

12.4.2.常用方法

1、sendEmptyMessage(int what);

作用:从work thead(工作线程)向主线程发送一个空消息。

说明:若多个线程向主线程发送消息,则参数what用于区别不同的线程。

2、sendEmptyMessageAtTime(int what,long uptime);

作用:从work thead(工作线程)按指定时间发送空消息。

说明:第二个参数uptime:指定的时间。

3、sendEmptyMessageDelayed(int what,long delay);

作用:从work thead(工作线程)延迟发送空消息。

说明:第二个参数用于指定延迟的时间,单位:毫秒。

5、sendMessage(Message msg);

作用:从work thead(工作线程)向主线程发送消息;

说明:msg是存放消息数据的对象。

6、sendMessageAtTime(Message msg,long uptime);

作用:从work thead(工作线程)按指定时间向主线程发送消息

7、sendMessageDelayed(Message msg,long delay);

作用:从work thead(工作线程)延迟指定时间向主线程发送消息。

8、handleMessage(Message msg);

作用:接收并处理从work thread发送的消息。

说明:参数-msg:send***Message发送过来的消息对象。

图-5是Android处理消息机制的示意图:

图-5

图-5显示了Android系统提供了一个称为Looper(循环对列)用来管理消息对列,各线程通过Handler类的Send***Message命 令将消息发送至消息对列,Looper再将消息对列中的消息依次交给主线程处理。

12.4.3.示例

从两个工作线程向主线程发送不同的消息,主线程接收消息并显示不同的处理信息。

步骤1、以下是Activity类中的onClick()方法中的代码,该方法用于处理按钮单击事件。

@Override

public void onClick(View v) {

switch(v.getId()){

case R.id.btnDownload://若单击了标题为下载的按钮

//创建一个work thread(工作线程)线程对象

new Thread(){

public void run() {

//以下一行代码模拟下载进度,执行时间约为10秒

CommonUtils.timeConsuming(10);

//创建消息对象

Message msg=new Message();

//CommonUtils.FLAG_DOWNLOAD值代表下载操作

msg.what=CommonUtils.FLAG_DOWNLOAD;

msg.obj="download finished";

mHandler.sendMessage(msg);//发送消息

};

}.start();//启动线程

break;

case R.id.btnUpdate://若单击了标题为更新的按钮

//创建一个work thread(工作线程对象)

new Thread(){

public void run() {

//以下一行代码模拟更新进度,执行时间约为10秒

CommonUtils.timeConsuming(8);

//创建消息对象

Message msg=new Message();

//CommonUtils.FLAG_UPDATE代表更新操作

msg.what=CommonUtils.FLAG_UPDATE;

msg.obj="update finished";

mHandler.sendMessage(msg);//发送消息

};

}.start();//启动线程

break;

}

}

步骤2、以下是在Activity.onCreate()方法中(主线程)中创建的Handler对象-mHandler:

Button btnDownload.setOnClickListener(this);

Button btnUpdate.setOnClickListener(this);

mHandler=new Handler(){

@Override

public void handleMessage(Message msg) {

switch(msg.what){

case CommonUtils.FLAG_DOWNLOAD:

mTextView.setText( 下载结束 );

break;

case CommonUtils.FLAG_UPDATE:

mTextView.setText( 更新结束 );

break;

}

}

说明:

1、CommonUtils是一个自定义的工具类,该类中包括以下两个int类型的常量:

public static final int FLAG_DOWNLOAD=1;

public static final int FLAG_UPDATE=2;

2、msg是work thread线程发送过来的消息对象,该线程代码在步骤1中列出。

3、若msg.what的值是CommonUtils.FLAG_DOWNLOAD,则在标签中显示:下载结束。

若msg.what的值是CommonUtils.FLAG_UPDATE ,则在标签中显示:更新结束。

12.4. 发送Runnalbe对象-更新UI

12.4.1.概述

1、Runnable接口

该接口的源代码如下所示:

public interface Runnable {

public void run();

}

Java规定:一个线程要实现Runnable中的run方法。Android的线程也同样如此。在new 一个Thread时,查看Android源代码,将发现内部代码也是要创建一个Runnable的对象,并实现run方法。

2、Handler.post()方法

Handler类中有一个方法:post(Runnable r);

作用:将Runnable对象作为参数发送至消息对列中,以下是post方法的源代码:

public final boolean post(Runnable r)

{

return sendMessageDelayed(getPostMessage(r), 0);

}

由上代码可知,Runnable对象r被当作消息的第一个参数发送至消息队列,然后由Looper交给主线程处理,如图-5所示。

根据以上原理,Runnable.run中的代码可以在主线程中运行,那么在Runnable.run方法中可以编写更新主线程UI的代码。

12.4.2.示例

在按钮单击事件方法中创建一个工作线程,在该线程中先模拟下载操作,操作结束后,向主线程发送了一个实现Runnable的内部匿名类,代码如下所示:

public void onClick(View v) {

switch(v.getId()){

case R.id.btnDownload:

//创建Handler对象

final Handler handler=new Handler();

//new一个工作线程模拟下载操作

new Thread(){

public void run() {

CommonUtils.timeConsuming(5);//模拟下载操作

/*下载结束后,

*将Runable的内部匿名类的代码

*当作消息中的第一个参数发送至消息对列,

* 再由Looper交给主线程执行*/

handler.post(new Runnable() {

@Override

public void run() {

mTextView.setText("download finished");

}

});

};

}.start();//启动工作线程

break;

case R.id.btnUpdate:

break;

}

}

12.5.runOnUiThread()发送Runnable对象

12.5.1.概述

Activity类中提供了一个runOnUiThread方法,该方法封装了Handler.post方法,因此作用与Handler.post相同。

12.5.2.示例

用runOnUiThread方法发送Runnable对象,让主线程执行该对象中的run方法。示例代码如下:

//new一个工作线程

new Thread(){

public void run() {

CommonUtils.timeConsuming(8); //模拟更新操作

//创建一个Runnable接口的实例

Runnable action=new Runnable() {

//实现run方法,在该方法中可修改UI

@Override

public void run() {

mTextView.setText("update finished");

}

};

//将action对象发送至消息对列,交由主线程执行run方法中的代码

runOnUiThread(action);

};

}.start();//启动线程

12.6. View.post()发送Runnable对象-更新UI

12.6.1.概述

View类中也提供了发送Runnable对象至消息对列,然后由Looper交给主线程的方法:

1、Post(Runnable r);

2、postDelayed(Runnable r,long delayMillis);

作用:延迟指定时间,再将r对象发送至主线程执行。

12.6.2.示例

@Override

public void onClick(final View v) {

switch (v.getId()) {

case R.id.download:

new Thread() {

public void run() {

CommonUtils.timeConsuming(1);

Runnable action = new Runnable() {

@Override

public void run() {

mTextView.setText("下载完成");

}

};

//v-当前的按钮对象,延迟1秒发送action对象由主线程执行

v.postDelayed(action, 1000);

}

}.start();

break;

case R.id.update:

new Thread() {

public void run() {

CommonUtils.timeConsuming(8);

Runnable action = new Runnable() {

@Override

public void run() {

mTextView.setText("更新新闻完成");

}

};

/v-当前的按钮对象,延迟3秒发送action对象由主线程执行

v.postDelayed(action, 3000);

}

}.start();

break;

12.7.post总结

12.7.1.概述

Android提供了post方法,将Runnable对象(当作消息中的第一参数)发送至消息对列,然后由Looper将消息对列中的消息交给主线程执 行。如此,就可以在Runnable.run中编写修改UI的代码。但要注意:不要在run方法中编写耗时操作的代码。耗时的代码要放在工作线程的run 方法中运行。以下列出已介绍过的post的方法

1、runOnUiThread(Runnable):该方法对handler.post进行了封装。

2、View.post(Runnable):运行被选择的控件对象发送Runnable对象。

3、Handler.post(Runnable)

4、View.postDelayed(Runnable, long):延迟发送

5、Handler.postDelayed(Runnable, long):延迟发送

12.7.2.Handler.post与runOnUiThread的比较

1. Handler可以实现线程间消息通讯。

2. 在代码的可读性和性能(创建对象的个数)方面,Handler都更有优势。

12.8. AsyncTask更新UI

12.8.1.概述

前面我们采用的都是new一个线程对象,采用这种匿名线程的方式存在以下缺陷:

第一,线程的开销较大,如果每个任务都要创建一个线程,那么应用程序的效率要低很多;

第二,线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。 另外,在工作线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。

为了解决这一问题,引入了AsyncTask。AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行, 这就有效地避免了使用Handler带来的麻烦。阅读AsyncTask的源码可知,AsyncTask是使用java.util.concurrent 框架来管理线程以及任务的执行的,concurrent框架是一个非常成熟,高效的框架,经过了严格的测试。这说明AsyncTask的设计很好的解决了 匿名线程存在的问题。

AsyncTask是抽象类,子类必须实现抽象方法doInBackground(Params... p) ,在此方法中实现任务的执行工作,比如连接网络获取数据等。通常还应该实现onPostExecute(Result r)方法,因为应用程序关心的结果在此方法中返回。

提示:AsyncTask一定要在主线程中创建实例。

AsyncTask定义了三种泛型类型 Params,Progress和Result。

(1)Params 启动任务执行的输入参数,比如HTTP请求的URL。

(2)Progress 后台任务执行的百分比。

(3)Result 后台执行任务最终返回的结果,比如String。

12.8.2.常用方法

AsyncTask 的执行分为四个步骤,每一步都对应一个回调方法,需要注意的是这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。在任务的执行过程中,这些 方法被自动调用。

1、 onPreExecute();

作用:当任务执行之前开始调用此方法,可以在这里显示进度对话框。

2、doInBackground(Params...);

作用:在后台线程执行,执行耗时操作。在执行过程中可以调用publicProgress(Progress...)来更新任务的进度。

提示:publicProgress()相当于Handler.sendmessage()方法

3、onProgressUpdate(Progress...);

作用:此方法在主线程执行,用于显示任务执行的进度。

4、onPostExecute(Result);

作用:此方法在主线程执行,任务执行的结果作为此方法的参数返回。

12.8.3.示例

用AsyncTask实现图-6效果,当单击dwonload按钮后,在进度条中显示模拟的下载进度,并在标签中显示进度的百分比。下载结束后显示 download finised。

图-6a 图-6b

public class MainActivity extends Activity implements OnClickListener{

Handler mHandler;

ProgressBar mProgressBar;//声明进度条

TextView mTextView;//标签:显示进度的百分比和操作的结果

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

//实例化各控件

mProgressBar=(ProgressBar)findViewById(R.id.progressBar);

mTextView=(TextView)findViewById(R.id.tvMessage);

Button btnDownload=(Button)findViewById(R.id.btnDownload);

Button btnUpdate=(Button)findViewById(R.id.btnUpdate);

//注册按钮的单击事件

btnDownload.setOnClickListener(this);

btnUpdate.setOnClickListener(this);

}

//实现按钮的单击时间事件

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btnDownload:

//创建对象

MyAsyncTask myAsyncTask=new MyAsyncTask();

myAsyncTask.execute(null);//执行任务

break;

case R.id.btnUpdate:

break;

}

}

private class MyAsyncTask extends AsyncTask<URL, Integer, String>{

//在UI中执行,更新UI

@Override

protected void onProgressUpdate(Integer... values) {

mProgressBar.setProgress(values[0]);

if(values[0]<100){

mTextView.setText("progress="+values[0]+"%");

}

}

//现在work thread中执行耗时操作

@Override

protected String doInBackground(URL... params) {

for (int i = 0; i < 100; i++) {

publishProgress(i+1);//向onProgressUpdate发送消息

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

return "download finished";

}

//doInBackground结束后,执行本方法,result是doInBackground方法返回的数据

@Override

protected void onPostExecute(String result) {

mTextView.setText(result);

}

}

}

12.9. 软件开发术语

12.9.1性能

临时对象越多,垃圾回收(GC)的频率越高

GC占用CPU,CPU被占用时,无法响应用户的操作

用户感觉到卡,影响用户体验。

12.9.2资源池

存放一定数量的同样类型的对象,当程序需要使用时,可以从资源池中获取,使用完成,收回资源池。

等待下一次被使用。

示例:从资源池中获取Message对象。

Message msg=Message.obtainMessage();

提示:若之前没有创建过Message,则创建给对象。Android系统会在适当时候回收该对象,方便下次取用。

提示:解决性能问题的前提是不能影响功能。

ListView. CHOICE_MODE_NONE:默认选择方式,如图-2所示。

ListView. CHOICE_MODE_SINGLE:单选按钮。

ListView.CHOICE_MODE_MULTIPLE:复选框。

8.2.4.示例

显示图-2的列表:

图-2

步骤1、按8.1.2的步骤在res/values文件夹下创建arrays.xml文件,在该文件中定义四个直辖市的名称。

步骤2、打开res/layout/main.xml文件,在该文件中定义ListView控件,如下代码所示:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ListView

android:id="@+id/listView"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:entries="@array/citys"/>

</LinearLayout>

说明:以上代码中红框中的属性指定在列表中显示res/values/arrays.xml文件中数组名为citys的数组。

8.3.OnItemClick接口

8.3.1.概述

OnItemClick接口负责监听列表项单击事件。该接口在android.widget.AdapterView包下。

8.3.2.接口中声明的方法

onItemClick(AdapterView<?> parent, View view, int position, long id)

作用:监听用户单击列表项的事件。

说明:

(1)第一个参数-parent:当前列表对象,如ListView对象。

(2)第二个参数-view:被单击的列表项对象。

(3)第三个参数-position:被单击的列表项在列表中的索引值。

(4)第四个参数-id:被单击的列表项在列表中所处的行的索引值。

大部分情况下,position与id的值相同。

8.3.3.【示例】

在Activity.onCreate()方法中调用8.2.3中定义的ListView,当单击了该列表中某项时,显示该项的标题,效果如图-3所示:

图-3

代码如下所示:

public class MainAct extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

//实例化ListView控件

ListView listView=(ListView)findViewById(R.id.listView);

//定义列表项单击事件

listView.setOnItemClickListener(new OnItemClickListener() {

@Override

public void onItemClick(AdapterView<?> parent, View view,

int position, long id) {

//获取索引值是position的列表项,转换为字符串

String text=parent.getItemAtPosition(position).toString();

Toast.makeText(MainAct.this, text, 3000).show();

}

});

}

}

说明:

以上代码中用的是内部匿名类的方式实现了OnIntemClickListener接口。

8.4.OnItemLongClick接口

8.4.1.概述

OnItemLongClick接口负责监听用户长按列表项的事件。该接口在android.widget.AdapterView包下。

8.4.2.接口中声明的方法

boolean onItemLongClick(AdapterView<?> parent, View view,

int position, long id)

作用:监听用户长按列表项事件。

说明:各参数的作用参见8.3.2.

8.4.3.【示例】

当用户长按图-3中列表项时,显示该列表项标题,代码如下所示:

public class MainAct extends Activity

implements OnItemClickListener{

//实现OnItemLongClickListener.onItemLongClick方法

@Override

public boolean onItemClick(AdapterView<?> parent, View view,

int position, long id) {

//获得被长按的列表项

String text=parent.getItemAtPosition(position).toString();

Toast.makeText(MainAct.this, text, 3000).show();

return false;

}

说明:

以上采用的是MainAct类实现了OnItemClickListener接口,并实现了OnItemClick方法。这与8.2.4.的实现方式不 同,但效果相同。

8.5.CheckedTextView控件

8.5.1.概述

CheckTextView控件专用于显示单选风格或复选风格的标签,该控件与ListView配合使用,可实现列表的单选/复选的风格。,如图-4所 示:

图-4

8.5.2.常用属性

XML属性

说明

checkMark

设置ListView的选择行为标签的显示风格,有以下两个值:

(1) ?android:attr/listChoiceIndicatorSingle

(2) ?android:attr/listChoiceIndicatorMultipl

示例(1):设置单选风格显示,如下代码所示:

<CheckedTextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:checkMark="?android:attr/listChoiceIndicatorSingle">

</CheckedTextView>

示例(2);设置复选风格显示,如下代码所示:

<CheckedTextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:checkMark="?android:attr/listChoiceIndicatorMultiple">

</CheckedTextView>

示例(3)与ListView配合使用,在列表中显示单选风格按钮,效果如图-4所示。

步骤1、在res/layout文件夹下创建single_choice.xml文件,输入示例(1)所示代码。

步骤2、打开res/layout/main.xml,按以下代码中红框所示定义ListView控件:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ListView

android:id="@+id/listView"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:choiceMode="singleChoice"/>

</LinearLayout>

示例(4)用代码方式设置单选风格

步骤1与示例(3)相同。

步骤2、将示例(3)步骤2中红框内走后一行代码删除(该行代码将用Java代码实现)。

步骤3、在Java代码中设置ListView列表单选风格,如下代码所示:

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ListView ListView=(ListView)findViewById(R.id.listView);

listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

}

示例(5)设置复选风格的列表。

步骤1、在res/layout文件夹下创建single_choice.xml文件,输入示例(2)所示代码。

步骤2、打开res/layout/main.xml,按以下代码中红框所示定义ListView控件:

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ListView

android:id="@+id/listView"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:choiceMode=" listChoiceIndicatorMultiple"/>

</LinearLayout>

示例(6)用Java代码设置复选风格的列表

步骤1与示例(5)的步骤1相同。

步骤2、将示例(5)红框中最后一行删除。

步骤3、在Java代码中设置ListView列表复选风格,如下代码所示:

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ListView ListView=(ListView)findViewById(R.id.listView);

listView.setChoiceMode(ListView. CHOICE_MODE_MULTIPLE);

}

8.5.3.重要提示

1、ListView中每一行只能有一个CheckedTextView,通过查看ArrayAdapter类的getView()方法,发现该方法从资 源中获取布局对象后,强制转换为TextView类型,所以只能有一个CheckedTextView对象(因CheckedTextView类是 TtextView的子类)。

2、必须设置CheckedTextView的checkMark属性值。

3、在Java代码中必须设置ListView的选择模式,否则即使布局中显示了单选/复选风格,但点击列表项时也无法响应点击事件。

8.6. ArrayAdapter类

8.6.1.概述

ArrayAdapter类:数组适配器类,该类负责将数组按指定布局方式显示,通过与ListView等控件相关联,在列表类型的控件中按指定布局样式 显示数组中的数据。

8.6.2.创建ArrayAdapter对象

public ArrayAdapter(Context context, int textViewResourceId, T[] objects)

作用:创建ArrayAdapter对象。

说明:

参数-context:上下文对象,即当前窗口对象。

参数-textViewResourceId:标签控件的资源索引值。

参数-objects:数组。

8.6.3.【示例】

制作图-5所示效果,当长按列表项时,在屏幕下方显示该列表项标题。

图-5

步骤1、按8.1.2.所示的步骤在res/values文件夹中创建arrays.xml文件,在该文件中定义数组名为citys的数组。

步骤2、按8.5.2.中示例(1)和示例(3)创建单选风格的列表。

步骤3、在MainAct.java中,编写以下代码:

//本类实现了OnItemLongClickListener接口

public class MainAct extends Activity implements OnItemLongClickListener{

ListView mListView;

ArrayAdapter mAdapter;//定义数组适配器

String[] mCitys;

@Override

public void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

//步骤-1:实例化垂直列表控件

mListView=(ListView)findViewById(R.id.listView);

//步骤-2:创建资源管理对象

Resources res=getResources();

//步骤-3:获取资源中定义的字符串

mCitys=res.getStringArray(R.array.citys);

//步骤4-创建数组适配器对象,设置显示样式和显示的数据

mAdapter=new ArrayAdapter<String>(this,

R.layout.single_choice,mCitys);

//步骤-5:设置列表与适配器对象相关联

mListView.setAdapter(mAdapter);

//步骤-6:设置单选按钮风格

mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

//步骤7-注册长按事件

mListView.setOnItemLongClickListener(this);

}

//步骤8-编写回调方法

//实现OnItemLongClickListener.onItemLongClick方法

@Override

public boolean onItemLongClick(AdapterView<?> parent, View view,

int position, long id) {

/获得被长按的列表项

String text=(String)mListView.getItemAtPosition(position);

Toast.makeText(MainAct.this, text, 3000).show();

return false;

}

8.7.LayoutInflater类

8.7.1.概述

LayoutInflater类用于将资源中定义的布局或布局中的控件解析为Java的对象。

8.7.2.创建LayoutInflater对象

创建方式(1):Activity.getLayoutInflater(),通过该方法创建LayoutInflater对象。

示例:

LayoutInflater inflater=getLayoutInflater();

创建方式(2):LayoutInflater.from(Activity.this);

示例:

LayoutInflater inflater=LayoutInflater.from(this);

说明:this:这里是指当前的Activity对象。

8.7.3.常用方法

View inflate(int resource, ViewGroup root)

作用:解析指定资源中的布局,返回结果是View类型的对象。

参数-resource:指定索引值的布局。

参数-root:第一个参数的父容器,若没有则写为null。

示例:以下是res/layout文件夹下定义的布局文件:listview_item.xml,该文件代码如下所示:

<?xml version="1.0" encoding="UTF-8"?>

<!-- 布局中若只有一个控件,可省略布局管理器的定义 -->

<TextView

android:id="@+id/tvCity"

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content">

</TextView>

以下Java代码将该布局文件解析为TextView对象,并设置该对象的标题:

LayoutInflater inflater=getLayoutInflater();

TextView tvCityName=(TextView)

inflater.inflate(R.layout.listview_item, null);

//设置标签的标题

tvCityName.setText( 这是解析的标签 );

8.8.BaseAdapter

8.8.1.概述

BaseAdapter是ArrayAdapter的父类,该类是抽象类。通过创建该类的子类,可以灵活的设置适配器。以下通过分析该类结构,进一步理解 ArrayAdapter的运行机制。

8.8.2.常用方法

1、public View getView(int position, View convertView, ViewGroup parent)

作用:若当前列表的列表项未创建,则创建并返回列表项,包括列表项的显示样式和数据。

参数-position:当前列表项在列表中的索引值。

参数-convertView:当前列表项的View对象。

参数-parent:列表项的父容器对象-列表对象。

提示:该方法是抽象方法。

示例:以下代码创建一个标签对象,并返回该对象作为列表项:

@Override

public View getView(int position, View convertView, ViewGroup parent) {

//实例化标签对象

TextView tvCityName=

(TextView) findViewById(R.layout.listview_item);

//设置标签的标题为数组中索引值为position(即当前列表项的索引值)的元素,

tvCityName.setText(mCityNames[position]);

return tvCityName;//返回标签对象

}

2、long getItemId(int position)

作用:返回当前列表项的索引值。

参数-position:当前列表项的索引值。

提示:该方法是抽象方法。

【示例代码】以下代码返回当前列表项的索引值

@Override

public long getItemId(int position) {

return position;

}

3、Object getItem(int position)

作用:返回当前列表项对象,

参数-position:当前列表项的索引值。

提示:该方法是抽象方法。

【示例代码】

@Override

public Object getItem(int position) {

return mCityNames[position];

}

说明:mCityNames是一个字符串数组。以上代码返回该数组中的一个元素。

4、int getCount()

作用:返回该列表的对象总数

提示:该方法是抽象方法。

示例代码:

@Override

public int getCount() {

return mCityNames.length;

}

说明:以上方法返回数组的总长度,该数组的长度也就是列表项的个数。

8.8.3.【示例】

示例(1)在Activity类中定义一个方法:getAdapter,该方法创建并返回一个BaseAdapter类的对象。

代码如下所示:

//自定义getAdapter()方法,该方法返回一个ListAdapter类的对象

private BaseAdapter getAdapter(){

//定义一个继承自BaseAdapter的适配器类

BaseAdapter adapter=new BaseAdapter() {

//重写getView类,返回列表中的一个列表项对象

@Override

public View getView(int position, View convertView, ViewGroup parent){

//创建布局解析器对象

LayoutInflater inflater=getLayoutInflater();

//通过布局解析器创建标签对象

TextView tvCityName=

(TextView) inflater.inflate(R.layout.listview_item, null);

//设置标签的标题为数组中索引值为position(即当前列表项的索引值)的元素,

tvCityName.setText(mCityNames[position]);

return tvCityName;//返回标签对象

}

@Override

public long getItemId(int position) {

return position;

}

@Override

public Object getItem(int position) {

return mCityNames[position];

}

@Override

public int getCount() {

return mCityNames.length;

}

};

return adapter;//返回适配器对象

}

示例(2) 定义一个具有ArrayAdapter类相同功能的适配器类。

//MyArrayAdapter类继承自BaseAdapter类

public class MyArrayAdapter extends BaseAdapter {

//定义以下成员变量

Context mContext;//上下文

int mResId;//布局的资源索引

String[] mStrings;//显示的数据

//构造方法,通过参数获得所需的上下文对象、布局的资源索引值和显示的数据

public MyArrayAdapter(Context context, int mResId, String[] mStrings) {

super();

this.mContext=context;

this.mResId = mResId;

this.mStrings = mStrings;

}

//返回列表项的个数,即数组长度,该方法在构造方法执行后被调用

@Override

public int getCount() {

return mStrings.length;

}

//获得列表项对象

@Override

public Object getItem(int position) {

return mStrings[position];

}

//获得列表项的索引值

@Override

public long getItemId(int position) {

return position;

}

//创建并返回列表项,本方法现在只创建一个TextView对象

@Override

public View getView(int position, View convertView, ViewGroup parent) {

LayoutInflater inflater=LayoutInflater.from(mContext);

TextView tvCity=(TextView)inflater.inflate(mResId, null);

tvCity.setText(mStrings[position]);

return tvCity;

}

}

说明:

通过定义带形参的构造方法,MyArrayAdapter具有了一定的通用性,能通过外界传入的上下文对象、布局文件的资源索引值和数组中存放的数据完成 ArrayAdpater类相同的功能。对照ArrayAdapter和MyArrayAdapter两个类的构造方法,我们发现参数是相同的,而且 ArrayAdpater类也是BaseAdapter的子类。通过定义MyArrayAdapter类,我们对ArrayAdapter的实现原理有了 进一步地认识。

8.9.SimpleAdapter类

8.9.1.概述

SimpleAdapter是BaseAdapter的子类。是比ArrayAdapter类更加强大的适配器,用该适配器可定义表现更丰富的信息,例如 在ListView列表项中显示带文字和图片的信息。

8.9.2.构造方法

图-6

说明:

1、参数-context:上下文参数,该参数与ArrayAdapter类的构造方法中的第一个参数相同。

2、参数-data:一个List类型的集合,该集合的元素是Map集合,该Map集合的键是String类型,值是任意类型。每个Map对象中都存放着 列表项的信息。

例如,图-7是用ListView显示的图文并茂的列表。

其中,每个列表项的布局都是左图右字,即:左边是一个ImageView控件,右边是一个TextView控件。这种列表项需要用 SimpleAdapter适配器来实现。

SimpleAdpater适配器用Map对象存储列表项的数据信息。例如,图-7的显示北京市的列表项用以下代码将图片和文字的信息存放在Map对象 中:

//创建Map对象item,以键/值对的形式存放图片的资源索引值和标签的标题。

Map<String,Object> item=new HashMap<String, Object>();

//以键名photoId,值R.drawable.beijing存放图片数据

item.put("photoId", R.drawable.beijing);

item.put("name",北京 );//以name/北京的键/值对存放标签的标题

data.add(item);//将item添加至集合data中

说明:

资源索引值R.drawable.beijing是int类型,表示存放在res/drawable-mdpi/ beijing.png,该图片就是图-7中的北京市列表项中的图片。

第3行代码将item对象添加至data集合中。那么图-7有三个列表项,则以上三行代码要重复三次,将每个城市的信息先存放在Map对象中,再存放至 List集合中。

图-7

3、参数-resource:是布局文件的资源索引值,该布局控制列表项的显示样式,例如图-7的每个列表项的布局文件代码如下(该布局文件 名:listview_item.xml):

<?xml version="1.0" encoding="UTF-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content">

<!-- 城市图片 -->

<ImageView

android:id="@+id/ivCity"

android:layout_width="85dp"

android:layout_height="62dp"

android:src="@drawable/beijing"/>

<!-- 城市名称 -->

<TextView

android:id="@+id/tvCity"

android:layout_width="85dp"

android:layout_height="62dp"

android:textSize="20sp"

android:gravity="center_vertical"

android:text="北京"/>

</LinearLayout>

对以上布局文件,在图-6的构造方法中,第三个参数值是:R.layout.listview_item。

4、参数-from:一个字符串类型的数组,该数组中存放着所有键名,这些键名就是存放列表项的Map对象中出现的键。例如,上述代码中 的 photoId 和 name ,这时候第四个参数表示为:

new String[]{ photoId , name };

5、参数-to:存放列表项的布局文件中出现的所有控件的资源索引值,如listview_item.xml文件中出现的ImageView和 TextView控件,它们的索引值被分别定义为 @+id/ivCity 和 lvCity ,这时候第五个参数值是new int[]{R.id.ivCity,R.id,tvcity}。

控制图-7的适配器对象的完整的构造方法如下代码所示:

SimpleAdapter adapter=new SimpleAdapter(

this, data, R.layout.listview_item,

new String[]{"photoId","name"},

new int[]{R.id.ivCity,R.id.tvCity});

图-8是ListView和SimpleAdapter之间的关系的示意图:

这里ListView负责显示信息,而显示的样式和数据由SimpleAdapter设置,

8.9.3.示例

以下是显示图-7的效果的Java代码(相关图片和布局文件的定义省略):

public class MainAct extends Activity {

String[] mCityNames;//存放城市名称

//mList集合:存放所有城市的图片和名称

List<Map<String, Object>> mList=new ArrayList<Map<String,Object>>();

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

initData();//初始化集合

//创建列表对象

ListView lvCity=(ListView)findViewById(R.id.lvCity);

/*创建SimpleAdapter对象

* 第一个参数-this:上下文对象

* 第二个参数-mList:列表的数据(存放所有城市的集合)

* 第三个参数-R.layout.listview_item:列表项的布局资源索引值

* 第四个参数:键名数组

* 第五个参数:列表项布局中所有控件的资源索引值

*/

SimpleAdapter adapter=new SimpleAdapter(

this, mList, R.layout.listview_item,

new String[]{"photoId","name"},

new int[]{R.id.ivCity,R.id.tvCity});

lvCity.setAdapter(adapter);

}

//初始化集合数据

private void initData() {

//从资源中获得城市名称的数组

mCityNames=getResources().getStringArray(R.array.citys);

//photoId:城市图片的资源索引值数组

int[] photoId={

R.drawable.beijing,R.drawable.tianjing,

R.drawable.shanghai, R.drawable.chongqing

};

Map<String,Object> item;//存放一个城市的图片、名称的数据

//将北京市的图片和名称添加至mList集合中

item=new HashMap<String, Object>();

item.put("photoId", photoId[0]);

item.put("name", mCityNames[0]);

mList.add(item);

//将天津市的图片和名称添加至mList集合中

item=new HashMap<String, Object>();

item.put("photoId", photoId[1]);

item.put("name", mCityNames[1]);

mList.add(item);

//将上海市的图片和名称添加至mList集合中

item=new HashMap<String, Object>();

item.put("photoId", photoId[2]);

item.put("name", mCityNames[2]);

mList.add(item);

}

8.10.自定义SimpleAdapter

8.10.1.概述

为更好的理解SimpleAdapter类的运行机制,以下创建一个继承自BaseAdapter类的MySimpleAdapter类,该类模仿 SimpleAdapter类,实现类似于SimpleAdaptrer的功能。

8.10.2.定义MySimpleAdapter类

public class MySimpleAdapter extends BaseAdapter {

Context mContext;//上下文

//存放数据的集合

List<Map<String,Object>> mList;

int mResId;//布局View的资源索引值

String[] mKey;//键名数组

int[] mResItemIds;//控件的资源索引值数组

//构造方法

public MySimpleAdapter(Context mContext, List<Map<String, Object>> mList,

int mResId, String[] mKey, int[] mResItemIds) {

super();

this.mContext = mContext;

this.mList = mList;

this.mResId = mResId;

this.mKey = mKey;

this.mResItemIds = mResItemIds;

}

//返回集合元素总数

@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;

}

//重写getView方法,每次创建一个列表项

@Override

public View getView(int position, View convertView, ViewGroup parent) {

//从集合中取出当前(索引值为position)Map对象->item

Map<String,Object> item=mList.get(position);

//获得布局布局对象

LinearLayout ll=(LinearLayout)LayoutInflater.from(

mContext).inflate(mResId, null);

//创建图像对象

ImageView ivCity=(ImageView)ll.findViewById(mResItemIds[0]);

//设置图片

ivCity.setImageResource((Integer)item.get(mKey[0]));

//创建标签对象

TextView tvCity=(TextView)ll.findViewById(mResItemIds[1]);

//设置标签的标题

tvCity.setText(item.get(mKey[1]).toString());

//返回布局对象

return ll;

}

}

说明:

以上代码中有四块比较重要,

1、标注(1)处定义了五个成员变量,这五个成员变量分别表示上下文对象、存放列表数据的集合对象、存放布局文件的资源索引值变量,存放键名的数组和存放 布局中出现的所有控件的资源索引值数组。

2、标注(2)是类的构造方法,该方法带五个参数,这五个形参接收从外部传递过来的实参,并将这些数据赋值给标注(1)中定义的那五个成员变量。

注意:标注(2)中定义的五个参数以及这些参数的排列顺序与SimpleAdapter的构造方法中的参数完全一样。

3、标注(3)所指的getCount方法,该方法返回集合元素的总数,该方法在构造方法执行之后执行,确保列表能按照集合元素的个数创建相应数量的列表 项。

4、标注(4)所指的getView()方法,该方法负责创建一个列表项,并将list集合中索引值为position的Map对象取出并将图片和文字的 信息存放在ImageView和TextView对象的相应属性中。

注意:该方法返回一个LinearLayout类型的对象。

在标注(4)中首先通过解析器将listView_item.xml文件解析为一个LinearLayout对象,对象名为ll,并通过 ll.findViewById创建ImageView和TextView对象。注意:只有在findViewById方法前面指明ll,才能在该布局中 查找到相应的控件。

在标注(4)所指的代码中,

●mResId就是listview_item.xml的资源索引值。

●mResItemIds[0]是R.id.ivCity。

●mResItemIds[1]是R.id.lvCity。

●mKey[0]是键名photoId。

●mKey[1]是键名name。

8.10.3.调用MySimpleAdapter类

以下是在MainAct类中调用自定义的MySimpleAdapter类的关键代码,实现图-7的效果。

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

initData();//初始化集合对象mList

//实例化列表对象

ListView lvCity=(ListView)findViewById(R.id.lvCity);

/*创建自定义的MySimpleAdapter对象

* 第一个参数-this:上下文对象

* 第二个参数-mList:列表的数据(存放索引城市的集合)

* 第三个参数-R.layout.listview_item:列表项的布局资源索引值

* 第四个参数:键名数组

* 第五个参数:每个列表项用到的控件的资源索引值

*/

MySimpleAdapter adapter=new MySimpleAdapter(

this, mList, R.layout.listview_item,

new String[]{ photoId , name },

new int[R.id.ivCity,R.id.lvCity);

lvCity.setAdapter(adapter);//设置列表与适配器相关联

}

赞助本站

人工智能实验室
AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港