IndexableLayout.java 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. package me.yokeyword.indexablerv;
  2. import android.app.Activity;
  3. import android.content.Context;
  4. import android.content.res.ColorStateList;
  5. import android.content.res.TypedArray;
  6. import android.graphics.Color;
  7. import android.graphics.drawable.Drawable;
  8. import android.os.Handler;
  9. import android.os.Looper;
  10. import android.util.AttributeSet;
  11. import android.util.TypedValue;
  12. import android.view.Gravity;
  13. import android.view.MotionEvent;
  14. import android.view.View;
  15. import android.view.WindowManager;
  16. import android.widget.FrameLayout;
  17. import android.widget.TextView;
  18. import androidx.annotation.IntDef;
  19. import androidx.appcompat.widget.AppCompatTextView;
  20. import androidx.core.content.ContextCompat;
  21. import androidx.core.view.ViewCompat;
  22. import androidx.recyclerview.widget.GridLayoutManager;
  23. import androidx.recyclerview.widget.LinearLayoutManager;
  24. import androidx.recyclerview.widget.RecyclerView;
  25. import java.lang.annotation.Retention;
  26. import java.lang.annotation.RetentionPolicy;
  27. import java.util.ArrayList;
  28. import java.util.Collections;
  29. import java.util.Comparator;
  30. import java.util.List;
  31. import java.util.TreeMap;
  32. import java.util.concurrent.ExecutorService;
  33. import java.util.concurrent.Executors;
  34. import java.util.concurrent.Future;
  35. import me.yokeyword.indexablerv.database.DataObserver;
  36. import me.yokeyword.indexablerv.database.HeaderFooterDataObserver;
  37. import me.yokeyword.indexablerv.database.IndexBarDataObserver;
  38. /**
  39. * RecyclerView + IndexBar
  40. * Created by YoKey on 16/10/6.
  41. */
  42. @SuppressWarnings("unchecked")
  43. public class IndexableLayout extends FrameLayout {
  44. // 快速排序,只比对首字母(默认)
  45. public static final int MODE_FAST = 0;
  46. // 全字母比较排序, 效率最低
  47. public static final int MODE_ALL_LETTERS = 1;
  48. // 每个字母模块内:无需排序,效率最高
  49. public static final int MODE_NONE = 2;
  50. private static int PADDING_RIGHT_OVERLAY;
  51. static final String INDEX_SIGN = "#";
  52. private Context mContext;
  53. private boolean mShowAllLetter = true;
  54. private ExecutorService mExecutorService;
  55. private Future mFuture;
  56. private RecyclerView mRecy;
  57. private IndexBar mIndexBar;
  58. /**
  59. * 保存正在Invisible的ItemView
  60. * <p>
  61. * 使用mLastInvisibleRecyclerViewItemView来保存当前Invisible的ItemView,
  62. * 每次有新的ItemView需要Invisible的时候,把旧的Invisible的ItemView设为Visible。
  63. * 这样就修复了View复用导致的Invisible状态传递的问题。
  64. */
  65. private View mLastInvisibleRecyclerViewItemView;
  66. private boolean mSticyEnable = true;
  67. private RecyclerView.ViewHolder mStickyViewHolder;
  68. private String mStickyTitle;
  69. private RealAdapter mRealAdapter;
  70. private RecyclerView.LayoutManager mLayoutManager;
  71. private IndexableAdapter mIndexableAdapter;
  72. private TextView mCenterOverlay, mMDOverlay;
  73. private int mBarTextColor, mBarFocusTextColor;
  74. private float mBarTextSize, mBarTextSpace, mBarWidth;
  75. private Drawable mBarBg;
  76. private DataObserver mDataSetObserver;
  77. private int mCompareMode = MODE_FAST;
  78. private Comparator mComparator;
  79. private Handler mHandler;
  80. private HeaderFooterDataObserver<EntityWrapper> mHeaderFooterDataSetObserver = new HeaderFooterDataObserver<EntityWrapper>() {
  81. @Override
  82. public void onChanged() {
  83. if (mRealAdapter == null) return;
  84. mRealAdapter.notifyDataSetChanged();
  85. }
  86. @Override
  87. public void onAdd(boolean header, EntityWrapper preData, EntityWrapper data) {
  88. if (mRealAdapter == null) return;
  89. mRealAdapter.addHeaderFooterData(header, preData, data);
  90. }
  91. @Override
  92. public void onRemove(boolean header, EntityWrapper data) {
  93. if (mRealAdapter == null) return;
  94. mRealAdapter.removeHeaderFooterData(header, data);
  95. }
  96. };
  97. private IndexBarDataObserver mIndexBarDataSetObserver = new IndexBarDataObserver() {
  98. @Override
  99. public void onChanged() {
  100. mIndexBar.setDatas(mShowAllLetter, mRealAdapter.getItems());
  101. }
  102. };
  103. public IndexableLayout(Context context) {
  104. this(context, null);
  105. }
  106. public IndexableLayout(Context context, AttributeSet attrs) {
  107. this(context, attrs, 0);
  108. }
  109. public IndexableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  110. super(context, attrs, defStyleAttr);
  111. init(context, attrs);
  112. }
  113. /**
  114. * set RealAdapter
  115. * {@link #setLayoutManager(RecyclerView.LayoutManager)}
  116. */
  117. public <T extends IndexableEntity> void setAdapter(final IndexableAdapter<T> adapter) {
  118. if (mLayoutManager == null) {
  119. throw new NullPointerException("You must set the LayoutManager first");
  120. }
  121. this.mIndexableAdapter = adapter;
  122. if (mDataSetObserver != null) {
  123. adapter.unregisterDataSetObserver(mDataSetObserver);
  124. }
  125. mDataSetObserver = new DataObserver() {
  126. @Override
  127. public void onInited() {
  128. onSetListener(IndexableAdapter.TYPE_ALL);
  129. onDataChanged();
  130. }
  131. @Override
  132. public void onChanged() {
  133. if (mRealAdapter != null) {
  134. mRealAdapter.notifyDataSetChanged();
  135. }
  136. }
  137. @Override
  138. public void onSetListener(int type) {
  139. // set listeners
  140. if ((type == IndexableAdapter.TYPE_CLICK_TITLE || type == IndexableAdapter.TYPE_ALL) && adapter.getOnItemTitleClickListener() != null) {
  141. mRealAdapter.setOnItemTitleClickListener(adapter.getOnItemTitleClickListener());
  142. }
  143. if ((type == IndexableAdapter.TYPE_LONG_CLICK_TITLE || type == IndexableAdapter.TYPE_ALL) && adapter.getOnItemTitleLongClickListener() != null) {
  144. mRealAdapter.setOnItemTitleLongClickListener(adapter.getOnItemTitleLongClickListener());
  145. }
  146. if ((type == IndexableAdapter.TYPE_CLICK_CONTENT || type == IndexableAdapter.TYPE_ALL) && adapter.getOnItemContentClickListener() != null) {
  147. mRealAdapter.setOnItemContentClickListener(adapter.getOnItemContentClickListener());
  148. }
  149. if ((type == IndexableAdapter.TYPE_LONG_CLICK_CONTENT || type == IndexableAdapter.TYPE_ALL) && adapter.getOnItemContentLongClickListener() != null) {
  150. mRealAdapter.setOnItemContentLongClickListener(adapter.getOnItemContentLongClickListener());
  151. }
  152. }
  153. };
  154. adapter.registerDataSetObserver(mDataSetObserver);
  155. mRealAdapter.setIndexableAdapter(adapter);
  156. if (mSticyEnable) {
  157. initStickyView(adapter);
  158. }
  159. }
  160. /**
  161. * add HeaderView Adapter
  162. */
  163. public <T> void addHeaderAdapter(IndexableHeaderAdapter<T> adapter) {
  164. adapter.registerDataSetObserver(mHeaderFooterDataSetObserver);
  165. adapter.registerIndexBarDataSetObserver(mIndexBarDataSetObserver);
  166. mRealAdapter.addIndexableHeaderAdapter(adapter);
  167. }
  168. /**
  169. * removeData HeaderView Adapter
  170. */
  171. public <T> void removeHeaderAdapter(IndexableHeaderAdapter<T> adapter) {
  172. try {
  173. adapter.unregisterDataSetObserver(mHeaderFooterDataSetObserver);
  174. adapter.unregisterIndexBarDataSetObserver(mIndexBarDataSetObserver);
  175. mRealAdapter.removeIndexableHeaderAdapter(adapter);
  176. } catch (Exception ignored) {
  177. }
  178. }
  179. /**
  180. * add FooterView Adapter
  181. */
  182. public <T> void addFooterAdapter(IndexableFooterAdapter<T> adapter) {
  183. adapter.registerDataSetObserver(mHeaderFooterDataSetObserver);
  184. adapter.registerIndexBarDataSetObserver(mIndexBarDataSetObserver);
  185. mRealAdapter.addIndexableFooterAdapter(adapter);
  186. }
  187. /**
  188. * removeData FooterView Adapter
  189. */
  190. public <T> void removeFooterAdapter(IndexableFooterAdapter<T> adapter) {
  191. try {
  192. adapter.unregisterDataSetObserver(mHeaderFooterDataSetObserver);
  193. adapter.unregisterIndexBarDataSetObserver(mIndexBarDataSetObserver);
  194. mRealAdapter.removeIndexableFooterAdapter(adapter);
  195. } catch (Exception ignored) {
  196. }
  197. }
  198. /**
  199. * set sort-mode
  200. * Deprecated {@link #setCompareMode(int)}
  201. */
  202. @Deprecated
  203. public void setFastCompare(boolean fastCompare) {
  204. setCompareMode(fastCompare ? MODE_FAST : MODE_ALL_LETTERS);
  205. }
  206. @IntDef({MODE_FAST, MODE_ALL_LETTERS, MODE_NONE})
  207. @Retention(RetentionPolicy.SOURCE)
  208. @interface CompareMode {
  209. }
  210. /**
  211. * set sort-mode
  212. */
  213. public void setCompareMode(@CompareMode int mode) {
  214. this.mCompareMode = mode;
  215. }
  216. /**
  217. * set sort-mode
  218. */
  219. public <T extends IndexableEntity> void setComparator(Comparator<EntityWrapper<T>> comparator) {
  220. this.mComparator = comparator;
  221. }
  222. /**
  223. * set Sticky Enable
  224. */
  225. public void setStickyEnable(boolean enable) {
  226. this.mSticyEnable = enable;
  227. }
  228. /**
  229. * display all letter-index
  230. */
  231. public void showAllLetter(boolean show) {
  232. mShowAllLetter = show;
  233. }
  234. /**
  235. * display Material Design OverlayView
  236. */
  237. public void setOverlayStyle_MaterialDesign(int color) {
  238. if (mMDOverlay == null) {
  239. initMDOverlay(color);
  240. } else {
  241. ViewCompat.setBackgroundTintList(mMDOverlay, ColorStateList.valueOf(color));
  242. }
  243. mCenterOverlay = null;
  244. }
  245. /**
  246. * display Center OverlayView
  247. */
  248. public void setOverlayStyle_Center() {
  249. if (mCenterOverlay == null) {
  250. initCenterOverlay();
  251. }
  252. mMDOverlay = null;
  253. }
  254. /**
  255. * get OverlayView
  256. */
  257. public TextView getOverlayView() {
  258. return mMDOverlay != null ? mMDOverlay : mCenterOverlay;
  259. }
  260. /**
  261. * get RecyclerView
  262. */
  263. public RecyclerView getRecyclerView() {
  264. return mRecy;
  265. }
  266. /**
  267. * Set the enabled state of this IndexBar.
  268. */
  269. public void setIndexBarVisibility(boolean visible) {
  270. mIndexBar.setVisibility(visible ? VISIBLE : GONE);
  271. }
  272. private void init(Context context, AttributeSet attrs) {
  273. this.mContext = context;
  274. this.mExecutorService = Executors.newSingleThreadExecutor();
  275. PADDING_RIGHT_OVERLAY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics());
  276. if (attrs != null) {
  277. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IndexableRecyclerView);
  278. mBarTextColor = a.getColor(R.styleable.IndexableRecyclerView_indexBar_textColor, ContextCompat.getColor(context, R.color.default_indexBar_textColor));
  279. mBarTextSize = a.getDimension(R.styleable.IndexableRecyclerView_indexBar_textSize, getResources().getDimension(R.dimen.default_indexBar_textSize));
  280. mBarFocusTextColor = a.getColor(R.styleable.IndexableRecyclerView_indexBar_selectedTextColor, ContextCompat.getColor(context, R.color.default_indexBar_selectedTextColor));
  281. mBarTextSpace = a.getDimension(R.styleable.IndexableRecyclerView_indexBar_textSpace, getResources().getDimension(R.dimen.default_indexBar_textSpace));
  282. mBarBg = a.getDrawable(R.styleable.IndexableRecyclerView_indexBar_background);
  283. mBarWidth = a.getDimension(R.styleable.IndexableRecyclerView_indexBar_layout_width, getResources().getDimension(R.dimen.default_indexBar_layout_width));
  284. a.recycle();
  285. }
  286. if (mContext instanceof Activity) {
  287. ((Activity) mContext).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
  288. }
  289. mRecy = new RecyclerView(context);
  290. mRecy.setVerticalScrollBarEnabled(false);
  291. mRecy.setOverScrollMode(View.OVER_SCROLL_NEVER);
  292. addView(mRecy, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
  293. mIndexBar = new IndexBar(context);
  294. mIndexBar.init(mBarBg, mBarTextColor, mBarFocusTextColor, mBarTextSize, mBarTextSpace);
  295. LayoutParams params = new LayoutParams((int) mBarWidth, LayoutParams.WRAP_CONTENT);
  296. params.gravity = Gravity.END | Gravity.CENTER_VERTICAL;
  297. addView(mIndexBar, params);
  298. mRealAdapter = new RealAdapter();
  299. mRecy.setHasFixedSize(true);
  300. mRecy.setAdapter(mRealAdapter);
  301. initListener();
  302. }
  303. /**
  304. * {@link #setAdapter(IndexableAdapter)}
  305. *
  306. * @param layoutManager One of LinearLayoutManager and GridLayoutManager
  307. */
  308. public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
  309. if (layoutManager == null)
  310. throw new NullPointerException("LayoutManager == null");
  311. mLayoutManager = layoutManager;
  312. if (layoutManager instanceof GridLayoutManager) {
  313. final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
  314. gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
  315. @Override
  316. public int getSpanSize(int position) {
  317. int spanSize = 0;
  318. if (mRealAdapter.getItemViewType(position) == EntityWrapper.TYPE_TITLE) {
  319. spanSize = gridLayoutManager.getSpanCount();
  320. } else if (mRealAdapter.getItemViewType(position) == EntityWrapper.TYPE_CONTENT) {
  321. spanSize = 1;
  322. }
  323. return spanSize;
  324. }
  325. });
  326. }
  327. mRecy.setLayoutManager(mLayoutManager);
  328. }
  329. private void initListener() {
  330. mRecy.addOnScrollListener(new RecyclerView.OnScrollListener() {
  331. @Override
  332. public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
  333. super.onScrolled(recyclerView, dx, dy);
  334. processScrollListener();
  335. }
  336. });
  337. mIndexBar.setOnTouchListener(new OnTouchListener() {
  338. @Override
  339. public boolean onTouch(View v, MotionEvent event) {
  340. int touchPos = mIndexBar.getPositionForPointY(event.getY());
  341. if (touchPos < 0) return true;
  342. if (!(mLayoutManager instanceof LinearLayoutManager)) return true;
  343. LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mLayoutManager;
  344. switch (event.getAction()) {
  345. case MotionEvent.ACTION_DOWN:
  346. case MotionEvent.ACTION_MOVE:
  347. showOverlayView(event.getY(), touchPos);
  348. if (touchPos != mIndexBar.getSelectionPosition()) {
  349. mIndexBar.setSelectionPosition(touchPos);
  350. if (touchPos == 0) {
  351. linearLayoutManager.scrollToPositionWithOffset(0, 0);
  352. } else {
  353. linearLayoutManager.scrollToPositionWithOffset(mIndexBar.getFirstRecyclerViewPositionBySelection(), 0);
  354. }
  355. }
  356. break;
  357. case MotionEvent.ACTION_UP:
  358. case MotionEvent.ACTION_CANCEL:
  359. if (mCenterOverlay != null) mCenterOverlay.setVisibility(GONE);
  360. if (mMDOverlay != null) mMDOverlay.setVisibility(GONE);
  361. break;
  362. }
  363. return true;
  364. }
  365. });
  366. }
  367. private void processScrollListener() {
  368. if (!(mLayoutManager instanceof LinearLayoutManager)) return;
  369. LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mLayoutManager;
  370. int firstItemPosition;
  371. firstItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
  372. if (firstItemPosition == RecyclerView.NO_POSITION) return;
  373. mIndexBar.setSelection(firstItemPosition);
  374. if (!mSticyEnable) return;
  375. ArrayList<EntityWrapper> list = mRealAdapter.getItems();
  376. if (mStickyViewHolder != null && list.size() > firstItemPosition) {
  377. EntityWrapper wrapper = list.get(firstItemPosition);
  378. String wrapperTitle = wrapper.getIndexTitle();
  379. if (EntityWrapper.TYPE_TITLE == wrapper.getItemType()) {
  380. if (mLastInvisibleRecyclerViewItemView != null && mLastInvisibleRecyclerViewItemView.getVisibility() == INVISIBLE) {
  381. mLastInvisibleRecyclerViewItemView.setVisibility(VISIBLE);
  382. mLastInvisibleRecyclerViewItemView = null;
  383. }
  384. mLastInvisibleRecyclerViewItemView = linearLayoutManager.findViewByPosition(firstItemPosition);
  385. if (mLastInvisibleRecyclerViewItemView != null) {
  386. mLastInvisibleRecyclerViewItemView.setVisibility(INVISIBLE);
  387. }
  388. }
  389. // hide -> show
  390. if (wrapperTitle == null && mStickyViewHolder.itemView.getVisibility() == VISIBLE) {
  391. mStickyTitle = null;
  392. mStickyViewHolder.itemView.setVisibility(INVISIBLE);
  393. } else {
  394. stickyNewViewHolder(wrapperTitle);
  395. }
  396. // GirdLayoutManager
  397. if (mLayoutManager instanceof GridLayoutManager) {
  398. GridLayoutManager gridLayoutManager = (GridLayoutManager) mLayoutManager;
  399. if (firstItemPosition + gridLayoutManager.getSpanCount() < list.size()) {
  400. for (int i = firstItemPosition + 1; i <= firstItemPosition + gridLayoutManager.getSpanCount(); i++) {
  401. processScroll(linearLayoutManager, list, i, wrapperTitle);
  402. }
  403. }
  404. } else { // LinearLayoutManager
  405. if (firstItemPosition + 1 < list.size()) {
  406. processScroll(linearLayoutManager, list, firstItemPosition + 1, wrapperTitle);
  407. }
  408. }
  409. }
  410. }
  411. private void processScroll(LinearLayoutManager layoutManager, ArrayList<EntityWrapper> list, int position, String title) {
  412. EntityWrapper nextWrapper = list.get(position);
  413. View nextTitleView = layoutManager.findViewByPosition(position);
  414. if (nextTitleView == null) return;
  415. if (nextWrapper.getItemType() == EntityWrapper.TYPE_TITLE) {
  416. if (nextTitleView.getTop() <= mStickyViewHolder.itemView.getHeight() && title != null) {
  417. mStickyViewHolder.itemView.setTranslationY(nextTitleView.getTop() - mStickyViewHolder.itemView.getHeight());
  418. }
  419. if (INVISIBLE == nextTitleView.getVisibility()) {
  420. //特殊情况:手指向下滑动的时候,需要及时把成为第二个可见View的TitleView设置Visible,
  421. // 这样才能配合StickyView制造两个TitleView切换的动画。
  422. nextTitleView.setVisibility(VISIBLE);
  423. }
  424. return;
  425. } else if (mStickyViewHolder.itemView.getTranslationY() != 0) {
  426. mStickyViewHolder.itemView.setTranslationY(0);
  427. }
  428. return;
  429. }
  430. private void stickyNewViewHolder(String wrapperTitle) {
  431. if ((wrapperTitle != null && !wrapperTitle.equals(mStickyTitle))) {
  432. if (mStickyViewHolder.itemView.getVisibility() != VISIBLE) {
  433. mStickyViewHolder.itemView.setVisibility(VISIBLE);
  434. }
  435. mStickyTitle = wrapperTitle;
  436. mIndexableAdapter.onBindTitleViewHolder(mStickyViewHolder, wrapperTitle);
  437. }
  438. }
  439. private <T extends IndexableEntity> void initStickyView(final IndexableAdapter<T> adapter) {
  440. mStickyViewHolder = adapter.onCreateTitleViewHolder(mRecy);
  441. mStickyViewHolder.itemView.setOnClickListener(new OnClickListener() {
  442. @Override
  443. public void onClick(View v) {
  444. if (adapter.getOnItemTitleClickListener() != null) {
  445. int position = mIndexBar.getFirstRecyclerViewPositionBySelection();
  446. ArrayList<EntityWrapper> datas = mRealAdapter.getItems();
  447. if (datas.size() > position && position >= 0) {
  448. adapter.getOnItemTitleClickListener().onItemClick(
  449. v, position, datas.get(position).getIndexTitle());
  450. }
  451. }
  452. }
  453. });
  454. mStickyViewHolder.itemView.setOnLongClickListener(new OnLongClickListener() {
  455. @Override
  456. public boolean onLongClick(View v) {
  457. if (adapter.getOnItemTitleLongClickListener() != null) {
  458. int position = mIndexBar.getFirstRecyclerViewPositionBySelection();
  459. ArrayList<EntityWrapper> datas = mRealAdapter.getItems();
  460. if (datas.size() > position && position >= 0) {
  461. return adapter.getOnItemTitleLongClickListener().onItemLongClick(
  462. v, position, datas.get(position).getIndexTitle());
  463. }
  464. }
  465. return false;
  466. }
  467. });
  468. for (int i = 0; i < getChildCount(); i++) {
  469. if (getChildAt(i) == mRecy) {
  470. mStickyViewHolder.itemView.setVisibility(INVISIBLE);
  471. addView(mStickyViewHolder.itemView, i + 1);
  472. return;
  473. }
  474. }
  475. }
  476. private void showOverlayView(float y, final int touchPos) {
  477. if (mIndexBar.getIndexList().size() <= touchPos) return;
  478. if (mMDOverlay != null) {
  479. if (mMDOverlay.getVisibility() != VISIBLE) {
  480. mMDOverlay.setVisibility(VISIBLE);
  481. }
  482. if (y < PADDING_RIGHT_OVERLAY - mIndexBar.getTop() && y >= 0) {
  483. y = PADDING_RIGHT_OVERLAY - mIndexBar.getTop();
  484. } else if (y < 0) {
  485. if (mIndexBar.getTop() > PADDING_RIGHT_OVERLAY) {
  486. y = 0;
  487. } else {
  488. y = PADDING_RIGHT_OVERLAY - mIndexBar.getTop();
  489. }
  490. } else if (y > mIndexBar.getHeight()) {
  491. y = mIndexBar.getHeight();
  492. }
  493. mMDOverlay.setY(mIndexBar.getTop() + y - PADDING_RIGHT_OVERLAY);
  494. String index = mIndexBar.getIndexList().get(touchPos);
  495. if (!mMDOverlay.getText().equals(index)) {
  496. if (index.length() > 1) {
  497. mMDOverlay.setTextSize(30);
  498. }
  499. mMDOverlay.setText(index);
  500. }
  501. }
  502. if (mCenterOverlay != null) {
  503. if (mCenterOverlay.getVisibility() != VISIBLE) {
  504. mCenterOverlay.setVisibility(VISIBLE);
  505. }
  506. String index = mIndexBar.getIndexList().get(touchPos);
  507. if (!mCenterOverlay.getText().equals(index)) {
  508. if (index.length() > 1) {
  509. mCenterOverlay.setTextSize(32);
  510. }
  511. mCenterOverlay.setText(index);
  512. }
  513. }
  514. }
  515. private void initCenterOverlay() {
  516. mCenterOverlay = new TextView(mContext);
  517. mCenterOverlay.setBackgroundResource(R.drawable.indexable_bg_center_overlay);
  518. mCenterOverlay.setTextColor(Color.WHITE);
  519. mCenterOverlay.setTextSize(40);
  520. mCenterOverlay.setGravity(Gravity.CENTER);
  521. int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 70, getResources().getDisplayMetrics());
  522. LayoutParams params = new LayoutParams(size, size);
  523. params.gravity = Gravity.CENTER;
  524. mCenterOverlay.setLayoutParams(params);
  525. mCenterOverlay.setVisibility(INVISIBLE);
  526. addView(mCenterOverlay);
  527. }
  528. private void initMDOverlay(int color) {
  529. mMDOverlay = new AppCompatTextView(mContext);
  530. mMDOverlay.setBackgroundResource(R.drawable.indexable_bg_md_overlay);
  531. ((AppCompatTextView) mMDOverlay).setSupportBackgroundTintList(ColorStateList.valueOf(color));
  532. mMDOverlay.setSingleLine();
  533. mMDOverlay.setTextColor(Color.WHITE);
  534. mMDOverlay.setTextSize(38);
  535. mMDOverlay.setGravity(Gravity.CENTER);
  536. int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics());
  537. LayoutParams params = new LayoutParams(size, size);
  538. params.rightMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 33, getResources().getDisplayMetrics());
  539. params.gravity = Gravity.END;
  540. mMDOverlay.setLayoutParams(params);
  541. mMDOverlay.setVisibility(INVISIBLE);
  542. addView(mMDOverlay);
  543. }
  544. void onDataChanged() {
  545. if (mFuture != null) {
  546. mFuture.cancel(true);
  547. }
  548. mFuture = mExecutorService.submit(new Runnable() {
  549. @Override
  550. public void run() {
  551. final ArrayList<EntityWrapper> datas = transform(mIndexableAdapter.getItems());
  552. if (datas == null) return;
  553. getSafeHandler().post(new Runnable() {
  554. @Override
  555. public void run() {
  556. mRealAdapter.setDatas(datas);
  557. mIndexBar.setDatas(mShowAllLetter, mRealAdapter.getItems());
  558. if (mIndexableAdapter.getIndexCallback() != null) {
  559. mIndexableAdapter.getIndexCallback().onFinished(datas);
  560. }
  561. processScrollListener();
  562. }
  563. });
  564. }
  565. });
  566. }
  567. /**
  568. * List<T> -> List<Indexable<T>
  569. */
  570. private <T extends IndexableEntity> ArrayList<EntityWrapper<T>> transform(final List<T> datas) {
  571. try {
  572. TreeMap<String, List<EntityWrapper<T>>> map = new TreeMap<>(new Comparator<String>() {
  573. @Override
  574. public int compare(String lhs, String rhs) {
  575. if (lhs.equals(INDEX_SIGN)) {
  576. return rhs.equals(INDEX_SIGN) ? 0 : 1;
  577. } else if (rhs.equals(INDEX_SIGN)) {
  578. return -1;
  579. }
  580. return lhs.compareTo(rhs);
  581. }
  582. });
  583. for (int i = 0; i < datas.size(); i++) {
  584. EntityWrapper<T> entity = new EntityWrapper<>();
  585. T item = datas.get(i);
  586. String indexName = item.getFieldIndexBy();
  587. String pinyin = PinyinUtil.getPingYin(indexName);
  588. entity.setPinyin(pinyin);
  589. // init EntityWrapper
  590. if (PinyinUtil.matchingLetter(pinyin)) {
  591. entity.setIndex(pinyin.substring(0, 1).toUpperCase());
  592. entity.setIndexByField(item.getFieldIndexBy());
  593. } else if (PinyinUtil.matchingPolyphone(pinyin)) {
  594. entity.setIndex(PinyinUtil.gePolyphoneInitial(pinyin).toUpperCase());
  595. entity.setPinyin(PinyinUtil.getPolyphoneRealPinyin(pinyin));
  596. String hanzi = PinyinUtil.getPolyphoneRealHanzi(indexName);
  597. entity.setIndexByField(hanzi);
  598. // 把多音字的真实indexField重新赋值
  599. item.setFieldIndexBy(hanzi);
  600. } else {
  601. entity.setIndex(INDEX_SIGN);
  602. entity.setIndexByField(item.getFieldIndexBy());
  603. }
  604. entity.setIndexTitle(entity.getIndex());
  605. entity.setData(item);
  606. entity.setOriginalPosition(i);
  607. item.setFieldPinyinIndexBy(entity.getPinyin());
  608. String inital = entity.getIndex();
  609. List<EntityWrapper<T>> list;
  610. if (!map.containsKey(inital)) {
  611. list = new ArrayList<>();
  612. list.add(new EntityWrapper<T>(entity.getIndex(), EntityWrapper.TYPE_TITLE));
  613. map.put(inital, list);
  614. } else {
  615. list = map.get(inital);
  616. }
  617. list.add(entity);
  618. }
  619. ArrayList<EntityWrapper<T>> list = new ArrayList<>();
  620. for (List<EntityWrapper<T>> indexableEntities : map.values()) {
  621. if (mComparator != null) {
  622. Collections.sort(indexableEntities, mComparator);
  623. } else {
  624. Comparator comparator;
  625. if (mCompareMode == MODE_FAST) {
  626. comparator = new InitialComparator<T>();
  627. Collections.sort(indexableEntities, comparator);
  628. } else if (mCompareMode == MODE_ALL_LETTERS) {
  629. comparator = new PinyinComparator<T>();
  630. Collections.sort(indexableEntities, comparator);
  631. }
  632. }
  633. list.addAll(indexableEntities);
  634. }
  635. return list;
  636. } catch (Exception e) {
  637. e.printStackTrace();
  638. return null;
  639. }
  640. }
  641. private Handler getSafeHandler() {
  642. if (mHandler == null) {
  643. mHandler = new Handler(Looper.getMainLooper());
  644. }
  645. return mHandler;
  646. }
  647. }