什么都是假的,先看效果图
三级联动
首先感谢这两个作者
https://github.com/venshine/WheelView
https://github.com/wulianghuan/Cascade_Master
这个效果我只是搬运工,整合了这两个作者的东西,弄出来的,也没啥好说的,就是一个popupwin,后面我会放源码,大家自行下载观看(不要积分,给个start就行)
recyclerview首字母悬浮效果
思路主要分为三点:
1、首先拿到数据的拼音的首字母,排序
2、在adapter里面检查首字母第一次出现的位置,从而控制显示隐藏
3、首字母出现的位置设置tag,跟主页面的字母进行动画替换(重点)
4、点击侧边的字母进行联动
我们先通过 android源码的HanziToPinyin这个类,把我们的数据转成拼音,然后截取首字母
/**
* 比较器,比较拼音的首字母
*/
public class PinYinComparator implements Comparator {
@Override
public int compare(CityBean lhs, CityBean rhs) {
return lhs.getSortLetter().compareTo(rhs.getSortLetter());
}
/**
* 实体类,用来存储,汉字,和对应的首字母的拼音
*/
public static class CityBean {
private String name;
private String sortLetter;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSortLetter() {
return sortLetter;
}
public void setSortLetter(String sortLetter) {
this.sortLetter = sortLetter;
}
}
/**
* 汉字转拼音
* @param input 传入的汉字
* @return
*/
public static String getPinYin(String input) {
ArrayList tokens = HanziToPinyin.getInstance().get(input);
StringBuilder sb = new StringBuilder();
if (tokens != null && tokens.size() > 0) {
Iterator iterator = tokens.iterator();
while (iterator.hasNext()) {
HanziToPinyin.Token token = (HanziToPinyin.Token) iterator.next();
if (2 == token.type) {
sb.append(token.target);
} else {
sb.append(token.source);
}
}
}
return sb.toString().toUpperCase();
}
}
后面拿到数据,循环调用上面的getPinYin就能得到字母,然后截取第一个,在存到上面的实体类里面,然后使用比较器进行排序
Collections.
sort
(
mCityList
,
new
PinYinComparator());
到这里我们的第一步就搞定了。
下面我们来看,控制首字母的显示和隐藏,我们来看adapter的onBindViewHolder
方法,我们先拿到实体类,然后拿首字母,通过getPositionForSelect得到这个字母在排序中的位置,而我们的recyclerview的position到达这个位置时就显示首字母和设置tag,否则的话就隐藏,这里设置tag是为了下一步的进行。
PinYinComparator.CityBean info = mData.get(position);
holder.name.setText(info.getName());
char selection = info.getSortLetter().charAt(0);
int letterPosition = getPositionForSelection(selection);
if (letterPosition == position) {
holder.letter_tv.setText(info.getSortLetter());
holder.letter_tv.setVisibility(View.VISIBLE);
holder.itemView.setTag(HAS_STICKY_VIEW);
} else {
holder.letter_tv.setVisibility(View.GONE);
holder.itemView.setTag(NONE_STICKY_VIEW);
}
动画替换
在主页面,我们用一个和字母一样的布局跟recyclerview嵌套在一起,其实那个悬浮头就是在主页面里的布局,
而不是item的那个字母头,我们只是监听滑动,到了我们上面设置tag的地方,就让他滑动他自身的高度,就是下面的
mLetterText
{
View letterView = mRecyclerView.findChildViewUnder(mLetterText.getMeasuredWidth() / 2, 3);
if (letterView != null && letterView.getContentDescription() != null) {
mLetterText.setText(String.valueOf(letterView.getContentDescription()));
}
View recyclerItem = mRecyclerView.findChildViewUnder(mLetterText.getMeasuredWidth() / 2,
mLetterText.getMeasuredHeight() + 1);
if (recyclerItem != null && recyclerItem.getTag() != null) {
//这个recycleritem其实是和mLetterText一样的
//但是滑动recyclerview他的top会改变,此时,我们就让mLetterText跟着改变
int deltaY = recyclerItem.getTop() - mLetterText.getMeasuredHeight();
if ((int) recyclerItem.getTag() == mCityAdapter.HAS_STICKY_VIEW) {
if (recyclerItem.getTop() > 0) {
mLetterText.setTranslationY(deltaY);
} else {
mLetterText.setTranslationY(0);
}
} else if ((int) recyclerItem.getTag() == mCityAdapter.NONE_STICKY_VIEW) {
mLetterText.setTranslationY(0);
}
}
}
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
letterScroll();
}
});
绘制导航字母条
这里难度不大,只是我们绘制的时候要知道,每个字母的x和y坐标应该怎么定,y坐标是计算空件自身的高度,然后除于27(还有一个#)
这样就得到每个字母的y坐标了,x就是控件自身的宽度的一般减去字母的宽度的一半。如何知道我们点击了哪个字母呢?
看下我们的公式就明白了:点击的字母/27=点击的y坐标/总高度
而我们求:点击的字母=(27*点击的y坐标)/总高度
/**
* 字母导航控件
*/
public class NavigationView extends View {
private Paint mPaint;
private String[] mDate = new String[]{"A", "B", "C", "D", "E", "F",
"G", "H", "I", "J", "K", "L", "N", "M", "O", "P", "Q", "R",
"S", "T", "U", "V", "W", "X", "Y", "Z", "#"};
private int position = -1;
private int mHeight;
public NavigationView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(23);
mPaint.setTypeface(Typeface.DEFAULT_BOLD);
}
public NavigationView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mHeight = getHeight();
int width = getWidth();
int simpleHeight = mHeight / mDate.length;
for (int i = 0; i < mDate.length; i++) {
mPaint.setColor(Color.BLUE);
if (i == position) {
mPaint.setColor(Color.RED);
}
float x = (width - mPaint.measureText(mDate[i])) / 2.0f;
float y = simpleHeight * (i + 1);
canvas.drawText(mDate[i], x, y, mPaint);
}
}
@SuppressLint("NewApi")
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
int oldPosition = position;
int c = (y * mDate.length) / mHeight;
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
setBackground(new ColorDrawable(0));
if (mDialog != null) {
mDialog.setVisibility(View.GONE);
}
position = -1;
break;
default:
setBackgroundColor(Color.parseColor("#EEE5DE"));
if (c >= 0 && c < mDate.length && c != oldPosition) {
if (mOnTouchItemListener != null) {
mOnTouchItemListener.onTouch(mDate[c]);
Log.e("------", "onTouchEvent: "+mDate[c] );
}
if (mDialog != null) {
mDialog.setText(mDate[c]);
mDialog.setVisibility(View.VISIBLE);
}
}
position = c;
invalidate();
break;
}
return true;
}
private TextView mDialog;
public void setDialog(TextView dialog) {
mDialog = dialog;
}
private OnTouchItemListener mOnTouchItemListener;
public void setOnTouchItemListener(OnTouchItemListener onTouchItemListener) {
mOnTouchItemListener = onTouchItemListener;
}
public interface OnTouchItemListener {
void onTouch(String var1);
}
}
大功告成,github地址:https://github.com/WoBuShuo/SelectUtil