03.自定义View(AlphabetView字母索引View)

简介: 感谢红橙Darren博主package com.rzm.commonlibrary.views;import android.content.Context;import android.

感谢红橙Darren博主

package com.rzm.commonlibrary.views;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import com.rzm.commonlibrary.R;


/**
 * Created by renzhenming on 2018/3/24.
 * 实现根据字母分类排序的列表,多用于地址选择或者手机通讯录
 */

public class AlphabetView extends View{

    //普通模式绘制的画笔
    private final Paint mPaint;

    //触摸时绘制用的画笔
    private final Paint mTouchPaint;

    //默认字母大小
    private int mTextSize = 16;

    //默认字母颜色
    private int mTextColor = Color.BLACK;

    //触摸时字母颜色
    private int mTouchTextColor = Color.RED;

    //控件的高度
    private int mViewHeight;

    //26个字母
    private String mLetters[] = {"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"};
    private int mItemHeight;

    //当前触摸的字母
    private String mCurrentTouchLetter;

    //当前是否在触摸
    private boolean mCurrentIsTouch = false;

    //抬起之后是否清除当前选中的高亮颜色
    private boolean mClearAfterUp = true;

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

    public AlphabetView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,-1);
    }

    public AlphabetView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //获取自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AlphabetView);
        mTextColor = typedArray.getColor(R.styleable.AlphabetView_textColor, mTextColor);
        mTouchTextColor = typedArray.getColor(R.styleable.AlphabetView_touchTextColor, mTouchTextColor);
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.AlphabetView_textSize, mTextSize);
        typedArray.recycle();

        //设置画笔用于绘制字母

        /**
         * 为什么使用两个画笔而不是单独的使用一个然后切换颜色的设置,因为paint是有一个
         * 设置颜色的方法的setColor,我们看一下setColor的源码,发现是native方法实现的,
         *  private static native void nSetColor(long paintPtr, @ColorInt int color);
         * 如果在onDraw中频繁的切换颜色性能会降低,所以这就是通常使用几种颜色定义几个画笔
         * 的原因
         */
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(mTextColor);
        mPaint.setTextSize(sp2px(mTextSize));

        mTouchPaint = new Paint();
        mTouchPaint.setAntiAlias(true);
        mTouchPaint.setDither(true);
        mTouchPaint.setColor(mTouchTextColor);
        mTouchPaint.setTextSize(sp2px(mTextSize));
    }

    private float sp2px(int sp){
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,sp,getResources().getDisplayMetrics());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //根据布局中设置的宽度模式确定宽度
        int width =0;
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY){
            width = MeasureSpec.getSize(widthMeasureSpec);
        }
        if (mode == MeasureSpec.AT_MOST){
            //计算宽度,两边的padding加上文字宽度,这个文字随意,不过最好是26个字母中最宽的
            float textWidth = mPaint.measureText("");
            width = (int) (getPaddingLeft() + getPaddingRight()+textWidth);
        }
        //获取设置的高度
        mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(width, mViewHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //获取每个字母占的高度
        mItemHeight = (getHeight()-getPaddingTop() - getPaddingBottom())/mLetters.length;

        //绘制26个字母
        for (int i = 0; i < mLetters.length; i++) {
            //得到每个字母的中心位置,防止给布局设置了paddingTop,所以这里+上
            int letterCenterY = i* mItemHeight + mItemHeight /2+getPaddingTop();

            //得到每个字母的中心位置
            Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
            int dy = (int) ((fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom);

            //根据字母中心位置求字母的基线
            //baseline = center + (FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom;
            int baseLine = letterCenterY+dy;

            //获取每一个字母绘制的x坐标
            float letterWidth = mPaint.measureText(mLetters[i]);
            //确保每一个字母都在中间位置
            int x = (int) (getWidth()/2 - letterWidth/2);
            //开始绘制
            /**
             * text 需要绘制的文字
             x 绘制文字原点X坐标
             y 绘制文字原点Y坐标
             xy指的时文字左下角的坐标,也就是基线和文字最左边下方的交点
             paint 画笔
             */
            if (mLetters[i].equals(mCurrentTouchLetter)){
                canvas.drawText(mLetters[i],x,baseLine,mTouchPaint);
            }else{
                canvas.drawText(mLetters[i],x,baseLine,mPaint);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:

                //当前触摸的位置,相对与控件的位置而不是相对于屏幕
                float startY = event.getY();
                //计算当前字母的位置
                int position = (int)startY/mItemHeight;

                //防止当触摸超出控件范围的时候,设定position值,防止数组越界
                if (position <0){
                    position = 0;
                }
                if (position > mLetters.length - 1){
                    position = mLetters.length - 1;
                }
                //滑动过程中如果滑动的位置和上一次相同,就不再处理,减少invalidate的次数
                if (mLetters[position].equals(mCurrentTouchLetter)){
                    return true;
                }
                //记录当前触摸的弥足
                mCurrentTouchLetter = mLetters[position];
                mCurrentIsTouch = true;
                if (mTouchListener != null){
                    mTouchListener.onTouch(mCurrentTouchLetter,mCurrentIsTouch);
                }

                break;

            case MotionEvent.ACTION_UP:
                //抬起时设置为false
                mCurrentIsTouch = false;
                if (mTouchListener != null){
                    mTouchListener.onTouch(mCurrentTouchLetter,mCurrentIsTouch);
                }
                if (mClearAfterUp){
                    //将当前触摸的字母设置为null,up之后重绘界面,清除高亮状态
                    mCurrentTouchLetter = null;
                }
                break;
        }
        invalidate();
        return true;
    }

    /**
     * 设置手指触摸抬起之后,是否恢复高亮状态为正常状态,默认为true
     * @param mClearAfterUp
     */
    public void setClearAfterUp(boolean mClearAfterUp) {
        this.mClearAfterUp = mClearAfterUp;
    }

    // 设置触摸监听
    private OnAlphabetTouchListener mTouchListener;
    public void setOnAlphabetTouchListener(OnAlphabetTouchListener touchListener) {
        this.mTouchListener = touchListener;
    }
    public interface OnAlphabetTouchListener {
        void onTouch(String letter, boolean isTouch);
    }
}
相关文章
|
1月前
|
XML 数据格式
PopupWindow的简单用法
PopupWindow的简单用法
22 1
【Flutter】GridView 网格布局 ( GridView.count 构造函数 | crossAxisCount 参数指定每行元素个数 )
【Flutter】GridView 网格布局 ( GridView.count 构造函数 | crossAxisCount 参数指定每行元素个数 )
499 0
【Flutter】GridView 网格布局 ( GridView.count 构造函数 | crossAxisCount 参数指定每行元素个数 )
|
XML Java Android开发
Android各种动画效果ScaleAnimation,AlphaAnimation,TranslateAnimation,RotateAnimation(文章结尾有代码)
 终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~      在各种应用中,通常会使用到多种动画效果,在Android中常见的动画有四种:Scale,Alpha,Translate,Rotate。
2002 0
|
Android开发
解析6种常用View 的滑动方法
  > View 的滑动是Android 实现自定义控件的基础,实现View 滑动有很多种方法,在这里主要讲解6 种滑动方法,分别是layout()、offsetLeftAndRight()与offsetTopAndBottom()、LayoutParams、动画、scollTo 与scollBy,以及Scroller。
1555 0
|
Android开发
Android RecyclerView 实现position列表倒序排列(汇总)
转载请标明出处: http://blog.csdn.net/djy1992/article/details/76201794 本文出自:【奥特曼超人的博客】 刚刚群里有人在问Position倒序的问题,刚好有点时间在这里总结下分享给大家。
3497 0
|
Android开发
Android下拖动任意View代码(使用属性动画,眼前一亮)
mLocalFrameLayout.setOnTouchListener(new View.
1232 0
|
Android开发
Android TextView 字符串展示不同大小文字
用Spannable字符串实现:   String s= "Hello Everyone"; SpannableString ss1= new SpannableString(s); ss1.
951 0
|
API
TextView使用Spannable设置复合文本
端午过后又一天,还是没事干,再写一篇博客,过后就要期末考试了,可能最近就不出博文了,等暑假再重出江湖吧。 今天来弄弄这个TextView的效果,应用场景还是很广泛的,一个TextView文本拥有各种各样的样式,以前给文本插入图片都是那种drawableLeft啥的right的,图片大小还不好控制,排版又不好看,一次只能插入一张图片,根本满足不了需求,比如一些数学公式的上标和
1519 0
ScrollView嵌套EditText联带滑动的解决办法
本篇文章的相关内容需结合上文:从ScrollView嵌套EditText的滑动事件冲突分析触摸事件的分发机制以及TextView的简要实现和冲突的解决办法 在说完了如何解决ScrollView嵌套EditText的滑动事件冲突之后,我们接下来说一下如何实现它们两者之间的联带滑动。
1068 0