【原版的:参赛作品】窥秘懒---android打开下拉手势刷新时代

小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处

*****************************************************************

       前言:窥视懒人那些不为人知的秘密

*****************************************************************

  作为一个程序猿,哪有不勤奋的道理。当我们都在为技术奋不顾身的时候。偏偏懒人创造了世界。

  有的时候真心没有办法理解。为什么?

  为什么懒人什么都不做。却能创造出一个又一个的奇迹,创造出一个又一个的经典。

  非常早都听过一句话:懒人创造世界。

  由于懒得记住那些复杂的DOS命令,于是,比尔盖茨继承图形界面的设计。完毕了一次图形界面与机器的完美结合,当全世界的电脑都一模一样的是,他也成为了世界首富;

  由于懒得整天带个随身听听音乐。于是,乔布斯公布了iPod。不仅改变了人们赞赏音乐的方式。还改变了整个音乐产业。

  由于懒得开每天电脑。于是移动互联网应运而生。由于懒得点击那些那些难过的button,开发人员们不断的改进科技,一次来迎合越来越懒的人的需求。

  是的。讲到这里。我们仿佛明确了一个道理。

所谓懒惰不是真的懒,而是为了,他们在满足我们高效高速的生活同一时候,极力让产品更加的人性化。让产品更能服务懒人的生活。

懒人们的福音,而创造世界的人。恰恰看到了这一点。他们是为懒人思考的懒人。

  

  当众多的开发框架,开源项目在网上激起一波又一波的项目模仿浪潮,我们能够看到的是,我们的周围充斥着一样的东西。那就是懒人逻辑。模仿懒人逻辑。

说了这么多。都没有引出我们今天的主题。不是由于我没有料,而是,在引出料的同一时候,希望自己能过有很多其它的思考。

那些料究竟是为了迎合谁的胃口而被调试出来的。是的,我们就是为了满足不同人的口味,而在不断的调试着一种一种的料。以求这样的料在投入市场的时候,能做出适合很多其它人的产品。

   在一次小的面试中,面试老师问道了我一个问题,像QQ一样的软件,都有一个功能叫下拉刷新,它是怎么实现的。

这个我们起初在回答的时候。肯定都是仅仅停留在去解释它的实现代码的含义。却不清楚为什么要这种实现,又为什么有这种实现。

在看了几天的开源项目后,在此。我想对自己的理解做一次总结。

******************************************************************************

       面对一个东西。我们总要问了:为什么?

******************************************************************************

 1.为什么有滑动这种实现效果?

    下拉刷新技术的发明者是Twitter以前最好的第三方clientTweetie的创始人洛伦.布里切特(Loren Brichter)。Twitter于2010年收购该公司时就已获取该项技术的专利权。

    在《宅男成功:28岁的应用设计教父》一文中,我们能够看到这种设计师出自什么情况下。随着简约和设计在竞争异常激烈的应用程序行业变得越加重要。布里切特也正与凭着各种点子成为众所周知在应用设计领域有影响力的人。

    原文摘录:

Mr. Brichter, whose design aesthetic is inspired by information theorists like Edward Tufte, a proponent of minimizing extraneous information in graphic designs, says he thinks up new features for apps based on how people move objects in the real world.
布里切特的设计美学受到了爱德华?塔夫特(Edward Tufte)等信息理论家的启示,塔夫特主张在图形设计里将无关的信息降低到最小程度。布里切特说他是依据人们在现实世界里移动物体的方式构思出应用程序的新功能的。

‘Everything should come from somewhere and go somewhere,‘ he says, adding that he‘s irked by apps that have menus that pop up or collapse on themselves because the interactions aren‘t real. ‘The most important thing is obviousness. The problem is overdesign.‘
他说:“不论什么事物都应该有来源有去处。

”他还说自己非常不喜欢那些菜单自己弹出或者收起的应用程序,由于那种互动是不真实的。“最重要的事情是要显而易见。问题出在过度设计上。”

    在这片文章中。我们看到这种话,仿佛知道了些什么。在设计下拉刷新的过程中。他的设计美学受到了爱德华 塔夫特(Edward Tufte)等信息理论家的启示,塔夫特主张在图形设计里将无关的信息降低到最小程度。

布里特说他是人们在线式世界里移动物体的方式构思出应用程序的新功能的。而正是这样希望用更少的动作,去实如今懒人们看来没有必要太多的动作。形成的效果设计,满足的人机交互中最简单的一个理论:降低人们的记忆符合。

    为什么这么说呢?由于,软件最大的特点就是与机器进行交流。而人要与机器交流就要通过软件。

假设软件中操作步骤过多,就是easy阻碍人的使用感。那么用户体验就会由于的开发人员的疏忽而变的糟糕。

当下拉刷新出如今GitHub。一度成为软件中最流行的效果。当然,发现最新技术的人都是对技术有着灵敏的嗅觉。随时探測技术中的流行趋势。

  2.为什么要这样实现?

    下拉刷新,我们常见的效果应该是例如以下图所看到的:这样的事QQ界面中的下拉刷新

         

    在GitHub中总共同拥有十种下拉刷新的效果,各自是:

    

     ------ ListView

      

     ------ ExpandableListView

      

     ------ GridView

      

     ------ WebView

      

     ------ ScrollView

     ------ Horizontal ScrollView

     ------ ViewPager

     ------ ListView Fragment

     ------ WebView Advanced

     ------ ListView in ViewPager

    以下,我先介绍一下。怎么获取得到下拉刷新的开源项目:

    首先,在GitHub搜索Android-PullToRefresh-master

    

    其次,下载文件,解压

    

    

    将项目加入到eclipse中后。有非常多的问题出现。比方library会一直报错。这种话。就要又一次载入一下。library的引用库文件。这个项目是通过项目与项目之间的关联性,整合到一起的。

    为什么要这样实现?一来是由于。下拉刷新简单方便。有用详细。

二来对于开发人员来说也是一项新的改变。改变曾经button点击的时代。从简单的button监听。用户看不到刷新的过程。带下拉刷新,用户參与到整个刷新的过程,连续的动画,让这样的效果瞬间风靡移动互联网应用也就是情理之中的事了。

  3.为什么android功能代码要这样写?

    在開始抛析功能代码之前,我们先想想它的实现场景。

    

    当用户进行下拉刷新操作时,设计效果经过的三个步骤:

    第一步:当手指下拉的时候,在listView的上方有出现一个向下的箭头,和pull to refresh。。。

的字样。

(可见当手指下拉的时候。箭头的imageView,和textView从隐藏到显示)

    第二步:在手指下拉超过了一定的高度时,箭头方向反转向上。和release to refresh 。

。。的字样。

(可见当用户在操作出现过度的时候要实施进行提醒,而此时,箭头通过一个反转的动画,使整个效果看起来精美,连贯)

    第三步:在手指松开时箭头消失。出现ProgressBar旋转载入图标和Loading的字样。(可见载入的过程摆在用户面前。自己的简化了操作的复杂性,同一时候保持了用户在操作中的參与度。

    第四步:当刷新完毕的时候,UI恢复到刷新前的状态。头部填充布局都消失。(这样就悄无声息的实现了刷新的整个过程,如此简单的设计。却有着不简单的思考)

    就这样,我们開始分析代码的实现过程吧。

    对于android项目来说。这三步都被封装在一个类中,就是PullToRefreshListView自己定义布局中。通过对布局的声明和调用,就可以实现这种一个过程。

    我们都知道。在listView中,能够在该控件的上端加入布局。如图所看到的:

    

    对于功能代码的解析,我们分为两个方面

    一方面:要先建立一个头部布局的layout文件

        1.建立布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pull_to_refresh_header"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF"
    android:gravity="center"
    android:paddingBottom="15dip"
    android:paddingTop="10dip" >

    <ProgressBar
        android:id="@+id/pull_to_refresh_progress"
        style="?

android:attr/progressBarStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="30dip"
        android:layout_marginRight="20dip"
        android:layout_marginTop="10dip"
        android:indeterminate="true"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/pull_to_refresh_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="30dip"
        android:layout_marginRight="20dip"
        android:gravity="center"
        android:src="@drawable/arrow"
        android:visibility="gone" />

    <TextView
        android:id="@+id/pull_to_refresh_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textColor="#000000"
        android:gravity="center"
        android:paddingTop="5dip"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/pull_to_refresh_updated_at"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/pull_to_refresh_text"
        android:layout_gravity="center"
        android:gravity="center"
        android:textAppearance="?

android:attr/textAppearanceSmall"
        android:visibility="gone" />

</RelativeLayout>

        2.对建立的头部布局进行注冊,以及填充到listView中

View
Code

/**
 * 初始化控件和箭头动画
 * @param context
 */
    private void init(Context context) {
        mFlipAnimation = new RotateAnimation(0, -180,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        mFlipAnimation.setInterpolator(new LinearInterpolator());
        mFlipAnimation.setDuration(250);
        mFlipAnimation.setFillAfter(true);
        mReverseFlipAnimation = new RotateAnimation(-180, 0,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
        mReverseFlipAnimation.setDuration(250);
        mReverseFlipAnimation.setFillAfter(true);

        mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        mRefreshView = (RelativeLayout) mInflater.inflate(
                R.layout.pull_to_refresh_header, this, false);
        mRefreshViewText = (TextView) mRefreshView
                .findViewById(R.id.pull_to_refresh_text);
        mRefreshViewImage = (ImageView) mRefreshView
                .findViewById(R.id.pull_to_refresh_image);
        mRefreshViewProgress = (ProgressBar) mRefreshView
                .findViewById(R.id.pull_to_refresh_progress);
        mRefreshViewLastUpdated = (TextView) mRefreshView
                .findViewById(R.id.pull_to_refresh_updated_at);

        mRefreshViewImage.setMinimumHeight(50);
        mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();

        mRefreshState = TAP_TO_REFRESH;
        //将上述布局文件以及动画效果 增加ListView的头部
        addHeaderView(mRefreshView);

        super.setOnScrollListener(this);

        measureView(mRefreshView);
        mRefreshViewHeight = mRefreshView.getMeasuredHeight();

    }

还有一方面:通过代码控制实现效果的转换

        1.监听手势的变化

View
Code   

/**
 * 重写的一个触摸事件处理
 */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int y = (int) event.getY();
        mBounceHack = false;

        switch (event.getAction()) {

        case MotionEvent.ACTION_UP:
            if (!isVerticalScrollBarEnabled()) {
                setVerticalScrollBarEnabled(true);
            }
            if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
                //拖动距离达到一定距离的时候 (须要刷新)
                if ((mRefreshView.getBottom() >= mRefreshViewHeight || mRefreshView
                        .getTop() >= 0) && mRefreshState == RELEASE_TO_REFRESH) {
                    //将状态设置为 正在刷新
                    mRefreshState = REFRESHING;
                    //准备刷新
                    prepareForRefresh();
                    //刷新
                    onRefresh();
                    //假设取消拖动 或者 拖的距离不够
                } else if (mRefreshView.getBottom() < mRefreshViewHeight
                        || mRefreshView.getTop() <= 0) {
                    //终止刷新
                    resetHeader();
                    setSelection(1);
                }
            }
            break;
        case MotionEvent.ACTION_DOWN:
            //获取按下的y轴的位置
            mLastMotionY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            //计算边距
            applyHeaderPadding(event);
            break;
        }
        return super.onTouchEvent(event);
    }

 2.获取headerView的边距和高度

View
Code

/**
     * 获取headerView的边距
     * @param ev
     */
    private void applyHeaderPadding(MotionEvent ev) {

        int pointerCount = ev.getHistorySize();

        for (int p = 0; p < pointerCount; p++) {
            if (mRefreshState == RELEASE_TO_REFRESH) {
                if (isVerticalFadingEdgeEnabled()) {
                    setVerticalScrollBarEnabled(false);
                }

                int historicalY = (int) ev.getHistoricalY(p);
                //控制下拉的程度 拉动效果
                int topPadding = (int) (((historicalY - mLastMotionY) - mRefreshViewHeight) / 1.7);

                mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
                        topPadding, mRefreshView.getPaddingRight(),
                        mRefreshView.getPaddingBottom());
            }
        }
    }

View
Code

/**
 * 将HeaderView的边距 重置为初始的数值
 */
    private void resetHeaderPadding() {
        mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
                mRefreshOriginalTopPadding, mRefreshView.getPaddingRight(),
                mRefreshView.getPaddingBottom());
    }

View
Code

/**
 * 将整个HeaderView重置为下拉之前的状态
 */
    private void resetHeader() {
        if (mRefreshState != TAP_TO_REFRESH) {
            mRefreshState = TAP_TO_REFRESH;

            resetHeaderPadding();
            //将图片又一次换成箭头
            mRefreshViewImage
                    .setImageResource(R.drawable.arrow);
            //清除动画效果
            mRefreshViewImage.clearAnimation();
            //隐藏图标以及进度条
            mRefreshViewImage.setVisibility(View.GONE);
            mRefreshViewProgress.setVisibility(View.GONE);
        }
    }

View
Code

/**
 * 估算headView的宽和高
 * @param child
 */
    private void measureView(View child) {
        ViewGroup.LayoutParams p = child.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
        int lpHeight = p.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                    MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                    MeasureSpec.UNSPECIFIED);
        }
        child.measure(childWidthSpec, childHeightSpec);
    }

        3.通过手势移动的位置,计算高度。并对滑动过程中效果的变化进行更新操作

View
Code

@Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        //在headerView全然可见的时候 将文字设置为"松开载入..." 同一时候翻转箭头
        if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
                && mRefreshState != REFRESHING) {
            if (firstVisibleItem == 0) {
                mRefreshViewImage.setVisibility(View.VISIBLE);
                if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20 || mRefreshView
                        .getTop() >= 0) && mRefreshState != RELEASE_TO_REFRESH) {
                    mRefreshViewText.setText("松开载入...");
                    mRefreshViewImage.clearAnimation();
                    mRefreshViewImage.startAnimation(mFlipAnimation);
                    mRefreshState = RELEASE_TO_REFRESH;
                } else if (mRefreshView.getBottom() < mRefreshViewHeight + 20
                        && mRefreshState != PULL_TO_REFRESH) {
                    mRefreshViewText.setText("下拉刷新...");
                    if (mRefreshState != TAP_TO_REFRESH) {
                        mRefreshViewImage.clearAnimation();
                        mRefreshViewImage.startAnimation(mReverseFlipAnimation);
                    }
                    mRefreshState = PULL_TO_REFRESH;
                }
            } else {
                mRefreshViewImage.setVisibility(View.GONE);
                resetHeader();
            }
        } else if (mCurrentScrollState == SCROLL_STATE_FLING
                && firstVisibleItem == 0 && mRefreshState != REFRESHING) {
            setSelection(1);
            mBounceHack = true;
        } else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {
            setSelection(1);
        }

        if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(view, firstVisibleItem,
                    visibleItemCount, totalItemCount);
        }

        //
        if (mCurrentScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
            //获取最后一条数据的索引
            lastVisibleIndex = firstVisibleItem + visibleItemCount - 1;
            //当全部的数据 都已经载入出来了(全部的条目数等于最大条目数) 移除掉底部的footerView
            if (totalItemCount == MaxDateNum + 1) {
                removeFooterView(moreView);
                Toast.makeText(context, "数据全部载入完毕。没有很多其它数据。", Toast.LENGTH_LONG)
                        .show();
            }
        }
    }

View
Code

/**
     * 滚动变化监听器
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        mCurrentScrollState = scrollState;

        if (mOnScrollListener != null) {
            mOnScrollListener.onScrollStateChanged(view, scrollState);
        }
        //当滑动究竟部的时候  运行自己主动载入功能
        if (mCurrentScrollState == OnScrollListener.SCROLL_STATE_IDLE
                && lastVisibleIndex == this.getAdapter().getCount()) {
            //、、、、、、、、、 异步载入数据的代码
            pg.setVisibility(View.VISIBLE);
            bt.setVisibility(View.GONE);
        }
    }

        4.定义一系列的调用方法,以便上层代码进行调用。

View
Code

/**
     * 准备刷新
     */
    public void prepareForRefresh() {
        resetHeaderPadding();//恢复HeaderView的边距
        //将图片隐藏
        mRefreshViewImage.setVisibility(View.GONE);
        //将图片置为null
        mRefreshViewImage.setImageDrawable(null);
        mRefreshViewProgress.setVisibility(View.VISIBLE);

        mRefreshViewText.setText("载入中...");

        mRefreshState = REFRESHING;
    }

    public void onRefresh() {

        if (mOnRefreshListener != null) {
            mOnRefreshListener.onRefresh();
        }
    }
    /**
     *  当ListView载入完 能够调用该方法 设置最后更新时间
     * @param lastUpdated
     */
    public void onRefreshComplete(CharSequence lastUpdated) {
        setLastUpdated(lastUpdated);
        onRefreshComplete();
    }
    /**
     *当ListView载入完 能够调用该方法 可是没有设置最后更新时间
     */
    public void onRefreshComplete() {

        resetHeader();

        // 假设refreshview在载入结束后可见,下滑到下一个条目
        if (mRefreshView.getBottom() > 0) {
            invalidateViews();
            setSelection(1);
        }
    }

View
Code

/**
 * 刷新监听器接口
 * @author ChnAdo
 *
 */
    public interface OnRefreshListener {
        /**
         * 须要刷新时调用该方法
         */
        public void onRefresh();
    }

 完整的功能代码例如以下:

View
Code

public class PullToRefreshListView extends ListView implements OnScrollListener {

    private Context context;

    private static final int TAP_TO_REFRESH = 1;
    private static final int PULL_TO_REFRESH = 2;
    private static final int RELEASE_TO_REFRESH = 3;
    private static final int REFRESHING = 4;

    private OnRefreshListener mOnRefreshListener;

    private OnScrollListener mOnScrollListener;
    private LayoutInflater mInflater;

    private RelativeLayout mRefreshView;
    private TextView mRefreshViewText;
    private ImageView mRefreshViewImage;
    private ProgressBar mRefreshViewProgress;
    private TextView mRefreshViewLastUpdated;

    private int mCurrentScrollState;

    private int mRefreshState;

    private RotateAnimation mFlipAnimation;
    private RotateAnimation mReverseFlipAnimation;

    private int mRefreshViewHeight;
    private int mRefreshOriginalTopPadding;
    private int mLastMotionY;

    private boolean mBounceHack;

    private int MaxDateNum;
    private View moreView;
    public Button bt;
    private ProgressBar pg;
    private int lastVisibleIndex;
    public boolean isclick;

    public PullToRefreshListView(Context context) {
        super(context);
        init(context);
        this.context = context;
    }

    public PullToRefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
        this.context = context;
    }

    public PullToRefreshListView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        init(context);
        this.context = context;
    }
/**
 * 初始化控件和箭头动画
 * @param context
 */
    private void init(Context context) {
        mFlipAnimation = new RotateAnimation(0, -180,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        mFlipAnimation.setInterpolator(new LinearInterpolator());
        mFlipAnimation.setDuration(250);
        mFlipAnimation.setFillAfter(true);
        mReverseFlipAnimation = new RotateAnimation(-180, 0,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
        mReverseFlipAnimation.setDuration(250);
        mReverseFlipAnimation.setFillAfter(true);

        mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        mRefreshView = (RelativeLayout) mInflater.inflate(
                R.layout.pull_to_refresh_header, this, false);
        mRefreshViewText = (TextView) mRefreshView
                .findViewById(R.id.pull_to_refresh_text);
        mRefreshViewImage = (ImageView) mRefreshView
                .findViewById(R.id.pull_to_refresh_image);
        mRefreshViewProgress = (ProgressBar) mRefreshView
                .findViewById(R.id.pull_to_refresh_progress);
        mRefreshViewLastUpdated = (TextView) mRefreshView
                .findViewById(R.id.pull_to_refresh_updated_at);

        mRefreshViewImage.setMinimumHeight(50);
        mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();

        mRefreshState = TAP_TO_REFRESH;
        //将上述布局文件以及动画效果 增加ListView的头部
        addHeaderView(mRefreshView);

        super.setOnScrollListener(this);

        measureView(mRefreshView);
        mRefreshViewHeight = mRefreshView.getMeasuredHeight();

        //给ListView载入一个FooterView
        MaxDateNum = 20;
        moreView = LayoutInflater.from(context)
                .inflate(R.layout.moredata, null);
        bt = (Button) moreView.findViewById(R.id.bt_load);
        pg = (ProgressBar) moreView.findViewById(R.id.pg);
        addFooterView(moreView);
        //给底部的button实现一个监听事件
        bt.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                //点这个button 让进度条可见 button自身隐藏
                pg.setVisibility(View.VISIBLE);
                bt.setVisibility(View.GONE);
                isclick = true;
            }
        });
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        setSelection(1);
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(adapter);

        setSelection(1);
    }
    /**
     * 设置一个滚动(滑动)监听器
     */
    @Override
    public void setOnScrollListener(AbsListView.OnScrollListener l) {
        mOnScrollListener = l;
    }
    /**
     * 当ListView的列表须要刷新的时候 又一次回调的一个监听器
     * @param onRefreshListener
     */
    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
        mOnRefreshListener = onRefreshListener;
    }
/**
 * 设置文字标题显示  比如能够显示近期刷新时间等等
 * @param lastUpdated
 */
    public void setLastUpdated(CharSequence lastUpdated) {
        if (lastUpdated != null) {
            mRefreshViewLastUpdated.setVisibility(View.VISIBLE);
            mRefreshViewLastUpdated.setText(lastUpdated);
        } else {
            mRefreshViewLastUpdated.setVisibility(View.GONE);
        }
    }
/**
 * 重写的一个触摸事件处理
 */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int y = (int) event.getY();
        mBounceHack = false;

        switch (event.getAction()) {

        case MotionEvent.ACTION_UP:
            if (!isVerticalScrollBarEnabled()) {
                setVerticalScrollBarEnabled(true);
            }
            if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
                //拖动距离达到一定距离的时候 (须要刷新)
                if ((mRefreshView.getBottom() >= mRefreshViewHeight || mRefreshView
                        .getTop() >= 0) && mRefreshState == RELEASE_TO_REFRESH) {
                    //将状态设置为 正在刷新
                    mRefreshState = REFRESHING;
                    //准备刷新
                    prepareForRefresh();
                    //刷新
                    onRefresh();
                    //假设取消拖动 或者 拖的距离不够
                } else if (mRefreshView.getBottom() < mRefreshViewHeight
                        || mRefreshView.getTop() <= 0) {
                    //终止刷新
                    resetHeader();
                    setSelection(1);
                }
            }
            break;
        case MotionEvent.ACTION_DOWN:
            //获取按下的y轴的位置
            mLastMotionY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            //计算边距
            applyHeaderPadding(event);
            break;
        }
        return super.onTouchEvent(event);
    }
    /**
     * 获取headerView的边距
     * @param ev
     */
    private void applyHeaderPadding(MotionEvent ev) {

        int pointerCount = ev.getHistorySize();

        for (int p = 0; p < pointerCount; p++) {
            if (mRefreshState == RELEASE_TO_REFRESH) {
                if (isVerticalFadingEdgeEnabled()) {
                    setVerticalScrollBarEnabled(false);
                }

                int historicalY = (int) ev.getHistoricalY(p);
                //控制下拉的程度 拉动效果
                int topPadding = (int) (((historicalY - mLastMotionY) - mRefreshViewHeight) / 1.7);

                mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
                        topPadding, mRefreshView.getPaddingRight(),
                        mRefreshView.getPaddingBottom());
            }
        }
    }
/**
 * 将HeaderView的边距 重置为初始的数值
 */
    private void resetHeaderPadding() {
        mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
                mRefreshOriginalTopPadding, mRefreshView.getPaddingRight(),
                mRefreshView.getPaddingBottom());
    }
/**
 * 将整个HeaderView重置为下拉之前的状态
 */
    private void resetHeader() {
        if (mRefreshState != TAP_TO_REFRESH) {
            mRefreshState = TAP_TO_REFRESH;

            resetHeaderPadding();
            //将图片又一次换成箭头
            mRefreshViewImage
                    .setImageResource(R.drawable.arrow);
            //清除动画效果
            mRefreshViewImage.clearAnimation();
            //隐藏图标以及进度条
            mRefreshViewImage.setVisibility(View.GONE);
            mRefreshViewProgress.setVisibility(View.GONE);
        }
    }
/**
 * 估算headView的宽和高
 * @param child
 */
    private void measureView(View child) {
        ViewGroup.LayoutParams p = child.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
        int lpHeight = p.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                    MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                    MeasureSpec.UNSPECIFIED);
        }
        child.measure(childWidthSpec, childHeightSpec);
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        //在headerView全然可见的时候 将文字设置为"松开载入..." 同一时候翻转箭头
        if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
                && mRefreshState != REFRESHING) {
            if (firstVisibleItem == 0) {
                mRefreshViewImage.setVisibility(View.VISIBLE);
                if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20 || mRefreshView
                        .getTop() >= 0) && mRefreshState != RELEASE_TO_REFRESH) {
                    mRefreshViewText.setText("松开载入...");
                    mRefreshViewImage.clearAnimation();
                    mRefreshViewImage.startAnimation(mFlipAnimation);
                    mRefreshState = RELEASE_TO_REFRESH;
                } else if (mRefreshView.getBottom() < mRefreshViewHeight + 20
                        && mRefreshState != PULL_TO_REFRESH) {
                    mRefreshViewText.setText("下拉刷新...");
                    if (mRefreshState != TAP_TO_REFRESH) {
                        mRefreshViewImage.clearAnimation();
                        mRefreshViewImage.startAnimation(mReverseFlipAnimation);
                    }
                    mRefreshState = PULL_TO_REFRESH;
                }
            } else {
                mRefreshViewImage.setVisibility(View.GONE);
                resetHeader();
            }
        } else if (mCurrentScrollState == SCROLL_STATE_FLING
                && firstVisibleItem == 0 && mRefreshState != REFRESHING) {
            setSelection(1);
            mBounceHack = true;
        } else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {
            setSelection(1);
        }

        if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(view, firstVisibleItem,
                    visibleItemCount, totalItemCount);
        }

        //
        if (mCurrentScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
            //获取最后一条数据的索引
            lastVisibleIndex = firstVisibleItem + visibleItemCount - 1;
            //当全部的数据 都已经载入出来了(全部的条目数等于最大条目数) 移除掉底部的footerView
            if (totalItemCount == MaxDateNum + 1) {
                removeFooterView(moreView);
                Toast.makeText(context, "数据全部载入完毕,没有很多其它数据!

", Toast.LENGTH_LONG)
                        .show();
            }
        }
    }

    /**
     * 滚动变化监听器
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        mCurrentScrollState = scrollState;

        if (mOnScrollListener != null) {
            mOnScrollListener.onScrollStateChanged(view, scrollState);
        }
        //当滑动究竟部的时候  运行自己主动载入功能
        if (mCurrentScrollState == OnScrollListener.SCROLL_STATE_IDLE
                && lastVisibleIndex == this.getAdapter().getCount()) {
            //、、、、、、、、、 异步载入数据的代码
            pg.setVisibility(View.VISIBLE);
            bt.setVisibility(View.GONE);
        }
    }
    /**
     * 准备刷新
     */
    public void prepareForRefresh() {
        resetHeaderPadding();//恢复HeaderView的边距
        //将图片隐藏
        mRefreshViewImage.setVisibility(View.GONE);
        //将图片置为null
        mRefreshViewImage.setImageDrawable(null);
        mRefreshViewProgress.setVisibility(View.VISIBLE);

        mRefreshViewText.setText("载入中...");

        mRefreshState = REFRESHING;
    }

    public void onRefresh() {

        if (mOnRefreshListener != null) {
            mOnRefreshListener.onRefresh();
        }
    }
    /**
     *  当ListView载入完 能够调用该方法 设置最后更新时间
     * @param lastUpdated
     */
    public void onRefreshComplete(CharSequence lastUpdated) {
        setLastUpdated(lastUpdated);
        onRefreshComplete();
    }
    /**
     *当ListView载入完 能够调用该方法 可是没有设置最后更新时间
     */
    public void onRefreshComplete() {

        resetHeader();

        // 假设refreshview在载入结束后可见,下滑到下一个条目
        if (mRefreshView.getBottom() > 0) {
            invalidateViews();
            setSelection(1);
        }
    }
/**
 * 刷新监听器接口
 * @author ChnAdo
 *
 */
    public interface OnRefreshListener {
        /**
         * 须要刷新时调用该方法
         */
        public void onRefresh();
    }

    /**
     * onMoreComplete() 刷新
     */
    public void onMoreComplete() {
        bt.setVisibility(View.GONE);//设置button消失
        pg.setVisibility(View.VISIBLE);//设置载入滚动栏显示
        isclick = false;
    }
    //结束进度条
    public void dismissProgress() {
        bt.setVisibility(View.GONE);
        pg.setVisibility(View.INVISIBLE);

    }
}

         在使用该自己定义listView的Activity中,要实现两个方法。

View
Code

/**
         * 实现blogslist上滑载入
         */
        blogslist.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
                        && !blogslist.isclick
                        && visiableItemCount == visiableLastIndex) {
                    addArrayList();
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {
                AllBlogsActivity.visiableItemCount = totalItemCount;
                visiableLastIndex = firstVisibleItem + visibleItemCount;
            }
        });

至此,我们下拉刷新的样例就完毕了。事实上,非常多代码,每个程序猿都会的就是学习,利用和改造。

可是,在学习他们的成果的时候,自己是否也在想,我为什么没有这种想法。看起来如此之简单,实现起来如此之easy,使用起来如此之爽。偏偏,我们总是后知后觉。

没错,思考。

。。

在生活中,我们不缺乏的就是经验。可是将经验之谈转换通过思考转化成简单的逻辑。在由简单的逻辑去创造出懒人们的产品,是的,

这句话没错:懒人创造世界。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-11-26 05:27:31

【原版的:参赛作品】窥秘懒---android打开下拉手势刷新时代的相关文章

Xamarin. Android实现下拉刷新功能

下拉刷新功能在安卓和iOS中非常常见,一般实现这样的功能都是直接使用第三方的库,网上能找到很多这样的开源库.然而在Xamarin. Android中要实现一个好用的下拉刷新功能却不是很容易,在网上找了几个Xamarin.Android的下拉刷新控件,都不是很满意,所以想重新绑定一个java写的下拉刷新控件.在网上找了几个这样的开源库,通过对比发现android-pull-to-refresh实现的功能比较多,实现的效果也比较满意. Android-Pull-To-Refresh项目地址:http

android学习---下拉刷新组建

Google官方的下拉刷新组建 activity代码实现: /** * The SwipeRefreshLayout should be used whenever the user * can refresh the contents of a view via a vertical swipe gesture. * */public class MainActivity extends Activity implements SwipeRefreshLayout.OnRefreshListe

Android实现下拉导航选择菜单效果【转载地址:http://www.cnblogs.com/hanyonglu/archive/2012/07/31/2617488.html】

本文介绍在Android中如何实现下拉导航选择菜单效果.   关于下拉导航选择菜单效果在新闻客户端中用的比较多,当然也可以用在其他的项目中,这样可以很方便的选择更多的菜单.我们可以让我们的应用顶部有左右滑动或进行切换的导航菜单,也可以为了增强用户体验在应用中添加这样的下拉导航选择菜单效果. 关于它的实现原理,其实也是挺简单的,就是使用PopupWindow来进行展现,在显示时控制其高度并配置以相应的动画效果.在PopupWindow中我使用GridView来控制里面的菜单项,每个菜单项对应相应的

Android ActionBar下拉选项

package com.example.actionBarTest.actionBarList; import android.app.ActionBar; import android.app.Activity; import android.app.Fragment; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.SpinnerAdapter; import com.ex

Android PullToRefresh 下拉刷新,上拉更多,支持ScrollView,ListView,可方便拓展GridView,WebView等

在写着东西之前,从网上找到很多这方面的源码,但是基本没有找到满意的,包括在GitHub上的比较有名的Android-PullToRefresh-master,思来想去还是自己写吧,当然其中借鉴了一些别的开源代码! 废话不多说,直接上代码,注释很全乎,应该不难理解,Demo下载地址在最后: package com.zs.pulltorefreshtest; import android.content.Context; import android.util.AttributeSet; impor

Android 之 下拉框(Spinner)的使用-转

下拉列表 Spinner. Spinner的使用,可以极大提高用户的体验性.当需要用户选择的时候,可以提供一个下拉列表将所有可选的项列出来.供用户选择. Demo如下,可以留作参考 一.使用数组作为数据源 1.新建一个android的工程 2.工程的layout.xml文件如下:声明一个TextView控件和一个Spinner控件 Xml代码   <?xml version="1.0" encoding="utf-8"?> <LinearLayou

Android做下拉刷新的时候,在做些什么

1. 简介 好长时间没有写博客了,一来是工作忙,抽不出空,二来是迷上了王者荣耀.现在正好赶上项目空闲期,写一篇关于下拉刷新的文章,个人觉得上来加载更多功能使用场景非常少,而且没有必要做的那么麻烦,文章最后会提一下加载更多的实现. 最近项目中遇见了下拉刷新的需求,正好研究了一下,分享一下自己的心得. 主要参考文章或工程: 郭霖大神-Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 自个儿写Android的下拉刷新/上拉加载控件 XListView 这三篇文章各自提供了实现下拉刷新的思

Android ListView下拉/上拉刷新:设计原理与实现

 <Android ListView下拉/上拉刷新:设计原理与实现> Android上ListView的第三方开源的下拉刷新框架很多,应用场景很多很普遍,几乎成为现在APP的通用设计典范,甚至谷歌官方都索性在Android SDK层面支持下拉刷新,我之前写了一篇文章<Android SwipeRefreshLayout:谷歌官方SDK包中的下拉刷新>专门介绍过(链接地址:http://blog.csdn.net/zhangphil/article/details/4696537

Android实现下拉导航选择菜单效果(转)

本文转载自互联网 关于下拉导航选择菜单效果在新闻客户端中用的比较多,当然也可以用在其他的项目中,这样可以很方便的选择更多的菜单.我们可以让我们的应用顶部有左右滑动或进行切换的导航菜单,也可以为了增强用户体验在应用中添加这样的下拉导航选择菜单效果. 关于它的实现原理,其实也是挺简单的,就是使用PopupWindow来进行展现,在显示时控制其高度并配置以相应的动画效果.在PopupWindow中我使用GridView来控制里面的菜单项,每个菜单项对应相应的图片和文字.当然了,也有其他的实现方式.为了