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