«

android最基本的lsitvew实现下拉刷新,上拉加载更多的demo

时间:2024-3-2 17:21     作者:韩俊     分类: Android


接着上次来讲,这次来动手写一下listview的下拉刷新功能和上拉加载更多功能。

当然google在android4.0以上的API里面的提供了一个可以下拉加载更多的控件,这个小圆圈加载控件在豆瓣,知乎日报里面都有运用到,而我在下一篇博客也会提到。

先来了解一下最基本的listview的的加载功能吧。

首先是下拉刷新功能,我先说一下基本的思路。listveiw的面提供了一个addheader()方法,我们可以重写listview,然后用addheader方法加载我们自定义的加载布局。然后就是隐藏这个header,然后复写监听方法OnScrollListener()和OnTouch()方法,最后再提供一个接口方法来让用户实现加载数据。具体的我在代码里面都注释好了。

再来说一下上拉加载,这个相比于下拉加载就简单多了,我们可以addfooter()方法添加布局,然后监听OnScrollListener就可以了,当最后一个可见的item等于总数量的item时,就可以加载数据了。具体在代码里面斗注释好了。


效果图:



先发布局文件:

<RelativeLayout 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"
    tools:context="com.example.listview_pulltorefresh.MainActivity" >

    <com.example.listview_pulltorefresh.RefreshListview 
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </com.example.listview_pulltorefresh.RefreshListview>

</RelativeLayout>

header.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >

        <LinearLayout
            android:id="@+id/layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/tip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉刷新" />

            <TextView
                android:id="@+id/lastrefresh_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <ImageView
            android:id="@+id/arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_toLeftOf="@id/layout"
            android:src="@drawable/pull_to_refresh_arrow" />
        <!-- android:indeterminateDrawable="@drawable/loading_anim" -->

        <ProgressBar
            android:id="@+id/progress"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_toLeftOf="@id/layout"
            android:visibility="gone" />
    </RelativeLayout>

</LinearLayout>


footer.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/load_footer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >

        <ProgressBar
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="正在加载"/>
    </LinearLayout>

</LinearLayout>

listitem.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="50dp">

    <ImageView 
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:src="@drawable/ic_launcher"/>

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="91dp"
        android:layout_toRightOf="@+id/image"
        android:text="数据列" />

</RelativeLayout>

自定义的listview方法:

package com.example.listview_pulltorefresh;

import java.sql.Date;
import java.text.SimpleDateFormat;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class RefreshListview extends ListView {
    private View header;// 顶部布局文件
    private View footer;// 底部布局
    private int headerHeight;// 顶部布局文件的高度
    private int firstVisibleItem;// 当前第一个可见item的位置
    private boolean isRemark;// 标记当前listviews是否最顶端摁下
    private int startY;// 开始的Y值

    private int mscrollState;// 当前listview的滚动状态
    private int state;// 当前状态
    private static final int NONE = 0;// 正常状态
    private static final int PULL = 1;// 下拉状态
    private static final int RELEASE = 2;// 松开状态
    private static final int REFRESHING = 3;// 刷新状态

    private int mtotalItemCount;//全部item的数量
    private int lastVisableItem;//最后一个可见的item
    private boolean isLoading=false;//是否正在加载

    public RefreshListview(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public RefreshListview(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public RefreshListview(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    /**
     * 添加顶部布局文件
     * 
     * @param context
     */
    private void initView(Context context) {
        LayoutInflater inflater = LayoutInflater.from(context);
        footer = inflater.inflate(R.layout.footer_loading, null, false);
        header = inflater.inflate(R.layout.header_layout, null, false);
        measureView(header);
        headerHeight = header.getMeasuredHeight();
        Log.i("test", "headHeight:" + headerHeight);
        topPadding(-headerHeight);
        this.addHeaderView(header);
        //先设置底部隐藏
        footer.setVisibility(View.GONE);
        this.addFooterView(footer);
        this.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // TODO Auto-generated method stub
                mscrollState = scrollState;

                //最后一个可见的item是总数量,并且当前滚动状态停止,就加载数据
                if (mtotalItemCount==lastVisableItem&&scrollState==OnScrollListener.SCROLL_STATE_IDLE) {
                    if (!isLoading) {
                        //加载数据
                        isLoading=true;
                        footer.setVisibility(VISIBLE);
                        mListener2.onReflashMore();

                    }

                }
            }

            /**
             * firstvisebleitem第一个可见的位置
             */
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {
                // TODO Auto-generated method stub
                lastVisableItem=firstVisibleItem+visibleItemCount;
                mtotalItemCount=totalItemCount;
            }

        });

        this.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    if (firstVisibleItem == 0) {
                        isRemark = true;
                        startY = (int) event.getY();
                    }
                case MotionEvent.ACTION_MOVE:
                    onMove(event);

                    break;
                case MotionEvent.ACTION_UP:
                    if (state == RELEASE) {

                        state = REFRESHING;
                        reflashViewByState();
                        mListener.onrReflash(); // 加载数据
                        // 在外部调用reflashcomplete
                    } else if (state == PULL) {
                        state = NONE;
                        isRemark = false;
                        reflashViewByState();
                    }
                    break;

                default:
                    break;
                }
                return false;
            }
        });
    }

    /**
     * 通过view获取layoutparams,然后初始化lp, 要调用measure()方法来设置子view的宽高
     * measure的方法参数有变化的是用MeasureSpec.makeMeasureSpec设置
     * 没有变化的用getChildMeasureSpec()方法设置
     * 
     * @param view
     */

    public void measureView(View view) {
        ViewGroup.LayoutParams lParams = view.getLayoutParams();
        if (lParams == null) {
            lParams = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
            // spec左右边距,padding内边距
            int width = ViewGroup.getChildMeasureSpec(0, 0, lParams.width);
            int height;
            int tempHeight = lParams.height;
            if (tempHeight > 0) {
                // 填充用exactly
                height = MeasureSpec.makeMeasureSpec(tempHeight,
                        MeasureSpec.EXACTLY);

            } else {
                // 意思就是<=0时,则告诉父布局子view高度填充0
                height = MeasureSpec
                        .makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            }
            view.measure(width, height);
        }

    }

    /**
     * 设置header布局的上边距
     * 
     * @param topPadding
     */
    public void topPadding(int topPadding) {
        header.setPadding(header.getPaddingLeft(), topPadding,
                header.getPaddingRight(), header.getPaddingBottom());
        header.invalidate();
    }

    /**
     * 判断移动过程中的操作
     * 
     * @param event
     */
    private void onMove(MotionEvent event) {
        // TODO Auto-generated method stub
        if (!isRemark) {
            return;
        }
        int tempY = (int) event.getY();// 获取当前y
        int space = tempY - startY;// 显示的高度
        int topPadding = space - headerHeight;// 因为是要用负值,所以减去高度
        switch (state) {
        case NONE:
            if (space > 0) {
                state = PULL;
                reflashViewByState();
            }

            break;
        case PULL:
            topPadding(topPadding);
            // 大于heigh+30且在滑动时,则是可以刷新
            if (space > headerHeight + 30
                    && mscrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                state = RELEASE;
                reflashViewByState();
            }

            break;

        case RELEASE:
            topPadding(topPadding);
            // 为释放状态时,则可以回到下拉状态
            if (space < headerHeight + 30) {
                state = PULL;
                reflashViewByState();
            } else if (space <= 0) {
                state = NONE;
                isRemark = false;
                reflashViewByState();
            }

            break;

        case REFRESHING:

            break;

        default:
            break;
        }
    }

    /**
     * 改变下拉过程中的header布局中的控件的内容
     */
    public void reflashViewByState() {
        TextView tip = (TextView) header.findViewById(R.id.tip);
        ImageView arrow = (ImageView) header.findViewById(R.id.arrow);
        ProgressBar progressBar = (ProgressBar) header
                .findViewById(R.id.progress);
        // RotateAnimation旋转动画,旋转的角度,相对与自己,中心位置
        RotateAnimation animation = new RotateAnimation(0, 180,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        animation.setDuration(500);// 时间间隔
        animation.setFillAfter(true);// 保存状态
        RotateAnimation animation2 = new RotateAnimation(180, 0,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        animation2.setDuration(500);// 时间间隔
        animation2.setFillAfter(true);// 保存状态
        switch (state) {
        case NONE:
            arrow.clearAnimation();
            topPadding(-headerHeight);
            break;
        case PULL:
            arrow.setVisibility(View.VISIBLE);
            progressBar.setVisibility(GONE);
            arrow.clearAnimation();
            arrow.setAnimation(animation2);
            tip.setText("下拉可以刷新");
            break;
        case RELEASE:
            arrow.setVisibility(View.VISIBLE);
            progressBar.setVisibility(GONE);
            arrow.clearAnimation();
            arrow.setAnimation(animation);
            tip.setText("松开可以刷新");
            break;
        case REFRESHING:
            topPadding(50);
            arrow.clearAnimation();
            arrow.setVisibility(View.GONE);
            progressBar.setVisibility(VISIBLE);
            tip.setText("正在刷新");
            break;

        default:
            break;
        }
    }

    /**
     * 获取完数据
     */
    public void reflashComplete() {
        state = NONE;
        isRemark = false;
        reflashViewByState();
        TextView lastTimeReflash = (TextView) header
                .findViewById(R.id.lastrefresh_time);
        SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
        Date date = new Date(System.currentTimeMillis());
        String timeString = format.format(date);
        lastTimeReflash.setText(timeString);
    }

    /**
     * 底部加载完毕
     */
    public void reflashFooterComplete() {
        isLoading=false;
        footer.setVisibility(GONE);
    }

    /**
     * 刷新数据接口
     * 
     * @author nickming
     *
     */
    public interface OnReflashListener {
        public void onrReflash();
    }

    public OnReflashListener mListener;// 刷新数据的接口

    public void setOnReflashListener(OnReflashListener listener) {
        mListener = listener;
    }

    /**
     * 加载更多接口
     * @author nickming
     *
     */
    public interface OnReflashMoreListener{
        public void onReflashMore();
    }

    public OnReflashMoreListener mListener2;

    public void  setOnReflashMoreListener(OnReflashMoreListener listener) {
        mListener2=listener;
    }

}


MainActivity.class

package com.example.listview_pulltorefresh;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

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

import com.example.listview_pulltorefresh.RefreshListview.OnReflashListener;
import com.example.listview_pulltorefresh.RefreshListview.OnReflashMoreListener;

public class MainActivity extends Activity {
    private RefreshListview mlistview;
    MyAdapter adapter;
    List<DataBean> mdata;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mlistview = (RefreshListview) findViewById(R.id.listview);
        mdata = new ArrayList<DataBean>();
        for (int i = 0; i < 20; i++) {
            DataBean dataBean = new DataBean();
            dataBean.setTextString("数据列:" + i);
            mdata.add(dataBean);
        }
        adapter = new MyAdapter(this, mdata);
        mlistview.setAdapter(adapter);
        mlistview.setOnReflashListener(new OnReflashListener() {

            @Override
            public void onrReflash() {
                // TODO Auto-generated method stub
                // 模拟网络延时
                Handler mHandler = new Handler();
                mHandler.postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        // 获取最新数据
                        addHeaderData();
                        // 通知布局显示
                        adapter.notifyDataSetChanged();
                        // listview刷新
                        mlistview.reflashComplete();
                    }
                }, 3000);

            }
        });

        mlistview.setOnReflashMoreListener(new OnReflashMoreListener() {

            @Override
            public void onReflashMore() {
                // TODO Auto-generated method stub
                Handler mHandler=new Handler();
                mHandler.postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        addFooterData();
                        adapter.notifyDataSetChanged();
                        mlistview.reflashFooterComplete();

                    }
                }, 2000);
            }
        });
    }

    public void addHeaderData() {
        for (int i = 0; i < 10; i++) {
            DataBean dataBean = new DataBean();
            String nameString = UUID.randomUUID().toString();
            dataBean.setTextString("最新数据:" + nameString);
            mdata.add(0, dataBean);// 放在最前面
        }
    }

    public void addFooterData() {
        for (int i = 0; i < 10; i++) {
            DataBean dataBean = new DataBean();
            String nameString = UUID.randomUUID().toString();
            dataBean.setTextString("最新数据:" + nameString);
            mdata.add( dataBean);// 放在最后面
        }
    }
}

adapter

package com.example.listview_pulltorefresh;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class MyAdapter extends BaseAdapter {

    private Context mcontext;
    private List<DataBean> mData;
    LayoutInflater inflater;

    public MyAdapter(Context context, List<DataBean> mData) {
        super();
        this.mcontext = context;
        this.mData = mData;
        inflater=LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return mData.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        viewHolder holder=null;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.listitem, parent,false);
            holder = new viewHolder();
            holder.textView = (TextView) convertView
                    .findViewById(R.id.textView1);
            convertView.setTag(holder);

        }else {
            holder=(viewHolder) convertView.getTag();
        }
        holder.textView.setText(mData.get(position).getTextString());

        return convertView;
    }

    class viewHolder {
        TextView textView;
    }

}

DataBean.class

package com.example.listview_pulltorefresh;

import android.widget.TextView;

public class DataBean {

    String textString;

    public DataBean() {
        // TODO Auto-generated constructor stub
    }

    public DataBean(String textString) {
        super();
        this.textString = textString;
    }

    public String getTextString() {
        return textString;
    }

    public void setTextString(String textString) {
        this.textString = textString;
    }

}

基本上没什么问题了,其实实现还是很简单的,不过就是要多多练习。


标签: android

热门推荐