Android之通讯录索引SideBarView

废话不多说

首先上效果图:


要做的这样的效果 首先导入一个pinyin包

  compile 'com.belerweb:pinyin4j:2.5.1'

接下来做一些准备工作吧

主页面的Xml

       里面用到了自定义的 SideBarView

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:poplar="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#d5d5d5">

    <TextView
        android:id="@+id/tv_friends_toolbar"
        android:layout_width="match_parent"
        android:layout_height="52dp"
        android:background="#41c2fc"
        android:gravity="center"
        android:text="通讯录"
        android:textColor="#fff"
        android:textSize="20sp"/>

    <ListView
        android:id="@+id/lv_friends_listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/tv_friends_toolbar"/>

    <com.cooltone.demo.SideBarView
        android:id="@+id/sbv_friends_bar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/tv_friends_toolbar"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        poplar:additionalTipOffset="40dp"
        poplar:fontColor="#6e6e6e"
        poplar:maxBezierHeight="150dp"
        poplar:maxBezierWidth="180dp"
        poplar:maxFontSize="60"
        poplar:minFontSize="32"
        poplar:tipFontColor="#41c2fc"
        poplar:tipFontSize="72"
        poplar:widthOffset="15dp"/>

    <TextView
        android:id="@+id/tv_friends_center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_below="@id/tv_friends_toolbar"
        android:layout_centerInParent="true"
        android:background="@drawable/sidebar_background"
        android:gravity="center"
        android:text="A"
        android:textColor="#fff"
        android:textSize="36sp"
        android:visibility="gone"/>

</RelativeLayout>

上面包红没关系啊 一步步来,记得一会自定义View 要换包名。接下来是listView 条目 布局

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_friend_title"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:background="#919191"
        android:gravity="center_vertical"
        android:paddingLeft="20dp"
        android:text="A"
        android:textSize="14sp"/>

    <TextView
        android:id="@+id/tv_friend_name"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="#fff"
        android:gravity="center_vertical"
        android:paddingLeft="20dp"
        android:text="宋江"
        android:textColor="#00f"
        android:textSize="16sp"/>

</LinearLayout>
接下来什么呢 ,在drawable文件下 建一个背景吧 sidebar_background

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <!-- 通讯录 -->
    <corners android:radius="25dp"/>
    <solid android:color="#66000000"/>
</shape>

接下来就是重要人物出场了

建 SideBarView 继承 View 写我们的索引控件 

里面的东西自己想研究下的话, 可以自己研究,本人也不是很懂,用了贝塞尔曲线

/**
 * 作者:CoolTone
 * 描述:右侧导航栏字母列表
 */
public class SideBarView extends View {

    private OnTouchLetterChangedListener mListener;

    public interface OnTouchLetterChangedListener {
        void onTouchLetterChanged(String letter);
    }

    public void setOnTouchLetterChangedListener(OnTouchLetterChangedListener listener) {
        this.mListener = listener;
    }

    // 向右偏移多少画字符, default 30
    float mWidthOffset = 30.0f;
    // 最小字体大小
    int mMinFontSize = 24;
    // 最大字体大小
    int mMaxFontSize = 48;
    // 提示字体大小
    int mTipFontSize = 52;
    // 提示字符的额外偏移
    float mAdditionalTipOffset = 20.0f;
    // 贝塞尔曲线控制的高度
    float mMaxBezierHeight = 150.0f;
    // 贝塞尔曲线单侧宽度
    float mMaxBezierWidth = 240.0f;
    // 贝塞尔曲线单侧模拟线量
    int mMaxBezierLines = 32;
    // 列表字符颜色
    int mFontColor = 0xffffffff;
    // 提示字符颜色
    int mTipFontColor = 0xffd33e48;

    private final String[] ConstChar = {"#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"
            , "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};

    int mChooseIndex = -1;
    Paint mPaint = new Paint();
    PointF mTouch = new PointF();

    PointF[] mBezier1;
    PointF[] mBezier2;

    float mLastOffset[] = new float[ConstChar.length]; // 记录每一个字母的x方向偏移量, 数字<=0
    PointF mPointF = new PointF();

    Scroller mScroller;
    boolean mAnimating = false;
    float mAnimationOffset;

    boolean mHideAnimation = false;
    int mAlpha = 255;

    Handler mHideWaitingHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                mHideAnimation = true;
                mAnimating = false;
                SideBarView.this.invalidate();
                return;
            }
            super.handleMessage(msg);
        }
    };

    public SideBarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initData(context, attrs);
    }

    public SideBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initData(context, attrs);
    }

    public SideBarView(Context context) {
        super(context);
        initData(null, null);
    }

    private void initData(Context context, AttributeSet attrs) {

        if (context != null && attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FancyIndexer, 0, 0);

            mWidthOffset = a.getDimension(R.styleable.FancyIndexer_widthOffset, mWidthOffset);
            // 最小
            mMinFontSize = a.getInteger(R.styleable.FancyIndexer_minFontSize, mMinFontSize);
            // 最大
            mMaxFontSize = a.getInteger(R.styleable.FancyIndexer_maxFontSize, mMaxFontSize);
            mTipFontSize = a.getInteger(R.styleable.FancyIndexer_tipFontSize, mTipFontSize);
            mMaxBezierHeight = a.getDimension(R.styleable.FancyIndexer_maxBezierHeight, mMaxBezierHeight);
            mMaxBezierWidth = a.getDimension(R.styleable.FancyIndexer_maxBezierWidth, mMaxBezierWidth);
            mMaxBezierLines = a.getInteger(R.styleable.FancyIndexer_maxBezierLines, mMaxBezierLines);
            mAdditionalTipOffset = a.getDimension(R.styleable.FancyIndexer_additionalTipOffset, mAdditionalTipOffset);

            // 颜色
            mFontColor = a.getColor(R.styleable.FancyIndexer_fontColor, mFontColor);

            // 提示颜色
            mTipFontColor = a.getColor(R.styleable.FancyIndexer_tipFontColor, mTipFontColor);
            a.recycle();
        }
        mScroller = new Scroller(getContext());
        mTouch.x = 0;
        mTouch.y = -10 * mMaxBezierWidth;

        mBezier1 = new PointF[mMaxBezierLines];
        mBezier2 = new PointF[mMaxBezierLines];

        calculateBezierPoints();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 控件宽高
        int height = getHeight();
        int width = getWidth();
        // 单个字母高度
        float singleHeight = height / (float) ConstChar.length;

        int workHeight = 0;

        if (mAlpha == 0)
            return;

        mPaint.reset();

        int saveCount = 0;

        if (mHideAnimation) {
            saveCount = canvas.save();
            canvas.saveLayerAlpha(0, 0, width, height, mAlpha, Canvas.ALL_SAVE_FLAG);
        }

        for (int i = 0; i < ConstChar.length; i++) {
            mPaint.setColor(mFontColor);
            mPaint.setAntiAlias(true);

            float xPos = width - mWidthOffset;
            float yPos = workHeight + singleHeight / 2;

            // float adjustX = adjustXPos( yPos, i == mChooseIndex );
            // 根据当前字母y的位置计算得到字体大小
            int fontSize = adjustFontSize(i, yPos);
            mPaint.setTextSize(fontSize);

            // 添加一个字母的高度
            workHeight += singleHeight;

            // 绘制字母
            drawTextInCenter(canvas, ConstChar[i], xPos + ajustXPosAnimation(i, yPos), yPos);

            // 绘制的字母和当前触摸到的一致, 绘制红色被选中字母
            if (i == mChooseIndex) {
                mPaint.setColor(mTipFontColor);
                mPaint.setFakeBoldText(true);
                mPaint.setTextSize(mTipFontSize);
                yPos = mTouch.y;

                float pos = 0;

                if (mAnimating || mHideAnimation) {
                    pos = mPointF.x;
                    yPos = mPointF.y;
                } else {
                    pos = xPos + ajustXPosAnimation(i, yPos) - mAdditionalTipOffset;
                    mPointF.x = pos;
                    mPointF.y = yPos;
                }
                drawTextInCenter(canvas, ConstChar[i], pos, yPos);
            }
            mPaint.reset();
        }

        if (mHideAnimation) {
            canvas.restoreToCount(saveCount);
        }
    }

    /**
     * @param canvas  画板
     * @param string  被绘制的字母
     * @param xCenter 字母的中心x方向位置
     * @param yCenter 字母的中心y方向位置
     */
    private void drawTextInCenter(Canvas canvas, String string, float xCenter, float yCenter) {

        Paint.FontMetrics fm = mPaint.getFontMetrics();

        float fontHeight = mPaint.getFontSpacing();
        float drawY = yCenter + fontHeight / 2 - fm.descent;

        if (drawY < -fm.ascent - fm.descent)
            drawY = -fm.ascent - fm.descent;

        if (drawY > getHeight())
            drawY = getHeight();

        mPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(string, xCenter, drawY, mPaint);
    }

    private int adjustFontSize(int i, float yPos) {

        // 根据水平方向偏移量计算出一个放大的字号
        float adjustX = Math.abs(ajustXPosAnimation(i, yPos));

        int adjustSize = (int) ((mMaxFontSize - mMinFontSize) * adjustX / mMaxBezierHeight) + mMinFontSize;

        return adjustSize;
    }

    /**
     * x 方向的向左偏移量
     *
     * @param i    当前字母的索引
     * @param yPos y方向的初始位置
     * @return
     */
    private float ajustXPosAnimation(int i, float yPos) {

        float offset;
        if (this.mAnimating || this.mHideAnimation) {
            // 正在动画中或在做隐藏动画
            offset = mLastOffset[i];
            if (offset != 0.0f) {
                offset += this.mAnimationOffset;
                if (offset > 0)
                    offset = 0;
            }
        } else {
            // 根据当前字母y方向位置, 计算水平方向偏移量
            offset = adjustXPos(yPos);
            // 当前触摸的x方向位置
            float xPos = mTouch.x;

            float width = getWidth() - mWidthOffset;
            width = width - 60;

            // 字母绘制时向左偏移量 进行修正, offset需要是<=0的值
            if (offset != 0.0f && xPos > width)
                offset += (xPos - width);
            if (offset > 0)
                offset = 0;

            mLastOffset[i] = offset;
        }
        return offset;
    }

    private float adjustXPos(float yPos) {

        float dis = yPos - mTouch.y; // 字母y方向位置和触摸时y值坐标的差值, 距离越小, 得到的水平方向偏差越大
        if (dis > -mMaxBezierWidth && dis < mMaxBezierWidth) {
            // 在2个贝赛尔曲线宽度范围以内 (一个贝赛尔曲线宽度是指一个山峰的一边)
            // 第一段 曲线
            if (dis > mMaxBezierWidth / 4) {
                for (int i = mMaxBezierLines - 1; i > 0; i--) {
                    // 从下到上, 逐个计算
                    if (dis == -mBezier1[i].y) // 落在点上
                        return mBezier1[i].x;

                    // 如果距离dis落在两个贝塞尔曲线模拟点之间, 通过三角函数计算得到当前dis对应的x方向偏移量
                    if (dis > -mBezier1[i].y && dis < -mBezier1[i - 1].y) {
                        return (dis + mBezier1[i].y) * (mBezier1[i - 1].x - mBezier1[i].x) / (-mBezier1[i - 1].y + mBezier1[i].y) + mBezier1[i].x;
                    }
                }
                return mBezier1[0].x;
            }

            // 第三段 曲线, 和第一段曲线对称
            if (dis < -mMaxBezierWidth / 4) {
                for (int i = 0; i < mMaxBezierLines - 1; i++) {
                    // 从上到下
                    if (dis == mBezier1[i].y) // 落在点上
                        return mBezier1[i].x;

                    // 如果距离dis落在两个贝塞尔曲线模拟点之间, 通过三角函数计算得到当前dis对应的x方向偏移量
                    if (dis > mBezier1[i].y && dis < mBezier1[i + 1].y) {
                        return (dis - mBezier1[i].y) * (mBezier1[i + 1].x - mBezier1[i].x) / (mBezier1[i + 1].y - mBezier1[i].y) + mBezier1[i].x;
                    }
                }
                return mBezier1[mMaxBezierLines - 1].x;
            }

            // 第二段 峰顶曲线
            for (int i = 0; i < mMaxBezierLines - 1; i++) {

                if (dis == mBezier2[i].y)
                    return mBezier2[i].x;

                // 如果距离dis落在两个贝塞尔曲线模拟点之间, 通过三角函数计算得到当前dis对应的x方向偏移量
                if (dis > mBezier2[i].y && dis < mBezier2[i + 1].y) {
                    return (dis - mBezier2[i].y) * (mBezier2[i + 1].x - mBezier2[i].x) / (mBezier2[i + 1].y - mBezier2[i].y) + mBezier2[i].x;
                }
            }
            return mBezier2[mMaxBezierLines - 1].x;
        }
        return 0.0f;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        final float y = event.getY();
        final int oldmChooseIndex = mChooseIndex;
        final OnTouchLetterChangedListener listener = mListener;
        final int c = (int) (y / getHeight() * ConstChar.length);

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (this.getWidth() > mWidthOffset) {
                    if (event.getX() < this.getWidth() - mWidthOffset)
                        return false;
                }

                mHideWaitingHandler.removeMessages(1);

                mScroller.abortAnimation();
                mAnimating = false;
                mHideAnimation = false;
                mAlpha = 255;

                mTouch.x = event.getX();
                mTouch.y = event.getY();

                if (oldmChooseIndex != c && listener != null) {

                    if (c > 0 && c < ConstChar.length) {
                        listener.onTouchLetterChanged(ConstChar[c]);
                        mChooseIndex = c;
                    }
                }
                invalidate();
                break;

            case MotionEvent.ACTION_MOVE:
                mTouch.x = event.getX();
                mTouch.y = event.getY();
                invalidate();
                if (oldmChooseIndex != c && listener != null) {

                    if (c >= 0 && c < ConstChar.length) {
                        listener.onTouchLetterChanged(ConstChar[c]);
                        mChooseIndex = c;
                    }
                }
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mTouch.x = event.getX();
                mTouch.y = event.getY();

                mScroller.startScroll(0, 0, (int) mMaxBezierHeight, 0, 500);
                mAnimating = true;
                postInvalidate();
                break;
        }
        return true;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            if (mAnimating) {
                float x = mScroller.getCurrX();
                mAnimationOffset = x;
            } else if (mHideAnimation) {
                mAlpha = 255 - mScroller.getCurrX();
            }
            invalidate();
        } else if (mScroller.isFinished()) {
            if (mAnimating) {
                mHideWaitingHandler.sendEmptyMessage(1);
            } else if (mHideAnimation) {
                mHideAnimation = false;
                this.mChooseIndex = -1;
                mTouch.x = -10000;
                mTouch.y = -10000;
            }
        }
    }

    /**
     * 计算出所有贝塞尔曲线上的点
     * 个数为 mMaxBezierLines * 2 = 64
     */
    private void calculateBezierPoints() {

        PointF mStart = new PointF();   // 开始点
        PointF mEnd = new PointF();     // 结束点
        PointF mControl = new PointF(); // 控制点

        // 计算第一段红色部分 贝赛尔曲线的点
        // 开始点
        mStart.x = 0.0f;
        mStart.y = -mMaxBezierWidth;

        // 控制点
        mControl.x = 0.0f;
        mControl.y = -mMaxBezierWidth / 2;

        // 结束点
        mEnd.x = -mMaxBezierHeight / 2;
        mEnd.y = -mMaxBezierWidth / 4;

        mBezier1[0] = new PointF();
        mBezier1[mMaxBezierLines - 1] = new PointF();

        mBezier1[0].set(mStart);
        mBezier1[mMaxBezierLines - 1].set(mEnd);

        for (int i = 1; i < mMaxBezierLines - 1; i++) {
            mBezier1[i] = new PointF();
            mBezier1[i].x = calculateBezier(mStart.x, mEnd.x, mControl.x, i / (float) mMaxBezierLines);
            mBezier1[i].y = calculateBezier(mStart.y, mEnd.y, mControl.y, i / (float) mMaxBezierLines);
        }

        // 计算第二段蓝色部分 贝赛尔曲线的点
        mStart.y = -mMaxBezierWidth / 4;
        mStart.x = -mMaxBezierHeight / 2;

        mControl.y = 0.0f;
        mControl.x = -mMaxBezierHeight;

        mEnd.y = mMaxBezierWidth / 4;
        mEnd.x = -mMaxBezierHeight / 2;

        mBezier2[0] = new PointF();
        mBezier2[mMaxBezierLines - 1] = new PointF();

        mBezier2[0].set(mStart);
        mBezier2[mMaxBezierLines - 1].set(mEnd);

        for (int i = 1; i < mMaxBezierLines - 1; i++) {
            mBezier2[i] = new PointF();
            mBezier2[i].x = calculateBezier(mStart.x, mEnd.x, mControl.x, i / (float) mMaxBezierLines);
            mBezier2[i].y = calculateBezier(mStart.y, mEnd.y, mControl.y, i / (float) mMaxBezierLines);
        }
    }

    /**
     * 贝塞尔曲线核心算法
     *
     * @param start
     * @param end
     * @param control
     * @param val
     * @return 公式及动图, 维基百科: https://en.wikipedia.org/wiki/B%C3%A9zier_curve
     * 中文可参考此网站: http://blog.csdn.net/likendsl/article/details/7852658
     */
    private float calculateBezier(float start, float end, float control, float val) {
        float t = val;
        float s = 1 - t;
        float ret = start * s * s + 2 * control * s * t + end * t * t;
        return ret;
    }
}
之后来,当然是善后啦 因为里面会包红呀!

接下来在values里面创建attrs文件

    <!-- 通讯录 -->
    <declare-styleable name="FancyIndexer">
        <attr name="widthOffset" format="dimension"/>
        <attr name="minFontSize" format="integer"/>
        <attr name="maxFontSize" format="integer"/>
        <attr name="tipFontSize" format="integer"/>
        <attr name="maxBezierHeight" format="dimension"/>
        <attr name="maxBezierWidth" format="dimension"/>
        <attr name="maxBezierLines" format="integer"/>
        <attr name="additionalTipOffset" format="dimension"/>
        <attr name="fontColor" format="color"/>
        <attr name="tipFontColor" format="color"/>
    </declare-styleable>

做完这些估计就不会包红了吧! 因为我这写完了没有什么红 所以我也记不清了!

这里因为要给好友列表排序 当然少不了拼音 和 实体类

FriendEntity实体类

/**
 * 作者: CoolTone
 * 描述: 好友列表实体类
 */
public class FriendEntity implements Comparable<FriendEntity> {
    private String name;
    private String pinyin;

    public FriendEntity() {
    }

    @Override
    public int compareTo(FriendEntity another) {
        return pinyin.compareTo(another.pinyin);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPinyin() {
        return pinyin;
    }

    public void setPinyin(String pinyin) {
        this.pinyin = pinyin;
    }

    @Override
    public String toString() {
        return "FriendEntity{" +
                "name='" + name + '\'' +
                ", pinyin='" + pinyin + '\'' +
                '}';
    }
}

对于上面要说一句 不要忘了 很重要哦!排序用的

    @Override
    public int compareTo(FriendEntity another) {
        return pinyin.compareTo(another.pinyin);
    }
接下来 PinyinUtil 帮助类

/**
 * 作者: CoolTone
 * 描述: 通讯录 拼音 帮助类
 */
public class PinyinUtil {

    /**
     * 根据汉字获取对应的拼音
     *
     * @param str
     * @return
     */
    public static String getPinyin(String str) {
        // 设置输出配置
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
        // 设置大写
        format.setCaseType(HanyuPinyinCaseType.UPPERCASE);
        // 设置不需要音调
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);

        StringBuilder sb = new StringBuilder();

        // 获取字符数组
        char[] charArray = str.toCharArray();
        for (int i = 0; i < charArray.length; i++) {
            char c = charArray[i];
            if (Character.isWhitespace(c)) { // 如果是空格, 跳过当前的循环
                continue;
            }

            if (c > 128 || c < -127) {  // 可能是汉字
                try {   // 根据字符获取对应的拼音
                    String s = PinyinHelper.toHanyuPinyinStringArray(c, format)[0];
                    sb.append(s);
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                }
            } else {
                // ~!@#$%^&*()_+{}|:"<>?\/1234567890
                // 不需要转换, 直接添加
                // 根据字符获取对应的拼音
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
这个工具里面有注释就不多说了!

接下来是Adapter的工具类 因为本人比较喜欢用ListView 所以这个父类的Adapter 很有用

/**
 * 作者: CoolTone
 * 描述: 父类Adapter
 */
public abstract class SecondAdapter<T> extends BaseAdapter {

    private List<T> mData;           // 数据信息
    private LayoutInflater mInflater;// 填充布局
    private Context mContext;        // 上下文

    public SecondAdapter(Context context) {
        mData = new ArrayList<>();
        mInflater = LayoutInflater.from(context);
        mContext = context;
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public T getItem(int position) {
        return mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public List<T> getData() {
        return mData;
    }

    public LayoutInflater getInflater() {
        return mInflater;
    }

    public Context getContext() {
        return mContext;
    }

    /**
     * 添加集合
     */
    public void addAll(List<T> data) {
        mData.addAll(data);
        notifyDataSetChanged();
    }

    /**
     * 清除集合
     */
    public void clear() {
        mData.clear();
        notifyDataSetChanged();
    }
}
然后就是写 FriendAdapter 主要赋值嘛 

这里面要做一些逻辑处理:

比如判断是否是拼音,是否拼音的第一位等等

/**
 * 作者: CoolTone
 * 描述: 数据Adapter
 */
public class FriendAdapter extends SecondAdapter<FriendEntity> {

    public FriendAdapter(Context context) {
        super(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = getInflater().inflate(R.layout.item_friend, null);
        }
        ItemViewHolder holder = (ItemViewHolder) convertView.getTag();
        if (holder == null) {
            holder = new ItemViewHolder(convertView);
            convertView.setTag(holder);
        }
        holder.bindView(position);
        return convertView;
    }

    class ItemViewHolder {
        private final TextView mName;
        private final TextView mTitle;

        public ItemViewHolder(View itemView) {
            mName = ((TextView) itemView.findViewById(R.id.tv_friend_name));
            mTitle = (TextView) itemView.findViewById(R.id.tv_friend_title);
        }

        public void bindView(int position) {
            FriendEntity friend = getData().get(position);
            String pinyin = friend.getPinyin();
            if (!pinyin.isEmpty()) {
                String s = String.valueOf(pinyin.charAt(0));
                if ((s.charAt(0) <= 'Z' && s.charAt(0) >= 'A') || (s.charAt(0) <= 'z' && s.charAt(0) >= 'a')) {
                    oneYesLetter(position, friend, s);
                } else {
                    oneNoLetter(position, friend, s);
                }
            }
        }

        // 第一位是否是字母 Y
        private void oneYesLetter(int position, FriendEntity friend, String currentLetter) {
            String indexStr = null;
            if (position == 0) {    // 如果是第一位
                indexStr = currentLetter;
            } else {                // 获取上一个拼音
                String preLetter = getData().get(position - 1).getPinyin().charAt(0) + "";
                if (!TextUtils.equals(currentLetter, preLetter)) {
                    indexStr = currentLetter;   // 当跟上一个不同时, 赋值, 显示
                }
            }
            mTitle.setVisibility(indexStr == null ? View.GONE : View.VISIBLE);
            mTitle.setText(indexStr);
            mName.setText(friend.getName());
        }

        // 第一位是否是字母 N
        private void oneNoLetter(int position, FriendEntity friend, String currentLetter) {
            String indexStr = null;
            if (position == 0) {  // 如果是第一位
                indexStr = currentLetter;
            }
            mTitle.setVisibility(indexStr == null ? View.GONE : View.VISIBLE);
            mTitle.setText("#");
            mName.setText(friend.getName());
        }
    }
}

这就完了! 但是还有写主程序呀!你逗我

/**
 * 作者: CoolTone
 * 描述: 好友列表
 */
public class MainActivity extends AppCompatActivity implements SideBarView.OnTouchLetterChangedListener {

    private ArrayList<FriendEntity> mFriends;
    private FriendAdapter mAdapter;
    private ListView mListView;
    private SideBarView mSideView;
    private TextView mLetter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
        setListener();
    }

    private void setListener() {
        mSideView.setOnTouchLetterChangedListener(this);
    }

    private void initData() {
        mFriends = new ArrayList<>();
        mAdapter = new FriendAdapter(this);
        mListView.setAdapter(mAdapter);

        String[] names = new String[]{"逗霸最伟大", "鬧夠了就去吃藥", "破灭等于拥有", "贪恋剧情小丑",
                "若隐若现", "我叫意志力", "龍飛鳳舞", "别拿爱情说宿命",
                "青春帅气先森", "年华仅是无效信", "年华仅是无效信", "中国好同桌", "折磨",
                "马子大", "不哭的理由", "唱歌跑调那叫范", "爱4太给力", "你与寂寞有染",};

        for (int i = 0; i < names.length; i++) {
            FriendEntity friend = new FriendEntity();
            friend.setName(names[i]);
            friend.setPinyin(PinyinUtil.getPinyin(names[i]));
            mFriends.add(friend);
        }
        Collections.sort(mFriends); // 这里千万不能忘
        mAdapter.addAll(mFriends);
        mAdapter.notifyDataSetChanged();
    }

    private void initView() {
        mListView = ((ListView) findViewById(R.id.lv_friends_listView));
        mSideView = ((SideBarView) findViewById(R.id.sbv_friends_bar));
        mLetter = ((TextView) findViewById(R.id.tv_friends_center));
    }

    @Override
    public void onTouchLetterChanged(String letter) {
//        showLetter(letter);
        if (!mFriends.isEmpty()) {
            for (int i = 0; i < mFriends.size(); i++) {
                FriendEntity friend = mFriends.get(i);
                String s = String.valueOf(friend.getPinyin().charAt(0));
                if (TextUtils.equals(s, letter)) { // 匹配成功, 中断循环, 跳转到i位置
                    mListView.setSelection(i);
                    break;
                }
            }
        }
    }


//    /**
//     * 显示字母提示
//     *
//     * @param letter
//     */
//    protected void showLetter(String letter) {
//        mLetter.setVisibility(View.VISIBLE);
//        mLetter.setText(letter);
//
//        // 取消掉刚刚所有的演示操作
//        mHandler.removeCallbacksAndMessages(null);
//        mHandler.postDelayed(new Runnable() {
//            @Override
//            public void run() {
//                // 隐藏
//                mCenter.setVisibility(View.GONE);
//            }
//        }, 500);
//    }
}

嗯哼大功告成了吧! 反正我已经晕了 应该没有丢下东西才对 

注意事项:好友列表数据  在给Adapter 添加数据之前千万不要忘了 Collections.sort(); 排序

for (int i = 0; i < names.length; i++) {
      FriendEntity friend = new FriendEntity();
      friend.setName(names[i]);
      friend.setPinyin(PinyinUtil.getPinyin(names[i]));
      mFriends.add(friend);
}
Collections.sort(mFriends); // 这里千万不能忘
mAdapter.addAll(mFriends);
mAdapter.notifyDataSetChanged();

Demo下载地址:http://download.csdn.net/detail/qq_35352552/9797827






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值