一:概述
这个教程好像网上挺多的。viewpager用fragment作为每一个item,但是似乎好像indicator似乎都有点不太满意,下面的滑动条是直接跳动的,不是类似动画滑动的。现在就说说这个滑动条。然后再说说其他的东西(比如activity传数据给fragment啊。这些)。
先看看效果图:
二:源码解读
我们先讲讲viewpager吧。
private MyPageAdapter adapter ;
private ViewPager pager;
private List<MyFragment> listFragment ;
...
//这里初始化10个fragment
for(int i = 0 ;i < 10 ;i ++){
Bundle bundle = new Bundle();
bundle.putString("who","hello world "+ i);
MyFragment fragment = MyFragment.getInstance(bundle);
listFragment.add(fragment);
}
通过activity传数据给fragment,直接调用fr.setArguments(bundle);就可以了。bundle就是数据集,然后在fragment中直接调用Bundle bundle = getArguments();就可以获得了。
public static MyFragment getInstance(Bundle bundle){
MyFragment fr = new MyFragment();
fr.setArguments(bundle);
return fr;
}
然后为viewpager添加适配器。注意其中的getPageTitle()方法,这个方法的返回值就可以作为indicator中的值。
class MyPageAdapter extends FragmentPagerAdapter {
public MyPageAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return listFragment.get(position);
}
@Override
public long getItemId(int position) {
return super.getItemId(position);
}
@Override
public int getCount() {
return listFragment.size();
}
@Override
public CharSequence getPageTitle(int position) {
return "title - "+position;
}
}
然后看看TestIndicatorActivity这个activity实现了MyIndicator.CustomeTextViewInter 接口
主要实现了两个方法,用来让开发者可以直接在代码中自定义indicator,被选择或者没有被选择的效果
selected():被选择的效果。
unSelect():没有被选择的效果。
两个都只是简单的修改了下背景颜色而已。(你们可以实现自己想实现的,比如加粗等等。)
@Override
public TextView selected(TextView tv) {
tv.setBackgroundColor(Color.parseColor("#8FFF58"));
return tv;
}
@Override
public TextView unSelect(TextView tv) {
tv.setBackgroundColor(Color.parseColor("#FFFFFF"));
return tv;
}
我们只需要写下两句代码就可以使用indicator。
//自定义被选择,没被选择的接口
indicator.setCustomeTextViewInter(this);
...
indicator.setViewPager(pager);
好了我们详细说说indicator的实现方法。
我写了个attr文件(好像叫自定义属性文件)
<resources>
<declare-styleable name="MyIndicator">
<attr name="lineHeight" format="dimension"/>
<attr name="lineColor" format="color"/>
</declare-styleable>
</resources>
然后在布局文件中调用如下就可以
记得在Android studio 布局文件的根元素中加入
xmlns:custom=”http://schemas.android.com/apk/res-auto”
<com.example.tongmin.testproject.MyIndicator
android:layout_width="match_parent"
android:layout_height="50dp"
custom:lineColor="#5680FF"
custom:lineHeight="5dp"
android:id="@+id/myindicator"
/>
使用下面的代码就可以读取其中的数据了。
TypedArray a = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.MyIndicator, defStyleAttr, 0);
lineHeight = a.getDimension(R.styleable.MyIndicator_lineHeight, 10);
lineColor = a.getColor(R.styleable.MyIndicator_lineColor, Color.BLACK);
a.recycle();
然后整个indicator时继承自 HorizontalScrollView ,因为都是横着滑动的。
然后看看下面代码。注释也看看
//首先将自带的横向的滑动条禁用掉。
this.setHorizontalScrollBarEnabled(false);
//因为scrollview只能有一个子元素,就添加个LinearLayout
linearContain = new LinearLayout(context);
linearContain.setOrientation(LinearLayout.HORIZONTAL);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
this.addView(linearContain, params);
然后看看setViewPager()这个方法,将viewpager设置进来后做了一些初始化工作。
setheader就是将有多少个fragment,然后就初始化多少个indicator的item。
public void setViewPager(ViewPager viewPager) {
this.viewPager = viewPager;
this.viewPager.addOnPageChangeListener(this);
setHeader();
changeTextView(0);
}
这个就是添加一个个item的具体的方法。其中添加的其实是textview。
因为我们放出了一个接口,让用户自己实现选择,没被选择的textview。所以需要做一下判断。
private void addTextView(String text , int position) {
//判断用户是否自定义了textview
TextView textView = new Text(context,position);
if (customeTextViewInter != null) {
textView = customeTextViewInter.unSelect(textView);
if (textView == null) {
throw new RuntimeException("customeTextViewInter textview == null");
}
}
textView.setText(text);
linearContain.addView(textView);
}
其中Text是自定义的TextView,其中的position属性是记录我这个textView是第几个item,因为要添加点击事件,点击跳转到对应fragment。
然后注意还写了params.bottomMargin = (int) lineHeight;
这个是为了下面的滑动条腾出空间来。不然把他遮挡了。
private class Text extends TextView {
int position;
public Text(Context context , int position) {
super(context);
this.position = position;
this.init(context);
}
private void init(Context context) {
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
this.setLayoutParams(params);
int size = dip2px(context, 10);
params.bottomMargin = (int) lineHeight;
this.setPadding(size, 0, size, 0);
this.setGravity(Gravity.CENTER);
this.setTextSize(20);
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
viewPager.setCurrentItem(position);
}
});
}
}
好了。现在我们手动为这个viewpager设置一个ViewPager.OnPageChangeListener
监听viewpager的滑动。
这个接口需要实现三个方法。
1.onPageScrolled()是在滑动的时候回调的一个接口。
a).position是在滑动的时候所在的position(比如我在第一个fragment滑动到第二个fragment的时候这个position就是0,从0开始的啊。)
b).positionOffset就是当前的滑动在整个viewpager的比例。
c).positionOffsetPixels。就是这个fragment滑动的总像素数。
2.onPageSelected()是在滑动到被选择的fragment回调的。
a).position就是被选择的position。(从第一个滑向第二个,这个position就指向第二个)。
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
changeLinePosition(position, positionOffset);
}
@Override
public void onPageSelected(int position) {
changeTextView(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
我们触发滑动条滑动时间就是在onPageScrolled()中,然后修改textview的样式的时在onPageSelected();
我们看看changeLinePosition();方法
首先找到对应的textview然后算出他的宽度。
currentPosition 就是当前滑动条所在的 X 坐标。
this.scrollTo((int) currentPosition - scroll, 0);
是让当前HorizontalScrollView滑动到指定位置。不然会出现像滑动条已经到最右边了。但是scrollview并没有滚动过去。就看不见了。
看下图:
private void changeLinePosition(int position, float offset) {
int left = linearContain.getChildAt(position).getLeft();
int right = linearContain.getChildAt(position).getRight();
lineWidth = right - left;
offset = lineWidth * offset;
currentPosition = left + offset;
this.scrollTo((int) currentPosition - scroll, 0);
invalidate();
}
最后在ondraw()方法中,画出下面的滑动条就可以了。找到自己的坐标就可以了。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int textHeight = linearContain.getHeight();
canvas.drawRect(currentPosition, textHeight - lineHeight,
currentPosition + lineWidth, textHeight, linePaint);
}
—————————————————-2.14(修改)情人节这天回来改代码——————————
做了一个小修改,当顶部的标签头部不足以撑满屏幕宽度的时候,那么几个标签就平分屏幕的宽度。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
LinearLayout contain = (LinearLayout) getChildAt(0);
int childCount = contain.getChildCount();
if (contain.getMeasuredWidth() < getMeasuredWidth()) {
//宽度不够,让子控件平分宽度
contain.setMinimumWidth(getMeasuredWidth());
int childWidth = getMeasuredWidth() / childCount;
for (int i = 0; i < childCount; ++i) {
View view = contain.getChildAt(i);
view.setMinimumWidth(childWidth);
}
} else {
super.onLayout(changed, l, t, r, b);
}
}
就做了一个简单的判断 contain.getMeasuredWidth() < getMeasuredWidth()。
当想修改一个控件的尺寸的时候可以使用两个方法。
1.setLayoutParams().
但是这个方法会调用 requestLayout() ,如果我们在onLayout中调用的时候会
requestLayout() improperly called by…等。
2.直接调用控件的.setMinimumWidth(),setMinimumHight() 等。
加个好友共同学习(不是公众号):
因为小弟水平有限,如果有写的有问题,希望指出。