今日长缨在手,何时缚住苍龙?–《清平乐·六 盘 山》
前言
本文继续总结Android中常用的控件,是对前面常识篇和基础篇的进一步补充,内容包括图像类视图,列表类视图,对话框,翻页类视图,视频视图,表面视图,纹理视图。关于控件的学习,首先要有个感性的认识,要知道它长啥样,会将数据以何种视觉效果呈现给用户,其次才是学习它的使用方法。本文的风格和前面的文章类似,总结控件的方式仍为:“控件作用+控件外观+控件使用方法”。另外,关于控件方面的总结文章还有最后的高级篇,主要总结屏幕显示,自定义控件,图形变换控制,碎片等方面。从常识篇到高级篇共四篇文章,比较全面的总结了工具类APP的GUI部分开发。限于篇幅,本小结由2P组成,P1总结图像类视图,列表类视图,P2总结对话框,翻页类视图,视频视图,表面视图,纹理视图。一、图像类视图
1.图形基础
(1)图形文件
Drawable(可绘制的)是Android中代表所有可以显示的图形的抽象类,这里说的图形包括图片和xml格式的描述性图形,图片是指众所周知的照片这类肉眼可见的。开发者通常将图形文件放在模块的res资源目录下,通过模块的文件组织结构可以看到res目录下有很多带drawable字样的文件夹:
①xml格式的描述性图形一般存放在\res\drawable目录中。
②而带有后缀格式的图片文件一般放在对应分辨率的\res\drawable-xxx目录中,开发者可将“.png , .jpg , .gif , .9.png”等格式的图片文件分别复制到具体的drawable目录中,如果不同分辨率的drawable目录中出现了同名的图片文件,则当APP运行时,Android系统会根据具体的手机屏幕分辨率来自动适配图片。
上述的.9.png格式的图片是一种特殊的png图片,称为点九图片,可以自定义拉伸区域保证图片显示清楚。AS集成了点九图片的制作工具,开发者只需对原始的png图片鼠标右键,选择“Create 9-Patch file”选项即可开始制作。点九图片是在原始图片的四周添加了宽度为1像素的线条,决定了图片的缩放规则,前景文字的显示规则。左侧和上侧的像素线条决定了图片拉伸过程中需要变形的边界,右侧和下侧的像素线条决定了图片前景添加的文字的显示边界。
这里总结3种xml格式的描述性图形:状态列表图形,层次图形,形状图形,它们都是Drawable的子类,都有特定的使用场景。
①状态列表图形StateListDrawable用于组织多个Drawable对象,并根据目标组件状态的变化,切换不同的图形,通常作为按钮,开关等具有状态变化的控件的背景图形。要创建状态列表图形,只需对drawable目录鼠标右键,选择New->Drawable Resource File,设置好文件名,选择根标签为selector即可创建。根标签selector一般包含两个子标签item,子标签item的属性android:color或android:drawable用于指定子标签代表的颜色或者图形,android:state_xxx指定子标签代表的一个状态,包括:state_pressed(是否按下),state_checked(是否选中),state_focused(是否获得焦点),state_selected(是否选中)。如果其中一个子标签item指定了控件一个状态,则另一个子标签item则默认代表控件的相反状态。
②层次图形LayerDrawable一般也包含两个图形,通常用于设置进度条,拖动条等条状显示的控件。创建方式同上,只需注意根标签选择layer-list,一般包含两个子标签item,子标签item的android:drawable属性用于指定图形文件,属性android:id用于指定目标控件的ID,通常取值Android自带的常量ID值,如:@android:id/background(设置控件的背景),@android:id/progress(设置控件的进度显示图形)。
③形状图形ShapeDrawable代表一个基本的几何图形,通常用于设置按钮,文本框等形状规则的控件的背景。创建方式同上,注意根标签选择shape,它的可用属性和子标签如下表:
实际开发中,形状图形常用的子标签有corners,solid,stroke,属性shape保持默认为矩形即可。
(2)图形引用方法:
Android SDK会自动加载res目录下的各种图形文件,并在R资源清单类中生成该图形文件的索引。开发者要在xml文件中引用图形文件时,则使用@drawable/xxx的形式,在代码中引用图形文件,则分为两种情况:
①方法的参数是指定资源ID的int类型,则使用R.drawable.xxx的形式指定图形。
②方法的参数是Drawable或其他类型的,则需使用Resources类的getDrawable方法,从指定资源ID的图形文件中生成Drawable对象,然后再具体情况具体处理。
2.图像视图
(1)作用和外观
图像视图ImageView继承自View,是专门用于显示图形的控件,可以装载来自各种来源(如资源或内容提供者)的图形,没有指定src属性则外观是一个空白区域,指定了图形来源后外观如下:
(2)使用方法
ImageView在xml文件中常用属性有:
①scaleType:指定图形的拉伸规则类型,取值范围有:fitXY(拉伸图形填满整个图像视图),fitStart(保持宽高比例,拉伸图形并放置在图像视图的左侧或上侧),fitCenter(保持宽高比例,拉伸图形并放置在图像视图的中间),fitEnd(保持宽高比例,拉伸图形并放置在图像视图的下侧或右侧),center(保持原始图形不变,并放置在图像视图的中间),centerCrop(拉伸图形直到填满整个图像视图,并居中显示),centerInside(当图形的大小大于图像视图时,相当于fitCenter,小于时,则等同于center)。
②src:指定图形的来源,并按照scaleType进行图形拉伸。
③background:设置图像视图的背景,但不按照scaleType拉伸图形。注意和src同时设置时,就相当于背景和前景的关系。
ImageView在代码中常用的方法有:
①void setImageAlpha(int alpha):设置图形的透明度。
②void setImageBitmap(Bitmap bm):设置ImageView显示位图对象。
③void setImageDrawable(Drawable drawable):设置ImageView显示Drawable对象。
④void setImageResource(int resId):设置ImageView显示指定ID的图形文件。
⑤void setImageURI(Uri uri):将此ImageView的内容设置为Uri指定的图形。
⑥void setScaleType(ImageView.ScaleType scaleType):控制如何调整图像的大小或移动以适应ImageView。
3.图像按钮
(1)作用,外观,使用方法
图像按钮ImageButton继承自ImageView,只是额外多了一个按钮的外观,就把它当做一个特殊的按钮使用即可,为它指定了src属性为@mipmap/ic_launcher_round的外观图:
补充:表格布局
(1)作用和外观
表格布局TableLayout采用行和列来管理子控件,但不显示其行,列或单元格的边框线。典型的布局如图:
(2)使用方法
TableLayout由多个TableRow子标签组成,TableRow控制着表格布局的行数和列数:每增加一个TableRow就增加一行,每往TableRow里添加一个控件就新增一列,若直接往TableLayout里添加一个控件,则表示该控件独自占用一行。
TableLayout的总宽度由其父布局定义,无论是在代码和xml中,列号是从零开始的,列可以缩小和拉伸,在这种情况下,该列将更改其宽度以占用父布局剩余空间。在代码中,可以通过调用setColumnCollapsed方法来隐藏一列,列的宽度由该列中具有最宽的子控件的宽度决定,但是,TableLayout可以通过调用setColumnShrinkable或setColumnStretchable方法将某些列指定为可收缩或可拉伸的: 如果一列被标记为可收缩,则可以收缩列宽以使TableLayout适合其父布局, 如果标记为可拉伸,则可以在宽度上扩展以占用额外的空间。 在xml文件中,通过设置shrinkColumns,collapseColumns和stretchColumns属性指定列号,可以实现控制列宽的效果,要同时指定多个列,则用“,”分隔列号。
二、列表类视图
1.列表视图
(1)作用和外观
列表视图ListView继承自AdapterView,它使用统一风格的布局来显示每一项数据,待展示的数据集合必须是可以抽象出同样的特征,在这个特征下的个体数据可以不同。比如常见的如全班成绩排名列表,抽象出来的特征是同学的名字,同学的照片,学号,各科成绩等。ListView的默认外观如图:
(2)使用方法
它的常用属性有:
①divider:指定分割线图形。
②dividerHeight:分割线图形的高度。
③footerDividersEnabled:是否显示列表结尾的分割线。
④headerDividersEnabled:是否显示列表开头的分割线。
它的常用方法有:
①void setAdapter(ListAdapter adapter):设置ListView的数据适配器,它的适配器一般继承自基本适配器BaseAdapter。
②void setDivider(Drawable divider):设置列表的分割线。
③void setDividerHeight(int height):设置列表中的每个列表项之间绘制的分隔线的高度。
④void setSelection(int position):设置当前选择的列表项。
⑤void setOnItemClickListener(AdapterView.OnItemClickListener listener):设置列表项的点击监听器。
⑥void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener):设置列表项的长按监听器。
⑦void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener):设置列表项被选中时的监听器。
乍一看,和前文基础篇中的Spinner控件使用方式类似,但使用时需要注意三点:
①通常将ListView的高度设置为具体高度或者match_parent,若ListView的高度设为wrap_content,则只会显示一行列表项,若ListView的下面还有其它控件,则需要将ListView的高度设为0dp,权重设为1。
②对列表项注册的长按监听器和上下文菜单需要分开处理,否侧APP会闪退。大致步骤是在长按事件处理完成之后,才触发上下文菜单,确保不会同时响应。
③若列表项中有EditText和Button等具有抢占焦点的子控件时,应把ListView的descendantFocusability属性设置为blocksDescendants来确保是列表项处理点击事件,而不是列表项中的子控件。
基本适配器BaseAdapter作为一个适配器基类,通过继承它可以最大限度的自定义列表项的子控件,基础篇中总结的ArrayAdapter,SimpleAdapter都是它的子类。
继承BaseAdapter的子类首先需要实现以下方法:
①构造函数:传入一个上下文Context对象,指定待展示的数据集,数据集通常是ArrayList类型。
②abstract int getCount():获取数据项的个数。
③abstract Object getItem(int position):获取数据集指定位置的数据项。
④abstract long getItemId(int position):获取与指定位置数据项关联的ID。
⑤abstract View getView(int position, View convertView, ViewGroup parent):获取指定位置的列表项的页面视图,并为视图填充数据。
其次子类还需定义一个内部类-视图持有者ViewHolder,类中要声明列表项的布局中所有的子控件类型,以便复用列表项的布局视图资源。
当列表项数目较多时,系统只会加载位于屏幕可见区域的列表项,其他项只有屏幕滚动到他们那里才会即时加载。如此,便可复用已经生成的列表项页面布局,在上述的getView方法中,参数convertView便代表一个列表项布局,若为null,则需要从布局文件中重新创建列表项的布局,若convertView非空,则调用convertView的getTag方法获取ViewHolder对象。当然,无论是复用缓存的还是会重新创建的列表项布局,都需要重新设置列表项中控件的显示数据。
前面的文章中都多次使用过列表视图了,这里便通过一个简单的例子再次熟悉一下吧,例子主要通过一个列表展示几个软件的图标,软件的名称,软件的作用简介:
首先创建一个软件描述的bean类SoftwareBean :
public class SoftwareBean {
public int image;//软件图标
public String name;//软件名称
public String desc;//软件描述
public SoftwareBean(int image, String name, String desc) {
this.image = image;
this.name = name;
this.desc = desc;
}
private static int[] iconArray = {R.drawable.as, R.drawable.qq, R.drawable.browser,
R.drawable.huya, R.drawable.keil5, R.drawable.ad19};
private static String[] nameArray = {"as", "qq", "browser", "huya", "keil5", "ad19"};
private static String[] descArray = {
"Android开发集成工具",
"腾讯聊天工具",
"双核浏览器",
"虎牙直播软件",
"单片机开发工具",
"电路板设计工具"
};
public static ArrayList<SoftwareBean> getDefaultList() {
ArrayList<SoftwareBean> planetList = new ArrayList<>();
for (int i = 0; i < iconArray.length; i++) {
planetList.add(new SoftwareBean(iconArray[i], nameArray[i], descArray[i]));
}
return planetList;
}
}
其次创建一个布局文件software_list.xml,代表一个列表项的页面布局,如图:
然后创建一个继承自BaseAdapter的子类SoftwareAdapter并实现列表项的点击,长按监听接口:
public class SoftwareAdapter extends BaseAdapter implements
AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
private Context mContext; // 声明一个上下文对象
private ArrayList<SoftwareBean> mSoftwareList; // 声明一个工具软件队列
// 工具软件适配器的构造函数,传入上下文与工具软件队列
public SoftwareAdapter(Context context, ArrayList<SoftwareBean> software_list) {
mContext = context;
mSoftwareList = software_list;
}
// 获取列表项的个数
public int getCount() {
return mSoftwareList.size();
}
// 获取列表项的数据
public Object getItem(int arg0) {
return mSoftwareList.get(arg0);
}
// 获取列表项的编号
public long getItemId(int arg0) {
return arg0;
}
// 获取指定位置的列表项视图
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) { // 转换视图为空
holder = new ViewHolder(); // 创建一个新的视图持有者
// 根据布局文件item_list.xml生成转换视图对象
convertView = LayoutInflater.from(mContext).inflate(R.layout.software_list, null);
holder.iv_icon = convertView.findViewById(R.id.iv_icon);
holder.tv_name = convertView.findViewById(R.id.tv_name);
holder.tv_desc = convertView.findViewById(R.id.tv_desc);
// 将视图持有者保存到转换视图当中
convertView.setTag(holder);
} else { // 转换视图非空
// 从转换视图中获取之前保存的视图持有者
holder = (ViewHolder) convertView.getTag();
}
//注意,无论是否复用缓存,接下来都需要为控件重新设置数据
SoftwareBean software = mSoftwareList.get(position);
holder.iv_icon.setImageResource(software.image); // 显示工具软件的图片
holder.tv_name.setText(software.name); // 显示工具软件的名称
holder.tv_desc.setText(software.desc); // 显示工具软件的描述
return convertView;
}
// 定义一个视图持有者,以便重用列表项的视图资源
public final class ViewHolder {
public ImageView iv_icon; // 声明工具软件图片的图像视图对象
public TextView tv_name; // 声明工具软件名称的文本视图对象
public TextView tv_desc; // 声明工具软件描述的文本视图对象
}
// 处理列表项的点击事件,由接口OnItemClickListener触发
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String desc = String.format("您点击了第%d个工具软件,它的名字是%s", position + 1,
mSoftwareList.get(position).name);
Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show();
}
// 处理列表项的长按事件,由接口OnItemLongClickListener触发
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
String desc = String.format("您长按了第%d个工具软件,它的名字是%s", position + 1,
mSoftwareList.get(position).name);
Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show();
return true;
}
}
activity的页面布局就只放置一个ListView,列表视图初始化代码如下:
//初始化列表视图
private void initSoftwareList() {
ArrayList<SoftwareBean> mSoftwareList = SoftwareBean.getDefaultList();
// 构建一个软件队列的列表适配器
SoftwareAdapter adapter = new SoftwareAdapter(this, mSoftwareList);
// 从布局视图中获取名叫lv_software的列表视图
ListView lv_software = findViewById(R.id.lv_software);
// 给lv_software设置软件列表适配器
lv_software.setAdapter(adapter);
// 给lv_software设置列表项的点击监听器
lv_software.setOnItemClickListener(adapter);
// 给lv_software设置列表项的长按监听器
lv_software.setOnItemLongClickListener(adapter);
}
进入页面后点击列表项后,效果如图:
2.可展开的列表视图
(1)作用和外观
可展开的列表视图ExpandableListView是ListView的子类,它的每个列表项(分组)可以展开为更多的子列表项,用户点击一个分组,即可展开该分组下的子列表项,再次点击该时,则会收起子列表,它的默认外观如图:
(2)使用方法
ExpandableListView的常用方法有:
①void setAdapter(ExpandableListAdapter adapter):设置可展开列表视图的适配器。
②boolean expandGroup(int groupPos):展开指定的分组。
③boolean collapseGroup(int groupPos):折叠指定分组。
④boolean isGroupExpanded(int groupPosition):给定的分组是否展开。
⑤void setSelectedGroup(int groupPosition):选中指定位置的分组。
⑥boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup):选中指定分组中指定的子项。
⑦void setOnChildClickListener(ExpandableListView.OnChildClickListener onChildClickListener)
void setOnGroupClickListener(ExpandableListView.OnGroupClickListener onGroupClickListener)
void setOnGroupCollapseListener(ExpandableListView.OnGroupCollapseListener onGroupCollapseListener)
void setOnGroupExpandListener(ExpandableListView.OnGroupExpandListener onGroupExpandListener):见名知意,设置分组,子项的点击监听器,设置分组展开,折叠的监听器。
⑧void setChildIndicator(Drawable childIndicator)
void setGroupIndicator(Drawable groupIndicator):设置要在分组,子项左边绘制的图形。
可展开的列表适配器ExpandableListAdapter 是一个接口,通常它的子类主要实现五个方法:
①public int getGroupCount():获取分组的个数。
②public int getChildrenCount(int groupPosition):获取指定分组中子项的个数。
③public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent):获取分组的布局视图,并为布局中的控件设置数据。
④public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent):获取指定分组中每个子项的布局视图,并为布局中的控件设置数据。
⑤public boolean isChildSelectable(int groupPosition, int childPosition):是否选中指定分组中的指定子项。
从以上方法来看,ExpandableListAdapter的使用方法和BaseAdapter类似,无非是BaseAdapter只配置一个列表,而另一个要同时配置两个列表:Group(分组)和Child(子项)列表罢了。
限于篇幅,这里不再举例。
3.循环视图
(1)作用和外观
循环视图RecyclerView是功能强大,使用广泛的控件,它可以用列表布局的方式展示数据,也可用网格布局的方式展示数据。如果你在模块中第一次使用这个控件,则需要先在GUI设计界面点击RecyclerView下载,或者在build.gradle文件中的dependencies节点添加implementation 'androidx.recyclerview:recyclerview:1.0.0'
,它的默认外观如图:
(2)使用方法
RecyclerView的常用方法有:
①void setAdapter(Adapter adapter):设置适配器,通常需要继承RecyclerView.Adapter。
②void setLayoutManager(RecyclerView.LayoutManager layout):设置RecyclerView将使用的布局管理器,取值范围有:LinearLayoutManager(列表状的线性布局管理器),GridLayoutManager(网格布局管理器),StaggeredGridLayoutManager(瀑布流网格布局管理器)。
③void addItemDecoration(RecyclerView.ItemDecoration decor):添加数据项之间的分割线。
④void removeItemDecoration (RecyclerView.ItemDecoration decor):删除数据项之间的分割线。
⑤void setItemAnimator(RecyclerView.ItemAnimator animator):添加数据项增,删的展示动画效果,如果不自定义动画,则可以使用默认动画DefaultItemAnimator。
⑥void addOnItemTouchListener(RecyclerView.OnItemTouchListener listener):添加数据项的触摸监听器,需要开发者在这里自己区分点击和长按事件。
⑦void removeOnItemTouchListener(RecyclerView.OnItemTouchListener listener):移除触摸监听器。
⑧void scrollToPosition(int position):滚动到指定位置的数据项。
循环视图适配器RecyclerView.Adapter需要实现三个方法来完成数据项的布局视图和设置子控件中的数据内容:
①abstract int getItemCount():返回适配器持有的数据项总数。
②abstract VH onCreateViewHolder(ViewGroup parent, int viewType):创建整个布局的视图持有者并保存。
③abstract void onBindViewHolder(VH holder, int position):绑定视图持有者,并设置子控件显示内容。
方法②和③组合起来相当于BaseAdapter的getView方法,当getView的参数convertView为空时,重新创建的过程就在方法②onCreateViewHolder中处理,为子控件绑定数据的代码由③onBindViewHolder实现。
当适配器持有的数据发生增删时,可使用适配器的以下方法来通知适配器来更新页面:
①final void notifyItemInserted(int position):通知适配器在 position已新插入数据项。
②final void notifyItemMoved(int fromPosition, int toPosition):通知适配器在指定范围的数据项已被删除。
③final void notifyDataSetChanged():通知适配器持有的数据集合已经发生了改变。
④final void notifyItemChanged(int position):通知适配器position位置的数据项已变化。
循环视图的视图持有者RecyclerView.ViewHolder也是一个抽象类,它的子类也需要声明数据项的布局中所有的子控件,并在构造函数中传入具体的布局视图来实例化这些控件。
循环视图布局管理器RecyclerView.LayoutManager是循环视图的核心,决定着数据项以何种方式展示,而且setLayoutManager方法可以动态的改变原有布局方式。
LinearLayoutManager(列表状的线性布局管理器)的常用方法有:
①LinearLayoutManager(Context context, int orientation, boolean reverseLayout):构造函数,指定列表方向和是否从相反方向布局。
②void setOrientation(int orientation):设置布局的方向:HORIZONTAL或者VERTICAL。
③void setReverseLayout(boolean reverseLayout):是否从相反方向布局,默认false。
GridLayoutManager(网格布局管理器)的常用方法有:
①GridLayoutManager(Context context, int spanCount):构造函数,指定网格的列数。
②void setSpanCount(int spanCount):设置网格布局的列数。
③void setSpanSizeLookup(GridLayoutManager.SpanSizeLookup spanSizeLookup):设置网格的列占用规则。默认一个数据项占一列,开发者可以通过继承SpanSizeLookup实现他的abstract int getSpanSize(int position)方法自定义占位规则,识别特定的position数据项,返回它需要占用的列数。
StaggeredGridLayoutManager(瀑布流网格布局管理器)可以根据具体数据项来展示不同大小的布局,只要在适配器中动态设置好每个网格的高度,系统就会在页面依次瀑布般排列展示不同大小的数据项。它的常用方法有:
①StaggeredGridLayoutManager(int spanCount, int orientation):用给定的列数和方向创建一个StaggeredGridLayoutManager实例。
②void setSpanCount(int spanCount):设置网格布局的列数。
③void setOrientation(int orientation):设置布局的方向:HORIZONTAL或者VERTICAL。
接下来通过使用这三种不同布局的例子来熟悉使用流程,首先在页面中放置三个循环视图,数据项分别用列表状的线性布局,网格布局,瀑布流网格布局来展示:
接着,数据项根据不同的布局方式,自定义在不同布局中的外观:
数据项在列表状的线性布局中的外观item_linear.xml如下:
数据项在网格布局中的外观item_grid.xml如下:
数据项在瀑布流网格布局中的外观item_staggered.xml如下:
以上设计好了数据项的展示相关的页面布局,接下来需要为不同的布局定义适配器来提供数据项的数据来源,当然,这三个适配器唯一的区别就是根据数据项的外观不同创建并绑定不同的布局,然后设置显示内容。
自定义列表状的线性布局适配器RecyclerLinearAdapter :
public class RecyclerLinearAdapter extends RecyclerView.Adapter<ViewHolder> {
private Context mContext; // 声明一个上下文对象
private ArrayList<SoftwareBean> mSoftwareList;
public RecyclerLinearAdapter(Context context, ArrayList<SoftwareBean> software_list) {
mContext = context;
mSoftwareList = software_list;
}
// 获取列表项的个数
@Override
public int getItemCount() {
return mSoftwareList.size();
}
// 这里返回每项的类型,开发者可自定义头部类型与一般类型,
//然后在onCreateViewHolder方法中根据类型加载不同的布局,从而实现带头部的网格布局
@Override
public int getItemViewType(int position) {
return 0;
}
// 获取列表项的序号
@Override
public long getItemId(int position) {
return position;
}
// 创建列表项的视图持有者
@Override
public ViewHolder onCreateViewHolder(ViewGroup vg, int viewType) {
// 根据布局文件item_linear.xml生成视图对象
View v = LayoutInflater.from(mContext).inflate(R.layout.item_linear, vg, false);
return new ItemHolder(v);
}
// 绑定列表项的视图持有者
@Override
public void onBindViewHolder(ViewHolder vh, final int position) {
ItemHolder holder = (ItemHolder) vh;
holder.iv_pic.setImageResource(mSoftwareList.get(position).image);
holder.tv_title.setText(mSoftwareList.get(position).name);
holder.tv_desc.setText(mSoftwareList.get(position).desc);
// 实现列表项的点击事件
holder.ll_item.setOnClickListener(v -> {
String desc = String.format("您点击了第%d项,名称是%s", position + 1,
mSoftwareList.get(position).name);
Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
});
// 实现列表项的长按事件
holder.ll_item.setOnLongClickListener(v -> {
String desc = String.format("您长按了第%d项,名称是%s", position + 1,
mSoftwareList.get(position).name);
Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
return true;
});
}
// 定义列表项的视图持有者
public class ItemHolder extends ViewHolder {
public LinearLayout ll_item; // 声明列表项的线性布局
public ImageView iv_pic; // 声明列表项图标的图像视图
public TextView tv_title; // 声明列表项标题的文本视图
public TextView tv_desc; // 声明列表项描述的文本视图
public ItemHolder(View v) {//构造函数里需要传入网格项的布局视图
super(v);
ll_item = v.findViewById(R.id.ll_item);
iv_pic = v.findViewById(R.id.iv_pic);
tv_title = v.findViewById(R.id.tv_title);
tv_desc = v.findViewById(R.id.tv_desc);
}
}
}
自定义网格布局的适配器RecyclerGridAdapter :
public class RecyclerGridAdapter extends RecyclerView.Adapter<ViewHolder> {
private Context mContext; // 声明一个上下文对象
private ArrayList<SoftwareBean> mSoftwareList;
public RecyclerGridAdapter(Context context, ArrayList<SoftwareBean> software_list) {
mContext = context;
mSoftwareList = software_list;
}
// 获取网格项的个数
@Override
public int getItemCount() {
return mSoftwareList.size();
}
// 获取网格项的类型,不做处理
@Override
public int getItemViewType(int position) {
return 0;
}
// 获取网格项的序号
@Override
public long getItemId(int position) {
return position;
}
// 创建网格项的视图持有者,相当于BaseAdapter的convertView为null的处理
@Override
public ViewHolder onCreateViewHolder(ViewGroup vg, int viewType) {
// 根据网格项布局文件item_grid.xml生成视图对象
View v = LayoutInflater.from(mContext).inflate(R.layout.item_grid, vg, false);
return new ItemHolder(v);
}
// 绑定网格项的视图持有者,与BaseAdapter的处理相同,不过需要自己绑定事件监听器
@Override
public void onBindViewHolder(ViewHolder vh, final int position) {
ItemHolder holder = (ItemHolder) vh;
holder.iv_pic.setImageResource(mSoftwareList.get(position).image);
holder.tv_title.setText(mSoftwareList.get(position).name);
// 实现网格项的点击事件
holder.ll_item.setOnClickListener(v -> {
String desc = String.format("您点击了第%d项,名称是%s", position + 1,
mSoftwareList.get(position).name);
Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
});
// 实现网格项的长按事件
holder.ll_item.setOnLongClickListener(v -> {
String desc = String.format("您长按了第%d项,名称是%s", position + 1,
mSoftwareList.get(position).name);
Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
return true;
});
}
// 定义网格项的视图持有者
public class ItemHolder extends ViewHolder {
public LinearLayout ll_item; // 声明网格项的线性布局
public ImageView iv_pic; // 声明网格项图标的图像视图
public TextView tv_title; // 声明网格项标题的文本视图
public ItemHolder(View v) {//构造函数里需要传入网格项的布局视图
super(v);
ll_item = v.findViewById(R.id.ll_item);
iv_pic = v.findViewById(R.id.iv_pic);
tv_title = v.findViewById(R.id.tv_title);
}
}
}
自定义瀑布流网格布局的适配器RecyclerStaggeredAdapter :
public class RecyclerStaggeredAdapter extends RecyclerView.Adapter<ViewHolder> {
private Context mContext; // 声明一个上下文对象
private ArrayList<SoftwareBean> mSoftwareList; // 声明一个工具软件队列
public RecyclerStaggeredAdapter(Context context, ArrayList<SoftwareBean> software_list) {
mContext = context;
mSoftwareList = software_list;
}
// 获取瀑布流网格项的个数
@Override
public int getItemCount() {
return mSoftwareList.size();
}
// 获取瀑布流网格项的类型
@Override
public int getItemViewType(int position) {
return 0;
}
// 获取瀑布流网格项的序号
@Override
public long getItemId(int position) {
return position;
}
// 创建瀑布流网格项的视图持有者
@Override
public ViewHolder onCreateViewHolder(ViewGroup vg, int viewType) {
// 根据布局文件item_staggered.xml生成视图对象
View v = LayoutInflater.from(mContext).inflate(R.layout.item_staggered, vg, false);
return new ItemHolder(v);
}
// 绑定瀑布流网格项的视图持有者
@Override
public void onBindViewHolder(ViewHolder vh, final int position) {
ItemHolder holder = (ItemHolder) vh;
holder.iv_pic.setImageResource(mSoftwareList.get(position).image);
holder.tv_title.setText(mSoftwareList.get(position).name);
// 实现瀑布流网格项的点击事件
holder.ll_item.setOnClickListener(v -> {
String desc = String.format("您点击了第%d项,名称是%s", position + 1,
mSoftwareList.get(position).name);
Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
});
// 实现瀑布流网格项的长按事件
holder.ll_item.setOnLongClickListener(v -> {
String desc = String.format("您长按了第%d项,名称是%s", position + 1,
mSoftwareList.get(position).name);
Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
return true;
});
}
// 定义瀑布流网格项的视图持有者
public class ItemHolder extends ViewHolder {
public LinearLayout ll_item; // 声明瀑布流网格项的线性布局
public ImageView iv_pic; // 声明瀑布流网格项图标的图像视图
public TextView tv_title; // 声明瀑布流网格项标题的文本视图
public ItemHolder(View v) {//构造函数里需要传入网格项的布局视图
super(v);
ll_item = v.findViewById(R.id.ll_item);
iv_pic = v.findViewById(R.id.iv_pic);
tv_title = v.findViewById(R.id.tv_title);
}
}
}
最后,在Activity中分别配置三个循环视图,配置的流程都是一样的:
// 初始化线性布局的循环视图
private void initRecyclerLinear() {
// 从布局文件中获取名叫rv_linear的循环视图
RecyclerView rv_linear = findViewById(R.id.rv_linear);
// 创建一个垂直方向的线性布局管理器
LinearLayoutManager manager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
// 设置循环视图的布局管理器
rv_linear.setLayoutManager(manager);
// 构建线性布局的适配器
RecyclerLinearAdapter adapter = new RecyclerLinearAdapter(this, SoftwareBean.getDefaultList());
// 设置适配器
rv_linear.setAdapter(adapter);
// 设置默认动画效果
rv_linear.setItemAnimator(new DefaultItemAnimator());
}
// 初始化网格布局的循环视图
private void initRecyclerGrid() {
// 从布局文件中获取名叫rv_grid的循环视图
RecyclerView rv_grid = findViewById(R.id.rv_grid);
// 创建一个垂直方向的网格布局管理器,每行3列
GridLayoutManager manager = new GridLayoutManager(this,3);
// 设置占位规则
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position == 0) { // 第一项占据两列
return 2;
} else { // 为其它项占据一列
return 1;
}
}
});
// 设置循环视图的布局管理器
rv_grid.setLayoutManager(manager);
// 构建一个网格适配器
RecyclerGridAdapter adapter = new RecyclerGridAdapter(this, SoftwareBean.getDefaultList());
// 给rv_grid设置适配器
rv_grid.setAdapter(adapter);
// 设置rv_grid的默认动画效果
rv_grid.setItemAnimator(new DefaultItemAnimator());
}
// 初始化瀑布流布局的循环视图
private void initRecyclerStaggered() {
// 从布局文件中获取名叫rv_staggered的循环视图
RecyclerView rv_staggered = findViewById(R.id.rv_staggered);
// 创建一个垂直方向的瀑布流布局管理器
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(
3, RecyclerView.VERTICAL);
// 设置循环视图的布局管理器
rv_staggered.setLayoutManager(manager);
// 构建一个瀑布流适配器
RecyclerStaggeredAdapter adapter = new RecyclerStaggeredAdapter(this, SoftwareBean.getDefaultList());
// 给rv_staggered设置瀑布流适配器
rv_staggered.setAdapter(adapter);
// 设置rv_staggered的默认动画效果
rv_staggered.setItemAnimator(new DefaultItemAnimator());
}
那么这个例子使用三种不同布局的循环视图的效果图如下: