ListView全解析

2019-07-07  本文已影响0人  fastcv

前言

ListView作为一个基本过去式的组件,作用就不在这里再说了,直接开始我们的总结。

常用属性

属性 说明
android:listSelector="@color/pink" listView item 选中时的颜色
android:divider="#f9b68b" 分割线颜色(android:divider="@drawable/@null" 不想显示分割线)
android:dividerHeight="1dp" 分割线边距
android:scrollbars="none" 不显示滚动条
android:fadingEdge="none" 去掉上边和下边黑色的阴影
android:transcriptMode="alwaysScroll" 通过设置的控件transcriptMode属性可以将Android平台的控件(支持ScrollBar)自动滑动到最底部。

使用

废话不多说,先来效果图:


listview.gif

简单的使用教程网上有许多,现在我们实现的是自定义的Listview(下拉刷新和上拉加载),适配器使用自定义的万能Listview适配器,分两步进行:

1、自定义Listview

1、继承Listview,重写构造方法

public class RefreshListView extends ListView {


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

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

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

2、初始化HeaderView和FooterView

ListView提供两个方法addFooterView和addHeaderView去添加头View和尾部View

private void initFooterView() {
        this.setOnScrollListener(this);
        mFooterView = View.inflate(getContext(), R.layout.refresh_listview_footer, null);
        this.addFooterView(mFooterView);
        tv_pull_list_header_title = mFooterView.findViewById(R.id.tv_pull_list_header_title);
        pb_pull_list_header = mFooterView.findViewById(R.id.pb_pull_list_header);
        mFooterView.measure(0,0);
        mFooterViewHeight = mFooterView.getMeasuredHeight();   //得到FooterView的高度
        mFooterView.setPadding(0,0,0,0-mFooterViewHeight);   //隐藏FooterView
    }

    private void initHeaderView() {
        mHearder = View.inflate(getContext(), R.layout.refresh_listview_header, null);
        this.addHeaderView(mHearder);
        refresh_tips = mHearder.findViewById(R.id.refresh_tips);
        refresh_last_time = mHearder.findViewById(R.id.refresh_last_time);
        ivArrow = mHearder.findViewById(R.id.ivArrow);
        pbWaiting = mHearder.findViewById(R.id.pbWaiting);
        mHearder.measure(0,0);
        mHeaderHeight = mHearder.getMeasuredHeight();   //得到HeaderView的高度
        mHearder.setPadding(0, 0-mHeaderHeight,0,0);    //隐藏HeaderView
    }

3、重写onTouchEvent方法监听HeaderView的状态

这里的逻辑是,拿到点击时的y坐标,在移动的时候去得到偏移量,然后和我们我HeaderView的高度进行运算得到padding,这个值即为我们HeaderView需要隐藏的高度

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction())
        {
            case MotionEvent.ACTION_DOWN:
            {
                if (valueAnimator.isRunning())
                    valueAnimator.end();
                startY = (int) ev.getY();
                Log.e("SayHallo","MotionEvent.ACTION_DOWN startY = " + startY);
            }
            break;
            case MotionEvent.ACTION_MOVE:
            {
                Log.e("SayHallo","MotionEvent.ACTION_MOVE");
                if (startY == -1) {// 确保startY有效
                    startY = (int) ev.getY();
                }

                if(mCurrrentState == LoadState.STATE_REFRESHING) break;

                endY = (int) ev.getY();
                moveY = px2dp(endY - startY,getContext());

                if( moveY > 0 && getFirstVisiblePosition() == 0){
                    // 计算padding
                    padding = moveY-mHeaderHeight;
                    if(padding < 1)  //预留位置
                      mHearder.setPadding(0,padding,0,0);

                    if (padding > 0 && mCurrrentState != LoadState.STATE_RELEASE_REFRESH )
                    {
                        mCurrrentState = LoadState.STATE_RELEASE_REFRESH;
                        refreshState();
                    }else if (padding < 0 && mCurrrentState != LoadState.STATE_PULL_REFRESH) {// 改为下拉刷新状态
                        mCurrrentState = LoadState.STATE_PULL_REFRESH;
                        refreshState();
                    }
                    return true;
                }


            }
            break;
            case MotionEvent.ACTION_UP:
            {
                Log.e("SayHallo","padding = " + padding);
                if (mCurrrentState == LoadState.STATE_RELEASE_REFRESH)
                {

                    mCurrrentState = LoadState.STATE_REFRESHING;
                    mHearder.setPadding(0,0,0,0);
                    refreshState();
                }else if (mCurrrentState == LoadState.STATE_PULL_REFRESH){
                    if (padding != 0)
                       scroll_Start(padding);
                }

                startY = -1;   //重置
                padding = 0;

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

4、实现onScrollStateChanged方法监听FooterView的状态

@Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        Log.e("SayHallo","到底了.....");
        if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
                || scrollState == OnScrollListener.SCROLL_STATE_FLING) {

            if (getLastVisiblePosition() == getCount() - 1 && !isLoadingMore) {// 滑动到最后
                Log.e("SayHallo","到底了.....");
//                mFooterView.setPadding(0, 0, 0, 0);// 显示
                setSelection(getCount() - 1);// 改变listview显示位置

                isLoadingMore = true;

                if (mListener != null) {
                    mListener.onLoadMore();
                }
            }
        }
    }

完整的代码如下:

public class RefreshListView extends ListView implements AbsListView.OnScrollListener {

    //头View中的一些组件
    private View mHearder;
    private TextView refresh_tips;   //显示文字(刷新状态)
    private TextView refresh_last_time;   //显示时间(刷新完成)
    private ImageView ivArrow;   //指向图
    private ProgressBar pbWaiting;   //刷新的圈圈
    private int mHeaderHeight;   //头View的测量高度

    //脚View中的一些组件
    private View mFooterView;
    private TextView tv_pull_list_header_title;   //显示文字(加载状态)
    private ProgressBar pb_pull_list_header;    //刷新的圈圈
    private int mFooterViewHeight;   //脚View的测量高度

    private int startY = -1;   //按下时的Y坐标(相对于屏幕的绝对坐标)
    private LoadState mCurrrentState = LoadState.STATE_PULL_REFRESH;
    private int endY;
    private int moveY;
    private ValueAnimator valueAnimator;
    private int padding = 0;

    private int lastEndY = 0;

    public RefreshListView(Context context) {
        super(context);
        initHeaderView();
        initFooterView();
        initArrowAnim();
    }

    public RefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initHeaderView();
        initFooterView();
        initArrowAnim();
    }

    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initHeaderView();
        initFooterView();
        initArrowAnim();
    }


    private void initFooterView() {
        this.setOnScrollListener(this);
        mFooterView = View.inflate(getContext(), R.layout.refresh_listview_footer, null);
        this.addFooterView(mFooterView);
        tv_pull_list_header_title = mFooterView.findViewById(R.id.tv_pull_list_header_title);
        pb_pull_list_header = mFooterView.findViewById(R.id.pb_pull_list_header);
        mFooterView.measure(0,0);
        mFooterViewHeight = mFooterView.getMeasuredHeight();
        mFooterView.setPadding(0,0,0,0-mFooterViewHeight);
    }

    private void initHeaderView() {
        mHearder = View.inflate(getContext(), R.layout.refresh_listview_header, null);
        this.addHeaderView(mHearder);
        refresh_tips = mHearder.findViewById(R.id.refresh_tips);
        refresh_last_time = mHearder.findViewById(R.id.refresh_last_time);
        ivArrow = mHearder.findViewById(R.id.ivArrow);
        pbWaiting = mHearder.findViewById(R.id.pbWaiting);
        mHearder.measure(0,0);
        mHeaderHeight = mHearder.getMeasuredHeight();
        mHearder.setPadding(0, 0-mHeaderHeight,0,0);
    }

    private void initArrowAnim() {
        valueAnimator = ValueAnimator.ofInt(0, 0);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction())
        {
            case MotionEvent.ACTION_DOWN:
            {
                if (valueAnimator.isRunning())
                    valueAnimator.end();
                startY = (int) ev.getY();
                Log.e("SayHallo","MotionEvent.ACTION_DOWN startY = " + startY);
            }
            break;
            case MotionEvent.ACTION_MOVE:
            {
                Log.e("SayHallo","MotionEvent.ACTION_MOVE");
                if (startY == -1) {// 确保startY有效
                    startY = (int) ev.getY();
                }

                if(mCurrrentState == LoadState.STATE_REFRESHING) break;

                endY = (int) ev.getY();
                moveY = px2dp(endY - startY,getContext());

                if( moveY > 0 && getFirstVisiblePosition() == 0){
                    // 计算padding
                    padding = moveY-mHeaderHeight;
                    if(padding < 1)  //预留位置
                      mHearder.setPadding(0,padding,0,0);

                    if (padding > 0 && mCurrrentState != LoadState.STATE_RELEASE_REFRESH )
                    {
                        mCurrrentState = LoadState.STATE_RELEASE_REFRESH;
                        refreshState();
                    }else if (padding < 0 && mCurrrentState != LoadState.STATE_PULL_REFRESH) {// 改为下拉刷新状态
                        mCurrrentState = LoadState.STATE_PULL_REFRESH;
                        refreshState();
                    }
                    return true;
                }


            }
            break;
            case MotionEvent.ACTION_UP:
            {
                Log.e("SayHallo","padding = " + padding);
                if (mCurrrentState == LoadState.STATE_RELEASE_REFRESH)
                {

                    mCurrrentState = LoadState.STATE_REFRESHING;
                    mHearder.setPadding(0,0,0,0);
                    refreshState();
                }else if (mCurrrentState == LoadState.STATE_PULL_REFRESH){
                    if (padding != 0)
                       scroll_Start(padding);
                }

                startY = -1;   //重置
                padding = 0;

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

    //px转化为dp
    public static int px2dp(float pxValue, Context activity) {
        final float scale = activity.getResources().getDisplayMetrics().density;
        return (int) ((pxValue - 0.5f) / scale);
    }

    /**
     * 刷新下拉控件的布局
     */
    private void refreshState() {
        switch (mCurrrentState) {
            case STATE_PULL_REFRESH:
                refresh_tips.setText("下拉刷新");
                ivArrow.setVisibility(View.VISIBLE);
                pbWaiting.setVisibility(View.INVISIBLE);
//                ivArrow.startAnimation(animDown);
                break;
            case STATE_RELEASE_REFRESH:
                refresh_tips.setText("松开刷新");
                ivArrow.setVisibility(View.VISIBLE);
                pbWaiting.setVisibility(View.INVISIBLE);
//                ivArrow.startAnimation(animUp);
                break;
            case STATE_REFRESHING:
                refresh_tips.setText("正在刷新...");
                ivArrow.clearAnimation();// 必须先清除动画,才能隐藏
                ivArrow.setVisibility(View.INVISIBLE);
                pbWaiting.setVisibility(View.VISIBLE);

                if (mListener != null) {
                    mListener.onRefresh();
                }
                break;

            default:
                break;
        }
    }

    OnRefreshListener mListener;
    private boolean isLoadingMore;

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        Log.e("SayHallo","到底了.....");
        if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
                || scrollState == OnScrollListener.SCROLL_STATE_FLING) {

            if (getLastVisiblePosition() == getCount() - 1 && !isLoadingMore) {// 滑动到最后
                Log.e("SayHallo","到底了.....");
//                mFooterView.setPadding(0, 0, 0, 0);// 显示
                setSelection(getCount() - 1);// 改变listview显示位置

                isLoadingMore = true;

                if (mListener != null) {
                    mListener.onLoadMore();
                }
            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    }


    public interface OnRefreshListener {
         void onRefresh();
         void onLoadMore();// 加载下一页数据
    }

    public void setOnRefreshListener(OnRefreshListener listener) {
        mListener = listener;
    }

    /*
     * 收起下拉刷新的控件
     */
    public void onRefreshComplete(boolean success) {
        if (isLoadingMore) {// 正在加载更多...
            mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏脚布局
            isLoadingMore = false;
        } else {
            mCurrrentState = LoadState.STATE_PULL_REFRESH;

            ivArrow.setVisibility(View.VISIBLE);
            pbWaiting.setVisibility(View.INVISIBLE);

            scroll_Start(1);
//            mHearder.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏

            if (success) {
                refresh_tips.setText("刷新成功");
                refresh_last_time.setText("最后刷新时间:" + getCurrentTime());
            }
        }
    }

    /**
     * 获取当前时间
     */
    public String getCurrentTime() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return format.format(new Date());
    }

    private void scroll_Start(int p) {

        valueAnimator.setIntValues(p,-mHeaderHeight);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();

                mHearder.setPadding(0,value,0,0);
            }
        });
        valueAnimator.setDuration(500);
        valueAnimator.start();
    }

    enum LoadState {
        STATE_PULL_REFRESH,     // 下拉刷新
        STATE_RELEASE_REFRESH ,     // 松开刷新
        STATE_REFRESHING        //正在刷新
    }
}

自定义万能Listview适配器

1、首先,实现万能ViewHolder

public class ListViewHolder {

    private SparseArray<View> mViews;
    private int mPosition;
    private View mConvertView;
    private Context context;

    public ListViewHolder(Context context, ViewGroup parent, int position, int layoutId) {
        this.mPosition = position;
        this.context = context;
        this.mViews = new SparseArray<View>();
        mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
        mConvertView.setTag(this);

    }

    public static ListViewHolder get(Context context, View convertView,
                                 ViewGroup parent,int position, int layoutId) {

        if (convertView == null) {
            return new ListViewHolder(context, parent, position, layoutId);
        } else {
            ListViewHolder holder = (ListViewHolder) convertView.getTag();
            return holder;
        }
    }

    /**
     * 通过viewId获取控件
     */
    public <T extends View> T getView(int viewId) {

        View view = mViews.get(viewId);

        if (view == null) {
            view = mConvertView.findViewById(viewId);
            mViews.put(viewId, view);
        }

        return (T) view;

    }

    public View getConvertView() {
        return mConvertView;
    }

    /**
     * 封装setText方法,设置TextView的值
     */
    public ListViewHolder setTextViewVualue(int viewId, String text) {
        TextView tv = getView(viewId);
        tv.setText(text);
        return this;
    }

    /**
     * 封装setText方法,设置TextView的值
     */
    public ListViewHolder setImageViewVualue(int viewId, int id) {
        ImageView iv = getView(viewId);
        iv.setImageDrawable(context.getResources().getDrawable(id));
        return this;
    }
}

2、实现万能适配器

public abstract class AbsListViewAdapter<T> extends BaseAdapter {

    protected Context mContext;
    protected List<T> mDatas;
    protected LayoutInflater mInflater;
    protected int mLayoutId;

    public AbsListViewAdapter(Context context, List<T> datas, int LayoutId) {
        this.mContext = context;
        mInflater = LayoutInflater.from(context);
        this.mLayoutId = LayoutId;
        this.mDatas = datas;
    }

    @Override
    public int getCount() {

        return mDatas.size();
    }

    @Override
    public T getItem(int position) {

        return mDatas.get(position);
    }

    @Override
    public long getItemId(int position) {

        return position;
    }

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

        ListViewHolder holder = ListViewHolder.get(mContext, convertView,
                parent, position, mLayoutId);

        convert(holder, getItem(position));

        return holder.getConvertView();
    }

    public abstract void convert(ListViewHolder holder, T t);
}

实现Listview的显示

1、实现实体类

public class Enety{

        public Enety(String title, int icon) {
            this.title = title;
            this.icon = icon;
        }

        public String title;
        public int icon;
    }

2、实现适配器

class MyAdapter extends AbsListViewAdapter<Enety> {

        public MyAdapter(Context context, List<Enety> datas, int LayoutId) {
            super(context, datas, LayoutId);
        }

        @Override
        public void convert(ListViewHolder holder, Enety enety) {
            holder.setTextViewVualue(R.id.title, enety.title)
                    .setImageViewVualue(R.id.image, enety.icon);
        }
    }

3、实现整个效果

public class MainActivity extends AppCompatActivity {

    private RefreshListView listView;
    private MyAdapter adapter1;
    private ArrayList<Enety> eneties;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = findViewById(R.id.refreshlist);
        eneties = new ArrayList<>();
        for (int i = 0;i< 100 ; i++) {
            Enety enety = new Enety("这是标题"+i,R.mipmap.ic_launcher);
            eneties.add(enety);
        }
        adapter1 = new MyAdapter(this,eneties,R.layout.shouxinrenzhengliebiao);
        listView.setAdapter(adapter1);
        listView.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        Enety enety = new Enety("下拉新增数据",R.mipmap.ic_launcher);
                        eneties.add(0,enety);
                        adapter1.notifyDataSetChanged();
                        listView.onRefreshComplete(true);
                    }
                },3000);


            }

            @Override
            public void onLoadMore() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        Enety enety = new Enety("上拉加载数据",R.mipmap.ic_launcher);
                        eneties.add(enety);
                        adapter1.notifyDataSetChanged();
                        listView.onRefreshComplete(true);
                    }
                },500);
            }
        });

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Log.e("Say1","position = " + position);
            }
        });

        listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                Log.e("Say1","onItemLongClick position = " + position);
                return true;
            }
        });
    }

    public class Enety{
        public Enety(String title, int icon) {
            this.title = title;
            this.icon = icon;
        }

        public String title;
        public int icon;
    }


    class MyAdapter extends AbsListViewAdapter<Enety> {

        public MyAdapter(Context context, List<Enety> datas, int LayoutId) {
            super(context, datas, LayoutId);
        }

        @Override
        public void convert(ListViewHolder holder, Enety enety) {
            holder.setTextViewVualue(R.id.title, enety.title)
                    .setImageViewVualue(R.id.image, enety.icon);
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读