背景介绍

最近项目打算做一个界面,类似于dayone首页的界面效果,dayone 是一款付费应用,目前只有IOS端。作为一个资深懒惰的程序员,奉行的宗旨是绝对不重复造一个轮子。于是乎,去网上找一大堆开源项目,发现没有找到合适的,然后,只能硬着头皮自己来了。先看看效果:

其实写起来也比较简单,就是控制ListView的头部和底部的高度就可以了, 如果用RecycleView实现起来也是一样,只是RecycleView添加头和尾巴稍微麻烦一点,处理点击事件也不是很方便,所以就基于ListView去实现了。

实现的代码, 我已经上传到github上了。

使用方法

github地址: https://github.com/yll2wcf/YLListView 可以帮我点个star啊~

使用方法

  1. compile 'com.a520wcf.yllistview:YLListView:1.0.1'

使用介绍:

布局:

布局注意一个小细节android:layout_height 最好是match_parent, 否则ListView每次滑动的时候都有可能需要重新计算条目高度,比较耗费CPU;

  1. <com.a520wcf.yllistview.YLListView
  2. android:divider="@android:color/transparent"
  3. android:id="@+id/listView"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. />

代码:

  1. private YLListView listView;
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. listView = (YLListView) findViewById(R.id.listView);
  7. // 不添加也有默认的头和底
  8. View topView=View.inflate(this,R.layout.top,null);
  9. listView.addHeaderView(topView);
  10. View bottomView=new View(getApplicationContext());
  11. listView.addFooterView(bottomView);
  12. // 顶部和底部也可以固定最终的高度 不固定就使用布局本身的高度
  13. listView.setFinalBottomHeight(100);
  14. listView.setFinalTopHeight(100);
  15. listView.setAdapter(new DemoAdapter());
  16. //YLListView默认有头和底 处理点击事件位置注意减去
  17. listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
  18. @Override
  19. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  20. position=position-listView.getHeaderViewsCount();
  21. }
  22. });
  23. }

源码介绍

其实这个项目里面只有一个类,大家不需要依赖,直接把这个类复制到项目中就可以了,来看看源码:

  1. package com.a520wcf.yllistview;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.view.MotionEvent;
  5. import android.view.View;
  6. import android.view.ViewTreeObserver.OnGlobalLayoutListener;
  7. import android.view.animation.DecelerateInterpolator;
  8. import android.widget.AbsListView;
  9. import android.widget.ListView;
  10. import android.widget.Scroller;
  11. /**
  12. * 邮箱 yll@520wcf.com
  13. * Created by yull on 12/17.
  14. */
  15. public class YLListView extends ListView implements AbsListView.OnScrollListener {
  16. private Scroller mScroller; // used for scroll back
  17. private float mLastY = -1;
  18. private int mScrollBack;
  19. private final static int SCROLLBACK_HEADER = 0;
  20. private final static int SCROLLBACK_FOOTER = 1;
  21. private final static int SCROLL_DURATION = 400; // scroll back duration
  22. private final static float OFFSET_RADIO = 1.8f;
  23. // total list items, used to detect is at the bottom of ListView.
  24. private int mTotalItemCount;
  25. private View mHeaderView; // 顶部图片
  26. private View mFooterView; // 底部图片
  27. private int finalTopHeight;
  28. private int finalBottomHeight;
  29. public YLListView(Context context) {
  30. super(context);
  31. initWithContext(context);
  32. }
  33. public YLListView(Context context, AttributeSet attrs) {
  34. super(context, attrs);
  35. initWithContext(context);
  36. }
  37. public YLListView(Context context, AttributeSet attrs, int defStyle) {
  38. super(context, attrs, defStyle);
  39. initWithContext(context);
  40. }
  41. private void initWithContext(Context context) {
  42. mScroller = new Scroller(context, new DecelerateInterpolator());
  43. super.setOnScrollListener(this);
  44. this.getViewTreeObserver().addOnGlobalLayoutListener(
  45. new OnGlobalLayoutListener() {
  46. @Override
  47. public void onGlobalLayout() {
  48. if(mHeaderView==null){
  49. View view=new View(getContext());
  50. addHeaderView(view);
  51. }
  52. if(mFooterView==null){
  53. View view=new View(getContext());
  54. addFooterView(view);
  55. }
  56. getViewTreeObserver()
  57. .removeGlobalOnLayoutListener(this);
  58. }
  59. });
  60. }
  61. @Override
  62. public boolean onTouchEvent(MotionEvent ev) {
  63. if (mLastY == -1) {
  64. mLastY = ev.getRawY();
  65. }
  66. switch (ev.getAction()) {
  67. case MotionEvent.ACTION_DOWN:
  68. mLastY = ev.getRawY();
  69. break;
  70. case MotionEvent.ACTION_MOVE:
  71. final float deltaY = ev.getRawY() - mLastY;
  72. mLastY = ev.getRawY();
  73. if (getFirstVisiblePosition() == 0 && (mHeaderView.getHeight() > finalTopHeight || deltaY > 0)
  74. && mHeaderView.getTop() >= 0) {
  75. // the first item is showing, header has shown or pull down.
  76. updateHeaderHeight(deltaY / OFFSET_RADIO);
  77. } else if (getLastVisiblePosition() == mTotalItemCount - 1
  78. && (getFootHeight() >finalBottomHeight || deltaY < 0)) {
  79. updateFooterHeight(-deltaY / OFFSET_RADIO);
  80. }
  81. break;
  82. default:
  83. mLastY = -1; // reset
  84. if (getFirstVisiblePosition() == 0 && getHeaderHeight() > finalTopHeight) {
  85. resetHeaderHeight();
  86. }
  87. if (getLastVisiblePosition() == mTotalItemCount - 1 ){
  88. if(getFootHeight() > finalBottomHeight) {
  89. resetFooterHeight();
  90. }
  91. }
  92. break;
  93. }
  94. return super.onTouchEvent(ev);
  95. }
  96. /**
  97. * 重置底部高度
  98. */
  99. private void resetFooterHeight() {
  100. int bottomHeight = getFootHeight();
  101. if (bottomHeight > finalBottomHeight) {
  102. mScrollBack = SCROLLBACK_FOOTER;
  103. mScroller.startScroll(0, bottomHeight, 0, -bottomHeight+finalBottomHeight,
  104. SCROLL_DURATION);
  105. invalidate();
  106. }
  107. }
  108. // 计算滑动 当invalidate()后 系统会自动调用
  109. @Override
  110. public void computeScroll() {
  111. if (mScroller.computeScrollOffset()) {
  112. if (mScrollBack == SCROLLBACK_HEADER) {
  113. setHeaderHeight(mScroller.getCurrY());
  114. } else {
  115. setFooterViewHeight(mScroller.getCurrY());
  116. }
  117. postInvalidate();
  118. }
  119. super.computeScroll();
  120. }
  121. // 设置顶部高度
  122. private void setHeaderHeight(int height) {
  123. LayoutParams layoutParams = (LayoutParams) mHeaderView.getLayoutParams();
  124. layoutParams.height = height;
  125. mHeaderView.setLayoutParams(layoutParams);
  126. }
  127. // 设置底部高度
  128. private void setFooterViewHeight(int height) {
  129. LayoutParams layoutParams =
  130. (LayoutParams) mFooterView.getLayoutParams();
  131. layoutParams.height =height;
  132. mFooterView.setLayoutParams(layoutParams);
  133. }
  134. // 获取顶部高度
  135. public int getHeaderHeight() {
  136. AbsListView.LayoutParams layoutParams =
  137. (AbsListView.LayoutParams) mHeaderView.getLayoutParams();
  138. return layoutParams.height;
  139. }
  140. // 获取底部高度
  141. public int getFootHeight() {
  142. AbsListView.LayoutParams layoutParams =
  143. (AbsListView.LayoutParams) mFooterView.getLayoutParams();
  144. return layoutParams.height;
  145. }
  146. private void resetHeaderHeight() {
  147. int height = getHeaderHeight();
  148. if (height == 0) // not visible.
  149. return;
  150. mScrollBack = SCROLLBACK_HEADER;
  151. mScroller.startScroll(0, height, 0, finalTopHeight - height,
  152. SCROLL_DURATION);
  153. invalidate();
  154. }
  155. /**
  156. * 设置顶部高度 如果不设置高度,默认就是布局本身的高度
  157. * @param height 顶部高度
  158. */
  159. public void setFinalTopHeight(int height) {
  160. this.finalTopHeight = height;
  161. }
  162. /**
  163. * 设置底部高度 如果不设置高度,默认就是布局本身的高度
  164. * @param height 底部高度
  165. */
  166. public void setFinalBottomHeight(int height){
  167. this.finalBottomHeight=height;
  168. }
  169. @Override
  170. public void addHeaderView(View v) {
  171. mHeaderView = v;
  172. super.addHeaderView(mHeaderView);
  173. mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
  174. new OnGlobalLayoutListener() {
  175. @Override
  176. public void onGlobalLayout() {
  177. if(finalTopHeight==0) {
  178. finalTopHeight = mHeaderView.getMeasuredHeight();
  179. }
  180. setHeaderHeight(finalTopHeight);
  181. getViewTreeObserver()
  182. .removeGlobalOnLayoutListener(this);
  183. }
  184. });
  185. }
  186. @Override
  187. public void addFooterView(View v) {
  188. mFooterView = v;
  189. super.addFooterView(mFooterView);
  190. mFooterView.getViewTreeObserver().addOnGlobalLayoutListener(
  191. new OnGlobalLayoutListener() {
  192. @Override
  193. public void onGlobalLayout() {
  194. if(finalBottomHeight==0) {
  195. finalBottomHeight = mFooterView.getMeasuredHeight();
  196. }
  197. setFooterViewHeight(finalBottomHeight);
  198. getViewTreeObserver()
  199. .removeGlobalOnLayoutListener(this);
  200. }
  201. });
  202. }
  203. private OnScrollListener mScrollListener; // user's scroll listener
  204. @Override
  205. public void setOnScrollListener(OnScrollListener l) {
  206. mScrollListener = l;
  207. }
  208. @Override
  209. public void onScrollStateChanged(AbsListView view, int scrollState) {
  210. if (mScrollListener != null) {
  211. mScrollListener.onScrollStateChanged(view, scrollState);
  212. }
  213. }
  214. @Override
  215. public void onScroll(AbsListView view, int firstVisibleItem,
  216. int visibleItemCount, int totalItemCount) {
  217. // send to user's listener
  218. mTotalItemCount = totalItemCount;
  219. if (mScrollListener != null) {
  220. mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
  221. totalItemCount);
  222. }
  223. }
  224. private void updateHeaderHeight(float delta) {
  225. setHeaderHeight((int) (getHeaderHeight()+delta));
  226. setSelection(0); // scroll to top each time
  227. }
  228. private void updateFooterHeight(float delta) {
  229. setFooterViewHeight((int) (getFootHeight()+delta));
  230. }
  231. }

仿IOS效果-带弹簧动画的ListView的更多相关文章

  1. Android-PickerView【仿iOS的PickerView控件,并封装了时间选择和选项选择这两种选择器】使用

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本文主要演示Android-PickerView的选项选择器.时间选择器的简单运用.由于每一个版本略有不用,所以实际使用方式以git ...

  2. 在uwp仿IOS的页面切换效果

    有时候我们需要编写一些迎合IOS用户使用习惯的uwp应用,我在这里整理一下仿IOS页面切换效果的代码. 先分析IOS的页面切换.用户使用左右滑动方式进行前进和后退,播放类似于FlipView的切换动画 ...

  3. (转载) Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框

    Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框 标签: Android清除功能EditText仿IOS的输入框 2013-09-04 17:33 70865人阅读  ...

  4. Android仿IOS回弹效果 ScrollView回弹 总结

    Android仿IOS回弹效果  ScrollView回弹 总结 应项目中的需求  须要仿IOS 下拉回弹的效果 , 我在网上搜了非常多 大多数都是拿scrollview 改吧改吧 试了一些  发现总 ...

  5. CADisplayLink+弹簧动画实现果冻效果

    项目中在Tabbar中间的按钮要从底部弹出视图并有果冻效果,在CocoaChina中找了一篇博客用 UIBezierPath 实现果冻效果,github,自己就按着上面的demo修改了一下( 之前也是 ...

  6. 自己定义控件:onDraw 方法实现仿 iOS 的开关效果

    概述 本文主要解说怎样在 Android 下实现高仿 iOS 的开关按钮,并不是是在 Android 自带的 ToggleButton 上改动,而是使用 API 提供的 onDraw.onMeasur ...

  7. [转]Android UI:看看Google官方自定义带旋转动画的ImageView-----RotateImageView怎么写(附 图片淡入淡出效果)

    http://blog.csdn.net/yanzi1225627/article/details/22439119 众所周知,想要让ImageView旋转的话,可以用setRotation()让其围 ...

  8. 带中文索引的ListView 仿微信联系人列表

    因为各种原因,项目经理和产品经理把我做的东西给否定了,所以决定分享出去. 主要功能: 1 .带中文索引的ListView 2.自己定义顶部搜索视图,能够对返回button,搜索button加入事件监听 ...

  9. iOS CAReplicatorLayer 实现脉冲动画效果

    iOS CAReplicatorLayer 实现脉冲动画效果 效果图 脉冲数量.速度.半径.透明度.渐变颜色.方向等都可以设置.可以用于地图标注(Annotation).按钮长按动画效果(例如录音按钮 ...

随机推荐

  1. Django--多对多表的创建、contentType、ajax、ajax传输json数据格式、ajax传输文件数据、 自定义分页器

    MTV与MVC(了解): MTV模型(Django用的就是MTV): M:模型层(models.py) T:templates C:views MVC模型: M:模型层(models.py) V:视图 ...

  2. js顺序加载与并行加载

    前端优化过程中常提到js的加载方式,下面说下几种常用的加载方式: 1:head标签内插入<script>标签 <script type="text/javaScript&q ...

  3. 七牛云+MPic-图床神器搭建

    1. 注册七牛云 2. 新建存储空间 3. 密钥 4. 记录自己账户四个值: 测试域名:xxxxx.xx.clouddn.com ak:xxxxxxxxxxxxxxxxxxxx sk:xxxxxxxx ...

  4. Codeforces Round #189 (Div. 2) A. Magic Numbers【正难则反/给出一个数字串判断是否只由1,14和144组成】

    A. Magic Numbers time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...

  5. js数组求交集

    求两个数组的交集 var arr1 = [1,2,3]; var arr2 = [2,3,4]; var arr3; arr3 = arr1.filter(function(num) { return ...

  6. PHP定时任务获取微信access_token的方法

    一.使用brew安装php多版本方法 # brew install php56# brew install php70二.安装切换工具 # brew install php-version# sour ...

  7. Mysql+php报错原因

    SQL syntax --语法错误,看near,错误会在near后引号中的内容 的附近 Table/Database....... dosen't existes ---表/库(库名/表名) 不存在 ...

  8. nginx简介 (转)

    1.Nginx概述 2.Nginx安装与控制指令 3.Nginx如何工作 4.Nginx配置实例 4.1如何配置Linux下Nginx 4.1.1配置Nginx代理HTTP请求到Tomcat 4.1. ...

  9. Java编程基础23——IO(其他流)&Properties

    1_序列流(了解) 1.什么是序列流 序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推. 2.使用方式 整合两个: S ...

  10. Mongodb停止和启动

    mongodb开启.停止.重启操作 #开启service mongodb start#停止service mongodb stop#重启service mongodb restart