ViewPager+Indicator(如何写indicator)

一:概述

这个教程好像网上挺多的。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() 等。

源码下载

加个好友共同学习(不是公众号):

这里写图片描述

因为小弟水平有限,如果有写的有问题,希望指出。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值