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

    <attr name="y_indicator_color" format="color"/>
    <attr name="y_indicator_radius" format="dimension"/>

    <declare-styleable name="YVPDotIndicator">
        <attr name="y_indicator_color"/>
        <attr name="y_indicator_radius"/>
    </declare-styleable>

3.2 代码实现

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

class YVPDotIndicator : LinearLayout {
    private var mStartPos: Float = 0.0F//indicator开始位置
    private var mWidthOffset: Int = 0//初始offset

    private var mPaint: Paint? = null

    private var mIndicatorColor = Color.parseColor("#FFFFFF")//indicator颜色
    private var mIndicatorRadius = 2//圆形indicator半径

    private var mVp: ViewPager? = null
    private var pageListener = InterPageChangeListener()

    private var mTabCount: Int? = 0
    private var mTabWidth: Float? = 0.0F
    private val defaultLayoutParams = LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f)

    constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
        setWillNotDraw(false)

        val dm = resources.displayMetrics

        val a = context.theme.obtainStyledAttributes(attrs, R.styleable.YVPDotIndicator, defStyle, 0)
        mIndicatorRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mIndicatorRadius.toFloat(), dm).toInt()

        mIndicatorColor = a.getColor(R.styleable.YVPDotIndicator_y_indicator_color, Color.parseColor("#FFFFFF"))
        mIndicatorRadius = a.getDimensionPixelSize(R.styleable.YVPDotIndicator_y_indicator_radius, 2)
        a.recycle()

        initPaint()
    }

    /**
     * 设置ViewPager
     */
    fun setViewPager(vp: ViewPager) {
        mVp = vp
        if (vp.adapter == null) {
            throw IllegalArgumentException()
        }
        notifyDataSetChanged()
        mVp?.addOnPageChangeListener(pageListener)
    }

    fun notifyDataSetChanged() {
        this.removeAllViews()
        mTabCount = mVp?.adapter?.count
        for (i in 0..mTabCount?.let { it - 1 } as Int) {
            addTextTab(i, mVp?.adapter?.getPageTitle(i).toString())
        }
    }

    fun addTextTab(position: Int, title: String) {
        var tab = TextView(context)
        tab.text = title
        tab.gravity = Gravity.CENTER
        tab.setSingleLine()

        tab.isFocusable = true
        tab.setOnClickListener { mVp?.currentItem = position }

        this.addView(tab, position, defaultLayoutParams)
    }

    /**
     * 初始化画笔
     */
    private fun initPaint() {
        mPaint = Paint()
        mPaint?.color = mIndicatorColor
        mPaint?.isAntiAlias = true
        mPaint?.style = Paint.Style.FILL
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mTabWidth = (w / childCount).toFloat()
        mStartPos = mTabWidth?.let { it/2 } as Float
    }

    override fun dispatchDraw(canvas: Canvas?) {
        canvas?.save()
        canvas?.translate(0.0F, height.toFloat())
        canvas?.drawCircle(mStartPos + mWidthOffset, -mIndicatorRadius.toFloat(), mIndicatorRadius.toFloat(), mPaint)
        canvas?.restore()
        super.dispatchDraw(canvas)
    }

    inner class InterPageChangeListener: ViewPager.OnPageChangeListener {
        override fun onPageScrollStateChanged(state: Int) {

        }

        override fun onPageSelected(position: Int) {

        }

        override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
            val tabWidth = screenWidth / childCount
            mWidthOffset = (tabWidth * position + tabWidth * positionOffset).toInt()
            invalidate()
        }
    }

    /**
     * 获取屏幕宽度

     * @return
     */
    private val screenWidth: Int
        get() {
            val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
            val displayMetrics = DisplayMetrics()
            windowManager.defaultDisplay.getMetrics(displayMetrics)
            return displayMetrics.widthPixels
        }
}

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. Spring Boot 全局异常配置

    Spring Boot 全局异常配置,处理异常控制器需要和发生异常的方法在一个类中.使用 ControllerAdvice 注解 package com.li.controller; import o ...

  2. 5.2 Components — Defining A Component

    一.概述 1. 为了定义一个组件,创建一个模板,它的名字以components/开头.为了定义一个新组件{{blog-post}},例如,创建一个components/blog-post模板. 2.注 ...

  3. mac3.0环境搭建

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

  4. rails 数据验证

    validates :money,      :presence => true, :numericality => {:only_integer => true}

  5. ACM ICPC, JUST Collegiate Programming Contest (2018) Solution

    A:Zero Array 题意:两种操作, 1 p v  将第p个位置的值改成v  2  查询最少的操作数使得所有数都变为0  操作为可以从原序列中选一个非0的数使得所有非0的数减去它,并且所有数不能 ...

  6. Springmvc的拦截器执行顺序及各方法作用

    实现HandlerInterceptor接口或者继承HandlerInterceptor的子类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInt ...

  7. 20155201 实验三《Java面向对象程序设计》实验报告

    20155201 实验三<Java面向对象程序设计>实验报告 一.实验内容 XP基础 XP核心实践 相关工具 二.实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门 ...

  8. Ubuntu16.04 远程访问RabbitMQ

    我们在虚拟机里面安装好RabbitMQ以后,虽然可以在虚拟机中访问,但是在主机端并不能访问 现在要解决这个问题 第一:账户 RabbitMQ为了安全性考虑,默认的guest账户只能在本地127.0.0 ...

  9. 【建项目】eclipse maven建立多模块工程

    在工作的时候,大多时候都是用Maven来管理项目,可是一般我们都知道怎么用maven管理工程,却不知道通过Maven自己来建立多模块工程.于是自己抽时间,在网上找些资料,做了起来. 建立简单的Mave ...

  10. 路由器实操 能够登陆QQ 收发信息正常 但游览器无法连接网页

    这种情况下,网络是没有问题的. ping www.baidu.com 失败,但是能上QQ说明网络没有问题:这一般是域名解析DNS的问题. 游览器登陆192.168.1.1.输入用户密码,均为admin ...