我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android自定义字母导航栏

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android自定义字母导航栏

本文实例为大家分享了Android字母导航栏的具体代码,供大家参考,具体内容如下

效果

实现逻辑

明确需求

字母导航栏在实际开发中还是比较多见的,城市选择、名称选择等等可能需要到。 现在要做到的就是在滑动控件过程中可以有内容以及 下标的回调,方便处理其他逻辑!

整理思路

1、确定控件的尺寸,防止内容显示不全。相关的逻辑在onMeasure()方法中处理;
2、绘制显示的内容,在按下和抬起不同状态下文字、背景的颜色。相关逻辑在onDraw()方法中;
3、滑动事件的处理以及事件回调。相关逻辑在onTouchEvent()方法中;

动手实现

在需求明确、思路清晰的情况下就要开始动手实现(需要了解自定义View的一些基础API)。核心代码就onDraw()中。在代码中有思路和注释,可以结合代码一起看看。如果有疑惑、优化、错误的地方请在评论区提出,共同进步!

完整代码



public class CustomLetterNavigationView extends View {
  private static final String TAG = "CustomLetterNavigation";
  //导航内容
  private String[] mNavigationContent;
  //导航栏内容间隔
  private float mContentDiv;
  //导航栏文字大小
  private float mContentTextSize;
  //导航栏文字颜色
  private int mContentTextColor;
  //导航栏按下时背景颜色
  private int mBackgroundColor;
  //导航栏按下时圆角度数
  private int mBackGroundAngle = 0;
  //导航栏按下时文字颜色
  private int mDownContentTextColor;
  private TextPaint mTextPaint;
  private Paint mPaintBackgrount;
  private boolean mEventActionState = false;
  private String mCurrentLetter = "";
  private OnNavigationScrollerListener mOnNavigationScrollerListener;
  private final RectF mRectF = new RectF();
  public CustomLetterNavigationView(Context context) {
    this(context, null);
  }
  public CustomLetterNavigationView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }
  public CustomLetterNavigationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initDefaultData();//初始化默认数据
    initAttrs(context, attrs);
  }
  @Override
  protected void onDraw(Canvas canvas) {
    
    int mViewWidth = getWidth();
    //绘制背景
    mRectF.set(0, 0, mViewWidth, getHeight());
    if (mEventActionState) {
      mTextPaint.setColor(mDownContentTextColor);
      mPaintBackgrount.setColor(mBackgroundColor);
      canvas.drawRoundRect(mRectF, mBackGroundAngle, mBackGroundAngle, mPaintBackgrount);
    } else {
      mTextPaint.setColor(mContentTextColor);
      mPaintBackgrount.setColor(Color.TRANSPARENT);
      Drawable mBackground = getBackground();
      if (mBackground instanceof ColorDrawable) {
        mPaintBackgrount.setColor(((ColorDrawable) mBackground).getColor());
      }
      canvas.drawRoundRect(mRectF, mBackGroundAngle, mBackGroundAngle, mPaintBackgrount);
    }
    //绘制文本
    float textX = mViewWidth / 2;
    //X轴坐标
    int contentLenght = getContentLength();
    //Y轴坐标(这里在测量的时候多加入了两个间隔高度要减去,同时还有Padding值)
    float heightShould = (getHeight() - mContentDiv * 2 - getPaddingTop() - getPaddingBottom()) / contentLenght;
    for (int i = 0; i < contentLenght; i++) {
      //计算Y轴的坐标
      float startY = ((i + 1) * heightShould) + getPaddingTop();
      //绘制文字
      canvas.drawText(mNavigationContent[i], textX, startY, mTextPaint);
    }
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    
    float mEventY = event.getY();
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //手指按下的时候,修改Enent状态、重绘背景、触发回调
        mEventActionState = true;
        invalidate();
        if (mOnNavigationScrollerListener != null) {
          mOnNavigationScrollerListener.onDown();
        }
        scrollCount(mEventY);
        break;
      case MotionEvent.ACTION_MOVE:
        scrollCount(mEventY);
        break;
      case MotionEvent.ACTION_CANCEL:
      case MotionEvent.ACTION_UP:
        //手指离开的时候,修改Enent状态、重绘背景、触发回调
        mEventActionState = false;
        invalidate();
        if (mOnNavigationScrollerListener != null) {
          mOnNavigationScrollerListener.onUp();
        }
        break;
    }
    return true;
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
    int widhtMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    //获取控件的尺寸
    int actualWidth = MeasureSpec.getSize(widthMeasureSpec);
    int actualHeight = MeasureSpec.getSize(heightMeasureSpec);
    int contentLegth = getContentLength();
    //计算一个文字的尺寸
    Rect mRect = measureTextSize();
    //内容的最小宽度
    float contentWidth = mRect.width() + mContentDiv * 2;
    //内容的最小高度
    float contentHeight = mRect.height() * contentLegth + mContentDiv * (contentLegth + 3);
    if (MeasureSpec.AT_MOST == widhtMode) {
      //宽度包裹内容
      actualWidth = (int) contentWidth + getPaddingLeft() + getPaddingRight();
    } else if (MeasureSpec.EXACTLY == widhtMode) {
      //宽度限制
      if (actualWidth < contentWidth) {
        actualWidth = (int) contentWidth + getPaddingLeft() + getPaddingRight();
      }
    }
    if (MeasureSpec.AT_MOST == heightMode) {
      //高度包裹内容
      actualHeight = (int) contentHeight + getPaddingTop() + getPaddingBottom();
    } else if (MeasureSpec.EXACTLY == widhtMode) {
      //高度限制
      if (actualHeight < contentHeight) {
        actualHeight = (int) contentHeight + getPaddingTop() + getPaddingBottom();
      }
    }
    setMeasuredDimension(actualWidth, actualHeight);
  }
  
  private void initDefaultData() {
    mNavigationContent = new String[]{"搜", "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"};
    mContentDiv = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
    mContentTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics());
    mContentTextColor = Color.parseColor("#333333");
    mDownContentTextColor = Color.WHITE;
    mBackgroundColor = Color.parseColor("#d7d7d7");
    mBackGroundAngle = 0;
    //绘制文字画笔
    mTextPaint = new TextPaint();
    mTextPaint.setAntiAlias(true);
    mTextPaint.setTextSize(mContentTextSize);
    mTextPaint.setColor(mContentTextColor);
    mTextPaint.setTextAlign(Paint.Align.CENTER);
    //绘制背景画笔
    mPaintBackgrount = new Paint();
    mPaintBackgrount.setAntiAlias(true);
    mPaintBackgrount.setStyle(Paint.Style.FILL);
  }
  
  private void initAttrs(Context context, AttributeSet attrs) {
    TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomLetterNavigationView);
    mContentTextColor = mTypedArray.getColor(R.styleable.CustomLetterNavigationView_customTextColorDefault, mContentTextColor);
    mBackgroundColor = mTypedArray.getColor(R.styleable.CustomLetterNavigationView_customBackgroundColorDown, mBackgroundColor);
    mDownContentTextColor = mTypedArray.getColor(R.styleable.CustomLetterNavigationView_customTextColorDown, mDownContentTextColor);
    mContentTextSize = mTypedArray.getDimension(R.styleable.CustomLetterNavigationView_customTextSize, mContentTextSize);
    mContentDiv = mTypedArray.getFloat(R.styleable.CustomLetterNavigationView_customLetterDivHeight, mContentDiv);
    mBackGroundAngle = mTypedArray.getInt(R.styleable.CustomLetterNavigationView_customBackgroundAngle, mBackGroundAngle);
    mTypedArray.recycle();
  }
  
  private int getContentLength() {
    if (mNavigationContent != null) {
      return mNavigationContent.length;
    }
    return 0;
  }
  
  private void scrollCount(float mEventY) {
    //滑动的时候利用滑动距离和每一个字符高度进行取整,获取到Index
    Rect mRect = measureTextSize();
    int index = (int) ((mEventY - getPaddingTop() - getPaddingBottom() - mContentDiv * 2) / (mRect.height() + mContentDiv));
    //防止越界
    if (index >= 0 && index < getContentLength()) {
      String newLetter = mNavigationContent[index];
      //防止重复触发回调
      if (!mCurrentLetter.equals(newLetter)) {
        mCurrentLetter = newLetter;
        if (mOnNavigationScrollerListener != null) {
          mOnNavigationScrollerListener.onScroll(mCurrentLetter, index);
        }
      }
    }
  }
  
  public Rect measureTextSize() {
    Rect mRect = new Rect();
    if (mTextPaint != null) {
      mTextPaint.getTextBounds("田", 0, 1, mRect);
    }
    return mRect;
  }
  
  public void setOnNavigationScrollerListener(OnNavigationScrollerListener onNavigationScrollerListener) {
    this.mOnNavigationScrollerListener = onNavigationScrollerListener;
  }
  
  public void setNavigationContent(String content) {
    if (!TextUtils.isEmpty(content)) {
      mNavigationContent = null;
      mNavigationContent = new String[content.length()];
      for (int i = 0; i < content.length(); i++) {
        mNavigationContent[i] = String.valueOf(content.charAt(i));
      }
    }
    //需要重新测量
    requestLayout();
  }
  public interface OnNavigationScrollerListener {
    //按下
    void onDown();
    //滑动
    void onScroll(String letter, int position);
    //离开
    void onUp();
  }
}

自定义属性


<declare-styleable name="CustomLetterNavigationView">
    <attr name="customTextColorDefault" format="color" />
    <attr name="customTextColorDown" format="color" />
    <attr name="customBackgroundColorDown" format="color" />
    <attr name="customLetterDivHeight" format="dimension" />
    <attr name="customTextSize" format="dimension" />
    <attr name="customBackgroundAngle" format="integer" />
</declare-styleable>
您可能感兴趣的文章:Android程序开发之Fragment实现底部导航栏实例代码Android实现沉浸式通知栏通知栏背景颜色跟随app导航栏背景颜色而改变Android实现底部导航栏功能(选项卡)Android 沉浸式状态栏与隐藏导航栏实例详解Android仿网易客户端顶部导航栏效果超简单的几行代码搞定Android底部导航栏功能Android实现顶部导航栏可点击可滑动效果(仿微信仿豆瓣网)Android仿今日头条顶部导航栏效果的实例代码Android实现简单底部导航栏 Android仿微信滑动切换效果Android项目实战之仿网易顶部导航栏效果


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android自定义字母导航栏

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android自定义字母导航栏

本文实例为大家分享了Android字母导航栏的具体代码,供大家参考,具体内容如下 效果实现逻辑 明确需求字母导航栏在实际开发中还是比较多见的,城市选择、名称选择等等可能需要到。 现在要做到的就是在滑动控件过程中可以有内容以及 下标的回调,方
2022-06-06

Android自定义View实现字母导航栏

很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章。思路分析:1、自定义View实现字母导航栏2、ListView实现联
2022-06-06

Android自定义View实现字母导航栏的代码

思路分析: 1、自定义View实现字母导航栏 2、ListView实现联系人列表 3、字母导航栏滑动事件处理 4、字母导航栏与中间字母的联动 5、字母导航栏与ListView的联动 效果图:首先,我们先甩出主布局文件,方便后面代码的说明
2022-06-06

Android自定义ViewPagerIndicator实现炫酷导航栏指示器(ViewPager+Fragment)

ViewPagerIndicator导航栏指示器运行效果:实现这个效果,我是看了很多大神写的博客和视频后自己敲的,欢迎指正 github地址:https://github.com/dl10210950/TabViewPagerIndicat
2022-06-06

Android design包自定义tablayout的底部导航栏的实现方法

以前做项目大多用的radiobutton,今天用tablayout来做一个tab切换页面的的效果. 实现的效果就是类似QQ.微信的页面间(也就是Fragment间)的切换.如图: 布局只要一个tablayout
2022-06-06

uniapp微信小程序怎么自定义导航栏

本篇内容介绍了“uniapp微信小程序怎么自定义导航栏”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!首先我们在自定义导航栏的时候,我们需要知
2023-07-02

Android怎么实现字母导航控件

这篇文章主要介绍“Android怎么实现字母导航控件”,在日常操作中,相信很多人在Android怎么实现字母导航控件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android怎么实现字母导航控件”的疑惑有所
2023-06-26

示例演示uniapp如何实现自定义导航栏

随着移动端应用的发展,导航栏成为了许多应用必备的功能之一。而Uni-app是一款可以同时开发多个移动端应用(包括iOS、Android等)的全栈式框架,它提供了丰富的组件以及API,方便开发者快速复用和定制应用功能。在Uni-app中,要实现导航栏可以使用uni-ui组件库中的导航栏组件,也可以使用自定义组件的方式进行实现。下面我们将通过实例来演示如何实现自定义导航栏。## 1
2023-05-14

微信小程序怎么实现自定义导航栏

今天小编给大家分享一下微信小程序怎么实现自定义导航栏的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1、要实现自定义导航栏,首
2023-06-29

SwiftUI怎么自定义导航

这篇文章主要介绍“SwiftUI怎么自定义导航”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SwiftUI怎么自定义导航”文章能帮助大家解决问题。前言默认情况下,SwiftUI提供的各种导航API在
2023-06-30

Android仿微信实现首字母导航条

本文介绍Android实现首字母导航条,先看张效果图,具体怎么实现看代码吧具体的步骤 1.整体布局的显示 2. 实现A-Z的分组 3. 自定义A-Z的导航条 4. 中间显示/隐藏触摸到导航条具体的字母 activity_main.xm
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录