仿連絡人清單或其他上的字母索引,仿連絡人字母索引
這個小功能github有很多。不同的應用可能需求稍微有些差別,比如listview滑動時字母是不是跟隨滑動;手動點擊字母是不是在螢幕中間實現一個提示。實現思路上也有多種,比如自己去draw每個字母,然後處理滑動;有些可能就是藉助TextView來展示字母列表。有些是點擊索引的時候,整個索引的背景出來,離開後背景消失。當然這些都是細節問題。看了幾個demo,感覺還目前自己的需求有些差別,而且為了實現“大而全”有些多餘的東西,因此決定自己寫個。
需求是這樣的:中文參與索引,字母或者其他開頭的item不參與索引,直接放到第一個“#”裡;點擊字母索引其被點擊字母列表顏色跟隨變化;手動滑動listview字母索引的字母顏色也跟隨變化;自己順手加了個點擊某個字母彈出提示的介面。最後效果如:
決定直接使用垂直方向的LinearLayout嵌套TextView來展示字母列表。思路如下:根據索引高度,大概計算每個字母的高度,然後添加到Linearlayout中;然後處理Linearlayout的touch事件,拿到座標計算當前應該索引哪個。其中遇到的主要問題就是TextView的把握,比如它的高度改如何計算,真正實現的時候你會發現它有個paddingTop和paddingBottom。其他的比如滑動邏輯這些調試下即可,直接上代碼:
package com.ttdevs.indicator.widget;import java.util.List;import android.annotation.SuppressLint;import android.content.Context;import android.util.AttributeSet;import android.util.TypedValue;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.TextView;import com.ttdevs.indicator.R;/** * * 右側字母索引。可作為一個獨立的view插入自己的布局中<br> * <br> * 描述:<br> * 線型布局中嵌套一個線型布局(目的為了增大可滑動地區的面積,若不考慮此可不使用嵌套)。<br> * 每個字母插入內嵌的線性布局中,根據控制項的高度計算每個字母的尺寸。<br> * <br> * 使用:<br> * 將此view插入你的布局檔案中,初始化完成之後設定你的ListView和索引用的資料來源(需要自己組織排序資料來源)即可。<br> * <br> * 資料來源的組織:若某項不參與排序則資料來源中設定為'0'或其他小於'A'的ASCII字元,內部會將所有字元轉換成大寫,所以務必在外部做好排序。<br> * <br> * 建議:若你的資料中包括ASCII碼為a0、20的字元,建議剔除,如:<br> * str.replace(' ', ' '); // a0->20 str.replaceAll(" ", "");<br> * <br> * 如果你處理的是中文,可使用libs中的pinyin4j處理 * * @author ttdevs 2014-07-31 * */public class ListViewLetterIndicator extends LinearLayout implements OnScrollListener {private static final String INDICATOR = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";private LinearLayout llMain;private ListView mListView; // ListViewprivate List<String> mData; // 資料來源private TextView tvAlert; // 顯示當前的字母private int mIndex; // 當前所處的indicator位置private boolean scrollable = true; //public ListViewLetterIndicator(Context context) {this(context, null);}public ListViewLetterIndicator(Context context, AttributeSet attrs) {super(context, attrs);setOrientation(LinearLayout.VERTICAL);setBackgroundColor(getResources().getColor(R.color.letter_indicator_background));// setPadding(8, 8, 8, 8);llMain = new LinearLayout(getContext());llMain.setOrientation(LinearLayout.VERTICAL);llMain.setGravity(Gravity.CENTER);// llMain.setPadding(2, 2, 2, 2);int width = (int) getResources().getDimension(R.dimen.letter_indicator_width);addView(llMain, width, LinearLayout.LayoutParams.MATCH_PARENT);}/** * 設定資料來源 * * @param lv * 綁定的ListView * @param data * 排序用的資料來源 */public void setData(ListView lv, List<String> data) {setData(lv, data, null);}/** * 設定資料來源 * * @param lv * 綁定的ListView * @param data * 排序用的資料來源 * @param tv * 顯示當前所處字母的TextView */@SuppressLint("DefaultLocale")public void setData(ListView lv, List<String> data, TextView tv) {mListView = lv;mData = data;tvAlert = tv;for (int i = 0; i < mData.size(); i++) {String str = mData.get(i);mData.set(i, str.toUpperCase());}mListView.setOnScrollListener(this);mIndex = 0;}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);initView();}private void initView() {int childCount = llMain.getChildCount();if (childCount == INDICATOR.length()) {// llMain.invalidate();return;}int height = llMain.getHeight();int textHeight = (int) Math.floor(height / (INDICATOR.length() + 6));LinearLayout.LayoutParams llpText = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);for (int i = 0; i < INDICATOR.length(); i++) {String str = String.valueOf(INDICATOR.charAt(i));TextView tvIndicator = new TextView(getContext());tvIndicator.setText(str);tvIndicator.setIncludeFontPadding(false);tvIndicator.setTextSize(TypedValue.COMPLEX_UNIT_PX, textHeight);tvIndicator.setTextColor(getResources().getColor(R.color.letter_indicator_text_normal));// tvIndicator.setPadding(0, -4, 0, -4);llMain.addView(tvIndicator, llpText);}}@SuppressLint("DefaultLocale")@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {int action = ev.getAction();switch (action) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE:scrollable = false;float y = ev.getY();float childY = 0;int index = 0;for (int i = 0; i < INDICATOR.length(); i++) {TextView view = (TextView) llMain.getChildAt(i);childY = view.getTop();int height = view.getHeight();if (childY < y && childY + height > y) {index = i;break;}view = null; // not neccessary}TextView view = (TextView) llMain.getChildAt(INDICATOR.length() - 1);if (y > view.getTop()) {index = INDICATOR.length() - 1;}view = null;changeIndicatorColor(index);char indexIndicator = INDICATOR.charAt(index);// A:65, #:23if (indexIndicator < 'A') {mListView.setSelection(0);} else {for (int i = 0; i < mData.size(); i++) {if (mData.get(i).charAt(0) >= indexIndicator) {mListView.setSelection(i);return true;}}}break;case MotionEvent.ACTION_UP:postDelayed(new Runnable() {@Overridepublic void run() {scrollable = true;}}, 100);showText("", false);break;default:break;}return true;}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {// invalidate();}@SuppressLint("DefaultLocale")@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {try {if (!scrollable || null == mData || mData.size() == 0) {return;}if (null != mData) {String str = mData.get(firstVisibleItem);char indicator = str.charAt(0);if (indicator < 'A') {changeIndicatorColor(0);return;}for (int i = 1; i < INDICATOR.length(); i++) {if (INDICATOR.charAt(i) == indicator) {changeIndicatorColor(i);return;}}}} catch (Exception e) {e.printStackTrace();}}private void changeIndicatorColor(int index) {if (mIndex != 0 && mIndex == index) {return;}TextView tv = (TextView) llMain.getChildAt(mIndex);tv.setTextColor(getResources().getColor(R.color.letter_indicator_text_normal));tv = (TextView) llMain.getChildAt(index);tv.setTextColor(getResources().getColor(R.color.letter_indicator_text_select));mIndex = index;showText(String.valueOf(INDICATOR.charAt(mIndex)), true);}private void showText(String text, boolean isShow) {if (null != tvAlert) {tvAlert.setText(text);tvAlert.setVisibility(isShow ? View.VISIBLE : View.INVISIBLE);}}// private Drawable initBackground() {// float[] roundRect = new float[] { 12, 12, 12, 12, 12, 12, 12, 12 };// RoundRectShape reoundRechShape = new RoundRectShape(roundRect, null,// null);// ShapeDrawable drawable = new ShapeDrawable(reoundRechShape);// drawable.getPaint().setColor(Color.parseColor(GRAY_AA));// // drawable.getPaint().setStyle(Paint.Style.FILL);// return drawable;// }}
測試過程中發現,有些item明明是漢字開頭的(這個時候你可能還需要個漢字轉拼音的工具包),卻出現在“#”裡,仔細檢查,你會發現,那個並不是空格,細節請看代碼注釋。 由於很簡單,不做詳細介紹,僅僅做個記錄,有問題可以留言一起討論。最後附個測試demo:下載
百度詞條 字母索引問題
目錄很好用啊
你要先定義標題樣式和層級,然後插入——引用——索引和目錄——目錄就可以自動插入了。
關於編製索引
索引列出了一篇文檔中的詞條和主題,以及它們出現的頁碼。要編製索引,需要先在文檔中標記索引項目並產生索引。標記索引項目後,Microsoft Word 會在文檔中添加特殊的 XE(索引項目)域:
您可以:
為單個的詞、片語或符號建立索引項目。
為包含連續多頁的主題建立索引項目。
引用另外的項,例如,“Transportation 請參閱 Bicycles”。
在標記好了所有的索引項目之後,接下來您要做的事就是選擇一種設計好的索引格式並產生最終的索引。Word 會收集索引項目,將它們按字母順序排序,引用其頁碼,找到並且刪除同一頁上的重複索引,然後在文檔中顯示該索引。
Word 將一些符號(如 @)置於索引的前面。如果您選擇的索引格式包含字母組的標題,符號位於 #(數字記號)標題之後。
若要使用其他選項自訂索引,可使用域。例如,可以僅為文檔中的一部分建立索引。
安卓手機連絡人的字母索引不好使了,手機裡面的一些觸摸按鍵也不好使了,救助
返廠吧,實在不行刷機試一下