Android自定义View-下拉刷新控件

Android自定义View-下拉刷新控件

   下拉刷新是android开发过程中很常见的功能,github上面有许多下拉刷新的开源控件可以使用。但有时候这些开源控件不能完全符合我们的项目要求,这时就需要自己进行修改,这时候我们就需要了解下拉刷新的原理,才能自由的修改它的功能,因此我自己写了一个简单的下拉刷新控件,以了解其原理

下拉刷新原理

下拉刷新控件主要由两部分组成,内容部分,与下拉头部分

其主要流程:初始时将下拉头的位置设置到屏幕上方,当内容部分滑动到顶部时则根据手指滑动的y轴方向参数,将下拉头滑动到屏幕中,当滑动距离达到某一个值时则可以刷新,这时手指松开,则进入刷新状态,刷新完成之后回到初始位置

初始化状态

实现

大概原理已经清楚了,那我们就要开始实现一个下拉刷新控件了
先写一个下拉头的布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <ImageView
        android:id="@+id/arrow"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_centerInParent="true"
        android:src="@mipmap/arrow"
        />

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="30dip"
        android:layout_height="30dip"
        android:layout_centerInParent="true"
        android:visibility="gone"
        />

</RelativeLayout>

然后新建一个RefreshableView的类,这个继承自LinearLayout,之所以继承LinearLayout是因为我们的下拉刷新控件是一个线性布局的ViewGroup

添加下拉头布局

public RefreshableView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //添加下拉头布局
        header = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh , null , true);
        arrow = (ImageView)header.findViewById(R.id.arrow);
        progressBar = (ProgressBar)header.findViewById(R.id.progress_bar);
        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        setOrientation(VERTICAL);
        addView(header , 0);

        mScroller = new Scroller(context);
}

在Layout时设置下拉头的初始位置

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if(changed && !loadOnce){
            //下拉头向上偏移,隐藏下拉头
            hideHeaderHeight = -header.getHeight();
            ((MarginLayoutParams)header.getLayoutParams()).topMargin = hideHeaderHeight;
            //设置ListView的触摸事件
            listView = (ListView)getChildAt(1);
            listView.setOnTouchListener(this);
            loadOnce = true;
        }
}

ListView滑动监听的onTouch方法

 @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        //判断ListView是否滑动到顶部
        isAbleToPull(motionEvent);
        if(ableToPull){
            switch (motionEvent.getAction()){
                case MotionEvent.ACTION_DOWN:
                    yDown = motionEvent.getRawY();
                    mLastY = motionEvent.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    //手指上滑不处理事件
                    if(motionEvent.getRawY() - yDown < 0 && getScrollY() == 0){
                        return false;
                    }
                    //小于最小滑动距离
                    if(motionEvent.getRawY() - yDown < touchSlop){
                        return false;
                    }
                    //手指是下滑并且下拉头完全隐藏执行下滑
                    if(motionEvent.getRawY() - yDown > 0 && getScrollY() >= 0){
                        //滑动距离大于下拉头高度松手可刷新
                        if(getScrollY() < hideHeaderHeight){
                            currentStatus = STATUS_RELEASE_TO_REFRESH;
                        }else{
                            currentStatus = STATUS_PULL_TO_REFRESH;
                        }
                        scrollBy(0 , -(int)(motionEvent.getRawY() - mLastY));
                    }
                    mLastY = motionEvent.getRawY();
                    break;
                case MotionEvent.ACTION_UP:
                default:
                    if(currentStatus == STATUS_PULL_TO_REFRESH){
                        //隐藏下拉头
                        smoothScrollBy( 0 , -getScrollY());
                    }
                    if(currentStatus == STATUS_RELEASE_TO_REFRESH){
                        //刷新
                        onRefresh();
                    }
                    break;
            }
            // 时刻记得更新下拉头中的信息
            if (currentStatus == STATUS_PULL_TO_REFRESH
                    || currentStatus == STATUS_RELEASE_TO_REFRESH) {
                updateHeaderView();
                // 当前正处于下拉或释放状态,要让ListView失去焦点,否则被点击的那一项会一直处于选中状态
                listView.setPressed(false);
                listView.setFocusable(false);
                listView.setFocusableInTouchMode(false);
                lastStatus = currentStatus;
                // 当前正处于下拉或释放状态,通过返回true屏蔽掉ListView的滚动事件
                return true;
            }
        }
        return false;
    }

isAbleToPull方法判断ListView是否滑动到顶部

public void isAbleToPull(MotionEvent motionEvent){
        View fristChiler = listView.getChildAt(0);
        if(fristChiler != null) {
            //如果ListView滑动到顶部可以下拉下拉头
            if (listView.getFirstVisiblePosition() == 0 && fristChiler.getTop() == 0){
                if(!ableToPull) {
                    yDown = motionEvent.getRawY();
                }
                ableToPull = true;
            }else{
                ableToPull = false;
            }
        }else{
            //ListView为空,也可以下拉下拉头
            ableToPull = true;
        }
}

rotateArrow方法根据当前状态旋转箭头

private void rotateArrow() {
        float pivotX = arrow.getWidth() / 2f;
        float pivotY = arrow.getHeight() / 2f;
        float fromDegrees = 0f;
        float toDegrees = 0f;
        if (currentStatus == STATUS_PULL_TO_REFRESH) {
            fromDegrees = 180f;
            toDegrees = 360f;
        } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
            fromDegrees = 0f;
            toDegrees = 180f;
        }
        RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
        animation.setDuration(100);
        animation.setFillAfter(true);
        arrow.startAnimation(animation);
    }

updateHeaderView根据当前状态更新下拉头的信息

private void updateHeaderView() {
        if (lastStatus != currentStatus) {
            if (currentStatus == STATUS_PULL_TO_REFRESH) {
                arrow.setVisibility(View.VISIBLE);
                progressBar.setVisibility(View.GONE);
                rotateArrow();
            } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
                arrow.setVisibility(View.VISIBLE);
                progressBar.setVisibility(View.GONE);
                rotateArrow();
            } else if (currentStatus == STATUS_REFRESHING) {
                progressBar.setVisibility(View.VISIBLE);
                arrow.clearAnimation();
                arrow.setVisibility(View.GONE);
            }
        }
    }

这段代码实现平滑滑动

    public void smoothScrollBy(int dx , int dy){
        mScroller.startScroll( 0 , getScrollY() , 0 , dy , SCROLL_TIME);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX() , mScroller.getCurrY());
            postInvalidate();
        }
    }

定义一个下拉刷新监听借口

 public interface PullToRefreshListener {

        /**
         * 刷新时会去回调此方法,在方法内编写具体的刷新逻辑。注意此方法是在子线程中调用的, 你可以不必另开线程来进行耗时操作。
         */
        void onRefresh();

    }

刷新完成方法,设置刷新事件监听器,刷新方法

 //刷新结束后调用的方法
    public void finishRefresh(){
        currentStatus = STATUS_REFRESH_FINISHED;
        smoothScrollBy( 0 , -getScrollY());
    }
    //设置刷新事件监听
    public void setRefreshListener(PullToRefreshListener l){
        this.mListener = l;
    }
    //刷新方法
    public void onRefresh(){
        smoothScrollBy( 0 , (-getScrollY()) + hideHeaderHeight);
        if(mListener != null){
            mListener.onRefresh();
        }
    }

控件已经写完了,接下来我们要看一下实现效果如果
在activity布局文件中加入如下布局

<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=".MainActivity" >

    <com.example.pulltorefreshtest.RefreshableView
        android:id="@+id/refreshable_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >

        <ListView
            android:id="@+id/list_view"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scrollbars="none" >
        </ListView>
    </com.example.pulltorefreshtest.RefreshableView>

</RelativeLayout>

在MainActivity中设置下拉刷新事件

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        refreshableView = (RefreshableView) findViewById(R.id.refreshable_view);
        listView = (ListView) findViewById(R.id.list_view);
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
        listView.setAdapter(adapter);
        refreshableView.setRefreshListener(new RefreshableView.PullToRefreshListener() {
            @Override
            public void onRefresh() {
                refreshableView.finishRefresh();
            }
        });
    }

源码地址:https://github.com/GameT/PullToRefreshDemo

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值