首页 > 代码库 > 防手机联系人功能的实现

防手机联系人功能的实现

package com.loaderman.contactsrecycledemo;/* * This code is cloned from DefaultItemAnimator provided by support library v7-recyclerView * * Copyright (C) 2014 The Android Open Source Project * Copyright (c) 2014-2015 Gabriele Mariotti. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */import android.support.v4.animation.AnimatorCompatHelper;import android.support.v4.view.ViewCompat;import android.support.v4.view.ViewPropertyAnimatorCompat;import android.support.v4.view.ViewPropertyAnimatorListener;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.SimpleItemAnimator;import android.view.View;import java.util.ArrayList;import java.util.List;/** * This implementation of {@link android.support.v7.widget.RecyclerView.ItemAnimator} provides basic * animations on remove, add, and move events that happen to the items in * a RecyclerView. * * @see android.support.v7.widget.RecyclerView#setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator) */public abstract class BaseItemAnimator extends SimpleItemAnimator {    /**     * RecyclerView     */    protected RecyclerView mRecyclerView;    //------------------------------------------------------------    // Constructor    //------------------------------------------------------------    public BaseItemAnimator(RecyclerView recyclerView){        mRecyclerView = recyclerView;    }    //------------------------------------------------------------    // Default Item Animator    //------------------------------------------------------------    private static final boolean DEBUG = false;    private ArrayList<RecyclerView.ViewHolder> mPendingRemovals = new ArrayList<>();    private ArrayList<RecyclerView.ViewHolder> mPendingAdditions = new ArrayList<>();    private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();    private ArrayList<ArrayList<RecyclerView.ViewHolder>> mAdditionsList = new ArrayList<>();    private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();    private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();    protected ArrayList<RecyclerView.ViewHolder> mAddAnimations = new ArrayList<>();    protected ArrayList<RecyclerView.ViewHolder> mMoveAnimations = new ArrayList<>();    protected ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<>();    protected ArrayList<RecyclerView.ViewHolder> mChangeAnimations = new ArrayList<>();    private static class MoveInfo {        public RecyclerView.ViewHolder holder;        public int fromX, fromY, toX, toY;        private MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {            this.holder = holder;            this.fromX = fromX;            this.fromY = fromY;            this.toX = toX;            this.toY = toY;        }    }    private static class ChangeInfo {        public RecyclerView.ViewHolder oldHolder, newHolder;        public int fromX, fromY, toX, toY;        private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {            this.oldHolder = oldHolder;            this.newHolder = newHolder;        }        private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,                           int fromX, int fromY, int toX, int toY) {            this(oldHolder, newHolder);            this.fromX = fromX;            this.fromY = fromY;            this.toX = toX;            this.toY = toY;        }        @Override        public String toString() {            return "ChangeInfo{" +                    "oldHolder=" + oldHolder +                    ", newHolder=" + newHolder +                    ", fromX=" + fromX +                    ", fromY=" + fromY +                    ", toX=" + toX +                    ", toY=" + toY +                    ‘}‘;        }    }    @Override    public void runPendingAnimations() {        boolean removalsPending = !mPendingRemovals.isEmpty();        boolean movesPending = !mPendingMoves.isEmpty();        boolean changesPending = !mPendingChanges.isEmpty();        boolean additionsPending = !mPendingAdditions.isEmpty();        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {            // nothing to animate            return;        }        // First, remove stuff        for (RecyclerView.ViewHolder holder : mPendingRemovals) {            animateRemoveImpl(holder);        }        mPendingRemovals.clear();        // Next, move stuff        if (movesPending) {            final ArrayList<MoveInfo> moves = new ArrayList<>();            moves.addAll(mPendingMoves);            mMovesList.add(moves);            mPendingMoves.clear();            Runnable mover = new Runnable() {                @Override                public void run() {                    for (MoveInfo moveInfo : moves) {                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,                                moveInfo.toX, moveInfo.toY);                    }                    moves.clear();                    mMovesList.remove(moves);                }            };            if (removalsPending) {                View view = moves.get(0).holder.itemView;                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());            } else {                mover.run();            }        }        // Next, change stuff, to run in parallel with move animations        if (changesPending) {            final ArrayList<ChangeInfo> changes = new ArrayList<>();            changes.addAll(mPendingChanges);            mChangesList.add(changes);            mPendingChanges.clear();            Runnable changer = new Runnable() {                @Override                public void run() {                    for (ChangeInfo change : changes) {                        animateChangeImpl(change);                    }                    changes.clear();                    mChangesList.remove(changes);                }            };            if (removalsPending) {                RecyclerView.ViewHolder holder = changes.get(0).oldHolder;                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());            } else {                changer.run();            }        }        // Next, add stuff        if (additionsPending) {            final ArrayList<RecyclerView.ViewHolder> additions = new ArrayList<>();            additions.addAll(mPendingAdditions);            mAdditionsList.add(additions);            mPendingAdditions.clear();            Runnable adder = new Runnable() {                public void run() {                    for (RecyclerView.ViewHolder holder : additions) {                        animateAddImpl(holder);                    }                    additions.clear();                    mAdditionsList.remove(additions);                }            };            if (removalsPending || movesPending || changesPending) {                long removeDuration = removalsPending ? getRemoveDuration() : 0;                long moveDuration = movesPending ? getMoveDuration() : 0;                long changeDuration = changesPending ? getChangeDuration() : 0;                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);                View view = additions.get(0).itemView;                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);            } else {                adder.run();            }        }    }    @Override    public boolean animateAdd(final RecyclerView.ViewHolder holder) {        resetAnimation(holder);        prepareAnimateAdd(holder);        ViewCompat.setAlpha(holder.itemView, 0);        mPendingAdditions.add(holder);        return true;    }    protected abstract void prepareAnimateAdd(final RecyclerView.ViewHolder holder);    protected abstract void animateAddImpl(final RecyclerView.ViewHolder holder);    @Override    public boolean animateRemove(final RecyclerView.ViewHolder holder) {        resetAnimation(holder);        mPendingRemovals.add(holder);        return true;    }    protected abstract void animateRemoveImpl(final RecyclerView.ViewHolder holder);    @Override    public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,                               int toX, int toY) {        final View view = holder.itemView;        fromX += ViewCompat.getTranslationX(holder.itemView);        fromY += ViewCompat.getTranslationY(holder.itemView);        resetAnimation(holder);        int deltaX = toX - fromX;        int deltaY = toY - fromY;        if (deltaX == 0 && deltaY == 0) {            dispatchMoveFinished(holder);            return false;        }        if (deltaX != 0) {            ViewCompat.setTranslationX(view, -deltaX);        }        if (deltaY != 0) {            ViewCompat.setTranslationY(view, -deltaY);        }        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));        return true;    }    private void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {        final View view = holder.itemView;        final int deltaX = toX - fromX;        final int deltaY = toY - fromY;        if (deltaX != 0) {            ViewCompat.animate(view).translationX(0);        }        if (deltaY != 0) {            ViewCompat.animate(view).translationY(0);        }        // TODO: make EndActions end listeners instead, since end actions aren‘t called when        // vpas are canceled (and can‘t end them. why?)        // need listener functionality in VPACompat for this. Ick.        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mMoveAnimations.add(holder);        animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {            @Override            public void onAnimationStart(View view) {                dispatchMoveStarting(holder);            }            @Override            public void onAnimationCancel(View view) {                if (deltaX != 0) {                    ViewCompat.setTranslationX(view, 0);                }                if (deltaY != 0) {                    ViewCompat.setTranslationY(view, 0);                }            }            @Override            public void onAnimationEnd(View view) {                animation.setListener(null);                dispatchMoveFinished(holder);                mMoveAnimations.remove(holder);                dispatchFinishedWhenDone();            }        }).start();    }    @Override    public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,                                 int fromX, int fromY, int toX, int toY) {        if (oldHolder == newHolder) {            // Don‘t know how to run change animations when the same view holder is re-used.            // run a move animation to handle position changes.            return animateMove(oldHolder, fromX, fromY, toX, toY);        }        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);        resetAnimation(oldHolder);        int deltaX = (int) (toX - fromX - prevTranslationX);        int deltaY = (int) (toY - fromY - prevTranslationY);        // recover prev translation state after ending animation        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);        if (newHolder != null) {            // carry over translation values            resetAnimation(newHolder);            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);            ViewCompat.setAlpha(newHolder.itemView, 0);        }        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));        return true;    }    private void animateChangeImpl(final ChangeInfo changeInfo) {        final RecyclerView.ViewHolder holder = changeInfo.oldHolder;        final View view = holder == null ? null : holder.itemView;        final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;        final View newView = newHolder != null ? newHolder.itemView : null;        if (view != null) {            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(                    getChangeDuration());            mChangeAnimations.add(changeInfo.oldHolder);            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);            oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {                @Override                public void onAnimationStart(View view) {                    dispatchChangeStarting(changeInfo.oldHolder, true);                }                @Override                public void onAnimationEnd(View view) {                    oldViewAnim.setListener(null);                    ViewCompat.setAlpha(view, 1);                    ViewCompat.setTranslationX(view, 0);                    ViewCompat.setTranslationY(view, 0);                    dispatchChangeFinished(changeInfo.oldHolder, true);                    mChangeAnimations.remove(changeInfo.oldHolder);                    dispatchFinishedWhenDone();                }            }).start();        }        if (newView != null) {            final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);            mChangeAnimations.add(changeInfo.newHolder);            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).                    alpha(1).setListener(new VpaListenerAdapter() {                @Override                public void onAnimationStart(View view) {                    dispatchChangeStarting(changeInfo.newHolder, false);                }                @Override                public void onAnimationEnd(View view) {                    newViewAnimation.setListener(null);                    ViewCompat.setAlpha(newView, 1);                    ViewCompat.setTranslationX(newView, 0);                    ViewCompat.setTranslationY(newView, 0);                    dispatchChangeFinished(changeInfo.newHolder, false);                    mChangeAnimations.remove(changeInfo.newHolder);                    dispatchFinishedWhenDone();                }            }).start();        }    }    private void endChangeAnimation(List<ChangeInfo> infoList, RecyclerView.ViewHolder item) {        for (int i = infoList.size() - 1; i >= 0; i--) {            ChangeInfo changeInfo = infoList.get(i);            if (endChangeAnimationIfNecessary(changeInfo, item)) {                if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {                    infoList.remove(changeInfo);                }            }        }    }    private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {        if (changeInfo.oldHolder != null) {            endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);        }        if (changeInfo.newHolder != null) {            endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);        }    }    private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) {        boolean oldItem = false;        if (changeInfo.newHolder == item) {            changeInfo.newHolder = null;        } else if (changeInfo.oldHolder == item) {            changeInfo.oldHolder = null;            oldItem = true;        } else {            return false;        }        ViewCompat.setAlpha(item.itemView, 1);        ViewCompat.setTranslationX(item.itemView, 0);        ViewCompat.setTranslationY(item.itemView, 0);        dispatchChangeFinished(item, oldItem);        return true;    }    @Override    public void endAnimation(RecyclerView.ViewHolder item) {        final View view = item.itemView;        // this will trigger end callback which should set properties to their target values.        ViewCompat.animate(view).cancel();        // TODO if some other animations are chained to end, how do we cancel them as well?        for (int i = mPendingMoves.size() - 1; i >= 0; i--) {            MoveInfo moveInfo = mPendingMoves.get(i);            if (moveInfo.holder == item) {                ViewCompat.setTranslationY(view, 0);                ViewCompat.setTranslationX(view, 0);                dispatchMoveFinished(item);                mPendingMoves.remove(i);            }        }        endChangeAnimation(mPendingChanges, item);        if (mPendingRemovals.remove(item)) {            ViewCompat.setAlpha(view, 1);            dispatchRemoveFinished(item);        }        if (mPendingAdditions.remove(item)) {            ViewCompat.setAlpha(view, 1);            dispatchAddFinished(item);        }        for (int i = mChangesList.size() - 1; i >= 0; i--) {            ArrayList<ChangeInfo> changes = mChangesList.get(i);            endChangeAnimation(changes, item);            if (changes.isEmpty()) {                mChangesList.remove(i);            }        }        for (int i = mMovesList.size() - 1; i >= 0; i--) {            ArrayList<MoveInfo> moves = mMovesList.get(i);            for (int j = moves.size() - 1; j >= 0; j--) {                MoveInfo moveInfo = moves.get(j);                if (moveInfo.holder == item) {                    ViewCompat.setTranslationY(view, 0);                    ViewCompat.setTranslationX(view, 0);                    dispatchMoveFinished(item);                    moves.remove(j);                    if (moves.isEmpty()) {                        mMovesList.remove(i);                    }                    break;                }            }        }        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {            ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);            if (additions.remove(item)) {                ViewCompat.setAlpha(view, 1);                dispatchAddFinished(item);                if (additions.isEmpty()) {                    mAdditionsList.remove(i);                }            }        }        // animations should be ended by the cancel above.        //noinspection PointlessBooleanExpression,ConstantConditions        if (mRemoveAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mRemoveAnimations list");        }        //noinspection PointlessBooleanExpression,ConstantConditions        if (mAddAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mAddAnimations list");        }        //noinspection PointlessBooleanExpression,ConstantConditions        if (mChangeAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mChangeAnimations list");        }        //noinspection PointlessBooleanExpression,ConstantConditions        if (mMoveAnimations.remove(item) && DEBUG) {            throw new IllegalStateException("after animation is cancelled, item should not be in "                    + "mMoveAnimations list");        }        dispatchFinishedWhenDone();    }    private void resetAnimation(RecyclerView.ViewHolder holder) {        AnimatorCompatHelper.clearInterpolator(holder.itemView);        endAnimation(holder);    }    @Override    public boolean isRunning() {        return (!mPendingAdditions.isEmpty() ||                !mPendingChanges.isEmpty() ||                !mPendingMoves.isEmpty() ||                !mPendingRemovals.isEmpty() ||                !mMoveAnimations.isEmpty() ||                !mRemoveAnimations.isEmpty() ||                !mAddAnimations.isEmpty() ||                !mChangeAnimations.isEmpty() ||                !mMovesList.isEmpty() ||                !mAdditionsList.isEmpty() ||                !mChangesList.isEmpty());    }    /**     * Check the state of currently pending and running animations. If there are none     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any     * listeners.     */    protected void dispatchFinishedWhenDone() {        if (!isRunning()) {            dispatchAnimationsFinished();        }    }    @Override    public void endAnimations() {        int count = mPendingMoves.size();        for (int i = count - 1; i >= 0; i--) {            MoveInfo item = mPendingMoves.get(i);            View view = item.holder.itemView;            ViewCompat.setTranslationY(view, 0);            ViewCompat.setTranslationX(view, 0);            dispatchMoveFinished(item.holder);            mPendingMoves.remove(i);        }        count = mPendingRemovals.size();        for (int i = count - 1; i >= 0; i--) {            RecyclerView.ViewHolder item = mPendingRemovals.get(i);            dispatchRemoveFinished(item);            mPendingRemovals.remove(i);        }        count = mPendingAdditions.size();        for (int i = count - 1; i >= 0; i--) {            RecyclerView.ViewHolder item = mPendingAdditions.get(i);            View view = item.itemView;            ViewCompat.setAlpha(view, 1);            dispatchAddFinished(item);            mPendingAdditions.remove(i);        }        count = mPendingChanges.size();        for (int i = count - 1; i >= 0; i--) {            endChangeAnimationIfNecessary(mPendingChanges.get(i));        }        mPendingChanges.clear();        if (!isRunning()) {            return;        }        int listCount = mMovesList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<MoveInfo> moves = mMovesList.get(i);            count = moves.size();            for (int j = count - 1; j >= 0; j--) {                MoveInfo moveInfo = moves.get(j);                RecyclerView.ViewHolder item = moveInfo.holder;                View view = item.itemView;                ViewCompat.setTranslationY(view, 0);                ViewCompat.setTranslationX(view, 0);                dispatchMoveFinished(moveInfo.holder);                moves.remove(j);                if (moves.isEmpty()) {                    mMovesList.remove(moves);                }            }        }        listCount = mAdditionsList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);            count = additions.size();            for (int j = count - 1; j >= 0; j--) {                RecyclerView.ViewHolder item = additions.get(j);                View view = item.itemView;                ViewCompat.setAlpha(view, 1);                dispatchAddFinished(item);                additions.remove(j);                if (additions.isEmpty()) {                    mAdditionsList.remove(additions);                }            }        }        listCount = mChangesList.size();        for (int i = listCount - 1; i >= 0; i--) {            ArrayList<ChangeInfo> changes = mChangesList.get(i);            count = changes.size();            for (int j = count - 1; j >= 0; j--) {                endChangeAnimationIfNecessary(changes.get(j));                if (changes.isEmpty()) {                    mChangesList.remove(changes);                }            }        }        cancelAll(mRemoveAnimations);        cancelAll(mMoveAnimations);        cancelAll(mAddAnimations);        cancelAll(mChangeAnimations);        dispatchAnimationsFinished();    }    void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {        for (int i = viewHolders.size() - 1; i >= 0; i--) {            ViewCompat.animate(viewHolders.get(i).itemView).cancel();        }    }    protected static class VpaListenerAdapter implements ViewPropertyAnimatorListener {        @Override        public void onAnimationStart(View view) {}        @Override        public void onAnimationEnd(View view) {}        @Override        public void onAnimationCancel(View view) {}    };}

 

package com.loaderman.contactsrecycledemo;import java.util.Arrays;import java.util.List;import java.util.Random;/** * @author amulya * @datetime 14 Oct 2014, 5:20 PM */public class ColorGenerator {    public static ColorGenerator DEFAULT;    public static ColorGenerator MATERIAL;    static {        DEFAULT = create(Arrays.asList(                0xfff16364,                0xfff58559,                0xfff9a43e,                0xffe4c62e,                0xff67bf74,                0xff59a2be,                0xff2093cd,                0xffad62a7,                0xff805781        ));        MATERIAL = create(Arrays.asList(                0xffe57373,                0xfff06292,                0xffba68c8,                0xff9575cd,                0xff7986cb,                0xff64b5f6,                0xff4fc3f7,                0xff4dd0e1,                0xff4db6ac,                0xff81c784,                0xffaed581,                0xffff8a65,                0xffd4e157,                0xffffd54f,                0xffffb74d,                0xffa1887f,                0xff90a4ae        ));    }    private final List<Integer> mColors;    private final Random mRandom;    public static ColorGenerator create(List<Integer> colorList) {        return new ColorGenerator(colorList);    }    private ColorGenerator(List<Integer> colorList) {        mColors = colorList;        mRandom = new Random(System.currentTimeMillis());    }    public int getRandomColor() {        return mColors.get(mRandom.nextInt(mColors.size()));    }    public int getColor(Object key) {        return mColors.get(Math.abs(key.hashCode()) % mColors.size());    }}

 

package com.loaderman.contactsrecycledemo;import android.graphics.Color;import android.graphics.Paint;/** * Created by MQ on 2017/5/18. */public class ColorUtil {    /**     * 根据数据位置来给Paint循环设置颜色     *     * @param mPaint   Paint     * @param position position     */    public static void setPaintColor(Paint mPaint, int position) {        int pos = position % 6;        switch (pos) {            case 0:                mPaint.setColor(Color.parseColor("#EC5745"));                break;            case 1:                mPaint.setColor(Color.parseColor("#377caf"));                break;            case 2:                mPaint.setColor(Color.parseColor("#4ebcd3"));                break;            case 3:                mPaint.setColor(Color.parseColor("#6fb30d"));                break;            case 4:                mPaint.setColor(Color.parseColor("#FFA500"));                break;            case 5:                mPaint.setColor(Color.parseColor("#bf9e5a"));                break;        }    }}

 

package com.loaderman.contactsrecycledemo;import android.view.View;import com.github.promeg.pinyinhelper.Pinyin;import java.util.Collections;import java.util.Comparator;import java.util.List;/** * Created by MQ on 2017/5/3. */public class CommonUtil {    /**     * 测量View的宽高     *     * @param view View     */    public static void measureWidthAndHeight(View view) {        int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);        int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);        view.measure(w, h);    }    /**     * 对数据进行排序     *     * @param list 要进行排序的数据源     */    public static void sortData(List<ContactBean> list) {        if (list == null || list.size() == 0) return;        for (int i = 0; i < list.size(); i++) {            ContactBean bean = list.get(i);            String tag = Pinyin.toPinyin(bean.getName().substring(0, 1).charAt(0)).substring(0, 1);            if (tag.matches("[A-Z]")) {                bean.setIndexTag(tag);            } else {                bean.setIndexTag("#");            }        }        Collections.sort(list, new Comparator<ContactBean>() {            @Override            public int compare(ContactBean o1, ContactBean o2) {                if ("#".equals(o1.getIndexTag())) {                    return 1;                } else if ("#".equals(o2.getIndexTag())) {                    return -1;                } else {                    return o1.getIndexTag().compareTo(o2.getIndexTag());                }            }        });    }    /**     * @param beans 数据源     * @return tags 返回一个包含所有Tag字母在内的字符串     */    public static String getTags(List<ContactBean> beans) {        StringBuilder builder = new StringBuilder();        for (int i = 0; i < beans.size(); i++) {            if (!builder.toString().contains(beans.get(i).getIndexTag())) {                builder.append(beans.get(i).getIndexTag());            }        }        return builder.toString();    }}

 

package com.loaderman.contactsrecycledemo;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;/** * Created by MQ on 2017/5/8. */public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.MyRecycleHolder> {    private List<ContactBean> contactBeanList;    private Context mContext;    // declare the color generator and drawable builder    private ColorGenerator mColorGenerator = ColorGenerator.MATERIAL;    private TextDrawable.IBuilder mDrawableBuilder = TextDrawable.builder().round();    public ContactAdapter(Context context) {        this.mContext = context;        contactBeanList = new ArrayList<>();    }    public void addAll(List<ContactBean> beans) {        if (contactBeanList.size() > 0) {            contactBeanList.clear();        }        contactBeanList.addAll(beans);        notifyDataSetChanged();    }    public void add(ContactBean bean, int position) {        contactBeanList.add(position, bean);        notifyItemInserted(position);    }    public void add(ContactBean bean) {        contactBeanList.add(bean);        notifyItemChanged(contactBeanList.size() - 1);    }    @Override    public MyRecycleHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycle_item_layout, parent, false);        return new MyRecycleHolder(view);    }    @Override    public void onBindViewHolder(MyRecycleHolder holder, int position) {        if (contactBeanList == null || contactBeanList.size() == 0 || contactBeanList.size() <= position)            return;        ContactBean bean = contactBeanList.get(position);        if (bean != null) {            holder.tv_name.setText(bean.getName());            TextDrawable drawable = mDrawableBuilder.build(String.valueOf(bean.getName().charAt(0)), mColorGenerator.getColor(bean.getName()));            holder.iv_img.setImageDrawable(drawable);        }    }    @Override    public int getItemCount() {        return contactBeanList.size();    }    public static class MyRecycleHolder extends RecyclerView.ViewHolder {        public final TextView tv_name;        public final ImageView iv_img;        public MyRecycleHolder(View itemView) {            super(itemView);            tv_name = (TextView) itemView.findViewById(R.id.tv_name);            iv_img = (ImageView) itemView.findViewById(R.id.iv_img);        }    }}

 

package com.loaderman.contactsrecycledemo;/** * Created by MQ on 2017/5/16. */public class ContactBean {    private String indexTag;    private String name;    public String getIndexTag() {        return indexTag;    }    public void setIndexTag(String indexTag) {        this.indexTag = indexTag;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

 

package com.loaderman.contactsrecycledemo;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.support.v4.view.ViewCompat;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.text.TextUtils;import android.view.View;import java.util.List;/** * Created by MQ on 2017/5/8. */public class CustomItemDecoration extends RecyclerView.ItemDecoration {    private Paint             mPaint;    private List<ContactBean> mBeans;    private static final int dividerHeight = 80;    private Context mContext;    private final Rect mBounds = new Rect();    private String tagsStr;    public void setDatas(List<ContactBean> mBeans, String tagsStr) {        this.mBeans = mBeans;        this.tagsStr = tagsStr;    }    public CustomItemDecoration(Context mContext) {        this.mContext = mContext;        mPaint = new Paint();        mPaint.setAntiAlias(true);        mPaint.setDither(true);        mPaint.setTextAlign(Paint.Align.CENTER);    }    @Override    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {        if (parent.getLayoutManager() == null) {            return;        }        canvas.save();        final int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = parent.getChildAt(i);            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();            int position = params.getViewLayoutPosition();            if (mBeans == null || mBeans.size() == 0 || mBeans.size() <= position || position < 0) {                continue;            }            if (position == 0) {                //第一条数据有bar                drawTitleBar(canvas, parent, child, mBeans.get(position), tagsStr.indexOf(mBeans.get(position).getIndexTag()));            } else if (position > 0) {                if (TextUtils.isEmpty(mBeans.get(position).getIndexTag())) continue;                //与上一条数据中的tag不同时,该显示bar了                if (!mBeans.get(position).getIndexTag().equals(mBeans.get(position - 1).getIndexTag())) {                    drawTitleBar(canvas, parent, child, mBeans.get(position), tagsStr.indexOf(mBeans.get(position).getIndexTag()));                }            }        }        canvas.restore();    }    /**     * 绘制bar     *     * @param canvas Canvas     * @param parent RecyclerView     * @param child  ItemView     */    private void drawTitleBar(Canvas canvas, RecyclerView parent, View child, ContactBean bean, int position) {        final int left = 0;        final int right = parent.getWidth();        //返回一个包含Decoration和Margin在内的Rect        parent.getDecoratedBoundsWithMargins(child, mBounds);        final int top = mBounds.top;        final int bottom = mBounds.top + Math.round(ViewCompat.getTranslationY(child)) + dividerHeight;        mPaint.setColor(Color.WHITE);        canvas.drawRect(left, top, right, bottom, mPaint);        //根据位置不断变换Paint的颜色        ColorUtil.setPaintColor(mPaint, position);        mPaint.setTextSize(40);        canvas.drawCircle(DpUtil.dp2px(mContext, 42.5f), bottom - dividerHeight / 2, 35, mPaint);        mPaint.setColor(Color.WHITE);        canvas.drawText(bean.getIndexTag(), DpUtil.dp2px(mContext, 42.5f), bottom - dividerHeight / 3, mPaint);    }    @Override    public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {        //用来绘制悬浮框        int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();        if (mBeans == null || mBeans.size() == 0 || mBeans.size() <= position || position < 0) {            return;        }        final int bottom = parent.getPaddingTop() + dividerHeight;        mPaint.setColor(Color.WHITE);        canvas.drawRect(parent.getLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + dividerHeight, mPaint);        ColorUtil.setPaintColor(mPaint, tagsStr.indexOf(mBeans.get(position).getIndexTag()));        mPaint.setTextSize(40);        canvas.drawCircle(DpUtil.dp2px(mContext, 42.5f), bottom - dividerHeight / 2, 35, mPaint);        mPaint.setColor(Color.WHITE);        canvas.drawText(mBeans.get(position).getIndexTag(), DpUtil.dp2px(mContext, 42.5f), bottom - dividerHeight / 3, mPaint);    }    @Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        int position = parent.getChildAdapterPosition(view);        if (mBeans == null || mBeans.size() == 0 || mBeans.size() <= position || position < 0) {            super.getItemOffsets(outRect, view, parent, state);            return;        }        if (position == 0) {            //第一条数据有bar            outRect.set(0, dividerHeight, 0, 0);        } else if (position > 0) {            if (TextUtils.isEmpty(mBeans.get(position).getIndexTag())) return;            //与上一条数据中的tag不同时,该显示bar了            if (!mBeans.get(position).getIndexTag().equals(mBeans.get(position - 1).getIndexTag())) {                outRect.set(0, dividerHeight, 0, 0);            }        }    }}

 

package com.loaderman.contactsrecycledemo;import android.app.Activity;import android.content.Context;import android.util.DisplayMetrics;/** * Created by MQ on 2016/12/16. */public class DpUtil {    /**     * dp转换成px     *     * @param context Context     * @param dp      dp     * @return px值     */    public static float dp2px(Context context, float dp) {        final float scale = context.getResources().getDisplayMetrics().density;        return dp * scale + 0.5f;    }    /**     * sp转换成px     *     * @param context Context     * @param sp      sp     * @return px值     */    public static float sp2px(Context context, float sp) {        final float scale = context.getResources().getDisplayMetrics().scaledDensity;        return sp * scale;    }    /**     * 获得屏幕宽度     *     * @param context Context     * @return 屏幕宽度(像素)     */    public static int getScreenSizeWidth(Context context) {        DisplayMetrics metric = new DisplayMetrics();        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metric);        return metric.widthPixels;    }}

 

package com.loaderman.contactsrecycledemo;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;/** * Created by MQ on 2017/5/18. */public class IndexBar extends ViewGroup {    private int mHeight, mWidth;    private Context mContext;    private Paint mPaint;    private float centerY;    private String tag = "";    private boolean isShowTag;    private int position;    private final float circleRadius = 100;    public IndexBar(Context context) {        this(context, null);    }    public IndexBar(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public IndexBar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mContext = context;        init(attrs);    }    private void init(AttributeSet attrs) {        setWillNotDraw(false);        mPaint = new Paint();        mPaint.setColor(Color.RED);        mPaint.setAntiAlias(true);        mPaint.setDither(true);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mHeight = h;        mWidth = w;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        measureChildren(widthMeasureSpec, heightMeasureSpec);        //MeasureSpec封装了父View传给子View的布局要求        int wMode = MeasureSpec.getMode(widthMeasureSpec);        int wSize = MeasureSpec.getSize(widthMeasureSpec);        int hMode = MeasureSpec.getMode(heightMeasureSpec);        int hSize = MeasureSpec.getSize(heightMeasureSpec);        switch (wMode) {            case MeasureSpec.EXACTLY:                mWidth = wSize;                break;            case MeasureSpec.AT_MOST:                mWidth = wSize;                break;            case MeasureSpec.UNSPECIFIED:                break;        }        switch (hMode) {            case MeasureSpec.EXACTLY:                mHeight = hSize;                break;            case MeasureSpec.AT_MOST:                mHeight = hSize;                break;            case MeasureSpec.UNSPECIFIED:                break;        }        setMeasuredDimension(mWidth, mHeight);    }    private int childWidth;    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int childNum = getChildCount();        if (childNum <= 0) return;        //得到SideBar        View childView = getChildAt(0);        childWidth = childView.getMeasuredWidth();        //把SideBar排列到最右侧        childView.layout((mWidth - childWidth), 0, mWidth, mHeight);    }    /**     * @param centerY  要绘制的圆的Y坐标     * @param tag      要绘制的字母Tag     * @param position 字母Tag所在位置     */    public void setDrawData(float centerY, String tag, int position) {        this.position = position;        this.centerY = centerY;        this.tag = tag;        isShowTag = true;        invalidate();    }    /**     * 通过标志位来控制是否来显示圆     *     * @param isShowTag 是否显示圆     */    public void setTagStatus(boolean isShowTag) {        this.isShowTag = isShowTag;        invalidate();    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (isShowTag) {            //根据位置来不断变换Paint的颜色            ColorUtil.setPaintColor(mPaint, position);            //绘制圆和文字            canvas.drawCircle((mWidth - childWidth) / 2, centerY, circleRadius, mPaint);            mPaint.setColor(Color.WHITE);            mPaint.setTextSize(80);            canvas.drawText(tag, (mWidth - childWidth - mPaint.measureText(tag)) / 2, centerY - (mPaint.ascent() + mPaint.descent()) / 2, mPaint);        }    }    @Override    protected LayoutParams generateDefaultLayoutParams() {        return super.generateDefaultLayoutParams();    }    @Override    protected LayoutParams generateLayoutParams(LayoutParams p) {        return super.generateLayoutParams(p);    }}

 

package com.loaderman.contactsrecycledemo;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;/** * Created by MQ on 2017/5/17. */public class SideBar extends View {    private int mHeight;    private int mWidth;    private Paint mPaint;    private int singleHeight;    private Context mContext;    private indexChangeListener listener;    private final int TOTAL_MARGIN = 160;    private final int TOP_MARGIN = 80;    public SideBar(Context context) {        this(context, null);    }    public SideBar(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public SideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        this.mContext = context;        init();    }    private void init() {        mPaint = new Paint();        mPaint.setDither(true);        mPaint.setAntiAlias(true);        mPaint.setColor(Color.GRAY);        mPaint.setTextSize(35);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        //导航栏居中显示,上下各有80dp的边距        mHeight = (int) (h - DpUtil.dp2px(mContext, TOTAL_MARGIN));        mWidth = w;        singleHeight = mHeight / indexStr.length();    }    @Override    protected void onDraw(Canvas canvas) {        for (int i = 0; i < indexStr.length(); i++) {            String textTag = indexStr.substring(i, i + 1);            float xPos = (mWidth - mPaint.measureText(textTag)) / 2;            canvas.drawText(textTag, xPos, singleHeight * (i + 1) + DpUtil.dp2px(mContext, TOP_MARGIN), mPaint);        }    }    private String indexStr = "ABCDEFGHIJKLMNOPQRSTUVWXY#";    public void setIndexStr(String indexStr) {        this.indexStr = indexStr;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                //按下                mPaint.setColor(Color.BLACK);                invalidate();            case MotionEvent.ACTION_MOVE:                //滑动 event.getY()得到在父View中的Y坐标,通过和总高度的比例再乘以字符个数总长度得到按下的位置                int position = (int) ((event.getY() - getTop() - DpUtil.dp2px(mContext, 80)) / mHeight * indexStr.toCharArray().length);                if (position >= 0 && position < indexStr.length()) {                    ((IndexBar) getParent()).setDrawData(event.getY(), String.valueOf(indexStr.toCharArray()[position]), position);                    if (listener != null) {                        listener.indexChanged(indexStr.substring(position, position + 1));                    }                }                break;            case MotionEvent.ACTION_UP:                //抬起                ((IndexBar) getParent()).setTagStatus(false);                mPaint.setColor(Color.GRAY);                invalidate();                break;        }        return true;    }    public interface indexChangeListener {        void indexChanged(String tag);    }    public void setIndexChangeListener(indexChangeListener listener) {        this.listener = listener;    }}

 

/* * ****************************************************************************** *   Copyright (c) 2014-2015 Gabriele Mariotti. * *   Licensed under the Apache License, Version 2.0 (the "License"); *   you may not use this file except in compliance with the License. *   You may obtain a copy of the License at * *   http://www.apache.org/licenses/LICENSE-2.0 * *   Unless required by applicable law or agreed to in writing, software *   distributed under the License is distributed on an "AS IS" BASIS, *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *   See the License for the specific language governing permissions and *   limitations under the License. *  ***************************************************************************** */package com.loaderman.contactsrecycledemo;import android.support.v4.view.ViewCompat;import android.support.v4.view.ViewPropertyAnimatorCompat;import android.support.v7.widget.RecyclerView;import android.view.View;/** * @see android.support.v7.widget.RecyclerView#setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator) */public class SlideInOutLeftItemAnimator extends BaseItemAnimator {    public SlideInOutLeftItemAnimator(RecyclerView recyclerView) {        super(recyclerView);    }    protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) {        final View view = holder.itemView;        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mRemoveAnimations.add(holder);        animation                .setDuration(getRemoveDuration())                .alpha(0)                .translationX(-mRecyclerView.getLayoutManager().getWidth())                .setListener(new VpaListenerAdapter() {                    @Override                    public void onAnimationStart(View view) {                        dispatchRemoveStarting(holder);                    }                    @Override                    public void onAnimationEnd(View view) {                        animation.setListener(null);                        ViewCompat.setAlpha(view, 1);                        ViewCompat.setTranslationX(view, -mRecyclerView.getLayoutManager().getWidth());                        dispatchRemoveFinished(holder);                        mRemoveAnimations.remove(holder);                        dispatchFinishedWhenDone();                    }                }).start();    }    @Override    protected void prepareAnimateAdd(RecyclerView.ViewHolder holder) {        ViewCompat.setTranslationX(holder.itemView, -mRecyclerView.getLayoutManager().getWidth());    }    protected void animateAddImpl(final RecyclerView.ViewHolder holder) {        final View view = holder.itemView;        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);        mAddAnimations.add(holder);        animation.translationX(0)                .alpha(1)                .setDuration(getAddDuration())                .setListener(new VpaListenerAdapter() {                    @Override                    public void onAnimationStart(View view) {                        dispatchAddStarting(holder);                    }                    @Override                    public void onAnimationCancel(View view) {                        ViewCompat.setTranslationX(view, 0);                        ViewCompat.setAlpha(view, 1);                    }                    @Override                    public void onAnimationEnd(View view) {                        animation.setListener(null);                        ViewCompat.setTranslationX(view, 0);                        ViewCompat.setAlpha(view, 1);                        dispatchAddFinished(holder);                        mAddAnimations.remove(holder);                        dispatchFinishedWhenDone();                    }                }).start();    }}

 

package com.loaderman.contactsrecycledemo;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.ColorFilter;import android.graphics.Paint;import android.graphics.PixelFormat;import android.graphics.Rect;import android.graphics.RectF;import android.graphics.Typeface;import android.graphics.drawable.ShapeDrawable;import android.graphics.drawable.shapes.OvalShape;import android.graphics.drawable.shapes.RectShape;import android.graphics.drawable.shapes.RoundRectShape;/** * @author amulya * @datetime 14 Oct 2014, 3:53 PM */public class TextDrawable extends ShapeDrawable {    private final Paint textPaint;    private final Paint borderPaint;    private static final float SHADE_FACTOR = 0.9f;    private final String text;    private final int color;    private final RectShape shape;    private final int height;    private final int width;    private final int fontSize;    private final float radius;    private final int borderThickness;    private TextDrawable(Builder builder) {        super(builder.shape);        // shape properties        shape = builder.shape;        height = builder.height;        width = builder.width;        radius = builder.radius;        // text and color        text = builder.toUpperCase ? builder.text.toUpperCase() : builder.text;        color = builder.color;        // text paint settings        fontSize = builder.fontSize;        textPaint = new Paint();        textPaint.setColor(builder.textColor);        textPaint.setAntiAlias(true);        textPaint.setFakeBoldText(builder.isBold);        textPaint.setStyle(Paint.Style.FILL);        textPaint.setTypeface(builder.font);        textPaint.setTextAlign(Paint.Align.CENTER);        textPaint.setStrokeWidth(builder.borderThickness);        // border paint settings        borderThickness = builder.borderThickness;        borderPaint = new Paint();        borderPaint.setColor(getDarkerShade(color));        borderPaint.setStyle(Paint.Style.STROKE);        borderPaint.setStrokeWidth(borderThickness);        // drawable paint color        Paint paint = getPaint();        paint.setColor(color);    }    private int getDarkerShade(int color) {        return Color.rgb((int)(SHADE_FACTOR * Color.red(color)),                (int)(SHADE_FACTOR * Color.green(color)),                (int)(SHADE_FACTOR * Color.blue(color)));    }    @Override    public void draw(Canvas canvas) {        super.draw(canvas);        Rect r = getBounds();        // draw border        if (borderThickness > 0) {            drawBorder(canvas);        }        int count = canvas.save();        canvas.translate(r.left, r.top);        // draw text        int width = this.width < 0 ? r.width() : this.width;        int height = this.height < 0 ? r.height() : this.height;        int fontSize = this.fontSize < 0 ? (Math.min(width, height) / 2) : this.fontSize;        textPaint.setTextSize(fontSize);        canvas.drawText(text, width / 2, height / 2 - ((textPaint.descent() + textPaint.ascent()) / 2), textPaint);        canvas.restoreToCount(count);    }    private void drawBorder(Canvas canvas) {        RectF rect = new RectF(getBounds());        rect.inset(borderThickness/2, borderThickness/2);        if (shape instanceof OvalShape) {            canvas.drawOval(rect, borderPaint);        }        else if (shape instanceof RoundRectShape) {            canvas.drawRoundRect(rect, radius, radius, borderPaint);        }        else {            canvas.drawRect(rect, borderPaint);        }    }    @Override    public void setAlpha(int alpha) {        textPaint.setAlpha(alpha);    }    @Override    public void setColorFilter(ColorFilter cf) {        textPaint.setColorFilter(cf);    }    @Override    public int getOpacity() {        return PixelFormat.TRANSLUCENT;    }    @Override    public int getIntrinsicWidth() {        return width;    }    @Override    public int getIntrinsicHeight() {        return height;    }    public static IShapeBuilder builder() {        return new Builder();    }    public static class Builder implements IConfigBuilder, IShapeBuilder, IBuilder {        private String text;        private int color;        private int borderThickness;        private int width;        private int height;        private Typeface font;        private RectShape shape;        public int textColor;        private int fontSize;        private boolean isBold;        private boolean toUpperCase;        public float radius;        private Builder() {            text = "";            color = Color.GRAY;            textColor = Color.WHITE;            borderThickness = 0;            width = -1;            height = -1;            shape = new RectShape();            font = Typeface.create("sans-serif-light", Typeface.NORMAL);            fontSize = -1;            isBold = false;            toUpperCase = false;        }        public IConfigBuilder width(int width) {            this.width = width;            return this;        }        public IConfigBuilder height(int height) {            this.height = height;            return this;        }        public IConfigBuilder textColor(int color) {            this.textColor = color;            return this;        }        public IConfigBuilder withBorder(int thickness) {            this.borderThickness = thickness;            return this;        }        public IConfigBuilder useFont(Typeface font) {            this.font = font;            return this;        }        public IConfigBuilder fontSize(int size) {            this.fontSize = size;            return this;        }        public IConfigBuilder bold() {            this.isBold = true;            return this;        }        public IConfigBuilder toUpperCase() {            this.toUpperCase = true;            return this;        }        @Override        public IConfigBuilder beginConfig() {            return this;        }        @Override        public IShapeBuilder endConfig() {            return this;        }        @Override        public IBuilder rect() {            this.shape = new RectShape();            return this;        }        @Override        public IBuilder round() {            this.shape = new OvalShape();            return this;        }        @Override        public IBuilder roundRect(int radius) {            this.radius = radius;            float[] radii = {radius, radius, radius, radius, radius, radius, radius, radius};            this.shape = new RoundRectShape(radii, null, null);            return this;        }        @Override        public TextDrawable buildRect(String text, int color) {            rect();            return build(text, color);        }        @Override        public TextDrawable buildRoundRect(String text, int color, int radius) {            roundRect(radius);            return build(text, color);        }        @Override        public TextDrawable buildRound(String text, int color) {            round();            return build(text, color);        }        @Override        public TextDrawable build(String text, int color) {            this.color = color;            this.text = text;            return new TextDrawable(this);        }    }    public interface IConfigBuilder {        public IConfigBuilder width(int width);        public IConfigBuilder height(int height);        public IConfigBuilder textColor(int color);        public IConfigBuilder withBorder(int thickness);        public IConfigBuilder useFont(Typeface font);        public IConfigBuilder fontSize(int size);        public IConfigBuilder bold();        public IConfigBuilder toUpperCase();        public IShapeBuilder endConfig();    }    public static interface IBuilder {        public TextDrawable build(String text, int color);    }    public static interface IShapeBuilder {        public IConfigBuilder beginConfig();        public IBuilder rect();        public IBuilder round();        public IBuilder roundRect(int radius);        public TextDrawable buildRect(String text, int color);        public TextDrawable buildRoundRect(String text, int color, int radius);        public TextDrawable buildRound(String text, int color);    }}

 

package com.loaderman.contactsrecycledemo;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.text.TextUtils;import android.view.View;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private RecyclerView         rl_recycle_view;    private   ContactAdapter       mAdapter;    private   CustomItemDecoration decoration;    private   SideBar              side_bar;    List<ContactBean> nameList = new ArrayList<>();    private LinearLayoutManager layoutManager;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initViews();        side_bar.setIndexChangeListener(new SideBar.indexChangeListener() {            @Override            public void indexChanged(String tag) {                if (TextUtils.isEmpty(tag) || nameList.size() <= 0) return;                for (int i = 0; i < nameList.size(); i++) {                    if (tag.equals(nameList.get(i).getIndexTag())) {                        layoutManager.scrollToPositionWithOffset(i, 0);                        //                        layoutManager.scrollToPosition(i);                        return;                    }                }            }        });    }    private void initViews() {        mAdapter = new ContactAdapter(this);        rl_recycle_view = (RecyclerView) findViewById(R.id.rl_recycle_view);        //侧边导航栏        side_bar = (SideBar) findViewById(R.id.side_bar);        layoutManager = new LinearLayoutManager(this);        rl_recycle_view.setLayoutManager(layoutManager);        rl_recycle_view.addItemDecoration(decoration = new CustomItemDecoration(this));        rl_recycle_view.setItemAnimator(new SlideInOutLeftItemAnimator(rl_recycle_view));        initDatas();        rl_recycle_view.setAdapter(mAdapter);    }    private void initDatas() {        String[] names = {"孙尚香", "安其拉", "白起", "不知火舞", "@小马快跑", "_德玛西亚之力_", "妲己", "狄仁杰", "典韦", "韩信",                "老夫子", "刘邦", "刘禅", "鲁班七号", "墨子", "孙膑", "孙尚香", "孙悟空", "项羽", "亚瑟",                "周瑜", "庄周", "蔡文姬", "甄姬", "廉颇", "程咬金", "后羿", "扁鹊", "钟无艳", "小乔", "王昭君", "虞姬",                "李元芳", "张飞", "刘备", "牛魔", "张良", "兰陵王", "露娜", "貂蝉", "达摩", "曹操", "芈月", "荆轲", "高渐离",                "钟馗", "花木兰", "关羽", "李白", "宫本武藏", "吕布", "嬴政", "娜可露露", "武则天", "赵云", "姜子牙",};        for (String name : names) {            ContactBean bean = new ContactBean();            bean.setName(name);            nameList.add(bean);        }        //对数据源进行排序        CommonUtil.sortData(nameList);        //返回一个包含所有Tag字母在内的字符串并赋值给tagsStr        String tagsStr = CommonUtil.getTags(nameList);        side_bar.setIndexStr(tagsStr);        decoration.setDatas(nameList, tagsStr);        mAdapter.addAll(nameList);    }    public void add(View view) {        ContactBean bean = new ContactBean();        bean.setName("安其拉666");        nameList.add(bean);        //对数据源进行排序        CommonUtil.sortData(nameList);        //返回一个包含所有Tag字母在内的字符串并赋值给tagsStr        String tagsStr = CommonUtil.getTags(nameList);        side_bar.setIndexStr(tagsStr);        decoration.setDatas(nameList, tagsStr);        //这里写死位置1 只是为了看动画效果        mAdapter.add(bean, 1);    }}

 activity_main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.loaderman.contactsrecycledemo.MainActivity">    <android.support.v7.widget.RecyclerView        android:id="@+id/rl_recycle_view"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:background="@color/white"/>    、    <com.loaderman.contactsrecycledemo.IndexBar        android:id="@+id/index_bar"        android:layout_width="200dp"        android:layout_height="match_parent"        android:layout_alignParentEnd="true"        android:layout_alignParentRight="true"        android:background="@color/transparent">        <com.loaderman.contactsrecycledemo.SideBar            android:id="@+id/side_bar"            android:layout_width="30dp"            android:layout_height="match_parent"/>    </com.loaderman.contactsrecycledemo.IndexBar>    <Button        android:id="@+id/btn"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:onClick="add"        android:text="ADD"/></RelativeLayout>

 recycle_item_layout.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:background="@color/white"    android:orientation="horizontal">    <ImageView        android:id="@+id/iv_img"        android:layout_width="45dp"        android:layout_height="45dp"        android:layout_gravity="center_vertical"        android:layout_marginLeft="20dp"        android:contentDescription="@string/app_name"        android:src="http://www.mamicode.com/@mipmap/ic_launcher" />    <TextView        android:id="@+id/tv_name"        android:layout_width="match_parent"        android:layout_height="60dp"        android:layout_marginLeft="20dp"        android:gravity="center_vertical"        android:text="电话号码"        android:textColor="@color/black_deep"        android:textSize="16sp" /></LinearLayout>

 color.xml

    <color name="white">#fff</color>    <color name="transparent">#00000000</color>    <color name="black_deep">#FF000000</color>

 效果图:

技术分享

防手机联系人功能的实现