Tab指示符——Indicator

it2026-10-06  2

先说说我们的思路吧。 其实思路也很简单,就是在咱们的导航下面画一个小矩形,不断的改变这个矩形距离左边的位置。

思路就这么简单,有了思路,接下来就是实现了,看代码:

 

[java]  view plain  copy   public class Indicator extends LinearLayout {      private Paint mPaint; // 画指示符的paint            private int mTop; // 指示符的top      private int mLeft; // 指示符的left      private int mWidth; // 指示符的width      private int mHeight = 5; // 指示符的高度,固定了      private int mColor; // 指示符的颜色      private int mChildCount; // 子item的个数,用于计算指示符的宽度            public Indicator(Context context, AttributeSet attrs) {          super(context, attrs);          setBackgroundColor(Color.TRANSPARENT);  // 必须设置背景,否则onDraw不执行                    // 获取自定义属性 指示符的颜色          TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.Indicator, 0, 0);          mColor = ta.getColor(R.styleable.Indicator_color, 0X0000FF);          ta.recycle();                    // 初始化paint          mPaint = new Paint();          mPaint.setColor(mColor);          mPaint.setAntiAlias(true);      }            @Override      protected void onFinishInflate() {          super.onFinishInflate();          mChildCount = getChildCount();  // 获取子item的个数      }            @Override      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {          super.onMeasure(widthMeasureSpec, heightMeasureSpec);          mTop = getMeasuredHeight(); // 测量的高度即指示符的顶部位置          int width = getMeasuredWidth(); // 获取测量的总宽度          int height = mTop + mHeight; // 重新定义一下测量的高度          mWidth = width / mChildCount; // 指示符的宽度为总宽度/item的个数                    setMeasuredDimension(width, height);      }            /**      * 指示符滚动      * @param position 现在的位置      * @param offset  偏移量 0 ~ 1      */      public void scroll(int position, float offset) {          mLeft = (int) ((position + offset) * mWidth);          invalidate();      }            @Override      protected void onDraw(Canvas canvas) {          // 圈出一个矩形          Rect rect = new Rect(mLeft, mTop, mLeft + mWidth, mTop + mHeight);          canvas.drawRect(rect, mPaint); // 绘制该矩形          super.onDraw(canvas);      }  }  

 

代码加上注释60多行,是不是很简单?  下面我们来分析一下代码。

先来看看构造方法。

 

[java]  view plain  copy   public Indicator(Context context, AttributeSet attrs) {      super(context, attrs);      setBackgroundColor(Color.TRANSPARENT);  // 必须设置背景,否则onDraw不执行                // 获取自定义属性 指示符的颜色      TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.Indicator, 0, 0);      mColor = ta.getColor(R.styleable.Indicator_color, 0X0000FF);      ta.recycle();                // 初始化paint      mPaint = new Paint();      mPaint.setColor(mColor);      mPaint.setAntiAlias(true);  }  

第三行需要注意下,我们在自定义的LinearLayout设置了一个背景,而且注释写着必须设置背景,这是为什么呢? 因为ViewGroup默认是不走onDraw方法的,因为ViewGroup是不需要绘制的,需要绘制的是ViewGroup的子item,这里我们设置一下背景颜色,ViewGroup就会走onDraw方法去绘制它自己的背景,那么我们需要onDraw吗? 当然需要,我们要在onDraw中绘制指示符。

 

接下来的三行代码就是获取咱们的自定义属性,也就是指示符的颜色,再继续,初始化了绘制指示符的paint。

 

[java]  view plain  copy   @Override  protected void onFinishInflate() {      super.onFinishInflate();      mChildCount = getChildCount();  // 获取子item的个数  }  

在onFinishLayout方法中,我们做的工作也很简单,就是获取子item的个数,因为我们需要根据LinearLayout的宽度和子item的个数来确定指示符的宽度。

 

 

[java]  view plain  copy   @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {      super.onMeasure(widthMeasureSpec, heightMeasureSpec);      mTop = getMeasuredHeight(); // 测量的高度即指示符的顶部位置      int width = getMeasuredWidth(); // 获取测量的总宽度      int height = mTop + mHeight; // 重新定义一下测量的高度      mWidth = width / mChildCount; // 指示符的宽度为总宽度/item的个数                setMeasuredDimension(width, height);  }  

 

继续走,在onMeasure方法中,首先我们调用了父类的onMeasure,目的就是让他调用默认的代码去测量,接下来我们通过getMeasuredHeight获取测量的高度,该高度对我们有什么用处呢? 我的指示符的top值就是测量的高度。再往下走,获取测量的宽度,并且重新定义了测量的高度,因为我们要把指示符的高度也加上。width/mChildCount上面我们提过,是要计算指示符的宽度,最后我们把调整后的height值保存起来,让它默认去layout吧。

接下来的一个自定义方法,我们先不说,先来看看onDraw方法。

 

[java]  view plain  copy   @Override  protected void onDraw(Canvas canvas) {      // 圈出一个矩形      Rect rect = new Rect(mLeft, mTop, mLeft + mWidth, mTop + mHeight);      canvas.drawRect(rect, mPaint); // 绘制该矩形      super.onDraw(canvas);  }  

在onDraw中我们要做的工作就更容易了,就是找位置把我们的指示符画上,可以看到,指示符我们使用了一个矩形,left的值是我们要在外面不断改变的。

 

最后再看看自定义个那个scroll方法。

 

[java]  view plain  copy   /**  * 指示符滚动 * @param position 现在的位置  * @param offset  偏移量 0 ~ 1  */  public void scroll(int position, float offset) {      mLeft = (int) ((position + offset) * mWidth);      invalidate();  }  

接受两个参数,其实就是对应ViewPager.OnPageChangeListener.onPageScrolled(int position, float positionOffset, int positionOffsetPixels)前两个参数,这里我们尽量把工作都交给了咱们的Indicator,如此一来外面用起来就相当方便了。

 

那么我们都是做了哪些工作呢?1、计算矩形left的值,2、重绘。

看看left的值是如何计算的,position和offset相加再乘指示符的宽度,为什么呢? 想想,position的值是值当前ViewPager显示第几页,也就是当前是第几个tab,offset指的是从当前页偏移了百分之几,也就是说偏移量是一个0~1的值,这样(position + offset) * mWidth的结果也巧好就是我们需要的矩形的left的值。

至此,我们自定义个Indicator就完成了,来使用一下试试吧:

首先在布局文件中:

 

[html]  view plain  copy   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:indicator="http://schemas.android.com/apk/res/org.loader.indicatortest"      xmlns:tools="http://schemas.android.com/tools"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:orientation="vertical"      tools:context=".MainActivity" >        <org.loader.indicatortest.Indicator          android:id="@+id/indicator"          android:layout_width="match_parent"          android:layout_height="wrap_content"          android:paddingBottom="10dip"          android:paddingTop="10dip"          android:weightSum="4"          indicator:color="#FFFF0000" >            <TextView              android:id="@+id/tab_one"              android:layout_width="0dip"              android:layout_height="wrap_content"              android:layout_weight="1"              android:gravity="center_horizontal"              android:text="TAB1" />            <TextView              android:id="@+id/tab_two"              android:layout_width="0dip"              android:layout_height="wrap_content"              android:layout_weight="1"              android:gravity="center_horizontal"              android:text="TAB2" />            <TextView              android:id="@+id/tab_three"              android:layout_width="0dip"              android:layout_height="wrap_content"              android:layout_weight="1"              android:gravity="center_horizontal"              android:text="TAB3" />            <TextView              android:id="@+id/tab_four"              android:layout_width="0dip"              android:layout_height="wrap_content"              android:layout_weight="1"              android:gravity="center_horizontal"              android:text="TAB4" />      </org.loader.indicatortest.Indicator>        <android.support.v4.view.ViewPager          android:id="@+id/container"          android:layout_width="match_parent"          android:layout_height="wrap_content"/>  </LinearLayout>  

首先定义了一系列的“tab”, 接下来就是一个ViewPager,再来看看Activity中。

 

 

[java]  view plain  copy   public class MainActivity extends Activity implements OnClickListener {      private Indicator mIndicator;      private TextView mTabOne;      private TextView mTabTwo;      private TextView mTabThree;      private TextView mTabFour;      private ViewPager mContainer;            private ArrayList<TextView> mViews = new ArrayList<TextView>(4);            @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);                    mIndicator = (Indicator) findViewById(R.id.indicator);          mContainer = (ViewPager) findViewById(R.id.container);                    mTabOne = (TextView) findViewById(R.id.tab_one);          mTabTwo = (TextView) findViewById(R.id.tab_two);          mTabThree = (TextView) findViewById(R.id.tab_three);          mTabFour = (TextView) findViewById(R.id.tab_four);                    mTabOne.setOnClickListener(this);          mTabTwo.setOnClickListener(this);          mTabThree.setOnClickListener(this);          mTabFour.setOnClickListener(this);                    initViews();          mContainer.setAdapter(new PagerAdapter() {              @Override              public boolean isViewFromObject(View arg0, Object arg1) {                  return arg0 == arg1;              }                            @Override              public int getCount() {                  return mViews.size();              }                            @Override              public Object instantiateItem(ViewGroup container, int position) {                  View view = mViews.get(position);                  container.addView(view);                  return view;              }                            @Override              public void destroyItem(ViewGroup container, int position,                      Object object) {                  container.removeView(mViews.get(position));              }          });                    mContainer.setOnPageChangeListener(new OnPageChangeListener() {              @Override              public void onPageSelected(int position) {              }                            @Override              public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {                  mIndicator.scroll(position, positionOffset);              }                            @Override              public void onPageScrollStateChanged(int position) {                                }          });      }            private void initViews() {          for(int i=0;i<4;i++) {              TextView tv = new TextView(this);              tv.setText("hello android" + i);              mViews.add(tv);          }      }        @Override      public void onClick(View v) {          switch (v.getId()) {          case R.id.tab_one:              mContainer.setCurrentItem(0);              break;          case R.id.tab_two:              mContainer.setCurrentItem(1);              break;          case R.id.tab_three:              mContainer.setCurrentItem(2);              break;          case R.id.tab_four:              mContainer.setCurrentItem(3);              break;          }      }  }  

好吧,写的很混乱,挑重点看,其实重点就一行代码,在开始的地方我也说过了,我们用一行代码就可以使用。

 

看onPageScrolled中的一行代码:

 

[java]  view plain  copy   mIndicator.scroll(position, positionOffset);  

ok,就是这一行代码,控制了我们的指示符的滑动。

转载于:https://www.cnblogs.com/hanfeihanfei/p/5461718.html

相关资源:数据结构—成绩单生成器
最新回复(0)