GVKun编程网logo

Android ListView 疯狂之旅 之 《自定义下拉刷新功能的ListView》(android下拉刷新框架)

32

本篇文章给大家谈谈AndroidListView疯狂之旅之《自定义下拉刷新功能的ListView》,以及android下拉刷新框架的知识点,同时本文还将给你拓展androidlistview下拉刷新以

本篇文章给大家谈谈Android ListView 疯狂之旅 之 《自定义下拉刷新功能的ListView》,以及android下拉刷新框架的知识点,同时本文还将给你拓展android listview 下拉刷新以及加载更多、Android ListView 疯狂之旅 之 《自定义下拉刷新功能的 ListView》、Android ListView下拉与上拉刷新加载更多(一)、Android listView作为来自iPhone的listView等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

Android ListView 疯狂之旅 之 《自定义下拉刷新功能的ListView》(android下拉刷新框架)

Android ListView 疯狂之旅 之 《自定义下拉刷新功能的ListView》(android下拉刷新框架)

效果图:

一 首先创建一个类,继承ListView,编写其构造方法

public class RefreshListView  extends ListView {
    public RefreshListView(Context context) {
        this(context, null);
    }
    public RefreshListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    private void init() {
    }
}

二 在activity中使用我们的RefreshListView控件

向ListView中设置数据的过程与我们android中提供的ListView设置数据的方式一至,这里就不贴出代码块了

此时运行的效果图


可以看到与我们正常的ListView是一样的

三 向RefresListView中添加我们下拉刷新使用到的头布局


头布局中的显示,包括显示下拉状态的向下的指示箭头,包括指示正在加载中的圆形进度条,包括显示状态与时间的文本框

定义头布局文件

xlistview_footer.xml文件中

<pre name="code"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="bottom" >
    <RelativeLayout
        android:id="@+id/xlistview_header_content"
        android:layout_width="fill_parent"
        android:layout_height="60dp" >
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="vertical"
            android:id="@+id/xlistview_header_text">
            <TextView
                android:id="@+id/xlistview_header_hint_textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉刷新" />
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="3dp" >
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="上次更新时间:"
                    android:textSize="12sp" />
                <TextView
                    android:id="@+id/xlistview_header_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="12sp" />
            </LinearLayout>
        </LinearLayout>

        <ImageView
            android:id="@+id/xlistview_header_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@id/xlistview_header_text"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-35dp"
            android:src="@drawable/xlistview_arrow" />

        <ProgressBar
            android:id="@+id/xlistview_header_progressbar"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_alignLeft="@id/xlistview_header_text"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-40dp"
            android:visibility="visible" />
    </RelativeLayout>

</LinearLayout>



将头布局文件添加给ListView

在RefresListView中

private void init(Context context) {
        View view = View.inflate(context,R.layout.xlistview_header,null);
        this.addHeaderView(view);
    }

四 隐藏我们添加的头布局

首先让系统去帮我们测量头布局的高度,测量好后,我们就可以设置头布局的显示位置

 private void init(Context context) {
        View view = View.inflate(context, R.layout.xlistview_header, null);
        view.measure(0, 0);
        final int measuredHeight = view.getMeasuredHeight();
        view.setPadding(0,-measuredHeight,0,0);
        this.addHeaderView(view);
    }

五 设置头布局与LisView的联动

使用我们的控件实现OnTouchListener接口,并复写onTouch方法

import android.view.View.OnTouchListener;
public class RefreshListView  extends ListView implements OnTouchListener {....

@Override
    public boolean onTouch(View v, MotionEvent event) {
        return true;
    }

}


在onTouch方法中我们需要重新设置显示头布局,所以我们需要将部分变量修改为成员变量

private void init(Context context) {
        setonTouchListener(this);
        //获取头布局
        mHeaderView = View.inflate(context, R.layout.xlistview_header, null);
        //进行测量
        mHeaderView.measure(0, 0);
        //获取头布局的高度
        mHeaderHeight = mHeaderView.getMeasuredHeight();
        //设置头布局显示
        mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);
        //添加到ListView中
        this.addHeaderView(mHeaderView);
    }

在onTouch方法中进行设置显示随ListView的滑动而显示的头布局

@Override
    public boolean onTouchEvent( MotionEvent event) {
        int downY = 0;
         switch ( event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 //获取按下时的Y轴的位置
                downY = (int)event.getY();
             break;
             case MotionEvent.ACTION_MOVE:
                int moveY = (int)event.getY();
                 //计算在Y轴方向的滑动偏移量,
                 int diffY = moveY - downY;
                 //计算我们的头布局需要移动的距离
                 int paddingTop = -mHeaderHeight +diffY;
                 //设置显示头布局
                 mHeaderView.setPadding(0,paddingTop,0,0);
             break;
             default:
                 break;
         }
        return true;
    }

注:这里之所以使用使用的是-mHeaderHeight高度,是因为我们在上面设置的头布局的paddingTop的大小正好是头布局的高度,也就是说刚刚好将头布局显示在屏幕外侧


优化代码

 //设置显示头布局,当显示的条目为第一条的时候并且移动的距离大于0的时候再开时向下移动
                 if(getFirstVisiblePosition()==0&&diffY>0){
                     mHeaderView.setPadding(0,paddingTop,0,0);
                     return  true;
                 }

六 定义更新下拉刷新过程中的头布局显示变化

定义下拉至释放刷新的中的变量

  // 代表下拉刷新状态 
    private final int PULL_REFRESH_STATE = 0;
    // 释放刷新状态
    private final int RELEASE_STATE = 1; 
    // 正在刷新状态
    private final int RELEASEING = 2; 
    // 默认当前是下拉刷新状态
    private int header_current_state = PULL_REFRESH_STATE;

创建更新头布局的方法

//更新头布局的方法
    private void updateHeaderView() {
        switch (header_current_state) {
            case PULL_REFRESH_STATE:   //下拉刷新状态
                break;
            case RELEASE_STATE:        //释放刷新状态
                break;
            case RELEASEING:           //正在刷新的状态
                break;
        }
    }

在onTouchEvent方法 中ACTION_MOVE事件处理中调用使用我们的更新头布局的方法

case MotionEvent.ACTION_MOVE:
                int moveY = (int)event.getY();
                 //计算在Y轴方向的滑动偏移量,
                 int diffY = moveY - downY;
                 //计算我们的头布局需要移动的距离
                 int paddingTop = -mHeaderHeight +diffY;
                 //设置显示头布局,当显示的条目为第一条的时候并且移动的距离大于0的时候再开时向下移动
                 if(getFirstVisiblePosition()==0&&diffY>0){
                     //判断paddingTop 来更新头布局
                     if (paddingTop > 0 && header_current_state != RELEASE_STATE) {
                         // System.out.println("进入释放刷新状态");
                         header_current_state = RELEASE_STATE;
                         // 更新头布局状态
                         updateHeaderView();
                     } else if (paddingTop < 0
                             && header_current_state != PULL_REFRESH_STATE) {
                         // System.out.println("~~~~下拉刷新状态~~~~");
                         header_current_state = PULL_REFRESH_STATE;
                         updateHeaderView();
                     }
                     mHeaderView.setPadding(0,paddingTop,0,0);
                     return  true;
                 }

然后我们在方法updateHeaderView这个方法中对应的状态下更新设置显示我们头布局中的信息

 //显示下拉上拉正在刷新状态的textview
        mXlistviewHeaderHintTextview = (TextView) mHeaderView.findViewById(R.id.xlistview_header_hint_textview);
       //头布局中显示时间的控件
        mXlistviewHeaderTime = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time);
        //头布局中显示的指示箭头
        mXlistviewHeaderArrow = (ImageView) mHeaderView.findViewById(R.id.xlistview_header_arrow);
        //头布局中显示的加载圆形进度条
        mXlistviewHeaderProgressbar = (ProgressBar) mHeaderView.findViewById(R.id.xlistview_header_progressbar);

当头布局出现在屏幕上,但是还没有完全显示在屏幕上的时候,指示箭头应当向下,状太信息应当为下拉刷新

当头布局完全显示在屏幕上的时候,指示箭头指示方向向上,状态为释放刷新

//更新头布局的方法
    private void updateHeaderView() {
        switch (header_current_state) {
            case PULL_REFRESH_STATE:   //下拉刷新状态
                //[1]更改iv状态
                mXlistviewHeaderArrow.startAnimation(downAnimation);
                //[2]设置 tv_update 状态
                mXlistviewHeaderHintTextview.setText("下拉刷新");
                //[3]隐藏头布局
                mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);
                break;
            case RELEASE_STATE:        //释放刷新状态
                mXlistviewHeaderArrow.startAnimation(upAnimation);
                mXlistviewHeaderHintTextview.setText("释放刷新");
                break;
            case RELEASEING:           //正在刷新的状态
                //[1]把动画图片隐藏
                mXlistviewHeaderArrow.setVisibility(View.INVISIBLE);
                mXlistviewHeaderArrow.clearanimation();
                //[2]显示进度条
                mXlistviewHeaderProgressbar.setVisibility(View.VISIBLE);
                //[3]刷新状态的文字改为 正在刷新
                mXlistviewHeaderHintTextview.setText("正在刷新ing");
                //[4]设置头布局回到屏幕顶部
                mHeaderView.setPadding(0, 0, 0, 0);
                break;
        }


在onTouchEvent中处理ACTION_UP事件

如果当前的状态是释放刷新,那当手指抬起的时候,我们将要进行正在刷新状态,并进行相关UI显示更新操作

case MotionEvent.ACTION_UP:
                     if (header_current_state == PULL_REFRESH_STATE) {
                         updateHeaderView();
                     }else if (header_current_state == RELEASE_STATE) {
                         //[1]把正在刷新状态 赋值给 当前状态
                         header_current_state = RELEASEING;
                         //[2]调用更新头布局的方法
                         updateHeaderView();
                         //[3]更新为下拉状态
//				header_current_state =PULL_REFRESH_STATE;
                     }
             break;


可以看到上面使用到了将指示箭头进行旋转操作的动画,可以在构造中进行初始化操作

 private RotateAnimation upAnimation;
 private RotateAnimation downAnimation;



//初始化头布局 图片旋转的动画
    private void initAnim() {
        //向上旋转的动画
        upAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        upAnimation.setDuration(500);//设置动画执行的时长
        upAnimation.setFillAfter(true);

        //向下旋转的动画
        downAnimation = new RotateAnimation(-180, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        downAnimation.setDuration(500);//设置动画执行的时长
        downAnimation.setFillAfter(true);

    }


七 设置下拉刷新的监听回调接口

 //设置下拉刷新数据的接口
    public interface OnPullDownRefreshListener{
        public void onPullDownRefresh();
    }
    private  OnPullDownRefreshListener mOnPullDownRefreshListener;
    //设置刷新的监听
    public void setonPullDownRefreshListener(OnPullDownRefreshListener listener){
        mOnPullDownRefreshListener = listener;
    }

然后在onTouchEvent方法中的ACTION_UP事件中片回调

   if (mOnPullDownRefreshListener!=null) {
                             mOnPullDownRefreshListener.onRefresh();
                         }

八 设置加载数据完成后,隐藏头布局并更新显示时间

ublic void setonLoadFinish() {
        //[0]进度条隐藏 
        mXlistviewHeaderProgressbar.setVisibility(View.INVISIBLE);
        //[2]在这里更新时间 
        mXlistviewHeaderTime.setText(getCurrentTimerr());
        //[3]隐藏头布局
        mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);
        //[4]把状态置为下拉状态 
        header_current_state = PULL_REFRESH_STATE;
    }
也就是说当我们的数据更新完毕后,需要调用我们设置的方法将显示刷新头布局隐藏,并更新相应的状态与显示信息

更新刷新显示的时间 :

public String getCurrentTimerr(){
     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     return sdf.format(new Date());
	}

到这里,我们的这个完整的具有下拉刷新功能的ListView就可以进行使用了

九 添加上拉加载更多数据功能

private void initFootView() {
        //[1]通过打气筒把一个布局转换成一个view对象
        mfootView = View.inflate(getContext(), R.layout.xlistview_footer, null);
        mfootView.measure(0, 0);
        footHeight = mfootView.getMeasuredHeight();
        //[1]默认情况隐藏脚布局
         mfootView.setPadding(0, -footHeight, 0, 0);
        //[2]添加脚布局
        this.addFooterView(mfootView);
        //[3]给listview设置滑动监听
        this.setonScrollListener(this);
    }

脚布局文件中 :

<pre name="code"><?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:padding="10dp" >

        <TextView
            android:id="@+id/xlistview_footer_hint_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/xlistview_footer_hint_normal" />
    <ProgressBar
        android:layout_toLeftOf="@id/xlistview_footer_hint_textview"
        android:id="@+id/xlistview_footer_progressbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="visible" />
    </RelativeLayout>


可以看到这里设置了一个滑动监听,在这里我们需要将我们的控件实现滑动监听事件

public class RefreshListView  extends ListView implements AbsListView.OnScrollListener {

...



    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    }
}

设置上拉加载更多的回调监听

 private OnUpLoadingMoreListener mOnUpLoadingMoreListener;
    public void setonLoadingMoreListener(OnUpLoadingMoreListener l){
        mOnUpLoadingMoreListener = l;
    }
    public interface OnUpLoadingMoreListener{
        public void onl oadingMore();
    }

在listView的滑动监听事件中回调监听方法

@Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (scrollState== SCROLL_STATE_IDLE ||scrollState == SCROLL_STATE_FLING) {
            //判断lisview 是否滑动到了底部
            if (getLastVisiblePosition()==getCount()-1 ) {
                //[1]把脚布局显示出来
                mfootView.setPadding(0, 0, 0, 0);
                if (mOnUpLoadingMoreListener!=null) {
                    mOnUpLoadingMoreListener.onLoadingMore();
                }
            }
        }
    }

设置加载完成后调用方法来隐藏加载更多的显示布局

 //加载更多完成 需要处理的逻辑
    public void setonLoadIngMoreFinish() {
        //[1]把加载更多脚布局隐藏
        mfootView.setPadding(0, -footHeight, 0, 0);
    }


点击下载源码

下载密码:uvfr

Android ListView 疯狂之旅  第一季     《自定义侧拉删除》 点击打开链接查看
Android ListView 疯狂之旅  第二季     《分组排序显示数据》   点击打开链接查看


最新脑筋急转:  男人与女人一起比赛猜智力题 ,一般女人会赢,因为”难得糊涂“,男的糊涂

android listview 下拉刷新以及加载更多

android listview 下拉刷新以及加载更多

本例是实现下拉时刷新并且底部设置加载更多选择并在下拉和回滑时加入相应的动画效果, 功能实现主要为自定义一个Layout在此布局中头部刷新主要是用到了在头部添加布局view控件并使用GestureDetector控制下拉时的变化以便控制滑动的效果,在自定义底部也加一个加载更多的布局,中间内嵌一个ListView控件以显示内容。具体步骤如下:

第一步:设计自定义布局PullDownListView继承FrameLayout 实现GestureDetector.OnGestureListener和Animation.AnimationListener接口:

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;
import com.example.pulldownview.R;
public class PullDownListView extends FrameLayout implements
GestureDetector.OnGestureListener, Animation.AnimationListener {
public static int MAX_LENGHT = 0;
public static final int STATE_REFRESH = 1;
public static final int SCROLL_TO_CLOSE = 2;
public static final int SCROLL_TO_REFRESH = 3;
public static final double SCALE = 0.9d;
private static final int CLOSEDELAY = 300;
private static final int REFRESHDELAY = 300;
private Animation mAnimationDown;
private Animation mAnimationUp;
private ImageView mArrow;
private View emptyHeaderView;
private ProgressBar mProgressBar;
private TextView more;
private ProgressBar mProgressBar2;
private int mState;
private TextView mTitle;
public ListView mListView;
LinearLayout foot;
LinearLayout footer_layout;
LinearLayout header;
private GestureDetector mDetector;
private FlingRunnable mFlinger;
private int mPading;
private int mDestPading;
private int mLastTop;
private LinearLayout mFirstChild;
private FrameLayout mUpdateContent;
private OnRefreshListioner mRefreshListioner;
private boolean isAutoLoadMore = false;
private boolean hasMore = true;
private boolean isEnd = true;
private boolean listviewDoScroll = false;
private boolean isFirstLoading = false;
private boolean mLongPressing;// 如果设置为true说明刚好到了执行长按的时间
private boolean mPendingRemoved = false;//
private String pulldowntorefresh;
private String releasetorefresh;
private String loading;
Rect r = new Rect();
private MotionEvent downEvent;
private CheckForLongPress mPendingCheckForLongPress = new CheckForLongPress();
private CheckForLongPress2 mPendingCheckForLongPress2 = new CheckForLongPress2();
private float lastY;
private boolean useempty = true;
//这个标签作为测试用
String TAG = "PullDownListView";
/**
 * 长按检查方法执行1线程
 * @author Administrator
 *
 */
private class CheckForLongPress implements Runnable {
    public void run() {  
        if (mListView.getOnItemLongClickListener() == null) {  
            
        } else {  
                postDelayed(mPendingCheckForLongPress2, 100);     
        }  
     }  
} 
/**
 * 长按检查方法执行2线程 ----> 延后 100 
 * @author Administrator
 *
 */
private class CheckForLongPress2 implements Runnable {
    public void run() {
        mLongPressing = true;
        MotionEvent e = MotionEvent.obtain(downEvent.getDownTime(),downEvent.getEventTime()
            + ViewConfiguration.getLongPressTimeout(),
        MotionEvent.ACTION_CANCEL, downEvent.getX(),
        downEvent.getY(), downEvent.getMetaState());
        PullDownListView.super.dispatchTouchEvent(e);
    }
}
class FlingRunnable implements Runnable {
    private void startCommon() {
        removeCallbacks(this);
    }
    public void run() {
        boolean noFinish = mScroller.computeScrollOffset();
        int curY = mScroller.getCurrY();
        int deltaY = curY - mLastFlingY;
        if (noFinish) {
            move(deltaY, true);
            mLastFlingY = curY;
            post(this);
        } else {
            removeCallbacks(this);
        if (mState == SCROLL_TO_CLOSE) {
            mState = -1;
        }
    }
}
public void startUsingDistance(int distance, int duration) {
    if (distance == 0)
        distance--;
    startCommon();
    mLastFlingY = 0;
    mScroller.startScroll(0, 0, 0, distance, duration);
    post(this);
}
private int mLastFlingY;
private Scroller mScroller;
public FlingRunnable() {
    mScroller = new Scroller(getContext());
    }
}
/**
 * 下拉刷新以及加载更多回调监听接口
 * @author Administrator
 *
 */
public interface OnRefreshListioner {
    public abstract void onRefresh();
    public abstract void onLoadMore();
}
/**
 * 直接new时调用的构造方法 
 * @param context
 */
public PullDownListView(Context context) {
    super(context);
    mDetector = new GestureDetector(context, this);
    mFlinger = new FlingRunnable();
    init();
    addRefreshBar();
}
/**
 * 在xml中使用时调用的构造方法
 * @param context
 */
public PullDownListView(Context context, AttributeSet att) {
    super(context, att);
    useempty = att.getAttributeBooleanValue(null, "useempty", true);
    mDetector = new GestureDetector(this);
    mFlinger = new FlingRunnable();
    init();
    addRefreshBar();
}
    View view;
/**
 * 添加刷新头部的控件
 */
private void addRefreshBar() {
    //向上滑动的动画
    mAnimationUp = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_up);
    mAnimationUp.setAnimationListener(this);
    //向下滑动的动画
    mAnimationDown = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_down);
    mAnimationDown.setAnimationListener(this);
    //刷新头部的view
    view = LayoutInflater.from(getContext()).inflate(R.layout.refresh_bar,null);
    //添加view在本控件中
    addView(view);
/*
 * 以下都是刷新头部的一些控件的设置
 */
mFirstChild = (LinearLayout) view;
mUpdateContent = (FrameLayout) getChildAt(0).findViewById(R.id.iv_content);
mArrow = new ImageView(getContext());
FrameLayout.LayoutParams layoutparams = new FrameLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
mArrow.setScaleType(ImageView.ScaleType.FIT_CENTER);
mArrow.setLayoutParams(layoutparams);
mArrow.setImageResource(R.drawable.arrow_down);
mUpdateContent.addView(mArrow);
FrameLayout.LayoutParams layoutparams1 = new FrameLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
layoutparams1.gravity = Gravity.CENTER;
mProgressBar = new ProgressBar(getContext(), null,
android.R.attr.progressBarStyleSmallInverse);
mProgressBar.setIndeterminate(false);
int i = getResources().getDimensionPixelSize(R.dimen.updatebar_padding);
mProgressBar.setPadding(i, i, i, i);
mProgressBar.setLayoutParams(layoutparams1);
mUpdateContent.addView(mProgressBar);
mTitle = (TextView) findViewById(R.id.tv_title);
}
public void setGone() {
    mTitle.setVisibility(View.GONE);
    mUpdateContent.setVisibility(View.GONE);
}
protected void onFinishInflate() {
    super.onFinishInflate();
    mListView = (ListView) getChildAt(1);
    footer_layout = (LinearLayout) LayoutInflater.from(getContext())
    .inflate(R.layout.empty_main, null);
    foot = (LinearLayout) LayoutInflater.from(getContext()).inflate(
    R.layout.ref2, null);
    more = (TextView) foot.findViewById(R.id.ref);
    mProgressBar2 = (ProgressBar) foot.findViewById(R.id.refbar);
    mProgressBar2.setVisibility(View.GONE);
    if (useempty) {
        header = (LinearLayout) LayoutInflater.from(getContext()).inflate(
        R.layout.empty_main, null);
        mListView.addHeaderView(header);
    }
    mListView.addFooterView(footer_layout);
    foot.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            if (!isAutoLoadMore)
                onLoadMore();
         }
});
mListView.setOnScrollListener(new OnScrollListener() {
    public void onScrollStateChanged(AbsListView view, int scrollState) {
         if (isEnd && scrollState == SCROLL_STATE_IDLE && hasMore&& isAutoLoadMore) {
                onLoadMore();
        }
    }
public void onScroll(AbsListView view, int f, int v, int t) {
    if (isAutoLoadMore) {
        if (f + v >= t - 1)
            isEnd = true;
        else
            isEnd = false;
        }
    }
});
}
/**
 * 设置没有数据时默认图片
 * 
 * @param empty
 */
public void setEmptyHeaderView(View empty) {
    emptyHeaderView = empty;
}
/**
 * 添加空的定部view
 */
public void addEmptyHeaderView() {
    header.removeAllViews();
    if (emptyHeaderView != null)
        header.addView(emptyHeaderView);
}
/**
 * 移除顶部空view
 */
public void removeEmptyHeaderView() {
    if (emptyHeaderView != null)
        header.removeView(emptyHeaderView);
}
/**
 * 初始化设置及变量
 */
private void init() {
    MAX_LENGHT = getResources().getDimensionPixelSize(
    R.dimen.updatebar_height);// 62.0dip
    setDrawingCacheEnabled(false);
    setBackgroundDrawable(null);
    setClipChildren(false);
    mDetector.setIsLongpressEnabled(false);
    mPading = -MAX_LENGHT;
    mLastTop = -MAX_LENGHT;
    pulldowntorefresh = getContext().getText(R.string.drop_dowm).toString();
    releasetorefresh = getContext().getText(R.string.release_update)
    .toString();
    loading = getContext().getText(R.string.loading).toString();
}
/** deltaY > 0 向上 */
private boolean move(float deltaY, boolean auto) {
    //move 方法执行 " 
    if (deltaY > 0 && mFirstChild.getTop() == -MAX_LENGHT) {
    mPading = -MAX_LENGHT;
    return false;
}
if (auto) {
    //move 方法执行
if (mFirstChild.getTop() - deltaY < mDestPading) {
    deltaY = mFirstChild.getTop() - mDestPading;
}
mFirstChild.offsetTopAndBottom((int) -deltaY);
mListView.offsetTopAndBottom((int) -deltaY);
mPading = mFirstChild.getTop();
if (mDestPading == 0 && mFirstChild.getTop() == 0&& mState == SCROLL_TO_REFRESH) {
    //onRefresh 刷新方法执行
    onRefresh();
    } else if (mDestPading == -MAX_LENGHT) {
        
    }
invalidate();
updateView();
return true;
} else {
    if (mState != STATE_REFRESH || (mState == STATE_REFRESH && deltaY > 0)) {
    mFirstChild.offsetTopAndBottom((int) -deltaY);
    mListView.offsetTopAndBottom((int) -deltaY);
    mPading = mFirstChild.getTop();
} else if (mState == STATE_REFRESH && deltaY < 0&& mFirstChild.getTop() <= 0) {
    if (mFirstChild.getTop() - deltaY > 0) {
        deltaY = mFirstChild.getTop();
    }
    mFirstChild.offsetTopAndBottom((int) -deltaY);
    mListView.offsetTopAndBottom((int) -deltaY);
    mPading = mFirstChild.getTop();
    }
}
if (deltaY > 0 && mFirstChild.getTop() <= -MAX_LENGHT) {
    mPading = -MAX_LENGHT;
    deltaY = -MAX_LENGHT - mFirstChild.getTop();
    mFirstChild.offsetTopAndBottom((int) deltaY);
    mListView.offsetTopAndBottom((int) deltaY);
    mPading = mFirstChild.getTop();
    updateView();
    invalidate();
    return false;
}
updateView();
invalidate();
return true;
}
private void updateView() {
String s = "";
if (mState != STATE_REFRESH) {
    if (mFirstChild.getTop() < 0) {
        mArrow.setVisibility(View.VISIBLE);
        mProgressBar.setVisibility(View.INVISIBLE);
        mTitle.setText(pulldowntorefresh);
        if (mLastTop >= 0 && mState != SCROLL_TO_CLOSE) {
            mArrow.startAnimation(mAnimationUp);//向上移动动画
        }
    } else if (mFirstChild.getTop() > 0) {
            mTitle.setText(releasetorefresh + s);
            mProgressBar.setVisibility(View.INVISIBLE);
            mArrow.setVisibility(View.VISIBLE);
        if (mLastTop <= 0) {
            mArrow.startAnimation(mAnimationDown);//向下移动动画
        }
    }
}
mLastTop = mFirstChild.getTop();
}
//release 方法执行 
private boolean release() {
    if (listviewDoScroll) {
        listviewDoScroll = false;
        return true;
    }
    if (mFirstChild.getTop() > 0) {
        scrollToUpdate(false);
    } else {
        scrollToClose();
    }
        invalidate();
        return false;
    }
    private void scrollToClose() {
        mDestPading = -MAX_LENGHT;
        mFlinger.startUsingDistance(MAX_LENGHT, CLOSEDELAY);
    }
//scrollToUpdate 方法执行
public void scrollToUpdate(boolean load) {
    mState = SCROLL_TO_REFRESH;
    mDestPading = 0;
    if (load) {
        mFlinger.startUsingDistance(50, REFRESHDELAY);
        load = false;
    } else
        mFlinger.startUsingDistance(mFirstChild.getTop(), REFRESHDELAY);
}
private void onRefresh() {
mState = STATE_REFRESH;
mTitle.setText(loading);
mProgressBar.setVisibility(View.VISIBLE);
mArrow.setVisibility(View.INVISIBLE);
if (mRefreshListioner != null) {
    mRefreshListioner.onRefresh();
}
}
    public void onRefreshComplete() {
    onRefreshComplete(null);
}
public void onRefreshComplete(String date) {
    mState = SCROLL_TO_CLOSE;
    mArrow.setImageResource(R.drawable.arrow_down);
    mProgressBar2.setVisibility(View.INVISIBLE);
    updateCommon();
    scrollToClose();
}
public void setMore(boolean hasMore) {
if (hasMore) {
    mListView.setFooterDividersEnabled(true);
    footer_layout.removeAllViews();
    footer_layout.addView(foot);
} else {
    mListView.setFooterDividersEnabled(false);
    footer_layout.removeAllViews();
}
}
private void updateCommon() {
if (mListView.getCount() == (mListView.getHeaderViewsCount() + mListView .getFooterViewsCount())) {
    Log.e("out", "数据为空");
if (useempty)
    addEmptyHeaderView();
} else {
    removeEmptyHeaderView();
    mListView.setFooterDividersEnabled(false);
    footer_layout.removeAllViews();
}
}
public void setFoot() {
    footer_layout.setVisibility(View.GONE);
}
public void onFirstLoad() {
if (footer_layout.getChildCount() == 0) {
    footer_layout.addView(foot);
}
isFirstLoading = true;
foot.setEnabled(false);
//onFirstLoad 方法执行 
mState = STATE_REFRESH;
mProgressBar2.setVisibility(View.VISIBLE);
more.setText(R.string.loading);
}
public void onLoadMore() {
//onLoadMore 方法执行 
foot.setEnabled(false);
mState = STATE_REFRESH;
mProgressBar2.setVisibility(View.VISIBLE);
more.setText(R.string.loading);
if (mRefreshListioner != null) {
    mRefreshListioner.onLoadMore();
}
}
public void onLoadMoreComplete(String date) {
mState = -1;
mProgressBar2.setVisibility(View.INVISIBLE);
more.setText(R.string.seen_more);
updateCommon();
if (isFirstLoading)
isFirstLoading = false;
foot.setEnabled(true);
}
public void onLoadMoreComplete() {
    onLoadMoreComplete(null);
}
public boolean dispatchTouchEvent(MotionEvent e) {
if (isFirstLoading) {
    return false;
}
int action;
float y = e.getY();
action = e.getAction();
if (mLongPressing && action != MotionEvent.ACTION_DOWN) {
    return false;
}
if (e.getAction() == MotionEvent.ACTION_DOWN) {
    mLongPressing = true;
}
boolean handled = true;
handled = mDetector.onTouchEvent(e);
switch (action) {
case MotionEvent.ACTION_UP:
boolean f1 = mListView.getTop() <= e.getY()
&& e.getY() <= mListView.getBottom();
if (!handled && mFirstChild.getTop() == -MAX_LENGHT && f1|| mState == STATE_REFRESH) {
    super.dispatchTouchEvent(e);
} else {
    //执行释放方法 
    handled = release();
}
break;
case MotionEvent.ACTION_CANCEL:
handled = release();
super.dispatchTouchEvent(e);
break;
case MotionEvent.ACTION_DOWN:
downEvent = e;
mLongPressing = false;
//长按的时间间隔
postDelayed(mPendingCheckForLongPress,
ViewConfiguration.getLongPressTimeout() + 100);
mPendingRemoved = false;
super.dispatchTouchEvent(e);
break;
case MotionEvent.ACTION_MOVE:
float deltaY = lastY - y;
lastY = y;
if (!mPendingRemoved) {
    removeCallbacks(mPendingCheckForLongPress);
    mPendingRemoved = true;
}
if (!handled && mFirstChild.getTop() == -MAX_LENGHT) {
    try {
        return super.dispatchTouchEvent(e);
    } catch (Exception e2) {
        e2.printStackTrace();
        return true;
    }
} else if (handled && mListView.getTop() > 0 && deltaY < 0) {// deltaY小于0,向�?
    e.setAction(MotionEvent.ACTION_CANCEL);
    super.dispatchTouchEvent(e);
}
break;
default:
break;
}
return true;
}
public void onAnimationEnd(Animation animation) {
int top = mFirstChild.getTop();
if (top < 0)
    mArrow.setImageResource(R.drawable.arrow_down);
else if (top > 0)
    mArrow.setImageResource(R.drawable.arrow_up);
else {
    if (top < mLastTop) {
        mArrow.setImageResource(R.drawable.arrow_down);
    } else {
        mArrow.setImageResource(R.drawable.arrow_up);
    }
}
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
public boolean onDown(MotionEvent e) {
    return false;
}
public boolean onFling(MotionEvent motionevent, MotionEvent e, float f,
float f1) {
    return false;
}
protected void onLayout(boolean flag, int i, int j, int k, int l) {
    int top = mPading;
    int w = getMeasuredWidth();
    mFirstChild.layout(0, top, w, top + MAX_LENGHT);
    int h = getMeasuredHeight() + mPading + MAX_LENGHT;
    mListView.layout(0, top + MAX_LENGHT, w, h);
}
public void onLongPress(MotionEvent e) {
}
/** deltaY > 0 向上 */
public boolean onScroll(MotionEvent curdown, MotionEvent cur, float deltaX,
float deltaY) {
    deltaY = (float) ((double) deltaY * SCALE);
    boolean handled = false;
    boolean flag = false;
if (mListView.getCount() == 0) {
    flag = true;
} else {
    View c = mListView.getChildAt(0);
if (mListView.getFirstVisiblePosition() == 0 && c != null
    && c.getTop() == 0) {
    flag = true;
}
}
if (deltaY < 0F && flag || getChildAt(0).getTop() > -MAX_LENGHT) { // deltaY
// <
// 0
// 向下
handled = move(deltaY, false);
} else
handled = false;
return handled;
}
public void onShowPress(MotionEvent motionevent) {
}
public boolean onSingleTapUp(MotionEvent motionevent) {
    return false;
}
public void setRefreshListioner(OnRefreshListioner RefreshListioner) {
    mRefreshListioner = RefreshListioner;
}
public boolean isAutoLoadMore() {
    return isAutoLoadMore;
}
public void setAutoLoadMore(boolean isAutoLoadMore) {
    this.isAutoLoadMore = isAutoLoadMore;
if (!isAutoLoadMore) {
    foot.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
    onLoadMore();
}
});
mListView.setOnScrollListener(null);
} else {
mListView.setOnScrollListener(new OnScrollListener() {
public void onScrollStateChanged(AbsListView view,
int scrollState) {
if (isEnd && scrollState == SCROLL_STATE_IDLE && hasMore) {
onLoadMore();
}
}
public void onScroll(AbsListView view, int f, int v, int t) {
if (f + v >= t - 1)
    isEnd = true;
else
    isEnd = false;
}
});
foot.setOnClickListener(null);
}
}
public void setHasMore(boolean hasMore) {
    this.hasMore = hasMore;
}
public void removeFoot() {
    footer_layout.removeAllViews();
}
public void addFoot() {
    footer_layout.removeAllViews();
    footer_layout.addView(foot);
}
}

第二步:设计xml这里注意的是在自定义控件PullDownListView中内嵌一个ListView控件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
       <com.example.widget.PullDownListView
        android:id="@+id/sreach_list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
        <ListView
            android:id="@+id/list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="0.0"
            android:divider="@null"
            android:drawSelectorOnTop="false"
            android:fadingEdgeLength="0.0sp" />
    </com.example.widget.PullDownListView>
</RelativeLayout>

第三步:编写MainActivity 实现PullDownListView.OnRefreshListioner回调接口实现功能的演示:

import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.ListView;
import com.example.widget.PullDownListView;
public class MainActivity extends Activity  implements PullDownListView.OnRefreshListioner{
private PullDownListView mPullDownView;
private ListView mListView;
private List<String> list = new ArrayList<String>();
private MyAdapter adapter;
private Handler mHandler = new Handler();
private int maxAount = 20;//设置了最大数据值
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    	mPullDownView = (PullDownListView) findViewById(R.id.sreach_list);
mPullDownView.setRefreshListioner(this);
mListView = mPullDownView.mListView;
addLists(10);
    adapter = new MyAdapter(this,list);
    mPullDownView.setMore(true);//这里设置true表示还有更多加载,设置为false底部将不显示更多
    mListView.setAdapter(adapter);
    }
    
    private void addLists(int n){
    
    	 n += list.size();
    	 for(int i=list.size();i<n;i++){
        list.add("选项"+i);
     }
    }
    
    /**
     * 刷新,先清空list中数据然后重新加载更新内容
     */
public void onRefresh() {
mHandler.postDelayed(new Runnable() {
public void run() {
list.clear();
addLists(10);
mPullDownView.onRefreshComplete();//这里表示刷新处理完成后把上面的加载刷新界面隐藏
mPullDownView.setMore(true);//这里设置true表示还有更多加载,设置为false底部将不显示更多
adapter.notifyDataSetChanged();
}
}, 1500);
}
/**
 * 加载更多,在原来基础上在添加新内容
 */
public void onLoadMore() {
mHandler.postDelayed(new Runnable() {
public void run() {
addLists(5);//每次加载五项新内容
mPullDownView.onLoadMoreComplete();//这里表示加载更多处理完成后把下面的加载更多界面(隐藏或者设置字样更多)
if(list.size()<maxAount)//判断当前list中已添加的数据是否小于最大值maxAount,是那么久显示更多否则不显示
mPullDownView.setMore(true);//这里设置true表示还有更多加载,设置为false底部将不显示更多
else
mPullDownView.setMore(false);
adapter.notifyDataSetChanged();
}
}, 1500);
}
    
}

第四:运行效果如图: 输入图片说明 输入图片说明 输入图片说明 输入图片说明 输入图片说明

Android ListView 疯狂之旅 之 《自定义下拉刷新功能的 ListView》

Android ListView 疯狂之旅 之 《自定义下拉刷新功能的 ListView》



效果图:




一 首先创建一个类,继承 ListView,编写其构造方法


public class RefreshListView  extends ListView {
    public RefreshListView(Context context) {
        this(context, null);
    }
    public RefreshListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    private void init() {
    }
}


二 在 activity 中使用我们的 RefreshListView 控件


向 ListView 中设置数据的过程与我们 android 中提供的 ListView 设置数据的方式一至,这里就不贴出代码块了

此时运行的效果图


可以看到与我们正常的 ListView 是一样的


三 向 RefresListView 中添加我们下拉刷新使用到的头布局



头布局中的显示,包括显示下拉状态的向下的指示箭头,包括指示正在加载中的圆形进度条,包括显示状态与时间的文本框


定义头布局文件

xlistview_footer.xml 文件中

<pre name="code" class="html"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="bottom" >
    <RelativeLayout
        android:id="@+id/xlistview_header_content"
        android:layout_width="fill_parent"
        android:layout_height="60dp" >
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="vertical"
            android:id="@+id/xlistview_header_text">
            <TextView
                android:id="@+id/xlistview_header_hint_textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉刷新" />
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="3dp" >
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="上次更新时间:"
                    android:textSize="12sp" />
                <TextView
                    android:id="@+id/xlistview_header_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="12sp" />
            </LinearLayout>
        </LinearLayout>

        <ImageView
            android:id="@+id/xlistview_header_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@id/xlistview_header_text"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-35dp"
            android:src="@drawable/xlistview_arrow" />

        <ProgressBar
            android:id="@+id/xlistview_header_progressbar"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_alignLeft="@id/xlistview_header_text"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-40dp"
            android:visibility="visible" />
    </RelativeLayout>

</LinearLayout>


将头布局文件添加给 ListView

在 RefresListView 中

private void init(Context context) {
        View view = View.inflate(context,R.layout.xlistview_header,null);
        this.addHeaderView(view);
    }

四 隐藏我们添加的头布局

首先让系统去帮我们测量头布局的高度,测量好后,我们就可以设置头布局的显示位置

 private void init(Context context) {
        View view = View.inflate(context, R.layout.xlistview_header, null);
        view.measure(0, 0);
        final int measuredHeight = view.getMeasuredHeight();
        view.setPadding(0,-measuredHeight,0,0);
        this.addHeaderView(view);
    }


五 设置头布局与 LisView 的联动

使用我们的控件实现 OnTouchListener 接口,并复写 onTouch 方法

import android.view.View.OnTouchListener;
public class RefreshListView  extends ListView implements OnTouchListener {....

@Override
    public boolean onTouch(View v, MotionEvent event) {
        return true;
    }

}

在 onTouch 方法中我们需要重新设置显示头布局,所以我们需要将部分变量修改为成员变量

private void init(Context context) {
        setOnTouchListener(this);
        //获取头布局
        mHeaderView = View.inflate(context, R.layout.xlistview_header, null);
        //进行测量
        mHeaderView.measure(0, 0);
        //获取头布局的高度
        mHeaderHeight = mHeaderView.getMeasuredHeight();
        //设置头布局显示
        mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);
        //添加到ListView中
        this.addHeaderView(mHeaderView);
    }

在 onTouch 方法中进行设置显示随 ListView 的滑动而显示的头布局

@Override
    public boolean onTouchEvent( MotionEvent event) {
        int downY = 0;
         switch ( event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 //获取按下时的Y轴的位置
                downY = (int)event.getY();
             break;
             case MotionEvent.ACTION_MOVE:
                int moveY = (int)event.getY();
                 //计算在Y轴方向的滑动偏移量,
                 int diffY = moveY - downY;
                 //计算我们的头布局需要移动的距离
                 int paddingTop = -mHeaderHeight +diffY;
                 //设置显示头布局
                 mHeaderView.setPadding(0,paddingTop,0,0);
             break;
             default:
                 break;
         }
        return true;
    }

注:这里之所以使用使用的是 - mHeaderHeight 高度,是因为我们在上面设置的头布局的 paddingTop 的大小正好是头布局的高度,也就是说刚刚好将头布局显示在屏幕外侧



优化代码

 //设置显示头布局,当显示的条目为第一条的时候并且移动的距离大于0的时候再开时向下移动
                 if(getFirstVisiblePosition()==0&&diffY>0){
                     mHeaderView.setPadding(0,paddingTop,0,0);
                     return  true;
                 }

六 定义更新下拉刷新过程中的头布局显示变化


定义下拉至释放刷新的中的变量

  // 代表下拉刷新状态 
    private final int PULL_REFRESH_STATE = 0;
    // 释放刷新状态
    private final int RELEASE_STATE = 1; 
    // 正在刷新状态
    private final int RELEASEING = 2; 
    // 默认当前是下拉刷新状态
    private int header_current_state = PULL_REFRESH_STATE;

创建更新头布局的方法

//更新头布局的方法
    private void updateHeaderView() {
        switch (header_current_state) {
            case PULL_REFRESH_STATE:   //下拉刷新状态
                break;
            case RELEASE_STATE:        //释放刷新状态
                break;
            case RELEASEING:           //正在刷新的状态
                break;
        }
    }

在 onTouchEvent 方法 中 ACTION_MOVE 事件处理中调用使用我们的更新头布局的方法

case MotionEvent.ACTION_MOVE:
                int moveY = (int)event.getY();
                 //计算在Y轴方向的滑动偏移量,
                 int diffY = moveY - downY;
                 //计算我们的头布局需要移动的距离
                 int paddingTop = -mHeaderHeight +diffY;
                 //设置显示头布局,当显示的条目为第一条的时候并且移动的距离大于0的时候再开时向下移动
                 if(getFirstVisiblePosition()==0&&diffY>0){
                     //判断paddingTop 来更新头布局
                     if (paddingTop > 0 && header_current_state != RELEASE_STATE) {
                         // System.out.println("进入释放刷新状态");
                         header_current_state = RELEASE_STATE;
                         // 更新头布局状态
                         updateHeaderView();
                     } else if (paddingTop < 0
                             && header_current_state != PULL_REFRESH_STATE) {
                         // System.out.println("~~~~下拉刷新状态~~~~");
                         header_current_state = PULL_REFRESH_STATE;
                         updateHeaderView();
                     }
                     mHeaderView.setPadding(0,paddingTop,0,0);
                     return  true;
                 }

然后我们在方法 updateHeaderView 这个方法中对应的状态下更新设置显示我们头布局中的信息

 //显示下拉上拉正在刷新状态的textview
        mXlistviewHeaderHintTextview = (TextView) mHeaderView.findViewById(R.id.xlistview_header_hint_textview);
       //头布局中显示时间的控件
        mXlistviewHeaderTime = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time);
        //头布局中显示的指示箭头
        mXlistviewHeaderArrow = (ImageView) mHeaderView.findViewById(R.id.xlistview_header_arrow);
        //头布局中显示的加载圆形进度条
        mXlistviewHeaderProgressbar = (ProgressBar) mHeaderView.findViewById(R.id.xlistview_header_progressbar);

当头布局出现在屏幕上,但是还没有完全显示在屏幕上的时候,指示箭头应当向下,状太信息应当为下拉刷新

当头布局完全显示在屏幕上的时候,指示箭头指示方向向上,状态为释放刷新

//更新头布局的方法
    private void updateHeaderView() {
        switch (header_current_state) {
            case PULL_REFRESH_STATE:   //下拉刷新状态
                //[1]更改iv状态
                mXlistviewHeaderArrow.startAnimation(downAnimation);
                //[2]设置 tv_update 状态
                mXlistviewHeaderHintTextview.setText("下拉刷新");
                //[3]隐藏头布局
                mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);
                break;
            case RELEASE_STATE:        //释放刷新状态
                mXlistviewHeaderArrow.startAnimation(upAnimation);
                mXlistviewHeaderHintTextview.setText("释放刷新");
                break;
            case RELEASEING:           //正在刷新的状态
                //[1]把动画图片隐藏
                mXlistviewHeaderArrow.setVisibility(View.INVISIBLE);
                mXlistviewHeaderArrow.clearAnimation();
                //[2]显示进度条
                mXlistviewHeaderProgressbar.setVisibility(View.VISIBLE);
                //[3]刷新状态的文字改为 正在刷新
                mXlistviewHeaderHintTextview.setText("正在刷新ing");
                //[4]设置头布局回到屏幕顶部
                mHeaderView.setPadding(0, 0, 0, 0);
                break;
        }




在 onTouchEvent 中处理 ACTION_UP 事件

如果当前的状态是释放刷新,那当手指抬起的时候,我们将要进行正在刷新状态,并进行相关 UI 显示更新操作

case MotionEvent.ACTION_UP:
                     if (header_current_state == PULL_REFRESH_STATE) {
                         updateHeaderView();
                     }else if (header_current_state == RELEASE_STATE) {
                         //[1]把正在刷新状态 赋值给 当前状态
                         header_current_state = RELEASEING;
                         //[2]调用更新头布局的方法
                         updateHeaderView();
                         //[3]更新为下拉状态
//				header_current_state =PULL_REFRESH_STATE;
                     }
             break;



可以看到上面使用到了将指示箭头进行旋转操作的动画,可以在构造中进行初始化操作

 private RotateAnimation upAnimation;
 private RotateAnimation downAnimation;



//初始化头布局 图片旋转的动画
    private void initAnim() {
        //向上旋转的动画
        upAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        upAnimation.setDuration(500);//设置动画执行的时长
        upAnimation.setFillAfter(true);

        //向下旋转的动画
        downAnimation = new RotateAnimation(-180, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        downAnimation.setDuration(500);//设置动画执行的时长
        downAnimation.setFillAfter(true);

    }


七 设置下拉刷新的监听回调接口


 //设置下拉刷新数据的接口
    public interface OnPullDownRefreshListener{
        public void onPullDownRefresh();
    }
    private  OnPullDownRefreshListener mOnPullDownRefreshListener;
    //设置刷新的监听
    public void setOnPullDownRefreshListener(OnPullDownRefreshListener listener){
        mOnPullDownRefreshListener = listener;
    }

然后在 onTouchEvent 方法中的 ACTION_UP 事件中片回调

   if (mOnPullDownRefreshListener!=null) {
                             mOnPullDownRefreshListener.onRefresh();
                         }

八 设置加载数据完成后,隐藏头布局并更新显示时间


ublic void setOnLoadFinish() {
        //[0]进度条隐藏 
        mXlistviewHeaderProgressbar.setVisibility(View.INVISIBLE);
        //[2]在这里更新时间 
        mXlistviewHeaderTime.setText(getCurrentTimerr());
        //[3]隐藏头布局
        mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);
        //[4]把状态置为下拉状态 
        header_current_state = PULL_REFRESH_STATE;
    }
也就是说当我们的数据更新完毕后,需要调用我们设置的方法将显示刷新头布局隐藏,并更新相应的状态与显示信息

更新刷新显示的时间 :

public String getCurrentTimerr(){
     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     return sdf.format(new Date());
	}

到这里,我们的这个完整的具有下拉刷新功能的 ListView 就可以进行使用了


九 添加上拉加载更多数据功能

private void initFootView() {
        //[1]通过打气筒把一个布局转换成一个view对象
        mfootView = View.inflate(getContext(), R.layout.xlistview_footer, null);
        mfootView.measure(0, 0);
        footHeight = mfootView.getMeasuredHeight();
        //[1]默认情况隐藏脚布局
         mfootView.setPadding(0, -footHeight, 0, 0);
        //[2]添加脚布局
        this.addFooterView(mfootView);
        //[3]给listview设置滑动监听
        this.setOnScrollListener(this);
    }


脚布局文件中 :

<pre name="code" class="html"><?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:padding="10dp" >

        <TextView
            android:id="@+id/xlistview_footer_hint_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/xlistview_footer_hint_normal" />
    <ProgressBar
        android:layout_toLeftOf="@id/xlistview_footer_hint_textview"
        android:id="@+id/xlistview_footer_progressbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="visible" />
    </RelativeLayout>

可以看到这里设置了一个滑动监听,在这里我们需要将我们的控件实现滑动监听事件

public class RefreshListView  extends ListView implements AbsListView.OnScrollListener {

...



    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    }
}

设置上拉加载更多的回调监听

 private OnUpLoadingMoreListener mOnUpLoadingMoreListener;
    public void setOnLoadingMoreListener(OnUpLoadingMoreListener l){
        mOnUpLoadingMoreListener = l;
    }
    public interface OnUpLoadingMoreListener{
        public void onLoadingMore();
    }

在 listView 的滑动监听事件中回调监听方法

@Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (scrollState== SCROLL_STATE_IDLE ||scrollState == SCROLL_STATE_FLING) {
            //判断lisview 是否滑动到了底部
            if (getLastVisiblePosition()==getCount()-1 ) {
                //[1]把脚布局显示出来
                mfootView.setPadding(0, 0, 0, 0);
                if (mOnUpLoadingMoreListener!=null) {
                    mOnUpLoadingMoreListener.onLoadingMore();
                }
            }
        }
    }

设置加载完成后调用方法来隐藏加载更多的显示布局

 //加载更多完成 需要处理的逻辑
    public void setOnLoadIngMoreFinish() {
        //[1]把加载更多脚布局隐藏
        mfootView.setPadding(0, -footHeight, 0, 0);
    }








点击下载源码

下载密码:uvfr



Android ListView 疯狂之旅  第一季     《自定义侧拉删除》 点击打开链接查看
Android ListView 疯狂之旅  第二季     《分组排序显示数据》   点击打开链接查看



最新脑筋急转:  男人与女人一起比赛猜智力题 ,一般女人会赢,因为” 难得糊涂 “,男的糊涂

本文同步分享在 博客 “早起的年轻人”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与 “OSC 源创计划”,欢迎正在阅读的你也加入,一起分享。

Android ListView下拉与上拉刷新加载更多(一)

Android ListView下拉与上拉刷新加载更多(一)





效果图:

                 


java代码中的实现:



public class MainActivity extends ActionBarActivity implements WaterDropListView.IWaterDropListViewListener  {

    private WaterDropListView waterDropListView;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    waterDropListView.stopRefresh();
                    break;
                case 2:
                    waterDropListView.stopLoadMore();
                    break;
            }

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        waterDropListView = (WaterDropListView) findViewById(R.id.waterdrop_listview);
        waterDropListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, getData()));
        waterDropListView.setWaterDropListViewListener(this);
        waterDropListView.setPullLoadEnable(true);
    }

    private List<String> getData(){
        List<String> data = new ArrayList<String>();
        for (int i = 0; i < 30; i++) {
			 data.add("this is a test");
		}
        return data;
    }
    @Override
    public void onRefresh() {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    handler.sendEmptyMessage(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

    }

    @Override
    public void onLoadMore() {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    handler.sendEmptyMessage(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

activity_activity.xml中


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.listviewreflesdemo.MainActivity" >
 <com.example.listviewreflesdemo.view.WaterDropListView
           android:id="@+id/waterdrop_listview"
           android:layout_width="match_parent"
           android:layout_height="wrap_content">
       </com.example.listviewreflesdemo.view.WaterDropListView>

</RelativeLayout>


这里需要添加的所依赖的自定义的控件:

http://download.csdn.net/detail/zl18603543572/9338819











本文同步分享在 博客“早起的年轻人”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

Android listView作为来自iPhone的listView

Android listView作为来自iPhone的listView

我想实现一个类似于iPhone的ListView.

表格的第一个元素是图像,其他元素是文本加箭头.

我知道如何使用自定义图像适配器,但仅用于唯一的自定义,而不是自定义每一行.

该信息是静态的,我应该只能点击所需的元素.

非常感谢你.

解决方法:

我找到了解决方案,

我可以使用标题列表视图和自定义边框.
这是一个例子:

drawable文件夹中的listview_border.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="4dp" android:color="#FFFFFFFF" />
<padding android:left="7dp" android:top="7dp"
        android:right="7dp" android:bottom="7dp" />
<corners android:radius="15dp" />
<solid android:color="#FFFFFFFF"/>
</shape> 

对于列表视图,设置:

 android:background="@drawable/listview_border"

然后对于hedser,在布局中创建listviaw_header.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

   <ImageView
       android:id="@+id/imageView1"
       android:layout_width="128dp"
       android:layout_height="wrap_content"
       android:src="@drawable/contact_image_fr"
       android:layout_marginLeft="10dp" />

</LinearLayout> 

然后设置标题:

ListView contact = (ListView) findViewById(R.id.contactListView);


    View header = getLayoutInflater().inflate(R.layout.contact_listview_header, null);

    contact.addHeaderView(header);  

就这样.请享用…

关于Android ListView 疯狂之旅 之 《自定义下拉刷新功能的ListView》android下拉刷新框架的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于android listview 下拉刷新以及加载更多、Android ListView 疯狂之旅 之 《自定义下拉刷新功能的 ListView》、Android ListView下拉与上拉刷新加载更多(一)、Android listView作为来自iPhone的listView的相关知识,请在本站寻找。

本文标签: