自定义控件--下拉刷新

时间:2021-02-06 20:38:38

下拉刷新

  • 继承已有的控件

    • 继承ListView

      • 给ListView 加个Header头部, 用来下拉刷新

        1. 静态的头部加上去
        2. 根据用户手指触摸, 动态的修改mHeaderView的paddingTop
        3. 手指抬起时, 根据当前状态,更新头布局
      • 给ListView 加个Footer脚部, 用来加载更多

    • 自定义头布局步骤:

      <?xml version="1.0" encoding="utf-8"?>
      <rotate xmlns:android="http://schemas.android.com/apk/res/android"
          android:fromDegrees="0"
          android:pivotX="50%"
          android:pivotY="50%"
          android:toDegrees="360" >
      
          <shape
              android:innerRadiusRatio="2.5"
              android:shape="ring"
              android:thicknessRatio="10"
              android:useLevel="false" >
              <gradient
                  android:centerColor="#FF6666"
                  android:endColor="#FF0000"
                  android:startColor="#FFFFFF"
                  android:type="sweep" />
          </shape>
      
      </rotate>
      
      * 在xml布局中使用:
      
      <ProgressBar
          android:id="@+id/pb_header_loading"
          android:layout_width="50dp"
          android:layout_height="50dp"
          android:indeterminateDrawable="@drawable/red_progress_bar"
          android:layout_gravity="center" />
      

MainActivity类的内容

public class MainActivity extends Activity {

    private List<String> mListData;
    private MyAdapter adapter;
    private RefreshListView rlv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        rlv = (RefreshListView) findViewById(R.id.rlv);

        setData(rlv);

        rlv.setOnRefreshListener(new OnRefreshListener() {

            @Override
            public void onRefreshRequest() {
                System.out.println("用户有刷新请求");
                new Thread(){
                    public void run() {
                        SystemClock.sleep(3000);

                        mListData.add(0, "这是下拉刷新出来的数据");

                        runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                adapter.notifyDataSetChanged();
                                rlv.onRefreashFinish();
                            }
                        });
                    };
                }.start();
            }

            @Override
            public void onLoadMore() {
                System.out.println("用户有加载更多请求");
                new Thread(){
                    public void run() {
                        SystemClock.sleep(3000);

                        mListData.add("这是加载更多 出来的数据1");
                        mListData.add("这是加载更多 出来的数据2");
                        mListData.add("这是加载更多 出来的数据3");

                        runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                adapter.notifyDataSetChanged();
                                rlv.onRefreashFinish();
                            }
                        });
                    };
                }.start();


            }
        });

    }

    private void setData(RefreshListView rlv) {
        // 初始化数据
        mListData = new ArrayList<String>();
        for (int i = 0; i < 30; i++) {
            mListData.add("这是一条ListView的数据: " + i);
        }

        // 设置Header, 必须在setAdapter之前设置
//      Button button = new Button(this);
//      button.setText("这是一个按钮");
//      rlv.addHeaderView(button);

        //设置适配器

        adapter = new MyAdapter();
        rlv.setAdapter(adapter);
    }

    class MyAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return mListData.size();
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            TextView textView = new TextView(MainActivity.this);
            textView.setTextSize(18);
            textView.setText(mListData.get(position));

            return textView;
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

    }

}

新建RefreshListView类继承ListView

    /**
 * 下拉刷新控件, 上拉加更多
 * @author poplar
 *
 */
public class RefreshListView extends ListView implements OnScrollListener{

    private View mHeaderView; // 头布局
    private int downY; // 按下时 Y 轴坐标值
    private int mHeaderViewHeight;

    private final int PULL_DOWN = 0; // 下拉刷新状态
    private final int RELEASE_REFRESH = 1; // 释放刷新状态
    private final int REFRESHING = 2; // 正在刷新中...

    int currentState = PULL_DOWN;// 默认是下拉刷新状态
    private ProgressBar pbHeaderLoading;
    private TextView tvHeaderState;
    private TextView tvHeaderLastRefreshTime;
    private ImageView ivHeaderArrow;
    private RotateAnimation rotateUp;
    private RotateAnimation rotateDown;

    private OnRefreshListener listener; // 监听回调
    private View mFooterView;  // 脚布局
    private int mFooterViewHeight; //脚布局高度
    private boolean isLoadMore = false; // 是否是加载更多

    public RefreshListView(Context context) {
        super(context);
        init();
    }

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

    public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * 初始化
     * onMeasure -> onLayout -> onDraw
     * 
     */
    private void init() {
        initHeaderView();
        initAnimation();

        initFooterView();

        setOnScrollListener(this);
    }

    /**
     * 初始化脚布局
     */
    private void initFooterView() {
        mFooterView = View.inflate(getContext(), R.layout.layout_list_footer, null);

        // 隐藏脚布局
        mFooterView.measure(0, 0);
        mFooterViewHeight = mFooterView.getMeasuredHeight();

        mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);

        this.addFooterView(mFooterView);
    }

    private void initAnimation() {
        // 箭头向上的动画
        rotateUp = new RotateAnimation(
                0, -180, 
                Animation.RELATIVE_TO_SELF, 0.5f, 
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateUp.setDuration(500);
        rotateUp.setFillAfter(true);

        // 箭头向下的动画
        rotateDown = new RotateAnimation(
                -180, -360,
                Animation.RELATIVE_TO_SELF, 0.5f, 
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateDown.setDuration(500);
        rotateDown.setFillAfter(true);

    }

    /**
     * 初始化Header, 头布局
     */
    private void initHeaderView() {
        mHeaderView = View.inflate(getContext(), R.layout.layout_list_header, null);
        pbHeaderLoading = (ProgressBar) mHeaderView.findViewById(R.id.pb_header_loading);
        pbHeaderLoading.setVisibility(View.INVISIBLE);

        tvHeaderState = (TextView) mHeaderView.findViewById(R.id.tv_header_state);
        tvHeaderLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_header_last_refresh_time);

        ivHeaderArrow = (ImageView) mHeaderView.findViewById(R.id.iv_header_arrow);



        int height = mHeaderView.getHeight(); // 只有在布局被绘制到界面上时, 才能获取到高度
        // 获取测量的高度
        mHeaderView.measure(0, 0); // 手动测量, 指定 宽高0,0. 表示按其默认设置测量.
        mHeaderViewHeight = mHeaderView.getMeasuredHeight();
        System.out.println("height: " + height  + "measuredHeight: " + mHeaderViewHeight);
        // 设置内边距
        mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

        addHeaderView(mHeaderView);
    }

    // 根据用户手指触摸, 动态的修改mHeaderView的paddingTop
    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getY();

                break;
            case MotionEvent.ACTION_MOVE:

                // 如果正在刷新中, 返回, 按父类处理
                if(currentState == REFRESHING){
                    return super.onTouchEvent(ev);
                }

                int moveY = (int) ev.getY();

                // 获取Y轴的移动量
                int diffY = moveY - downY;
//              System.out.println("diffY: " + diffY);

                // 当前ListView最上边能看到的条目, 是ListView所有条目的第一条. 
                if(diffY > 0 && getFirstVisiblePosition() == 0){
                    // 下拉, 把头布局显示出来
//                  int paddingTop= - 60 + (moveY - downY) 
                    int paddingTop = - mHeaderViewHeight + diffY / 2;
                    mHeaderView.setPadding(0, paddingTop, 0, 0);

                    if(paddingTop > 0 && currentState != RELEASE_REFRESH){ // 刚刚不是释放刷新模式才进来
                        // 头布局已经完全显示了,  切换成 释放刷新 模式
                        System.out.println("切换 释放刷新 模式");
                        currentState = RELEASE_REFRESH;
                        updateHeaderView();

                    }else if(paddingTop < 0 && currentState != PULL_DOWN){// 刚刚不是下拉刷新模式才进来
                        // 头布局没有完全显示,  切换成 下拉刷新 模式
                        System.out.println("切换成 下拉刷新 模式");
                        currentState = PULL_DOWN;
                        updateHeaderView();
                    }

                    return true; // 子类处理的触摸事件, 不再传递给父类处理
                }

                break;
            case MotionEvent.ACTION_UP:
                // 手指抬起了
                if(currentState == PULL_DOWN){
                    // 抬起时, 是下拉刷新状态, 隐藏
                    mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
                }else if(currentState == RELEASE_REFRESH){
                    // 抬起时, 是释放刷新, 恢复成初始大小
                    mHeaderView.setPadding(0, 0, 0, 0);

                    currentState = REFRESHING;
                    updateHeaderView();
                }

                break;

            default:
                break;
        }

        return super.onTouchEvent(ev);
    }

    /**
     * 刷新结束时调用
     */
    public void onRefreashFinish() {
        if(isLoadMore){
            System.out.println("加载更多完毕");
            // 如果是加载更多, 恢复脚布局
            isLoadMore = false;
            // 隐藏脚布局
            mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);

        }else {
            // 如果是下拉刷新, 恢复头布局
            currentState = PULL_DOWN;
            ivHeaderArrow.setVisibility(View.VISIBLE);
            pbHeaderLoading.setVisibility(View.INVISIBLE);
            tvHeaderState.setText("下拉刷新");
            tvHeaderLastRefreshTime.setText("最后刷新时间: " + getLastRefreshTime());
            //  隐藏头布局
            mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
        }
    }

    // 2015-5-22 11:26:53
    private String getLastRefreshTime() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String str = format.format(new Date());
        return str;
    }

    //  private final int PULL_DOWN = 0; // 下拉刷新状态
//  private final int RELEASE_REFRESH = 1; // 释放刷新状态
//  private final int REFRESHING = 2; // 正在刷新中...
    /**
     * 根据当前的状态更新 头布局
     */
    private void updateHeaderView() {
        switch (currentState) {
        case PULL_DOWN:
            // 更新 下拉刷新状态 时头布局
            // 更新文本
            tvHeaderState.setText("下拉刷新");
            // 执行动画
            ivHeaderArrow.startAnimation(rotateDown);

            break;
        case RELEASE_REFRESH:
            // 更新 释放刷新状态 时头布局
            // 更新文本
            tvHeaderState.setText("释放刷新");
            // 执行动画
            ivHeaderArrow.startAnimation(rotateUp);

            break;
        case REFRESHING:
            // 更新 正在刷新中...时头布局

            ivHeaderArrow.clearAnimation();
            ivHeaderArrow.setVisibility(View.INVISIBLE);

            pbHeaderLoading.setVisibility(View.VISIBLE);
            tvHeaderState.setText("正在刷新中...");

            if(listener != null){
                listener.onRefreshRequest();
            }

            break;

        default:
            break;
        }
    }

    public void setOnRefreshListener(OnRefreshListener listener){
        this.listener = listener;
    }


//  public static int SCROLL_STATE_IDLE = 0;
//  public static int SCROLL_STATE_TOUCH_SCROLL = 1;
//  public static int SCROLL_STATE_FLING = 2;
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

        System.out.println("scrollState");
        // 当状态改变时候调用
        if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING){
            // 如果当前状态是 闲置/滑翔  状态
            // 到最后一条
            int lastVisiblePosition = getLastVisiblePosition();

            System.out.println("lastVisiblePosition: " + lastVisiblePosition);
            if(lastVisiblePosition == getCount() - 1){
                // 显示脚布局
                mFooterView.setPadding(0, 0, 0, 0);

                // 让ListView滚动到最下边
                setSelection(getCount());

                isLoadMore  = true;
                if(listener != null){
                    listener.onLoadMore();
                }
            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        // 当状滑动时调用
    }

    /**
     * 刷新接口,监听
     * @author poplar
     *
     */
    public interface OnRefreshListener{

        void onRefreshRequest();

        void onLoadMore();

    }

}