Android中仿新浪微博刷新列表

大家看到新浪微博中的列表往下拉的时候, 头部出现正在刷新,然后获取到数据了以后列表会刷新的效果了吧,是不是都觉得很棒啊。 笔者今天参考进行实现。 当然也借鉴了前人的劳动成果。 下面对具体实现进行剖析。
首先要明确一点的是,ListView提供了HeaderView 和FooterView用于增加顶部和底部视图。 头部的刷新调和底部的更多条目都是使用这两个来实现的。
下面我们来看看怎么实现的:
1.  对ListView进行集成,主要是监听滑动事件,触发底端和顶端事件:
package com.drawthink.nxjobhunting.ui.components;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;


/**
* <p>一个可以监听ListView是否滚动到最顶部或最底部的自定义控件</p>
* 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br>
* 如果加以改进,可以实现监听scroll滚动的具体位置等
* @author Seya Qu</br> Email:[email protected]
*/

public class ScrollOverListView extends ListView {

private int mLastY;
private int mTopPosition;
private int mBottomPosition;

public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

public ScrollOverListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public ScrollOverListView(Context context) {
super(context);
init();
}

private void init(){
mTopPosition = 0;
mBottomPosition = 0;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final int y = (int) ev.getRawY();

switch(action){
case MotionEvent.ACTION_DOWN:{
mLastY = y;
final boolean isHandled = mOnScrollOverListener.onMotionDown(ev);
if (isHandled) {
mLastY = y;
return isHandled;
}
break;
}

case MotionEvent.ACTION_MOVE:{
final int childCount = getChildCount();
if(childCount == 0) return super.onTouchEvent(ev);

final int itemCount = getAdapter().getCount() - mBottomPosition;

final int deltaY = y - mLastY;
//DLog.d("lastY=%d y=%d", mLastY, y);

final int firstTop = getChildAt(0).getTop();
final int listPadding = getListPaddingTop();

final int lastBottom = getChildAt(childCount - 1).getBottom();
final int end = getHeight() - getPaddingBottom();

final int firstVisiblePosition = getFirstVisiblePosition();

final boolean isHandleMotionMove = mOnScrollOverListener.onMotionMove(ev, deltaY);

if(isHandleMotionMove){
mLastY = y;
return true;
}

//DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY);
if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) {
            final boolean isHandleOnListViewTopAndPullDown;
            isHandleOnListViewTopAndPullDown = mOnScrollOverListener.onListViewTopAndPullDown(deltaY);
            if(isHandleOnListViewTopAndPullDown){
            mLastY = y;
            return true;
            }
        }

// DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY);
        if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) {
        final boolean isHandleOnListViewBottomAndPullDown;
        isHandleOnListViewBottomAndPullDown = mOnScrollOverListener.onListViewBottomAndPullUp(deltaY);
        if(isHandleOnListViewBottomAndPullDown){
        mLastY = y;
        return true;
        }
        }
break;
}

case MotionEvent.ACTION_UP:{
final boolean isHandlerMotionUp = mOnScrollOverListener.onMotionUp(ev);
if (isHandlerMotionUp) {
mLastY = y;
return true;
}
break;
}
}

mLastY = y;
return super.onTouchEvent(ev);
}


/**空的*/
private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){

@Override
public boolean onListViewTopAndPullDown(int delta) {
return false;
}

@Override
public boolean onListViewBottomAndPullUp(int delta) {
return false;
}

@Override
public boolean onMotionDown(MotionEvent ev) {
return false;
}

@Override
public boolean onMotionMove(MotionEvent ev, int delta) {
return false;
}

@Override
public boolean onMotionUp(MotionEvent ev) {
return false;
}

};







// =============================== public method ===============================

/**
* 可以自定义其中一个条目为头部,头部触发的事件将以这个为准,默认为第一个
*
* @param index 正数第几个,必须在条目数范围之内
*/
public void setTopPosition(int index){
if(getAdapter() == null)
throw new NullPointerException("You must set adapter before setTopPosition!");
if(index < 0)
throw new IllegalArgumentException("Top position must > 0");

mTopPosition = index;
}

/**
* 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个
*
* @param index 倒数第几个,必须在条目数范围之内
*/
public void setBottomPosition(int index){
if(getAdapter() == null)
throw new NullPointerException("You must set adapter before setBottonPosition!");
if(index < 0)
throw new IllegalArgumentException("Bottom position must > 0");

mBottomPosition = index;
}

/**
* 设置这个Listener可以监听是否到达顶端,或者是否到达低端等事件</br>
*
* @see OnScrollOverListener
*/
public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener){
mOnScrollOverListener = onScrollOverListener;
}

/**
* 滚动监听接口</br>
* @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener)
*
* @author Seya Qu</br> Email:[email protected]
*/
public interface OnScrollOverListener {

/**
* 到达最顶部触发
*
* @param delta 手指点击移动产生的偏移量
* @return
*/
boolean onListViewTopAndPullDown(int delta);

/**
* 到达最底部触发
*
* @param delta 手指点击移动产生的偏移量
* @return
*/
boolean onListViewBottomAndPullUp(int delta);

/**
* 手指触摸按下触发,相当于{@link MotionEvent#ACTION_DOWN}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionDown(MotionEvent ev);

/**
* 手指触摸移动触发,相当于{@link MotionEvent#ACTION_MOVE}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionMove(MotionEvent ev, int delta);

/**
* 手指触摸后提起触发,相当于{@link MotionEvent#ACTION_UP}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionUp(MotionEvent ev);

}


}


2. 定义PullDownView, 继承LinearLayout, 主要是为了调用和监听ListView, 通过接口OnPullDownListener的onRefresh来通知外部进行顶部数据的刷新, onMore触发请求底部更多数据, 具体如何去获取数据, 需要在外面的Activity中实现。
package com.drawthink.nxjobhunting.ui.components;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import com.drawthink.nxjobhunting.R;
import com.drawthink.nxjobhunting.ui.components.ScrollOverListView.OnScrollOverListener;
import com.drawthink.nxjobhunting.utils.GlobalVar;
import com.drawthink.nxjobhunting.utils.LogX;

/**
* 下拉刷新控件</br>
* 真正实现下拉刷新的是这个控件,
* ScrollOverListView只是提供触摸的事件等
* @author Seya Qu</br> Email:[email protected]
*/
public class PullDownView extends LinearLayout implements OnScrollOverListener{

private static final int START_PULL_DEVIATION = 50; // 移动误差
private static final int AUTO_INCREMENTAL = 10; // 自增量,用于回弹

private static final int WHAT_DID_LOAD_DATA = 1; // Handler what 数据加载完毕
private static final int WHAT_ON_REFRESH = 2; // Handler what 刷新中
private static final int WHAT_DID_REFRESH = 3; // Handler what 已经刷新完
private static final int WHAT_SET_HEADER_HEIGHT = 4;// Handler what 设置高度
private static final int WHAT_DID_MORE = 5; // Handler what 已经获取完更多

private static final int DEFAULT_HEADER_VIEW_HEIGHT = 105; // 头部文件原本的高度

private static SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm");

private View mHeaderView;
private LayoutParams mHeaderViewParams;
private TextView mHeaderViewDateView;
private TextView mHeaderTextView;
private ImageView mHeaderArrowView;
private View mHeaderLoadingView;
private View mFooterView;
private TextView mFooterTextView;
private View mFooterLoadingView;
private ScrollOverListView mListView;

private OnPullDownListener mOnPullDownListener;
private RotateAnimation mRotateOTo180Animation;
private RotateAnimation mRotate180To0Animation;

private int mHeaderIncremental; // 增量
private float mMotionDownLastY; // 按下时候的Y轴坐标

private boolean mIsDown; // 是否按下
private boolean mIsRefreshing; // 是否下拉刷新中
private boolean mIsFetchMoreing; // 是否获取更多中
private boolean mIsPullUpDone; // 是否回推完成
private boolean mEnableAutoFetchMore; // 是否允许自动获取更多

// 头部文件的状态
private static final int HEADER_VIEW_STATE_IDLE = 0; // 空闲
private static final int HEADER_VIEW_STATE_NOT_OVER_HEIGHT = 1; // 没有超过默认高度
private static final int HEADER_VIEW_STATE_OVER_HEIGHT = 2; // 超过默认高度
private int mHeaderViewState = HEADER_VIEW_STATE_IDLE;


public PullDownView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeaderViewAndFooterViewAndListView(context);
}

public PullDownView(Context context) {
super(context);
initHeaderViewAndFooterViewAndListView(context);
}

/*
* ==================================
* Public method
* 外部使用,具体就是用这几个就可以了
*
* ==================================
*/

/**
* 刷新事件接口
* @author 何龙威 Email:[email protected]
*/
public interface OnPullDownListener {
void onRefresh();
void onMore();
}

/**
* 通知加载完了数据,要放在Adapter.notifyDataSetChanged后面
* 当你加载完数据的时候,调用这个notifyDidLoad()
* 才会隐藏头部,并初始化数据等
*/
public void notifyDidLoad() {
mUIHandler.sendEmptyMessage(WHAT_DID_LOAD_DATA);
}

/**
* 通知已经刷新完了,要放在Adapter.notifyDataSetChanged后面
* 当你执行完刷新任务之后,调用这个notifyDidRefresh()
* 才会隐藏掉头部文件等操作
*/
public void notifyDidRefresh() {
mUIHandler.sendEmptyMessage(WHAT_DID_REFRESH);
}

/**
* 通知已经获取完更多了,要放在Adapter.notifyDataSetChanged后面
* 当你执行完更多任务之后,调用这个notyfyDidMore()
* 才会隐藏加载圈等操作
*/
public void notifyDidMore() {
mUIHandler.sendEmptyMessage(WHAT_DID_MORE);
}

/**
* 设置监听器
* @param listener
*/
public void setOnPullDownListener(OnPullDownListener listener){
mOnPullDownListener = listener;
}

/**
* 获取内嵌的listview
* @return ScrollOverListView
*/
public ListView getListView(){
return mListView;
}

/**
* 是否开启自动获取更多
* 自动获取更多,将会隐藏footer,并在到达底部的时候自动刷新
* @param index 倒数第几个触发
*/
public void enableAutoFetchMore(boolean enable, int index){
if(enable){
mListView.setBottomPosition(index);
mFooterLoadingView.setVisibility(View.VISIBLE);
}else{
mFooterTextView.setText("更多");
mFooterLoadingView.setVisibility(View.GONE);
}
mEnableAutoFetchMore = enable;
}

/*
* ==================================
* Private method
* 具体实现下拉刷新等操作
*
* ==================================
*/

/**
* 初始化界面
*/
private void initHeaderViewAndFooterViewAndListView(Context context){
setOrientation(LinearLayout.VERTICAL);
//setDrawingCacheEnabled(false);
/*
* 自定义头部文件
* 放在这里是因为考虑到很多界面都需要使用
* 如果要修改,和它相关的设置都要更改
*/
mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null);
mHeaderViewParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
addView(mHeaderView, 0, mHeaderViewParams);

mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text);
mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow);
mHeaderLoadingView = mHeaderView.findViewById(R.id.pulldown_header_loading);

// 注意,图片旋转之后,再执行旋转,坐标会重新开始计算
mRotateOTo180Animation = new RotateAnimation(0, 180,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotateOTo180Animation.setDuration(250);
mRotateOTo180Animation.setFillAfter(true);

mRotate180To0Animation = new RotateAnimation(180, 0,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotate180To0Animation.setDuration(250);
mRotate180To0Animation.setFillAfter(true);

/**
* 自定义脚部文件
*/
mFooterView = LayoutInflater.from(context).inflate(R.layout.pulldown_footer, null);
mFooterTextView = (TextView) mFooterView.findViewById(R.id.pulldown_footer_text);
mFooterLoadingView = mFooterView.findViewById(R.id.pulldown_footer_loading);
mFooterView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(!mIsFetchMoreing){
mIsFetchMoreing = true;
mFooterTextView.setText("加载更多中...");
mFooterLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onMore();
}
}
});

/*
* ScrollOverListView 同样是考虑到都是使用,所以放在这里
* 同时因为,需要它的监听事件
*/
mListView = new ScrollOverListView(context);
mListView.setVerticalScrollBarEnabled(false);
mListView.setOnScrollOverListener(this);
mListView.setCacheColorHint(0);

mListView.setDivider(GlobalVar.appContext.getResources().getDrawable(R.drawable.dot));

addView(mListView, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);

// 空的listener
mOnPullDownListener = new OnPullDownListener() {
@Override
public void onRefresh() {}
@Override
public void onMore() {}
};
}

/**
* 在下拉和回推的时候检查头部文件的状态</br>
* 如果超过了默认高度,就显示松开可以刷新,
* 否则显示下拉可以刷新
*/
private void checkHeaderViewState(){
if(mHeaderViewParams.height >= DEFAULT_HEADER_VIEW_HEIGHT){
if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return;
mHeaderViewState = HEADER_VIEW_STATE_OVER_HEIGHT;
mHeaderTextView.setText("松开可以刷新");
mHeaderArrowView.startAnimation(mRotateOTo180Animation);
}else{
if(mHeaderViewState == HEADER_VIEW_STATE_NOT_OVER_HEIGHT
|| mHeaderViewState == HEADER_VIEW_STATE_IDLE) return;
mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT;
mHeaderTextView.setText("下拉可以刷新");
mHeaderArrowView.startAnimation(mRotate180To0Animation);
}
}

private void setHeaderHeight(final int height){
mHeaderIncremental = height;
mHeaderViewParams.height = height;
mHeaderView.setLayoutParams(mHeaderViewParams);
}

/**
* 自动隐藏动画
* @author 何龙威 Email:[email protected]
*/
class HideHeaderViewTask extends TimerTask{
@Override
public void run() {
if(mIsDown) {
cancel();
return;
}
mHeaderIncremental -= AUTO_INCREMENTAL;
if(mHeaderIncremental > 0){
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
}else{
mHeaderIncremental = 0;
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
cancel();
}
}
}

/**
* 自动显示动画
* @author 何龙威 Email:[email protected]
*/
class ShowHeaderViewTask extends TimerTask{

@Override
public void run() {
if(mIsDown) {
cancel();
return;
}
mHeaderIncremental -= AUTO_INCREMENTAL;
if(mHeaderIncremental > DEFAULT_HEADER_VIEW_HEIGHT){
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
}else{
mHeaderIncremental = DEFAULT_HEADER_VIEW_HEIGHT;
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
if(!mIsRefreshing){
mIsRefreshing = true;
mUIHandler.sendEmptyMessage(WHAT_ON_REFRESH);
}
cancel();
}
}
}


private Handler mUIHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case WHAT_DID_LOAD_DATA:{
mHeaderViewParams.height = 0;
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderTextView.setText("下拉可以刷新");
mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date);
mHeaderViewDateView.setVisibility(View.VISIBLE);
mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
mHeaderArrowView.setVisibility(View.VISIBLE);
showFooterView();
return;
}

case WHAT_ON_REFRESH:{
// 要清除掉动画,否则无法隐藏
mHeaderArrowView.clearAnimation();
mHeaderArrowView.setVisibility(View.INVISIBLE);
mHeaderLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onRefresh();
return;
}

case WHAT_DID_REFRESH :{
mIsRefreshing = false;
mHeaderViewState = HEADER_VIEW_STATE_IDLE;
mHeaderArrowView.setVisibility(View.VISIBLE);
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
setHeaderHeight(0);
showFooterView();
return;
}

case WHAT_SET_HEADER_HEIGHT :{
setHeaderHeight(mHeaderIncremental);
return;
}

case WHAT_DID_MORE :{
mIsFetchMoreing = false;
mFooterTextView.setText("更多");
mFooterLoadingView.setVisibility(View.GONE);
}
}
}

};

/**
* 显示脚步脚部文件
*/
private void showFooterView(){
if(mListView.getFooterViewsCount() == 0 && isFillScreenItem()){
mListView.addFooterView(mFooterView);
mListView.setAdapter(mListView.getAdapter());
}
}

/**
* 条目是否填满整个屏幕
*/
private boolean isFillScreenItem(){
final int firstVisiblePosition = mListView.getFirstVisiblePosition();
final int lastVisiblePostion = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount();
final int visibleItemCount = lastVisiblePostion - firstVisiblePosition + 1;
final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount();

if(visibleItemCount < totalItemCount) return true;
return false;
}

/*
* ==================================
* 实现 OnScrollOverListener接口
*
*
* ==================================
*/

@Override
public boolean onListViewTopAndPullDown(int delta) {
if(mIsRefreshing || mListView.getCount() - mListView.getFooterViewsCount() == 0) return false;

int absDelta = Math.abs(delta);
final int i = (int) Math.ceil((double)absDelta / 2);

mHeaderIncremental += i;
if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeight
setHeaderHeight(mHeaderIncremental);
checkHeaderViewState();
}
return true;
}

@Override
public boolean onListViewBottomAndPullUp(int delta) {
if(!mEnableAutoFetchMore || mIsFetchMoreing) return false;
// 数量充满屏幕才触发
if(isFillScreenItem()){
mIsFetchMoreing = true;
mFooterTextView.setText("加载更多中...");
mFooterLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onMore();
return true;
}
return false;
}

@Override
public boolean onMotionDown(MotionEvent ev) {
mIsDown = true;
mIsPullUpDone = false;
mMotionDownLastY = ev.getRawY();
return false;
}

@Override
public boolean onMotionMove(MotionEvent ev, int delta) {
//当头部文件回推消失的时候,不允许滚动
if(mIsPullUpDone) return true;

// 如果开始按下到滑动距离不超过误差值,则不滑动
final int absMotionY = (int) Math.abs(ev.getRawY() - mMotionDownLastY);
if(absMotionY < START_PULL_DEVIATION) return true;

final int absDelta = Math.abs(delta);
final int i = (int) Math.ceil((double)absDelta / 2);

// onTopDown在顶部,并上回推和onTopUp相对
if(mHeaderViewParams.height > 0 && delta < 0){
mHeaderIncremental -= i;
if(mHeaderIncremental > 0){
setHeaderHeight(mHeaderIncremental);
checkHeaderViewState();
}else{
mHeaderViewState = HEADER_VIEW_STATE_IDLE;
mHeaderIncremental = 0;
setHeaderHeight(mHeaderIncremental);
mIsPullUpDone = true;
}
return true;
}
return false;
}

@Override
public boolean onMotionUp(MotionEvent ev) {
mIsDown = false;
// 避免和点击事件冲突
if(mHeaderViewParams.height > 0){
// 判断头文件拉动的距离与设定的高度,小了就隐藏,多了就固定高度
int x = mHeaderIncremental - DEFAULT_HEADER_VIEW_HEIGHT;
Timer timer = new Timer(true);
if(x < 0){
timer.scheduleAtFixedRate(new HideHeaderViewTask(), 0, 10);
}else{
timer.scheduleAtFixedRate(new ShowHeaderViewTask(), 0, 10);
}
return true;
}
return false;
}

}

3. xml布局文件Layout中的定义
pulldown_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="10dp"
    android:paddingTop="10dp" >

    <TextView
        android:id="@+id/pulldown_footer_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="更多"
        android:textSize="15dp" />

    <ProgressBar
        android:id="@+id/pulldown_footer_loading"
        style="@android:style/Widget.ProgressBar.Small"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="20dp"
        android:visibility="gone" />

</RelativeLayout>

pulldown_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="10dp"
    android:paddingTop="10dp" >

    <ImageView
        android:id="@+id/pulldown_header_arrow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="20dp"
        android:scaleType="centerCrop"
        android:src="@drawable/z_arrow_down"
        android:visibility="invisible" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/pulldown_header_arrow"
        android:layout_alignTop="@+id/pulldown_header_arrow"
        android:layout_centerHorizontal="true"
        android:gravity="center_vertical"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/pulldown_header_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="加载中..." />

        <TextView
            android:id="@+id/pulldown_header_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="更新于:"
            android:visibility="gone" />
    </LinearLayout>

    <ProgressBar
        android:id="@+id/pulldown_header_loading"
        style="@android:style/Widget.ProgressBar.Small"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="20dp" />

</RelativeLayout>

调用自定义的PullDownView部分布局文件
um_information_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:background="#FFFFFF" android:layout_width="fill_parent" android:layout_height="fill_parent"
  xmlns:android="http://schemas.android.com/apk/res/android">
...
<com.drawthink.nxjobhunting.ui.components.PullDownView
    android:id="@+id/latest_list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:background="@android:color/white"
    android:layout_below="@id/category_layout"
    >

</com.drawthink.nxjobhunting.ui.components.PullDownView>
</RelativeLayout>

4. Activity文件
JobsInformationActivity
package com.drawthink.nxjobhunting.ui;

import java.util.ArrayList;
import java.util.List;

import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;

import com.drawthink.nxjobhunting.R;
import com.drawthink.nxjobhunting.handler.HandlerManager;
import com.drawthink.nxjobhunting.http.response.CateListRespProcessor;
import com.drawthink.nxjobhunting.http.response.LatestJobsRespProcessor;
import com.drawthink.nxjobhunting.logic.CategoryManager;
import com.drawthink.nxjobhunting.logic.JobsManager;
import com.drawthink.nxjobhunting.ui.adapter.CategoryExpListAdapter;
import com.drawthink.nxjobhunting.ui.adapter.JobsListAdapter;
import com.drawthink.nxjobhunting.ui.components.PullDownView;
import com.drawthink.nxjobhunting.ui.components.PullDownView.OnPullDownListener;
import com.drawthink.nxjobhunting.utils.Constants;
import com.drawthink.nxjobhunting.utils.GlobalKeys;
import com.drawthink.nxjobhunting.vo.CategoryItem;
import com.drawthink.nxjobhunting.vo.JobInfo;

public class JobsInformationActivity extends BaseActivity implements OnPullDownListener, OnItemClickListener{
public static enum TabIndex{
LATEST,
FULL_TIME,
PART_TIME
}

private CategoryManager mManager;
private JobsManager jobManager;
// private ToolBar mToolBar;
private Button latest;
private Button fullTime;
private Button partTime;
private CategoryExpListAdapter mAdapter;
private ExpandableListView exListV;
private Button searchBtn;
private EditText searchInput;
private LinearLayout catelogL;
private RelativeLayout loadingL;

//最新职位列表
private PullDownView mPullDownView;
private ListView mListView;
private JobsListAdapter jobListAdapter;
private List<JobInfo> listData;

private TabIndex tabIndex = TabIndex.LATEST;

private String lastRecTime = "";

//keep category list in memory
public static List<CategoryItem> fullTimeGList = null;


public static List<CategoryItem> partTimeGList = null;


private OnChildClickListener childListener = new OnChildClickListener() {

@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
Intent intent = new Intent(JobsInformationActivity.this, JobsListActivity.class);
intent.putExtra(GlobalKeys.CATEGORY_ID, id);
startActivity(intent);
return false;
}

};
public void onCreate(Bundle bundle) {
super.onCreate(bundle);

setView(R.layout.um_information_activity);
setTitle(getString(R.string.job_info_main));
setTitleBar(R.drawable.home_back, "", 0, "");

// mToolBar = (ToolBar) findViewById(R.id.tool_bar);
// mToolBar.setSelection(3);
latest = (Button) findViewById(R.id.latest);
fullTime = (Button) findViewById(R.id.fulltime);
partTime = (Button) findViewById(R.id.parttime);

fullTime.setOnClickListener(this);
partTime.setOnClickListener(this);
latest.setOnClickListener(this);

latest.requestFocus();

mPullDownView =  (PullDownView) findViewById(R.id.latest_list);
mPullDownView.setOnPullDownListener(this);
mListView = mPullDownView.getListView();

jobListAdapter = new JobsListAdapter(this);
mListView.setAdapter(jobListAdapter);
mListView.setOnItemClickListener(this);
       
        mPullDownView.enableAutoFetchMore(false, 1);
       
catelogL = (LinearLayout) findViewById(R.id.category_layout);

exListV = (ExpandableListView) findViewById(R.id.categoryList);
exListV.setGroupIndicator(null);

loadingL = (RelativeLayout) findViewById(R.id.loading);

mAdapter = new CategoryExpListAdapter(this);
exListV.setAdapter(mAdapter);
exListV.setOnChildClickListener(childListener);

searchBtn = (Button) findViewById(R.id.search);
searchInput = (EditText) findViewById(R.id.search_input);

searchBtn.setOnClickListener(this);

mManager = new CategoryManager();
jobManager = new JobsManager();

HandlerManager.handlerMap.put(HandlerManager.KEY_CATEGORY, mHandler);

// mManager.getCategoryList(1);

// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
jobManager.getLatestJobs(lastRecTime);
}


@Override
public void handleClick(int buttonId) {
switch (buttonId) {

case R.id.latest:
if (tabIndex == TabIndex.LATEST) {
return;
}
//设置背景色
latest.setBackgroundResource(R.drawable.zuixin2);
fullTime.setBackgroundResource(R.drawable.quanzhi2);
partTime.setBackgroundResource(R.drawable.jianzhi2);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj1);

if (mPullDownView.getVisibility() == View.GONE) {
exListV.setVisibility(View.GONE);
mPullDownView.setVisibility(View.VISIBLE);
}
tabIndex = TabIndex.LATEST;
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
// mManager.getCategoryList(1);
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.fulltime:
if (tabIndex == TabIndex.FULL_TIME) {
return;
}
//设置背景色
latest.setBackgroundResource(R.drawable.zuixin1);
fullTime.setBackgroundResource(R.drawable.quanzhi1);
partTime.setBackgroundResource(R.drawable.jianzhi2);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj2);

if (fullTimeGList == null) {
loadingL.setVisibility(View.VISIBLE);
exListV.setVisibility(View.GONE);
mManager.getCategoryList(1);
} else {
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
exListV.setVisibility(View.VISIBLE);
mAdapter.setData(fullTimeGList);
}
if (tabIndex == TabIndex.LATEST) {
exListV.setVisibility(View.VISIBLE);
mPullDownView.setVisibility(View.GONE);
}

tabIndex = TabIndex.FULL_TIME;


// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.parttime:
if (tabIndex == TabIndex.PART_TIME) {
return;
}
latest.setBackgroundResource(R.drawable.zuixin1);
fullTime.setBackgroundResource(R.drawable.quanzhi2);
partTime.setBackgroundResource(R.drawable.jianzhi1);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj3);

if (partTimeGList == null) {
loadingL.setVisibility(View.VISIBLE);
exListV.setVisibility(View.GONE);
mManager.getCategoryList(2);
} else {
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
exListV.setVisibility(View.VISIBLE);
mAdapter.setData(partTimeGList);
}

if (tabIndex == TabIndex.LATEST) {
exListV.setVisibility(View.VISIBLE);
mPullDownView.setVisibility(View.GONE);
}

tabIndex = TabIndex.PART_TIME;
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.search:
final String keyword = searchInput.getText().toString();
if (!TextUtils.isEmpty(keyword)) {
Intent intent = new Intent(Constants.ACTION_SEARCH);
intent.putExtra(GlobalKeys.KEYWORD, keyword);
startActivity(intent);
}

break;
}
}

public void onMessage(Message msg) {
switch (msg.what) {
case LatestJobsRespProcessor.MSG_REFRESH_JOBS:
List<JobInfo> list = (List<JobInfo>)msg.obj;
boolean isFirstLoad = "".equals(lastRecTime);
if (list.size() > 0) {
lastRecTime = list.get(0).date;
jobListAdapter.addDataToFront(list);
}
if (isFirstLoad) {
mPullDownView.notifyDidLoad();
} else {
mPullDownView.notifyDidRefresh();
}

break;
case CateListRespProcessor.MSG_REFRESH:
List<CategoryItem> itemList = (List<CategoryItem>)msg.obj;
mAdapter.setData(itemList);

if (tabIndex == TabIndex.FULL_TIME) {
fullTimeGList = itemList;
} else if (tabIndex == TabIndex.PART_TIME) {
partTimeGList = itemList;
}

if (mProgressDialog != null) {
mProgressDialog.cancel();
mProgressDialog = null;
}

if (mPullDownView.getVisibility() == View.VISIBLE) {
mPullDownView.setVisibility(View.GONE);
}

if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}

exListV.setVisibility(View.VISIBLE);

break;
}
}

@Override
protected void onLeftAction() {
finish();
}


@Override
protected void onRightAction() {

}


@Override
public void onMore() {
mPullDownView.notifyDidMore();
}


@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
Intent intent = new Intent(this, JobDetailActivity.class);
intent.putExtra("id", (int)id);
JobInfo info = (JobInfo) adapter.getItemAtPosition(position);
intent.putExtra("title", info.title);
intent.putExtra("number", info.num);
startActivity(intent);
}


@Override
public void onRefresh() {
jobManager.getLatestJobs(lastRecTime);
mListView.setSelection(-1);
}


}
其中onRefresh异步调用网络数据, 在onMessage中返回, 更新列表数据。

至此结束,希望对大家有帮助。

猜你喜欢

转载自seya.iteye.com/blog/1733492