PinterestLikeAdapterView 瀑布流实现原理
来源:互联网 发布:2016雾霾数据统计图 编辑:程序博客网 时间:2024/05/17 23:25
PinterestLikeAdapterView是github上开源的项目,实现了流畅的瀑布流功能,上个图看看:
功能实现:
1、瀑布流,可简单配置显示列数。
2、下拉刷新。
3、上拉加载更多。
我从github上刚下的代码目录:
貌似结构挺复杂,其实挺简单,按照功能需求分析下目录结构:
1、要实现这种特殊的瀑布流布局界面,要自定义个View,自定义View的包:huewu.pla.lib
MutiColumnListView ----自定义的瀑布流View
PLA_ListView ------MutiColumnListView的父类,主要是对瀑布流的显示的方法。
PLA_AbsListView -----PLA_ListView 的父类,是自定义listview的基类,对绑定数据进行处理。
PLA_AdapterView----自定义View的是数据适配器,PLA_AbsListView 的父类。
PLA_HeaderViewListAdapter ----- 自定义View的头布局和尾布局的适配器,在PLA_AbsListView用到。
2、要实现上拉下拉功能,需要对MutiColumnListView进行重写,把头尾和各种手势处理加上:me.maxwin.view
XListView-----对MutiColumnListView进行重写,把头尾和各种手势处理加上,项目中直接用到的类。
XListViewFooter-----添加的尾布局。
XListViewHeader-----添加的头布局。
3、这里瀑布流加载的是网络图片,图片下载显示工具包:com.example.android.bitmapfun.util
bitmapfun是google教程提供的工具包,是对图片的异步加载、缓存的工具,使用很简单,就一个方法把图片url和imageview控件传进去就可以。其显现逻辑感兴趣的可以学一下。
4、由于瀑布流图片显示的自动调整宽高,就要对imageview进行重写:com/dodowaterfall/widget/ScaleImageView.java
widget包里的另外还有两个自定义View不知道干啥使得,项目中也没用到,暂时先不管了。
5、工具包和自定义view准备好,调用自己界面显示瀑布流:com/huewu/pla/sample/PullToRefreshSampleActivity.java
图片数据显示首先通过网络获得图片的数据,图片数据的实体类包:com.dodola.model
再按照实现流程分析下,瀑布流功能的实现逻辑:
1、获取网络图片数据,http+json流程没必要说,看下获得的数据:
try { if (null != json) { JSONObject newsObject = new JSONObject(json); JSONObject jsonObject = newsObject.getJSONObject("data"); JSONArray blogsJson = jsonObject.getJSONArray("blogs"); for (int i = 0; i < blogsJson.length(); i++) { JSONObject newsInfoLeftObject = blogsJson.getJSONObject(i); DuitangInfo newsInfo1 = new DuitangInfo(); newsInfo1.setAlbid(newsInfoLeftObject.isNull("albid") ? "" : newsInfoLeftObject.getString("albid")); newsInfo1.setIsrc(newsInfoLeftObject.isNull("isrc") ? "" : newsInfoLeftObject.getString("isrc")); newsInfo1.setMsg(newsInfoLeftObject.isNull("msg") ? "" : newsInfoLeftObject.getString("msg")); newsInfo1.setHeight(newsInfoLeftObject.getInt("iht")); duitangs.add(newsInfo1); } } } catch (JSONException e) { e.printStackTrace(); }
获得id、isrc、msg这三个一看就知道是什么,iht这个是图片的高,刚看的时候,不知道具体是干啥用的,肯定是来控制图片的显示宽高。
2、自定义adapter对imageview设置数据:
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; DuitangInfo duitangInfo = mInfos.get(position); if (convertView == null) { LayoutInflater layoutInflator = LayoutInflater.from(parent.getContext()); convertView = layoutInflator.inflate(R.layout.infos_list, null); holder = new ViewHolder(); holder.imageView = (ScaleImageView) convertView.findViewById(R.id.news_pic); holder.contentView = (TextView) convertView.findViewById(R.id.news_title); convertView.setTag(holder); } holder = (ViewHolder) convertView.getTag(); holder.imageView.setImageWidth(duitangInfo.getWidth()); holder.imageView.setImageHeight(duitangInfo.getHeight()); holder.contentView.setText(duitangInfo.getMsg()); mImageFetcher.loadImage(duitangInfo.getIsrc(), holder.imageView); return convertView; }
这里设置了imageview 的图片资源的宽高,我们现在至少知道高是从网络获得的,宽哪来的:
public int getWidth(){return 200;}
这里是固定的200,可以推测我们获取的高的值,是相对于宽200 的相对值。
3、那Imageview是的实际宽高是在自定义ScaleImageView里动态计算的:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); /** * if both width and height are set scale width first. modify in future * if necessary */ if (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST) { scaleToWidth = true; } else if (heightMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.AT_MOST) { scaleToWidth = false; } else throw new IllegalStateException("width or height needs to be set to match_parent or a specific dimension"); if (imageWidth == 0) { // nothing to measure super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } else { if (scaleToWidth) { int iw = imageWidth; int ih = imageHeight; int heightC = width * ih / iw; if (height > 0) if (heightC > height) { // dont let hegiht be greater then set max heightC = height; width = heightC * iw / ih; } this.setScaleType(ScaleType.CENTER_CROP); setMeasuredDimension(width, heightC); } else { // need to scale to height instead int marg = 0; if (getParent() != null) { if (getParent().getParent() != null) { marg += ((RelativeLayout) getParent().getParent()).getPaddingTop(); marg += ((RelativeLayout) getParent().getParent()).getPaddingBottom(); } } int iw = imageWidth; int ih = imageHeight; width = height * iw / ih; height -= marg; setMeasuredDimension(width, height); } } }
以上根据宽高属性设置的判断,对实际宽高进行重新设置,先看下布局文件宽高设置:
<me.maxwin.view.XListView xmlns:pla="http://schemas.android.com/apk/res-auto" android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" android:fastScrollEnabled="true" android:scrollbars="vertical" pla:plaColumnNumber="3" />
那就是scaleToWidth = true;根据图片资源的宽高比计算出iamgeview的高:int heightC = width * ih / iw;图片居中显示。
根据瀑布流显示列数和边界可以计算出显示图片的imageview的宽。
上面还限制了imageview显示高的上限,如果超过的话,就来缩小imageview的宽,也就是宽不一定是match_parent。
4、自定义MultiColumnListView的实现原理
创建瀑布流view时候首先初始化列数:
private void init(AttributeSet attrs) { getWindowVisibleDisplayFrame(mFrameRect); if (attrs == null) { mColumnNumber = (DEFAULT_COLUMN_NUMBER); // default column number is // 2. } else { TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.PinterestLikeAdapterView); int landColNumber = a.getInteger(R.styleable.PinterestLikeAdapterView_plaLandscapeColumnNumber, 3); int defColNumber = a.getInteger(R.styleable.PinterestLikeAdapterView_plaColumnNumber, DEFAULT_COLUMN_NUMBER); if (mFrameRect.width() > mFrameRect.height() && landColNumber != -1) { mColumnNumber = (landColNumber); } else if (defColNumber != -1) { mColumnNumber = (defColNumber); } else { mColumnNumber = (DEFAULT_COLUMN_NUMBER); } mColumnPaddingLeft = a.getDimensionPixelSize(R.styleable.PinterestLikeAdapterView_plaColumnPaddingLeft, 0); mColumnPaddingRight = a.getDimensionPixelSize(R.styleable.PinterestLikeAdapterView_plaColumnPaddingRight, 0); a.recycle(); } mColumns = new Column[getColumnNumber()]; for (int i = 0; i < getColumnNumber(); ++i) mColumns[i] = new Column(i); mFixedColumn = new FixedColumn(); }
可以看出列数没有设置的话,会有一个默认值:DEFAULT_COLUMN_NUMBER。
根据设置的列数,定义了一个列数组,在存储每一列的数据:mColumns = new Column[getColumnNumber()];
初始化每一列的宽度和横向位置:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); columnWidth = (getMeasuredWidth() - mListPadding.left - mListPadding.right - mColumnPaddingLeft - mColumnPaddingRight) / getColumnNumber(); for (int index = 0; index < getColumnNumber(); ++index) { mColumns[index].mColumnWidth = columnWidth; mColumns[index].mColumnLeft = mListPadding.left + mColumnPaddingLeft + columnWidth * index; } mFixedColumn.mColumnLeft = mListPadding.left; mFixedColumn.mColumnWidth = getMeasuredWidth(); }
接下来就是想每一列中添加数据显示了,大体原理就是:
1、判断是在在头部添加还是尾部添加,也就是上拉还是下拉。
2、通过计算对比每一列的高,在最短的一列添加上要添加的数据。
3、根据添加后的数据,刷新布局显示。
原理如此,过程比较复杂,如果耐心足够的话可以认真研究下。
这里只是介绍了大体的实现原理,至于详细的控件自定义还有图片加载逻辑,还是慢慢分析代码吧。可以到github下载最新的,csdn我也传了一份暂时最新的:http://download.csdn.net/detail/xiangxue336/7059861
- PinterestLikeAdapterView 瀑布流实现原理
- 瀑布流PinterestLikeAdapterView实现原理
- 仿瀑布流的实现原理,例PinterestListView,PinterestLikeAdapterView,PinterestLikeAdapterView
- PinterestLikeAdapterView 完美瀑布流
- [Github开源库PinterestLikeAdapterView]--Android瀑布流的实现
- [Github开源库PinterestLikeAdapterView]--Android瀑布流的实现
- android中最好的瀑布流控件PinterestLikeAdapterView
- android中最好的瀑布流控件PinterestLikeAdapterView
- android中最好的瀑布流控件PinterestLikeAdapterView
- IOS瀑布流实现原理
- 瀑布流的实现原理
- android瀑布流简单实现原理
- 封装自定义瀑布流,实现原理
- 瀑布流的实现原理与实例
- PinterestLikeAdapterView
- jquery实现无限滚动瀑布流实现原理
- scroll实现瀑布流原理,数据获取使用getJSON。
- 实例解释瀑布流图的实现原理与方法
- win7 英文版64位操作系统下,破解lr不成功 ——解决办法
- 原标题:机长扎哈里 道行有点深
- 循环与判断语句
- eclipse中格式化代码快捷键Ctrl+Shift+F失效的解决办法
- 算法实践篇-快速排序-随机化版本
- PinterestLikeAdapterView 瀑布流实现原理
- 第三周作业-循环与判断语句
- 常用Java性能测试工具的分析与对比
- HTML5矩形画法
- android中点击空白处隐藏软键盘
- 插入一张图纸作为块参照
- 国外网站汇总
- 验证快就是慢、慢就是快
- 统计文件某一列/某一字段的和