Android 自定义控件实现 (多行选择条)

by 吴思博

一、实现思路(如何实现?)

二、读源码:TabLayout

三、自定义实现主要过程

四、该页面其他一些相关问题(Fragment销毁后RadioGroup恢复)

五、小结

虽然Android提供了一套GUI库,里面有很多控件,但是很多时候我们并不满足于系统的控件,通过自定义view,我们可以实现各种五花八门的效果,但是自定义控件有一定难度,尤其是复杂的自定义控件。

在云阅读新版本(5.4.3)中,交互需要实现多行的选择条,平常我们看到的基本上都是网易新闻这种单行的TabLayout。 那我们改如何实现各种定制化的控件呢? 先上图:

图1.网易新闻单行选择条

图2.网易云阅读多行选择条

图3.网易云阅读界面预览

一、实现思路(如何实现)

a、首先… …、没错、、、第一个思路就是网上去搜搜看,有没有类似控件,去网上找了一圈没有发现类似的多行的选择器。

发现了两个单行的开源指示器还不错,有兴趣可以看看

https://github.com/hackware1993/MagicIndicatorhttp://blog.csdn.net/analyzesystem/article/details/51426473

b、既然网上没有,就只能自己实现了。实现自定义View一般有4种思路。

1.继承特定的View(比如TextView)。例如定制的TextView可以继承TextView再添加或修改一些特定的方法。

2.继承View重写onDraw的方法。(例如实现圆形view)

3.继承特定的ViewGroup。比如LinearLayout,几种常见View组合在一起的时候,可以采用此方法。不需要自己处理ViewGroup的测量和布局的两个过程。

4.继承ViewGroup,需要自己处理ViewGroup的测量和布局的两个过程。

思路2、3都不适合当前方法,剩下思路1、和2。一个是继承Tablayout重写onMeasure和onLayout对子view重写测量和排列,另一种是自己继承viewGroup,自己处理ViewGroup的测量和布局的两个过程。第一种好像更简单,但是我们可以先看看TabLayout源码。

二、读源码:TabLayout

1、内部类及分析其关系:

lTab类和TabView类和SlidingTabStrip类为TabLayout提供了三个基本的元素。

lTabLayoutOnPageChangeListener和ViewPagerOnTabSelectedListener实现了ViewPager类的两个接口,作用是监听ViewPager页面改变和Tab选中状态。

lPagerAdapterObserver为观察者监控PagerAdapter数据变化。

2、TabLayout常用的方法如下:

3、源码中选择tab的关键实现:

Tab是每个item的mode类。Tab内部类定义了item的成员变量,并set和get方法,然后又封装了Tab的属性设置方法。

TabView是每个item的View,继承自LinearLayout。它的作用是让Tab同时显示文字和图片,也可以通过mCustomview设置自定义item。

TabLayout前两个个构造函数都是调用了自己的第三个构造函数,第三个构造函数里面添加了一个私有内部类SlidingTabStrip作为子view。

这个自定义view SlidingTabStrip是私有内部类,它继承自LinearLayout,作用为获取tab的最宽宽度,设置SlidingTabStrip的宽度,并设置一个动画,随着tab的改变绘制SlidingTabStrip。

在将选项添加到选项卡的addTabView方法中,看到每个item其实是加到SlidingTabStrip中。

三、自定义实现主要过程

通过读TabLayout源码,我们发现思路1,直接继承Tablayout重写onLayout和onMeasure对子view重写排列和测量不行,因为TabLayout里是加了一个私有的SlidingTabStrip作为子类,Item的view都是加在SlidingTabStrip中,没有办法通过重写私有SlidingTabStrip的onLayout和onMeasure。

所以现在只能继承ViewGroup,需要自己处理ViewGroup的测量和布局的两个过程。我们可以通过仿写TabLayout实现MyTabLayout,最简单的办法就是先把TabLayout拷贝出来到MyTabLayout,再进行定制化修改。

实现:

1.先把TabLayout拷贝出来到MyTabLayout,先跑通。

2.TabLayout包裹了一个SlidingTabStrip,我们只需要重写SlidingTabStrip的onLayout和onMeasure, 再重写TabLayout的onMeasure,让他的宽高为唯一子类SlidingTabStrip的宽高。

ViewGroup常用的生命周期回调:initView(构造方法)、onFinishInflate(当布局加载完成调用)、onMeasure(当测量时调用)、onSizeChanged(当尺寸改变调用)、onLayout(当布局时调用)、onDraw和dispatchDraw(绘制背景以及绘制子View)。

初始化ViewGroup的流程大致为:构造方法创建对象->从布局加载(xml中定义时)->第一遍测量->开始改变尺寸->第一遍布局->第二遍测量->第二遍布局

重载onMeasure()方法

为什么要重载onMeasure()方法这里就不赘述了。测算间距space如果间距小于3dp按顺序排列,否则每行N个(item为4个字N=5,2个字N=7)。setMeasuredDimension(resolveSize(mScreenWidth, widthMeasureSpec), Hight);设置自身宽高(宽为屏幕宽,高为子View的排数)。

由于ViewGroup的定位就是一个容器,用来盛放子控件的,所以就必须定义要以什么的方式来盛放,比如LinearLayout就是以横向或者纵向顺序存放,而RelativeLayout则以相对位置来摆放子控件,同样,我们的自定义ViewGroup也必须给出我们期望的布局方式,而这个定义就通过onLayout()函数来实现。

childView.layout(mPainterPosX + space, mPainterPosY, mPainterPosX + width + space, mPainterPosY + height);执行ChildView的绘制。

最后重写,TabLayout的onMeasure方法。

到目前为止,已经基本实现了,多层的选择器。

另外:

1、可以看到原来的TabLayout继承自HorizontalScrollView,所以需要禁止滑动功能,可以在构造函数添加如下代码:

2、细节的处理,还有很多间距什么需要处理,通过View的自定义参数的方式实现,这里不详叙述了。

3、如果每个item可以实现自定义view,处理好如下方法即可。

四、该页面其他一些相关问题(Fragment销毁后RadioGroup恢复)

由一个个单独的Fragment改成了TabLayout的形式,往后翻几个之前的Fragment容易被回收,这里就需要恢复Fragment了,通onSaveInstanceState()方法实现。

RadioGroup的选择项恢复有很多坑。发现RadioGroup恢复后不会按设定存的选项进行设置,主要就是需要注意以下的两个问题:

1、一组RadioGroup设置选中时候,要以RadioGroup为单位设置check(),不要给单个RadioButton设置button.setChecked(true),否则恢复很容易出问题。应该以组为单位使用check()。

2、RadioButton设置id的时候应该设置一个独一无二的id,否则恢复也会出现巨坑。改成随机数后恢复正常、不再随便乱选中。

五、小结:

1.一种自定义View可能有多种实现方式、我们要找到代价最小、最高效的方式。

2.通过实践有了更深的体会。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352