欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

AndroidUI组件SlidingTabLayout实现ViewPager页滑动效果

程序员文章站 2024-03-04 19:21:36
使用slidingtablayout需要准备2个类,分别是 slidingtablayout,与slidingtabstrip,,放进项目中时只用修改下包名即可。...

使用slidingtablayout需要准备2个类,分别是 slidingtablayout,与slidingtabstrip,,放进项目中时只用修改下包名即可。

AndroidUI组件SlidingTabLayout实现ViewPager页滑动效果

效果制作的不是很好。
这篇文章,也是在网上搜了很多资源参考,对 slidingtablayout.java和slidingtabstrip.java进行了修改。大家可以更改他的格式字体大小、选中状态,分割线调整等等。先上传这两个文件,改动支出都做了注释。
slidingtablayout.java

/*
 * copyright (c) 2013 the android open source project
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *  http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */

package com.example.my.slidingtablayout;

import android.content.context;
import android.content.res.typedarray;
import android.graphics.color;
import android.graphics.typeface;
import android.os.build;
import android.support.v4.view.pageradapter;
import android.support.v4.view.viewpager;
import android.util.attributeset;
import android.util.typedvalue;
import android.view.gravity;
import android.view.layoutinflater;
import android.view.view;
import android.widget.horizontalscrollview;
import android.widget.linearlayout;
import android.widget.textview;

/**
 * to be used with viewpager to provide a tab indicator component which give constant feedback as to
 * the user's scroll progress.
 * <p>
 * to use the component, simply add it to your view hierarchy. then in your
 * {@link android.app.activity} or {@link android.support.v4.app.fragment} call
 * {@link #setviewpager(viewpager)} providing it the viewpager this layout is being used for.
 * <p>
 * the colors can be customized in two ways. the first and simplest is to provide an array of colors
 * via {@link #setselectedindicatorcolors(int...)} and {@link #setdividercolors(int...)}. the
 * alternative is via the {@link tabcolorizer} interface which provides you complete control over
 * which color is used for any individual position.
 * <p>
 * the views used as tabs can be customized by calling {@link #setcustomtabview(int, int)},
 * providing the layout id of your custom layout.
 */
public class slidingtablayout extends horizontalscrollview {

 /**
  * allows complete control over the colors drawn in the tab layout. set with
  * {@link #setcustomtabcolorizer(tabcolorizer)}.
  */
 public interface tabcolorizer {

  /**
   * @return return the color of the indicator used when {@code position} is selected.
   */
  int getindicatorcolor(int position);

  /**
   * @return return the color of the divider drawn to the right of {@code position}.
   */
  int getdividercolor(int position);

 }

 private static final int title_offset_dips = 24;
 private static final int tab_view_padding_dips = 16; //内边距
 private static int tab_view_text_size_sp = 16; //字体大小

 private int mtitleoffset;

 private int mtabviewlayoutid;
 private int mtabviewtextviewid;

 // 定义两种需要添加的选项卡颜色
 private int mdefaulttextcolor;
 private int mselectedtextcolor;

 private viewpager mviewpager;
 private viewpager.onpagechangelistener mviewpagerpagechangelistener;

 private final slidingtabstrip mtabstrip;

 public slidingtablayout(context context) {
  this(context, null);
 }

 public slidingtablayout(context context, attributeset attrs) {
  this(context, attrs, 0);
 }

 public slidingtablayout(context context, attributeset attrs, int defstyle) {
  super(context, attrs, defstyle);

  // 获取选项卡颜色,如果未定义的话,则使用主题默认的颜色
  typedarray a = context.obtainstyledattributes(attrs,
    r.styleable.slidingtablayout);
  int defaulttextcolor = a.getcolor(
    r.styleable.slidingtablayout_android_textcolorprimary, 0);
  mdefaulttextcolor = a.getcolor(
    r.styleable.slidingtablayout_textcolortabdefault, defaulttextcolor);
  mselectedtextcolor = a.getcolor(
    r.styleable.slidingtablayout_textcolortabselected ,defaulttextcolor);
  a.recycle();


  // disable the scroll bar
  sethorizontalscrollbarenabled(false);
  // make sure that the tab strips fills this view
  setfillviewport(true);

  mtitleoffset = (int) (title_offset_dips * getresources().getdisplaymetrics().density);

  mtabstrip = new slidingtabstrip(context);
  addview(mtabstrip, layoutparams.match_parent, layoutparams.wrap_content);
 }

 // 在每次选项改变时更新选项卡文本颜色的新方法
 private void updateselectedtitle(int position) {
  final pageradapter adapter = mviewpager.getadapter();
  for (int i = 0; i < adapter.getcount(); i++) {
   final view tabview = mtabstrip.getchildat(i);
   if (textview.class.isinstance(tabview)) {
    textview titleview = (textview)tabview;
    boolean isselected = i == position;
    titleview.settextcolor(isselected ? mselectedtextcolor : mdefaulttextcolor);
   }
  }
 }
 /**
  * set the custom {@link tabcolorizer} to be used.
  * <p>
  * if you only require simple custmisation then you can use
  * {@link #setselectedindicatorcolors(int...)} and {@link #setdividercolors(int...)} to achieve
  * similar effects.
  */
 public void setcustomtabcolorizer(tabcolorizer tabcolorizer) {
  mtabstrip.setcustomtabcolorizer(tabcolorizer);
 }


 /**
  * sets the colors to be used for indicating the selected tab. these colors are treated as a
  * circular array. providing one color will mean that all tabs are indicated with the same color.
  */
 public void setselectedindicatorcolors(int... colors) {
  mtabstrip.setselectedindicatorcolors(colors);
 }

 /**
  * sets the colors to be used for tab dividers. these colors are treated as a circular array.
  * providing one color will mean that all tabs are indicated with the same color.
  */
 public void setdividercolors(int... colors) {
  mtabstrip.setdividercolors(colors);
 }

 //...设置字体大小
 public void settitlesize(int size) {
  this.tab_view_text_size_sp = size;
 }

 /**
  * set the {@link viewpager.onpagechangelistener}. when using {@link slidingtablayout} you are
  * required to set any {@link viewpager.onpagechangelistener} through this method. this is so
  * that the layout can update it's scroll position correctly.
  *
  * @see viewpager#setonpagechangelistener(viewpager.onpagechangelistener)
  */
 public void setonpagechangelistener(viewpager.onpagechangelistener listener) {
  mviewpagerpagechangelistener = listener;
 }

 /**
  * set the custom layout to be inflated for the tab views.
  *
  * @param layoutresid layout id to be inflated
  * @param textviewid id of the {@link textview} in the inflated view
  */
 public void setcustomtabview(int layoutresid, int textviewid) {
  mtabviewlayoutid = layoutresid;
  mtabviewtextviewid = textviewid;
 }

 /**
  * sets the associated view pager. note that the assumption here is that the pager content
  * (number of tabs and tab titles) does not change after this call has been made.
  */
 public void setviewpager(viewpager viewpager) {
  mtabstrip.removeallviews();

  mviewpager = viewpager;
  if (viewpager != null) {
   viewpager.setonpagechangelistener(new internalviewpagerlistener());
   populatetabstrip();
  }
 }

 /**
  * create a default view to be used for tabs. this is called if a custom tab view is not set via
  * {@link #setcustomtabview(int, int)}.
  */
 protected textview createdefaulttabview(context context) {
  textview textview = new textview(context);
  textview.setgravity(gravity.center);
  textview.settextsize(typedvalue.complex_unit_sp, tab_view_text_size_sp);
  textview.settypeface(typeface.default_bold);

  //...这会移除 holo 的默认背景强调以及选项卡的粗体文本
  if (build.version.sdk_int >= build.version_codes.honeycomb) {
   // if we're running on honeycomb or newer, then we can use the theme's
   // selectableitembackground to ensure that the view has a pressed state
   typedvalue outvalue = new typedvalue();
   getcontext().gettheme().resolveattribute(android.r.attr.selectableitembackground,
     outvalue, true);
   textview.setbackgroundresource(outvalue.resourceid);
  }

  if (build.version.sdk_int >= build.version_codes.ice_cream_sandwich) {
   // if we're running on ics or newer, enable all-caps to match the action bar tab style
   textview.setallcaps(true);
  }

  int padding = (int) (tab_view_padding_dips * getresources().getdisplaymetrics().density);
  textview.setpadding(padding, padding, padding, padding);

  return textview;
 }

 private void populatetabstrip() {
  final pageradapter adapter = mviewpager.getadapter();
  final onclicklistener tabclicklistener = new tabclicklistener();

  for (int i = 0; i < adapter.getcount(); i++) {
   view tabview = null;
   textview tabtitleview = null;

   if (mtabviewlayoutid != 0) {
    // if there is a custom tab view layout id set, try and inflate it
    tabview = layoutinflater.from(getcontext()).inflate(mtabviewlayoutid, mtabstrip,
      false);
    tabtitleview = (textview) tabview.findviewbyid(mtabviewtextviewid);
   }

   if (tabview == null) {
    tabview = createdefaulttabview(getcontext());
   }

   if (tabtitleview == null && textview.class.isinstance(tabview)) {
    tabtitleview = (textview) tabview;
   }

   tabtitleview.settext(adapter.getpagetitle(i));
   //... 设置头部均分
   linearlayout.layoutparams layoutparams = new linearlayout.layoutparams(0, layoutparams.wrap_content, 1.0f);
   tabview.setlayoutparams(layoutparams);
   tabview.setonclicklistener(tabclicklistener);

   mtabstrip.addview(tabview);
  }
 }

 @override
 protected void onattachedtowindow() {
  super.onattachedtowindow();

  if (mviewpager != null) {
   scrolltotab(mviewpager.getcurrentitem(), 0);
  }
 }

 private void scrolltotab(int tabindex, int positionoffset) {
  final int tabstripchildcount = mtabstrip.getchildcount();
  if (tabstripchildcount == 0 || tabindex < 0 || tabindex >= tabstripchildcount) {
   return;
  }

  view selectedchild = mtabstrip.getchildat(tabindex);
  if (selectedchild != null) {

   // 调用在每次选项改变时更新文本颜色的新方案
   updateselectedtitle(tabindex);

   int targetscrollx = selectedchild.getleft() + positionoffset;

   if (tabindex > 0 || positionoffset > 0) {
    // if we're not at the first child and are mid-scroll, make sure we obey the offset
    targetscrollx -= mtitleoffset;
   }

   scrollto(targetscrollx, 0);
  }
 }

 private class internalviewpagerlistener implements viewpager.onpagechangelistener {
  private int mscrollstate;

  @override
  public void onpagescrolled(int position, float positionoffset, int positionoffsetpixels) {
   int tabstripchildcount = mtabstrip.getchildcount();
   if ((tabstripchildcount == 0) || (position < 0) || (position >= tabstripchildcount)) {
    return;
   }

   mtabstrip.onviewpagerpagechanged(position, positionoffset);

   view selectedtitle = mtabstrip.getchildat(position);
   int extraoffset = (selectedtitle != null)
     ? (int) (positionoffset * selectedtitle.getwidth())
     : 0;
   scrolltotab(position, extraoffset);

   if (mviewpagerpagechangelistener != null) {
    mviewpagerpagechangelistener.onpagescrolled(position, positionoffset,
      positionoffsetpixels);
   }
  }

  @override
  public void onpagescrollstatechanged(int state) {
   mscrollstate = state;

   if (mviewpagerpagechangelistener != null) {
    mviewpagerpagechangelistener.onpagescrollstatechanged(state);
   }
  }

  @override
  public void onpageselected(int position) {
   if (mscrollstate == viewpager.scroll_state_idle) {
    mtabstrip.onviewpagerpagechanged(position, 0f);
    scrolltotab(position, 0);
   }

   if (mviewpagerpagechangelistener != null) {
    mviewpagerpagechangelistener.onpageselected(position);
   }
  }

 }

 private class tabclicklistener implements onclicklistener {
  @override
  public void onclick(view v) {
   for (int i = 0; i < mtabstrip.getchildcount(); i++) {
    if (v == mtabstrip.getchildat(i)) {
     mviewpager.setcurrentitem(i);
     return;
    }
   }
  }
 }

}

slidingtabstrip.java

/*
 * copyright (c) 2013 the android open source project
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *  http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */

package com.example.my.slidingtablayout;

import android.r;
import android.content.context;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.paint;
import android.util.attributeset;
import android.util.typedvalue;
import android.view.view;
import android.widget.linearlayout;

class slidingtabstrip extends linearlayout {

 private static final int default_bottom_border_thickness_dips = 0; //去除阴影
 private static final byte default_bottom_border_color_alpha = 0x26;
 private static final int selected_indicator_thickness_dips = 4;  //设置滚动条的高度
 private static final int default_selected_indicator_color = 0xff33b5e5;


 private static final int default_divider_thickness_dips = 1;
 private static final byte default_divider_color_alpha = 0x20;
 private static final float default_divider_height = 0.5f;

 private final int mbottomborderthickness;
 private final paint mbottomborderpaint;

 private final int mselectedindicatorthickness;
 private final paint mselectedindicatorpaint;

 private final int mdefaultbottombordercolor;

 private final paint mdividerpaint;
 private final float mdividerheight;

 private int mselectedposition;
 private float mselectionoffset;

 private slidingtablayout.tabcolorizer mcustomtabcolorizer;
 private final simpletabcolorizer mdefaulttabcolorizer;

 slidingtabstrip(context context) {
  this(context, null);
 }

 slidingtabstrip(context context, attributeset attrs) {
  super(context, attrs);
  setwillnotdraw(false);

  final float density = getresources().getdisplaymetrics().density;

  typedvalue outvalue = new typedvalue();
  context.gettheme().resolveattribute(r.attr.colorforeground, outvalue, true);
  final int themeforegroundcolor = outvalue.data;

  mdefaultbottombordercolor = setcoloralpha(themeforegroundcolor,
    default_bottom_border_color_alpha);

  mdefaulttabcolorizer = new simpletabcolorizer();
  mdefaulttabcolorizer.setindicatorcolors(default_selected_indicator_color);
  mdefaulttabcolorizer.setdividercolors(setcoloralpha(themeforegroundcolor,
    default_divider_color_alpha));

  mbottomborderthickness = (int) (default_bottom_border_thickness_dips * density);
  mbottomborderpaint = new paint();
  mbottomborderpaint.setcolor(mdefaultbottombordercolor);

  mselectedindicatorthickness = (int) (selected_indicator_thickness_dips * density);
  mselectedindicatorpaint = new paint();

  mdividerheight = default_divider_height;
  mdividerpaint = new paint();
  mdividerpaint.setstrokewidth((int) (default_divider_thickness_dips * density));
 }

 void setcustomtabcolorizer(slidingtablayout.tabcolorizer customtabcolorizer) {
  mcustomtabcolorizer = customtabcolorizer;
  invalidate();
 }

 void setselectedindicatorcolors(int... colors) {
  // make sure that the custom colorizer is removed
  mcustomtabcolorizer = null;
  mdefaulttabcolorizer.setindicatorcolors(colors);
  invalidate();
 }

 void setdividercolors(int... colors) {
  // make sure that the custom colorizer is removed
  mcustomtabcolorizer = null;
  mdefaulttabcolorizer.setdividercolors(colors);
  invalidate();
 }

 void onviewpagerpagechanged(int position, float positionoffset) {
  mselectedposition = position;
  mselectionoffset = positionoffset;
  invalidate();
 }

 @override
 protected void ondraw(canvas canvas) {
  final int height = getheight();
  final int childcount = getchildcount();
  final int dividerheightpx = (int) (math.min(math.max(0f, mdividerheight), 1f) * height);
  final slidingtablayout.tabcolorizer tabcolorizer = mcustomtabcolorizer != null
    ? mcustomtabcolorizer
    : mdefaulttabcolorizer;

  // thick colored underline below the current selection
  if (childcount > 0) {
   view selectedtitle = getchildat(mselectedposition);
   int left = selectedtitle.getleft();
   int right = selectedtitle.getright();
   int color = tabcolorizer.getindicatorcolor(mselectedposition);

   if (mselectionoffset > 0f && mselectedposition < (getchildcount() - 1)) {
    int nextcolor = tabcolorizer.getindicatorcolor(mselectedposition + 1);
    if (color != nextcolor) {
     color = blendcolors(nextcolor, color, mselectionoffset);
    }

    // draw the selection partway between the tabs
    view nexttitle = getchildat(mselectedposition + 1);
    left = (int) (mselectionoffset * nexttitle.getleft() +
      (1.0f - mselectionoffset) * left);
    right = (int) (mselectionoffset * nexttitle.getright() +
      (1.0f - mselectionoffset) * right);
   }

   mselectedindicatorpaint.setcolor(color);

   canvas.drawrect(left, height - mselectedindicatorthickness, right,
     height, mselectedindicatorpaint);
  }

  // thin underline along the entire bottom edge
  canvas.drawrect(0, height - mbottomborderthickness, getwidth(), height, mbottomborderpaint);

  // vertical separators between the titles
  int separatortop = (height - dividerheightpx) / 2;
  for (int i = 0; i < childcount - 1; i++) {
   view child = getchildat(i);
   mdividerpaint.setcolor(tabcolorizer.getdividercolor(i));
   canvas.drawline(child.getright(), separatortop, child.getright(),
     separatortop + dividerheightpx, mdividerpaint);
  }
 }

 /**
  * set the alpha value of the {@code color} to be the given {@code alpha} value.
  */
 private static int setcoloralpha(int color, byte alpha) {
  return color.argb(alpha, color.red(color), color.green(color), color.blue(color));
 }

 /**
  * blend {@code color1} and {@code color2} using the given ratio.
  *
  * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
  *    0.0 will return {@code color2}.
  */
 private static int blendcolors(int color1, int color2, float ratio) {
  final float inverseration = 1f - ratio;
  float r = (color.red(color1) * ratio) + (color.red(color2) * inverseration);
  float g = (color.green(color1) * ratio) + (color.green(color2) * inverseration);
  float b = (color.blue(color1) * ratio) + (color.blue(color2) * inverseration);
  return color.rgb((int) r, (int) g, (int) b);
 }

 private static class simpletabcolorizer implements slidingtablayout.tabcolorizer {
  private int[] mindicatorcolors; //
  private int[] mdividercolors;  //


  @override
  public final int getindicatorcolor(int position) {
   return mindicatorcolors[position % mindicatorcolors.length];
  }

  @override
  public final int getdividercolor(int position) {
   return mdividercolors[position % mdividercolors.length];
  }

  void setindicatorcolors(int... colors) {
   mindicatorcolors = colors;
  }

  void setdividercolors(int... colors) {
   mdividercolors = colors;
  }
 }
}

上边因为使用了自定义的颜色,所以这里要在attrs.xml声明一下,不然找不到:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="slidingtablayout">
  <attr name="android:textcolorprimary" />
  <attr name="textcolortabdefault" format="color" />
  <attr name="textcolortabselected" format="color" />
 </declare-styleable>
</resources>

布局文件也要用到自定义:

<?xml version="1.0" encoding="utf-8"?>
<linearlayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:id="@+id/activity_main"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context="com.example.my.slidingtablayout.mainactivity">
 <!-- 在上方就在上面,在下方就在下面(tab栏) -->
 <com.example.my.slidingtablayout.slidingtablayout
  android:id="@+id/sliding"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  app:textcolortabdefault="#000000"
  app:textcolortabselected="@color/coloraccent"
  />

 <android.support.v4.view.viewpager
  android:id="@+id/view_pager"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  />
</linearlayout>

最后一道就是在你的activity运用这种开源:可以调整之处也做了说明

package com.example.my.slidingtablayout;

import android.graphics.color;
import android.os.bundle;
import android.support.v4.view.pageradapter;
import android.support.v4.view.viewpager;
import android.support.v7.app.appcompatactivity;
import android.view.view;
import android.view.viewgroup;
import android.widget.linearlayout;

import java.util.arraylist;

public class mainactivity extends appcompatactivity {
 //创建 颜色数组 用来做viewpager的背景
 @override
 protected void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);
  viewpager pager = (viewpager) findviewbyid(r.id.view_pager);
  slidingtablayout tab = (slidingtablayout) findviewbyid(r.id.sliding);
  tab.setdividercolors(color.transparent); //设置标题的分割线
  tab.setselectedindicatorcolors(color.rgb(51, 181, 229)); //设置滚动条的颜色
  tab.settitlesize(18); //...设置字体的颜色,默认16

  myadapte adapter = new myadapte();
  pager.setadapter(adapter);
  tab.setviewpager(pager);
 }

 int[] colors = {0xff123456, 0xff654321, 0xff336699};

 class myadapte extends pageradapter {
  //可以考虑把这个数组添加到集合里面
  string[] titles = {"aa", "bb", "cc"};


  arraylist<linearlayout> layouts = new arraylist<linearlayout>();

  myadapte() {

   for (int i = 0; i < 3; i++) {
    linearlayout l = new linearlayout(mainactivity.this);
    l.setbackgroundcolor(colors[i]);
    l.setlayoutparams(new viewgroup.layoutparams(viewgroup.layoutparams.match_parent, viewgroup.layoutparams.match_parent));
    layouts.add(l);
   }

  }

  @override
  public int getcount() {
   return layouts.size();
  }

  @override
  public boolean isviewfromobject(view view, object o) {
   return view == o;
  }

  @override
  public object instantiateitem(viewgroup container, int position) {
   linearlayout l = layouts.get(position);
   container.addview(l);
   return l;
  }

  @override
  public void destroyitem(viewgroup container, int position, object object) {
   container.removeview(layouts.get(position));
  }

  @override
  public charsequence getpagetitle(int position) {
   //...可以返回集合list.get(position);
   return titles[position];
  }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。