• Android中自定义ListView实现上拉加载更多和下拉刷新


    ListView是Android中一个功能强大而且很常用的控件,在很多App中都有ListView的下拉刷新数据和上拉加载更多这个功能。这里我就简单记录一下实现过程。

    实现这个功能的方法不止一个,GitHub上有一些开源库可以使用,但是本着学习的精神,我做的是使用自定义ListView实现这个功能。

    思路:谷歌提供的ListView是不能提供下拉刷新和下拉加载的,所以我们就需要重写ListView。在ListView的头部和尾部加上我们的布局文件(progressbar)。

    先说上拉加载更多实现原理:

    首先先写一个底部布局的xml:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
         android:id="@+id/foot_load"
         android:padding="5dp"
         android:gravity="center">
         <ProgressBar
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             style="?android:attr/progressBarStyle"
             />
         <TextView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="加载中。。。。"
             />
     </LinearLayout>
    
    </LinearLayout>

        写一个自定义ListView继承自ListView实现它的三个构造方法,这里必须实现三个构造方法,不实现的话就无法显示ListView.并且在每个构造方法中都写一个方法用于初始化相关控件。

    public class MyListView extends ListView {
    public MyListView(Context context) {
            super(context);
            initView(context);
        }
        public MyListView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView(context);
        }
    
        public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context);
        }
    }

    在initView(context)方法中通过LayoutInfalter.from(context).from(你的xml,null);拿到xml。返回一个View对象,ListView中有一个setfootView()方法,使用他可以设置View到ListView的底部,当然做了这两步是完全不够的,我们还需要对这个View做一个交互的操作,当用户滑到底部时这个View显示,数据加载完毕之后这个View消失。

    第一步:需要对这个底部footView隐藏:

    第一种方式是通过setVisibility(View.GONE)这个方法让这个View不显示。

    第二种方式:

           //测量footview的高度,通过measure()传入两个0参数,系统会不认这个0,自动帮我们测量出加在底部View的长度信息
            footview.measure(0,0);
            //拿到高度
            footViewHeight=footview.getMeasuredHeight();
            //隐藏view,让头部显示高度为实际测量高度的负数,实现布局的隐藏
            footview.setPadding(0,-footViewHeight,0,0);

    第二部:当滑到底部的时候显示这个footView。这里就要实现下滑监听接口implements AbsListView.OnScrollListener,实现接口的两个方法。

      @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (totaItemCounts==lasstVisible&&scrollState==SCROLL_STATE_IDLE) {
                if (!isLoading) {
                    isLoading=true;
                   // footview.findViewById(R.id.foot_load).setVisibility(View.VISIBLE);
                    footview.setPadding(0,0,0,0);
                    //加载数据
                    loadListener.onLoad();}
            }
    
        }
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    //最后一个可见item是第一个可见item加上所有可见item(第一个可见item为0)
    this.lasstVisible=firstVisibleItem+visibleItemCount;
    //所有item为listview的所有item
    this.totaItemCounts=totalItemCount; }

    那么如何在刷新的时候通知ListView加载数据呢?这里使用一个接口回调的方法在自定义ListView写一个接口,里面写一个更新ListView的方法,让主布局调用这个接口实现这个接口的方法。

     public void setInterface(LoadListener loadListener){
            this.loadListener=loadListener;
    
        }
        //接口回调
        public interface LoadListener{
    
            void onLoad();
            
      }
        @Override
        public void onLoad() {
            //设置三秒延迟模仿延时获取数据
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    //加载数据
                    for (int j=1;j<11;j++){
    
                        list.add(j);
                    }
                    //更新 数据
                    adapter.notifyDataSetChanged();
                    //加载完毕,就是让View隐藏
                    listview.loadComplete();
    
                }
            },3000);
    }

    好了,这就实现了一个简单的上拉加载功能。

    下拉刷新和上拉加载过程差不多,但是有时候,下拉刷新有提示,下拉刷新,松开刷新,正在刷新提示,这又该如何实现呐,这里就需要实现onTouchEvent方法,设置下拉长度匹配刷新数据。同时,设置progrebar的可见状态。

       @Override
        public boolean onTouchEvent(MotionEvent ev) {
            switch (ev.getAction()){
                case MotionEvent.ACTION_DOWN:
                    yload=(int)ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int moveY=(int)ev.getY();
                    int paddingY=-headViewHeight+(moveY-yload)/2;
    
                        if (paddingY<0){
                            updateInfo.setText("下拉刷新。。。");
                            progressBar.setVisibility(View.GONE);
                        }
                        if (paddingY>0){
                            updateInfo.setText("松开刷新。。。");
                            progressBar.setVisibility(View.GONE);
                        }
    
                    headview.setPadding(0,paddingY,0,0);
    
                    break;
    //            case MotionEvent.ACTION_UP:
    //                headview.setPadding(0,0,0,0);
    //                updateInfo.setText("正在刷新。。。");
    //                progressBar.setVisibility(View.VISIBLE);
    //                loadListener.pullLoad();
    //
    //                break;
            }
            return super.onTouchEvent(ev);
        }

    附上完整源码:

    main.xml布局:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.yakir.view.MyListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/list_view"></com.yakir.view.MyListView>
    
    
    </RelativeLayout>

    main activity:

    package com.yakir.demo;
    
    import android.os.Bundle;
    import android.os.Handler;
    import android.support.v7.app.AppCompatActivity;
    import android.widget.ArrayAdapter;
    
    import com.yakir.view.MyListView;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity implements MyListView.LoadListener{
        private MyListView listview;
        private List<Integer>list=new ArrayList<>();
        private ArrayAdapter adapter;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            for (int i=1;i<20;i++){
                list.add(i);
            }
            initView();
            adapter=new ArrayAdapter<>(this,android.R.layout.simple_expandable_list_item_1,list);
            listview.setAdapter(adapter);
    
        }
    
        private void initView() {
            listview=(MyListView) findViewById(R.id.list_view);
            listview.setInterface(this);
        }
    
        @Override
        public void onLoad() {
            //设置三秒延迟模仿延时获取数据
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    //加载数据
                    for (int j=1;j<11;j++){
    
                        list.add(j);
                    }
                    //更新 数据
                    adapter.notifyDataSetChanged();
                    //加载完毕
                    listview.loadComplete();
    
                }
            },3000);
    
    
        }
    
        @Override
        public void pullLoad() {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    list.clear();
                    for (int i=1;i<20;i++){
                        list.add(i+1);
                    }
                    adapter.notifyDataSetChanged();
                    listview.loadComplete();
    
                }
            },2000);
    
        }
    }

    头布局headview:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/head"
            android:orientation="horizontal"
            android:gravity="center"
            android:padding="5dp">
            <ProgressBar
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="?android:attr/progressBarStyleInverse"
                android:id="@+id/progressbar"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="正在刷新。。。。"
                android:id="@+id/update_info"/>
    
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/update_time"
                />
    
        </LinearLayout>
    
    
    </LinearLayout>

    尾布局footview:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
         android:id="@+id/foot_load"
         android:padding="5dp"
         android:gravity="center">
         <ProgressBar
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             style="?android:attr/progressBarStyle"
             />
         <TextView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="加载中。。。。"
             />
     </LinearLayout>
    
    </LinearLayout>

    自定义ListView:

    package com.yakir.view;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.AbsListView;
    import android.widget.ListView;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    
    import com.yakir.demo.R;
    
    import java.text.SimpleDateFormat;
    
    /**
     * 自定义listview实现上拉刷新下拉加载
     * Created by qbqw7 on 2016/7/12.
     */
    public class MyListView extends ListView implements AbsListView.OnScrollListener{
        private View footview;
        private View headview;
        private int totaItemCounts;
        private int lasstVisible;
        private int fistVisiable;
        private LoadListener loadListener;
        private int footViewHeight;
        private int headViewHeight;
        private int yload;
        boolean isLoading;
        private TextView updateInfo;
        private TextView updateTime;
        private ProgressBar progressBar;
        public MyListView(Context context) {
            super(context);
            initView(context);
        }
        public MyListView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView(context);
        }
    
        public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context);
        }
    
        private void initView(Context context) {
            //拿到头布局xml
            headview=LayoutInflater.from(context).inflate(R.layout.head_layouyt,null);
            updateInfo=(TextView)headview.findViewById(R.id.update_info);
            updateTime=(TextView)headview.findViewById(R.id.update_time);
            progressBar=(ProgressBar)headview.findViewById(R.id.progressbar);
            updateTime.setText("更新于:"+new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss").format(System.currentTimeMillis()));
            //拿到尾布局xml
            footview=LayoutInflater.from(context).inflate(R.layout.proces_layout,null);
            //测量footview的高度
            footview.measure(0,0);
            //拿到高度
            footViewHeight=footview.getMeasuredHeight();
            //隐藏view
            footview.setPadding(0,-footViewHeight,0,0);
            headview.measure(0,0);
            headViewHeight=headview.getMeasuredHeight();
            headview.setPadding(0,-headViewHeight,0,0);
            //设置不可见
            // footview.findViewById(R.id.foot_load).setVisibility(View.GONE);
            // headview.findViewById(R.id.head).setVisibility(View.GONE);
            //添加到listview底部
            this.addFooterView(footview);
            //添加到listview头部
            this.addHeaderView(headview);
            //设置拉动监听
            this.setOnScrollListener(this);
    
    
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            switch (ev.getAction()){
                case MotionEvent.ACTION_DOWN:
                    yload=(int)ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int moveY=(int)ev.getY();
                    int paddingY=-headViewHeight+(moveY-yload)/2;
    
                        if (paddingY<0){
                            updateInfo.setText("下拉刷新。。。");
                            progressBar.setVisibility(View.GONE);
                        }
                        if (paddingY>0){
                            updateInfo.setText("松开刷新。。。");
                            progressBar.setVisibility(View.GONE);
                        }
    
                    headview.setPadding(0,paddingY,0,0);
    
                    break;
    //            case MotionEvent.ACTION_UP:
    //                headview.setPadding(0,0,0,0);
    //                updateInfo.setText("正在刷新。。。");
    //                progressBar.setVisibility(View.VISIBLE);
    //                loadListener.pullLoad();
    //
    //                break;
            }
            return super.onTouchEvent(ev);
        }
    
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (totaItemCounts==lasstVisible&&scrollState==SCROLL_STATE_IDLE) {
                if (!isLoading) {
                    isLoading=true;
                   // footview.findViewById(R.id.foot_load).setVisibility(View.VISIBLE);
                    footview.setPadding(0,0,0,0);
                    //加载数据
                    loadListener.onLoad();
    
                }
            }
            if (fistVisiable==0){
                headview.setPadding(0,0,0,0);
                updateInfo.setText("正在刷新。。。");
                progressBar.setVisibility(View.VISIBLE);
                loadListener.pullLoad();
            }
        }
    
    
    
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            this.fistVisiable=firstVisibleItem;
            this.lasstVisible=firstVisibleItem+visibleItemCount;
            this.totaItemCounts=totalItemCount;
    
        }
        //加载完成
        public void loadComplete(){
            isLoading=false;
            //footview.findViewById(R.id.foot_load).setVisibility(View.GONE);
            footview.setPadding(0,-footViewHeight,0,0);
            headview.setPadding(0,-headViewHeight,0,0);
    
    
        }
        public void setInterface(LoadListener loadListener){
            this.loadListener=loadListener;
    
        }
        //接口回调
        public interface LoadListener{
    
            void onLoad();
            void pullLoad();
    
    
        }
    }
  • 相关阅读:
    mysql 导入CSV数据 [转]
    Linux用户态程序计时方式详解[转]
    [转] Bash脚本:怎样一行行地读文件(最好和最坏的方法)
    第二次作业
    软件工程原理与方法 第一次作业
    2017-02-19,周日整理
    2017-02-12,周日整理
    cnblogs,第一次博客纪念。
    堆和栈的区别(转过无数次的文章)
    Flash Player版本相关问题
  • 原文地址:https://www.cnblogs.com/lovelyYakir/p/5668331.html
Copyright © 2020-2023  润新知