view

View-ViewPager(写一个指示器TrackIndica

2020-04-14  本文已影响0人  isLJli

使用:

xml布局一个指示器View和viewPager

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <com.haiming.myapplication.incodor.TrackIndicatorView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="horizontal"
      android:paddingTop="10dp"
      android:paddingBottom="10dp"
      android:id="@+id/indicator_view"
   />
      <androidx.viewpager.widget.ViewPager
          android:layout_width="match_parent"
          android:layout_height="0dp"
          android:id="@+id/view_page"
          android:layout_weight="1"
          />
</LinearLayout>

在activity中给指示器和viewPager设置adapter:

public class ViewPagerActivity extends AppCompatActivity {

  private String[] items ={"直播视频","推荐段子你","精华","直播","推荐","视频","图片","段子你好的","精华","直播","推荐"};
  private TrackIndicatorView mTrackIndicatorView;
  private ViewPager mViewPager;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_view_page);
      init();
  }

  private void init() {
      mTrackIndicatorView = findViewById(R.id.indicator_view);
      mViewPager = findViewById(R.id.view_page);
      initIndicator();
      initViewPager();
  }

  private void initViewPager() {
      //缓存当前页面的左右各两面
      mViewPager.setOffscreenPageLimit(2);
      mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
          @NonNull
          @Override
          public Fragment getItem(int position) {
              return ItemFragment.newInstance(items[position]);
          }

          @Override
          public int getCount() {
              return items.length;
          }
      });

     mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
         @Override
         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
         }


         @Override
         public void onPageSelected(int position) {

         }

         @Override
         public void onPageScrollStateChanged(int state) {

         }
     });

  }

  private void initIndicator() {
      IndicatorAdapter<String> mDapter = new IndicatorAdapter<String>(Arrays.asList(items)) {
          @Override
          public View getView(int position, ViewGroup parent, List<String> dataList) {
              TextView textView = new TextView(ViewPagerActivity.this);
              textView.setTextColor(Color.BLACK);
              textView.setTextSize(12);
              textView.setText(dataList.get(position));
              return textView;
          }

          @Override
          public int getCount() {
              return items.length;
          }

          @Override
          public void heightLighIndicator(View view) {
              TextView textView = (TextView) view;
              textView.setTextColor(Color.RED);
          }

          @Override
          public void restoreIndicator(View view) {
              TextView textView = (TextView) view;
              textView.setTextColor(Color.BLACK);
          }
      };

       mTrackIndicatorView.setAdapter(mDapter,mViewPager);

  }
    
}

TrackIndicatorView源码

适配器,自定义view,和选择和未选择时view的状态

public abstract class IndicatorAdapter<T> {

  private List<T> mDataList;

  public IndicatorAdapter(List<T> dataList){
      this.mDataList=dataList;
  }

  public abstract View getView(int position, ViewGroup parent,List<T> dataList);

  public abstract int getCount();

  // 高亮当前位置
  public abstract void heightLighIndicator(View view);

  // 重置上一位置
  public abstract void restoreIndicator(View view);

  //底部指示器
  public  View getBottomTrackView(){
      return null;
  }

  public void setDataList(List<T> dataList) {
      mDataList = dataList;
  }


  public List<T> getDataList() {
      return mDataList;
  }
}

作用是拿到adapter和viewpager,这样就能拿到view,并根据指示器和viewPager的变化相互转换。还有就是确定每个itemwidth()的大小。

/**
* viewPager 滑动指示器
*/
public class TrackIndicatorView extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
  public IndicatorAdapter mAdapter;

  private IndicatorGroupView mIndicatorGroup;

  //一屏幕显示多少个
  private int mTabVisibleNums = 0;

  private int mItemWidth;

  //当前切换位置
  private int mCurrentPosition = 0;

  public TrackIndicatorView(Context context) {
      this(context,null);
  }

  public TrackIndicatorView(Context context, AttributeSet attrs) {
      this(context, attrs,0);
  }

  public TrackIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
      mIndicatorGroup = new IndicatorGroupView(context);
      addView(mIndicatorGroup);
      //4.指定item的宽度 自定义属性
      initAttrbute(context,attrs);

  }

  /**
   * 初始化
   * @param context
   * @param attrs
   */
  private void initAttrbute(Context context, AttributeSet attrs) {
      TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TrackIndicatorView);

//可设置一屏幕item最多占的数量
       mTabVisibleNums = array.getInteger(R.styleable.TrackIndicatorView_tabVisibleNums,0);

      array.recycle();
  }


 /**
   * 设置适配器
   * @param adapter
   * @param viewPager
   */
  private ViewPager mViewPager;
  public void setAdapter(IndicatorAdapter adapter, ViewPager viewPager){
     //与viewPager绑定
      this.mViewPager=viewPager;
      setAdapter(adapter);
      if(viewPager == null){
          throw  new NullPointerException("viewPager id is null");
      }
      //设置滑动监听
      mViewPager.addOnPageChangeListener(this);
  }


  public void setAdapter(IndicatorAdapter adapter){
      if(adapter == null){
          throw  new NullPointerException("adapter id is null");
      }

      mAdapter=adapter;

      int itemCount = mAdapter.getCount();

      for(int i=0 ; i<itemCount;i++){
         //通过适配器拿到itemview,并为每个view设置点击事件
          View itemView = mAdapter.getView(i,mIndicatorGroup);
          mIndicatorGroup.addItemView(itemView);
          // 设置点击事件
          if(mViewPager != null){
              switchItemClick(itemView,i);
          }
      }
    //默认第一个为红色
    mAdapter.heightLighIndicator(mIndicatorGroup.getItemAt(mCurrentPosition));
  }

  private void switchItemClick(View itemView, final int position) {
      itemView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View v) {
             //ViewPager划到相应的页数
              mViewPager.setCurrentItem(position);
             //给itemView移动到中间的位置
              SmoothScrollCurrentIndicator(position);
            //移动底部的指示器      
            mIndicatorGroup.scrollBottomIndicator(position);
          }
      });
  }

  private void SmoothScrollCurrentIndicator(int position) {
      //当前移出屏幕的位置
      float totalScroll = (position)*mItemWidth;
      //中间左边的位置
      int offsetScroll = (getWidth()- mItemWidth)/2;
      //减去中间左边的位置
      final int finalScroll = (int) (totalScroll-offsetScroll);
      smoothScrollTo(finalScroll,0);
  }

 //不断的滚动指示器
  private void scrollCurrentIndicator(int position, float positionOffset) {

      //当前移出屏幕的位置
      float totalScroll = (position+positionOffset)*mItemWidth;

      //中间左边的位置
      int offsetScroll = (getWidth()- mItemWidth)/2;

      //减去中间左边的位置
      final int finalScroll = (int) (totalScroll-offsetScroll);

      Log.d("移动位置",""+finalScroll);
      scrollTo(finalScroll,0);
  }

 private boolean isExecuteScroll = false;

 @Override
  public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
      if(isExecuteScroll){
          //滚动的时候,会不断的调用
          scrollCurrentIndicator(position,positionOffset);
          mIndicatorGroup.scrollBottomTrack(position,positionOffset);
      }
  }

  @Override
  public void onPageSelected(int position) {
      //将上一个位置重置
      mAdapter.restoreIndicator(mIndicatorGroup.getItemAt(mCurrentPosition));
      //将当前位置点亮
      mCurrentPosition = position;
      mAdapter.heightLighIndicator(mIndicatorGroup.getItemAt(mCurrentPosition));

  }

  @Override
  public void onPageScrollStateChanged(int state) {
      if(state == 1){
          isExecuteScroll =true;
      }

      if(state == 0){
          isExecuteScroll =false;
      }

  }

 //只有在onLayout()时子布局的大小才确定,会在这里重新分配每个ItemWidth
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
      super.onLayout(changed, l, t, r, b);
            mItemWidth = getItemWidth();
         
           //循环指定item的宽度
          for(int i=0;i<mAdapter.getCount();i++){
              mIndicatorGroup.getItemAt(i).getLayoutParams().width=mItemWidth;
          }
// 传给底部指示器
      mIndicatorGroup.addBottomTrackView(mAdapter.getBottomTrackView(),mItemWidth);
     requestLayout();

  }

  /**
   * 获取item的宽度
   * @return
   */
  private int getItemWidth() {
     
     //拿到全屏的宽度
      int parentWidth = getWidth();
      //指定全屏的数量
      if(mTabVisibleNums != 0){
          //取平均值
          return parentWidth/mTabVisibleNums;
      }

      //没有指定的,我们取最大的
      int itemWidth = 0;
      //获取最宽的
      int maxItemWidth = 0;
      int allWidh = 0;
      for(int i=0 ; i< mAdapter.getCount();i++){
         //拿到最大的宽度
          int currentItemWidth = mIndicatorGroup.getItemAt(i).getMeasuredWidth();
          maxItemWidth = Math.max(currentItemWidth,maxItemWidth);
          allWidh+=currentItemWidth;

      }
       itemWidth = maxItemWidth;
      //最后算一次
      //如果全部加起来的最大宽度 小于 屏幕宽度
      if(itemWidth*mAdapter.getCount() < parentWidth){
          itemWidth = parentWidth/mAdapter.getCount();
      }

      return itemWidth;
  }

}

HorizontalScrollView 只能有一个子view,所以我们通过一个ViewGroup来装载我们的指示器的全部布局。

/**
* 底部的指示器
*/
public class IndicatorGroupView extends FrameLayout {

  //文字指示器条目的容器
  private LinearLayout mIndicatorGroup;
  //底部指示器,通过adapter拿到
  private View mBottomTrackView;
  //底部指示器宽度
  private int mItemWidth;
  //底部指示器的LayoutParams
  private FrameLayout.LayoutParams params;
 
  //底部指示器的Margin初始值
  private int mInitMargin;


  public IndicatorGroupView(@NonNull Context context) {
      this(context,null);
  }

  public IndicatorGroupView(@NonNull Context context, @Nullable AttributeSet attrs) {
      this(context, attrs,0);
  }

  public IndicatorGroupView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
      mIndicatorGroup = new LinearLayout(context);
      addView(mIndicatorGroup);
  }


  /**
   * 添加文字itemVeiw
   * @param itemView
   */
  public void addItemView(View itemView) {
      mIndicatorGroup.addView(itemView);
  }

 //获取文字view
  public View getItemAt(int i) {
      return mIndicatorGroup.getChildAt(i);
  }

  public void addBottomTrackView(View bottomTrackView, int itemWidth) {
      if(bottomTrackView == null){
          return;
      }
      this.mBottomTrackView = bottomTrackView;
      this.mItemWidth = itemWidth;
      addView(mBottomTrackView);
      //让它在底部,一个条目的宽度
      params = (LayoutParams) mBottomTrackView.getLayoutParams();
      params.gravity= Gravity.BOTTOM;
      int trackWidth = params.width;
      if(params.width == ViewGroup.LayoutParams.MATCH_PARENT){
          trackWidth=mItemWidth;
      }

      //设置的宽度过大
      if(params.width>mItemWidth){
          trackWidth = mItemWidth;
      }

      params.width=trackWidth;
      mInitMargin = (mItemWidth-trackWidth)/2;
      params.leftMargin = mInitMargin;
      mBottomTrackView.setLayoutParams(params);
      requestLayout();

  }

  /**
   * 滚动底部的显示器,滑动时
   * @param position
   * @param positionOffset
   */
  public void scrollBottomTrack(int position, float positionOffset) {
      if(mBottomTrackView == null){
          return;
      }

      int leftMargin = (int) ((position+positionOffset)*mItemWidth);
      params.leftMargin=leftMargin+mInitMargin;
      mBottomTrackView.setLayoutParams(params);
  }


  //点击时
  public void scrollBottomIndicator(int position) {

      if(mBottomTrackView == null){
          return;
      }

      int leftMargin = (int) ((position)*mItemWidth)+mInitMargin;
      int current = params.leftMargin;
      int distance = leftMargin-current;

      //带动画
      ValueAnimator valueAnimator = ObjectAnimator.ofFloat(current,leftMargin).setDuration((long) (Math.abs(distance)*0.5f));
      valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
          @Override
          public void onAnimationUpdate(ValueAnimator animation) {
              float currentLeft = (float) animation.getAnimatedValue();
              params.leftMargin= (int) currentLeft;
              mBottomTrackView.setLayoutParams(params);
          }
      });
      valueAnimator.setInterpolator(new DecelerateInterpolator());
      valueAnimator.start();

  }
}
上一篇 下一篇

猜你喜欢

热点阅读