Android开发 - 创建用户界面
关键字:merge标签、include标签、ViewStub、Lint工具、Fragment、Animations、Adapter
- 使用视图和布局
- 理解Fragment
- 优化布局
- 创建分辨率无关的用户界面
- 扩展、分组、创建和使用视图
- 使用适配器将数据绑定到视图
将用户界面分配给Activity
setContentView

public class MyActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
通过使用findViewById来得到布局内每个视图的引用
EditText editText = (EditText)findViewById(R.id.editText);
当然也可以使用传统的代码方式来构建UI。
public class MyActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView myTextView = new TextView(this); setContentView(myTextView); myTextView.setText("Hello, Android"); } }
定义布局
在XML中实现布局可以把表示层从视图、Fragment和Activity代码中分离出来。它也可以创建支持特定硬件的、无需更改代码就可以动态地加载的变体。

优化布局
填充布局是一个开销巨大的过程;每个额外的嵌套布局和它所包含的View,都直接影响应用程序的性能和响应能力。
为了使应用程序流畅地运行和快速地响应,重要的是尽可能地保持布局的简单和避免出现因为相对较小UI的变动而完全填充新的布局的情况。
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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="@string/hello" /> </FrameLayout>
上面代码中的FrameLayout被添加到父容器时,就会变成一个冗余。因为layout_width和layout_height都被设置为MATCH_PARENT,这是没有意义的。
这时最好的方案是选择merge标签。
merge标签
<?xml version="1.0" encoding="utf-8"?> <merge 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="@string/hello" /> </merge>
当包含merge标签的布局被添加到另一个布局时,该布局的merge节点会被删除,而该布局的子View会被直接添加到新的父布局中。
include标签
merge标签结合include标签一起使用尤其有用,include标签是用来把一个布局的内容插入到另一个布局中。
<?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" > <include android:id="@+id/my_action_bar" layout="@layout/actionbar"/> <include android:id="@+id/my_image_text_layout" layout="@layout/image_text_layout"/> </LinearLayout>
避免使用过多的View
填充每个额外的View都需要花费时间和资源。为了最大限度地提高应用程序的速度和响应能力,布局包含的View个数不应该超过80。如果超过这个限制,填充布局花费的时间将成为一个显著的问题。
要想在复杂的布局内填充的View的数量越少,可以使用ViewStub。
ViewStub的工作原理就像是一个延时填充的include标签 - 一个stub代表了在父布局中指定的(多个)子View - 但只有显式地调用inflate()方法或者被置为可见的时候,这个stub才会被填充。
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/myListView"></ListView> <ViewStub android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:id="@+id/download_progress_panel_stub" android:layout="@layout/progress_overlay_panel" android:inflatedId="@+id/download_progress_panel"/> </FrameLayout>
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //查找stub View stub = findViewById(R.id.download_progress_panel_stub); //将其设置为可见,以填充子布局 stub.setVisibility(View.VISIBLE); //查找以填充的stub布局的根节点 View downloadProgressPanel = findViewById(R.id.download_progress_panel); } }
使用Lint工具来分析布局
http://tools.android.com/tips/lint
ToDoList示例
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/myEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/addItemHint" android:contentDescription="@string/addItemContentDescription" /> <ListView android:id="@+id/myListView" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">ToDoList</string> <string name="addItemHint">New To Do Item</string> <string name="addItemContentDescription">New To Do Item</string> </resources>
public class ToDoListActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Inflate your View setContentView(R.layout.main); // Get references to UI widgets ListView myListView = (ListView)findViewById(R.id.myListView); final EditText myEditText = (EditText)findViewById(R.id.myEditText); // Create the Array List of to do items final ArrayList<String> todoItems = new ArrayList<String>(); // Create the Array Adapter to bind the array to the List View final ArrayAdapter<String> aa; aa = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, todoItems); // Bind the Array Adapter to the List View myListView.setAdapter(aa); myEditText.setOnKeyListener(new View.OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER) || (keyCode == KeyEvent.KEYCODE_ENTER)) { todoItems.add(0, myEditText.getText().toString()); aa.notifyDataSetChanged(); myEditText.setText(""); return true; } return false; } }); } }
Fragment
Fragment允许将Activity拆分成多个完全独立封装的可重用的组件,每个组件有它自己的生命周期和UI布局。Fragment最大的优点是可以为不同屏幕大小的设备创建动态灵活的UI。
创建新的Fragment
大多数情况下,需要为Fragment分配一个UI。也可以为一个Activity创建一个没有任何UI但提供后台行为的Fragment。
Fragment如果需要UI,可以重写onCreateView方法来填充并返回所需要的View层次。
public class MySkeletonFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 创建或者填充Fragment的UI,并且返回它 // 如果这个Fragment没有UI,返回null return inflater.inflate(R.layout.my_fragment, container, false); } }
和Activity不同,Fragment不需要在manifest.xml中注册。这是因为Fragment只有嵌入到一个Activity时,它才能够存在,它的生命周期也依赖于它所嵌入的Activity。
Fragment的生命周期

/** * Listing 4-4: Fragment skeleton code * Listing 4-5: Fragment lifecycle event handlers */ package com.paad.fragments; import android.app.Activity; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class MySkeletonFragment extends Fragment { // 调用该方法时Fragment会被连接到它的父Activity上。 @Override public void onAttach(Activity activity) { super.onAttach(activity); // 获取对父Activity的引用 } // 调用该方法来进行Fragment的创建 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化Fragment } // 一旦Fragment已被创建,要创建它自己的用户界面时调用该方法 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 创建或者填充Fragment的UI,并返回它 // 如果这个Fragment没有UI,那么返回null return inflater.inflate(R.layout.my_fragment, container, false); } // 一旦父Activity和Fragment的UI已被创建,则调用该方法 @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 完成Fragment的初始化 - 尤其是那些父Activity被初始化完成后或者Fragment的View被完全填充后才能做的事情 } // 在可见生命周期的开始时被调用 @Override public void onStart(){ super.onStart(); // 应用所有需要的UI变化,现在Fragment是可见的 } // 在活动生命周期的开始时被调用 @Override public void onResume(){ super.onResume(); // 回复所有暂停的Fragment需要的UI更新,线程或者进程,但在非活动状态它是暂停的 } // 在活动生命周期结束时被调用 @Override public void onPause(){ // 当Activity不是活动的的前台Activity时,需要暂停UI的更新、挂起线程或者暂停那些不需要更新的CPU的集中处理 // 由于调用这个方法后,进程可能被终止,所以要保存所有的编辑和状态改变信息 super.onPause(); } // 在活动生命周期结束时,调用该方法保存UI的状态变化 @Override public void onSaveInstanceState(Bundle savedInstanceState) { // 将UI的状态改变信息保存到savedInstanceState中 // 这个Bundle会被传递到onCreate、onCreateView和onActivityCreate(如果它的父Activity被终止并且重新启动)方法中 super.onSaveInstanceState(savedInstanceState); } // 在可见生命周期结束时调用该方法 @Override public void onStop(){ // 当Fragment不可见时,暂停其余的UI更新、挂起线程或者暂停不再需要的处理 super.onStop(); } // 当Fragment的View被分离时,调用该方法 @Override public void onDestroyView() { // 清除资源相关的View super.onDestroyView(); } // 在整个生命周期结束时调用该方法 @Override public void onDestroy(){ // 清除所有的资源,包括结束线程和关闭数据库连接等 super.onDestroy(); } // 当Fragment从它的父Activity上分离时,调用该方法 @Override public void onDetach() { super.onDetach(); } }
从父Activity中绑定和分离Fragment
Fragment完整的生命周期开始于绑定到它的父Activity,结束于从父Activity上分离。通过分别调用onAttach和onDetach来表示这些事件。
在Fragment/Activity被暂停后,由于任何其他处理程序都可以被调用,可能就会出现它的父Activity进程没有完成它的全部生命周期被终止从而导致onDetach不会被调用的情况。
创建和销毁Fragment
已创建Fragment的生命周期存在于首次对onCreate的调用和最终对onDestroy的调用的期间。因为对Activity的进程来说,再没有相应的onDestroy方法被调用而被终止的情况很常见,所以Fragment不能依赖触发onDestroy方法来销毁它。
与Activity一样,应该使用onCreate方法来初始化Fragment。在Fragment生命周期内创建的任何类作用于对象,最好确保它们只被创建一次。
与Activity不同,Fragment的UI不在onCreate方法中初始化。
创建和销毁用户界面
Fragment的UI是在onCreateView和onDestroyView中初始化和销毁。
使用onCreateView方法来初始化Fragment:填充UI、获取它所包含的View的引用,然后创建所需的任何的Service和Timer。
一旦已经填充好了View层次,该View应该从这个处理程序返回:
return inflater.inflate(R.layout.m_fragment, container, false);
如果Fragment需要和它的父Activity的UI交互,需要一直等到onActivityCreate事件被触发。该事件被触发意味着Fragment所在的Activity已经完成了初始化并且它的UI也已经完全构建好了。
Fragment状态
和Activity一样,当Fragment所属的Activity处在前台并拥有焦点时,这些Fragment也是活动的。当Activity被暂停或停止,它所包含的Fragment也会被暂停或停止。一个非活动的Activity包含的Fragment也是非活动的。当Activity最终被销毁时,它所包含的每一个Fragment同样也会被销毁。
当Android内存管理器通过随即关闭一些应用程序来释放资源时,那些Activity所包含的Fragment也同样会被销毁。
当Fragment暂停或停止时,保存所有的UI状态和持久化所有的数据是非常重要的。和Activity一样,当一个Fragment再次变为活动状态时,它应该恢复之前保存的状态。
Fragment Manager介绍
每一个Activity都包含一个Fragment Manager来管理它所包含的Fragment。可以通过getFragmentManager方法来访问Fragment Manager:
FragmentManager fm = getFragmentManager();
Fragment Manager提供了很多方法来访问当前添加到Activity上的Fragment,通过执行Fragment Transaction来添加、删除和替换Fragment。
向Activity中添加Fragment
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.paad.weatherstation.MyListFragment" android:id="@+id/my_list_fragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" /> <fragment android:name="com.paad.weatherstation.DetailsFragment" android:id="@+id/details_fragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="3" /> </LinearLayout>
当使用Fragment基于不同屏幕尺寸来定义一系列静态布局时,这个技术是行之有效的。
如果打算在运行时通过添加、删除或者替换Fragment的方式来动态地修改布局,更好的方法是基于当前的应用程序状态使用容器View来创建布局,Fragment可以在运行时放入到一个容器View内。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/ui_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" /> <FrameLayout android:id="@+id/details_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="3" /> </LinearLayout>
然后,需要在Activity的onCreate处理程序中使用Fragment Transaction来创建相应的Fragment,并把它添加到对应的父容器中。
FragmentTransaction ft = fm.beginTransaction(); ft.add(R.id.details_container, new DetailsFragment()); ft.add(R.id.ui_container, new MyListFragment()); ft.commit();
使用Fragment Transaction
在运行时,Fragment Transaction可以用来在一个Activity内添加、删除和替换Fragment。使用Fragment Transaction,可以让布局称为动态的。它们会根据与用户的交互和应用程序的状态进行自适应和改变。
一个新的Fragment Transaction是通过使用Activity的Fragment Manager中的beginTransaction方法创建的。在设置显示动画之前,可以根据要求使用add、remove和replace方法来修改布局,并设置适当的back栈行为。当准备执行改变时,调用commit方法将事物添加到UI队列。
FragmentTransaction ft = fm.beginTransaction(); ft.add(R.id.details_container, new DetailsFragment()); ft.add(R.id.ui_container, new MyListFragment()); ft.commit();
添加、删除和替换Fragment
添加一个新的UIFragment时,需要指定要添加的Fragment实例和将要放置它的容器View。另外,还可以为这个Fragment制定一个tag标识,后面通过这个标识,可以使用findFragmentByTag方法找到对应的Fragment。
FragmentTransaction ft = fm.beginTransaction(); ft.add(R.id.ui_container, new MyListFragment()); ft.commit();
要想删除一个Fragment,首先需要找到对这个Fragment的引用,通常是通过Fragment Manager的findFragmentById或者findFragmentByTag来实现。然后,把找到的Fragment实例作为参数传给Fragment Transaction的remove方法。
FragmentTransaction ft = fm.beginTransaction(); DetailsFragment detailsFragment = (DetailsFragment)fm.findFragmentById(R.id.details_container); ft.remove(detailsFragment); ft.commit();
还可以把一个Fragment替换成另一个。可以使用replace方法,指定要替换的Fragment的父容器ID、一个替换它的新Fragment和新Fragment的tag标识(可选)。
FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.details_container, new DetailsFragment(selected_index)); ft.commit();
使用Fragment Manager查找Fragment
要想在Activity中查找Fragment,可以使用Fragment Manager的findFragmentById方法来实现。如果是通过XML布局方式把Fragment加入到Activity中的,可以使用这个Fragment的资源标识符。
detailsFragment = (DetailsFragment)fm.findFragmentById(R.id.details_container);
如果已经通过Fragment Transaction添加了一个Fragment,应该把容器View的资源标识符指定给添加了想要查找的Fragment。另外,还可以通过使用findFragmentByTag来查找在Fragment Transaction中指定了tag标识的Fragment。
detailsFragment = (DetailsFragment)fm.findFragmentByTag(MY_FRAGMENT_TAG);
使用Fragment填充动态的Activity布局
如果希望在运行时动态地改变Fragment的结构和布局,最好在XML文件的布局中只定义父容器,并在运行时使用Fragment Transaction来单独填充它,从而当因配置改变(如屏幕旋转)而引起的UI重新构建时,能够保持一致性。
public class MyFragmentActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 填充包含Fragment容器的布局 setContentView(R.layout.fragment_container_layout); FragmentManager fm = getFragmentManager(); // 检查该布Fragment back栈是否被填充,如果没有,创建并填充布局。 DetailsFragment detailsFragment = (DetailsFragment)fm.findFragmentById(R.id.details_container); if (detailsFragment == null) { FragmentTransaction ft = fm.beginTransaction(); ft.add(R.id.details_container, new DetailsFragment()); ft.add(R.id.ui_container, new MyListFragment()); ft.commit(); } } }
在一个给定方向的布局中删除一个Fragment容器,只需要简单地将布局定义中该Fragment的visbility属性设置为“gone”即可。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/ui_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" /> <FrameLayout android:id="@+id/details_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="3" android:visibility="gone" /> </LinearLayout>
Fragment和back栈
Fragment能够创建动态的Activity布局,这些布局可以被修改来使UI发生重大的改变。在某些情况下,这些改变可以被视为一个新的屏幕,在这种情况下,用户可能会理所当然地期望Back按键会返回到前一个布局。同样包括回滚前一个已执行的Fragment Transaction。
想要将Fragment Transaction添加到back栈中,可以在调用commit方法前,在Fragment Transaction中调用addToBackStack方法。
FragmentTransaction ft = fragmentManager.beginTransaction(); ft.add(R.id.ui_container,new MyListFragment()); Fragment fragment = fragmentManager.findFragmentById(R.id.detailsFragment); ft.remove(fragment); String tag = null; ft.addToBackStack(tag); ft.commit();
当按下Back按键时,之前的Fragment Transaction将会回滚并且UI将返回到之前的布局。
使Fragment Transaction动起来
想要应用众多默认过度动画中的一个,可以对任何Fragment Transaction使用setTransition方法,并传入一个FragmentTransaction.TRANSIT_FRAGMENT_*常量:
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
也可以通过使用setCustomAnimations方法对Fragment Transaction应用自定义动画。
这个方法接受两个动画XML资源,一个是通过该事务添加到布局的Fragment,另一个是被删除的Fragment。
fragmentTransaction.setCustomAnimations(R.animator.fragment_slide_left_enter,R.animator.fragment_slide_right_exit);
Fragment和Activity之间的接口
在任何Fragment中使用getActivity方法来返回它所嵌入的Activity的引用。
TextView textView =(TextView) getActivity().findViewById(R.id.textView);
尽管Fragment可以直接使用主Activity的Fragment Manager进行通信,但是通常最好考虑使用Activity来做媒介。这样会让Fragment尽可能独立和松耦合,而Fragment的职责在于决定Fragment中的一个事件应该如何影响主Activity的整体UI性能下降。
在Fragment需要和它的主Activity共享事件的地方(如提示UI选中),最好在Fragment中创建一个callback接口,而在Activity中实现它。
以下代码在Fragment派生类中定义了一个公共的事件监听接口,通过重写onAttach处理程序用来获得主Activity的引用,并确保主Activity实现了必要的接口。
public class SeasonFragment extends Fragment { public interface OnSeasonSelectedListener { public void onSeasonSelected(Season season); } private OnSeasonSelectedListener onSeasonSelectedListener; private Season currentSeason; @Override public void onAttach(Activity activity) { super.onAttach(activity); try { onSeasonSelectedListener = (OnSeasonSelectedListener)activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnSeasonSelectedListener"); } } private void setSeason(Season season) { currentSeason = season; onSeasonSelectedListener.onSeasonSelected(season); } }
没有用户界面的Fragment
也可以创建没有UI的Fragment来提供后台行为,该行为会一直持续到Activity重新启动。
这特别适合于定期和UI交互的后台任务或者当因配置改变而导致的Activity重新启动时,保存状态变得特别重要的场合。
当Fragment的父Activity重新创建时,可以选择使用Fragment的setRetainInstance方法让一个活动的Fragment保留它的实例。在调用该方法后,Fragment的生命周期会改变。
当Activity重新启动时,同一个Fragment的实例会被保留下来,而不是和它的父Activity一起被销毁和重新创建。但Fragment所在的Activity被销毁时,将会收到onDetach事件,之后当新的父Activity实例化后,还会收到onAttach、onCreateView和onActivityCreated事件。
要把没有UI的Fragment加入到Activity中,必须创建一个新的Fragment Transaction,并指定一个tag来标识该Fragment。
Adapter
Adapter用来把数据绑定到扩展了AdapterView类的视图组(如ListView或者Gallery)。Adapter负责创建代表所绑定父视图中的低层数据的子视图。
因为Adapter既负责提供数据,有负责创建代表每一个条目的视图,所以Adapter可以从根本上修改它们所绑定的控件的外观和功能。
定制ArrayAdapter
默认情况下,ArrayAdapter将使用一个对象数组的每个原色的toString值来填充指定布局中的TextView。
public class MyArrayAdapter extends ArrayAdapter<MyClass> { int resource; public MyArrayAdapter(Context context, int _resource, List<MyClass> items) { super(context, _resource, items); resource = _resource; } @Override public View getView(int position, View convertView, ViewGroup parent) { // 创建并填充要显示的视图 LinearLayout newView; if (convertView == null) { // 如果不是第一次更新,则填充一个新视图 newView = new LinearLayout(getContext()); String inflater = Context.LAYOUT_INFLATER_SERVICE; LayoutInflater li; li = (LayoutInflater)getContext().getSystemService(inflater); li.inflate(resource, newView, true); } else { // 否则更新现有视图 newView = (LinearLayout)convertView; } MyClass classInstance = getItem(position); // TODO: 从classInstance变量检索要显示的值 // TODO: 获得对视图的引用来填充布局 // TODO: 使用对象的属性值填充视图 return newView; } }
使用Adapter绑定数据到视图
要把一个Adapter应用到一个由AdapterView派生的类中,可以调用视图的setAdapter方法,并传递给它一个Adapter实例。
ArrayList<String> myStringArray = new ArrayList<String>(); int layoutID = android.R.layout.simple_list_item_1; ArrayAdapter<String> myAdapterInstance; myAdapterInstance = new ArrayAdapter<String>(this, layoutID, myStringArray); myListView.setAdapter(myAdapterInstance);
使用SimpleCursorAdapter
SimpleCursorAdapter用于将一个Cursor绑定到一个AdapterView,并使用一个布局来定义每个行/条目的UI。每个行的视图的内容使用底层Cursor中对应行的列值进行填充的。
SimpleCursorAdapter是通过传入当前的上下文、用于每个条目的一个布局资源、一个代表要显示的数据的Cursor和两个整数数组进行构建的,这两个数组的其中一个包含了要使用的列(包含源数据),另一个(同样大小的)数组存储资源ID,用于指定布局内的哪些视图应该用来显示相应列的内容。
SimpleCursorAdapter adapter = new SimpleCursorAdapter( this, android.R.layout.simple_expandable_list_item_1, cursor, new String[]{People.NAME}, new int[]{android.R.id.text1} );

关键字:merge标签、include标签、ViewStub、Lint工具、Fragment、Animations、Adapter
浙公网安备 33010602011771号