Welcome

首页 / 移动开发 / IOS / 仿IOS效果 带弹簧动画的ListView

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



效果图

其实写起来也比较简单,就是控制ListView的头部和底部的高度就可以了, 如果用RecycleView实现起来也是一样,只是RecycleView添加头和尾巴稍微麻烦一点,处理点击事件也不是很方便,所以就基于ListView去实现了。实现的代码, 我已经上传到github上了。
1、使用方法
compile "com.a520wcf.yllistview:YLListView:1.0.1
2、使用介绍:
1)、布局:
布局注意一个小细节android:layout_height 最好是match_parent, 否则ListView每次滑动的时候都有可能需要重新计算条目高度,比较耗费CPU;

 <com.a520wcf.yllistview.YLListViewandroid:divider="@android:color/transparent" android:id="@+id/listView"android:layout_width="match_parent"android:layout_height="match_parent" />
2)、代码:
 private YLListView listView; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);listView = (YLListView) findViewById(R.id.listView);// 不添加也有默认的头和底View topView=View.inflate(this,R.layout.top,null);listView.addHeaderView(topView);View bottomView=new View(getApplicationContext());listView.addFooterView(bottomView);// 顶部和底部也可以固定最终的高度 不固定就使用布局本身的高度listView.setFinalBottomHeight(100);listView.setFinalTopHeight(100);listView.setAdapter(new DemoAdapter());//YLListView默认有头和底 处理点击事件位置注意减去listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {position=position-listView.getHeaderViewsCount(); }}); }

3、源码介绍
其实这个项目里面只有一个类,大家不需要依赖,直接把这个类复制到项目中就可以了,来看看源码:
package com.a520wcf.yllistview;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewTreeObserver.OnGlobalLayoutListener;import android.view.animation.DecelerateInterpolator;import android.widget.AbsListView;import android.widget.ListView;import android.widget.Scroller;public class YLListView extends ListView implements AbsListView.OnScrollListener { private Scroller mScroller; // used for scroll back private float mLastY = -1; private int mScrollBack; private final static int SCROLLBACK_HEADER = 0; private final static int SCROLLBACK_FOOTER = 1; private final static int SCROLL_DURATION = 400; // scroll back duration private final static float OFFSET_RADIO = 1.8f; // total list items, used to detect is at the bottom of ListView. private int mTotalItemCount; private View mHeaderView; // 顶部图片 private View mFooterView; // 底部图片 private int finalTopHeight; private int finalBottomHeight; public YLListView(Context context) {super(context);initWithContext(context); } public YLListView(Context context, AttributeSet attrs) {super(context, attrs);initWithContext(context); } public YLListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initWithContext(context); } private void initWithContext(Context context) {mScroller = new Scroller(context, new DecelerateInterpolator());super.setOnScrollListener(this);this.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() {if(mHeaderView==null){ View view=new View(getContext()); addHeaderView(view);}if(mFooterView==null){ View view=new View(getContext()); addFooterView(view);}getViewTreeObserver().removeGlobalOnLayoutListener(this); }}); } @Override public boolean onTouchEvent(MotionEvent ev) {if (mLastY == -1) { mLastY = ev.getRawY();}switch (ev.getAction()) { case MotionEvent.ACTION_DOWN:mLastY = ev.getRawY();break; case MotionEvent.ACTION_MOVE:final float deltaY = ev.getRawY() - mLastY;mLastY = ev.getRawY();if (getFirstVisiblePosition() == 0 && (mHeaderView.getHeight() > finalTopHeight || deltaY > 0)&& mHeaderView.getTop() >= 0) { // the first item is showing, header has shown or pull down. updateHeaderHeight(deltaY / OFFSET_RADIO);} else if (getLastVisiblePosition() == mTotalItemCount - 1&& (getFootHeight() >finalBottomHeight || deltaY < 0)) { updateFooterHeight(-deltaY / OFFSET_RADIO);}break; default:mLastY = -1; // resetif (getFirstVisiblePosition() == 0 && getHeaderHeight() > finalTopHeight) { resetHeaderHeight();}if (getLastVisiblePosition() == mTotalItemCount - 1 ){if(getFootHeight() > finalBottomHeight) { resetFooterHeight();}}break;}return super.onTouchEvent(ev); } /*** 重置底部高度*/ private void resetFooterHeight() {int bottomHeight = getFootHeight();if (bottomHeight > finalBottomHeight) { mScrollBack = SCROLLBACK_FOOTER; mScroller.startScroll(0, bottomHeight, 0, -bottomHeight+finalBottomHeight, SCROLL_DURATION); invalidate();} } // 计算滑动 当invalidate()后 系统会自动调用 @Override public void computeScroll() {if (mScroller.computeScrollOffset()) { if (mScrollBack == SCROLLBACK_HEADER) {setHeaderHeight(mScroller.getCurrY()); } else {setFooterViewHeight(mScroller.getCurrY()); } postInvalidate();}super.computeScroll(); } // 设置顶部高度 private void setHeaderHeight(int height) {LayoutParams layoutParams = (LayoutParams) mHeaderView.getLayoutParams();layoutParams.height = height;mHeaderView.setLayoutParams(layoutParams); } // 设置底部高度 private void setFooterViewHeight(int height) {LayoutParams layoutParams =(LayoutParams) mFooterView.getLayoutParams();layoutParams.height =height;mFooterView.setLayoutParams(layoutParams); } // 获取顶部高度 public int getHeaderHeight() {AbsListView.LayoutParams layoutParams =(AbsListView.LayoutParams) mHeaderView.getLayoutParams();return layoutParams.height; } // 获取底部高度 public int getFootHeight() {AbsListView.LayoutParams layoutParams =(AbsListView.LayoutParams) mFooterView.getLayoutParams();return layoutParams.height; } private void resetHeaderHeight() {int height = getHeaderHeight();if (height == 0) // not visible. return;mScrollBack = SCROLLBACK_HEADER;mScroller.startScroll(0, height, 0, finalTopHeight - height,SCROLL_DURATION);invalidate(); } /*** 设置顶部高度 如果不设置高度,默认就是布局本身的高度* @param height 顶部高度*/ public void setFinalTopHeight(int height) {this.finalTopHeight = height; } /*** 设置底部高度 如果不设置高度,默认就是布局本身的高度* @param height 底部高度*/ public void setFinalBottomHeight(int height){this.finalBottomHeight=height; } @Override public void addHeaderView(View v) {mHeaderView = v;super.addHeaderView(mHeaderView);mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() {if(finalTopHeight==0) { finalTopHeight = mHeaderView.getMeasuredHeight();}setHeaderHeight(finalTopHeight);getViewTreeObserver().removeGlobalOnLayoutListener(this); }}); } @Override public void addFooterView(View v) {mFooterView = v;super.addFooterView(mFooterView);mFooterView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() {if(finalBottomHeight==0) { finalBottomHeight = mFooterView.getMeasuredHeight();}setFooterViewHeight(finalBottomHeight);getViewTreeObserver().removeGlobalOnLayoutListener(this); }}); } private OnScrollListener mScrollListener; // user"s scroll listener @Override public void setOnScrollListener(OnScrollListener l) {mScrollListener = l; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) {if (mScrollListener != null) { mScrollListener.onScrollStateChanged(view, scrollState);} } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {// send to user"s listenermTotalItemCount = totalItemCount;if (mScrollListener != null) { mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);} } private void updateHeaderHeight(float delta) {setHeaderHeight((int) (getHeaderHeight()+delta));setSelection(0); // scroll to top each time } private void updateFooterHeight(float delta) {setFooterViewHeight((int) (getFootHeight()+delta)); }}
以上就是本文的全部内容,希望对大家的学习有所帮助。