美文网首页
Android 用RecylerView实现列表索引实践

Android 用RecylerView实现列表索引实践

作者: 木木禾木 | 来源:发表于2019-08-16 17:45 被阅读0次

    android中经常用到列表索引(如联系人列表、城市列表等)

    列表索引
    通常都是通过自定义View来实现:
    void onDraw(Canvas canvas)中画出索引,在boolean onTouchEvent(MotionEvent event)中获取到触摸点,然后计算索引位置(对于复杂索引,位置计算真是要人老命啊)。

    这种方式在索引列表复杂的情况下(如索引间增加间隔、索引触摸时字体放大、... 等),实现起来真的让我焦头烂额🤷‍♂️

    于是,就想通过RecylerView来实现列表索引,毕竟RecylerView的ItemView可以千变万化,更易于控制。

    说干就干!

    1. 首先,继承与RecyclerView
    public class MIndexRecyclerView extends RecyclerView {
    
        public MIndexRecyclerView(Context context) {
            this(context, null);
        }
    
        public MIndexRecyclerView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MIndexRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context, attrs);
        }
    
        private void init(Context context, AttributeSet attrs) {
    
        }
    
    }
    
    1. 其次,init()方法中设置Adapter
            setAdapter(new RecyclerView.Adapter<RecyclerViewHolder>() {
                @Override
                public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int pos) {
                    //TextView textView = holder.view(R.id.m_recycler_index_item_text);
                    TextView textView = (TextView) holder.itemView;
                    textView.setText(mDataList.get(pos));
                    textView.setGravity(mIndexGravity);
                    if (mTouchIndex == pos) {
                        textView.setTextColor(mIndexSelectColor);
                    } else {
                        textView.setTextColor(mIndexNormalColor);
                    }
                    if (mIndexSelectSize != mIndexNormalSize) {
                        //触摸点文字放大效果
                        if (mTouchIndex == pos) {
                            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIndexSelectSize);
                        } else if (Math.abs(mTouchIndex - pos) == 1) {
                            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIndexNormalSize + (mIndexSelectSize - mIndexNormalSize) / 2);
                        } else {
                            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIndexNormalSize);
                        }
                    } else {
                        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIndexNormalSize);
                    }
                }
    
                @NonNull
                @Override
                public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
                    TextView itemView = new TextView(viewGroup.getContext());
                    itemView.setLayoutParams(new LinearLayoutCompat.LayoutParams(LinearLayoutCompat.LayoutParams.MATCH_PARENT,
                            LinearLayoutCompat.LayoutParams.WRAP_CONTENT));
                    itemView.setPadding(8, 0, 8, 0);
                    return new RecyclerViewHolder(viewGroup, itemView);
    //                return new RecyclerViewHolder(viewGroup, R.layout.m_recycler_index_item);
                }
    
                @Override
                public int getItemCount() {
                    return mDataList.size();
                }
            });
    
    1. 然后,init()方法中为RecyclerView添加OnItemTouchListener
            //设置触摸事件
            addOnItemTouchListener(new OnItemTouchListener() {
                @Override
                public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) {
                    //拦截触摸事件
                    return true;
                }
    
                @Override
                public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                        case MotionEvent.ACTION_MOVE:
                            float x = event.getX();
                            //点击边界限制
                            //if (x < 0 || x > getWidth()) {
                            //    x = getWidth() / 2f;
                            //}
                            //寻找触摸点对应的view的位置
                            View childView = findChildViewUnder(x, event.getY());
                            if (childView != null) {
                                refreshView(getChildLayoutPosition(childView), x, event.getY());
                            }
                            break;
                        default:
                            break;
                    }
                }
    
                @Override
                public void onRequestDisallowInterceptTouchEvent(boolean b) {
    
                }
            });
    
    1. 哦...没有4了~至此,已经借助RecyclerView实现了列表索引。

    当然,以上实现的一个简单的列表索引。不过,RecyclerView易于扩展索引样式,触摸位置很明确,再复杂的列表索引都不在话下了。

    附上完整代码如下:

    package com.test.wrapper.indexList;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.support.v7.widget.DefaultItemAnimator;
    import android.support.v7.widget.LinearLayoutCompat;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.Gravity;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    import com.test.wrapper.R;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MIndexRecyclerView extends RecyclerView {
    
        public MIndexRecyclerView(Context context) {
            this(context, null);
        }
    
        public MIndexRecyclerView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MIndexRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context, attrs);
        }
    
        /**
         * 正常字体大小
         */
        private float mIndexNormalSize;
        /**
         * 触摸时字体大小
         */
        private float mIndexSelectSize;
        /**
         * 正常字体颜色
         */
        private int mIndexNormalColor = 0xff333333;
        /**
         * 触摸时字体颜色
         */
        private int mIndexSelectColor = 0xff00cd00;
        /**
         * 文字位置
         */
        private int mIndexGravity = Gravity.CENTER;
        /**
         * 触摸的索引位置
         */
        private int mTouchIndex = 0;
        /**
         * 触摸的索引文字
         */
        private String mTouchText = "";
        /**
         * 触摸回调
         */
        private OnIndexTouchedListener mOnIndexTouchedListener;
        /**
         * 索引数据列表
         */
        private List<String> mDataList = new ArrayList<>();
    
        private void init(Context context, AttributeSet attrs) {
            //获取属性
            if (attrs != null) {
                TypedArray typed = context.obtainStyledAttributes(attrs, R.styleable.MIndexRecyclerView);
                if (typed != null) {
                    mIndexNormalColor = typed.getColor(R.styleable.MIndexRecyclerView_indexNormalTextColor, mIndexNormalColor);
                    mIndexSelectColor = typed.getColor(R.styleable.MIndexRecyclerView_indexSelectTextColor, mIndexSelectColor);
                    mIndexNormalSize = typed.getDimension(R.styleable.MIndexRecyclerView_indexNormalTextSize, mIndexNormalSize);
                    mIndexSelectSize = typed.getDimension(R.styleable.MIndexRecyclerView_indexSelectTextSize, mIndexSelectSize);
                    mIndexGravity = typed.getInteger(R.styleable.MIndexRecyclerView_android_gravity, mIndexGravity);
                    typed.recycle();
                }
            }
            if (mIndexNormalSize == 0) {
                mIndexNormalSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14f, getResources().getDisplayMetrics());
            }
            if (mIndexSelectSize == 0) {
                mIndexSelectSize = mIndexNormalSize;
            }
    
            //设置RecyclerView
            setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
            setItemAnimator(new DefaultItemAnimator());
            setAdapter(new RecyclerView.Adapter<RecyclerViewHolder>() {
                @Override
                public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int pos) {
                    //TextView textView = holder.view(R.id.m_recycler_index_item_text);
                    TextView textView = (TextView) holder.itemView;
                    textView.setText(mDataList.get(pos));
                    textView.setGravity(mIndexGravity);
                    if (mTouchIndex == pos) {
                        textView.setTextColor(mIndexSelectColor);
                    } else {
                        textView.setTextColor(mIndexNormalColor);
                    }
                    if (mIndexSelectSize != mIndexNormalSize) {
                        //触摸点文字放大效果
                        if (mTouchIndex == pos) {
                            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIndexSelectSize);
                        } else if (Math.abs(mTouchIndex - pos) == 1) {
                            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIndexNormalSize + (mIndexSelectSize - mIndexNormalSize) / 2);
                        } else {
                            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIndexNormalSize);
                        }
                    } else {
                        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIndexNormalSize);
                    }
                }
    
                @NonNull
                @Override
                public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
                    TextView itemView = new TextView(viewGroup.getContext());
                    itemView.setLayoutParams(new LinearLayoutCompat.LayoutParams(LinearLayoutCompat.LayoutParams.MATCH_PARENT,
                            LinearLayoutCompat.LayoutParams.WRAP_CONTENT));
                    itemView.setPadding(8, 0, 8, 0);
                    return new RecyclerViewHolder(viewGroup, itemView);
    //                return new RecyclerViewHolder(viewGroup, R.layout.m_recycler_index_item);
                }
    
                @Override
                public int getItemCount() {
                    return mDataList.size();
                }
            });
    
            //设置触摸事件
            addOnItemTouchListener(new OnItemTouchListener() {
                @Override
                public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) {
                    //拦截触摸事件
                    return true;
                }
    
                @Override
                public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                        case MotionEvent.ACTION_MOVE:
                            float x = event.getX();
                            //点击边界限制
                            //if (x < 0 || x > getWidth()) {
                            //    x = getWidth() / 2f;
                            //}
                            //寻找触摸点对应的view的位置
                            View childView = findChildViewUnder(x, event.getY());
                            if (childView != null) {
                                refreshView(getChildLayoutPosition(childView), x, event.getY());
                            }
                            break;
                        default:
                            break;
                    }
                }
    
                @Override
                public void onRequestDisallowInterceptTouchEvent(boolean b) {
    
                }
            });
        }
    
        private void refreshView(int pos, float touchX, float touchY) {
            if (mTouchIndex == pos) {
                return;
            }
            mTouchIndex = pos;
            if (getAdapter() != null) {
                getAdapter().notifyDataSetChanged();
            }
            if (mOnIndexTouchedListener != null) {
                if (pos >= 0 && pos < mDataList.size()) {
                    mTouchText = mDataList.get(pos);
                }
                mOnIndexTouchedListener.onTouched(pos, mTouchText, touchX, touchY);
            }
        }
    
        /**
         * 列表滑动时,设置选中某个索引
         *
         * @param str 索引
         */
        public void setSelected(String str) {
            if (!TextUtils.isEmpty(str)) {
                int index = mDataList.indexOf(str);
                if (index >= 0) {
                    mTouchIndex = index;
                    mTouchText = str;
                    if (getAdapter() != null) {
                        getAdapter().notifyDataSetChanged();
                    }
                }
            }
        }
    
        public void setData(List<String> dataList) {
            mDataList.clear();
            mDataList.addAll(dataList);
            if (getAdapter() != null) {
                getAdapter().notifyDataSetChanged();
            }
        }
    
        public void setOnIndexTouchedListener(OnIndexTouchedListener onIndexTouchedListener) {
            this.mOnIndexTouchedListener = onIndexTouchedListener;
        }
    
        /**
         * 触摸回调
         */
        interface OnIndexTouchedListener {
            void onTouched(int position, String text, float touchX, float touchY);
        }
    
    }
    
    

    自定义属性:

        <declare-styleable name="MIndexRecyclerView">
            <attr name="indexNormalTextColor" format="reference|color" />
            <attr name="indexSelectTextColor" format="reference|color" />
            <attr name="indexNormalTextSize" format="reference|dimension" />
            <attr name="indexSelectTextSize" format="reference|dimension" />
            <attr name="android:gravity" />
        </declare-styleable>
    

    使用:
    xml中布局:

        <com.test.wrapper.indexList.MIndexRecyclerView
            android:id="@+id/mIndexRecyclerView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right|center_vertical"
            app:indexNormalTextSize="12sp"
            app:indexSelectTextSize="30sp"
            app:indexNormalTextColor="#666666"
            app:indexSelectTextColor="#00cd00"/>
    

    java | kotlin 设置索引列表和回调:

            mIndexRecyclerView.setData(mBarList)
            mIndexRecyclerView.setOnIndexTouchedListener { position, text, touchX, touchY ->
                System.out.println("*********** $position ****** $text ****** $touchX ****** $touchY")
            }
    

    over ~
    欢迎大家踊跃留言交流哈 ·_·

    相关文章

      网友评论

          本文标题:Android 用RecylerView实现列表索引实践

          本文链接:https://www.haomeiwen.com/subject/zayqsctx.html