Android 为ListView上每个item上的按钮添加监听遇坑!

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Wengwuhua/article/details/84674870

背景

这周有一个新的需求,需要在原本项目的基础上修改,需要在原本的listView每个item上加一个按钮,监听这个按钮的点击事件做出响应。一想这很简单呀,于是马上开干,实现思路如下:在adapter中为每个item上的按钮添加监听,并在adapter中提供接口回调,在界面fragment中响应点击回调事件。

实现(伪代码)

adapter实现代码

public class MyAdapter extends BaseAdapter {

    private Context mContext;
    private List<String> datas;

    public MyAdapter(Context mContext, List<String> datas) {
        this.mContext = mContext;
        this.datas = datas;
    }

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

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = View.inflate(mContext,   			 R.layout.adapter_module_tab_test);
            holder = new ViewHolder();
            holder.btn = (Button) convertView.findViewById(R.btn);
            holder.tvTip = (TextView) convertView.findViewById(R.id.tvTip);
            
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.tvTip = datas.get(position);
        holder.btn.setTag(position);   //记录按钮所在item的position
				      
        holder.btn.setOnClickListener(MyOnClickListener.getInstance());
        return convertView;
    }


    public interface ButtonClickListener{
        void clickBtn(int position);
    }
    private static ButtonClickListener buttonClickListener;
    public void setButtonClickListener(ButtonClickListener buttonClickListener){
        this.buttonClickListener = buttonClickListener;
    }
  //用单例实现监听,避免在数据多时new太多监听者
    static class MyOnClickListener implements View.OnClickListener {
        private static MyOnClickListener instance = null;
        private MyOnClickListener() {}
        public static MyOnClickListener getInstance() {
            if (instance == null){
                instance = new MyOnClickListener();
            }
            return instance;
        }
        @Override
        public void onClick(View v) {
            int position = (Integer) ((Button) v).getTag();
            int id = v.getId();
            if(id == R.id.btn){
                ABLLog.i("item 上的按钮", "clickBtn " + position);
                buttonClickListener.clickBtn(position);
            }
        }
    }

    static class ViewHolder {
        Button btn;
        TextView tvTip;
    }
}

在fragment中接收按钮监听回调

adapter.setButtonClickListener(new MyAdapter.ButtonClickListener() {
                @Override
                public void clickBtn(int position) {
                   showDialog(position);
                }
            });

fragment布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.wu.views.refresh.SwipeRefreshView
        android:id="@+id/srv_refreshview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ListView
            android:id="@+id/lv_ks"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="@color/color_divider_line"
            android:layout_weight="1"
            android:drawSelectorOnTop="false"/>
    </com.wu.views.refresh.SwipeRefreshView>-->
  <LinearLayout/>  

SwipeRefreshView是自定义实现的上拉加载更多控件,继承自自带的下拉刷新布局SwipeRefreshLayout(用法和实现在下一篇博客)
问题就出在了listview外面包的这个上拉刷新控件。

问题描述

完成后下载到设备上,点了一下按钮没反应,很意外~马上查看了下代码发现没什么问题呀,然后滑动了一下list view正常。点击最上面第一个item上的button,能响应,再点第二个没反应,第三个没反应,见鬼了。
在仔细试了下,发现刚进入list view的节目时点击按钮是没反应的,必须先滑动一下再点击按钮,而且只有处于可见界面第一个item上的按钮的点击事件才有响应!往上滑动,处在第一个的item上的按钮就能响应,当滑动底没有更多数据的时候,在最后一个listview界面上的所有item的按钮又都可以响应了。
但item的点击事件却是可以响应的(设置了butting的focusable为false了),所以一开始想是不是点击事件被listview 拦截了,但是第一个item上的按钮是能响应的呀,所以应该不会是listView拦截(不过我还是试了一下让listview不拦截,但还是一样没用)。
后面发现可能是listview外面嵌了SwipeRefreshView的原因(因为之前在没有SwipeRefreshView的时候只有写按钮点击事件都是可以正常响应的)。试了一下去掉SwipeRefreshView(不能上拉加载更多了),果然都可以正常点击响应了。
至于原因暂时还没找到。。。希望有高手看到知道的话告知一声,感谢!SwipeRefreshView代码在最后。

解决办法

找了很久问题原因始终没找到,又由于项目的需要,没办法,只能暂时去掉SwipeRefreshView这一层,但是又要实现上拉加载更多(分页加载)
没办法,只能先用自定义listView继承ListView的方法来实现了。

public class LoadListView extends ListView implements AbsListView.OnScrollListener{

   // View footer;//底部布局
    int totalItemCount;//总数量
    int lastVisibleItem;//最后一个可见的Item
    boolean isLoading;//判断是否正在加载
    private IloadInterface mIloadInterface;
    private boolean isLasePage = false;

    public LoadListView(Context context) {
        super(context);
        initView(context);
    }

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

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public LoadListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView(context);
    }

    /**
     * 添加底部加载布局到ListView中
     *
     * @param context 上下文对象
     */
    private void initView(Context context) {

        //不用底部加载提示
      //  LayoutInflater inflater = LayoutInflater.from(context);
      //  footer = inflater.inflate(R.layout.pad_view_costom_footer_layout, null);
        //第一次进来下面正在加载是隐藏的
      //  footer.findViewById(R.id.load_layout).setVisibility(View.GONE);
      //  this.addFooterView(footer);
        this.setOnScrollListener(this);

    }

    //加载更多数据的回调方法
    public interface IloadInterface {
        void onLoad();
    }
    public void setLoadListener(IloadInterface mIloadInterface){
        this.mIloadInterface = mIloadInterface;
    }

    /**
     * 加载完毕
     */
    public void loadComplete() {
        isLoading = false;
      //  footer.findViewById(R.id.load_layout).setVisibility(View.GONE);
    }

    public void isLastPage(boolean isLasePage){
        this.isLasePage = isLasePage;
    }
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (this.totalItemCount == lastVisibleItem &&
                scrollState == SCROLL_STATE_IDLE) {   //重点,判断是否滑到了底部!
            if (!isLoading && (!isLasePage)) {
                isLoading = true;
          //      footer.findViewById(R.id.load_layout).setVisibility(View.VISIBLE);
                //加载更多数据
                mIloadInterface.onLoad();
            }

        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        this.lastVisibleItem = visibleItemCount + firstVisibleItem;    //当前最后一个position
        this.totalItemCount = totalItemCount;     //当前已加载所有position总数
    }
}

附上自定义SwipeRefreshView代码

public class SwipeRefreshView extends SwipeRefreshLayout {

    private final int mScaledTouchSlop;
    private final View mFooterView;
    private ListView mListView;
    private OnLoadListener mOnLoadListener;

    /**
     * 正在加载状态
     */
    private boolean isLoading;

    private boolean enabledHeaderRefresh = true;
    private boolean enabledFooterRefresh = true;

    public boolean isEnabledHeaderRefresh() {
        return enabledHeaderRefresh;
    }

    public void setEnabledHeaderRefresh(boolean enabledHeaderRefresh) {
        this.enabledHeaderRefresh = enabledHeaderRefresh;
    }

    public boolean isEnabledFooterRefresh() {
        return enabledFooterRefresh;
    }

    public void setEnabledFooterRefresh(boolean enabledFooterRefresh) {
        this.enabledFooterRefresh = enabledFooterRefresh;

        if (!this.enabledFooterRefresh) {
            if (mListView != null && mFooterView != null) {
                mListView.removeView(mFooterView);
                setLoading(false);
            }
        }
    }

    public SwipeRefreshView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 填充底部加载布局
        mFooterView = View.inflate(context, ABLCommonUtil.getLayoutResourceId(ABLApplication.getApplication().getAppDeviceTypePrefix(), "view_base_footer"), null);

        // 表示控件移动的最小距离,手移动的距离大于这个距离才能拖动控件
        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
//        System.out.println("====" + mScaledTouchSlop);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        // 获取ListView,设置ListView的布局位置
        if (mListView == null) {
            // 判断容器有多少个孩子
            if (getChildCount() > 0) {
                // 判断第一个孩子是不是ListView
                if (getChildAt(0) instanceof ListView ) {
                    // 创建ListView对象
                    mListView = (ListView) getChildAt(0);
                    // 设置ListView的滑动监听
                    setListViewOnScroll();
                }
            }
        }
    }

    /**
     * 在分发事件的时候处理子控件的触摸事件
     *
     * @param ev
     * @return
     */
    private float mDownY, mUpY;

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (!this.enabledFooterRefresh) {
            return super.dispatchTouchEvent(ev);
        }
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 移动的起点
                mDownY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 移动过程中判断时候能下拉加载更多
                if (enabledFooterRefresh) {
                    if (canLoadMore()) {
                        // 加载数据
                        loadData();
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
                // 移动的终点
                mUpY = getY();
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    /**
     * 判断是否满足加载更多条件
     *
     * @return
     */
    private boolean canLoadMore() {
        if (!this.enabledFooterRefresh) {
            return false;
        }
        // 1. 是上拉状态
        boolean condition1 = (mDownY - mUpY) >= mScaledTouchSlop;
        if (condition1) {
            System.out.println("是上拉状态");
        }

        // 2. 当前页面可见的item是最后一个条目
        boolean condition2 = false;
        if (mListView != null && mListView.getAdapter() != null) {
            condition2 = mListView.getLastVisiblePosition() >= (mListView.getAdapter().getCount() - 1);
        }

        if (condition2) {
            System.out.println("是最后一个条目");
        }
        // 3. 正在加载状态
        boolean condition3 = !isLoading;
        if (condition3) {
            System.out.println("不是正在加载状态");
        }
        return condition1 && !condition2 && condition3;
    }

    /**
     * 处理加载数据的逻辑
     */
    private void loadData() {
        if (!this.enabledFooterRefresh) {
            return;
        }
        System.out.println("加载数据...");
        if (mOnLoadListener != null) {
            // 设置加载状态,让布局显示出来
            setLoading(true);
            mOnLoadListener.onLoad();
        }

    }

    /**
     * 设置加载状态,是否加载传入boolean值进行判断
     *
     * @param loading
     */
    public void setLoading(boolean loading) {
        if (!this.enabledFooterRefresh) {
            return;
        }
        // 修改当前的状态
        isLoading = loading;
        if (isLoading) {
            // 显示布局
            if (mListView != null && mFooterView != null) {
                mListView.addFooterView(mFooterView);
            }
        } else {
            // 隐藏布局
            if (mListView != null && mFooterView != null) {
                mListView.removeFooterView(mFooterView);
            }

            // 重置滑动的坐标
            mDownY = 0;
            mUpY = 0;
        }
    }


    /**
     * 设置ListView的滑动监听
     */
    private void setListViewOnScroll() {

        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // 移动过程中判断时候能下拉加载更多
                if (canLoadMore()) {
                    // 加载数据
                    loadData();
                }
            }

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

            }
        });
    }


    /**
     * 上拉加载的接口回调
     */

    public interface OnLoadListener {
        void onLoad();
    }

    public void setOnLoadListener(OnLoadListener listener) {
        this.mOnLoadListener = listener;
    }
}

猜你喜欢

转载自blog.csdn.net/Wengwuhua/article/details/84674870