Android 5从入门到精通
上QQ阅读APP看书,第一时间看更新

4.4 常用Widget组件

在前面章节的学习中讲解了用户界面UI设计中布局方面的知识,其中涉及少数几个常用的组件,例如按钮、文本框等。在这一节中着重讲解Android用户UI设计中常用的各种组件的用法。

Android SDK提供了名为android.widget的包,其中提供了在应用程序界面设计中大部分常用的UI可视组件。之前章节中涉及的各种布局以及文本框、按钮等组件,都包含在这个包中。Android提供了强大的用户UI功能,要设计自己独特的应用程序界面,需要对各个组件有一个详细的了解。

4.4.1 创建Widget组件实例

在Eclipse中创建一个新的工程,名字为WidgetDemo,用于对各种常见UI组件进行学习。下面是工程实现步骤,在后续的章节中不会再赘述该过程:

步骤01 新建项目。单击File | New | Android Project,打开New Android Projec对话框,如图4.12所示。

图4.12 新建项目

步骤02 输入工程名称WidgetDemo,在Location后的文本框中输入工程的保存路径,单击Next后选择Android4.0,单击Next。

步骤03 输入包名“introduction.android.widgetDemo”和Activity的名称WidgetDemoActivity,单击Finish,则Eclipse会生成工程目录和相关文件。

WidgetDemoActivity.java文件是当前应用程序的入口类WidgetDemoActivity的定义文件。双击WidgetDemoActivity.java,发现Eclipse已经为其生成代码如下:

    package introduction.android.widgetDemo;
    import android.app.Activity;
    import android.os.Bundle;
    public class WidgetDemoActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
        }
    }

其中onCreate()方法中的setContentView(R.layout.main)表明WidgetDemoActivity使用的用户界面UI文件为main.xml。

双击main.xml文件,发现Eclipse为其提供了“Graphical Layout”和“main.xml”两种浏览方式。其中“Graphical Layout”方式为以图形方式浏览main.xml文件,其效果等同于main.xml在手机设备上运行的效果;“main.xml”方式为以代码方式浏览main.xml文件。这两种方式是等效的,都可以对main.xml文件进行编辑和查看。单击“main.xml”标签,发现Eclipse已经为其生成代码如下:

    <?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:orientation="vertical">
       <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello" />
    </LinearLayout>

该文件表明,当前main.xml文件所使用的布局为LinearLayout布局,该布局自动填满整个手机屏幕。在该布局中,放置了一个TextView组件,该TextView显示的内容为"@string/hello",表示string.xml文件中定义的hello变量的内容。双击values目录下的string.xml文件,会发现hello变量对应的值为“Hello World, WidgetDemoActivity!”。

单击main.xml的“Graphical Layout”浏览方式,可查看当前文件的图形化效果,如图4.13所示。

图4.13 文件的图形化效果

程序开发人员可以在该图形方式下,将左侧的各种组件直接拖动到屏幕上形成自己想要的布局,也可以直接修改main.xml文件的代码。

在后续章节中,在对布局文件进行修改时,若非特殊情况将不再单独描述。

4.4.2 按钮(Button)

Button按钮应该是用户交互中使用最多的组件,在很多应用程序中都很常见。当用户单击按钮的时候,会有相对应的响应动作。下面在WidgetDemo工程的主界面中main.xml中放置一个名为Button的按钮。文件代码如下:

    <?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:orientation="vertical">
       <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello" />



       <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button" />



    </LinearLayout>

其中,

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

表明在用户界面上放置了一个id为“button1”的按钮,按钮的高度(layout_height)和宽度(layout_width)都会根据实际内容调整(wrap_content),按钮上显示文字为Button,其运行效果如图4.14所示。

图4.14 Button的应用界面

按钮最重要的用户交互事件是“单击”事件。下面为Button1添加事件监听器和相应的单击事件。该过程在WidgetDemoActivity.java文件中完成,代码如下:

    package introduction.android.widgetDemo;



    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;



    public class WidgetDemoActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            Button btn=(Button)this.findViewById(R.id.button1);
            btn.setOnClickListener(new OnClickListener(){
                            @Override
                            public void onClick(View v){
                                    // TODO Auto-generated method stub
    setTitle("button1 被用户点击了");
                                    Log.i("widgetDemo", "button1 被用户点击了。");
                            }
            });
        }
    }

在WidgetDemoActivity的onCreate()方法中,通过findViewById(R.id.button1)方法获得的Button1的对象,通过setOnClickListener()方法为Button1设置了监听器。此处新建了一个实现了OnClickListener接口的匿名类作为监听器,并实现了onClick()方法。当Button1被单击,当前应用程序的标题被设置成“button1被用户单击了”,对应在LogCat中也会打印相应的字符串,运行结果如图4.15所示。

图4.15 单击按钮运行效果

4.4.3 文本框(TextView)

TextView是用于在界面上显示文字的组件,其显示的文本不可被用户直接编辑。程序开发人员可以设置TextView字体大小、颜色、样式等属性。在工程WidgetDemo的main.xml中添加一个TextView,代码如下:

    <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" />

运行效果如图4.16所示。

图4.16 TextView的应用界面

修改Button1的单击事件为:

    public void onClick(View view){
                    // TODO Auto-generated method stub
                    //setTitle("button1 被用户单击了");
                    Log.i("widgetDemo", "button1 被用户单击了。");
    TextView textview=(TextView)findViewById(R.id.textView1);
                    textview.setText("设置TextView的字体");
                    textview.setTextColor(Color.RED);
                    textview.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
    textview.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
    }

当Button1被单击时,通过setText()方法更改textView的显示内容为“设置TextView的字体”,通过setTextColor()方法修改textView显示字体的颜色为红色,通过setTextSize()方法修改textView显示字体的大小为20sp,通过setTypeface()方法修改textView显示字体的风格为加粗。

图4.17 再次单击按钮运行效果

当然,该过程也可以通过修改main.xml文件来实现。将TextView标签按照如下代码修改也可得到同样效果,但是失去了应用程序中与用户交互的过程:

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="设置TextView的字体"
        android:textColor="#ff0000"
        android:textSize="20sp"
        android:textStyle="bold"/>

4.4.4 编辑框(EditText)

EditText是TextView的子类,在TextView的基础上增加了文本编辑功能,用于处理用户输入,例如登录框等,是非常常用的组件。

在工程WidgetDemo的main.xml文件中添加一个EditText,并实现如下功能:当用户在EditText中输入信息的同时,用一个TextView显示用户输入的信息。

工程WidgetDemo中的布局文件main.xml中增加的代码如下:

    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

在WidgetDemoActivity的onCreate()方法中添加下列代码:

    editText=(EditText)findViewById(R.id.editText1);
    editText.addTextChangedListener(new TextWatcher(){



    @Overridepublic void afterTextChanged(Editable s){
            // TODO Auto-generated method stub



    }



    @Overridepublic void beforeTextChanged(CharSequence s, int start, int count,
            int after){
            // TODO Auto-generated method stub



    }



    @Overridepublic void onTextChanged(CharSequence s, int start, int before,
            int count){
            // TODO Auto-generated method stub
            String text=editText.getText().toString();
            textview.setText(text);
    }



    });

运行结果如图4.18所示。

图4.18 EditText的应用界面

4.4.5 多项选择按钮(CheckBox)

多项选择按钮CheckBox属于输入型组件,该组件允许用户一次选择多个选项。当不方便用户在手机屏幕上进行直接输入操作时,该组件的使用显得尤为便利。

下面通过实例讲解CheckBox的使用方法。该实例运行效果如图4.19所示。

图4.19 CheckBox的应用界面

在工程WidgetDemo的布局文件main.xml文件中添加一个Button,代码如下:

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBoxDemo" />

当该Button被用户单击时,启动一个名为CheckBoxActivity的Activity,在该Activity中演示CheckBox的使用方法。启动CheckBoxActivity的相关代码如下:

    Button ckbtn=(Button)this.findViewById(R.id.button2);
    ckbtn.setOnClickListener(new OnClickListener(){



    @Override
    public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this,CheckBoxActivity.class);
            startActivity(intent);
    }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name="CheckBoxActivity"></activity>

CheckBoxActivity所使用的布局文件为checkbox.xml,使用LinearLayout布局,其中放置了一个TextView和三个CheckBox。Checkbox.xml的文件内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">



       <TextView
            android:id="@+id/text"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/checkboxhello"/>
       <CheckBox
            android:id="@+id/CheckBox1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/football"/>
       <CheckBox
            android:id="@+id/CheckBox2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/song"/>
       <CheckBox
            android:id="@+id/CheckBox3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/book"/>



    </LinearLayout>

这四个组件在对应的strings.xml文件中定义的变量为:

    <string name="checkboxhello">你的爱好是:</string>
       <string name="football">篮球</string>
       <string name="song">听歌曲</string>
    <string name="book">看书</string>

当用户对多项选择按钮进行选择时,为了确定用户选择的是哪几项,需要对每个多项选择按钮进行监听。CompouButton.OnCheckedChangedListener接口可用于对CheckBox的状态进行监听。当CheckBox的状态在未被选中和被选中直接变化时,该接口的onCheckedChanged()方法会被系统调用。CheckBox通过setOnCheckedChangeListener()方法将该接口对象设置为自己的监听器。

CheckBoxActivity.java代码如下:

    package introduction.android.widgetDemo;



    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.CheckBox;
    import android.widget.CompoundButton;
    import android.widget.TextView;



    public class CheckBoxActivity extends Activity {
    private TextView textView;
    private CheckBox bookCheckBox;
    private CheckBox songCheckBox;
    private CheckBox footbaCheckBox;
    @Override
    protected void onCreate(Bundle savedInstanceState){
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.checkbox);
        textView=(TextView)findViewById(R.id.text);
        footbaCheckBox=(CheckBox)findViewById(R.id.CheckBox1);
        songCheckBox=(CheckBox)findViewById(R.id.CheckBox2);
        bookCheckBox=(CheckBox)findViewById(R.id.CheckBox3);



        footbaCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){



        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){
            // TODO Auto-generated method stub
            if(footbaCheckBox.isChecked()){
                textView.append(footbaCheckBox.getText().toString());
            }else {
                if(textView.getText().toString().contains("足球")){
                textView.setText(textView.getText().toString().replace("足球", ""));
                }
            }
            }
        });
        songCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){



            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){
            // TODO Auto-generated method stub
            if(songCheckBox.isChecked()){
                textView.append(songCheckBox.getText().toString());
            }else {
                if(textView.getText().toString().contains("唱歌")){
                textView.setText(textView.getText().toString().replace("唱歌", ""));
                }
            }
            }
        });



        bookCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){



            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){
            // TODO Auto-generated method stub
            if(bookCheckBox.isChecked()){
                textView.append(bookCheckBox.getText().toString());
            }else {
                if(textView.getText().toString().contains("读书")){
                textView.setText(textView.getText().toString().replace("读书", ""));
                }
            }
            }
        });
    }



    }

CheckBoxActivity为Checkbox.xml文件中的三个CheckBox分别添加了监听器。当CheckBox的状态发生改变时,通过Checkbox.isChecked()方法可以获取当前CheckBox按钮的选中状态,进而进行处理。

4.4.6 单项选择按钮组(RadioGroup)

RadioGroup为单选按钮组,其中可以包含多个RadioButton,即单选按钮,它们共同为用户提供一种多选一的选择方式。在多个RadioButton被同一个RadioGroup包含的情况下,多个RadioButton之间自动形成互斥关系,仅有一个可以被选择。单选按钮的使用方法和CheckBox的使用方法高度相似,其事件监听接口使用的是RadioGroup.OnCheckedChangeListener(),使用setOnCheckedChangeListener()方法将监听器设置到单选按钮上。按照CheckBox的讲解思路,启动一个名为RadioGroupActivity的Activity来对RadioGroup进行讲解。

RadioGroupActivity运行效果如图4.20所示。

图4.20 RadioGroup的应用界面

在工程WidgetDemo的布局文件main.xml文件中添加一个Button,并启动RadioGroupActivity的相关代码。在main.xml中添加代码如下:

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RadioGroupDemo" />

启动处理RadioGroup的Activity RadioGroupActivity的代码如下:

    Button radiotn=(Button)this.findViewById(R.id.button3);
    radiotn.setOnClickListener(new OnClickListener(){



    @Override
    public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this,RadioGroupActivity.class);
            startActivity(intent);
    }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name=" RadioGroupActivity "></activity>

RadioGroupActivity使用的是radiogroup.xml,其代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">



       <TextView
            android:id="@+id/radiohello"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello"/>



       <RadioGroup
            android:id="@+id/radiogroup1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_x="3px"
       >
           <RadioButton
                android:id="@+id/radiobutton1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/football"
            />
           <RadioButton
                android:id="@+id/radiobutton2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/bascketball"
            />
           <RadioButton
                android:id="@+id/radiobutton3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/badminton"
            />
       </RadioGroup>



    </LinearLayout>

该布局文件使用了LinearLayout布局,并且在其中放置了一个TextView和一个RadioGroup。RadioGroup中含有三个RadioButton。这些组件对应的strings.xml文件中定义的变量为:

    <string name="radiohello">你最喜欢的运动是:</string>
       <string name="bascketball">篮球</string>
       <string name="badminton">羽毛球</string>
    <string name="football">足球</string>

RadioGroupActivity.java代码如下:

    package introduction.android.widgetDemo;



    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.RadioButton;
    import android.widget.RadioGroup;
    import android.widget.TextView;



    public class RadioGroupActivity extends Activity {
     private TextView textview;
     private RadioGroup radiogroup;
     private RadioButton radio1,radio2,radio3;
     @Override
     public void onCreate(Bundle savedInstanceState){
          super.onCreate(savedInstanceState);
          setContentView(R.layout.radiogroup);
          textview=(TextView)findViewById(R.id.radiohello);
          radiogroup=(RadioGroup)findViewById(R.id.radiogroup1);
          radio1=(RadioButton)findViewById(R.id.radiobutton1);
          radio2=(RadioButton)findViewById(R.id.radiobutton2);
          radio3=(RadioButton)findViewById(R.id.radiobutton3);
          radiogroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener(){



        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId)
            {
            // TODO Auto-generated method stub
                String text="我最喜欢的运动是";
            if(checkedId==radio1.getId()){
                text+=radio1.getText().toString();
                textview.setText(text);
            }else if(checkedId==radio2.getId()){
                text+=radio2.getText().toString();
                textview.setText(text);
            }else if(checkedId==radio3.getId()){
                text+=radio3.getText().toString();
                textview.setText(text);
            }
            }
        });
    }
    }

在RadioGroupActivity的onCreate()方法中为RadioGroup添加监视器RadioGroup。OnCheckedChangeListener,在其回调方法onCheckedChanged()中对三个RadioButton分别进行处理。需要说明的是如果把RadioGroup去掉,只使用RadioButton的话,则需要为每个RadioButton单独设置监听器,其使用方法和CheckBox没有任何区别。

4.4.7 下拉列表(Spinner)

Spinner提供下拉列表式的输入方式,该方法可以有效节省手机屏幕上的显示空间。

下面用一个简单的实例讲解Spinner的使用方法。在工程WidgetDemo的布局文件main.xml文件中添加一个Button,用以启动SpinnerActivity。

在main.xml中添加代码如下:

    <Button
            android:id="@+id/button4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="SpinnerDemo" />

单击Button并启动SpinnerActivity的代码如下:

    Button spinnerbtn=(Button)this.findViewById(R.id.button4);
    spinnerbtn.setOnClickListener(new OnClickListener(){
     @Override
     public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this,SpinnerActivity.class);
            startActivity(intent);
    }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name=" SpinnerActivity "></activity>

SpinnerActivity的运行效果如图4.21所示。

图4.21 Spinner的应用界面

SpinnerActivity使用的布局文件为spiner.xml,其代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="textview"/>
       <Spinner
            android:id="@+id/spinner1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />



    </LinearLayout>

SpinnerActivity.java文件代码如下:

    package introduction.android.widgetDemo;



    import java.util.ArrayList;
    import java.util.List;
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.Spinner;
    import android.widget.TextView;



    public class SpinnerActivity extends Activity {
     private List<String>list=new ArrayList<String>();
     private TextView textview;
     private Spinner spinnertext;
     private ArrayAdapter<String>adapter;
     public void onCreate(Bundle savedInstanceState){
             super.onCreate(savedInstanceState);
             setContentView(R.layout.spiner);
             //第一步:定义下拉列表内容
             list.add("沈阳");
             list.add("天津");
             list.add("北京");
             list.add("上海");
             list.add("深圳");
             textview=(TextView)findViewById(R.id.textView1);
             spinnertext=(Spinner)findViewById(R.id.spinner1);
             //第二步:为下拉列表定义一个适配器
             adapter=new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, list);
             //第三步:设置下拉列表下拉时的菜单样式。
             adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
             //第四步:将适配器添加到下拉列表上
             spinnertext.setAdapter(adapter);
             //第五步:添加监听器,为下拉列表设置事件的响应
             spinnertext.setOnItemSelectedListener(new Spinner.OnItemSelectedListener(){
                 public void onItemSelected(AdapterView<?>arg0, View arg1, int arg2, long arg3){
                     // TODO Auto-generated method stub
                     /* 将所选spinnertext的值带入myTextView 中*/
                 textview.setText("我来自:"+adapter.getItem(arg2));
                     /* 将spinnertext显示*/
                     arg0.setVisibility(View.VISIBLE);
                 }
                 public void onNothingSelected(AdapterView<?>arg0){
                     // TODO Auto-generated method stub
                 textview.setText("NONE");
                     arg0.setVisibility(View.VISIBLE);
                 }
             });
             //将spinnertext添加到OnTouchListener 对内容选项触屏事件处理
             spinnertext.setOnTouchListener(new Spinner.OnTouchListener(){
                  @Override
                  public boolean onTouch(View v, MotionEvent event){
                        // TODO Auto-generated method stub
                        // 将mySpinner 隐藏
                                v.setVisibility(View.INVISIBLE);
                                Log.i("spinner","Spinner Touch事件被触发!");
                                return false;
                  }
             });
             //焦点改变事件处理
             spinnertext.setOnFocusChangeListener(new Spinner.OnFocusChangeListener(){
                 public void onFocusChange(View v, boolean hasFocus){
                     // TODO Auto-generated method stub
                     v.setVisibility(View.VISIBLE);
                     Log.i("spinner","Spinner FocusChange 事件被触发!");
                 }
             });
      }
    }

SpinnerActivity通过五个步骤将Spinner初始化并进行事件处理。分别为:


●定义下拉列表的列表项内容List<String>。

●为下拉列表Spinner定义一个适配器ArrayAdapter<String>,并与列表项内容相关联。

●使用ArrayAdapter.setDropDownViewResource()设置Spinner下拉列表在打开时的下拉菜单样式。

●使用Spinner. setAdapter()将适配器数据与Spinner关联起来。

●为Spinner添加事件监听器,进行事件处理。


Spinner支持多种事件处理方式,本实例中对Spinner被单击事件、焦点改变事件和Spinner的列表项被选中事件进行了处理。

在本实例中,SpinnerActivity在程序代码中动态建立了下拉列表每一项的内容。除此之外,还可以在XML文件中定义Spinner的下拉列表项,步骤如下:

在res/values文件夹下新建cities.xml文件夹:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
       <string-array name="city">
           <item>shenyang</item>
           <item>nanjing</item>
           <item>beijing</item>
           <item>tianjin</item>
       </string-array>
    </resources>

在SpinnerActivity.java中初始化Spinner:

    Spinner spinner=(Spinner)findViewById(R.id.spinner1);
    ArrayAdapter<CharSequence>adapter=ArrayAdapter.createFromResource(this, R.array.city, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);

运行效果如图4.22所示。

图4.22 Spinner的事件处理

4.4.8 自动完成文本(AutoCompleteTextView)

在使用百度或者Google搜索信息时,只需要在搜索框中输入几个关键字,就会有很多相关的信息以列表形式被列举出来供用户选择,这种效果在Android SDK中可以通过AutoCompleteTextView来实现。

下面用一个简单的实例讲解AutoCompleteTextView的使用方法。在工程WidgetDemo的布局文件main.xml中添加一个Button,用以启动AutoCompleteTextViewActivity。

在main.xml中添加代码如下:

    <Button
        android:id="@+id/button5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="AutoCompleteTextViewDemo" />

单击Button,并启动AutoCompleteTextViewActivity的代码如下:

    Button autobtn=(Button)this.findViewById(R.id.button5);
    autobtn.setOnClickListener(new OnClickListener(){
     @Override
     public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this,AutoCompleteTextViewActivity.class);
            startActivity(intent);
     }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name=" AutoCompleteTextViewActivity"></activity>

AutoCompleteTextViewActivity运行效果如图4.23所示。

图4.23 AutoCompleteTextViewActivity运行效果

AutoCompleteTextViewActivity使用的布局文件为autocompletetextview.xml,其具体内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
     <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="AutoCompleteTextView演示:" />



       <AutoCompleteTextView
            android:id="@+id/autoCompleteTextView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="">
           <requestFocus />
       </AutoCompleteTextView>



    </LinearLayout>

AutoCompleteTextViewActivity.java代码如下:

    package introduction.android.widgetDemo;



    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.ArrayAdapter;
    import android.widget.AutoCompleteTextView;



    public class AutoCompleteTextViewActivity extends Activity {
     private AutoCompleteTextView textView;
        private static final String[] autotext=new String[] {"hello","hello World","hello Android"};
        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.autocompletetextview);
            textView=(AutoCompleteTextView )findViewById(R.id.autoCompleteTextView1);
            /*new ArrayAdapterd对象将autotext字符串数组传入*/
            ArrayAdapter<String>adapter=new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,autotext);
            /*将ArrayAdapter添加到AutoCompleteTextView中*/
            textView.setAdapter(adapter);
        }
    }

AutoCompleteTextViewActivity中为可自动补全的内容建立对应字符串数组autotext,然后将该数组关联到ArrayAdapter中,然后将ArrayAdapter与AutoCompleteTextView相关联,进而实现自动完成文本功能。

AutoCompleteTextView提供一系列属性对显示效果进行设置,例如:


●completionThreshold:它的值决定了你在AutoCompleteTextView至少输入几个字符,它才会具有自动提示的功能。另外默认最多提示20条。

●dropDownAnchor:它的值是一个View的ID,指定后,AutoCompleteTextView会在这个View下弹出自动提示。

●dropDownSelector:设置自动提示项中当前选中项的背景色。

●dropDownWidth:设置自动提示列表的宽度。

4.4.9 日期选择器和时间选择器(DatePicker和TimePicker)

Android SDK提供了DatePicker和TimePicker组件,分别对日期和时间进行选择,方便日期和时间设定。

下面用一个简单的实例讲解DatePicker和TimePicker组件的使用方法。在工程WidgetDemo的布局文件main.xml中添加一个名为“Date/Time”的Button,用以启动TimeActivity。

在main.xml中添加代码如下:

    <Button
        android:id="@+id/button6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" Date/Time " />

单击Button并启动TimeActivity的代码如下:

    Button timebtn=(Button)this.findViewById(R.id.button6);
            timebtn.setOnClickListener(new OnClickListener(){
                @Override
                public void onClick(View v){
                    // TODO Auto-generated method stub
                    Intent intent=new Intent(WidgetDemoActivity.this,TimeActivity.class);
                    startActivity(intent);
                }
            });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name=" TimeActivity"></activity>

TimeActivity运行效果如图4.24所示。

图4.24 TimeActivity运行效果

TimeActivity使用的布局文件为time.xml,其内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
         <TextView
            android:id="@+id/timeview"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="DatePicker和TimePicker演示" />
    <TimePicker
            android:id="@+id/timepicker"
            android:layout_width="wrap_content"
            android:layout_height="116dp"
            android:background="#778888" />
    <!-- 设置背景色为墨绿 -->
     <DatePicker
            android:id="@+id/datepicker"
            android:layout_width="271dp"
            android:layout_height="196dp"
            android:background="#778899" />
    </LinearLayout>

TimeActivity.java的代码如下:

    package introduction.android.widgetDemo;
     import java.util.Calendar;
     import android.app.Activity;import android.os.Bundle;import android.widget.DatePicker;import android.widget.TextView;import android.widget.TimePicker;
     public class TimeActivity extends Activity {
     private TextView textview;
        private TimePicker timepicker;
        private DatePicker datepicker;
        /* 声明日期及时间变量 */
        private int year;
        private int month;
        private int day;
        private int hour;
        private int minute;



        @Override
        public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.time);
     /* 获取当前日期及时间 */
     Calendar calendar=Calendar.getInstance();
     year=calendar.get(Calendar.YEAR);
     month=calendar.get(Calendar.MONTH);
     day=calendar.get(Calendar.DAY_OF_MONTH);
     hour=calendar.get(Calendar.HOUR);
     minute=calendar.get(Calendar.MINUTE);
     datepicker=(DatePicker)findViewById(R.id.datepicker);
     timepicker=(TimePicker)findViewById(R.id.timepicker);
     /* 设置TextView对象,显示初始日期时间 */
     textview=(TextView)findViewById(R.id.timeview);
     textview.setText(new StringBuilder().append(year).append("/")
            .append(format(month+1)).append("/").append(format(day))
            .append("  ").append(format(hour)).append(":")
            .append(format(minute)));
     /* 设置OnDateChangedListener()*/
     datepicker.init(year, month, day,
            new DatePicker.OnDateChangedListener(){
                @Override
                public void onDateChanged(DatePicker view, int year,
                    int monthOfYear, int dayOfMonth){
                // TODO Auto-generated method stub
                TimeActivity.this.year=year;
                month=monthOfYear;
                day=dayOfMonth;
                textview.setText(new StringBuilder().append(year)
                    .append("/").append(format(month+1))
                    .append("/").append(format(day)).append("  ")
                    .append(format(hour)).append(":")
                    .append(format(minute)));
                }
            });
        timepicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener()
            {



                @Override
                public void onTimeChanged(TimePicker view, int hourOfDay, int minute)
                {
                    // TODO Auto-generated method stub
                    hour=hourOfDay;
                    TimeActivity.this.minute=minute;
                    textview.setText(new StringBuilder().append(year)
                .append("/").append(format(month+1))
                .append("/").append(format(day)).append("  ")
                .append(format(hour)).append(":")
                .append(format(minute)));
                }
            });



        }



        private String format(int time){
    String str=""+time;if(str.length()==1)
        str="0"+str;return str;
        }
    }

TimeActivity中使用java.util.Calendar对象获取当前系统时间。当更改DatePicker组件中的日期时,会触发DatePicker的OnDateChange()事件;当修改TimePacker的时间时,会触发TimePacker的OnDateChange()事件。

由本实例可见,DatePicker实现OnDateChangedListener监听器的方法与TimePicker实现setOnTimeChangedListener监听器的方法有所类似。DatePicker用init()方法设定年、月、日的同时设定监听器,而TimePicker使用setOnTimeChangedListener()直接设定。

4.4.10 进度条(ProgressBar)

当应用程序在后台运行时,可以使用进度条反馈给用户当前的进度信息。进度条被用以显示当前应用程序运行状况,功能完成多少等情况。Android SDK提供两种样式的进度条,一种是圆形的进度条,另一种是水平进度条。其中圆形进度条分大、中、小三种。

进度条本质上是一个整数,显示当前的整数值在特定范围内的比重。下面用一个简单的实例讲解ProgressBar组件的使用方法。

在工程WidgetDemo的布局文件main.xml中添加一个名为ProgressBarDemo的Button,用以启动ProcessBarActivity。

在main.xml中添加代码如下:

    <Button
        android:id="@+id/button7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ProgressBarDemo" />

单击Button并启动ProcessBarActivity的代码如下:

    Button processbtn=(Button)this.findViewById(R.id.button7);
            processbtn.setOnClickListener(new OnClickListener(){
                @Override
                public void onClick(View v){
                    // TODO Auto-generated method stub
                    Intent intent=new Intent(WidgetDemoActivity.this,ProcessBarActivity.class);
                    startActivity(intent);
                }
            });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name="ProcessBarActivity"></activity>

ProcessBarActivity运行效果如图4.25所示。

图4.25 ProcessBarActivity运行效果

ProcessBarActivity使用的布局文件为processbar.xml,其内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    <ProgressBar
            android:id="@+id/progressBar1"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />



       <ProgressBar
            android:id="@+id/progressBar2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />



       <ProgressBar
            android:id="@+id/progressBar3"
            style="?android:attr/progressBarStyleLarge"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />






       <ProgressBar
            android:id="@+id/progressBar4"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="209dp"
            android:layout_height="30dp"
            android:max="100"/>



    </LinearLayout>

该布局中放置了小、中、大三种类型的圆形进度条各一个,以及一个水平放置的条形进度条。一般情况下,开发人员不会为圆形进度条指定进度,圆形进度条只是展示运行效果,而不反映实际的进度。水平进度条则不同,开发人员会为条形进度条指定最大值,以及进度条当前值的获取方法。在本实例中,条形进度条的最大值为100。

ProcessBarActivity.java代码如下:

    package introduction.android.widgetDemo;
     import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.widget.ProgressBar;
     public class ProcessBarActivity extends Activity {
        ProgressBar progressBar;
        int i=0;
        int progressBarMax=0;
        /* 创建Handler对象 */
        Handler handler=new Handler();



        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.processbar);
            progressBar=(ProgressBar)findViewById(R.id.progressBar4);
            /* 获取最大值 */
            progressBarMax=progressBar.getMax();
            /* 匿名内部类启动实现效果的线程 */
            new Thread(new Runnable(){
                @Override
                public void run(){
                    while(i++<progressBarMax){
                        // 设置滚动条当前状态值
                        progressBar.setProgress(i);
                        try {
                            Thread.sleep(15);



                        } catch(Exception e){
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }

ProcessBarActivity对水平进度条进行了处理。先获取了水平进度条的最大值,然后启动了一个线程,由该线程来控制进度条的值,从0开始,每隔15毫秒增加1。

4.4.11 滚动视图(ScrollView)

当Activity提供的用户界面上有很多内容,以致当前手机屏幕不能完全显示全部内容时,就需要滚动视图来帮助浏览全部的内容。

以工程WidgetDemo为例,由于在讲述过程中不断地在main.xml文件中添加按钮和其他组件,目前已经不能显示全部内容,效果如图4.26所示。

图4.26 添加大量组件后的效果

这时候就需要使用ScrollView,即将当前的Activity的视图转化为滚动视图,以便于浏览。ScrollView的使用非常方便,只需在main.xml的<LinearLayout>标签外面加上ScrollView组件的声明即可。布局文件main.xml的内容如下:

    <ScrollView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
       <LinearLayout>....</LinearLayout>
    </ScrollView>

添加ScrollView后,main.xml布局的运行效果如图4.27所示。

图4.27 ScrollView的运行效果

4.4.12 拖动条(SeekBar)

SeekBar是水平进度条ProgressBar的间接子类,相当于一个可以拖动的水平进度条。下面仍以一个简单的实例讲解SeekBar组件的使用方法。

在工程WidgetDemo的布局文件main.xml中添加一个名为“SeekBarDemo”的Button,用以启动SeekBarActivity。

在main.xml中添加代码如下:

    <Button
        android:id="@+id/button8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="SeekBarDemo" />

单击Button并启动SeekBarActivity的代码如下:

    Button processbtn=(Button)this.findViewById(R.id.button8);
    processbtn.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this, SeekBarActivity.class);
            startActivity(intent);
        }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name="SeekBarActivity"></activity>

SeekBarActivity运行效果如图4.28所示。

图4.28 SeekBarActivity运行效果

SeekBarActivity使用的布局文件为seekbar.xml,其内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">



       <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" />



       <SeekBar
            android:id="@+id/seekBar1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
    android:max="100"/>



    </LinearLayout>

该文件确定SeekBar对象的最大值为100,宽度为手机屏幕的宽度。

SeekBarActivity.java的代码如下:

    package introduction.android.widgetDemo;
     import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.widget.SeekBar;import android.widget.TextView;
     public class SeekBarActivity extends Activity {
        private TextView textView;
        private SeekBar seekBar;
        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.seekbar);
            textView=(TextView)findViewById(R.id.textView1);
            seekBar=(SeekBar)findViewById(R.id.seekBar1);
            /* 设置SeekBar监听setOnSeekBarChangeListener */
            seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
                /* 拖动条停止拖动的时调用 */
                @Override
                public void onStopTrackingTouch(SeekBar seekBar){
                    Log.i("SeekBarActivity", "拖动停止");
                }
                /* 拖动条开始拖动的时调用 */
                @Override
                public void onStartTrackingTouch(SeekBar seekBar){
                    Log.i("SeekBarActivity", "开始拖动");
                }
                /* 拖动条进度改变的时调用 */
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser){
                    /* 拖动条进度改变的时调用 */
                    textView.setText("当前进度为:"+progress+"%");
                }
            });
        }
    }

SeekBar的事件处理接口为OnSeekBarChangeListener,该监听器提供对三种事件的监听,分别为当SeekBar的拖动条开始被拖动时、拖动条拖动停止时和拖动条的位置发生改变时。SeekBarActivity在拖动条开始被拖动和拖动停止时,会通过Logcat打印相关信息。当拖动条位置发生改变时,将当前的数值显示到TextView中。

4.4.13 评价条(RatingBar)

在网上购物的时候,经常会对所购买商品进行打分的情况。一般对商品的评价和打分是以五颗星的方式进行的。Android SDK提供了RatingBar这个组件来实现该功能。

RatingBar是SeekBar和ProgressBar的扩展,是ProgressBar的间接子类,可以使用ProgressBar相关的属性。RatingBar有三种风格分别为:默认风格(ratingBarStyle)、小风格(ratingBarStyleSmall)、大风格(ratingBarStyleIndicator)。其中,默认风格的RatingBar是我们通常使用的,可以进行交互,而其他两种不能进行交互。

以一个简单的实例讲解RatingBar组件的使用方法。在工程WidgetDemo的布局文件main.xml中添加一个名为“RatingBarrDemo”的Button,用以启动RatingBarActivity。

在main.xml中添加代码如下:

    <Button
         android:id="@+id/button9"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="RatingBarDemo" />

单击Button并启动RatingBarActivity的代码如下:

    Button ratingbarbtn=(Button)this.findViewById(R.id.button9);
    ratingbarbtn.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this,RatingBarActivity.class);
            startActivity(intent);
        }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name="RatingBarActivity"></activity>

RatingBarActivity运行效果如图4.29所示。

图4.29 RatingBarActivity运行效果

RatingBarActivity使用的布局文件ratingbar.xml内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">



       <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" />



       <RatingBar
            android:id="@+id/ratingBar1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:numStars="5"
            android:stepSize="0.5"
            android:rating="3"/>



    </LinearLayout>

该布局文件使用LinearLayout布局,其中放置了一个TextView和一个RatingBar,并对RatingBar的相关属性进行了设置。android:numStars="5"用于设置RatingBar显示的星星数量为5颗;android:stepSize="0.5"用于设置RatingBar的最小变化单位为半颗星星;android:rating ="3"表示RatingBar在初始状态下被选中的星星数量为3颗。

RatingBarActivity.java代码如下:

    package introduction.android.widgetDemo;



    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.View.OnTouchListener;
    import android.widget.RatingBar;
    import android.widget.RatingBar.OnRatingBarChangeListener;
    import android.widget.TextView;
    import android.widget.Toast;



    public class RatingBarActivity extends Activity {
        private RatingBar chooseRatingBar;
        private TextView textView;
        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.ratingbar);
            textView=(TextView)findViewById(R.id.textView1);
            chooseRatingBar=(RatingBar)findViewById(R.id.ratingBar1);



                /*创建RatingBar监听器 */
            chooseRatingBar.setOnRatingBarChangeListener(new
    OnRatingBarChangeListener(){



                      @Override
                public void onRatingChanged(RatingBar ratingBar,float rating, boolean fromUser){
                    chooseRatingBar=(RatingBar)findViewById(R.id.ratingBar1);
                    chooseRatingBar.setRating(rating);
                    textView.setText("您选择了"+rating+"个星星");
                }
            });
        }
    }

RatingBarActivity为RatingBar对象设置了OnRatingBarChangeListener监听器,当用户单击RatingBar引起被选中星星数量的变化时,该接口会监测到该事件,并且调用onRatingChanged()方法,更新TextView显示的内容。

onRatingChanged()的三个参数所对应的含义为:


●ratingBar:多个RatingBar可以同时指定同一个RatingBar监听器。该参数就是当前触发RatingBar监听器的那一个RatingBar对象。

●rating:当前评级分数。取值范围从0到RatingBar的总星星数。

●fromUser:如果触发监听器的是来自用户触屏点击或轨迹球左右移动,则为true。

4.4.14 图片视图(ImageView)和图片按钮(ImageButton)

ImageView是用于显示图片的组件,在很多场合都有比较普遍的使用。ImageView可以显示任意图像,加载各种来源的图片(如资源或图片库)。ImageView可以计算图片的尺寸以便在任意的布局中使用,并且可以提供缩放或者着色等选项供开发者使用。

ImageButton是ImageView的子类,相当于一个表明是图片而不是文字的Button。其使用方法和Button完全相同。

下面通过一个实例来了解一下这两个组件的使用方法。在工程WidgetDemo的布局文件main.xml中添加一个名为ImageButtonDemo的Button,用以启动ImageButtonActivity。

在main.xml中添加代码如下:

    <Button
            android:id="@+id/button10"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ImageButtonDemo" />

单击Button并启动RatingBarActivity的代码如下:

    Button imgbtn=(Button)this.findViewById(R.id.button10);
    imgbtn.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this,ImageButtonActivity.class);
            startActivity(intent);
        }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name="ImageButtonActivity"></activity>

ImageButtonActivity运行效果如图4.30所示。

图4.30 ImageButtonActivity运行效果

ImageButtonActivity的布局文件imgbtn.xml内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">



       <ImageView
            android:id="@+id/imageView1"
            android:layout_width="250dp"
            android:layout_height="250dp"
            android:src="@drawable/girl" />



       <ImageButton
            android:id="@+id/imageButton1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher" />



    </LinearLayout>

该文件使用LinearLayout布局,其中放入了一个ImageView组件,一个ImageButton组件。两个组件都通过android:src属性指定了显示的图片。该实例用到了两个图片资源,一个为girl,一个为ic_launcher。由于Android会根据手机设备的配置高低选择不同的资源,因此为了应用程序的通用性,在三个drawable文件夹下,都放置了girl.gif图像。ic_launcher.png是系统自带的资源文件。

图4.31 工程中的图片资源

ImageButtonActivity.java代码如下:

    package introduction.android.widgetDemo;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.ViewGroup.LayoutParams;import android.widget.ImageButton;import android.widget.ImageView;public class ImageButtonActivity extends Activity {
        private ImageButton imgbtn;
        private ImageView imgview;
        @Override
        protected void onCreate(Bundle savedInstanceState){
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.imgbtn);
            imgbtn=(ImageButton)this.findViewById(R.id.imageButton1);
            imgview=(ImageView)this.findViewById(R.id.imageView1);
            imgbtn.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    // TODO Auto-generated method stub
                    LayoutParams params=imgview.getLayoutParams();
                    params.height+=3;
                    params.width+=3;
                    imgview.setLayoutParams(params);
                }
            });
        }
    }

ImageButtonActivity为ImageButton添加了单击监听器,对用户单击imgbtn的事件进行了处理。每次用户单击图片按钮,都把ImageView组件的宽和高增大3。随着用户的不断单击,ImageView中显示的图片越来越大,显示了ImageView组件对图片的缩放功能。

4.4.15 图片切换器ImageSwitcher和图库Gallery

在使用Android手机设置壁纸的时候,会看到屏幕底部有很多可以滚动的图片,当单击某一图片时,在其上面的空间会显示当前选中的图片,此时我们用到的就是Gallery和ImageSwitcher。

Gallery组件用于横向显示图像列表,并且自动将当前图像放置到中间位置。ImageSwitcher则像是图片浏览器,可以切换图片,通过它可以制作简单的幻灯片等等。通常将这两个类结合在一起使用,可以制作有一定效果的相册。

下面通过一个实例来了解一下这两个组件的使用方法。

在工程WidgetDemo的布局文件main.xml中添加一个名为GalleryDemo的Button,用以启动GalleryActivity。在main.xml中添加代码如下:

    <Button
        android:id="@+id/button11"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GalleryDemo" />

单击Button并启动GalleryActivity的代码如下:

    Button gallerybtn=(Button)this.findViewById(R.id.button11);
    gallerybtn.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this,GalleryActivity.class);
                startActivity(intent);
            }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name="GalleryActivity"></activity>

GalleryActivity运行效果如图4.32所示。

图4.32 GalleryActivity运行效果

GalleryActivity使用的布局文件为gallery.xml,内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical">
       <ImageSwitcher
            android:id="@+id/switcher"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true">
       </ImageSwitcher>
    <Gallery
         android:id="@+id/gallery"
         android:background="#333333"
         android:layout_width="fill_parent"
         android:layout_height="60dp"
         android:layout_alignParentBottom="true"
         android:layout_alignParentLeft="true"
         android:gravity="center_vertical"
         android:spacing="16dp" />
    </RelativeLayout>

该布局文件使用的是相对布局,通过android:layout_alignParentTop="true"这个属性将ImageSwitcher放置于视图的顶端,其顶部与其父组件的顶部对齐,同时使用android:layout_alignParentLeft="true"这个属性使ImageSwitcher的左边缘与其父组件的左边缘对齐。在设置Gallery组件时,将其与屏幕的左下角对其,android:layout_alignParentBottom="true"是将该组件的底部与其父组件的底部对齐,并且使用android:spacing="16dp"属性设置了图片之间的间距。

GalleryActivity.java代码如下:

    package introduction.android.widgetDemo;
     import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.view.View;import android.view.ViewGroup;import android.view.ViewGroup.LayoutParams;import android.view.animation.AnimationUtils;import android.widget.AdapterView;import android.widget.AdapterView.OnItemSelectedListener;import android.widget.BaseAdapter;import android.widget.Gallery;import android.widget.ImageSwitcher;import android.widget.ImageView;import android.widget.ViewSwitcher.ViewFactory;
     public class GalleryActivity extends Activity {
        private Gallery gallery;
        private ImageSwitcher imageSwitcher;
        private int[] resids=new int[] {
              R.drawable.sample_0, R.drawable.sample_1,
                R.drawable.sample_2, R.drawable.sample_3,
                R.drawable.sample_4, R.drawable.sample_5,
                R.drawable.sample_6, R.drawable.sample_7};
        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.gallery);
            /* 加载Gallery和ImageSwitcher */
            gallery=(Gallery)findViewById(R.id.gallery);
            imageSwitcher=(ImageSwitcher)findViewById(R.id.switcher);
            /* 创建用于描述图像数据的ImageAdapter对象 */
            ImageAdapter imageAdapter=new ImageAdapter(this);
            /* 设置Gallery组件的Adapter对象 */
            gallery.setAdapter(imageAdapter);
            /* 添加Gallery监听器 */
            gallery.setOnItemSelectedListener(new OnItemSelectedListener(){



                @Override
                public void onItemSelected(AdapterView<?>parent, View view,
                        int position, long id){
                    // TODO Auto-generated method stub
                    // 当选取Grallery上的图片时,在ImageSwitcher组件中显示该图像
                    imageSwitcher.setImageResource(resids[position]);
                }



                @Override
                public void onNothingSelected(AdapterView<?>arg0){
                    // TODO Auto-generated method stub
                }



            });
            /* 设置ImageSwitcher组件的工厂对象 */
            imageSwitcher.setFactory(new ViewFactory(){
                /* ImageSwitcher用这个方法来创建一个View对象去显示图片 */
                @Override
                public View makeView(){
                    // TODO Auto-generated method stub
                    ImageView imageView=new ImageView(GalleryActivity.this);
                    /* setScaleType可以设置当图片大小和容器大小不匹配时的剪辑模式 */
                    imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
                    imageView.setLayoutParams(new ImageSwitcher.LayoutParams(
                            LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
                    return imageView;
                }



            });
            /* 设置ImageSwitcher组件显示图像的动画效果 */
            imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(this,
                    android.R.anim.fade_in));
            imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this,
                    android.R.anim.fade_out));
        }



        public class ImageAdapter extends BaseAdapter {
            /* 定义Context */
            private Context mContext;



            /* 声明ImageAdapter */
            public ImageAdapter(Context context){
                mContext=context;
            }



            @Override
            /* 获取图片的个数 */
            public int getCount(){
                // TODO Auto-generated method stub
                return resids.length;
            }



            /* 获取图片在库中的位置 */
            @Override
            public Object getItem(int position){
                // TODO Auto-generated method stub
                return position;
            }



            /* 获取图片ID */
            @Override
            public long getItemId(int position){
                // TODO Auto-generated method stub
                return position;
            }



            /* 返回具体位置的ImageView对象 */
            @Override
            public View getView(int position, View convertView, ViewGroup parent){
                ImageView imageview=new ImageView(mContext);
                /* 给ImageView设置资源 */
                imageview.setImageResource(resids[position]);
                /* 设置 图片布局大小为100*100 */
                imageview.setLayoutParams(new Gallery.LayoutParams(100, 100));
                /* 设置显示比例类型 */
                imageview.setScaleType(ImageView.ScaleType.FIT_XY);
                return imageview;
            }
        }
    }

Gallery要显示的图片来自资源文件。把需要显示的图片放在/res/drawable目录下后,将这些图片的ID保存在一个int数组中以备使用。相关代码如下:

    private int[] resids=new int[] {
        R.drawable.sample_0, R.drawable.sample_1,
          R.drawable.sample_2, R.drawable.sample_3,
          R.drawable.sample_4, R.drawable.sample_5,
          R.drawable.sample_6, R.drawable.sample_7};

Gallery通过setAdapter(imageAdapter)方法将组件和要显示的图片关联起来。本实例中为Gallery设定的适配器为ImageAdapter,主要用于描述图像信息,其为android.widget. BaseAdapter的子类。

在ImageAdapter类中有两个方法值得我们注意,其中一个是getCount()方法,它用于返回图片的总数,通常使用获取存放图片数组长度的方法获取图片总数,也可以规定具体的返回数,但不能超过实际图片数量;getView()方法是当Gallery中需要显示某一个图像时,将当前图片的索引,也就是position的值传入,从resids数组中获得相应的图片的ID。

GalleryActivity为添加Gallery监听器,处理了用户单击Gallery中图片的事件,并设置ImageSwitcher相关属性。其中代码如下:

    imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(this,
                    android.R.anim.fade_in));



    imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this,
                    android.R.anim.fade_out));

设置了ImageSwitcher组件图片切换时的渐入和渐出效果。

4.4.16 网格视图(GridView)

GridView提供了一个二维的可滚动的网格,按照行列的方式来显示内容,一般适合显示图标、图片等,适合浏览。

下面通过一个实例来了解一下GridView组件的使用方法。在工程WidgetDemo的布局文件main.xml中添加一个名为GridViewDemo的Button,用以启动GridViewActivity。

在main.xml中添加代码如下:

    <Button
        android:id="@+id/button12"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GridViewDemo" />

单击Button并启动GridViewActivity的代码如下:

    Button gridviewbtn=(Button)this.findViewById(R.id.button12);
    gridviewbtn.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v){
            // TODO Auto-generated method stub
            Intent intent=new Intent(WidgetDemoActivity.this,GridViewActivity.class);
                    startActivity(intent);
        }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name="GridViewActivity"></activity>

GridViewActivity运行效果如图4.33所示。

图4.33 GridViewActivity运行效果

GridViewActivity使用的布局文件为gridview.xml,其内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">



       <GridView
            android:id="@+id/gridView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:numColumns="3">
       </GridView>



    </LinearLayout>

该视图采用了LinearLayout的布局方式,其中放置了一个GridView组件,该组件由三列组成。

GridViewActivity.java代码如下:

    package introduction.android.widgetDemo;
     import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.BaseAdapter;import android.widget.GridView;import android.widget.ImageView;
     public class GridViewActivity extends Activity {
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.gridview);



            GridView gridview=(GridView)findViewById(R.id.gridView1);
            gridview.setAdapter(new ImageAdapter(this));



            gridview.setOnItemClickListener(new OnItemClickListener(){
                public void onItemClick(AdapterView<?>parent, View v,
                        int position, long id){
                    Log.i("gridview", "这是第"+position+"幅图像。");
                }
            });
        }



        public class ImageAdapter extends BaseAdapter {
            private Context mContext;



            public ImageAdapter(Context c){
                mContext=c;
            }



            /* 获取当前图片数量 */
            @Override
            public int getCount(){
                return mThumbIds.length;
            }



            /* 根据需要position获得在GridView中的对象 */
            @Override
            public Object getItem(int position){
                return position;
            }



            /* 获得在GridView中对象的ID */
            @Override
            public long getItemId(int id){
                return id;
            }



            @Override
            public View getView(int position, View convertView, ViewGroup parent){
                ImageView imageView;
                if(convertView==null){
                    /* 实例化ImageView对象 */
                    imageView=new ImageView(mContext);
                    /* 设置ImageView对象布局 ,设置View的height和width */
                    imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
                    /* 设置边界对齐 */
                    imageView.setAdjustViewBounds(false);
                    /* 按比例统一缩放图片(保持图片的尺寸比例)*/
                    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                    /* 设置间距 */
                    imageView.setPadding(8, 8, 8, 8);
                } else {
                    imageView=(ImageView)convertView;
                }
                imageView.setImageResource(mThumbIds[position]);
                return imageView;
            }
        }



        // references to our images
        private Integer[] mThumbIds={ R.drawable.sample_2, R.drawable.sample_3,
                R.drawable.sample_4, R.drawable.sample_5, R.drawable.sample_6,
                R.drawable.sample_7, R.drawable.sample_0, R.drawable.sample_1,
                R.drawable.sample_2, R.drawable.sample_3, R.drawable.sample_4,
                R.drawable.sample_5, R.drawable.sample_6, R.drawable.sample_7,
                R.drawable.sample_0, R.drawable.sample_1, R.drawable.sample_2,
                R.drawable.sample_3, R.drawable.sample_4, R.drawable.sample_5,
                R.drawable.sample_6, R.drawable.sample_7 };
    }

在主程序GridViewActivity中,为GridView设置了一个数据适配器,并处理了GridView的单击事件。适配器继承自BaseAdapter类,与上章节中用到的适配器高度相似,在此不再重复。

4.4.17 标签(Tab)

在有限的手机屏幕空间内,当要浏览的内容较多,无法在一个屏幕空间内全部显示时,可以使用滚动视图来延长屏幕的空间。当浏览的内容具有很强的类别性质时,更合适的方法是将不同类别的内容集中到各自的面板中,这时就需要使用面板Tab组件了。

Tab组件利用面板标签把不同的面板内容切换到屏幕上,以显示不同类别的内容。

下面通过一个实例来了解一下Tab组件的使用方法。在工程WidgetDemo的布局文件main.xml中添加一个名为TabDemo的Button,用以启动TabActivity。

在main.xml中添加代码如下:

    <Button
        android:id="@+id/button13"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TabDemo" />

单击Button并启动GridViewActivity的代码如下:

    Button tabbtn=(Button)this.findViewById(R.id.button13);
    tabbtn.setOnClickListener(new OnClickListener(){
    @Overridepublic void onClick(View v){
           // TODO Auto-generated method stub
        Intent intent=new Intent(WidgetDemoActivity.this,TabActivity.class);
           startActivity(intent);
    }
    });

同时在AndroidManifest.xml文件中声明该Activity:

    <activity android:name="TabActivity"></activity>

TabActivity运行效果如图4.34所示。

图4.34 TabActivity运行效果

要使用Tab必然涉及它的容器TabHost,TabHost包括TabWigget和FrameLayout两部分。TabWidget就是每个Tab的标签,FrameLayout是Tab的内容。

TabActivity使用的布局文件是tab.xml。在tab.xml中定义了每个Tab中要显示的内容,代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <TabHost xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/tabhost"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
       <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical">
           <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
           <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent">
               <TextView
                    android:id="@+id/tab1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="40dp"
                    android:text="Tab1页面" />
               <TextView
                    android:id="@+id/tab2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="40dp"
                    android:text="Tab2页面" />
               <TextView
                    android:id="@+id/tab3"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="40dp"
                    android:text="Tab3页面" />
           </FrameLayout>
       </LinearLayout>
    </TabHost>

在FrameLayout中我们放置了三个TextView组件,分别对应三个Tab所显示的内容,当切换不同的Tab时会自动显示不同的TextView内容。

在主程序TabActivity的OnCreate()方法中,首先获得了TabHost的对象,并调用setup()方法进行初始化,然后通过TabHost.TabSpec增加Tab页,通过setContent()增加当前Tab页显示的内容,通过setIndicator增加页的标签,最后设定当前要显示的Tab页。

TabActivity代码如下:

    package introduction.android.widgetDemo;



    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TabHost;



    public class TabsActivity extends Activity {
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.tab);
            // 步骤1:获得TabHost的对象,并进行初始化setup()
            TabHost tabs=(TabHost)findViewById(R.id.tabhost);
            tabs.setup();
            // 步骤2:通过TabHost.TabSpec增加tab的一页,通过setContent()增加内容,通过setIndicator增加页的标签
            /* 增加第1个Tab */
            TabHost.TabSpec spec=tabs.newTabSpec("Tag1");
            // 单击Tab要显示的内容
            spec.setContent(R.id.tab1);
            /* 显示Tab3内容 */
            spec.setIndicator("Tab1");
            tabs.addTab(spec);
            /* 增加第2个Tab */
            spec=tabs.newTabSpec("Tag2");
            spec.setContent(R.id.tab2);// 单击Tab要显示的内容
            /* 显示Tab3内容 */
            spec.setIndicator("Tab2");
            tabs.addTab(spec);
            /* 增加第3个Tab */
            spec=tabs.newTabSpec("Tag3");
            spec.setContent(R.id.tab3);// 单击Tab要显示的内容
            /* 显示Tab3内容 */
            spec.setIndicator("Tab3");
            tabs.addTab(spec);
            /* 步骤3:可通过setCurrentTab(index)指定显示的页,从0开始计算。 */
            tabs.setCurrentTab(0);
        }
    }

除了使用上述方法设置Tab页面的显示内容外,还可以使用setContent(Intent)方法启动某个Activity,并将该Activity的视图作为Tab页面的内容。

例如:

    Intent intent=new Intent().setClass(this, AlbumsActivity.class);
    spec=tabHost.newTabSpec("albums").setIndicator("Albums",
                          res.getDrawable(R.drawable.ic_tab_albums))
                      .setContent(intent);
    tabHost.addTab(spec);