刚刚接触Android开发时,Activiy、View、ViewGroup、Layout、Intent等等名称全部爆发,一时无从下手。
一、关于名词
- Activity:Activity即可视为一个窗口。在任何时候,一个应用程序只能有一个活动的Activity,此情景与模式对话框类似。
- View:界面的基本元素,包括图片显示框(ImageView)、文本显示框(TextView)、按钮(Button)等元素。
- ViewGroup:界面元素的组合,是所有Layout类的基类。
- Layout:界面的布置方案。界面上所有的元素均对应在一个XML文件中,保存在res/layout/目录下。该文件指明各个界面元素(View)是按照什么方式布置在界面上的什么位置的。具体有线性布局(Linear Layout)、相对布局(Relative Layout)、表格布局(Table Layout)、网格视图(Grid View)、标签布局(Tab Layout)、列表视图(List View)、绝对布局(AbsoluteLayout)等。
- Intent:用以在Activity之间传递数据(暂时的了解)。一个程序只能有一个活动的Activity,在一个Activity里面启动另一个Activity的方法即是使用Intent来完成。
二、关于Activity
一个Activity要与一个Layout对应。Acitivity里面包含界面逻辑,而Layout包含界面元素的位置。
建立DemoAcitivity的过程如下:
1. 在res/layout/下创建一个Layout,命名为demo_layout.xml,使用任何一种合适的布局,将界面元素按照需要布置在界面中。
2. 创建DemoActivity的代码,继承自Activity类。重写onCreate方法,在onCreate方法中调用
setContentView(R.layout.demo_layout);
这样,DemoActivity启动的时候,会查找并使用demo_layout来作为其显示的界面。
在多个界面中切换
为了在多个界面中切换程序,可以使用两种方法:
1. 使用Intent切换不同的Activity;
2. 使用setContentView切换不同的Layout;
二者各有好处。其中,
使用Intent切换Activity,各界面的逻辑包含在不同的类代码中,结构清晰。其缺点是Activity切换动作较大,消耗较多。同时,如果需要在不同的窗口之间传递数据,需要使用Intent来完成。
使用setContentView切换Layout,所有的逻辑以及数据均保存在同一个Activity类中,不需要进行数据传递。缺点是不同窗口的逻辑代码可能会混杂在一起。
切换方法1 : 使用Intent切换Activity
另一个类的实现代码与此类似,只是把Activity01与Activity02相互交换。首先在layout里建2个xml文件,分别对应两个不同的Activity的界面。每个界面上均有一个按钮,通过点击切换到另一个Activity。
public class Activity01 extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* 设置显示layout01.xml布局 */ setContentView(R.layout.layout01); /* findViewById(R.id.button1)取得布局layout01.xml中的button1 */ Button button = (Button) findViewById(R.id.button1); /* 监听button的事件信息 */ button.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { /* 新建一个Intent对象,并指定要启动的类 */ Intent intent = new Intent(Activity01.this, Activity02.class); /* 启动一个新的Activity */ startActivity(intent); /* 关闭当前的Activity */ Activity01.this.finish(); } }); } }
需要注意的是,这两个Activity都必须在AndroidManifest.xml文件中声明,并指定一个默认的启动类。典型的声明如下:
... <application> <activity android:name=".Activity01"> <intent-filter> <action android:name="android.intent.aciton.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".Activity02"></activity> </application> ...
如果要传递数据,可以使用Intent的putExtra来添加信息,然后再启动目标Activity。例如,要传递一个名字为“greeting”的字符串“How do you do”,使用方法是:
intent.putExtra("greeting","How do you do");
而在被启动的Activity中,使用Intent的getExtra来得到消息,如:传递其它的信息类似,只是get的时候需要知道信息的类型,如getBooleanExtra等等。Intent intent = getIntent(); String msg = intent.getStringExtra("greeting");
切换方法2:使用setContentView切换Layout
一个Activity可以对应多个Layout,可以根据需要在不同的时间显示不同的Layout来达到切换界面的目的。这样,不需要多个Activity就可以显示不同的界面,也不再需要在Activity间传送数据,变量可以直接引用。
如果使用setContentView(int layoutResID)方法切换Layout,在切换后再切换回,程序相当于重新显示一个界面,并非是把原来的界面隐藏后再显示。我们可以先用LayoutInflater把布局xml文件引入成View对象,再通过setContentView(View view)方法来切换视图。因为所有对View的修改都保存在View对象里,所以,当切换回原来的布局时,就可以直接显示原来修改后的样子。
例如有如下的两个Layout:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/tv1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Some Message" /> <Button android:id="@+id/b1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="切换到layout2"/> <Button android:id="@+id/b3" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="修改tv1"/> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:id="@+id/b2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="切换到layout1"/> </LinearLayout>
在一个Activity中使用这两个Layout的方法如下:
import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class Main extends Activity { /** Called when the activity is first created. */ TextView tv1, tv2; Button b1, b2,b3; View layout1, layout2; boolean view2Load = false;//main2是否载入过的flag @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LayoutInflater inflater = LayoutInflater.from(this); layout1 = inflater.inflate(R.layout.layout1, null); layout2 = inflater.inflate(R.layout.layout2, null); setView1(); tv1 = (TextView) findViewById(R.id.tv1); b1 = (Button) findViewById(R.id.b1); b3 = (Button) findViewById(R.id.b3); b3.setOnClickListener(l3); b1.setOnClickListener(l1); //控件及监听器只需一次查找绑定,切换view不影响 } private void setView1() { setContentView(layout1); } private void setView2() { setContentView(layout2); setView2Btn(); } private void setView2Btn(){ if(!view2Load){ //如果首次显示layout2,查找控件并绑定监听器 b2 = (Button) findViewById(R.id.b2); b2.setOnClickListener(l2); view2Load=true;//flag设为true } } OnClickListener l1 = new OnClickListener() { @Override public void onClick(View v) { setView2(); } }; OnClickListener l2 = new OnClickListener() { @Override public void onClick(View v) { setView1(); } }; OnClickListener l3 = new OnClickListener() { @Override public void onClick(View v) { tv1.setText("Another Message"); //修改tv1的值,切换到layout2后再切换回来到layout1,可以发现tv1的值被保存了 } }; }
Tips: android:layout_weight="1"可以在LinearLayout中表示某元素的权重,默认都为”0“。在默认情况下,如果屏幕排不下所有的元素,则屏幕外面的将会丢失。设置了权重值之后,系统将自动为各个元素分配其权重值对应的空间,数值大小表示其权重大小。
SplashForm的效果
有这两种切换方式,想要做到自动切换,达到SplashForm的效果也很简单了。以第一种切换方式为例,将Activity01画成SplashForm,AndroidManiefest.xml文件保证了Activity01是最先显示出来的。接着,将Activity01的实现代码改成:
public class Activity01 extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* 设置显示layout01.xml布局 */ setContentView(R.layout.layout01); final int nWelcomeScreenDisplay = 3000; //默认3秒 new Handler().postDelayed(new Runnable() { @Override public void run() { Intent intent = new Intent(Activity01.this, Activity02.class); startActivity(intent); Activity01.this.finish(); } }, nWelcomeScreenDisplay); } }
即可在3秒之后,自动切换到Activity02显示。
使用切换Layout的方式来完成SplashForm与此类似,只是将切换Activity的代码改成切换Layout的代码即可。
三、关于ListView和ListAdapter
四、关于GridView和ListAdapterListView中将要显示的数据由ListAdapter来提供。ListAdapter分为三种:使用ArrayAdapter显示一个数组形式的数据;SimpleAdapter能定义各种各样的布局出来,可以放上ImageView(图片)、Button(按钮)、CheckBox(复选框)等等;SimpleCursorAdapter用以显示数据库的内容。
1. ArrayAdapter最简单,只能显示一行文字,示例:
public class Activity01 extends Activity { private ListView listView; private String[] data = {"a","b","c","d"}; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listView = new ListView(this); listView.setAdapter(new ArrayAdapter<String>(this, R.layout.list_item,getData())); setContentView(listView); } }
需要注意的是,在使用ListView时,不管用哪种Adapter,res/layout/下的list_item.xml文件都只需要指明将要显示的ListView中一个元素的布局格式。比如上例,list_item.xml文件只需要给出每一个字符串该怎么显示,而不用管整个ListView该如何布局。
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="16sp" />
ListView在开始绘制的时候,系统首先调用getCount()函数,根据其返回值得到ListView的长度。然后根据这个长度,调用getView()得到每一行的元素,使用list_item.xml里面设置的布局方式逐一绘制每一行。如果getCount()返回值是0的话,列表将不显示。
2. SimpleCursorAdapter中Cursor的意思是游标,与MySQL数据库编程中的游标相同。顾名思义,SimpleCursorAdapter用以将数据库中游标处的数据显示在ListView中。
比如,要开发一个应用程序,以列表的形式显示通讯录数据库中的所有人的姓名。首先定义如下的布局文件simple_cursor_list_item.xml,指明每一个姓名的显示方式:
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="16sp" />
创建SimpleCursorAdapter来获取数据,并显示在ListView中:
public class Activity01 extends Activity { private ListView listView; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listView = new ListView(this); Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); startManagingCursor(cursor); ListAdapter listAdapter = new SimpleCursorAdapter(this, R.layout.simple_cursor_list_item, cursor, new String[]{ContactsContract.Contacts.DISPLAY_NAME}, new int[]{R.id.name}); listView.setAdapter(listAdapter); setContentView(listView); } }
如上例,创建一个Cursor对象,将其指向系统的通讯录数据库。然后调用startManagingCursor,使用Activity来管理该Cursor。创建SimpleCursorAdapter时,前三个参数与ArrayAdapter相同,第四个参数是数据库中将要显示的列的名称所构成的数组,第五个参数指明数据将映射到哪个组件上。第四、第五个参数的长度需要与布局文件中每一行的元素的个数相同。
该例子中还有个要注意的,因为需要访问通讯录,必须在AndroidManifest.xml文件中对该应用进行授权:
<activity android:name=".Activity01"> ... <uses-permission android:name="android.permission.READ_CONTACTS" /> ... </activity>
3. SimpleAdapter
在SimpleAdapter中,列表的每一行可以显示多个元素。使用SimpleAdapter的数据用一般都是HashMap构成的List,List的每个元素对应ListView的每一行。HashMap的每个键值数据映射到布局文件中对应id的组件上。
SimpleAdapter构造函数的参数依次是:this、数据列表、布局文件ID,HashMap的键值数组、布局文件中对应组件的ID数组。以一个图片加一个字符串为例:
布局文件simple_list_item.xml如下:
该布局文件表示,每一行包含一张图片和一段文字,二者以水平方向线性排列。<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5px"/> <TextView android:id="@+id/description" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="16sp" /> </LinearLayout>
对于的Activity如下:
public class Activity01 extends Activity { private ListView listView; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listView = new ListView(this); ListAdapter listAdapter = new SimpleAdapter(this, getData(), R.layout.simple_list_item, new String[]{"img","description"}, new int[]{R.id.img, R.id.description}); listView.setAdapter(listAdapter); setContentView(listView); } private List< Map<String, Object> > getData() { List< Map<String, Object> > list = new ArrayList< Map<String, Object> >(); Map<String, Object> map = new HashMap<String, Object>(); map.put("img", R.drawable.img01); map.put("description", R.string.des01); list.add(map); map = new HashMap<String, Object>(); map.put("img", R.drawable.img02); map.put("description", R.string.des02); list.add(map); return list; } }
同时,在res/drawable/下放入将要显示的图片,img01.png和img02.png,在res/values/string.xml中,加入将要显示的文字:
<resources> ... <string name="des01">Description 01</string> <string name="des02">Description 02</string> ... </resources>
4.比SimpleAdapter稍微复杂一点的Adapter
如果需要在每一行加上一个可以相应的按钮,则稍微复杂一点。此时,需要自己写一个Adapter类,继承自BaseAdapter。以一个图片一个按钮为例:
布局文件complex_list_item.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5px"/> <Button android:id="@+id/btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/btn_text" /> </LinearLayout>
对应的代码如下:
public class Activity01 extends Activity { private List<Map<String, Object>> mData; private ListView listView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listView = new ListView(this); mData = getData(); ComplexAdapter adapter = new ComplexAdapter(this); listView.setListAdapter(adapter); setContentView(listView); } private List< Map<String, Object> > getData() { List< Map<String, Object> > list = new ArrayList< Map<String, Object> >(); Map<String, Object> map = new HashMap<String, Object>(); map.put("img", R.drawable.img01); list.add(map); map = new HashMap<String, Object>(); map.put("img", R.drawable.img02); list.add(map); return list; } public final class ViewHolder{ public ImageView img; public Button btn; } public class ComplexAdapter extends BaseAdapter{ private LayoutInflater mInflater; public ComplexAdapter(Context context){ this.mInflater = LayoutInflater.from(context); } @Override public int getCount() { return mData.size();} @Override public Object getItem(int arg0) { return null;} @Override public long getItemId(int arg0) { return 0;} @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.complex_list_item, null); holder.img = (ImageView)convertView.findViewById(R.id.img); holder.btn = (Button)convertView.findViewById(R.id.btn); convertView.setTag(holder); }else { holder = (ViewHolder)convertView.getTag(); } holder.img.setBackgroundResource((Integer)mData.get(position).get("img")); holder.btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //On Click Logic... } }); return convertView; } } }
GridView即网格布局,与ListView一样,使用ListAdapter来提供数据。以GridView结合SimpleAdapter为例:
首先,在grid_layout.xml中定义整个GridView的布局:
在grid_item.xml中定义GridView中各个元素的布局:<?xml version="1.0" encoding="utf-8"?> <GridView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/grid" android:layout_width="wrap_content" android:layout_height="wrap_content" android:verticalSpacing="10dp" android:horizontalSpacing="10dp" android:numColumns="auto_fit" android:columnWidth="90dp" android:stretchMode="columnWidth"> </GridView>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical"> <ImageView android:id="@+id/griditem_pic" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" /> <TextView android:id="@+id/griditem_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" /> </LinearLayout>
如grid_item.xml所示,网格中的每一个元素包括一张图片和一段文件,二者纵向排列。Activity的代码如下:
public class GridActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.grid_layout); GridView gridView = (GridView) findViewById(R.id.grid_layout); List< Map<String, Object> > contents = new ArrayList< Map<String, Object> >(); for (int i = 0; i < 10; i++) { Map<String, Object> map = new HashMap<String, Object>(); map.put("img", R.drawable.img); map.put("title", "Image Title"); contents.add(map); } SimpleAdapter adapter = new SimpleAdapter(this, contents, R.layout.grid_item, new String[] { "img", "title" }, new int[] { R.id.griditem_pic,R.id.griditem_title,}); gridView.setAdapter(adapter); } }
最后,在res/drawable/下放入将要显示的图片,img.png。