第4章 Android基本界面控件
在第3章我们学习了Android的几种基本的界面布局,了解了程序中界面所呈现的东西大部分都会放在布局中。本章将开始学习Android中的一些基本控件,其中包含:文本框(TextView)、编辑框(EditView)、单选按钮(RadioButton)等,下面将一一进行分析。
4.1 文本控件
文本控件包括两种控件:文本框和编辑框。这两个文本控件最大的区别就是前者不可以进行编辑,而后者可以。下面来学习它们的属性及应用。
4.1.1 文本框的介绍与实例
文本框(TextView)是Android中最常见的控件之一,它一般使用在需要显示一些信息的时候,其不能输入,只能通过初始化设置或在程序中修改。下面就简述一下TextView。
首先先看一下布局代码,代码如下:
<TextView android:id="@+id/myTextView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:textColor="#222222" android:background="#cccccc" android:text="Hello!My name is TextView." />
上面一段布局代码中是TextView常见的一些属性,其中:
❑ android:id="@+id/myTextView"是设置该组件的唯一标识,即key。
❑ android:layout_width="fill_parent"是设置文本所占的宽度,其中fill_parent是指占父控件宽度的全部。
❑ android:layout_height="wrap_content"是设置文本所占的高度,其中wrap_content将根据使用该值的控件来决定大小,一般使用这个值的控件会显得较小,好处是不需要测量具体大小,它一定会正好把所有的值给显示出来。
❑ android:textSize="18dip"是设置文本字体的大小。
❑ android:textColor="#222222"是设置文本字体的颜色。
❑ android:background="#cccccc"是设置文本背景颜色。
❑ android:text="Hello!My name is TextView."是设置文本的内容,即所要表达的信息内容。
下面分别通过XML和代码设置TextView显示的内容,如图4.1所示。
图4.1 TextView实例
(1)我们来了解一下这个实例的布局文件,里面有两个TextView,上面那个是通过xml资源来设置的,后面通过代码设置文字(详细代码请参考光盘源代码:第4章\TextViewDemo\res\layout\main.xml)。
<?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" android:background="#ffffff" > <TextView android:id="@+id/textView" android:layout_height="wrap_content" android:layout_width="fill_parent" android:text="xml设置内容" android:textColor="#000000" android:textSize="19sp" /> <TextView android:id="@+id/myTxt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10dip" android:textColor="#000000" android:textSize="19sp" /> </LinearLayout>
(2)获取TextView的id,再设置其内容(详细代码请参考光盘源代码:第4章\TextViewDemo\src\com\zhy\textView\ TextViewDemoActivity.java)。
private TextView myTxt;//声明控件 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //实例化控件 myTxt = (TextView) findViewById(R.id.myTxt); //设置内容 myTxt.setText("\n\n\n\n\n这是用代码设置的..."); //设置文字的颜色 myTxt.setTextColor(Color.BLUE); //设置文字的大小 myTxt.setTextSize(19); }
从这个例子可以看出,TextView的应用比较简单,属性也很简单,该控件在以后的程序中使用的频率很高,大家要对其熟练掌握。
4.1.2 编辑框的介绍与实例
我们在使用腾讯QQ手机版的时候,需要输入用户名和密码才能进行登录,自然就想到了编辑框。Android系统中的编辑框是EditView,EditText与TextView一样也是Android最常见的控件之一。通过名字我们很容易想到EditText,顾名思义,在Android中是编辑框。编辑框作为一个可编辑的文本框,在组件中占有重要地位,下面就通过实例来简述EditText。
首先,在main.xml布局文件中添加一个EditText组件,然后给EditText一个id作为唯一标识,再设置组件的长与宽。XML布局代码如下:
<?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" > <EditText android:id="@+id/myEdit" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
其运行效果图如图4.2所示。
图4.2 EditView实例图
上面几个属性都是一个EditText显示出来必备的几个属性,除了这些还有其他一些属性,如表4.1所示。
表4.1 EditView常用属性
如图4.3所示的实例中使用了表4.1中的属性,其代码如下。
图4.3 EditView
<?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" android:background="#ffffff" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="文本框实例" android:textSize="18dip" android:textColor="#000000" /> <EditText android:id="@+id/myEdit" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#ff0000" android:textSize="18dip" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#000000" android:text="运用了hint属性效果" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="点击请输入..." /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#000000" android:text="输入类型为密码类型效果" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:inputType="textPassword" /> //设置为密码效果 </LinearLayout>
从上面的实例中可以看出,EditView不仅是简单的编辑框,还可以作为密码输入控件,只要我们对其属性android:inputType进行设置即可。
4.1.3 在Android中输出日志
在Java中输出日志,我们采用的是System.out.println,会在控制台(Console)输出,但是在Android中就不一样了。
在Android中,在控制台不能输出日志信息,只能输出应用程序的安装信息。在Android中程序进行信息输出时,一般会采用android.util.Log中的静态方法。
在Log类中日志内容由多到少依次是:VERBOSE、DEBUG、INFO、WARN、ERROR。这5种不同类型的类分别对应5种不同的静态方法:Log.v()、Log.d()、Log.i()、Log.w()、Log.e()。想要显示Android输出的信息,首先要打开显示日志的视图LogCat,如图4.4所示,对其中的信息进行筛选,可以在右边的下拉列表框中选择,如图4.5所示。
图4.4 打开LogCat视图
图4.5 选择输出的类型
下面通过一个例子来说明如何使用Android中的日志输出类,如图4.6所示,矩形中的内容就是我们输出的内容。
图4.6 日志输出
代码如下:
private static final String TAG = "MyLogCat"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); int i = 812+1030; Log.i(TAG, "i="+i); }
从上面的代码中看到Log.i(TAG,"i="+1),这个方法中有两个参数,tag表示的是为这条信息定义一个标签,而这个标签一般是采用类名,这样做的目的是为了查找日志方便;msg表示的是输出日志的内容。同时也可以单独添加一个过滤器,首先单击LogCat上面的“+”,会弹出如图4.7所示的界面,对相应的属性进行设置,最后运行项目会看到我们添加的过滤器显示本程序的日志,如图4.8所示。对于这个日志过滤器我们也可以进行编辑或者删除,编辑的界面如图4.9所示。
图4.7 添加过滤器
图4.8 过滤器的操作
图4.9 自定义的过滤器
4.2 选择按钮控件
选择按钮对于大家来说并不陌生,它包括两种:单选按钮和复选按钮。例如,选择性别的时候,我们采用的是单选按钮,而选择爱好的时候我们应该采用复选按钮。它们两个最大的区别就是前者只能选择一个,而后者则可以选择多个,下面来看看它们具体的用法。
4.2.1 单选按钮的介绍与实例
单选按钮是一种双状态的按钮,可以选中或不选中。在单选按钮没有被选中时,用户能够按下或单击来选中它。但是,与复选框相反,用户一旦选中就不能取消选中(可以通过代码来控制,界面上单击的效果是一旦选中之后就不能取消选中了)。
多个单选按钮通常与RadioGroup同时使用。当一个单选组(RadioGroup)包含几个单选按钮时,选中其中一个的同时将取消其他选中的单选按钮。RadioGroup可将各自不同的RadioButton放在同一个Radio按钮组中,同一个RadioGroup组中的按钮,只能做出单一选择(如单选题)。
下面通过一个实例来说明。首先,设计一个TextView Widget,以及一个RadioGroup,并在该RadioGroup内放置两个RadioButton,默认为都不选择。在程序运行阶段,利用onCheckedChanged作为启动事件装置,让User选择其中一个按钮,显示被选择的内容,最后将RadioButton的选项文字显示于TextView当中,程序运行的效果如图4.10和图4.11所示。
图4.10 没有选中
图4.11 被选中
(1)界面布局的实现。整个界面采用的线性布局,详细代码请参考光盘源代码:第4章\RadioButtonDemo\res\layout\main.xml。
<?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" android:background="#ffffff" > <TextView android:id="@+id/myTxt" android:textSize="18sp" android:textColor="#000000" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="单选按钮的使用实例:\n请问你选择的是Java还是Android?"/> <RadioGroup android:id="@+id/myGroup" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" > <RadioButton android:id="@+id/myRadioBtn01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:textColor="#000000" android:text="Java" /> <RadioButton android:id="@+id/myRadioBtn02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:textColor="#000000" android:text="Android" /> </RadioGroup> </LinearLayout>
(2)单选按钮事件。当单选按钮选中时,改变上面的文字内容,详细代码请参考光盘源代码:第4章\RadioButtonDemo\src\ com\zhy\radio\RadioButtonDemoActivity.java。
//单选按钮事件 mRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(RadioGroup group, int checkedId) { //当第一个被选中 if (mRadioBtn1.isChecked()) { //改变文字内容 myView.setText("单选按钮的使用实例:\n我选择的是" + mRadioBtn1.getText()); }else if(mRadioBtn2.isChecked()){ myView.setText("单选按钮的使用实例:\n我选择的是" + mRadioBtn2.getText()); } } });
4.2.2 复选框的介绍与实例
单选按钮只能选择一个,而有时需要选择多个,这时就需要用到复选框。复选框(CheckBox)也是一种双状态的按钮,可以选中或不选中。它不同于单选按钮(RadioButton),根据名称就可以知道它可以选择多个选项,正如4.2.1 节所说的,如果单选按钮就好比做单项选择题,而复选框则是做多项选择题。
相对于RadioButton,CheckBox在代码方面就没有那么复杂,一个选项就一个CheckBox,两个选项就两个CheckBox。对于事件监听它与RadioButton的监听是一样的,同样是通过onCheckedChangeListener来监听的。
下面就通过一个实例来说明。我们的程序主要构造多个CheckBox的对象,以及一个TextView对象,并通过setOnCheckedChangeLisener实现onCheckedChanged()方法来更新TextView文字。首先来看一下程序运行的效果,如图4.12和图4.13所示。
图4.12 复选框(未选中)
图4.13 复选框(已选中)
(1)界面布局的实现。整个界面采用的线性布局,详细代码请参考光盘源代码:第4章\CheckBoxDemo\res\layout\main.xml。
<?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:background="#ffffff" android:layout_height="fill_parent" > <TextView android:id="@+id/myTxt" android:textColor="#000000" android:textSize="18sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="复选框效果\n你的爱好是什么?" /> <CheckBox android:id="@+id/myCheckBox01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="18sp" android:text="篮球" /> <CheckBox android:id="@+id/myCheckBox02" android:layout_width="wrap_content" android:textColor="#000000" android:textSize="18sp" android:layout_height="wrap_content" android:text="足球" /> </LinearLayout>
(2)复选框事件。我们可以把界面上的复选框全部选中,也可以取消选中,详细代码请参考光盘源代码:第4章\CheckBoxDemo\src\com\zhy\chechBox\ CheckBoxDemoActivity.java。
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mView=(TextView) findViewById(R.id.myTxt); mBox1=(CheckBox) findViewById(R.id.myCheckBox01); mBox2=(CheckBox) findViewById(R.id.myCheckBox02); //添加事件 mBox1.setOnCheckedChangeListener(myListener); mBox2.setOnCheckedChangeListener(myListener); } //按钮事件 private OnCheckedChangeListener myListener = new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { String str1 = mBox1.getText().toString();//获得按钮的文字 String str2 = mBox2.getText().toString(); if(mBox1.isChecked()==true&&mBox2.isChecked()==true){ mView.setText("复选框效果:\n我的爱好是:" + str1 + "," + str2); }else if(mBox1.isChecked()==true&&mBox2.isChecked()==false){ mView.setText("复选框效果:\n我的爱好是:" + str1); }else if(mBox1.isChecked()==false&&mBox2.isChecked()==true){ mView.setText("复选框效果:\n我的爱好是:" + str2); }else if(mBox1.isChecked()==false&&mBox2.isChecked()==false){ mView.setText("复选框效果:\n我的爱好是:"); } } };
4.3 列表控件
在Android系统中列表分为两种:一种是ListView,另一种是Spinner(下拉列表),它们各有各的用处。在程序开发中,ListView相对使用得多一点,下面分别了解一下怎么在程序中使用它们。
4.3.1 普通列表——ListView
列表(ListView)在Android的程序中使用频率相对比较高,很多地方都会使用到这个控件,其中的内容会以一个列表的形式显示出来,如腾讯的微博显示的界面。但是在使用ListView时需要一个适配器(Adapter)类显示需要内容。
适配器在Android中为我们定义了一些,要显示比较的内容可以使用Android系统自带的,当显示的内容较复杂的时候,系统的适配器已经不能满足要求了,这时可以自定义适配器,写一个类继承BaseAdapter。如表4.2所示为ListView的一些常用属性。
表4.2 ListView的常用属性
1.应用系统自带的适配器
应用系统自带的适配器如图4.14所示,这是系统自带的适配器(ArrayApapter),下面看看这个适配器的一些参数:
ArrayAdapter adapter = new ArrayAdapter(Context context, int resource, T[]objects)
第一个参数Context表示的是整个上下文,resource表示采用的布局文件,在这个例子中应用的是系统中的android.R.layout.simple_list_item_1布局文件,最后一个参数表示的是显示内容的资源。这里列出的只是系统中的一个适配器,还有很多,读者可以查看API。
图4.14 ListView实例
接下来我们看看这个实例的实现代码。
private ListView listView;//声明ListView控件 private ArrayAdapter<String> adapter; //适配器 private String[] str = new String[]{"Android","Java","iPhone","C++"}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //获得ListView的实例 listView = (ListView) findViewById(R.id.listView); //采用Android自带的适配器 adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_ item_1, str); listView.setAdapter(adapter); }
2.自定义适配器显示列表
自定义适配器显示列表如图4.15所示,这个实例模拟一个音乐显示列表,其中包括歌曲的名字、艺术家及歌曲的大小,同时还有一个下载的按钮。该列表的显示采用系统自带的适配器很难实现,用自己定义的适配器去实现列表就很简单了,下面来看一下实现的步骤。
图4.15 自定义列表
(1)适配器的布局文件。从图4.15中可以看出,列表中的每一项就是一个布局文件,而这个布局文件中有图片和文字,我们应该想到采用相对布局(详细代码请参考光盘源代码:第4章\MyListViewDemo\res\layout\ layout_adapter.xml)。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" android:orientation="vertical" > <ImageView android:id="@+id/image" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginLeft="5dip" android:layout_marginTop="3dip" android:src="@drawable/music" /> <TextView android:id="@+id/musicName" android:layout_height="wrap_content" android:layout_width="wrap_content" android:textColor="#000000" android:textSize="20sp" android:text="传奇" android:layout_marginLeft="8dip" android:layout_toRightOf="@id/image" android:layout_marginTop="5dip" /> <TextView android:id="@+id/artist" android:layout_below="@id/musicName" android:layout_height="wrap_content" android:layout_width="wrap_content" android:textSize="16sp" android:textColor="#0A246A" android:text="王菲" android:layout_marginLeft="10dip" android:layout_marginTop="6dip" android:layout_toRightOf="@id/image" /> <TextView android:id="@+id/musicSize" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:text="3.12M" android:textColor="#FFF000" android:textSize="18sp" android:layout_marginRight="10dip" android:layout_marginTop="5dip" /> <ImageButton android:layout_height="40px" android:layout_width="40px" android:layout_below="@id/musicSize" android:src="@drawable/download" android:layout_alignParentRight="true" android:layout_marginRight="10dip" android:layout_marginTop="3dip" /> </RelativeLayout>
(2)适配器的布局文件已经写好了,接下来开始写适配器。
上面我们已经知道了,自定义适配器要继承BaseAdapter类,并且实现它的几个方法,在这几个方法中,getCount()和getView()方法很重要,getCount()表示在ListView中显示的行的数目,而getView()为ListView提供了一个视图,也就是说上面写的布局文件会在这个方法中加载(详细代码请参考光盘源代码:第4章\MyListViewDemo\src\com\zhy\myListView\adapter\MyAdapter.java)。
//这个方法决定了在listview数据怎么显示
public View getView(int position, View convertView, ViewGroup parent) {
//如果convertView为null
if(convertView == null){
convertView = inflater.inflate(R.layout.layout_adapter, null);
holder = new ViewHolder();
//获得各个控件的实例
holder.musicNameView = (TextView) convertView.findViewById
(R.id.musicName);
holder.artistView = (TextView) convertView.findViewById
(R.id.artist);
holder.musicSizeView = (TextView) convertView.findViewById
(R.id.musicSize);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
//获得MusicInfoBean对象
MusicInfoBean infoBean = musicInfoBeans.get(position);
//设置内容
holder.musicNameView.setText(infoBean.getMusicName());
holder.artistView.setText(infoBean.getArtist());
holder.musicSizeView.setText(infoBean.getMusicSize());
//返回值
return convertView;
}
//这个内部类主要为了ListView加载的一个性能优化
//View的findViewById()方法也是比较耗时的,因此可以考虑只调用一次,
//之后就用View.getTag()方法来获得ViewHolder对象
//有时候如果没有必要,我们就没有必要这样去做,可以直接在上面定义
private final class ViewHolder{
private TextView musicNameView; //显示音乐的名称
private TextView artistView; //显示歌唱家
private TextView musicSizeView; //显示歌曲的大小
}
(3)主界面的布局,这个布局文件中有两个控件:文本框和列表。对于ListView控件,这里使用了3 个常用的属性,在表4.2 中已有说明:android:cacheColorHint、android:divider和android:dividerHeight(详细代码请参考光盘源代码:第4章\MyListViewDemo\res\layout\main.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="fill_parent" android:background="#ffffff" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="16sp" android:text="自定义列表" /> <ListView android:id="@+id/musicList" android:layout_width="fill_parent" android:layout_height="fill_parent" android:cacheColorHint="#00000000" android:divider="#FF0000" android:dividerHeight="2.0dip" ></ListView> </LinearLayout>
(4)最后一步是声明ListView,获得它的实例并且为它设置一个适配器,这个适配器当然是我们上面写的,在整个实例中我们还用到了一个实体类——MusicInfoBean.java,在此没有详细介绍,详细代码请参考光盘源代码:第4章\MyListViewDemo\src\com\zhy\myListView\MyListViewDemoActivity.java。
private ArrayList<MusicInfoBean> infoBeans = new ArrayList<MusicInfoBean>(); private ListView listView;//声明ListView控件 private MyAdapter adapter;//声明适配器 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); listView = (ListView) findViewById(R.id.musicList); MusicInfoBean bean1 = new MusicInfoBean(); //添加信息 bean1.setMusicName("珊瑚海"); bean1.setArtist("周杰伦"); bean1.setMusicSize("4.12M"); infoBeans.add(bean1); MusicInfoBean bean2 = new MusicInfoBean(); //添加信息 bean2.setMusicName("越伤越爱"); bean2.setArtist("何洁"); bean2.setMusicSize("6.0M"); infoBeans.add(bean2); MusicInfoBean bean3 = new MusicInfoBean(); //添加信息 bean3.setMusicName("沉默是金"); bean3.setArtist("张国荣"); bean3.setMusicSize("5.9M"); infoBeans.add(bean3); //实例化适配器 adapter = new MyAdapter(this, infoBeans); listView.setAdapter(adapter); }
图4.14所示的效果通过这些代码就实现了。关于自定义适配器,主要在于适配器的布局文件,还有它需要继承BaseAdapter类,实现它的几个方法,特别是getView方法很重要。
4.3.2 下拉列表——Spinner
Spinner控件也是一种列表类型的控件,可以极大地提高用户的体验性。当需要用户选择时,可以提供一个下拉列表将所有可选的项列出来,供用户选择。
下面我们用一个实例来说明Spinner的应用,程序设计了一个TextView用于显示选择的内容,一个Spinner用于提供给用户选择,下面详细讲解。首先需要编写一个XML布局文件,布局代码如下:
<?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" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <TextView android:id="@+id/myTxt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="你所在的城市:" /> <Spinner android:id="@+id/mySpinner" android:layout_width="fill_parent" android:layout_height="wrap_content" android:prompt="@string/city"/> </LinearLayout>
上述代码中Spinner为下拉列表控件,其中prompt为单击时弹出的标题。Spinner后为跳出的选择框的标题栏上的文字。这里有一点需要特别注意的是,prompt中的值不可以直接使用文字内容,需要通过把字符串放进string.xml使用。string.xml代码如图4.16所示。
图4.16 string.xml内容
下面通过例子来说明一下Spinner的使用,Spinner也需要一个适配器为它提供数据。可以通过事件(setOnItemSelectedListener)来获取列表中的内容,将其显示在一个TextView中,如图4.17所示。
图4.17 Spinner实例
布局文件在上面我们已经实现了,下面主要看看如何设置Spinner的适配器,如何获取它当中的数据,代码如下:
private String[] mList = { "北京", "上海", "深圳", "杭州", "夏威夷", "长沙" }; private ArrayAdapter mAdapter; //定义适配器 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mSpinner = (Spinner) findViewById(R.id.mySpinner); mText = (TextView) findViewById(R.id.myTxt); /* 为下拉列表定义一个适配器,这里就用到里前面定义的list */ mAdapter=new ArrayAdapter(this,android.R.layout.simple_spinner_item, mList); /* 将适配器添加到下拉列表上 */ mSpinner.setAdapter(mAdapter); /* 为适配器设置下拉列表下拉时的菜单样式 */ mAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); /* 为下拉列表设置各种事件的响应,这个事响应菜单被选中 */ mSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { mText.setText("你所在的城市:" + mAdapter.getItem(arg2)); } //没有选择 public void onNothingSelected(AdapterView<?> arg0) { mText.setText("NONE"); } });
当我们回头再来看Spinner时,就会发现它在适配器方面大部分与ListView的适配器相同,只是两种列表的选项不一样。Spinner可以通过方法setSelection设置默认选择的项目。
4.3.3 多级列表——ExpandableListView
ExpandableListView控件提供的是一个多级列表(一般是两级),我们先来看一下效果图,如图4.18所示为头部列表,单击其中的每一项下面会显示第二级列表,如图4.19所示。
图4.18 第一级列表
图4.19 第二级列表
从图4.18和图4.19中可以看出,ExpandableListView为我们提供了一个极好的两级列表的展示控件。
但是如何实现这个两级列表呢?既然ExpandableListView采用列表的形式,它也应该有一个适配器,但是它的适配器不是继承BaseAdapter,而是要继承它独有的适配器BaseExpandableListAdapter,同时也需要实现其中的几个方法,如表4.3所示。
表4.3 BaseExpandableListAdapter中的方法
表4.3 简单地介绍了BaseExpandableListAdapter中的方法,下面来实现图4.18 和图4.19中的效果。
(1)布局文件。从图中可以看出我们需要用到3 个布局文件:一个是声明ExpandableListView,一个声明第一级菜单,一个是第二级菜单的布局文件。
声明ExpandableListView的布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/default_bg"> <ExpandableListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_alignParentLeft="true"/> </RelativeLayout>
第一级菜单布局文件:
<?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="40dip" android:layout_gravity="center_horizontal" > <LinearLayout android:id="@+id/layout_013" android:layout_width="fill_parent" android:layout_height="40dip" android:orientation="horizontal" > <ImageView android:id="@+id/ImageView01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical" android:paddingTop="10dip" android:src="@drawable/user_group" > </ImageView> <RelativeLayout android:id="@+id/layout_013" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/content_001" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_gravity="center_vertical" android:gravity="center_vertical" android:paddingLeft="10px" android:textColor="#FFFFFF" android:textSize="26px" > </TextView> <ImageView android:id="@+id/tubiao" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" > </ImageView> </RelativeLayout> </LinearLayout> </LinearLayout>
第二级菜单布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/childlayout" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <ImageView android:id="@+id/child_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="40dip" android:background="@drawable/child_image" android:paddingTop="10dip" > </ImageView> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/child_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center_vertical" android:text="" android:textSize="16dip" > </TextView> <TextView android:id="@+id/child_text2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center_vertical" android:text="" android:textSize="12dip" > </TextView> </LinearLayout> </LinearLayout>
(2)写一个类ExAdapter继承BaseExpandableListAdapter,并且实现它的方法。获取给定组的一个显示的视图:
//获取一个显示的视图给定组 public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { View view = convertView; if (view == null) { // 通过getSystemService方法实例化一个视图的填充器 LayoutInflater inflater = (LayoutInflater) getSystemService (Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.member_listview, null); } TextView title = (TextView) view.findViewById (R.id.content_001); title.setText(getGroup(groupPosition).toString()); ImageView image=(ImageView) view.findViewById(R.id.tubiao); //判断实例可以展开,如果可以则改变右侧的图标 if(isExpanded) image.setBackgroundResource(R.drawable.btn_browser2); else image.setBackgroundResource(R.drawable.btn_browser); return view; }
获取给定组的相关数据及显示的列数:
//获取给定组相关的数据 public Object getGroup(int groupPosition) { return groupData.get(groupPosition).get(G_TEXT).toString(); } //获取第一级列表的列数 public int getGroupCount() { return groupData.size(); }
获取一个视图显示在给定的组的孩子的数据:
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { View view = convertView; if (view == null) { //填充视图 LayoutInflater inflater = (LayoutInflater) getSystemService (Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.member_childitem, null); } final TextView title = (TextView) view.findViewById (R.id.child_text); title.setText(childData.get(groupPosition). get(childPosition).get(C_TEXT1).toString()); final TextView title2 = (TextView) view.findViewById (R.id.child_text2); title2.setText(childData.get(groupPosition). get(childPosition).get(C_TEXT2).toString()); return view; }
获取与孩子在给定的组相关的数据,以及孩子组显示的列数:
//获取与孩子在给定的组相关的数据 public Object getChild(int groupPosition, int childPosition) { return childData.get(groupPosition).get(childPosition).get(C_TEXT1).toString(); } //获取指定组中孩子的数量 public int getChildrenCount(int groupPosition) { return childData.get(groupPosition).size(); }
(3)通过方法findViewById获取ExpandableListView的实例,为其设置数据显示的适配器。
for (int i = 0; i < 5; i++) { Map<String, String> curGroupMap = new HashMap<String, String>(); groupData.add(curGroupMap); curGroupMap.put(G_TEXT, "Group " + i); List<Map<String, String>> children = new ArrayList<Map<String, String>>(); for (int j = 0; j < 5; j++) { Map<String, String> curChildMap = new HashMap<String, String>(); children.add(curChildMap); curChildMap.put(C_TEXT1, "Child " + j); curChildMap.put(C_TEXT2, "Child " + j); } childData.add(children); } adapter=new ExAdapter(ExpanListViewDemoActivity.this); exList = (ExpandableListView) findViewById(R.id.list); exList.setAdapter(adapter); exList.setGroupIndicator(null); exList.setDivider(null);
从这个实例中可以看到,ExpandableListView的功能主要在于它的适配器,只要写好了适配器,列表就会显示出来。上面的例子中,我们没有添加,单击第二级列表不会有任何反应,如果要有点击的效果,需要为ExpandableListView设置一个事件:setOnChildClickListener,实例化OnChildClickListener类,实现其中的public boolean onChildClick(ExpandableListView parent, View v,int groupPosition, int childPosition, long id)方法,这个事件就留给读者去实现。
4.4 自动提示(AutoComplete-TextView)
我们在网上进行搜索时,经常会看见一些这样的效果:当我们输入类似“Android”的字样时,和Android相关的选项会被列出来,供用户选择。这种效果在Android中是用AutoCompleteTextView实现的。
AutoCompleteTextView组件是一个可编辑的文本视图,能显示用户键入的相关信息。建议列表显示一个下拉菜单,用户可以从中选择一项,以完成输入。建议列表是从一个数据适配器获取的数据。它有3个重要的方法。
❑ clearListSelection():清除选中的列表项。
❑ dismissDropDown():如果存在关闭下拉菜单。
❑ getAdapter():获取适配器。
下面就通过一个实例来说明AutoCompleteTextView,同样,AutoCompleteTextView也需要一个适配器提供数据,当输入文字时,下面的提示就是一个列表,运行效果如图4.20所示。
图4.20 AutoCompleteTextView实例
实现的步骤:
(1)界面的实现。从图4.20中可以看到,这个界面上只有一个TextView和AutoCompleteTextView,布局文件如下:
<?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" android:background="#ffffff" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="请在下面输入字母:" android:textColor="#000000" android:textSize="20sp" /> <AutoCompleteTextView android:id="@+id/AutoCompleteTextView" android:layout_width="fill_parent" android:layout_height="wrap_content"> </AutoCompleteTextView> </LinearLayout>
(2)获得AutoCompleteTextView实例,设置适配器(详细代码请参考光盘源代码:第4章\AutoCompleteTextViewDemo\src\com\zhy\AutoCompleteTextView\AutoCompleteTextViewDemoActi vity.java)。
private AutoCompleteTextView autoTextView; //声明控件 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //获得AutoCompleteTextView实例 autoTextView=(AutoCompleteTextView)findViewById(R.id.AutoCompleteTextView); //定义 String[]str={"android","abc","abcd","andsdg","goodboy","google"}; //声明适配器 ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_ dropdown_item_1line, str); //设置适配器 autoTextView.setAdapter(adapter); }
从实例中可以知道,AutoCompleteTextView需要在适配器中指定数据的来源,否则无法进行自动查找。
4.5 按钮(Button、ImageButton)
按钮在许多Windows窗口应用程序中是最常见的控件(Controls),此控件也常在网页设计中出现,诸如网页注册窗体、应用程序里的“确定”等。Android系统提供了两种按钮:一种是普通的按钮(Button),我们可以设置按钮上的文字;另一种是图片按钮(ImageButton),以一张图片作为单击的对象。如图4.21所示是普通的按钮,只是在上面显示了几个文字。
图4.21 Button实例
其布局文件如下:
<?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" android:background="#ffffff" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="我的按钮实例" android:textSize="18dip" android:textColor="#000000" /> <Button android:id="@+id/myButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按我..."/> </LinearLayout>
其中,layout_width和layout_height属性是必需的,具体的赋值可以根据项目需要进行,一般可以是具体的大小,即数字+单位,如android:layout_height ="30px"或者设置枚举的几种值。fill_parent将自动放大到与父控件一样的大小。例如,android:layout_width="fill_parent"表示它的宽度将填满父控件的横向控件。
wrap_content将根据所占据的控件来决定大小,一般使用这个值的控件会显得较小,好处是不需要测量具体大小,它一定会把所有的值显示出来。
而Text属性是设置Button的值,如android;text="按我…",则Button显示的内容是“按我…”。
另一种显示Button内容的方法是把这个值先作为一个资源存放在Res\values\strings.xml中:
<string name="btnText">确定</string>
其中,btnText可以视做这个值的键或ID,“确定”则是它的值,最后绑定的是它的键,Android系统会自动根据键找到它的值。即android:text="@string/btnText"。
在Activity中我们如何获取这个Button实例呢?如果想把每个控件ID都背下来是不可能的,比较合适的做法是利用R.id来获取指定的名称,而这个名称又唯一对应了控件ID。如果我们希望在某个Activity中使用按钮,第一个想到的方法应该是findViewById,通过R中的静态ID,可以轻易获得控件实例:
Button btn = (Button)findViewById(R.id. myButton);
如果系统不能识别,需要导入Android的Button所在的类包:
import android.widget.Button;
还可以在Activity的生命周期内随时寻找到这个Button,这里强烈建议,如果需要多次调用这个Button时,在onCreate中利用findViewById找到它后,把它记录在Activity的一个全局变量中,以后不需要再去寻找这个Button,因为寻找本身也需要时间。由于Android运行在手机或者平板电脑上,建议编码时充分考虑代码的简洁、效率,从而节省资源和电量。获取这个Button的实例后,可以利用代码为它赋值,如myButton.setText("按钮的值改变了")。也可以对按钮添加按钮事件OnClickListener(),这里要覆盖它的onClick方法。
下面来看一下ImageButton,它的属性与Button差不多,只是在设置图片时有些区别。首先看看效果图,如图4.22所示。
图4.22 ImageButton实例
由图4.22可以看出,图片在按钮的中间,这里通过android:src属性进行设置。它的事件和上面的Button一样,在此不再赘述。
<?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" android:background="#ffffff" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="ImageButton实例" android:textSize="18dip" android:textColor="#000000" /> <ImageButton android:id="@+id/myImgBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/qq"/> </LinearLayout>
通过上述两个例子的学习,对Android系统中的按钮有了一定的了解,在程序的开发过程中,按钮用得很多,所以它的常用属性必须了解。
4.6 小结
本章主要学习了Android中的一些基本控件,如文本框(TextView)、编辑框(EditText)、列表、单选按钮等。这些控件在以后的程序开发中会时常用到,是开发界面的必需控件,所以对它们的属性要非常了解。
4.7 习题
【习题1】如图exe_4.1所示,实现当点击“提交”按钮时,获取到界面各个控件的内容,并通过Log.i()方法输出获取到的值。
提示:首先要通过findViewById方法获得各个控件的实例,在调用相应的方法进行判断取得输入或选择的值。这里要对相应的控件添加事件,如Spinner下拉列表框控件需要一个适配器和事件。
关键代码如下:
userName = (EditText) findViewById(R.id.userName); password = (EditText) findViewById(R.id.password); sexGroup = (RadioGroup) findViewById(R.id.sexRadio); manButton = (RadioButton) findViewById(R.id.manRadio); womanButton = (RadioButton) findViewById(R.id.manleRadio); toggleButton = (ToggleButton) findViewById(R.id.ToggleButton01); ballBox = (CheckBox) findViewById(R.id.CheckBox01); checkBox = (CheckBox) findViewById(R.id.CheckBox02); spinner = (Spinner) findViewById(R.id.Spinner01); btn_cancel = (Button) findViewById(R.id.btn_cancel); btn_submit = (Button) findViewById(R.id.btn_submit); //按钮添加事件 btn_cancel.setOnClickListener(this); btn_submit.setOnClickListener(this);
图exe_4.1 界面效果
【习题2】实现一个自动提示的程序,如图exe_4.2所示。
提示:参考书中关于AutoCompleteTextView的例子,适当改变其适配器中的布局文件,即可实现图exe_4.2所示的效果。
代码提示:
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:textColor="#ff0000" > </TextView> ArrayAdapter ad = new ArrayAdapter(this,R.layout.mydropdowm, strArray);
图exe_4.2 自动提示效果
【习题3】用控件ListView实现如图exe_4.3所示的效果。
提示:该界面通过适配器实现了ListView的分割,与一般的适配器不一样。在一般的适配器中只要继承BaseAdapter,并且覆盖相应的方法就行了,而该适配器则还需添加两个方法:public boolean areAllItemsEnabled()和public boolean isEnabled(int position)。
关键代码如下:
@Override public int getCount() { return items.length; } @Override public boolean areAllItemsEnabled() { return false; } @Override public boolean isEnabled(int position) { return !items[position].startsWith("-"); } @Override public View getView(int arg0, View arg1, ViewGroup arg2) { TextView tv; if (arg1 == null) { tv = (TextView) LayoutInflater.from(context).inflate( android.R.layout.simple_expandable_list_item_1, arg2,false); } else { tv = (TextView) arg1; } tv.setText(items[arg0]); return tv; }
图exe_4.3 控件ListView实现效果