ListView中能嵌套使用'GridView'吗?
2015-09-28 23:24
549 查看
在做app的时候或许都会遇到这样的需求,在一个ListView中显示类似GridView的情况。很容易想到的做法是在ListView中嵌套使用GridView,并且很多的人也在这样做。网上搜索ListView中嵌套使用GridView基本很大的一部分都会这样做。
首先看看这样怎么做,在ListView中嵌套GridView,需要的是将GridView的高度计算出来,不然GridView就可以滑动了。网上的做法,是自定义一个MyGridView(继承GridView),然后实现onMeasure()方法,计算出它的实际高度。(网上大神的源代码)
看看stackoverflow中大神的回答。Grid of images inside ScrollView
也就是说如果当GridView回收的比较频繁的时候,是不适合使用这样自定义GridView的方式的。那么是ListView中不能使用‘GridView’的了吗?但是,他的意思是自定义一个布局来显示。
也就是可以继承LinearLayout来实现,每行两个或者三个GridView的item,知道共有多少个,然后就可以自定义一个布局来实现。在这个布局中需要实现的是计算出它有多少行多少列。好吧,把数据的填充也写在这里面,但是,是的,但是,如何对GridView进行复用?Adapter中有这样的两个方法,getViewTypeCount()和getItemViewType(),可以知道ListView中是可以用多种不同类型的item,也就是说我如果有第一张图中的GridView
1和GridView 2中两种类型的话,在ListView滚动的过程中还是会复用的。在自定义这个布局的时候也需要基于这样的考虑,在实现布局和填充布局是分开的时候,也是需要考虑到复用,所以可以通过setTag()个getTag()的方式,在实现布局的时候每次都会通过setTag()的方式,在填充的时候则是每次都通过getTag()的方式。
在ListView对应的Adapter中,根据前面的需求,可以根据传过来的type以及包含的数量来决定一种类型。所以,Adapter可以这么写。
最后的效果图是这样的,还不错,滑动的时候也不会出现问题。
就不给源码了,哈哈。
首先看看这样怎么做,在ListView中嵌套GridView,需要的是将GridView的高度计算出来,不然GridView就可以滑动了。网上的做法,是自定义一个MyGridView(继承GridView),然后实现onMeasure()方法,计算出它的实际高度。(网上大神的源代码)
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); getLayoutParams().height = getMeasuredHeight(); }然后将 MyGridView作为ListView的item的内容就行了,ListView的Adapter的实现还是以前的方式。(本来是用郭霖大神的CSDN上面的图片的拼成了一个Json串,然后用Universal-Image-Loader来下载展示图片,但是他的图片都是正方形的,还需要处理成长方形,就放弃了。直接使用了一张默认图,然后填了一些网易新闻的信息)最后的完成以后的效果是这样的。不知道你有没有发现一点异常,是的,有些item的第一张图片的显示有点问题。
看看stackoverflow中大神的回答。Grid of images inside ScrollView
也就是说如果当GridView回收的比较频繁的时候,是不适合使用这样自定义GridView的方式的。那么是ListView中不能使用‘GridView’的了吗?但是,他的意思是自定义一个布局来显示。
也就是可以继承LinearLayout来实现,每行两个或者三个GridView的item,知道共有多少个,然后就可以自定义一个布局来实现。在这个布局中需要实现的是计算出它有多少行多少列。好吧,把数据的填充也写在这里面,但是,是的,但是,如何对GridView进行复用?Adapter中有这样的两个方法,getViewTypeCount()和getItemViewType(),可以知道ListView中是可以用多种不同类型的item,也就是说我如果有第一张图中的GridView
1和GridView 2中两种类型的话,在ListView滚动的过程中还是会复用的。在自定义这个布局的时候也需要基于这样的考虑,在实现布局和填充布局是分开的时候,也是需要考虑到复用,所以可以通过setTag()个getTag()的方式,在实现布局的时候每次都会通过setTag()的方式,在填充的时候则是每次都通过getTag()的方式。
public class MyGridView extends LinearLayout { private final static String TYPE_HORIZONTAL = "horizontal"; private final static String TYPE_VERTICAL = "vertical"; /* * 横图时显示2个 * */ private final static int NUM_HORIZONTAL = 2; /* * 竖图时显示3个 * */ private final static int NUM_VERTICAL = 3; /* * 自定义的类型,决定是2个还是3个 * */ private String mType; /* * 含有的列数 * */ private int mNumColumns = 0; private Context mContext; /* * 该控件中的数据信息 * */ private ItemListView mData; public MyGridView(Context context) { super(context); } public MyGridView(Context context, String type) { this(context); this.mContext = context; this.mType = type; // 横着的时候是每行2个 if(TYPE_HORIZONTAL.equals(type)) { mNumColumns = NUM_HORIZONTAL; } else if(TYPE_VERTICAL.equals(type)) { // 竖着的时候则是每行3个 mNumColumns = NUM_VERTICAL; } // 方向是竖直的 this.setOrientation(VERTICAL); } /* * 每一个MyGridView包含的布局 * */ public void addView() { addTitle(); addGridView();; } /* * 布局的标题 * */ public void addTitle() { TitleLayoutHolder titleHolder = new TitleLayoutHolder(); View titleLayout = LayoutInflater.from(mContext).inflate(R.layout.mygridview_title, null); titleHolder.layoutTitle = (TextView) titleLayout.findViewById(R.id.mygridview_view_title); titleLayout.setTag(titleHolder); this.addView(titleLayout); // 布局完成以后设置tag,方便填充数据 } /* * 布局的GridView部分 * */ public void addGridView() { if(mData == null) { return ; } // 根据屏幕的宽和每行的个数来计算宽度 int width = ((Activity)mContext).getWindowManager().getDefaultDisplay().getWidth(); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1); int rowSpacing = mContext.getResources().getDimensionPixelSize(R.dimen.image_spacing); int size = mData.getList().size(); int numRow = size / mNumColumns + ( size % mNumColumns == 0 ? 0 : 1 ); // 遍历行 for(int i = 0;i < numRow; i++) { LinearLayout linearLayout = new LinearLayout(mContext); //每一行都是一个横向的LinearLayout linearLayout.setOrientation(HORIZONTAL); linearLayout.setPadding(rowSpacing, 0, rowSpacing , 0); // 遍历列 for(int j = 0;j < mNumColumns && i * mNumColumns + j < size; j++) { View view = LayoutInflater.from(mContext).inflate(R.layout.mygridview_view, null); view.setPadding(rowSpacing, rowSpacing, rowSpacing, rowSpacing); ViewHolder holder = new ViewHolder(); holder.imageView = (ImageView) view.findViewById(R.id.mygridview_imageview); // 计算出ImageView的高和宽 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) holder.imageView.getLayoutParams(); int mImageHeight = 0; int mImageWidth = 0; if(mImageWidth == 0) { mImageWidth = width / mNumColumns; } // 分横竖屏计算出Image的高 if(TYPE_HORIZONTAL.equals(mType)) { mImageHeight = mImageWidth * 9 / 16; } else if(TYPE_VERTICAL.equals(mType)) { mImageHeight = mImageWidth * 4 / 3; } layoutParams.width = mImageWidth; layoutParams.height = mImageHeight; holder.overLap = (TextView) view.findViewById(R.id.overlap); holder.title = (TextView) view.findViewById(R.id.title); view.setTag(holder); if (i == mImageWidth - 1) { linearLayout.addView(view, j); } else { linearLayout.addView(view, params); } } this.addView(linearLayout); } } /* * 初始化数据,包括初始化布局 * */ public void setData(ItemListView data) { this.mData = data; addView(); fillData(mData); } /* * 将信息填充在MyGridView中 * */ public void fillData(ItemListView data) { if(data == null) { return; } this.mData = data; // 得到含有的所有子项的信息 int childCount = getChildCount(); // 填充标题 TitleLayoutHolder titleLayoutHolder = (TitleLayoutHolder) this.getChildAt(0).getTag(); titleLayoutHolder.layoutTitle.setText(data.getTitle()); // 填充view信息,从第1个到最后一个(是分行来加载的,就是填充View的每一行) for( int i = 1; i < childCount; i ++ ) { // 得到每一行的信息,包含多少个Item ViewGroup linear = (ViewGroup) this.getChildAt(i); int itemCount = linear.getChildCount(); // 每一行的起始的位置 int firstDataIndex = (i-1) * mNumColumns; // 遍历每一行的信息,显示所有的item for( int j = 0; j < itemCount; j ++) { // 所有信息包含的个数 int dataIndex = mData.getList().size(); if(firstDataIndex >= dataIndex) //如果当前的下标已经大于了数据的长度,就退出 { break; } // 得到本行的数据的信息 ItemListView.ItemGridView itemData = mData.getList().get(firstDataIndex); ViewHolder holder = (ViewHolder) linear.getChildAt(j).getTag(); holder.overLap.setText(itemData.getOverlap()); holder.title.setText(itemData.getTitle()); holder.imageView.setBackgroundResource(R.mipmap.empty_photo); firstDataIndex ++; } } } public class TitleLayoutHolder { public TextView layoutTitle; } public class ViewHolder { public ImageView imageView; public TextView overLap; public TextView title; } }
在ListView对应的Adapter中,根据前面的需求,可以根据传过来的type以及包含的数量来决定一种类型。所以,Adapter可以这么写。
public class ItemListViewAdapter extends BaseAdapter { // 原始数据 private List<ItemListView> list; // 加工以后的数据,知道是什么类型的 private List<DataHolder> dataList; private Context mContext; private LayoutInflater mInflater; // item的种类,可以根据Type字段和数量来做 private int mTypeCount = 0; public ItemListViewAdapter(List<ItemListView> list, Context context) { this.list = list; this.mContext = context; mInflater = LayoutInflater.from(context); // 在初始化的时候就需要计算出有多少种类 mTypeCount = countType(); } @Override public int getCount() { return list==null ? 0 : list.size(); } @Override public Object getItem(int position) { return list==null? null:list.get(position); } /* * 根据list得到有多少种不同的类型 * */ private int countType() { if(list == null) { return 0; } // 默认的类型 int typeNum = 0; // 总共含有的项 int size = list.size(); dataList = new ArrayList<DataHolder>(); boolean flag = false; for (int i = 0;i < size;i ++) { flag = false; DataHolder dataHolder = new DataHolder(); dataHolder.data = list.get(i); for (int j = 0;j < i;j ++) { if(isSameType(dataHolder.data, dataList.get(j).data)) { dataHolder.type = dataList.get(j).type; flag = true; break; } } if (!flag) { dataHolder.type = typeNum++; } dataList.add(dataHolder); } return typeNum; } /* * 判断src和dst是否相同的类型 * 类型相同并且含有的子项数量相同才相同 * */ private boolean isSameType(ItemListView src, ItemListView dst) { if(src == null || dst == null) { return false; } if(src.getType().equals(dst.getType())) { if(src.getList().size() == dst.getList().size()) { return true; } } return false; } /* * 得到某一个position对应的类型 * */ @Override public int getItemViewType(int position) { return dataList == null ? 0 : dataList.get(position).type; } /* * 计算出总体的类型 * */ @Override public int getViewTypeCount() { return mTypeCount > 0 ? mTypeCount : 1; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { String type = list.get(position).getType(); MyGridView myGridView = null; if(convertView == null) { // 如果为null的时候则需要初始化布局以及填充数据 myGridView = new MyGridView( mContext, type); myGridView.setData(list.get(position)); } else { // 如果不为null的时候只需要填充数据 myGridView = (MyGridView) convertView; myGridView.fillData(dataList.get(position).data); } return myGridView; } /* * 数据和数据的类型 * */ public class DataHolder { ItemListView data; int type; } }这样,应该就是完成了,最后,自己的数据。
public class Jsons { public static String data = "{\"modules\":[{\"tid\":\"horizontal\",\"data\":{\"title\":\"头条\",\"dlist\":[{\"title\":\"访美花絮\",\"overlap\":\"习大大与美副总统开玩笑\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383299_1976.jpg\"},{\"title\":\"中国代表团\",\"overlap\":\"回应希拉里批评\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383291_6518.jpg\"},{\"title\":\"男子被刑拘\",\"overlap\":\"发布虚假信息\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383291_8239.jpg\"},{\"title\":\"山东小伙扶人被讹\",\"overlap\":\"车主愿作证\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383290_9329.jpg\"},{\"title\":\"东风日产\",\"overlap\":\"新蓝鸟预售正式启动\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383290_1042.jpg\"},{\"title\":\"范爷\",\"overlap\":\"李晨为范爷系鞋带\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383275_3977.jpg\"}]}},{\"tid\":\"horizontal\",\"data\":{\"title\":\"娱乐\",\"dlist\":[{\"title\":\"爸爸3\",\"overlap\":\"最后一站\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383242_3127.jpg\"},{\"title\":\"郭美美服刑\",\"overlap\":\"回原籍湖南\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383242_9576.jpg\"},{\"title\":\"孙红雷发黄磊结婚照\",\"overlap\":\"称好看\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383242_1721.jpg\"},{\"title\":\"张艺谋女儿做月饼\",\"overlap\":\"为妈妈庆生\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383219_5806.jpg\"}]}},{\"tid\":\"vertical\",\"data\":{\"title\":\"热点\",\"dlist\":[{\"title\":\"张嘉倪游走米兰街头\",\"overlap\":\"百变魅力令人心动\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383214_7794.jpg\"},{\"title\":\"上海最险匝道\",\"overlap\":\"明天通行\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383213_4418.jpg\"},{\"title\":\"南京虐童案\",\"overlap\":\"重新鉴定伤情\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383213_3557.jpg\"},{\"title\":\"装修后悔药\",\"overlap\":\"十大装修补救\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383210_8779.jpg\"},{\"title\":\"女神也风流\",\"overlap\":\"与男友亲昵\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383166_2224.jpg\"},{\"title\":\"十女配一夫\",\"overlap\":\"拉脱维亚\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383172_4577.jpg\"},{\"title\":\"世界新七大奇观\",\"overlap\":\"中国一处上榜\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383166_3407.jpg\"},{\"title\":\"中国最美的村庄\",\"overlap\":\"太美了\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383166_7301.jpg\"},{\"title\":\"华为Nexus 6p\",\"overlap\":\"细节大抄底\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383165_7197.jpg\"}]}},{\"tid\":\"vertical\",\"data\":{\"title\":\"体育\",\"dlist\":[{\"title\":\"亚锦赛\",\"overlap\":\"伊朗负菲律宾\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383150_8410.jpg\"},{\"title\":\"小贝辣妹关系紧张\",\"overlap\":\"互爆粗口\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383131_3736.jpg\"},{\"title\":\"恒大发亚冠最新海报\",\"overlap\":\"破五关\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383130_5094.jpg\"},{\"title\":\"哈达迪双手暴扣\",\"overlap\":\"犯满离场\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383130_7393.jpg\"},{\"title\":\"梅西微笑抵训练场\",\"overlap\":\"苏神成司机\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383129_8813.jpg\"},{\"title\":\"C罗被高估\",\"overlap\":\"远逊梅西\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383100_3554.jpg\"}]}},{\"tid\":\"horizontal\",\"data\":{\"title\":\"财经\",\"dlist\":[{\"title\":\"中国柴油排放造假\",\"overlap\":\"污染非常严重\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383093_7894.jpg\"},{\"title\":\"小米涉嫌虚假广告\",\"overlap\":\"被立案调查\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383092_2432.jpg\"},{\"title\":\"26位国企董事长\",\"overlap\":\"超期服役\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383092_3071.jpg\"},{\"title\":\"超级稻\",\"overlap\":\"被质疑\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383091_3119.jpg\"},{\"title\":\"一号店回应员工离职\",\"overlap\":\"正常流动\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383059_6589.jpg\"},{\"title\":\"人人网私有化\",\"overlap\":\"报价太低造质疑\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383059_8814.jpg\"},{\"title\":\"辽宁食药监局\",\"overlap\":\"辉山产品合格\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383059_2237.jpg\"},{\"title\":\"地下钱庄洗钱\",\"overlap\":\"公司账户全匿名\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383058_4330.jpg\"}]}},{\"tid\":\"horizontal\",\"data\":{\"title\":\"科技\",\"dlist\":[{\"title\":\"iphone 6s税额\",\"overlap\":\"够买一部红米\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383038_3602.jpg\"},{\"title\":\"无人机\",\"overlap\":\"在英国如何使用\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382942_3079.jpg\"}]}},{\"tid\":\"horizontal\",\"data\":{\"title\":\"图片\",\"dlist\":[{\"title\":\"大师创作\",\"overlap\":\"香港首条水墨阶梯\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382942_8125.jpg\"},{\"title\":\"游客三亚赏月\",\"overlap\":\"留下29吨垃圾\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382942_4881.jpg\"},{\"title\":\"长沙抬恐龙比赛\",\"overlap\":\"美女战肌肉男\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382941_4559.jpg\"},{\"title\":\"重庆600平米巨型国旗升旗\",\"overlap\":\"迎国庆\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382941_3845.jpg\"}]}},{\"tid\":\"horizontal\",\"data\":{\"title\":\"跟贴\",\"dlist\":[{\"title\":\"下次2033年\",\"overlap\":\"嫦娥大姨妈也嫌月球路远\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383265_8550.jpg\"},{\"title\":\"叶良辰\",\"overlap\":\"赵日天不服\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383264_3954.jpg\"},{\"title\":\"网购悲剧\",\"overlap\":\"财富很快就没了\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383264_4787.jpg\"},{\"title\":\"放屁污染\",\"overlap\":\"专家称放屁影响pm2.5\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383248_3693.jpg\"},{\"title\":\"瞎子掰玉米\",\"overlap\":\"掰一穗,丢一穗\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383264_8243.jpg\"},{\"title\":\"中秋寄思\",\"overlap\":\"举头望明月\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406383243_5120.jpg\"}]}},{\"tid\":\"horizontal\",\"data\":{\"title\":\"直播\",\"dlist\":[{\"title\":\"马庸做客直播室\",\"overlap\":\"9月29日\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382924_8955.jpg\"},{\"title\":\"台湾金钟奖\",\"overlap\":\"第50届\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382923_2141.jpg\"},{\"title\":\"中超-鲁能VS上港\",\"overlap\":\"争冠焦点战\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382923_8437.jpg\"},{\"title\":\"网易时尚\",\"overlap\":\"Etro秀场直播\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382922_6166.jpg\"}]}},{\"tid\":\"vertical\",\"data\":{\"title\":\"轻松时刻\",\"dlist\":[{\"title\":\"小编漫话\",\"overlap\":\"新时代发财路\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382922_4843.jpg\"},{\"title\":\"新闻七点整\",\"overlap\":\"吃别人剩下的\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382905_5804.jpg\"},{\"title\":\"深夜畅聊\",\"overlap\":\"个人隐私\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382904_3362.jpg\"},{\"title\":\"暴走大事件\",\"overlap\":\"第四季\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382904_2312.jpg\"},{\"title\":\"一见你就笑\",\"overlap\":\"大学生了吗?\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382904_4960.jpg\"},{\"title\":\"轻松一刻语音版\",\"overlap\":\"低调求婚\",\"img\":\"http://img.my.csdn.net/uploads/201407/26/1406382900_2418.jpg\"}]}}]}"; }
最后的效果图是这样的,还不错,滑动的时候也不会出现问题。
就不给源码了,哈哈。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories