Android 用RecylerView实现列表索引实践

2019-08-16  本文已影响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 ~
欢迎大家踊跃留言交流哈 ·_·

上一篇 下一篇

猜你喜欢

热点阅读