package com.yuyh.library.view.list.indexablelistview; import android.app.Activity; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.support.v7.widget.AppCompatTextView; import android.util.AttributeSet; import android.util.SparseArray; import android.view.Gravity; import android.view.View; import android.view.WindowManager; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ListView; import android.widget.TextView; import com.yuyh.library.R; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * Created by YoKeyword on 16/3/21. */ public class IndexableStickyListView extends FrameLayout implements AdapterView.OnItemClickListener, AbsListView.OnScrollListener { private static final int MSG_BIND_DATA = 1; private OnItemTitleClickListener mOnTitleListener; private OnItemContentClickListener mOnContentListener; private int mBarTextColor, mBarSelectedTextColor, mRightOverlayColor; private float mBarTextSize; private int mTypeOverlay; private ArrayList<View> mAddHeaderViewList; private ListView mListView; private IndexBar mIndexBar; private SearchLayout mSearchLayout; private TextView mTvOverlay; private AppCompatTextView mTvRightOverlay; private Context mContext; private IndexableAdapter mAdapter; private List<IndexEntity> mItems; private IndexHeaderEntity[] mHeaderEntities; private int mCurrentScrollItemPosition, mCurrentScrollItemTop; private int mTitleHeight; private SparseArray<String> mTitleMap; private TextView mStickView; private HandlerThread mBindDataHandlerThread; private Handler mBindDataHandler; public IndexableStickyListView(Context context) { super(context); init(context, null); } public IndexableStickyListView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public IndexableStickyListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { mContext = context; if (attrs != null) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IndexableStickyListView); mBarTextColor = a.getColor(R.styleable.IndexableStickyListView_indexBar_textColor, getResources().getColor(R.color.default_indexBar_textcolor)); mBarTextSize = a.getDimension(R.styleable.IndexableStickyListView_indexBar_textSize, getResources().getDimension(R.dimen.default_indexBar_textSize)); mBarSelectedTextColor = a.getColor(R.styleable.IndexableStickyListView_indexBar_selected_textColor, getResources().getColor(R.color.dafault_indexBar_selected_textColor)); mRightOverlayColor = a.getColor(R.styleable.IndexableStickyListView_indexListView_rightOverlayColor, getResources().getColor(R.color.default_indexListView_rightOverlayColor)); mTypeOverlay = a.getInt(R.styleable.IndexableStickyListView_indexListView_type_overlay, 0); a.recycle(); } if (mContext instanceof Activity) { ((Activity) mContext).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); } mListView = new ListView(context); mListView.setVerticalScrollBarEnabled(false); mListView.setOverScrollMode(View.OVER_SCROLL_NEVER); mListView.setDivider(null); addView(mListView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); mIndexBar = new IndexBar(context, mBarTextColor, mBarSelectedTextColor, mBarTextSize); LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); params.gravity = Gravity.RIGHT; params.topMargin = IndexBar.dp2px(context, 16); params.bottomMargin = params.topMargin; addView(mIndexBar, params); if (mTypeOverlay == 1) { showCenterOverlayView(true); } else if (mTypeOverlay == 2) { showRightOverlayView(true, mRightOverlayColor); } mSearchLayout = new SearchLayout(context); LayoutParams paramsLayout = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); addView(mSearchLayout, paramsLayout); mSearchLayout.setVisibility(GONE); mListView.setOnItemClickListener(this); mListView.setOnScrollListener(this); mIndexBar.setOnIndexSelectedListener(new IndexBar.OnIndexTitleSelectedListener() { @Override public void onSelection(int position, String indexTitle) { if (mStickView != null) { if (!mStickView.getText().toString().equals(indexTitle)) { mStickView.setText(indexTitle); } if (mStickView.getY() != 0) { mStickView.setY(0); } } } }); } public void addHeaderView(View view) { if (mListView == null) return; if (mAddHeaderViewList == null) { mAddHeaderViewList = new ArrayList<>(); } mListView.addHeaderView(view); mAddHeaderViewList.add(view); } public void removeHeaderView(View view) { if (mListView == null || mAddHeaderViewList == null || !mAddHeaderViewList.contains(view)) { return; } mListView.removeHeaderView(view); mAddHeaderViewList.remove(view); } public int getHeaderViewsCount() { if (mListView == null) return 0; return mListView.getHeaderViewsCount(); } public ArrayList<View> getHeaderViews() { return mAddHeaderViewList; } /** * @return IndexListView的ListView */ public ListView getListView() { return mListView; } /** * @return IndexListView中心位置的 悬浮TextView */ public TextView getCenterOverlayTextView() { return mTvOverlay; } public IndexBar getIndexBar() { return mIndexBar; } /** * @param show 是否显示IndexListView右侧位置的 悬浮TextView */ public void showRightOverlayView(boolean show, int color) { if (show) { if (mTvRightOverlay == null) { initRightOverlayTextView(color); addView(mTvRightOverlay); mTvRightOverlay.invalidate(); mIndexBar.showTouchOverlayView(mTvRightOverlay); } } else { if (mTvRightOverlay != null) { removeView(mTvRightOverlay); mIndexBar.showTouchOverlayView(null); } } } /** * @param show 是否显示IndexListView中心位置的 悬浮TextView */ public void showCenterOverlayView(boolean show) { if (show) { if (mTvOverlay == null) { initOverlayTextView(); } addView(mTvOverlay); mIndexBar.setOverlayView(mTvOverlay); } else { if (mTvOverlay != null) { removeView(mTvOverlay); } mIndexBar.setOverlayView(null); } } private void initOverlayTextView() { mTvOverlay = new TextView(mContext); mTvOverlay.setBackgroundResource(R.drawable.bg_translucent_4dp); mTvOverlay.setTextColor(Color.WHITE); mTvOverlay.setTextSize(40); mTvOverlay.setGravity(Gravity.CENTER); int size = IndexBar.dp2px(mContext, 70); LayoutParams params = new LayoutParams(size, size); params.gravity = Gravity.CENTER; mTvOverlay.setLayoutParams(params); mTvOverlay.setVisibility(INVISIBLE); } private void initRightOverlayTextView(int color) { mTvRightOverlay = new AppCompatTextView(mContext); mTvRightOverlay.setBackgroundResource(R.drawable.bg_right_overlay); mTvRightOverlay.setSupportBackgroundTintList(ColorStateList.valueOf(color)); mTvRightOverlay.setTextColor(Color.WHITE); mTvRightOverlay.setTextSize(38); mTvRightOverlay.setGravity(Gravity.CENTER); int size = IndexBar.dp2px(mContext, 72); LayoutParams params = new LayoutParams(size, size); params.rightMargin = IndexBar.dp2px(mContext, 33); params.gravity = Gravity.RIGHT; mTvRightOverlay.setLayoutParams(params); mTvRightOverlay.setVisibility(INVISIBLE); } private static class BindDatasHanlder extends Handler { private final WeakReference<IndexableStickyListView> mIndexListView; public BindDatasHanlder(Looper looper, IndexableStickyListView indexListView) { super(looper); mIndexListView = new WeakReference<>(indexListView); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); final IndexableStickyListView indexListView = mIndexListView.get(); indexListView.mAdapter.setNeedShutdown(false); indexListView.mAdapter.setDatas(indexListView.mItems, indexListView.mHeaderEntities); if (indexListView.mAdapter.isNeedShutdown()) return; ((Activity) indexListView.mContext).runOnUiThread(new Runnable() { @Override public void run() { indexListView.updateListView(); } }); } } /** * 绑定数据 * * @param items 继承IndexEntity的List * @param headerEntities IndexListView自定义Header 如添加定位,热门城市等 * @param <T> 继承IndexEntity */ public <T extends IndexEntity> void bindDatas(final List<T> items, final IndexHeaderEntity... headerEntities) { mItems = new ArrayList<>(); mHeaderEntities = new IndexHeaderEntity[headerEntities.length]; mItems.addAll(items); for (int i = 0; i < headerEntities.length; i++) { mHeaderEntities[i] = headerEntities[i]; } if (mAdapter == null) { return; } if (mContext instanceof Activity) { mAdapter.setNeedShutdown(true); if (mBindDataHandlerThread == null) { mBindDataHandlerThread = new HandlerThread("BindData_Thread"); mBindDataHandlerThread.start(); mBindDataHandler = new BindDatasHanlder(mBindDataHandlerThread.getLooper(), this); } mBindDataHandler.sendEmptyMessage(MSG_BIND_DATA); } else { mAdapter.setDatas(mItems, headerEntities); updateListView(); } } /** * 搜索过滤处理 * * @param newText 变化后的内容 */ public void searchTextChange(final String newText) { mIndexBar.searchTextChange(newText); } /** * 为IndexListView设置Adapter */ public <T extends IndexEntity> void setAdapter(IndexableAdapter<T> adapter) { mAdapter = adapter; mAdapter.setParent(this); mListView.setAdapter(adapter); if (mItems != null) { bindDatas(mItems, mHeaderEntities); } } /** * 设置 IndexListView 标题头 部分的点击事件 * * @param listener */ public void setOnItemTitleClickListener(OnItemTitleClickListener listener) { mOnTitleListener = listener; } /** * 设置 IndexListView 内容item 部分的点击事件 * * @param listener */ public void setOnItemContentClickListener(OnItemContentClickListener listener) { mOnContentListener = listener; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (position < mListView.getHeaderViewsCount()) return; Object object = mAdapter.getItem(position - mListView.getHeaderViewsCount()); if (mOnTitleListener != null && object instanceof String) { String title = (String) object; mOnTitleListener.onItemClick(view, title); } else if (mOnContentListener != null && object instanceof IndexEntity) { IndexEntity indexEntity = (IndexEntity) object; mOnContentListener.onItemClick(view, indexEntity); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { mIndexBar.onListViewScrollStateChanged(scrollState); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mIndexBar.onListViewScroll(firstVisibleItem); if (mTitleHeight == 0 || mTitleMap == null) return; if (firstVisibleItem < mListView.getHeaderViewsCount()) { if (mStickView.getVisibility() == VISIBLE) { mStickView.setVisibility(INVISIBLE); } return; } else if (firstVisibleItem == mListView.getHeaderViewsCount()) { if (mStickView.getVisibility() != VISIBLE) { if (mAdapter == null || !mAdapter.isFilter()) { mStickView.setVisibility(VISIBLE); } } } if (firstVisibleItem > mCurrentScrollItemPosition) { // 向下 mCurrentScrollItemPosition = firstVisibleItem; processStick(firstVisibleItem, totalItemCount); } else if (firstVisibleItem < mCurrentScrollItemPosition) { // 向上 mCurrentScrollItemPosition = firstVisibleItem; processStick(firstVisibleItem, totalItemCount); } else { View firstView = mListView.getChildAt(0); if (firstView == null) return; int top = firstView.getTop(); if (top < mCurrentScrollItemTop) { // 向下 mCurrentScrollItemTop = top; processStick(firstVisibleItem, totalItemCount); } else { // 向上 mCurrentScrollItemTop = top; processStick(firstVisibleItem, totalItemCount); } } } private void processStick(int firstVisibleItem, int totalItemCount) { if (firstVisibleItem < totalItemCount - 1 && mTitleMap.get(firstVisibleItem - mListView.getHeaderViewsCount() + 1) != null) { int nextTop = mListView.getChildAt(1).getTop(); if (nextTop <= mTitleHeight) { if (mStickView.getVisibility() != VISIBLE) { if (mAdapter == null || !mAdapter.isFilter()) { mStickView.setVisibility(VISIBLE); } } mStickView.setTranslationY(nextTop - mTitleHeight); } } } public interface OnItemContentClickListener { void onItemClick(View v, IndexEntity indexEntity); } public interface OnItemTitleClickListener { void onItemClick(View v, String title); } /** * 更新相关View数据 */ private void updateListView() { mListView.post(new Runnable() { @Override public void run() { final TextView titleTextView = mAdapter.getTitleTextView(); titleTextView.post(new Runnable() { @Override public void run() { mTitleHeight = titleTextView.getHeight(); } }); } }); mTitleMap = mAdapter.getTitleMap(); if (mStickView == null) { if (mTitleMap.size() > 0) { View view = mAdapter.getView(mTitleMap.keyAt(0), null, mListView); mStickView = (TextView) view; addView(mStickView, 1); mStickView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mOnTitleListener != null) { mOnTitleListener.onItemClick(v, mStickView.getText().toString()); } } }); if (mListView.getHeaderViewsCount() > 0) { mStickView.setVisibility(INVISIBLE); } } mIndexBar.setOnSearchResultListener(new IndexBar.OnSearchResultListener() { @Override public void onStart() { if (!mSearchLayout.isProgressVisible()) { if (mContext instanceof Activity) { ((Activity) mContext).runOnUiThread(new Runnable() { @Override public void run() { mSearchLayout.showProgress(); } }); } } } @Override public void onResult(boolean isSearch, int dataSize) { if (mAdapter == null) return; if (!isSearch || dataSize > 0) { mSearchLayout.hide(); } else { mSearchLayout.showTip(); } mListView.setSelection(1); if (mAdapter.isFilter()) { if (mAddHeaderViewList != null) { for (View view : mAddHeaderViewList) { if (view.getHeight() != 0) { view.setTag(view.getHeight()); view.getLayoutParams().height = 1; } } } if (mStickView != null && mStickView.getVisibility() == VISIBLE) { mStickView.setVisibility(INVISIBLE); } } else { if (mAddHeaderViewList != null) { for (View view : mAddHeaderViewList) { view.getLayoutParams().height = (int) view.getTag(); } } if (mStickView != null && mStickView.getVisibility() != VISIBLE) { mStickView.setVisibility(VISIBLE); } } mListView.smoothScrollToPosition(0); } }); } else { if (mTitleMap.size() == 0) { removeView(mStickView); mStickView = null; } else { String title = mAdapter.getItemTitle(mListView.getFirstVisiblePosition()); mStickView.setText(title); } } mAdapter.notifyDataSetChanged(); mIndexBar.setListView(mListView); mIndexBar.postInvalidate(); } @Override protected void onDetachedFromWindow() { if (mBindDataHandlerThread != null) { mBindDataHandlerThread.quit(); } super.onDetachedFromWindow(); } }