Android用户界面 UI组件--AdapterView及其子类(一) ListView及各种Adapter详解

ListView就是列表组件,一般通过继承ListActivity使用系统提供的ListView.

所有的AdapterView组件都需要有一个对应的Adapter作为适配器来显示列表中元素的布局方式

见思维导图


AbsListView的常用XML属性:


android:choiceMode                  

设置ListView的选择行为 none:不显示任何选中项 singleChoice:允许单选  multipleChoice:允许多选 


android:drawSelectorOnTop

设置为true,则选中的列表项会显示在上面


android:fastScrollEnabled

是否允许快速滚动


android:listSelector

指定被选中的列表项上绘制的Drawable


android:scrollingCache

是否使用绘制缓存


android:smoothScrollBar

如果设置为false,则不在header View之后绘制分隔条


android:stackFromBottom 

设置是否从底端开始排列列表项


android:textFilterEnabled

设置是否对列表项进行过滤.当对应的Adapter实现了Filter接口时该属性才会起作用


android:transcriptMode

设置该组件的滚动模式。disable:关闭滚动,这是默认值   normal:当listView收到数据改变通知,且最后一个列表项可见时,将会自动滚动到底端。 alwaysScoll:该listView总会自动滚动到底端


android:divider

设置List列表项的分隔条(既可用颜色分隔,也可用Drawable分隔)


android:dividerHeight

分隔条高度


android:entries

指定一个数组资源,生成一个ListView


android:footerDividersEnabled

设置为false,则不在footer View之前绘制分隔条


android:headerDividersEnabled

设置为false,则不在header View之后绘制分隔条


ArrayAdapter: 数组或集合的适配器。

例:
private final String[] mous = {
        "郭嘉",
        "荀攸",
        "荀彧",
        "程昱",
        "戏志才",
        "徐庶"
};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mous);


也可以在XML文件中用android:entries属性指定绑定的数组资源文件
资源文件存放在value文件夹下
如:
android:entries="@array/book"

<string-array name="book" >
        <item >1</item>
        <item >2</item>
        <item >3</item>
        <item >3</item>
</string-array>   


例子

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >

    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

package com.light.android.study;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {
		
	    private final String[] mous = {
	      "郭嘉",
	      "荀攸",
	      "荀彧",
	      "程昱",
	      "戏志才",
	      "徐庶"
	    };
	    private ListView lv;
	    
		@Override
		public void onCreate(Bundle savedInstanceState)
		{
			super.onCreate(savedInstanceState);
			setContentView(R.layout.activity_main);
			init();
			initAdapter();
		}

		private void init(){
			lv = (ListView) findViewById(R.id.lv);
		}
		
		private void initAdapter(){
           ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mous);
           lv.setAdapter(adapter);
		}
		
}

效果:



SimpleAdapter:简单的Adapter实现

SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)
第一个参数是Context对象
第二个参数是保存有每一行数据的Map构成的List对象,也就是说,每一行数据里的每一个属性都由它的名字和它的值构成一个键值对,多个属性有多个键值对,每一行的多个键值对构成这一行的一个Map对象,这一行的Map对象对应到这个List中.
第三个参数布局文件Id
第四个参数Map对象中的多个键值对的key值
第五个参数布局文件中用来显示内容的组件ID


SimpleAdapter绑定数据到视图分两个阶段
1.首先,如果设置了SimpleAdapter.ViewBinder,那么这个设置的ViewBinder的setViewValue(android.view.View, Object, String)将被调用.如果setViewValue的返回值是true,则表示绑定已经完成,将不再调用系统默认的绑定实现.
2.如果返回值为false,视图将按以下顺序绑定数据:
•如果View实现了Checkable(例如CheckBox),期望绑定值是一个布尔类型.
•TextView.期望绑定值是一个字符串类型,通过调用setViewText(TextView, String)绑定.
•ImageView,期望绑定值是一个资源id或者一个字符串,通过调用setViewImage(ImageView, int) 或 setViewImage(ImageView, String)绑定数据.
如果没有一个合适的绑定发生将会抛出IllegalStateException.

例子:

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <!-- 定义一个List -->

    <ListView
        android:id="@+id/mylist"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    
</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- 定义一个ImageView,用于作为列表项的一部分。 -->

    <ImageView
        android:id="@+id/header"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        tools:ignore="ContentDescription" />

    <!-- 定义一个TextView,用于作为列表项的一部分。 -->

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:paddingLeft="10dp"
        android:textSize="16sp" />

</LinearLayout>

package com.light.android.study;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;

public class MainActivity extends Activity {
	private String[] names = new String[] { "杜甫", "弄玉", "清照", "李白" };
	private int[] imageIds = new int[] { R.drawable.tiger, R.drawable.nongyu,
			R.drawable.qingzhao, R.drawable.libai };

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		List<Map<String, Object>> listItems = new ArrayList<Map<String, Object>>();
		for (int i = 0; i < names.length; i++) {
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("header", imageIds[i]);
			map.put("personName", names[i]);
			listItems.add(map);
		}
		
		//創建SimpleAdapter
		SimpleAdapter simpleAdapter = new SimpleAdapter(this, listItems,
				R.layout.list_item_layout, new String[] { "personName", "header" },
				new int[] { R.id.name, R.id.header });
		
		ListView list = (ListView) findViewById(R.id.mylist);
		list.setAdapter(simpleAdapter);
	}
}

效果:



CursorAdapter:

Cursor型集合的适配器,与数据库连接的桥梁
其中游标携带的结果集中必须有列名为“_id”的列,否则这个类无法工作.
getView()
每一项显示的设置
bindView()
把数据和已经生成的View绑定在一起
newView()
新建一个View
CursorAdapter中源码 getView的实现
public View getView(int position, View convertView, ViewGroup parent) {
 View v;
 if (convertView == null) {
   v = newView(mContext, mCursor, parent);
 } else {
   v = convertView;
 }
 bindView(v, mContext, mCursor);
 return v;
}
getView首先使用已经存在的View,如果没有就调用newView 产生一个,然后把数据bind上去

我们一般使用其子类SimpleCursorAdapter来实现查询出来的数据List的Adapter

SimpleCursorAdapter(Context context,int layout,Cursor c,String[] from,int[] to,int flags)
context:当前Context对象
layout:布局文件Id
c:集合
from:需要绑定的集合中的列的名字
to:显示项目的View的id集合,在layout布局文件中定义
flags:用来判断适配器的行为标志
建议使用CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER
适配器会在游标上注册一个内容观测器,当通知到达时会调用 onContentChanged() 方法.使用该标志位时要注意:在注册观察器时需要先解除当前游标与适配器的关联,防止发生泄漏.
Android 3.0引入了CursorLoader实现异步加载数据,为了避免同步查询数据库时阻塞UI线程的问题。所以一般先将SimpleCursorAdapter中的Cursor对象放空,然后用CursorLoader对象加载数据,再放入适配器。
例子:

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>
Item布局文件:
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent" 
              android:layout_height="64dip" 
              android:orientation="horizontal" 
              android:gravity="center_vertical" 
              android:paddingLeft="8dip">
               
               <TextView android:id="@android:id/text1" 
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content" 
                         android:textSize="18sp" 
                         android:gravity="center_vertical" 
                         android:singleLine="true" 
                         android:fadingEdge="horizontal" 
                         android:fadingEdgeLength="3dip" 
                         android:ellipsize="marquee" 
                         /> 
</LinearLayout>

package com.light.android.study;

import android.app.ListActivity;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.SimpleCursorAdapter;

   //實現接口作為CursorLoader
public class MainActivity extends ListActivity implements LoaderCallbacks<Cursor> {
	private SimpleCursorAdapter mAdapter;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		// Create an empty adapter we will use to display the loaded data. 
		mAdapter = new SimpleCursorAdapter(MainActivity.this,
				R.layout.contacts_list_item, null,
				new String[] { ContactsContract.Contacts.DISPLAY_NAME },
				new int[] { android.R.id.text1 },
				0);
		
		getListView().setAdapter(mAdapter);
		
		//初始化Loader
		//第一個參數 為這個Loader對象指定唯一的标识ID,第二个可选参数,用来支持Loader的构造方法,
		//第三个参数是LoaderCallbacks接口类型
		getLoaderManager().initLoader(0, null, this);
	}

	public Loader<Cursor> onCreateLoader(int id, Bundle args) {
		// 去数据库读取数据等要消耗大量时间的操作放在 
		// 自定义 CursorLoader 的 onLoadInBackground
		return new MyCursorLoader(getApplicationContext());
	}

	
	public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
		// Swap the new cursor in.  (The framework will take care of closing the         
		// old cursor once we return.) 
		mAdapter.swapCursor(cursor); 
	}

	public void onLoaderReset(Loader<Cursor> arg0) {
		// This is called when the last Cursor provided to onLoadFinished()        
		// above is about to be closed.  We need to make sure we are no         
		// longer using it. 
		mAdapter.swapCursor(null);
	}

	public static class MyCursorLoader extends CursorLoader {
		String[] mContactProjection = { ContactsContract.Contacts._ID,
				ContactsContract.Contacts.DISPLAY_NAME };
		private Context mContext;

		public MyCursorLoader(Context context) {
			super(context);
			mContext = context;
		}

		/**
		 * 4.自定义 CursorLoader 的 onLoadInBackground
		 * 会返回一个Cursor,这里给SimpleCursorAdapter用 
		 * 来填充数据。查询数据等操作放在这里执行
		 */
		@Override
		protected Cursor onLoadInBackground() {
			Cursor cursor = mContext.getContentResolver().query(
					ContactsContract.Contacts.CONTENT_URI, mContactProjection,
					null, null, null);
			return cursor;
		}
	}
}

效果:


最后看下BaseAdapter

经常需要覆写的方法

boolean isEnabled (int position):

如果列表的一项item是separator(充当分隔项目,跟其他item项一样,也可以不一样,但是无法进行点击),返回true,也就是可以点击,并接收响应事件。如果此时position处的item是separator的话,返回false,也就无法响应点击或触摸事件,此项目是不可以点击的,表现形式为点了没任何反应,可以充当一个列表中的分隔,当然可以自定义这个分隔项的布局。

abstract Object getItem(int position)
得到位于position位置的项

public int getViewTypeCount() 

返回共有多少个不同的布局

abstract int getCount()
得到项目总数
abstract long getItemId(int position)
得到位于position位置的项的Id
public int getItemViewType(int position)
以int数值型返回itemView的类型。一般普通列表的item都是一样的布局,也就是说这个列表只有一种类型,但是很多时候我们需要列表显示不同的item,比如有的列表有普通item和separator两种类型,item用于响应用户点击事件,separator用于分隔item,不可以点击,这样这个列表就有了两种类型,重载这个方法,如果当前位置是item,我们可以返回1,如果是separator我们可以返回2,以此类推。
abstract View getView(int position,View convertVeiw,ViewGroup parent)
每一项显示的设置
在getView方法中需要缓存加载View优化ListView

官方建议使用ViewHolder,其实就是单例
例:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
 ViewHolder holder; 
 if (convertView == null) {
     final LayoutInflater inflater = (LayoutInflater)                        mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     convertView = inflater.inflate(R.layout.list_item_icon_text, null); 
      holder = new ViewHolder();  
      holder.icon = (ImageView) convertView.findViewById(R.id.icon); 
      holder.text = (TextView) convertView.findViewById(R.id.text); 
      convertView.setTag(holder); 
 } else { 
      holder = (ViewHolder) convertView.getTag(); 
 } 
      holder.icon.setImageResource(R.drawable.icon); 
      holder.text.setText(mData[position]); 
      return convertView; 
} 
static class ViewHolder { 
    ImageView icon; 
    TextView text; 
} 


知识点

①解决ListView滑动过程中背景颜色变黑的问题

android:cacheColorHint="#00000000" // setting transparent color
http://stackoverflow.com/questions/2833057/background-listview-becomes-black-when-scrolling/2833103#2833103


②ListView变圆角显示

定义一个shape:res/drawable/customshape.xml

<?xml version="1.0" encoding="UTF-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
     android:shape="rectangle"> 
     <gradient 
         android:startColor="#SomeGradientBeginColor"
         android:endColor="#SomeGradientEndColor" 
         android:angle="270"/> 

    <corners 
         android:bottomRightRadius="7dp" 
         android:bottomLeftRadius="7dp" 
         android:topLeftRadius="7dp" 
         android:topRightRadius="7dp"/> 
</shape> 
然后在XML中
android:background="@drawable/customshape"

http://stackoverflow.com/questions/1683185/how-do-i-create-a-listview-with-rounded-corners-in-android/1683195#1683195


③保留ListView上次滑动的位置

// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();

// ...

// restore index and position
mList.setSelectionFromTop(index, top);
说明:

ListView.getFirstVisiblePosition() returns the top visible list item. But this item may be partially scrolled out of view, and if you want to restore the exact scroll position of the list you need to get this offset. So ListView.getChildAt(0) returns the View for the top list item, and then View.getTop() returns its relative offset from the top of the ListView. Then, to restore the ListView's scroll position, we call ListView.setSelectionFromTop() with the index of the item we want and an offset to position its top edge from the top of the ListView.

http://stackoverflow.com/questions/3014089/maintain-save-restore-scroll-position-when-returning-to-a-listview





 



 




 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ListViewAndroid中最常用的UI组件之一,用于展示具有固定结构的大量数据。它继承自AdapterView,可以通过Adapter来动态地显示数据。 ListView的特点是:每一行数据都是相同的布局,并且每个Item的高度是相同的。因此,我们可以利用ListView来展示一些比较简单的数据列表。 在使用ListView时,我们需要实现一个Adapter来提供数据,并且可以对ListView的Item进行自定义布局。以下是一个简单的使用ListView的例子: ```java public class MainActivity extends AppCompatActivity { private ListView mListView; private List<String> mDataList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView = findViewById(R.id.list_view); // 初始化数据 mDataList = new ArrayList<>(); for (int i = 0; i < 50; i++) { mDataList.add("Item " + i); } // 创建适配器 ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mDataList); // 设置适配器 mListView.setAdapter(adapter); } } ``` 在这个例子中,我们使用了Android自带的ArrayAdapter作为ListView的适配器,并且使用了系统提供的简单列表项布局simple_list_item_1,将数据逐一展示在ListView中。 除了普通的ListViewAndroid还提供了一个ListActivity类,它是一个已经封装好的Activity,专门用来展示ListView。我们只需要继承ListActivity,实现一个适配器即可。以下是一个使用ListActivity的例子: ```java public class MainActivity extends ListActivity { private List<String> mDataList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化数据 mDataList = new ArrayList<>(); for (int i = 0; i < 50; i++) { mDataList.add("Item " + i); } // 创建适配器 ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mDataList); // 设置适配器 setListAdapter(adapter); } } ``` 在这个例子中,我们继承了ListActivity,并且在onCreate()方法中直接使用setListAdapter()方法设置适配器,省去了findViewById()和ListView的初始化过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值