1. 简介

学习Android,自定义View不可避免,之前一直忽视这块内容,现在开始学,应该不算太晚。从常见的ViewPagerIndicator开始,当然,万能的Github上包罗万象,好用的indicator也是不胜枚举,旨在学习自定义View的一般操作过程。


2. 大致思路

做一个简单的ViewPagerIndicator,只支持平均大小的TextView,支持点,矩形和三角形。

1. 使用LinearLayout作为父类;

2. 定义indicator的颜色,高度和半径,当然也可以定义其他的属性;

3. 使用到Path,Paint,Canvas等图形绘制的内容;

4. 使用Kotlin


3. 实现

以圆形indicator为例

3.1 定义自定义属性

styles.xml

  1. <attr name="y_indicator_color" format="color"/>
  2. <attr name="y_indicator_radius" format="dimension"/>
  3. <declare-styleable name="YVPDotIndicator">
  4. <attr name="y_indicator_color"/>
  5. <attr name="y_indicator_radius"/>
  6. </declare-styleable>

3.2 代码实现

通过设置ViewPager,然后从Adapter的getPageTitle方法获取TextView的显示内容,然后添加标签,然后绘制圆形指示器,通过ViewPager的滑动回调方法,设置圆形指示器的位置。

  1. class YVPDotIndicator : LinearLayout {
  2. private var mStartPos: Float = 0.0F//indicator开始位置
  3. private var mWidthOffset: Int = 0//初始offset
  4. private var mPaint: Paint? = null
  5. private var mIndicatorColor = Color.parseColor("#FFFFFF")//indicator颜色
  6. private var mIndicatorRadius = 2//圆形indicator半径
  7. private var mVp: ViewPager? = null
  8. private var pageListener = InterPageChangeListener()
  9. private var mTabCount: Int? = 0
  10. private var mTabWidth: Float? = 0.0F
  11. private val defaultLayoutParams = LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f)
  12. constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0)
  13. constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
  14. setWillNotDraw(false)
  15. val dm = resources.displayMetrics
  16. val a = context.theme.obtainStyledAttributes(attrs, R.styleable.YVPDotIndicator, defStyle, 0)
  17. mIndicatorRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mIndicatorRadius.toFloat(), dm).toInt()
  18. mIndicatorColor = a.getColor(R.styleable.YVPDotIndicator_y_indicator_color, Color.parseColor("#FFFFFF"))
  19. mIndicatorRadius = a.getDimensionPixelSize(R.styleable.YVPDotIndicator_y_indicator_radius, 2)
  20. a.recycle()
  21. initPaint()
  22. }
  23. /**
  24. * 设置ViewPager
  25. */
  26. fun setViewPager(vp: ViewPager) {
  27. mVp = vp
  28. if (vp.adapter == null) {
  29. throw IllegalArgumentException()
  30. }
  31. notifyDataSetChanged()
  32. mVp?.addOnPageChangeListener(pageListener)
  33. }
  34. fun notifyDataSetChanged() {
  35. this.removeAllViews()
  36. mTabCount = mVp?.adapter?.count
  37. for (i in 0..mTabCount?.let { it - 1 } as Int) {
  38. addTextTab(i, mVp?.adapter?.getPageTitle(i).toString())
  39. }
  40. }
  41. fun addTextTab(position: Int, title: String) {
  42. var tab = TextView(context)
  43. tab.text = title
  44. tab.gravity = Gravity.CENTER
  45. tab.setSingleLine()
  46. tab.isFocusable = true
  47. tab.setOnClickListener { mVp?.currentItem = position }
  48. this.addView(tab, position, defaultLayoutParams)
  49. }
  50. /**
  51. * 初始化画笔
  52. */
  53. private fun initPaint() {
  54. mPaint = Paint()
  55. mPaint?.color = mIndicatorColor
  56. mPaint?.isAntiAlias = true
  57. mPaint?.style = Paint.Style.FILL
  58. }
  59. override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
  60. super.onSizeChanged(w, h, oldw, oldh)
  61. mTabWidth = (w / childCount).toFloat()
  62. mStartPos = mTabWidth?.let { it/2 } as Float
  63. }
  64. override fun dispatchDraw(canvas: Canvas?) {
  65. canvas?.save()
  66. canvas?.translate(0.0F, height.toFloat())
  67. canvas?.drawCircle(mStartPos + mWidthOffset, -mIndicatorRadius.toFloat(), mIndicatorRadius.toFloat(), mPaint)
  68. canvas?.restore()
  69. super.dispatchDraw(canvas)
  70. }
  71. inner class InterPageChangeListener: ViewPager.OnPageChangeListener {
  72. override fun onPageScrollStateChanged(state: Int) {
  73. }
  74. override fun onPageSelected(position: Int) {
  75. }
  76. override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
  77. val tabWidth = screenWidth / childCount
  78. mWidthOffset = (tabWidth * position + tabWidth * positionOffset).toInt()
  79. invalidate()
  80. }
  81. }
  82. /**
  83. * 获取屏幕宽度
  84. * @return
  85. */
  86. private val screenWidth: Int
  87. get() {
  88. val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
  89. val displayMetrics = DisplayMetrics()
  90. windowManager.defaultDisplay.getMetrics(displayMetrics)
  91. return displayMetrics.widthPixels
  92. }
  93. }

4. 其他Indicator

类似的矩形指示器和三角形指示器,均可按照上面的方式实现。


5. 具体效果


6. 示例源码

请移步Github———-YVPIndicator

自定义ViewPagerIndicator的更多相关文章

  1. Android 教你打造炫酷的ViewPagerIndicator 不仅仅是高仿MIUI

    1.概述 哈,今天给大家带来一个ViewPagerIndicator的制作,相信大家在做tabIndicator的时候,大多数人都用过 TabPageIndicator,并且很多知名APP都使用过这个 ...

  2. 使用TabPageIndicator的样式问题

    在使用TabPageIndicator往往会出现一些样式问题,导致看不到字,下面是总结的步骤: 1.布局<LinearLayout xmlns:android="http://sche ...

  3. Android之实现ViewPagerIndicator

    PS:最近一直忙于学习任务,一直没有时间去写博客.今天周六,终于有时间了. 学习任务: 1.打造一个自己的ViewPagerIndicator   最近被安排了一大堆的学习任务,感觉老板还是很好的,让 ...

  4. 老猪带你玩转android自定义控件一——打造最简单viewpagerindicator

    viewpagerindicator,既使用viewpager翻页时候,标题的指示条随着改变的控件,是常用android控件之一,几乎所有的新闻类APP中都有使用.如下图所示: 今天,我们将从0到1实 ...

  5. PagerTabStrip及自定义的PagerTab

    如图是效果图      开发中经常会用到上面是一个Tab下面是一个ViewPager(ViewPager再包含几个Fragment),当点击Tab或是滑动ViewPager,Tab及ViewPager ...

  6. Android 使用Fragment,ViewPagerIndicator 制作csdn app主要框架

    转载  转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/23513993 本来准备下载个CSDN的客户端放手机上,没事可以浏览浏览资 ...

  7. android123 zhihuibeijing 新闻中心-新闻 页签 ViewPagerIndicator实现

    ## ViewPagerIndicator ## 使用导入ViewPagerIndicator库的方式相当于可以改源码,打包编译Eclips可以自动完成. ViewPager指针项目,在使用ViewP ...

  8. ViewPagerindicator 源码解析

        ViewPagerindicator 源码解析   1. 功能介绍 1.1 ViewPagerIndicator ViewPagerIndicator用于各种基于AndroidSupportL ...

  9. 开源控件ViewPagerIndicator的使用

    此文转载自http://www.jianshu.com/p/a2263ee3e7c3 前几天学习了ViewPager作为引导页和Tab的使用方法.后来也有根据不同的使用情况改用Fragment作为Ta ...

随机推荐

  1. js判断用户是在PC端或移动端访问

    js如何判断用户是在PC端和还是移动端访问.  最近一直在忙我们团队的项目“咖啡之翼”,在这个项目中,我们为移动平台提供了一个优秀的体验.伴随Android平台的红火发展.不仅带动国内智能手机行业,而 ...

  2. Cocos2d-JS实现的2048

    一.前言 2048是之前火过一段时间的休闲数字消除类游戏,它的玩法很简单,上手很容易,可是想到要得到高分却很难,看似简单的游戏却有着很多得分的技巧,想当初这个游戏也曾是陪伴我大学课堂的游戏之一.虽然在 ...

  3. 爬取51job职位信息之编码问题

    兴趣来潮,爬了下51job,但是遇到编码问题!以下是简单的一段代码 获取整个页面数据 # -*- coding:utf-8 -*- import requests import sysreload(s ...

  4. The adidas NMD Camo Singapore consists of four colorways

    Next within the popular selection of the adidas NMD Singapore is really a clean all-black form of th ...

  5. mac3.0环境搭建

    export ANDROID_SDK_ROOT=/Users/sjxxpc/Documents/ADT/sdk export ANDROID_NDK_ROOT=/Users/sjxxpc/Docume ...

  6. And Design:拓荒笔记——Form表单

    And Design:拓荒笔记——Form表单 Form.create(options) Form.create()可以对包含Form表单的组件进行改造升级,会返回一个新的react组件. 经 For ...

  7. hdu5057 分块处理,当数值大于数据范围时树状数组 真是巧 将大数据分为小数据来处理

    这题说的给了100000个数有100000次操作 询问 L和R 区间内 在D位上为P的个数,用树状数组存 要开[10][10][100000]的int 开不了但是能开 这么大的unsign short ...

  8. Linux内核分析08

    进程的切换和系统的一般执行过程 一,进程切换的关键代码switch_to分析 进程调度的时机 中断处理过程(包括时钟中断.I/O中断.系统调用和异常)中,直接调用schedule(),或者返回用户态时 ...

  9. Tensorflow代码解析(一)

    http://www.leiphone.com/news/201702/n0uj58iHaNpW9RJG.html?utm_source=tuicool&utm_medium=referral ...

  10. kernel: swapper: page allocation failure. order:1, mode:0x20

    场景:领导电话通知,我们的主站宕机了,到家后从另外一台机器上ssh一直处于等待状态,开始怀疑机器的负载比较高,后查看监控机器,发现网卡.cpu.nginx连接数.....通通都没有数据了,显然不是负载 ...