Android 实现listview 分页加载和下拉刷新

众所周知,在Android应用中,下拉刷新和上拉加载非常普遍,我们可以在任何一款应用上发现它的踪迹,现在提供一款能够完整实现的源代码和思路。

提供源码下载地址:(希望star一下 https://github.com/panyunyi97/CUFE_TRIP

首先我们来看一下相关代码(完整代码请见上述地址)

贴上一个自定义的listview 文件



    public class MyListView extends ListView implements OnScrollListener {

        private final static int RELEASE_To_REFRESH = 0;// 下拉过程的状态值  
        private final static int PULL_To_REFRESH = 1; // 从下拉返回到不刷新的状态值  
        private final static int REFRESHING = 2;// 正在刷新的状态值  
        private final static int DONE = 3;
        private final static int LOADING = 4;

        // 实际的padding的距离与界面上偏移距离的比例  
        private final static int RATIO = 3;
        private LayoutInflater inflater;

        // ListView头部下拉刷新的布局  
        private LinearLayout headerView;
        private TextView lvHeaderTipsTv;
        private TextView lvHeaderLastUpdatedTv;
        private ImageView lvHeaderArrowIv;
        private ProgressBar lvHeaderProgressBar;

        // 定义头部下拉刷新的布局的高度  
        private int headerContentHeight;

        private RotateAnimation animation;
        private RotateAnimation reverseAnimation;

        private int startY;
        private int state;
        private boolean isBack;

        // 用于保证startY的值在一个完整的touch事件中只被记录一次  
        private boolean isRecored;

        private OnRefreshListener refreshListener;

        private boolean isRefreshable;

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

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

        private void init(Context context) {
            setCacheColorHint((Color.TRANSPARENT));
            inflater = LayoutInflater.from(context);
            headerView = (LinearLayout) inflater.inflate(R.layout.lv_header, null);
            lvHeaderTipsTv = (TextView) headerView
                    .findViewById(R.id.lvHeaderTipsTv);
            lvHeaderLastUpdatedTv = (TextView) headerView
                    .findViewById(R.id.lvHeaderLastUpdatedTv);

            lvHeaderArrowIv = (ImageView) headerView
                    .findViewById(R.id.lvHeaderArrowIv);
            // 设置下拉刷新图标的最小高度和宽度  
            lvHeaderArrowIv.setMinimumWidth(70);
            lvHeaderArrowIv.setMinimumHeight(50);

            lvHeaderProgressBar = (ProgressBar) headerView
                    .findViewById(R.id.lvHeaderProgressBar);
            measureView(headerView);
            headerContentHeight = headerView.getMeasuredHeight();
            // 设置内边距,正好距离顶部为一个负的整个布局的高度,正好把头部隐藏  
            headerView.setPadding(0, -1 * headerContentHeight, 0, 0);
            // 重绘一下  
            headerView.invalidate();
            // 将下拉刷新的布局加入ListView的顶部  
            addHeaderView(headerView, null, false);
            // 设置滚动监听事件  
            setOnScrollListener(this);

            // 设置旋转动画事件  
            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);

            // 一开始的状态就是下拉刷新完的状态,所以为DONE  
            state = DONE;
            // 是否正在刷新  
            isRefreshable = false;
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                             int visibleItemCount, int totalItemCount) {
            if (firstVisibleItem == 0) {
                isRefreshable = true;
            } else {
                isRefreshable = false;
            }
        }

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (isRefreshable) {
                switch (ev.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        if (!isRecored) {
                            isRecored = true;
                            startY = (int) ev.getY();// 手指按下时记录当前位置
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        if (state != REFRESHING && state != LOADING) {
                            if (state == PULL_To_REFRESH) {
                                state = DONE;
                                changeHeaderViewByState();
                            }
                            if (state == RELEASE_To_REFRESH) {
                                state = REFRESHING;
                                changeHeaderViewByState();
                                onLvRefresh();
                            }
                        }
                        isRecored = false;
                        isBack = false;

                        break;

                    case MotionEvent.ACTION_MOVE:
                        int tempY = (int) ev.getY();
                        if (!isRecored) {
                            isRecored = true;
                            startY = tempY;
                        }
                        if (state != REFRESHING && isRecored && state != LOADING) {
                            // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
                            // 可以松手去刷新了
                            if (state == RELEASE_To_REFRESH) {
                                setSelection(0);
                                // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
                                if (((tempY - startY) / RATIO < headerContentHeight)// 由松开刷新状态转变到下拉刷新状态
                                        && (tempY - startY) > 0) {
                                    state = PULL_To_REFRESH;
                                    changeHeaderViewByState();
                                }
                                // 一下子推到顶了
                                else if (tempY - startY <= 0) {// 由松开刷新状态转变到done状态
                                    state = DONE;
                                    changeHeaderViewByState();
                                }
                            }
                            // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
                            if (state == PULL_To_REFRESH) {
                                setSelection(0);
                                // 下拉到可以进入RELEASE_TO_REFRESH的状态
                                if ((tempY - startY) / RATIO >= headerContentHeight) {// 由done或者下拉刷新状态转变到松开刷新
                                    state = RELEASE_To_REFRESH;
                                    isBack = true;
                                    changeHeaderViewByState();
                                }
                                // 上推到顶了
                                else if (tempY - startY <= 0) {// 由DOne或者下拉刷新状态转变到done状态
                                    state = DONE;
                                    changeHeaderViewByState();
                                }
                            }
                            // done状态下
                            if (state == DONE) {
                                if (tempY - startY > 0) {
                                    state = PULL_To_REFRESH;
                                    changeHeaderViewByState();
                                }
                            }
                            // 更新headView的size
                            if (state == PULL_To_REFRESH) {
                                headerView.setPadding(0, -1 * headerContentHeight
                                        + (tempY - startY) / RATIO, 0, 0);

                            }
                            // 更新headView的paddingTop
                            if (state == RELEASE_To_REFRESH) {
                                headerView.setPadding(0, (tempY - startY) / RATIO
                                        - headerContentHeight, 0, 0);
                            }

                        }
                        break;

                    default:
                        break;
                }
            }
            return super.onTouchEvent(ev);
        }

        // 当状态改变时候,调用该方法,以更新界面  
        private void changeHeaderViewByState() {
            switch (state) {
                case RELEASE_To_REFRESH:
                    lvHeaderArrowIv.setVisibility(View.VISIBLE);
                    lvHeaderProgressBar.setVisibility(View.GONE);
                    lvHeaderTipsTv.setVisibility(View.VISIBLE);
                    lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);

                    lvHeaderArrowIv.clearAnimation();// 清除动画
                    lvHeaderArrowIv.startAnimation(animation);// 开始动画效果

                    lvHeaderTipsTv.setText("松开刷新");
                    break;
                case PULL_To_REFRESH:
                    lvHeaderProgressBar.setVisibility(View.GONE);
                    lvHeaderTipsTv.setVisibility(View.VISIBLE);
                    lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
                    lvHeaderArrowIv.clearAnimation();
                    lvHeaderArrowIv.setVisibility(View.VISIBLE);
                    // 是由RELEASE_To_REFRESH状态转变来的
                    if (isBack) {
                        isBack = false;
                        lvHeaderArrowIv.clearAnimation();
                        lvHeaderArrowIv.startAnimation(reverseAnimation);

                        lvHeaderTipsTv.setText("下拉刷新");
                    } else {
                        lvHeaderTipsTv.setText("下拉刷新");
                    }
                    break;

                case REFRESHING:

                    headerView.setPadding(0, 0, 0, 0);

                    lvHeaderProgressBar.setVisibility(View.VISIBLE);
                    lvHeaderArrowIv.clearAnimation();
                    lvHeaderArrowIv.setVisibility(View.GONE);
                    lvHeaderTipsTv.setText("正在刷新...");
                    lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
                    break;
                case DONE:
                    headerView.setPadding(0, -1 * headerContentHeight, 0, 0);

                    lvHeaderProgressBar.setVisibility(View.GONE);
                    lvHeaderArrowIv.clearAnimation();
                    lvHeaderArrowIv.setImageResource(R.drawable.refresh);
                    lvHeaderTipsTv.setText("下拉刷新");
                    lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
                    break;
            }
        }

        // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height  
        private void measureView(View child) {
            ViewGroup.LayoutParams params = child.getLayoutParams();
            if (params == null) {
                params = new ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.FILL_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
            }
            int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0,
                    params.width);
            int lpHeight = params.height;
            int childHeightSpec;
            if (lpHeight > 0) {
                childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                        MeasureSpec.EXACTLY);
            } else {
                childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                        MeasureSpec.UNSPECIFIED);
            }
            child.measure(childWidthSpec, childHeightSpec);
        }

        public void setonRefreshListener(OnRefreshListener refreshListener) {
            this.refreshListener = refreshListener;
            isRefreshable = true;
        }

        public interface OnRefreshListener {
            public void onRefresh();
        }

        public void onRefreshComplete() {
            state = DONE;
            lvHeaderLastUpdatedTv.setText("最近更新:" + new Date().toLocaleString());
            changeHeaderViewByState();
        }

        private void onLvRefresh() {
            if (refreshListener != null) {
                refreshListener.onRefresh();
            }
        }

        public void setAdapter(list_Adapter adapter) {
            lvHeaderLastUpdatedTv.setText("最近更新:" + new Date().toLocaleString());
            super.setAdapter(adapter);
        }
    }

接下来是相关的listviewAdapter:

可以看见这里面使用了异步加载的工具类,在拿到图片地址后这边做异步的下载和加载
具体代码请参照上述地址

  • 使用viewHolder节省内存空间防止溢出
public class list_Adapter extends BaseAdapter{
            private Activity context;
            private List<String> list;
            private List<String>dataText;
            public static int count=0;
            public list_Adapter(Activity context, List<String> list, List<String>data) {
                Log.i(">>List",list.size()+"");
                this.context = context;
                this.list = list;
                this.dataText=data;
            }

        private void initData(ImageView imageview,int position) {
            AsynImageLoader asynImageLoader = new AsynImageLoader();
            asynImageLoader.showImageAsyn( imageview,list.get(position),0x7f0200e9);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Log.i(">>getView", "success" + "  " + position);
            System.out.print("" + position);
            LayoutInflater inflater = context.getLayoutInflater();
            ViewHolder holder;
            if(convertView==null){
                holder = new ViewHolder();
                convertView = inflater.inflate(R.layout.list_item, null);
                holder.imageSight=(ImageView)convertView.findViewById(R.id.list_image);
                holder.textSight = (TextView) convertView.findViewById(R.id.list_text);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }

            //String info = list.get(position);
            holder.textSight.setText(dataText.get(position));
            holder.imageSight.setImageBitmap(null);
            initData(holder.imageSight,position);

            return convertView;
        }

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

        @Override
        public Object getItem(int position) {
            return list.get(position);
        }

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

        class ViewHolder {
            ImageView imageSight;
            TextView textSight;

        }
        }

接下来是数据提供类:

为了方便我们把data.txt(地名)image.txt(下载地址)封装进asserts

public class ImageData {
        public static ArrayList<String> imageData=new ArrayList<>();
        public static ArrayList<String>imageUrl=new ArrayList<>();
        public static boolean TAG_URl=false;
        public static boolean TAG_Data=false;
        public static  ArrayList<String> getImageUrl(final Context context)  {
            if(imageUrl.size()>100){
                return imageUrl;
            }
             new Thread() {
                 public void run() {
                     try{
                     readFileByLines(context.getClass().getClassLoader().getResourceAsStream("assets/" + "image.txt"),imageUrl);
                     TAG_URl=true;
                     }catch (Exception ex){
                         ex.printStackTrace();
                     }
                }
            }.start();
            while(TAG_URl==false) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return imageUrl;
        }
        public static ArrayList<String> getImageData(final Context context){
            if(imageData.size()>100){
                return imageData;
            }
            new Thread() {
                public void run() {
                    try{
                    readFileByLines(context.getClass().getClassLoader().getResourceAsStream("assets/" + "data.txt"),imageData);
                    }catch (Exception ex){
                        ex.printStackTrace();
                    }
                    TAG_Data=true;
                }
            }.start();
            while(TAG_Data==false) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return imageData;
        }
        public static synchronized void readFileByLines(InputStream fileName, ArrayList<String>imageInfo) {
            //File file = new File(fileName);
            BufferedReader reader = null;
            try {
                System.out.println("以行为单位读取文件内容,一次读一整行:");
                reader = new BufferedReader(new InputStreamReader(fileName));
                String tempString = null;
                int line = 1;
                // 一次读入一行,直到读入null为文件结束
                while ((tempString = reader.readLine()) != null) {
                    // 显示行号
                    System.out.println("line " + line + ": " + tempString);
                    imageInfo.add(tempString);
                    line++;
                }
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e1) {
                    }
                }

            }
        }

    }

接下来是在Activity中的操作:

@Override

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_line2);
        listView = (MyListView) findViewById(R.id.friend_circle);

        Log.i(">>imageUrl", ImageData.imageUrl.size() + "");
        LoadingView mLoadingView = (LoadingView) findViewById(R.id.loading);
        LayoutInflater layoutInflater = LayoutInflater.from(this);

        footerView = layoutInflater.inflate(R.layout.footer_listview, null);


        listView.setEmptyView(mLoadingView);
        listView.setAdapter(new list_Adapter(this,
                ImageData.getImageUrl(getApplicationContext()).subList(tempRecord, tempRecord + 14),
                ImageData.getImageData(getApplicationContext()).subList(tempRecord,tempRecord + 14)));
        listView.getEmptyView().setVisibility(View.INVISIBLE);
        footerView.findViewById(R.id.footer_layout).setVisibility(View.VISIBLE);//设置底部布局不可见


        listView.addFooterView(footerView);
        listView.setOnScrollListener(this);
        listView.setonRefreshListener(new MyListView.OnRefreshListener() {
            @Override
            public void onRefresh() {
                if (tempRecord >= 15) {
                    tempRecord -= 15;
                    listView.setAdapter(new list_Adapter(
                                    LineActivity.this,
                                    ImageData.imageUrl.subList(tempRecord, tempRecord + 15),
                                    ImageData.imageData.subList(tempRecord,tempRecord + 15))
                            );
                }
                listView.onRefreshComplete();
            }
        });
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (totalItemCount == lastVisibieItem && scrollState == SCROLL_STATE_IDLE) {
                if (!isLoading) {
                    isLoading = true;
                    footerView.setVisibility(View.VISIBLE);
                    footerView.findViewById(R.id.footer_layout).setVisibility(View.VISIBLE);
                    // 加载更多(获取接口)
                    //iLoadListener.onLoad();
                    tempRecord += 15;
                    listView.setAdapter(new list_Adapter(
                            LineActivity.this,
                            ImageData.imageUrl.subList(tempRecord, tempRecord + 15),
                            ImageData.imageData.subList(tempRecord,tempRecord + 15))
                    );
                    isLoading = false;
                    footerView.setVisibility(View.GONE);
                }
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            this.lastVisibieItem = firstVisibleItem + visibleItemCount;
            this.totalItemCount = totalItemCount;
            footerView.setVisibility(View.VISIBLE);
        }

自行代码格式化即可。

!!!需要完整源码的请访问下面的地址 (如果觉得有帮助请帮忙star https://github.com/panyunyi97/CUFE_TRIP

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值