从零开始学Android编程
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第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实现效果