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

来源:互联网 发布:ubuntu 17.10 国际版 编辑:程序博客网 时间:2024/05/06 22:28

众所周知,在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 0
原创粉丝点击