viewpagerindicator,既使用viewpager翻页时候,标题的指示条随着改变的控件,是常用android控件之一,几乎所有的新闻类APP中都有使用。如下图所示:

  今天,我们将从0到1实现这一控件。

  其实,实现这一控件思路很简单:

  ①对头部的标题栏进行布局,头部标题栏,只能进行单选,这符合radiobutton的特质,但是普通的radiobutton,不是这样的吗?

  显然,我们在这里需要写样式进行处理,由于头部标题栏的条目非常的多,一个屏幕放不下,因此我们需要一个水平滚动条来盛放这很多的条目。

  ②我们看到底部的内容随着指示条再不断滑动,因此底部内容是一个viewpager。

  ③下面就是这个控件最关键的部分,就是指示条随着viewpager的内容相互滚动,这涉及到一定逻辑,下面我会阐述这方面的逻辑内容。

  说了一大段开场白以后,我们就应该庖丁解牛般的实现我们控件,废话少说,直接上代码,首先,粉墨登场是头部控件xml布局文件。

  

<!--
头部标题栏
包含一个水平滚动条
单选按钮组
以及一个标题指示条
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <HorizontalScrollView
android:id="@+id/hsv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:fadingEdge="none"
android:scrollbars="none" > <!-- 单选按钮 --> <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" > <RadioGroup
android:id="@+id/rg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</RadioGroup> <FrameLayout
android:layout_width="fill_parent"
android:layout_height="2dp" > <View
android:id="@+id/indicator"
android:layout_width="100dp"
android:layout_height="2dp"
android:background="#ff0000" />
</FrameLayout>
</LinearLayout>
</HorizontalScrollView> </LinearLayout>

  我们看到这个控件包含一个能够左右滚动水平滚动条,由于每个标题条目就是一个radiobutton,所以需要把这个radio button 放在一个单项按钮组中。当然,还需要一个指示翻到第几页的指示条。

  接下来,就是重头戏了,这个自定义控件逻辑控制。我们看一下他的流程图

  

  通过上述流程图,我们可以得出指示条伴随头部栏与viewpager滑动而滑动。

  做了这么多铺垫,这个控件的核心——逻辑代码将要暴露他的庐山真面目。

  一个自定义控件,首先要初始化原有控件。代码如下:

  

    /**
* 初始化 相应控件 并且为相应控件赋予相应的事件
* @param context
*/
@SuppressLint("InflateParams")
private void initView(Context context) { hsv = (HorizontalScrollView) findViewById(R.id.hsv); rg = (RadioGroup) findViewById(R.id.rg);
rg.setOnCheckedChangeListener(this);
indicator = findViewById(R.id.indicator); }  /**
* 为标题条目动态赋值
*/
public void setDatas(List<String> datas) {
this.datas = datas;
addView();
} /**
* 将标题控件动态添加radiobuttonGroup控件中去
*/
@SuppressLint("ResourceAsColor")
private void addView() {
for (int i = 0; i < datas.size(); i++) {
RadioButton radioButton = new RadioButton(getContext());
radioButton.setText(datas.get(i));
radioButton.setId(10000 + i);
radioButton.setBackgroundResource(R.color.white);
radioButton.setButtonDrawable(null);
radioButton.setButtonDrawable(android.R.color.transparent);
radioButton.setTextColor(R.color.black);
LayoutParams layoutParams = new LayoutParams(Dp2Px(getContext(),
100), LayoutParams.WRAP_CONTENT);
radioButton.setLayoutParams(layoutParams);
radioButton.setGravity(Gravity.CENTER);
int px = Dp2Px(getContext(), 8);
radioButton.setPadding(px, px, px, px); rg.addView(radioButton);
} }

  这里,大伙应当对两个方面的问题引起重视:

  ①我们所说哪些头部标题并不是固定的,是动态生成的,因此显示每个标题radiobutton控件,是随着标题的条目动态添加到父控件中去的。

  ②前文说道,由于每个radiobutton样式非常丑陋,不符合我们的需求,因此我们需要通过相应的代码控制它的样式。

  由于这个控件归根结底是对viewpager翻页效果进行一个指示,怎么能够少得了viewpager对象,相应的源代码如下:

    /**
* 设置viewpager对象
*/
public void setPager(ViewPager pager) {
this.pager = pager;
setPagerListener();
} /**
* 对viewpager翻页进行监听
*/
private void setPagerListener() {
pager.setOnPageChangeListener(this);
}

  其主要就是对于viewpager的翻页效果进行监听。

  怎么使翻页翻到那一页的时候,指示条也随之移动,让我妈老瞅瞅他的代码:

    /**
* 页面滚动 指示条随之移动 当条目滚出屏幕的时候,水平滚动条也随之移动
*/
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
int sum = 0;
sum = (int) ((position+positionOffset)*rg.getChildAt(position).getWidth()); int green = (pager.getWidth() - rg.getChildAt(position).getWidth()) / 2;
dx = sum - green;// 计算出要滑出去的距离
hsv.scrollTo(dx, 0);
tempx=hsv.getScrollX();
indicatorScroll(position, positionOffset); } /**
* 指示条随着页面的移动也跟随着滑动
*/
int tempx=0;
private void indicatorScroll(int position, float positionOffset) {
RadioButton button = (RadioButton) rg.getChildAt(position);
int[] location = new int[2];
button.getLocationInWindow(location);
// 开始做位移滑动
TranslateAnimation animation = new TranslateAnimation(fromX,
location[0] + positionOffset * button.getWidth()+tempx, 0, 0);
animation.setDuration(50);// 动画持续事件
animation.setFillAfter(true);
fromX = (int) (location[0] + button.getWidth() * positionOffset+tempx);
indicator.startAnimation(animation);// 线开始动画
}

  对于这个逻辑,大家应当注意两点:

  第一点,单条目滚出屏幕的时候,我们应当相应条目将其滚动到屏幕中央位置,那么他所需滚动位置,如图所示:

 

 第二点,关于指示条计算就是移动到相应条目位置-水平滚动条滚动的位置。

  这样一个自定义的viewpagerindicator就大功告成了,效果如下:

  

  源代码地址为:

  http://pan.baidu.com/s/1eQ9Fi62

老猪带你玩转android自定义控件一——打造最简单viewpagerindicator的更多相关文章

  1. 老猪带你玩转android自定义控件二——自定义索引栏listview

    带索引栏的listview,在android开发非常普遍,方便用户进行字母索引,就像微信通讯录这样: 今天,我们就从零到一实现这个具有索引栏的listview. 怎么实现这个控件了,我们应当梳理出一个 ...

  2. 老猪带你玩转自定义控件三——sai大神带我实现ios 8 时间滚轮控件

    ios 8 的时间滚轮控件实现了扁平化,带来很好用户体验,android没有现成控件,小弟不才,数学与算法知识不过关,顾十分苦恼,幸好在github上找到sai大神实现代码,甚为欣喜,顾把学习这个控件 ...

  3. 老司机带你玩转面试(2):Redis 过期策略以及缓存雪崩、击穿、穿透

    前文回顾 建议前一篇文章没看过的同学先看下前面的文章: 「老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化」 过期策略 Redis 的过期策略都有哪些? 在聊这个问题之前,一定 ...

  4. 老司机带你玩转面试(3):Redis 高可用之主从模式

    前文回顾 建议前面文章没看过的同学先看下前面的文章: 「老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化」 「老司机带你玩转面试(2):Redis 过期策略以及缓存雪崩.击穿. ...

  5. 老司机带你玩转面试(4):Redis 高可用之哨兵模式

    前文回顾 建议前面文章没看过的同学先看下前面的文章: 「老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化」 「老司机带你玩转面试(2):Redis 过期策略以及缓存雪崩.击穿. ...

  6. 老司机带你玩转面试(5):Redis 集群模式 Redis Cluster

    前文回顾 建议前面文章没看过的同学先看下前面的文章: 「老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化」 「老司机带你玩转面试(2):Redis 过期策略以及缓存雪崩.击穿. ...

  7. 玩转android自定义控件二——自定义索引栏listview

    带索引栏的listview,在android开发非常普遍,方便用户进行字母索引,就像微信通讯录这样: 今天,我们就从零到一实现这个具有索引栏的listview. 怎么实现这个控件了,我们应当梳理出一个 ...

  8. 老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化

    引言 今天周末,我在家坐着掐指一算,马上又要到一年一度的金九银十招聘季了,国内今年上半年受到 YQ 冲击,金三银四泡汤了,这就直接导致很多今年毕业的同学会和明年毕业的同学一起参加今年下半年的秋招,这个 ...

  9. 编程老司机带你玩转 CompletableFuture 异步编程

    本文从实例出发,介绍 CompletableFuture 基本用法.不过讲的再多,不如亲自上手练习一下.所以建议各位小伙伴看完,上机练习一把,快速掌握 CompletableFuture. 个人博文地 ...

随机推荐

  1. html5 图片上传,支持图片预览、压缩、及进度显示,兼容IE6+及标准浏览器

    以前写过上传组件,见 打造 html5 文件上传组件,实现进度显示及拖拽上传,兼容IE6+及其它标准浏览器,对付一般的上传没有问题,不过如果是上传图片,且需要预览的话,就力有不逮了,趁着闲暇时间,给上 ...

  2. 您还有心跳吗?超时机制分析(java)

    注:本人是原作者,首发于并发编程网(您还有心跳吗?超时机制分析),此文结合那里的留言作了一些修改. 问题描述 在C/S模式中,有时我们会长时间保持一个连接,以避免频繁地建立连接,但同时,一般会有一个超 ...

  3. Nginx配置proxy_pass

    nginx配置proxy_pass,需要注意转发的路径配置 1.location /test/ { proxy_pass http://t6:8300; } 2.location /test/ { p ...

  4. AngularJs自定义指令详解(8) - priority

    priority 默认值为0. 当一个元素上声明两个指令,而且它们的priority一样,谁先被调用?这个需要分情况讲.下面先给个例子: <!DOCTYPE html> <html& ...

  5. NopCommerce 框架系列(一)

    今天,终于抽出时间来写写博文,也希望自己能养成写博文的好习惯,大神勿喷. 我从NopCommerce官网上下载了源码,以便自己学习研究,如有需要下载源码的朋友,请点击链接: http://www.no ...

  6. 使用winpcap多线程抓包,以及简单的分析数据包

    刚开始使用winpcap数据包的时候,我在抓包的时候使用了 pcap_loop(adhandle, 0, packet_handler, NULL); 这个回调函数进行抓包.同时在回调函数中分析IP地 ...

  7. Freemarket学习整理。

    导入freemarker.jar包 把word文档另存为xml格式,2007以上版本支持. 编写代码,把路径更改为xml所在路径. 把需要更改的地方写成${}形式. package Document. ...

  8. TaskScheduler的启动

    <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...

  9. flex中下拉框的实现

    flex中下拉框的实现 <mx:ComboBox id = "combobox" dataProvider = "{deviceCodeType }" e ...

  10. linux系统编程之错误处理机制

    在讲解liunx错误处理机制之前我们先来看一段代码: #include<sys/types.h> #include<sys/stat.h> #include<fcntl. ...