android中经常用到列表索引(如联系人列表、城市列表等)
通常都是通过自定义View来实现:
在
void onDraw(Canvas canvas)
中画出索引,在boolean onTouchEvent(MotionEvent event)
中获取到触摸点,然后计算索引位置(对于复杂索引,位置计算真是要人老命啊)。
这种方式在索引列表复杂的情况下(如索引间增加间隔、索引触摸时字体放大、... 等),实现起来真的让我焦头烂额🤷♂️
于是,就想通过RecylerView来实现列表索引,毕竟RecylerView的ItemView可以千变万化,更易于控制。
说干就干!
- 首先,继承与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) {
}
}
- 其次,
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();
}
});
- 然后,
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) {
}
});
- 哦...没有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 ~
欢迎大家踊跃留言交流哈 ·_·
网友评论