美文网首页view
View-ViewPager(写一个指示器TrackIndica

View-ViewPager(写一个指示器TrackIndica

作者: isLJli | 来源:发表于2020-04-14 17:11 被阅读0次

    使用:

    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();
    
      }
    }
    

    相关文章

      网友评论

        本文标题:View-ViewPager(写一个指示器TrackIndica

        本文链接:https://www.haomeiwen.com/subject/ozupuhtx.html