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

7c6c75a4b8fea15e379a94818629ba5a.gif

效果图

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

1、使用方法

compile 'com.a520wcf.yllistview:YLListView:1.0.1

2、使用介绍:

1)、布局:

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

android: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; // reset

if (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 listener

mTotalItemCount = 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));

}

}

以上就是本文的全部内容,希望对大家的学习有所帮助。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐