`
xwangly
  • 浏览: 132266 次
  • 性别: Icon_minigender_1
  • 来自: 鄂州
社区版块
存档分类
最新评论

方便实用的下拉刷新控件,支持ScrollView、AbsListView

阅读更多

最近要做一个下拉刷新的功能,网上找了很多例子,也看了一些开源的下拉刷新项目,但是小例子比较简单,效果和稳定性都差强人意,而开源的项目又太庞大,看起来耗时费劲,所以只好综合一下各处的代码掌握其原理,自己实现一套下拉刷新功能。

 

该控件特点:

1.子控件必须是一个ScrollView或ListView;

2.支持自定义下拉布局;

3.自定义下拉布局可以不用处理下拉的各种状态(只需要实现几个接口即可),也可以自己处理各种下拉的状态。

 

先来看看效果图:



 



 



 

上代码:

首先看如何使用:

1.使用的布局:

 <LinearLayout 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"
    android:orientation="vertical" >

    <com.example.pulldown.PullDownScrollView
        android:id="@+id/refresh_root"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:background="#161616"
        android:orientation="vertical" >

        <ScrollView
            android:id="@+id/scrollview"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:scrollbars="none" >

            <LinearLayout
                android:id="@+id/mainView"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:background="#1f1f1f"
                android:orientation="vertical" >

                <!-- 自已的布局 -->

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dip"
                    android:gravity="center"
                    android:text="@string/hello_world"
                    android:textColor="@android:color/white"
                    android:textSize="18sp" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dip"
                    android:gravity="center"
                    android:text="@string/hello_world"
                    android:textColor="@android:color/white"
                    android:textSize="18sp" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dip"
                    android:gravity="center"
                    android:text="@string/hello_world"
                    android:textColor="@android:color/white"
                    android:textSize="18sp" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dip"
                    android:gravity="center"
                    android:text="@string/hello_world"
                    android:textColor="@android:color/white"
                    android:textSize="18sp" />
            </LinearLayout>
        </ScrollView>
    </com.example.pulldown.PullDownScrollView>

</LinearLayout>

 2.UI使用:

   首先,Activity实现接口:

implements RefreshListener

 部分代码如下:

  package com.example.pulldown;

import com.example.pulldown.PullDownScrollView.RefreshListener;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity implements RefreshListener{
    private PullDownScrollView mPullDownScrollView; 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mPullDownScrollView = (PullDownScrollView) findViewById(R.id.refresh_root);  
        mPullDownScrollView.setRefreshListener(this);  
        mPullDownScrollView.setPullDownElastic(new PullDownElasticImp(this));  
    }
    @Override  
    public void onRefresh(PullDownScrollView view) {  
        new Handler().postDelayed(new Runnable() {  
              
            @Override  
            public void run() {  
                // TODO Auto-generated method stub  
                mPullDownScrollView.finishRefresh("上次刷新时间:12:23");  
            }  
        }, 2000);  
    }  
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

}
 

3.再来看看控件代码:

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ScrollView;


/**
 * @author xwangly@163.com
 * @date 2013-7-9
 * 
 */
public class PullDownScrollView extends LinearLayout {

    private static final String TAG = "PullDownScrollView";

    private int refreshTargetTop = -60;
    private int headContentHeight;

    private RefreshListener refreshListener;

    private RotateAnimation animation;
    private RotateAnimation reverseAnimation;
    
    private final static int RATIO = 2;
    private int preY = 0;
    private boolean isElastic = false;
    private int startY;
    private int state;
    
    private String note_release_to_refresh = "松开更新";
    private String note_pull_to_refresh = "下拉刷新";
    private String note_refreshing = "正在更新...";
    
    private IPullDownElastic mElastic;
    

    public PullDownScrollView(Context context) {
        super(context);
        init();

    }

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

    private void init() {
        animation = new RotateAnimation(0, -180,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        animation.setInterpolator(new LinearInterpolator());
        animation.setDuration(250);
        animation.setFillAfter(true);

        reverseAnimation = new RotateAnimation(-180, 0,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        reverseAnimation.setInterpolator(new LinearInterpolator());
        reverseAnimation.setDuration(200);
        reverseAnimation.setFillAfter(true);
    }
    /**
     * 刷新监听
     * @param listener
     */
    public void setRefreshListener(RefreshListener listener) {
        this.refreshListener = listener;
    }
    /**
     * 下拉布局
     * @param elastic
     */
    public void setPullDownElastic(IPullDownElastic elastic) {
        mElastic = elastic;
        
        headContentHeight = mElastic.getElasticHeight();
        refreshTargetTop = - headContentHeight;
        LayoutParams lp = new LinearLayout.LayoutParams(
                LayoutParams.FILL_PARENT, headContentHeight);
        lp.topMargin = refreshTargetTop;
        addView(mElastic.getElasticLayout(), 0, lp);
    }
    
    /**
     * 设置更新提示语
     * @param pullToRefresh 下拉刷新提示语
     * @param releaseToRefresh 松开刷新提示语
     * @param refreshing 正在刷新提示语
     */
    public void setRefreshTips(String pullToRefresh, String releaseToRefresh, String refreshing) {
        note_pull_to_refresh = pullToRefresh;
        note_release_to_refresh = releaseToRefresh;
        note_refreshing = refreshing;
    }
    /*
     * 该方法一般和ontouchEvent 一起用 (non-Javadoc)
     * 
     * @see
     * android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Logger.d(TAG, "onInterceptTouchEvent");
        printMotionEvent(ev);
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            preY = (int) ev.getY();
        }
        if (ev.getAction() == MotionEvent.ACTION_MOVE) {

            Logger.d(TAG, "isElastic:" + isElastic + " canScroll:"+ canScroll() + " ev.getY() - preY:"+(ev.getY() - preY));
            if (!isElastic && canScroll()
                    && (int) ev.getY() - preY >= headContentHeight / (3*RATIO)
                    && refreshListener != null && mElastic != null) {

                isElastic = true;
                startY = (int) ev.getY();
                Logger.i(TAG, "在move时候记录下位置startY:" + startY);
                return true;
            }

        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Logger.d(TAG, "onTouchEvent");
        printMotionEvent(event);
        handleHeadElastic(event);
        return super.onTouchEvent(event);
    }

    private void handleHeadElastic(MotionEvent event) {
        if (refreshListener != null && mElastic != null) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Logger.i(TAG, "down");
                break;
            case MotionEvent.ACTION_UP:

                Logger.i(TAG, "up");
                if (state != IPullDownElastic.REFRESHING && isElastic) {
                    
                    if (state == IPullDownElastic.DONE) {
                        // 什么都不做
                        setMargin(refreshTargetTop);
                    }
                    if (state == IPullDownElastic.PULL_To_REFRESH) {
                        state = IPullDownElastic.DONE;
                        setMargin(refreshTargetTop);
                        changeHeaderViewByState(state, false);
                        Logger.i(TAG, "由下拉刷新状态,到done状态");
                    }
                    if (state == IPullDownElastic.RELEASE_To_REFRESH) {
                        state = IPullDownElastic.REFRESHING;
                        setMargin(0);
                        changeHeaderViewByState(state, false);
                        onRefresh();
                        Logger.i(TAG, "由松开刷新状态,到done状态");
                    }

                }
                isElastic = false;
                break;
            case MotionEvent.ACTION_MOVE:
                Logger.i(TAG, "move");
                int tempY = (int) event.getY();
                
                if (state != IPullDownElastic.REFRESHING && isElastic) {
                    // 可以松手去刷新了
                    if (state == IPullDownElastic.RELEASE_To_REFRESH) {
                        if (((tempY - startY) / RATIO < headContentHeight)
                                && (tempY - startY) > 0) {
                            state = IPullDownElastic.PULL_To_REFRESH;
                            changeHeaderViewByState(state, true);
                            Logger.i(TAG, "由松开刷新状态转变到下拉刷新状态");
                        } else if (tempY - startY <= 0) {
                            state = IPullDownElastic.DONE;
                            changeHeaderViewByState(state, false);
                            Logger.i(TAG, "由松开刷新状态转变到done状态");
                        }
                    }
                    if (state == IPullDownElastic.DONE) {
                        if (tempY - startY > 0) {
                            state = IPullDownElastic.PULL_To_REFRESH;
                            changeHeaderViewByState(state, false);
                        }
                    }
                    if (state == IPullDownElastic.PULL_To_REFRESH) {
                        // 下拉到可以进入RELEASE_TO_REFRESH的状态
                        if ((tempY - startY) / RATIO >= headContentHeight) {
                            state = IPullDownElastic.RELEASE_To_REFRESH;
                            changeHeaderViewByState(state, false);
                            Logger.i(TAG, "由done或者下拉刷新状态转变到松开刷新");
                        } else if (tempY - startY <= 0) {
                            state = IPullDownElastic.DONE;
                            changeHeaderViewByState(state, false);
                            Logger.i(TAG, "由DOne或者下拉刷新状态转变到done状态");
                        }
                    }
                    if (tempY - startY > 0) {
                        setMargin((tempY - startY)/2 + refreshTargetTop);
                    }
                }
                break;
            }
        }
    }
    
    /**
     * 
     */
    private void setMargin(int top) {
        LinearLayout.LayoutParams lp = (LayoutParams) mElastic.getElasticLayout()
                .getLayoutParams();
        lp.topMargin = top;
        // 修改后刷新
        mElastic.getElasticLayout().setLayoutParams(lp);
        mElastic.getElasticLayout().invalidate();
    }

    private void changeHeaderViewByState(int state, boolean isBack) {

        mElastic.changeElasticState(state, isBack);
        switch (state) {
        case IPullDownElastic.RELEASE_To_REFRESH:
            mElastic.showArrow(View.VISIBLE);
            mElastic.showProgressBar(View.GONE);
            mElastic.showLastUpdate(View.VISIBLE);
            mElastic.setTips(note_release_to_refresh);

            mElastic.clearAnimation();
            mElastic.startAnimation(animation);
            Logger.i(TAG, "当前状态,松开刷新");
            break;
        case IPullDownElastic.PULL_To_REFRESH:
            mElastic.showArrow(View.VISIBLE);
            mElastic.showProgressBar(View.GONE);
            mElastic.showLastUpdate(View.VISIBLE);
            mElastic.setTips(note_pull_to_refresh);

            mElastic.clearAnimation();

            // 是由RELEASE_To_REFRESH状态转变来的
            if (isBack) {
                mElastic.startAnimation(reverseAnimation);
            }
            Logger.i(TAG, "当前状态,下拉刷新");
            break;
        case IPullDownElastic.REFRESHING:
            mElastic.showArrow(View.GONE);
            mElastic.showProgressBar(View.VISIBLE);
            mElastic.showLastUpdate(View.GONE);
            mElastic.setTips(note_refreshing);

            mElastic.clearAnimation();
            Logger.i(TAG, "当前状态,正在刷新...");
            break;
        case IPullDownElastic.DONE:
            mElastic.showProgressBar(View.GONE);
            mElastic.clearAnimation();
//            arrowImageView.setImageResource(R.drawable.goicon);
            // tipsTextview.setText("下拉刷新");
            // lastUpdatedTextView.setVisibility(View.VISIBLE);
            Logger.i(TAG, "当前状态,done");
            break;
        }
    }

    private void onRefresh() {
        // downTextView.setVisibility(View.GONE);
//        scroller.startScroll(0, i, 0, 0 - i);
//        invalidate();
        if (refreshListener != null) {
            refreshListener.onRefresh(this);
        }
    }

    /**
     * 
     */
    @Override
    public void computeScroll() {
//        if (scroller.computeScrollOffset()) {
//            int i = this.scroller.getCurrY();
//            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) this.refreshView
//                    .getLayoutParams();
//            int k = Math.max(i, refreshTargetTop);
//            lp.topMargin = k;
//            this.refreshView.setLayoutParams(lp);
//            this.refreshView.invalidate();
//            invalidate();
//        }
    }

    /**
     * 结束刷新事件,UI刷新完成后必须回调此方法
     * @param text 一般传入:“上次更新时间:12:23”
     */
    public void finishRefresh(String text) {
        if (mElastic == null) {
            Logger.d(TAG, "finishRefresh mElastic:" + mElastic);
            return;
        }
        state = IPullDownElastic.DONE;
        mElastic.setLastUpdateText(text);
        changeHeaderViewByState(state,false);
        Logger.i(TAG, "执行了=====finishRefresh");

        mElastic.showArrow(View.VISIBLE);
        mElastic.showLastUpdate(View.VISIBLE);
        setMargin(refreshTargetTop);
//        scroller.startScroll(0, i, 0, refreshTargetTop);
//        invalidate();
    }

    private boolean canScroll() {
        View childView;
        if (getChildCount() > 1) {
            childView = this.getChildAt(1);
            if (childView instanceof AbsListView) {
                int top = ((AbsListView) childView).getChildAt(0).getTop();
                int pad = ((AbsListView) childView).getListPaddingTop();
                if ((Math.abs(top - pad)) < 3
                        && ((AbsListView) childView).getFirstVisiblePosition() == 0) {
                    return true;
                } else {
                    return false;
                }
            } else if (childView instanceof ScrollView) {
                if (((ScrollView) childView).getScrollY() == 0) {
                    return true;
                } else {
                    return false;
                }
            }

        }
        return canScroll(this);
    }
    
    /**
     * 子类重写此方法可以兼容其它的子控件,目前只兼容AbsListView和ScrollView
     * @param view
     * @return
     */
    public boolean canScroll(PullDownScrollView view) {
        return false;
    }

    private void printMotionEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Logger.d(TAG, "down");
            break;
        case MotionEvent.ACTION_MOVE:
            Logger.d(TAG, "move");
            break;
        case MotionEvent.ACTION_UP:
            Logger.d(TAG, "up");
        default:
            break;
        }
    }
    /**
     * 刷新监听接口
     */
    public interface RefreshListener {
        public void onRefresh(PullDownScrollView view);
    }

}

 

4.接口:

import android.view.View;
import android.view.animation.Animation;

/**
 * @author xwangly@163.com
 * @date 2013-7-10
 * 下拉控件接口
 */
public interface IPullDownElastic {
    public final static int RELEASE_To_REFRESH = 0;
    public final static int PULL_To_REFRESH = 1;
    public final static int REFRESHING = 2;
    public final static int DONE = 3;

    public View getElasticLayout();

    public int getElasticHeight();

    public void showArrow(int visibility);

    public void startAnimation(Animation animation);

    public void clearAnimation();

    public void showProgressBar(int visibility);

    public void setTips(String tips);

    public void showLastUpdate(int visibility);

    public void setLastUpdateText(String text);
    
    /**
     * 可以不用实现此方法,PullDownScrollView会处理ElasticLayout布局中的状态 
     * 如果需要特殊处理,可以实现此方法进行处理
     * 
     * @param state  @see RELEASE_To_REFRESH
     * @param isBack 是否是松开回退
     */
    public void changeElasticState(int state, boolean isBack);

}

 5.默认实现:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;


/**
 * @author xwangly@163.com
 * @date   2013-7-10
 * 默认下拉控件布局实现
 */
public class PullDownElasticImp implements IPullDownElastic {
    private View refreshView;
    private ImageView arrowImageView;
    private int headContentHeight;
    private ProgressBar progressBar;
    private TextView tipsTextview;
    private TextView lastUpdatedTextView;
    
    private Context mContext;
    public PullDownElasticImp(Context context) {
        mContext = context;
        init();
    }
    

    private void init() {
        // 刷新视图顶端的的view
        refreshView = LayoutInflater.from(mContext).inflate(
                R.layout.refresh_top_item, null);

        // 指示器view
        arrowImageView = (ImageView) refreshView
                .findViewById(R.id.head_arrowImageView);
        // 刷新bar
        progressBar = (ProgressBar) refreshView
                .findViewById(R.id.head_progressBar);
        // 下拉显示text
        tipsTextview = (TextView) refreshView.findViewById(R.id.refresh_hint);
        // 下来显示时间
        lastUpdatedTextView = (TextView) refreshView
                .findViewById(R.id.refresh_time);

        headContentHeight = Utils.dip2px(mContext, 50);
    }

    /**
     * @return
     * 
     */
    @Override
    public View getElasticLayout() {
        return refreshView;
    }

    /**
     * @return
     * 
     */
    @Override
    public int getElasticHeight() {
        return headContentHeight;
    }

    /**
     * @param show
     * 
     */
    @Override
    public void showArrow(int visibility) {
        arrowImageView.setVisibility(visibility);
    }

    /**
     * @param animation
     * 
     */
    @Override
    public void startAnimation(Animation animation) {
        arrowImageView.startAnimation(animation);
    }

    /**
     * 
     * 
     */
    @Override
    public void clearAnimation() {
        arrowImageView.clearAnimation();
    }

    /**
     * @param show
     * 
     */
    @Override
    public void showProgressBar(int visibility) {
        progressBar.setVisibility(visibility);
    }

    /**
     * @param tips
     * 
     */
    @Override
    public void setTips(String tips) {
        tipsTextview.setText(tips);
    }

    /**
     * @param show
     * 
     */
    @Override
    public void showLastUpdate(int visibility) {
        lastUpdatedTextView.setVisibility(visibility);
    }

    /**
     * @param text
     * 
     */
    public void setLastUpdateText(String text) {
        lastUpdatedTextView.setText(text);
    }


    /**
     * @param state
     * @param isBack
     * 
     */
    @Override
    public void changeElasticState(int state, boolean isBack) {
        // TODO Auto-generated method stub
        
    }

}

 

6.默认实现的布局:

<?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="-50.0dip"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0.0dip"
        android:layout_weight="1.0"
        android:gravity="center"
        android:orientation="horizontal" >

        <!-- 箭头图像、进度条 -->

        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="30dip" >

            <!-- 箭头 -->

            <ImageView
                android:id="@+id/head_arrowImageView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@drawable/goicon" />

            <!-- 进度条 -->

            <ProgressBar
                android:id="@+id/head_progressBar"
                style="@android:style/Widget.ProgressBar.Small.Inverse"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:visibility="gone" />
        </FrameLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="center"
            android:orientation="vertical" >

            <!-- 提示 -->

            <TextView
                android:id="@+id/refresh_hint"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉刷新"
                android:textColor="#f2f2f2"
                android:textSize="16sp" />

            <!-- 最近更新 -->

            <TextView
                android:id="@+id/refresh_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="上次更新"
                android:textColor="#b89766"
                android:textSize="10sp" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>

 6.图片资源:

@drawable/goicon



 

 

完结

 

 

 修改:从原工程中独立出来,并附上简单的工程,见附件。

  • 大小: 406 Bytes
  • 大小: 24.4 KB
  • 大小: 25.2 KB
  • 大小: 23.1 KB
2
2
分享到:
评论
5 楼 xingfuyizhan 2016-05-19  
写的很好,谢谢分享
4 楼 xingfuyizhan 2016-05-19  
在Fragment中使用布局怎么显示不出来
3 楼 xhAndroid 2013-12-09  
加入我工程的时候有个诡异的bug,下拉刷新布局就会覆盖主View,
我的布局里面就是前面加了个title,把title的初始化和设置监听注释掉就不会有这个BUG了,太诡异了
2 楼 xhAndroid 2013-10-30  
1 楼 freezingsky 2013-07-10  
写得很好。继续努力!

相关推荐

    安卓Android源码——下拉刷新控件(ListView好ScrollView版).rar

    在ScrollView中实现下拉刷新相对复杂,因为ScrollView没有内置的刷新支持。开发者需要自定义监听器,检测滑动手势并触发相应的刷新动作。这通常涉及到对滚动事件的监听和处理,以及自定义头部视图来展示刷新状态。 ...

    Android下拉刷新控件(ListView好ScrollView版).rar

    这个“Android下拉刷新控件(ListView好ScrollView版)”压缩包文件包含的是关于如何在Android中实现这种功能的具体资料,主要集中在ListView和ScrollView这两种常见的滚动视图上。 1. **下拉刷新概念** 下拉刷新...

    Android自定义上拉加载下拉刷新控件

    在Android开发中,上拉加载和下拉刷新是常见的组件功能,用于提升用户体验,使得用户在滚动列表到顶部时能够方便地获取更多数据,而在滚动到底部时加载更多内容。本示例“Android自定义上拉加载下拉刷新控件”提供了...

    ScrollView实现下拉刷新

    在Android开发中,ScrollView是一个非常常见的...通过这些步骤,我们可以为用户提供一个直观、流畅的下拉刷新体验,增强应用的交互性和实用性。在实际开发中,我们应不断优化和完善这一功能,以适应不同场景的需求。

    Android下拉刷新控件(ListView好ScrollView版).zip

    3. **ScrollView的下拉刷新**:ScrollView不支持内置的下拉刷新,因此需要更多的自定义工作。我们可以在ScrollView内部添加一个可滚动的容器,如LinearLayout,然后模拟ListView的头部刷新逻辑。监听ScrollView的...

    Android项目下拉刷新控件(ListView好ScrollView版).rar

    本资源为Android项目中的下拉刷新控件,支持ListView和ScrollView两种视图。该控件具有高度可定制性,可根据您的需求进行二次开发。 该下拉刷新控件采用流畅的动画效果,提供轻松的用户体验。当用户下拉刷新时,...

    安卓下拉上拉刷新相关-百行代码搞定下拉刷新通过继承ScrollView编写.zip

    本教程将介绍如何通过继承ScrollView来实现一个简单的下拉刷新功能,适用于初学者理解基本原理和动手实践。 首先,我们来看"PullToRefresh"这个文件,它是实现下拉刷新的核心部分。在Android中,下拉刷新通常涉及到...

    【计算机专业-Andorid项目源码100套之】Android下拉刷新控件(ListView好ScrollView版)

    在ScrollView中实现下拉刷新相对复杂,因为ScrollView本身并不支持直接的刷新事件。开发者需要自定义ScrollView或者使用第三方库,如PullToRefreshLayout,来实现类似SwipeRefreshLayout的效果。 4. **自定义控件**...

    Android应用源码下拉刷新控件(ListView好ScrollView版)-IT计算机-毕业设计.zip

    - 自定义View:由于ScrollView本身不支持直接添加下拉刷新功能,开发者通常需要自定义一个继承自ScrollView的类,添加额外的触摸事件处理,以检测用户的下拉动作。 - 使用第三方库:像PullToRefreshLayout这样的第...

    ios自定义下拉刷新控件

    自定义下拉刷新控件可以让你根据项目需求定制刷新动画和视觉效果,提升用户体验。下面我们将深入探讨如何在iOS中实现自定义下拉刷新控件。 一、下拉刷新基础 1. UIRefreshControl:苹果提供的系统下拉刷新控件,...

    基于Android的Android下拉刷新控件(ListView好ScrollView版).zip

    在Android应用开发中,下拉刷新控件是一个非常常见的功能,它允许用户通过在列表视图(ListView)或滚动视图(ScrollView)顶部向下拉动来更新数据。这种交互设计极大地提升了用户体验,使得用户能轻松获取最新的...

    解决Scrollview嵌套listview下拉刷新的一些问题

    ScrollView是一个可以包含单个直接子视图的滚动容器,而ListView则是一个可以展示多个相同类型的条目并且支持滚动的控件。当在一个ScrollView中嵌套一个ListView时,可能会遇到一些特定的问题,尤其是在实现下拉刷新...

Global site tag (gtag.js) - Google Analytics