Android之XListView下拉刷新,更新网络美女图

一、简介:

 下拉刷新是一种特定的手动刷新交互,和其他的同类操作不同的地方在于它采用了更加直觉的下拉操作,所以它的交互足够清晰明显。

下拉刷新主要用在类似ListView这样的控件,设计下拉刷新有三种方法:

  ① 使用SwipeRefreshLayout(没有在底部点击查看更多,需要自定义该功能), 常用的方法如下

   1、setOnRefreshListener(OnRefreshListener listener)  设置下拉监听,当用户下拉的时候会去执行回调

   2、setColorSchemeColors(int... colors) 设置 进度条的颜色变化,最多可以设置4种颜色

   3、setProgressViewOffset(boolean scale, int start, int end) 调整进度条距离屏幕顶部的距离

   4、setRefreshing(boolean refreshing) 设置SwipeRefreshLayout当前是否处于刷新状态,一般是在请求数据的时候设置为true,在数据被加载到View中后,设置为false。

  ②自定义控件,原理主要通过onTouchEvent中的ACTION_DOWN和ACTION_MOVE来计算触摸差值,再实现动画效果

  ③从github下载开源框架,直接使用,例如XListView

  实现的效果如下图:    demo地址 --http://files.cnblogs.com/files/wujiancheng/XListView%E6%9F%A5%E7%9C%8B%E5%9B%BEsample.zip

记得连接WIFi,查看网络图片超耗流量 Android之XListView下拉刷新,更新网络美女图Android之XListView下拉刷新,更新网络美女图

Android之XListView下拉刷新,更新网络美女图Android之XListView下拉刷新,更新网络美女图Android之XListView下拉刷新,更新网络美女图

  二、使用:

1、关于XUtils3(可以不用看它,重点是XListView) (https://github.com/wyouflf/xUtils3)

  主要作用: 请求网络、使用数据库、注解、绑定图片

先关联XUtils,compile project(':xutils')

设置权限 <uses-permission android:name="android.permission.INTERNET" />

             <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

初始化 创建一个 MyApplication继承自Application,在onCreate()方法内初始化下面两行代码(具体可参照github的例子)

   super.onCreate();

   x.Ext.init(this);

   x.Ext.setDebug(BuildConfig.DEBUG);// 是否输出debug日志, 开启debug会影响性能

在清单文件中必须配置 <application android:name="MyApplication" .../>   

因为图片是从网络上获取的,使用了请求网络,代码如下:

x.http().get(new RequestParams(url), new Callback.CommonCallback<String>() {
 @Override
 public void onSuccess(String result) {
 imageListAdapter.addSrc(getImgSrcList(result));
 imageListAdapter.notifyDataSetChanged();//通知listview更新数据
 }
 
 @Override
 public void onError(Throwable ex, boolean isOnCallback) {
 
 }
 
 @Override
 public void onCancelled(CancelledException cex) {
 
 }
 
 @Override
 public void onFinished() {
 
 }
 });

绑定图片,代码如下:

x.image().bind(holder.imgItem,
                     imgSrcList.get(position),
                     imageOptions,
                     new CustomBitmapLoadCallBack(holder));

2、关于XListView  (https://github.com/Maxwin-z/XListView-Android)

  主要作用:下拉刷新,上拉加载更多

XListView是一个自定控件,所以只需将它的源码直接用

Android之XListView下拉刷新,更新网络美女图Android之XListView下拉刷新,更新网络美女图  红色框内的需要拿来用

根据main.xml Android之XListView下拉刷新,更新网络美女图  可知<me.maxwin.view.XListView ......></me.maxwin.view.XListView> 所以需要把<ListView ..... />替换掉

根据XListViewActivityAndroid之XListView下拉刷新,更新网络美女图

......

mListView.setPullLoadEnable(true);

mListView.setXListViewListener(this);  //调用下面的接口IXListViewListener

......

public void setXListViewListener(IXListViewListener l) {     mListViewListener = l;}

.......

上面几行代码必须写上

/** * implements this interface to get refresh/load more event. */public interface IXListViewListener {     public void onRefresh();     public void onLoadMore();}

三、XListView源码分析

public class XListView extends ListView implements OnScrollListener{   ......//上拉加载更多,将一些控件显示出来,例如ProgressBar,文本的设置,没什么好分析的,只分析下拉刷新
/** * stop refresh, reset header view. */public void stopRefresh() {   if (mPullRefreshing == true) {      mPullRefreshing = false;      resetHeaderHeight();   }}
private void resetHeaderHeight() {
        //向下滑动的高度
        int height = mHeaderView.getVisiableHeight();
        if (height == 0) // 没滑动时,不做任何操作
            return;
        //滑动的高度小于header view's height,不做任何操作
        if (mPullRefreshing && height <= mHeaderViewHeight) {
            return;
        }
        int finalHeight = 0;  //若滑动后不做任何操作,值为0 否则为mHeaderViewHeight
        // 滑动的高度大于header view's height,设置finalHeight
        if (mPullRefreshing && height > mHeaderViewHeight) {
            finalHeight = mHeaderViewHeight;
        }
        mScrollBack = SCROLLBACK_HEADER;

        /**
         * 第一个参数是起始移动的x坐标值,第二个是起始移动的y坐标值
           第三及第四个参数都是移到某点的坐标值,duration 是执行移动的时间
         */
        mScroller.startScroll(0, height, 0, finalHeight - height,
                SCROLL_DURATION);


        //  触发computeScroll()
        /**
         * public void computeScroll ()
         Called by a parent to request that a child update its values for mScrollX and
         mScrollY if necessary. This will typically be done if the child is animating
         a scroll using a Scroller object.
         */
        //ontouch或invalidate()或postInvalidate()都会导致computeScroll()的执行
        invalidate();
    }
@Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mLastY == -1) {
            mLastY = ev.getRawY();
        }

        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //获取刚触碰的Y高度(原始的)
            mLastY = ev.getRawY();
            break;
        case MotionEvent.ACTION_MOVE:
            final float deltaY = ev.getRawY() - mLastY;
            mLastY = ev.getRawY();

            //getFirstVisiblePosition获取当前状态下list的第一个可见item的position
            //所以在listview中position总的加了1,获取准确的需要get(position - 1)
            if (getFirstVisiblePosition() == 0
                    && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {

                // 第一项是显示,下拉箭头显示或拉下来
                //类似于iOS下拉特性 deltaY / OFFSET_RADIO
                updateHeaderHeight(deltaY / OFFSET_RADIO);
                invokeOnScrolling();
            } else if (getLastVisiblePosition() == mTotalItemCount - 1
                    && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
                // 最后一个item,上拉有效
                updateFooterHeight(-deltaY / OFFSET_RADIO);
            }
            break;
        default:
            mLastY = -1; // 重置
            if (getFirstVisiblePosition() == 0) {
                // 调用刷新
                if (mEnablePullRefresh
                        && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
                    mPullRefreshing = true;
                    mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
                    if (mListViewListener != null) {
                        mListViewListener.onRefresh();
                    }
                }
                resetHeaderHeight();
            } else if (getLastVisiblePosition() == mTotalItemCount - 1) {
                // 调用加载更多
                if (mEnablePullLoad
                    && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA
                    && !mPullLoading) {
                    startLoadMore();
                }
                resetFooterHeight();
            }
            break;
        }
        return super.onTouchEvent(ev);
    }

// startScroll执行过程中即在duration时间内,computeScrollOffset  方法会一直返回false,但当动画执行完成后会返回返加true.

@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            //如果执行过下拉刷新则 mScrollBack == SCROLLBACK_HEADER
            if (mScrollBack == SCROLLBACK_HEADER) {
                mHeaderView.setVisiableHeight(mScroller.getCurrY());
            } else {
                mFooterView.setBottomMargin(mScroller.getCurrY());
            }

            //类似于invalidate(),用于刷新view,主要在非UI线程中使用
            postInvalidate();
            //回滚监听
            invokeOnScrolling();
        }
        super.computeScroll();
    }
......}
public class XListViewHeader extends LinearLayout {   ......
//旋转动画
        /**
         * RotateAnimation (float fromDegrees, float toDegrees, int pivotXType,
         * float pivotXValue, int pivotYType, float pivotYValue)
         参数说明:
         float fromDegrees:旋转的开始角度。
         float toDegrees:旋转的结束角度。
         int pivotXType:X轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。
         float pivotXValue:X坐标的伸缩值。
         int pivotYType:Y轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。
         float pivotYValue:Y坐标的伸缩值。
         */
        mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        //设置动画持续时间
        mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
        //动画执行完后是否停留在执行完的状态
        mRotateUpAnim.setFillAfter(true);
        mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
        mRotateDownAnim.setFillAfter(true);
......}
&nbsp;