Android自定义View(二):实例篇——字符索引栏

前言

现在在大多数具有联系人功能APP上边,很多都具有字符索引栏的功能,以方便用户更快的定位到要找的联系人。尤其是在联系人数量比较多的时候,这个功能就显得尤为快速方便了。此篇博客,将教大家来实现这么一个字符索引栏。话不多说,先看下效果图。

image

其中,最右侧的滑动栏我们称为“字符索引栏”,命名为CharSlideBar; 中间显示字符的视图我们称为“字符指示视图”,命名为CharIndicateView

步骤

1、绘制出字符索引栏CharSlideBar
2、添加字符索引栏CharSlideBar的点击滑动监听事件
3、绘制出字符指示视图CharIndicateView
4、字符索引栏CharSlideBar与字符指示视图CharIndicateView建立关联

具体实现

1、绘制出字符索引栏CharSlideBar

①、自定义View属性


    
    
    
    
    
    
    
    

②、实现CharSlideBar类,继承自View

同时实现其构造方法,并初始化相关自定义属性。

public class CharSlideBar extends View {

    /*可自定义相关属性*/
    private String mChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ#";  //要显示的所有字符(此处为默认值)
    private int mBackgroundColor = Color.GRAY;  //背景色(此处为默认值)
    private int mCharTextSize = 30;  //字体大小(此处为默认值)
    private int mCharTextColor = Color.BLACK;  //字体颜色(此处为默认值)

    private int mCanvasColor = Color.TRANSPARENT;  //画布颜色
    private int mLastSelectedPosition = -1;  //记录上次选中的位置

    private Paint mPaint;  //画笔
    private Paint.FontMetricsInt mFontMetricsInt;  //字体度量值
    private CharIndicateView mCharIndicateView;  //字符指示视图
    private OnSelectedListener mOnSelectedListener;  //字符选中的监听

    public CharSlideBar(Context context) {
        this(context, null);
    }

    public CharSlideBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        //初始化View相关自定义属性
        initFromAttributes(context, attrs);
        //初始化相关操作
        init();
    }

    /**
     * 初始化View相关自定义属性
     *
     * @param context
     * @param attrs
     */
    private void initFromAttributes(Context context, AttributeSet attrs) {
        //获取相关View属性
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CharSlideBar, 0, 0);
        try {
            String chars = a.getString(R.styleable.CharSlideBar_barChars);
            mChars = chars == null ? mChars : chars;
            mCharTextSize = a.getDimensionPixelSize(R.styleable.CharSlideBar_barTextSize, mCharTextSize);
            mCharTextColor = a.getColor(R.styleable.CharSlideBar_barTextColor, mCharTextColor);
            mBackgroundColor = a.getColor(R.styleable.CharSlideBar_barBackground, mBackgroundColor);
        } finally {
            //回收TypedArray
            a.recycle();
        }
    }

    /**
     * 初始化操作
     */
    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //文字水平居中显示
        mPaint.setTextAlign(Paint.Align.CENTER);
        //设置字体大小
        mPaint.setTextSize(mCharTextSize);
        //设置字体颜色
        mPaint.setColor(mCharTextColor);
        //获取FontMetricsInt
        mFontMetricsInt = mPaint.getFontMetricsInt();
    }

    ...//省略
}

此处不再详细介绍,具体可参见上篇博客:Android自定义View(一):基础篇

③、重写onDraw(Canvas canvas)方法
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //设置索引栏背景色
    canvas.drawColor(mCanvasColor);
    //单个字符所占的高度
    float singleCharHeight = ((float) getHeight()) / mChars.length();
    //字符要显示的x值
    float charX = ((float) getWidth()) / 2;
    //计算出字体高度
    int fontHeight = mFontMetricsInt.descent - mFontMetricsInt.ascent;
    //计算出竖直方向居中时的偏移量
    float centerYOffset = singleCharHeight / 2 - (-mFontMetricsInt.ascent - fontHeight / 2);

    //根据x、y值画出所有字符
    for (int i = 0; i < mChars.length(); i++) {
        canvas.drawText(mChars.substring(i, i + 1), charX, singleCharHeight * (i + 1) - centerYOffset, mPaint);
    }
}

在该方法内:

  • 首先得到索引栏的高度,除以所有字符的个数,即得到单个字符的高度,便实现了将所有字符平分高度的目的;
  • 得到宽度的中间X值,目的是为了字符水平居中;
  • 由于canvasdrawText()方法中的y值为Text的baseline,所以要想实现文本的垂直方向居中,就必须计算出文本的垂直方向的中间线与所占布局中间线的偏移量;
  • 根据x、y值,以及竖直方向居中时的偏移量,我们将所有字符平均绘制在垂直方向上。

注:计算文本的垂直方向的中间线与所占布局中间线的偏移量的方法为:

首先看一下Android中的字体度量:

Android自定义View(二):实例篇——字符索引栏_第1张图片

其中baseline为基线,基线以上为负值,以下为正值。即top, ascent为负值,descent, bottom为正值。

Android自定义View(二):实例篇——字符索引栏_第2张图片

如图,若想要文本垂直居中,则需要让文本中间线与布局中间线重合即可。所以对照此图就不难计算出两个中间线的偏移量了。

2、添加字符索引栏CharSlideBar的点击滑动监听事件

重写onTouchEvent(MotionEvent event)方法:

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:  //手指按下
            //设置画布颜色
            mCanvasColor = mBackgroundColor;
            //重新绘制
            invalidate();
            //显示字符指示View
            if (mCharIndicateView != null) {
                mCharIndicateView.setVisibility(View.VISIBLE);
            }
            //根据Y值得到选中的位置
            selectedPositionByY(event.getY());

            return true;
        case MotionEvent.ACTION_MOVE:  //手指滑动
            //根据Y值得到选中的位置
            selectedPositionByY(event.getY());

            return true;
        case MotionEvent.ACTION_UP:  //手指抬起
            //画布颜色设为透明
            mCanvasColor = Color.TRANSPARENT;
            //重新绘制
            invalidate();
            //隐藏字符指示View
            if (mCharIndicateView != null) {
                mCharIndicateView.setVisibility(View.GONE);
            }
            //复位记录上次选中位置的值
            mLastSelectedPosition = -1;

            return true;
    }
    return super.onTouchEvent(event);
}

首先根据按下与抬起的动作来设置索引栏的背景色。同时根据获取的View的Y值来计算出按下的相应字符。根据Y值得到选中的位置即相应字符的方法为:

/**
 * 根据View的Y值得到选中的字符位置
 *
 * @param y
 */
private void selectedPositionByY(float y) {
    //若滑动范围超出索引栏的高度范围,不再计算位置
    if (y < 0 || y > getHeight()) {
        return;
    }
    //单个字符所占的高度
    float singleCharHeight = ((float) getHeight()) / mChars.length();
    //计算出当前选中的位置
    int position = (int) (y / singleCharHeight);
    //防止重复显示
    if (position != mLastSelectedPosition) {
        //根据选中位置,获取相应位置的字符
        String selectedChar = mChars.substring(position, position + 1);
        //展示选中的字符
        if (mCharIndicateView != null) {
            mCharIndicateView.showSelectedChar(selectedChar);
        }
        //设置监听的回调方法
        if (mOnSelectedListener != null) {
            mOnSelectedListener.onSelected(position, selectedChar);
        }
        //记录下当前位置
        mLastSelectedPosition = position;
    }

}

随后,我们为字符索引栏设置一个监听选中事件的接口,并添加设置方法,并在上述的selectedPositionByY(float y)中实现了接口方法的回调。

/**
 * 字符选中的监听事件
 */
public interface OnSelectedListener {
    /**
     * 选中的回调方法
     *
     * @param position     选中的位置
     * @param selectedChar 选中的字符
     */
    public void onSelected(int position, String selectedChar);
}

/**
 * 设置监听事件
 *
 * @param onSelectedListener
 */
public void setOnSelectedListener(OnSelectedListener onSelectedListener) {
    mOnSelectedListener = onSelectedListener;
}

这样我们就可以在相应的Activity中设置选中监听事件了。

3、绘制出字符指示视图CharIndicateView

①、自定义View属性


    
    
    
    
    
    
    
    

②、实现CharIndicateView类,继承自TextView
public class CharIndicateView extends TextView {

    /*可自定义相关属性*/
    private int mIndicateTextSize = 50;  //字体大小(此处为默认值)
    private int mIndicateTextColor = Color.BLACK;  //字体颜色(此处为默认值)
    private int mBackgroundColor = Color.GRAY;  //背景色(此处为默认值)
    private int mBackgroundRadius = 10;  //矩形背景圆角半径(此处为默认值)

    public CharIndicateView(Context context) {
        this(context, null);
    }

    public CharIndicateView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //初始化View相关自定义属性
        initFromAttributes(context, attrs);
        //初始化
        init();
    }

    /**
     * 初始化View相关自定义属性
     *
     * @param context
     * @param attrs
     */
    private void initFromAttributes(Context context, AttributeSet attrs) {
        //获取相关View属性
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CharIndicateView, 0, 0);
        try {
            mIndicateTextSize = a.getDimensionPixelSize(R.styleable.CharIndicateView_indicateTextSize, mIndicateTextSize);
            mIndicateTextColor = a.getColor(R.styleable.CharIndicateView_indicateTextColor, mIndicateTextColor);
            mBackgroundColor = a.getColor(R.styleable.CharIndicateView_indicateBackground, mBackgroundColor);
            mBackgroundRadius = a.getDimensionPixelSize(R.styleable.CharIndicateView_indicateBackgroundRadius, mBackgroundRadius);
        } finally {
            //回收TypedArray
            a.recycle();
        }
    }

    /**
     * 初始化操作
     */
    private void init() {
        //设置圆角矩形背景
        //  float[] outerRadii = {10, 10, 10, 10, 10, 10, 10, 10};
        float[] outerRadii = new float[8];
        for (int i = 0; i < outerRadii.length; i++) {
            outerRadii[i] = mBackgroundRadius;
        }
        RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
        ShapeDrawable shapeDrawable = new ShapeDrawable(shape);
        shapeDrawable.getPaint().setColor(mBackgroundColor);
        //将圆角矩形背景设置到当前View
        this.setBackgroundDrawable(shapeDrawable);
        //文本居中显示
        this.setGravity(Gravity.CENTER);
        //设置字体大小
        this.setTextSize(mIndicateTextSize);
        //设置字体颜色
        this.setTextColor(mIndicateTextColor);
        //默认不显示该布局
        this.setVisibility(View.GONE);
    }

    /**
     * 展示选中字符
     *
     * @param selectedChar 要显示的字符
     */
    public void showSelectedChar(String selectedChar) {
        this.setText(selectedChar);
    }

}

字符指示布局的实现比较简单,直接继承自TextView,在初始化中设置了文本的颜色、大小、圆角矩形背景,并默认隐藏了该布局。此外,还添加了showSelectedChar(String selectedChar)方法,来显示相应的字符。

4、字符索引栏CharSlideBar与字符指示视图CharIndicateView建立关联

CharSlideBar我们添加如下关联方法:

/**
 * 和字符指示View建立联系
 *
 * @param charIndicateView
 */
public void setupWithIndicateView(CharIndicateView charIndicateView) {
    mCharIndicateView = charIndicateView;
}

然后在上述的selectedPositionByY(float y)中调用了CharIndicateViewshowSelectedChar(String selectedChar)方法。

到此,我们就可以实现本文开始时的效果了。

在应用中的使用方法

布局文件activity_main.xml:




    
    

    
    

    
    



Activity:MainActivity:

public class MainActivity extends AppCompatActivity {

    private final String TAG = this.getClass().getSimpleName();

    private List mContactList;  //联系人集合

    private ListView mContactListView;  //联系人列表
    private CharSlideBar mCharSlideBar;  //字符索引栏
    private CharIndicateView mCharIndicateView;  //字符指示视图
    private ContactlistAdapter mContactListAdapter;  //联系人列表适配器

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

        //初始化变量
        initVariables();
        //初始化View
        initView();
    }

    /**
     * 初始化变量
     */
    private void initVariables() {
        ContactModel model = new ContactModel();
        mContactList = model.getContactList();
        mContactListAdapter = new ContactlistAdapter(this, mContactList);
    }

    /**
     * 初始化View
     */
    private void initView() {
        mContactListView = (ListView) findViewById(R.id.contact_listview);
        mCharSlideBar = (CharSlideBar) findViewById(R.id.char_slider_bar);
        mCharIndicateView = (CharIndicateView) findViewById(R.id.char_indicate_view);

        //联系人设置适配器
        mContactListView.setAdapter(mContactListAdapter);
        //索引栏和指示视图建立联系
        mCharSlideBar.setupWithIndicateView(mCharIndicateView);
        //设置选中监听事件
        mCharSlideBar.setOnSelectedListener(new CharSlideBar.OnSelectedListener() {
            @Override
            public void onSelected(int position, String selectedChar) {
                Log.e(TAG, "选中--" + selectedChar);
                //根据选中的字符来定位ListView的位置
                locateListViewPositionByChar(selectedChar);
            }
        });
    }

    /**
     * 根据选中的字符来定位ListView的位置
     *
     * @param selectedChar
     */
    private void locateListViewPositionByChar(String selectedChar) {
        //遍历联系人列表找到对应字符的位置
        for (int i = 0; i < mContactList.size(); i++) {
            String nameInitial = mContactList.get(i).getNameInitial();
            if (nameInitial.equals(selectedChar)) {
                //定位ListView的位置
                mContactListView.setSelection(i);
                break;
            }
        }
    }
}

源码及Demo

Demo源码 请点击此处下载(https://github.com/shorr/notes_demo/tree/master/CharSlideBarDemo)

前言

现在在大多数具有联系人功能APP上边,很多都具有字符索引栏的功能,以方便用户更快的定位到要找的联系人。尤其是在联系人数量比较多的时候,这个功能就显得尤为快速方便了。此篇博客,将教大家来实现这么一个字符索引栏。话不多说,先看下效果图。

[图片上传失败...(image-14483a-1511186037179)]

其中,最右侧的滑动栏我们称为“字符索引栏”,命名为CharSlideBar; 中间显示字符的视图我们称为“字符指示视图”,命名为CharIndicateView

步骤

1、绘制出字符索引栏CharSlideBar
2、添加字符索引栏CharSlideBar的点击滑动监听事件
3、绘制出字符指示视图CharIndicateView
4、字符索引栏CharSlideBar与字符指示视图CharIndicateView建立关联

具体实现

1、绘制出字符索引栏CharSlideBar

①、自定义View属性


    
    
    
    
    
    
    
    

②、实现CharSlideBar类,继承自View

同时实现其构造方法,并初始化相关自定义属性。

public class CharSlideBar extends View {

    /*可自定义相关属性*/
    private String mChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ#";  //要显示的所有字符(此处为默认值)
    private int mBackgroundColor = Color.GRAY;  //背景色(此处为默认值)
    private int mCharTextSize = 30;  //字体大小(此处为默认值)
    private int mCharTextColor = Color.BLACK;  //字体颜色(此处为默认值)

    private int mCanvasColor = Color.TRANSPARENT;  //画布颜色
    private int mLastSelectedPosition = -1;  //记录上次选中的位置

    private Paint mPaint;  //画笔
    private Paint.FontMetricsInt mFontMetricsInt;  //字体度量值
    private CharIndicateView mCharIndicateView;  //字符指示视图
    private OnSelectedListener mOnSelectedListener;  //字符选中的监听

    public CharSlideBar(Context context) {
        this(context, null);
    }

    public CharSlideBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        //初始化View相关自定义属性
        initFromAttributes(context, attrs);
        //初始化相关操作
        init();
    }

    /**
     * 初始化View相关自定义属性
     *
     * @param context
     * @param attrs
     */
    private void initFromAttributes(Context context, AttributeSet attrs) {
        //获取相关View属性
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CharSlideBar, 0, 0);
        try {
            String chars = a.getString(R.styleable.CharSlideBar_barChars);
            mChars = chars == null ? mChars : chars;
            mCharTextSize = a.getDimensionPixelSize(R.styleable.CharSlideBar_barTextSize, mCharTextSize);
            mCharTextColor = a.getColor(R.styleable.CharSlideBar_barTextColor, mCharTextColor);
            mBackgroundColor = a.getColor(R.styleable.CharSlideBar_barBackground, mBackgroundColor);
        } finally {
            //回收TypedArray
            a.recycle();
        }
    }

    /**
     * 初始化操作
     */
    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //文字水平居中显示
        mPaint.setTextAlign(Paint.Align.CENTER);
        //设置字体大小
        mPaint.setTextSize(mCharTextSize);
        //设置字体颜色
        mPaint.setColor(mCharTextColor);
        //获取FontMetricsInt
        mFontMetricsInt = mPaint.getFontMetricsInt();
    }

    ...//省略
}

此处不再详细介绍,具体可参见上篇博客:Android自定义View(一):基础篇

③、重写onDraw(Canvas canvas)方法
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //设置索引栏背景色
    canvas.drawColor(mCanvasColor);
    //单个字符所占的高度
    float singleCharHeight = ((float) getHeight()) / mChars.length();
    //字符要显示的x值
    float charX = ((float) getWidth()) / 2;
    //计算出字体高度
    int fontHeight = mFontMetricsInt.descent - mFontMetricsInt.ascent;
    //计算出竖直方向居中时的偏移量
    float centerYOffset = singleCharHeight / 2 - (-mFontMetricsInt.ascent - fontHeight / 2);

    //根据x、y值画出所有字符
    for (int i = 0; i < mChars.length(); i++) {
        canvas.drawText(mChars.substring(i, i + 1), charX, singleCharHeight * (i + 1) - centerYOffset, mPaint);
    }
}

在该方法内:

  • 首先得到索引栏的高度,除以所有字符的个数,即得到单个字符的高度,便实现了将所有字符平分高度的目的;
  • 得到宽度的中间X值,目的是为了字符水平居中;
  • 由于canvasdrawText()方法中的y值为Text的baseline,所以要想实现文本的垂直方向居中,就必须计算出文本的垂直方向的中间线与所占布局中间线的偏移量;
  • 根据x、y值,以及竖直方向居中时的偏移量,我们将所有字符平均绘制在垂直方向上。

注:计算文本的垂直方向的中间线与所占布局中间线的偏移量的方法为:

首先看一下Android中的字体度量:

[站外图片上传中...(image-e8f71f-1511186037179)]

其中baseline为基线,基线以上为负值,以下为正值。即top, ascent为负值,descent, bottom为正值。

[图片上传失败...(image-8e011b-1511186037179)]

如图,若想要文本垂直居中,则需要让文本中间线与布局中间线重合即可。所以对照此图就不难计算出两个中间线的偏移量了。

2、添加字符索引栏CharSlideBar的点击滑动监听事件

重写onTouchEvent(MotionEvent event)方法:

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:  //手指按下
            //设置画布颜色
            mCanvasColor = mBackgroundColor;
            //重新绘制
            invalidate();
            //显示字符指示View
            if (mCharIndicateView != null) {
                mCharIndicateView.setVisibility(View.VISIBLE);
            }
            //根据Y值得到选中的位置
            selectedPositionByY(event.getY());

            return true;
        case MotionEvent.ACTION_MOVE:  //手指滑动
            //根据Y值得到选中的位置
            selectedPositionByY(event.getY());

            return true;
        case MotionEvent.ACTION_UP:  //手指抬起
            //画布颜色设为透明
            mCanvasColor = Color.TRANSPARENT;
            //重新绘制
            invalidate();
            //隐藏字符指示View
            if (mCharIndicateView != null) {
                mCharIndicateView.setVisibility(View.GONE);
            }
            //复位记录上次选中位置的值
            mLastSelectedPosition = -1;

            return true;
    }
    return super.onTouchEvent(event);
}

首先根据按下与抬起的动作来设置索引栏的背景色。同时根据获取的View的Y值来计算出按下的相应字符。根据Y值得到选中的位置即相应字符的方法为:

/**
 * 根据View的Y值得到选中的字符位置
 *
 * @param y
 */
private void selectedPositionByY(float y) {
    //若滑动范围超出索引栏的高度范围,不再计算位置
    if (y < 0 || y > getHeight()) {
        return;
    }
    //单个字符所占的高度
    float singleCharHeight = ((float) getHeight()) / mChars.length();
    //计算出当前选中的位置
    int position = (int) (y / singleCharHeight);
    //防止重复显示
    if (position != mLastSelectedPosition) {
        //根据选中位置,获取相应位置的字符
        String selectedChar = mChars.substring(position, position + 1);
        //展示选中的字符
        if (mCharIndicateView != null) {
            mCharIndicateView.showSelectedChar(selectedChar);
        }
        //设置监听的回调方法
        if (mOnSelectedListener != null) {
            mOnSelectedListener.onSelected(position, selectedChar);
        }
        //记录下当前位置
        mLastSelectedPosition = position;
    }

}

随后,我们为字符索引栏设置一个监听选中事件的接口,并添加设置方法,并在上述的selectedPositionByY(float y)中实现了接口方法的回调。

/**
 * 字符选中的监听事件
 */
public interface OnSelectedListener {
    /**
     * 选中的回调方法
     *
     * @param position     选中的位置
     * @param selectedChar 选中的字符
     */
    public void onSelected(int position, String selectedChar);
}

/**
 * 设置监听事件
 *
 * @param onSelectedListener
 */
public void setOnSelectedListener(OnSelectedListener onSelectedListener) {
    mOnSelectedListener = onSelectedListener;
}

这样我们就可以在相应的Activity中设置选中监听事件了。

3、绘制出字符指示视图CharIndicateView

①、自定义View属性


    
    
    
    
    
    
    
    

②、实现CharIndicateView类,继承自TextView
public class CharIndicateView extends TextView {

    /*可自定义相关属性*/
    private int mIndicateTextSize = 50;  //字体大小(此处为默认值)
    private int mIndicateTextColor = Color.BLACK;  //字体颜色(此处为默认值)
    private int mBackgroundColor = Color.GRAY;  //背景色(此处为默认值)
    private int mBackgroundRadius = 10;  //矩形背景圆角半径(此处为默认值)

    public CharIndicateView(Context context) {
        this(context, null);
    }

    public CharIndicateView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //初始化View相关自定义属性
        initFromAttributes(context, attrs);
        //初始化
        init();
    }

    /**
     * 初始化View相关自定义属性
     *
     * @param context
     * @param attrs
     */
    private void initFromAttributes(Context context, AttributeSet attrs) {
        //获取相关View属性
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CharIndicateView, 0, 0);
        try {
            mIndicateTextSize = a.getDimensionPixelSize(R.styleable.CharIndicateView_indicateTextSize, mIndicateTextSize);
            mIndicateTextColor = a.getColor(R.styleable.CharIndicateView_indicateTextColor, mIndicateTextColor);
            mBackgroundColor = a.getColor(R.styleable.CharIndicateView_indicateBackground, mBackgroundColor);
            mBackgroundRadius = a.getDimensionPixelSize(R.styleable.CharIndicateView_indicateBackgroundRadius, mBackgroundRadius);
        } finally {
            //回收TypedArray
            a.recycle();
        }
    }

    /**
     * 初始化操作
     */
    private void init() {
        //设置圆角矩形背景
        //  float[] outerRadii = {10, 10, 10, 10, 10, 10, 10, 10};
        float[] outerRadii = new float[8];
        for (int i = 0; i < outerRadii.length; i++) {
            outerRadii[i] = mBackgroundRadius;
        }
        RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
        ShapeDrawable shapeDrawable = new ShapeDrawable(shape);
        shapeDrawable.getPaint().setColor(mBackgroundColor);
        //将圆角矩形背景设置到当前View
        this.setBackgroundDrawable(shapeDrawable);
        //文本居中显示
        this.setGravity(Gravity.CENTER);
        //设置字体大小
        this.setTextSize(mIndicateTextSize);
        //设置字体颜色
        this.setTextColor(mIndicateTextColor);
        //默认不显示该布局
        this.setVisibility(View.GONE);
    }

    /**
     * 展示选中字符
     *
     * @param selectedChar 要显示的字符
     */
    public void showSelectedChar(String selectedChar) {
        this.setText(selectedChar);
    }

}

字符指示布局的实现比较简单,直接继承自TextView,在初始化中设置了文本的颜色、大小、圆角矩形背景,并默认隐藏了该布局。此外,还添加了showSelectedChar(String selectedChar)方法,来显示相应的字符。

4、字符索引栏CharSlideBar与字符指示视图CharIndicateView建立关联

CharSlideBar我们添加如下关联方法:

/**
 * 和字符指示View建立联系
 *
 * @param charIndicateView
 */
public void setupWithIndicateView(CharIndicateView charIndicateView) {
    mCharIndicateView = charIndicateView;
}

然后在上述的selectedPositionByY(float y)中调用了CharIndicateViewshowSelectedChar(String selectedChar)方法。

到此,我们就可以实现本文开始时的效果了。

在应用中的使用方法

布局文件activity_main.xml:




    
    

    
    

    
    



Activity:MainActivity:

public class MainActivity extends AppCompatActivity {

    private final String TAG = this.getClass().getSimpleName();

    private List mContactList;  //联系人集合

    private ListView mContactListView;  //联系人列表
    private CharSlideBar mCharSlideBar;  //字符索引栏
    private CharIndicateView mCharIndicateView;  //字符指示视图
    private ContactlistAdapter mContactListAdapter;  //联系人列表适配器

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

        //初始化变量
        initVariables();
        //初始化View
        initView();
    }

    /**
     * 初始化变量
     */
    private void initVariables() {
        ContactModel model = new ContactModel();
        mContactList = model.getContactList();
        mContactListAdapter = new ContactlistAdapter(this, mContactList);
    }

    /**
     * 初始化View
     */
    private void initView() {
        mContactListView = (ListView) findViewById(R.id.contact_listview);
        mCharSlideBar = (CharSlideBar) findViewById(R.id.char_slider_bar);
        mCharIndicateView = (CharIndicateView) findViewById(R.id.char_indicate_view);

        //联系人设置适配器
        mContactListView.setAdapter(mContactListAdapter);
        //索引栏和指示视图建立联系
        mCharSlideBar.setupWithIndicateView(mCharIndicateView);
        //设置选中监听事件
        mCharSlideBar.setOnSelectedListener(new CharSlideBar.OnSelectedListener() {
            @Override
            public void onSelected(int position, String selectedChar) {
                Log.e(TAG, "选中--" + selectedChar);
                //根据选中的字符来定位ListView的位置
                locateListViewPositionByChar(selectedChar);
            }
        });
    }

    /**
     * 根据选中的字符来定位ListView的位置
     *
     * @param selectedChar
     */
    private void locateListViewPositionByChar(String selectedChar) {
        //遍历联系人列表找到对应字符的位置
        for (int i = 0; i < mContactList.size(); i++) {
            String nameInitial = mContactList.get(i).getNameInitial();
            if (nameInitial.equals(selectedChar)) {
                //定位ListView的位置
                mContactListView.setSelection(i);
                break;
            }
        }
    }
}

源码及Demo

Demo源码 请点击此处下载(https://github.com/shorr/notes_demo/tree/master/CharSlideBarDemo)

你可能感兴趣的:(Android自定义View(二):实例篇——字符索引栏)