老猪带你玩转android自定义控件一——打造最简单viewpagerindicator
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