接下来一起来认识更高级的控件和更丰富的监听器,以及学会如何使用适配器搭建起数据源和视图界面的桥梁。高级控件篇第的一部分将围绕适配器讲解它是如何在某些高级控件发挥重要作用。本篇控件清单:
1. ListView 列表
2. Spinner 下拉列表
3. GridView 网格视图
4. ViewPager 视图滑动切换工具
那什么是数据适配器Adapter呢?如开头所说,它的作用是把复杂的数据填充在指定视图界面上。常用两种Adapter:ArrayAdapter(用于绑定单一的数据,数据源是数组或集合),SimpleAdapter(用于绑定格式复杂的数据,数据源是特定的泛型集合)。接下来将以ListView为例,看看这两种适配器的使用方法和效果。
1.ListView列表
ListView是最为常用的控件之一,它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示。下图是一个每项数据只有一行文本的ListView效果图,对于这类单一数据,用ArrayAdapter加载数据再合适不过,接下来一起学习一下。
布局界面只需要一个ListView,设置好宽高和id就够了。另外,还常用属性android:divider设置列表分割线的颜色,如透明色#00000000.
在MainActivity用id找到布局中的ListView之后,就是加载适配器的过程了:
可以看到使用过程无非三个步骤:数据源准备->适配器加载数据源->控件加载适配器,在关键的第二步对ArrayAdapter初始化中,提供的三个参数完成了在哪里显示、每一项数据如何显示(这里直接使用安卓提供好一个布局)、显示哪些数据及有多少项这些任务,再set到ListView上,就实现了一开始看到的界面效果。所以ListView只负责加载和管理视图,其他显示内容都是交给Adapter去做的。
当然ListView的每一项Item都是可以被监听的,监听器是OnItemClickListener,其中返回的参数position表示被点击的某项在整个List中的位置,从0起算,这样就能用ListView的getItemAtPosition()方法获取到被点击项的内容:
当点击第一项“wifi”时效果如下:
接下来再看一个页面效果:
在这个ListView能看到每个Item不再是简单的一行,有文字也有图片,这种格式复杂的数据就要用到SimpleAdapter了,还是在main.xml里准备好ListView控件,再回到MainActivity来学习如何用之前学会的三步骤来加载SimpleAdapter吧!
SimpleAdapter
第一步准备数据源,可以看到数据源dataList是一个特定的泛型集合,这里String代表文字,Object代表图片,然后调用getData()初始化dataList。
每一个Map对应一项Item,为了方便用for循环让每个Item里图标都一样,文字内容递增就可以,然后添加到dataList,这样就完成一个有20项Item的List。这里注意Map键值对里的键名,后面会需要。
第二步适配器加载数据源,在此之前,新建item.xml,这个不难理解,因为在ArrayAdpter例子里我们直接使用系统提供的布局而已。注意要给出TextView和ImageView的id,马上就会用到。
现在又到了关键一步,SimpleAdapter初始化比较复杂,需要用到五个参数,前三个容易理解,后两个就是之前需要留心的两个要点。这一步实现了控件与数据的一一绑定。最后一步加载适配器就大功告成了!
现在再介绍ListView上常用的监听器OnScrollListener,用于监听滚动变化,当用户拉到列表最底下的时候可帮助视图在滚动中加载数据。现在为列表设置监听器listView.setOnScrollListener(this),并实现onScrollStateChanged ()、onScroll()方法。
这里重写第一个方法,能看到事件会返回一个scrollState,它有三个状态值,下图打印出详细描述。因为需要在视图一直滑动到底端给出新的Item,为dataList增添新的map之后,要用到adpter非常关键的方法notifyDataSetChanged()通知适配器数据发生了变化要重新加载数据,这再次印证之前所说数据的显示是适配器的工作而不是列表。
效果如下,可以看到当用户看完20项继续向下拖时就会有源源不断的新内容更新上来。
代码如下:
private ListViewlistView;
private SimpleAdaptersimpleAdapter;
private List>dataList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listView);
dataList =new ArrayList<>();
dataList = getData();
// simpleAdapter = new SimpleAdapter(this, dataList, ,new String[]{"image", "text"},new int[]{R.id.iv_image, R.id.tv_textView});
simpleAdapter=new SimpleAdapter(MainActivity.this,dataList,R.layout.item,new String[]{"image", "text"},new int[]{R.id.iv_image, R.id.tv_textView});
listView.setAdapter(simpleAdapter);
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState){
case SCROLL_STATE_FLING:
Log.i("zyl", "手指离开屏幕,但由于惯性视图仍在滑动");
Map map =new HashMap<>();
map.put("image", R.mipmap.ic_launcher);
map.put("text", "increase");
dataList.add(map);
simpleAdapter.notifyDataSetChanged();
break;
case SCROLL_STATE_IDLE:
Log.i("zyl", "手机离开屏幕,视图已经停止滑动");
break;
case SCROLL_STATE_TOUCH_SCROLL:
Log.i("zyl", "手指没有离开屏幕,视图仍在滑动");
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
private List>getData(){
for (int i=1; i <20; i++){
Map map =new HashMap<>();
map.put("image", R.mipmap.ic_launcher);
map.put("text", "file" + (i +1));
dataList.add(map);
}
return dataList;
}
学完这两个常用适配器使用和适用情况之后,对比可看出ArrayAdapter使用起来明显简单许多,思考一个问题,ArrayAdapter的第二个参数如果不用系统提供的列表项布局而是自定义布局,是否也能做到图文并存的效果呢?答案是肯定的,只不过需要自定义一个适配器继承ArrayAdapter并重写一些方法了。下面就来学习如何定制一个ListView界面吧!
这次做一个更好看的界面,准备好小动物的图片就可以开始大展身手了!
回忆一下实例化一个ArrayAdapter时需要的三个参数,其中列表项布局以及适配器的适配类型都是要重新考虑的。那么先就从这开始准备吧!
每个Item都是由左边一张图片和右边一行文本组成的,下面代码中需要解释的是使用tools:的属性在我们预览能看到效果但不会出现在运行后的布局,方便我们提前看效果又不至于影响后续工作。
接着需要准备一个实体类Animal作为适配器的适配类型,这个类里提供动物图片和名称两个属性、用来初始化属性的构造方法以及对应的get方法即可。
然后到了关键一步,创建一个自定义的适配器且继承ArrayAdapter,重写父类一组含三个参数的构造函数,并将列表项子布局的id保存下来。接着重写getView()方法,先用getItem(position)得到当前Item项的Animal实例,再用LayoutInflater系列方法把子布局传入当前布局得到一个View,接着调用这个View的findViewById()找到ImageView和TextView实例,这样就可以把从当前项对象get的内容设置到这两个控件里去显示图片和文字了。
一切准备就绪之后,后面的步骤基本信手拈来了,相信下面这段代码你一定没问题了。
点击某个Item也会有响应:
这里对getView()多提几句,如果我们只是用上面几行代码来运行ListView的话效率会非常低,因为每次为了要显示每个子项去调用getView()方法后都会将布局重新加载一遍,如果能将显示过的Item View缓存起来,以后出现直接复用就能达到提升ListView运行效率的效果了。优化后代码如下:
以下是源代码:
public View getView(intposition,View convertView,ViewGroup parent){Animal animal=getItem(position);View view;ViewHolder viewHolder;if(convertView==null){view=LayoutInflater.from(getContext()).inflate(resourceId,null);viewHolder=new ViewHolder();viewHolder.imageView=(ImageView)view.findViewById(R.id.animal_image);viewHolder.textView=(TextView)view.findViewById(R.id.animal_name);view.setTag(viewHolder);}else{view=convertView;viewHolder=(ViewHolder)view.getTag();}viewHolder.textView.setText(animal.getAnimalName());viewHolder.imageView.setImageResource(animal.getImageId());returnview;}classViewHolder{ImageView imageView;TextView textView;}}
到此学了这么多,相信你对适配器可以熟练使用了吧!只要三步就搞定。想必在其他控件上应用适配器也很容易了,下面快来再认识两个高级控件。
2.Spinner 下拉列表
与ListView类似的,每个下拉列表项对应一个Item,列表项内容一般是文字,用ArrayAdapter就能做到,触类旁通,相信做一个下图所示的下拉列表已经难不倒你了!
选择系统提供的一个布局作为Spinnner的菜单样式,注意是设置在适配器上,这里给Spinner安装监听器是OnItemSelectListener,用适配器和列表都可以定位到某Item,完成后效果如下:
3.GridView 网格视图
从名字中能看出来GridView的特点,它使得每个Item以网格的形式展现,除此之外使用方式和ListView非常相似。下面准备用SimpleAdapter做一个这样的Demo:
GirdView本身还有些常用的属性:android:verticalSpacing(两列之间的间距),android:horizontalSpacing(两行之间的间距),android:numColumns(每行显示多少列,选值为auto_fit表示自动适应展示几列)。
接下来就是GridView绑定SimpleAdapter的过程了,不再细说,需要强调这里把图标和文字分别放在两个数组中且一一对应以便能通过循环得到数据源dataList。监听器是OnItemClickListener。
最后为了界面美观,在注册该活动时候设置theme是Black且NoTitleBar,注意被设置成android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"的活动一定继承的是android.app包下Activity,如果是V7兼容包下的AppCompatActivity会导致程序崩溃无法打开。点击运行来看看是不是达到上面的效果了呢?
其实除了这两个常用Adatper,还有一些Adapter也实用,下面通过ViewPager控件再来认识一个Adapter。
4.ViewPager 视图滑动切换工具
ViewPager是android扩展包v4包中的类,这个类可以让用户左右切换当前的视图(View、Fragment都可以),很多APP都用到这个功能,可见其重要程度,因此想用这点篇幅详解ViewPager是完全不够的,这里就仅仅给大家介绍用来帮助ViewPager管理View数据源的适配器PagerAdapter,感受一下风格各样的适配器。
首先在布局里导入v4包两个控件,其中PagerTabStrip是ViewPager子标签,包含在ViewPager里,这里用它作标题。
由于PagerAdapter是抽象类,使用时需要自定义子类。初始化时让这个适配器获取到两个数据源List:页卡List和标题List,之后重写几个方法更好的完善这个适配器的功能。
接着三步骤,在主活动准备好两个List,这里用View.inflate ()方法将布局转化成View对象,数据加载到自定义适配器上,adapter加载到ViewPager即可,又给ViewPager设置监听器OnPageChangeListener监听页卡是否发生变化。另外,我们还获取到控件PagerTabStrip去给标题做些美化工作。
最后效果如图,手指左右滑动就可以实现页面切换了。
其实所有这些Adapter都是从父类BaseAdapter扩展而来的,也就是说我们也可以根据自己的需要自定义一个Adapter继承BaseAdapter,然后具体实现下面4个方法:
由于adapter中含有要显示的数据集合,数据集合中元素个数即可被展示的View个数,每个数据的获取、每个Item View的样式都由adapter控制,每个position位置上数据都绑定到Item View上,这样数据和视图也就结合在一起了。由于篇幅原因不在这里接着具体展开,后续再深入探究。