ListView에는 일반적으로 두 가지 책임이 있습니다.
(1) 레이아웃에 데이터를 채웁니다.
(2) 사용자의 선택과 클릭 작업을 처리합니다.
첫 번째 요점은 이해하기 쉽습니다. ListView는 이 기능을 구현합니다. 두 번째 요점은 하기 어렵지 않습니다. 독자들은 다음 연구에서 그것이 매우 간단하다는 것을 알게 될 것입니다.
ListView를 생성하려면 3가지 요소가 필요합니다.
(1) ListView의 각 열에 대한 보기입니다.
(2) 뷰 등의 데이터나 사진을 입력합니다.
(3) 데이터와 ListView를 연결하는 어댑터입니다.
즉, ListView를 사용하려면 먼저 어댑터가 무엇인지 이해해야 합니다. Adapter는 데이터와 AdapterView를 연결하는 브릿지입니다. (ListView는 일반적인 AdapterView이며 다른 내용은 나중에 배우겠습니다.) 데이터와 AdapterView의 분리 설정을 효과적으로 실현할 수 있어 AdapterView와 데이터를 더 쉽게 바인딩하고 수정을 더 쉽게 할 수 있습니다. . 편의성
Android는 다양한 어댑터를 제공합니다. 표 4-5에는 일반적으로 사용되는 어댑터가 나열되어 있습니다.
표 4-5 일반적으로 사용되는 어댑터
어댑터
의미
ArrayAdapter
는 배열을 바인딩하는 데 사용되며 일반 작업을 지원합니다.
SimpleAdapter
는 xml에 정의된 컨트롤에 해당하는 데이터를 바인딩하는 데 사용됩니다
SimpleCursorAdapter
커서로 얻은 데이터를 바인딩하는데 사용
베이스 어댑터
범용 베이스 어댑터
실제로는 있습니다. 많은 어댑터에는 다양한 변환 방법과 기능만 있다는 점에 유의해야 합니다. 다음으로, 다양한 어댑터를 사용하여 데이터를 ListView에 바인딩할 것입니다(SimpleCursorAdapter는 지금은 논의되지 않지만 나중에 SQLite에 대해 이야기할 때 소개될 것입니다).
4.12.1 ListView는 ArrayAdapter를 사용합니다
ArrayAdapter는 간단한 ListView 데이터 바인딩을 구현하는 데 사용할 수 있습니다. 기본적으로 ArrayAdapter는 각 개체의 toString 값을 레이아웃의 미리 정의된 TextView 컨트롤에 바인딩합니다. ArrayAdapter의 사용은 매우 간단합니다.
예:
프로젝트 디렉터리: EX_04_12
레이아웃 파일에 ListView 컨트롤을 추가합니다.
android:layout_height="fill_parent" > ListView android:id="@+id/lv" android:layout_width= "fill_parent" android:layout_height="fill_parent" />
publicclass MyListView는 Activity {
privatestaticfinal String[] strs = new String[] {
"첫 번째", "두 번째", "세 번째", "네 번째", "다섯 번째"를 확장합니다. };//ListView의 내용을 표시하는 문자열 배열 정의 private ListView lv;/**활동이 처음 생성될 때 호출됩니다.*/
@Override
publicvoid onCreate(Bundle saveInstanceState) {super.onCreate(savedInstanceState) ;
setContentView(R.layout.main);
lv = (ListView) findViewById(R.id.lv);//ListView 객체에 대한 참조 가져오기/*ListView에 대한 어댑터 설정 데이터 바인딩 ***/ 🎜>
▲그림 4-29 ArrayAdapter를 사용한 ListView 실행 효과
코드는 매우 간단하며 실행 효과는 그림과 같습니다. 4-29.
사용된 단계를 분석합니다.
(2) ArrayAdapter 생성자를 구현하여 ArrayAdapter 객체를 생성합니다.
(3) ListView의 setAdapter() 메소드를 통해 ArrayAdapter를 바인딩합니다.
두 번째 단계에서 언급해야 할 것은 ArrayAdapter에는 여러 생성자가 있으며, 예제에 구현된 것이 가장 일반적으로 사용되는 생성자라는 것입니다. 첫 번째 매개변수는 컨텍스트이고, 두 번째 매개변수는 ListView의 각 행을 채우는 데 사용되는 TextView가 포함된 레이아웃 리소스 ID입니다. 세 번째 매개변수는 ListView의 내용입니다. 두 번째 매개변수는 레이아웃을 사용자 정의할 수 있지만 이 레이아웃에는 TextView 컨트롤이 있어야 합니다. 일반적으로 Android에서 제공하는 리소스를 사용합니다. 예제에 사용된 리소스 외에도 RadioButton 및 CheckBox를 사용하여 ListView를 구현하는 데 일반적으로 사용되는 리소스는 다음과 같습니다.
(1) android.R.layout.simple_list_item_checked 리소스를 지정하여 선택 상자가 있는 ListView를 구현합니다. 선택이 다중 선택인지 단일 선택인지를 설정하려면 setChoiceMode() 메소드를 사용해야 합니다. 그렇지 않으면 선택 효과가 그림 4-30에 표시됩니다.
구현 코드는 다음과 같습니다.
lv.setAdapter(new ArrayAdapter
> 🎜>
( 2) android.R.layout.simple_list_item_multiple_choice 리소스를 지정하여 CheckBox로 ListView를 구현합니다. 마찬가지로 단일 선택 또는 다중 선택을 설정하려면 setChoiceMode() 메서드를 사용해야 합니다. 작업 효과는 그림 4-31에 나와 있습니다.
> ) android.R.layout.simple_list_item_single_choice 리소스를 지정하여 RadioButton으로 ListView를 구현합니다. 여기서 주목해야 할 점은 여기에서는 라디오 선택이 지정되지 않는다는 것입니다. 다중 선택인지 단일 선택인지는 setChoiceMode() 메소드를 통해 지정해야 합니다. 작업 효과는 그림 4-32와 같습니다.
구현 코드는 다음과 같습니다.
▲ 그림 4- 30 선택 상자가 있는 ListView ▲ 그림 4-31 CheckBox가 있는 ListView ▲ 그림 4-32 RadioButton이 있는 ListView
앞서 언급했듯이 ListView의 책임은 데이터를 채우는 것 외에도 사용자 작업을 처리해야 합니다. . 다음 코드를 통해 클릭 리스너를 ListView에 바인딩할 수 있습니다. 클릭하면 클릭한 행 수가 제목 표시줄에 표시됩니다. 4.12.2 ListView는 SimpleAdapter를 사용합니다
목록에는 그림 등 텍스트 이외의 것을 표시해야 하는 경우가 많습니다. 이때 SimpleAdapter를 사용할 수 있습니다. SimpleAdapter의 사용법도 매우 간단하고 기능도 매우 강력합니다. 이를 사용하여 그림, 다중 선택 상자 등과 같은 ListView 항목의 내용을 사용자 정의할 수 있습니다. 각 행에 대해 ImageView 및 TextView를 사용하여 ListView를 구현하는 예를 살펴보겠습니다. 먼저 그림 4-34와 같이 실행 효과를 살펴보겠습니다.
lv.setOnItemClickListener(new OnItemClickListener() { @Override publicvoid onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { //点击后在标题上显示点击了第几行 setTitle("你点击了第"+arg2+"行"); } });
▲그림 4-34 아이콘이 있는 ListView
먼저 레이아웃 파일에 ListView 컨트롤을 추가합니다.
또한 ListView의 각 행에 대한 레이아웃을 정의하고 RelativeLayout을 사용하여 두 줄의 텍스트와 이미지로 레이아웃을 구현해야 합니다. item.xml: publicclass MyListViewSimple은 Activity { ▲ 그림 4-35 BaseAdapter의 메소드 BaseAdapter를 사용하려면 이를 상속할 클래스를 작성해야 합니다. 추상 클래스이며 해당 메서드를 상속하려면 구현되어야 합니다. BaseAdapter의 유연성은 많은 메소드를 다시 작성해야 한다는 것입니다. 그림 4-35는 BaseAdapter에서 상속된 SpeechListAdapter에 의해 구현된 메소드를 보여줍니다. 이 방법들은 무엇을 합니까? ListView의 원리를 분석하여 독자들에게 답변을 제공합니다. 当系统开始绘制ListView的时候,首先调用getCount()方法。得到它的返回值,即ListView的长度。然后系统调用getView()方法,根据这个长度逐一绘制ListView的每一行。也就是说,如果让getCount()返回1,那么只显示一行。而getItem()和getItemId()则在需要处理和取得Adapter中的数据时调用。那么getView如何使用呢?如果有10000行数据,就绘制10000次?这肯定会极大的消耗资源,导致ListView滑动非常的慢,那应该怎么做呢?通过一个例子来讲解如何在使用BaseAdapter的时候优化ListView的显示。例子中将上一节中的ImageView换成Button,并且处理Button的点击事件,其中对ListView的显示做了优化。 布局文件和上一例类同,读者可以在光盘的工程目录中查看,这里只给出Activity类。 运行效果如图4-36所示。还需要注意的是,Button会抢夺ListView的焦点,需要将Button设置为没有焦点。设置非常简单,只需要在xml的Button标签下加入一行:android:focusable=“false”代码就可以了。在LogCat观察点击后输出的信息,如图4-37所示。 ▲图4-36 목록 보기 및 어댑터 사용법 ▲图4-37 목록 보기 및 어댑터 사용법 代码中getView()方法不容易理解。其实完全可以不用所谓的convertView和ViewHolder,直接导入布局并且设置控件显示的内容就可以了。但是这意味着有多少行数据就需要绘制多少行ListView,这显然是不可取的。这里采用了一种优化的方法。代码中,在getView()方法中加入了一行log输出convertView的内容。滚动ListView,输出信息如图4-38所示。 从图4-38中可以看出,当启动Activity呈现第一屏ListView的时候,convertView为零。当用户向下滚动ListView时,上面的条目变为不可见,下面出现新的条目。这时候convertView不再为空,而是创建了一系列的convertView的值。当又往下滚一屏的时候,发现第11行的容器用来容纳第22行,第12行的容器用来容纳第23行。也就是说convertView相当于一个缓存,开始为0,当有条目变为不可见,它缓存了它的数据,后面再出来的条目只需要更新数据就可以了,这样大大节省了系统资料的开销。 还可以继续优化。虽然重复利用了已经绘制的view,但是要得到其中的控件,需要在控件的容器中通过findViewById的方法来获得。如果这个容器非常复杂,这显然会增加系统资源的开销。在上面的例子中,引入了Tag的概念。或许不是最好的办法,但是它确实能使ListView变得更流畅。代码中,当convertView为空时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。当convertView不为空,重复利用已经创建的view的时候,使用getTag()方法获取绑定的ViewHolder对象,这样就避免了findViewById对控件的层层查询,而是快速定位到控件。 ▲图4-38 목록 보기 및 어댑터 사용법 总结一下,这节介绍了用BaseAdapter来绑定ListView的数据。因为BaseAdapter非常灵活,使用也相对较其他控件麻烦。同时ListView的优化问题也值得读者去研究,一个流畅的ListView会带来更好的用户体验。
구성이 완료되면 Java 코드로 데이터를 ListView에 바인딩할 수 있습니다.
private ListView lv; /**활동이 처음 생성될 때 호출됩니다.*/ @Override
publicvoid onCreate(Bundle selectedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout .main);
lv = (ListView) findViewById(R.id.lv);/*동적 배열 정의*/
ArrayList
map.put("ItemImage", R.drawable.icon);//그림 추가 map.put("ItemTitle", "line "+i+") ;
map.put("ItemText", "이것은 "+i+" 라인입니다.");
listItem.add(map)
SimpleAdapter mSimpleAdapter = new er( This, listitem, // 바인딩해야 할 데이터
r.Layout.item, // 각 줄의 레이아웃 // 동적 배열의 데이터 소스 키는 정의의 정의된 레이아웃에 해당합니다. 구체적으로 "
,"ItemTitle", "ItemText"},
newint[] {R.id.ItemImage,R.id.ItemTitle,R.id.ItemText}
);
Lv.setAdapter (msimpleadapter); // 목록 보기 바인딩 어댑터입니다. lv.SetonitemClicklistener (New
OniTemClicklistener () {
@Override
PUBLICVOID Onitemclick (adapterview & lt;? & gt; arg0, view arg1, int arg2, long arg3) {
setTitle(""+arg2+" 줄을 클릭했습니다.");//클릭한 줄을 표시하도록 제목 표시줄 설정 > >}
simpleAdapter를 사용하는 데이터는 일반적으로 HashMap으로 구성된 목록이며 목록의 각 섹션은 ListView의 각 행에 해당합니다. SimpleAdapter의 생성자를 통해 HashMap의 각 키 데이터가 레이아웃 파일의 해당 컨트롤에 매핑됩니다. 이 레이아웃 파일은 일반적으로 자신의 필요에 따라 직접 정의합니다. SimpleAdapter를 사용하는 단계를 정리하겠습니다.
(1) 필요에 따라 ListView의 각 행에 구현된 레이아웃을 정의합니다.
(2) HashMap으로 구성된 목록을 정의하고 그 안에 데이터를 키-값 쌍으로 저장합니다.
(3) SimpleAdapter 객체를 생성합니다. publicclass MyListViewBase extends Activity {
private ListView lv; /*定义一个动态数组*/ ArrayList<HashMap<String, Object>>listItem;/** Called when the activity is first created. */
@Override
publicvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv = (ListView) findViewById(R.id.lv);
MyAdapter mAdapter = new MyAdapter(this);//得到一个MyAdapter对象 lv.setAdapter(mAdapter);//为ListView绑定Adapter /*为ListView添加点击事件*/
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
publicvoid onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
Log.v("MyListViewBase", "你点击了ListView条目" + arg2);//在LogCat中输出信息
}
});
}/*添加一个得到数据的方法,方便使用*/ private ArrayList<HashMap<String, Object>> getDate(){
ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>(); /*为动态数组添加数据*/ for(int i=0;i<30;i++)
{
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("ItemTitle", "第"+i+"行");
map.put("ItemText", "这是第"+i+"行");
listItem.add(map);
}
return listItem;
}/* * 新建一个类继承BaseAdapter,实现视图与数据的绑定 */ privateclass MyAdapter extends BaseAdapter { private LayoutInflater mInflater;//得到一个LayoutInfalter对象用来导入布局 /*构造函数*/
public MyAdapter(Context context) { this.mInflater = LayoutInflater.from(context);
}
@Override
publicint getCount() {
return getDate().size();//返回数组的长度 }
@Override public Object getItem(int position) {
returnnull;
}
@Override
publiclong getItemId(int position) { return 0;
} /*书中详细解释该方法*/ @Override public View getView(finalint position, View convertView, ViewGroup parent) {
ViewHolder holder; //观察convertView随ListView滚动情况
Log.v("MyListViewBase", "getView " + position + " " + convertView); if (convertView == null) {
convertView = mInflater.inflate(R.layout.item, null);
holder = new ViewHolder(); /*得到各个控件的对象*/
holder.title = (TextView) convertView.findViewById(R.id.ItemTitle);
holder.text = (TextView) convertView.findViewById(R.id.ItemText);
holder.bt = (Button) convertView.findViewById(R.id.ItemButton);
convertView.setTag(holder);//绑定ViewHolder对象 } else{
holder = (ViewHolder)convertView.getTag();//取出ViewHolder对象 } /*设置TextView显示的内容,即我们存放在动态数组中的数据*/
holder.title.setText(getDate().get(position).get("ItemTitle").toString());
holder.text.setText(getDate().get(position).get("ItemText").toString());
/*为Button添加点击事件*/ holder.bt.setOnClickListener(new OnClickListener() {
@Override
publicvoid onClick(View v) {
Log.v("MyListViewBase", "你点击了按钮" + position); //打印Button的点击信息
}
});
return convertView;
}
}/*存放控件*/ publicfinalclass ViewHolder{ public TextView title; public TextView text; public Button bt;
}
}