http://www.iteye.com/topic/40970
为了适应Handset和Tablet等不同分辨率的android设备,google在android3.0之后提供了一个新的API,也就是Fragment,大家可以查阅官方SDK的详细说明。
以下是摘自官方SDK 的一张设计图,很好地展示了Fragment在兼容Tablet和Handset设备的设计理念。
首先解释一下,上述的设计原理。
1.针对Tablet,Activity A中包含了Fragment A和Fragment B,而Handset中的Activity A中只包含了Fragment A,至于Fragment B则通过对Fragment A的事件监听,来启动新的Activity B(包含Fragment B)将Fragment B显示给用户。
既然这样的话就必须用到两个布局,layout目录下默认存放的是Handset的布局,layout-large目录下存放的则是Tablet的布局。
更详细的请参考:http://android-developers.blogspot.com/2011/07/new-tools-for-managing-screen-sizes.html
2.接着看如何在布局中定义Fragment
参考一下配置文件:
<?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="horizontal" > <fragment android:id="@+id/item" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="3" /> <fragment android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
3.系统内置了3种Fragment
DialogFragment:对话框式Fragment,管理模式类似与AlertDialog等对话框,允许用户返回之前的Fragment。
ListFragment:类似于ListActivity,并且提供了类似的功能,用法基本差不多。
PreferenceFragment:类似于PreferenceActivity,用法也差不多,可用来创建类似ipad的设置界面。
这里我们使用ListFragment来显示项目栏,内容栏通过Fragment来创建自定义布局。
直接上代码:
ItemActivity.java
package com.test.fragment; import com.test.fragment.R; import android.app.ListFragment; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class ItemActivity extends ListFragment{ private ItemAdapter adapter; private ImageView mLastIndicate = null; private Context mContext = null; private String [] mArr = null; private static OnItemChangeListener onItemChangeListener; public interface OnItemChangeListener{ void onItemChange(int position); } @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); mContext = getActivity(); mArr = new String[]{"item1","item2","item3"}; adapter = new ItemAdapter(); } @Override public void onActivityCreated(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onActivityCreated(savedInstanceState); setListAdapter(adapter); } @Override public void onListItemClick(ListView l, View v, int position, long id) { // TODO Auto-generated method stub super.onListItemClick(l, v, position, id); if(mLastIndicate != null){ mLastIndicate.setVisibility(View.GONE); } ImageView imageView = (ImageView)v.findViewById(R.id.item_indicate); imageView.setVisibility(View.VISIBLE); mLastIndicate = imageView; onItemChangeListener.onItemChange(position); } public static void setItemChangeListener(OnItemChangeListener l){ onItemChangeListener = l; } public class ItemAdapter extends BaseAdapter{ private ViewHolder mHolder = null; public ItemAdapter(){ } public int getCount() { // TODO Auto-generated method stub return mArr.length; } public Object getItem(int position) { // TODO Auto-generated method stub return position; } public long getItemId(int position) { // TODO Auto-generated method stub return position; } public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub if(convertView == null){ LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.item_layout, parent, false); mHolder = new ViewHolder(); mHolder.item_tv = (TextView)convertView.findViewById(R.id.item_tv); mHolder.item_indicate = (ImageView)convertView.findViewById(R.id.item_indicate); convertView.setTag(mHolder); }else{ mHolder = (ViewHolder)convertView.getTag(); } if(mLastIndicate == null){ if(position == 0){ mHolder.item_indicate.setVisibility(View.VISIBLE); mLastIndicate = mHolder.item_indicate; } } mHolder.item_tv.setText(mArr[position]); return convertView; } class ViewHolder{ public TextView item_tv; public ImageView item_indicate; } } }
在ItemActivity中设置onItemChangeListener监听器接口,在Item改变的时候,让MainActivity通过接口来刷新界面。
ContentActivity.java
package com.test.fragment; import com.test.fragment.R; import android.app.Fragment; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; public class ContentActivity extends Fragment { private String [] arr = null; private String [] arr0 = {"item1:","item1:","item1:","item1:","item1:"}; private String [] arr1 = {"item2:","item2:","item2:","item2:","item2:"}; private String [] arr2 = {"item3:","item3:","item3:","item3:","item3:"}; private ContentAdapter adapter = null; private ListView lv = null; private Context mContext = null; @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); Bundle bundle = getActivity().getIntent().getExtras(); int position = 0; if(bundle == null){ position = 0; }else{ position = bundle.getInt(MainActivity.KEY_POSITION); } switch(position){ case 0: arr = arr0; break; case 1: arr = arr1; break; case 2: arr = arr2; break; } mContext = getActivity(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // TODO Auto-generated method stub View view = inflater.inflate(R.layout.content_layout, container, false); lv = (ListView)view.findViewById(R.id.contentList); adapter = new ContentAdapter(); lv.setAdapter(adapter); return view; } public void onUpdateContent(int position){ switch(position){ case 0: arr = arr0; break; case 1: arr = arr1; break; case 2: arr = arr2; break; } adapter.notifyDataSetChanged(); } class ContentAdapter extends BaseAdapter{ private ViewHolder mHolder; public ContentAdapter(){ super(); } public int getCount() { // TODO Auto-generated method stub return arr.length; } public Object getItem(int position) { // TODO Auto-generated method stub return position; } public long getItemId(int position) { // TODO Auto-generated method stub return position; } public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub if(convertView == null){ LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.content_item_layout, parent, false); mHolder = new ViewHolder(); mHolder.content_tv = (TextView)convertView.findViewById(R.id.content_tv); convertView.setTag(mHolder); }else{ mHolder = (ViewHolder)convertView.getTag(); } mHolder.content_tv.setText(arr[position]); return convertView; } class ViewHolder{ public TextView content_tv; } } }
MainActivity.java
package com.test.fragment; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import com.test.fragment.ItemActivity.OnItemChangeListener; import com.test.fragment.R; public class MainActivity extends Activity implements OnItemChangeListener{ private ContentActivity contentFragment = null; // private ItemActivity itemFragment = null; public static final String KEY_POSITION = "position"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); contentFragment = (ContentActivity)getFragmentManager().findFragmentById(R.id.content); ItemActivity.setItemChangeListener(this); } public void onItemChange(int position) { // TODO Auto-generated method stub if(contentFragment == null){ Intent intent = new Intent(this,Content.class); Bundle bundle = new Bundle(); bundle.putInt(KEY_POSITION, position); intent.putExtras(bundle); startActivity(intent); }else{ contentFragment.onUpdateContent(position); } } }
MainActivity的实现了OnItemChangeListener监听器接口,那么首先就要在onCreate的时候将监听器注册到ItemActivity中,在实现的方法中分别针对Tablet和Handset设备做了不同的处理。
欲知更多fragment的使用技巧,请参考官方SDK。
代码下载,请看附件。
因为以前自己翻译的api笔记十分杂乱,所以决定以后有空每天用半小时至一小时来整理、翻译api,当然由于本人英语水平十分初级(堪堪过了四级),所以仅作笔记之用,供自己看看,免去整理笔记之烦恼。
Application Fundamentals
Android程序是基于java程序语言的。编译java代码包括任何的数据、资源是通过appt工具来实现的。在很多情况中,每个Android程序生存在它自己的世界中:
默认情况下,每个程序在他自己的Linux进程中执行。每个进程都有他自己的虚拟机,所以程序代码运行在一个与其他程序相互隔离的环境中。
默认情况下,每个程序分配有一个唯一的Linux用户ID,这使得程序资源只能是自身可见的,——当然,也可以通过其他方法来使其他程序可以访问本程序的资源。
将两个程序安排在一个linux用户ID中是可能的(非默认),这样他们就能够访问各自的资源,为了节约系统资源,在同一个Linux用户ID中的程序也可以被安排在用一个Linux进程中并共享同一个虚拟机。
Application Components
Android系统的一个核心特点是,可以利用其他程序的元素来构建自身程序(假设其他程序允许的情况下)。这样的话,你的程序并没有包含其他程序的代码或是连接,确切的说,这只是当需要时简单的启动了其他程序的一部分。
与其他编程语言系统不同的是Android系统没有一个单一的入口(没有main()方法),替代的是,不过却有一些必须的组件来使系统在需要时能够实例化,总过有四种这样的组件:
Activities
一个Activity代表了一个可聚焦用户操作的可视化用户接口。
窗口的可视化组件提一个视图对象的继承。这个对象由View 类起源。每个视图控制窗口中一个特殊的长方形空间。父类视图包括内容和安排其子类的布局。叶子视图(继承关系的最底层)在它们控制的矩形和直接响应用户动 作的空间中描绘。因此视图就是在用户发生activity的交互的地方。例如一个视图可以显示一个小的图片并且发起一个动作当用户点击这个图片。 Android有许多的你可以用的只读视图,包括按钮,文本框,滚动条,菜单,选择框等。
一个视图的继承关系是放置在activity的窗口通过Activity.setContentView() 方法。(ContentView)内容视图是View对象的关系的顶部。(See the separate User Interface document for more information on views and the hierarchy)
Services
一个Service没有一个可视化的用户接口,但他可以在后台无限期的运行。比如:一个Service可以在后台播放音乐,可以不断的接受网络数据,可以不断的计算,然后将计算结果传递给有需要的Activity。每个Service必须继承Service这个基础类。
一个典型的例子,一个音乐播放器,可以通过Service来实现音乐的后台播放,即使这个音乐播放器离开了屏幕,或是开始执行其他的应用程序。
可以绑定一个不断执行的Service组件(并且启动这个Service假如他不在运行中),当绑定以后,可以通过一个由Service提供的接口来实现与这个Service的通信。
就像Activities或是其他组件一样,Services运行在程序进程的主线程中。这样他就不会阻塞其他的组将或是用户接口了,他们还经常会产生一个另外的线程来完成一些费事的任务。
Broadcast receivers
一个广播接收器(Broadcast receiver)只是用来接收broadcast并做出反应。许多broadcast产生自系统代码,比如:时区的改变,电池电量低,图片被删除等。
一个程序可以有任意数量的Broadcast receive来接收他认为重要的信息,他们可以启动一个新的Activity,或是以各种方式来提醒用户。
Contetn providers
一个content provider是一些特别程序数据对其他程序可见。一个应用程序通常使用ContentResolver对象来取得content provider提供的数据。继承自ContentProvider类,同时继承了其中的一些方法,这些方法使得其他的程序能够检索和存储可识别的类型的数据。不过,程序不直接调用这些方法,而是使用一个ContentResolver对象来调用这些方法。
Activating components: intents
Content providers由一个content resolver激活,其它的三种组件(activity/service/braodcast receiver)都是由一个叫做Intent的异步信息来激活的。一个intent对象是用来保存信息内容的。对于activity和service来说,它指定了要要被请求的动作并指定要做出反应的data的URI。对于broadcast receiver来说,intent指定了要被宣布的动作。
一个activity可以通过startActivity()或startActivityForResult()来启动另一个activity,被启动的activity可以通过getIntent()来取得传入的intent数据。
一个service是通过Context.startService(Intent service)是得到的,Android会调用onStart()并传入Intent对象。
同样地,一个intent可以用来传入Context.bindService()来建立一个连接calling组件与一个目标service之间的连接。此时,service通过onBind()接收intent参数(假定这个service此时没在运行,bindService()能有选择的启动service)。比如:一个activity可以建立一个和音乐播放器连接的service,从而是用户通过提供的接口来实现对播放器的控制。
一个程序通过Context.sendBroadcast()/Context.sendOrderedBroadcast()/ Context.sendStickyBroadcast()等方法来初始化一个broadcast对象,可以通过onRceive()来接收broadcast对象。
Shutting down components
一个内容提供者只有在响应ContentResolver的请求时才是有效的。而一个广播接收者只有在响应广播消息(broadcase message)时是有效的,所以不需要特意的关闭他们。
活动,提供了用户的交互接口。可能会长时间的运行,即使在空闲时(未接受用户交互时)。同样的,服务(service)也可能长时间的运行。
* 一个活动能通过调用finish()方法来关闭。一个活动可以通过finishActivi ty()来关闭外一个活动(该活动需要是通过startActivityFor Result()启动的)。
*一个服务可以通过他的stopSelf(),或是调用Context.stopService()来关闭自身。
Intent filters
一个intent对象可以明确的指定一个目标组件。假如它明确指定了,Android会找到指定的组件(基于清单文件)并且激活它。不过假如该intent对象为明确指定目标组件,android系统必须找到最适合的组件来响应该intent(隐式启动组件)。这时主要通过对比该intent对象与潜在的intent filters来实现的。一个组件的intent filter定义了该组件可以获取何种意图(intent)。
一个组件可以有任意数量的intent filter,不过每个intent filter必须有一个action元素。
Activities and Tasks
就像前面提到的一样,一个活动可以启动另外一个活动(包括在另外一个程序中定义的活动)。一个栈就是对于使用者来说的一个“应用程序”,它是一组活动的集合(activity)。栈的底部是启动这个栈的那个活动,栈的顶部是当前正在接受用户操作的那个活动,前一个活动是保存在栈中的,只要用户单击”BACK”键就会将当前活动弹出栈,是前一个活动重新成为当前交互的活动。
栈是包含对象的,所以假如栈中包含同一个活动的多个实例对象,像多重地图显示(multiple map viewers),比如,每一个实例都有一个分离的入口,栈是不会重新安排位置的,只有pop或是push.。
在一个任务(task)中的所有活动是一个单元,整个任务(整个活动的栈)能被放置到前台或是后台,比如:现有一个包含4个活动的task,假如用户通过home键开启了另一个application(即另一个task)。那么当前的task将移至后台,一段时间后,用户又通过home键返回从task,然后用户按back键,返回的不是刚才那个application,而是四个activity中的前一个。
以上描述的只是活动与任务(task)中默认的行为。不过存在着一些方式来修改活动或是栈的几乎所有的行为。控制栈中活动的属性,可以通过启动activity的intent中flag属性及清单文件中的<activity>属性设置。请求者和应答者对于将要发生什么都有话语权。(Both requester and respondent have a say in what happens)
在这一点上,最主要的intent flag是:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
FLAG_ACTIVITY_SINGLE_TOP
最主要的<activity>属性是:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
Affinities(归属关系、亲属关系) and new tasks
默认情况下,一个应用中的所有活动相互之间多有一个亲和度(affinity)的存在。也就是说,对于同一个task中的活动有一个喜好(preference),尽管,每个活动可以在<activity>标签中设置独立的affinity。定义在不同application中的活动可以共享一个affinity。或是定义在同一个application中的活动可以被分配不同的affinity
,affinity在两种情况下起作用:当intent启动的活动包含FLAG_ACTIVITY_NEW_TASK标签时,或是一个活动将allowTaskReparenting设置成了’true’。
The FLAG_ACTIVITY_NEW_TASK flag
就像前面描述的一样,默认情况下,这是一个新的活动是通过startActivity()启动一个活动的,它将被压入(push)到和
调用者同一个task中。不过,如果要启动的活动包含了FLAG_ACTIVITY_NEW_TASK标签,那么系统将寻找另外一
个不同的task来安置这个新的活动,经常地,会是这个标签指示的名字,这将会是一个新的task,不过不一定非要这样,假如已经有一个包含和该新活动同样affinity的task存在了,这个活动就会载入到那个task中。
The allowTaskReparenting (允许栈抚养)attribute
假如一个活动的allowTaskReparenting被设置成了’true’,那么它就能从启动它的task中移动到另
外一个包含同样的affinity的task中(在这另外一个task转到前台时),比如:有一个旅行软件中有一个报告天气
情况的活动,并且它与这个旅行软件中的其它部分活动有同样的affnity,现在你的一个活动启动了它,原本它和
你的活动是在同一个栈中的,但现在,当这个旅行软件转到前台时,这个报告天气的活动就会被和旅行软件一起再
分配和显示。
假如一个apk文件包含多个对于用户来说是”application”的活动,那么你可能需要为他们分配不同的affinity来更好
的完成活动间的交互与管理。
未完,待续。。。。