Heim  >  Artikel  >  Java  >  Beispiel für die Erstellung eines MIUI-ähnlichen Tab-Wechseleffekts in der Android-App

Beispiel für die Erstellung eines MIUI-ähnlichen Tab-Wechseleffekts in der Android-App

高洛峰
高洛峰Original
2017-02-08 16:40:221513Durchsuche

In diesem Artikel wird hauptsächlich das Teilen von Beispielen zum Erstellen eines MIUI-ähnlichen Tab-Wechseleffekts in der Android-App und zum Implementieren des ViewPagerIndicator mit der Bildlauffunktion nach dem Scrollen mit dem Finger vorgestellt. Freunde in Not können sich auf

1 beziehen , Übersicht
Ha, heute werde ich Ihnen die Produktion eines ViewPagerIndicators vorstellen. Ich glaube, dass die meisten Leute bei der Erstellung von tabIndicator TabPageIndicator verwendet haben und viele bekannte APPs diesen Open-Source-Indikator verwendet haben. Haben Sie jemals darüber nachgedacht, wie Sie einen solchen Indikator selbst implementieren können und wie kompliziert der Code wäre? Heute werde ich Sie dazu bringen, einen solchen Indikator von Grund auf zu implementieren. Natürlich werde ich es nicht genau so machen . Schauen wir uns den Titel an, der mit MIUI zusammenhängt, also werden wir eine Funktion erstellen, die mit TabPageIndicator übereinstimmt, aber genauso aussieht wie MIUIs Tab~~ , es ist relativ einfach, MIUI zu imitieren:

Android App中制作仿MIUI的Tab切换效果的实例分享

Die Anzahl aller Tabs in MIUI bleibt jedoch grundsätzlich bei zwei auf vier, aber wir haben möglicherweise mehr Spalten, vorausgesetzt, wir haben 10. Was tun mit einem Tab? Sie können den Bildschirm nicht in 10 gleiche Teile unterteilen. Selbst mein 5.3-Sehvermögen kann dies nicht akzeptieren. Also müssen wir eine ähnliche Funktion erstellen zu TabPageIndicator, um einige anzuzeigen, und dann kann der Rest mit Tabs versehen werden, um dem ViewPager zu folgen. Die Darstellung ist wie folgt:


Android App中制作仿MIUI的Tab切换效果的实例分享

Rub, Look Wenn Sie eine Weile daran interessiert sind, ist es verlinkt, und die obige Registerkarte unterstützt auch das Klicken ~

Tatsächlich, nachdem Sie dies gelernt haben und mit dem, was unten unterstrichen ist, einverstanden sind, ändern Sie einfach ein paar Codezeilen ~ Egal, ob Sie es glauben oder nicht, ich glaube es jedenfalls.

2. Analyse vor der Umsetzung
Für einen solchen Indikator analysieren wir zunächst, wie er erstellt wird.
Grundsätzlich müssen wir den Inhaltsbereich nicht berücksichtigen, sondern nur ViewPager+FragmentPagerAdapter.
Hauptsächlich der Tab-Bereich oben:
Obwohl es sich um ein benutzerdefiniertes Steuerelement handelt, müssen wir zunächst nur eine Kombinationsmethode verwenden:
Steuerelementauswahl: Ich habe vor, LinearLayout für das äußere Layout zu verwenden. Legen Sie einfach die Richtung horizontal fest. Was den internen Titel betrifft, habe ich mich entschieden, standardmäßig TextView zu verwenden.
Benutzerdefiniertes Attribut: Da unser sichtbares Tab-Attribut vom Benutzer angepasst werden sollte, veröffentlichen wir ein benutzerdefiniertes Attribut für die Außenwelt, das vom Benutzer festgelegt wird. Die Breite jeder Textansicht beträgt ScreenWidth/mVisibleTab.
Dreieckiges TabIndikator-Zeichnen: Unabhängig davon, ob wir ein Dreieck oder einen Unterstreichungsindikator zeichnen, müssen wir es im äußeren Layout des Tabs zeichnen. Dann zeichnen wir während der Initialisierung ein Dreieck und schließlich zeichnen wir im DispatchDraw entsprechend der Position des Dreiecks direkt.
Die Position des Dreiecksindikators: Die Y-Koordinate der Position ist relativ einfach zu berechnen, daher werde ich hier nicht auf Details eingehen. Hauptsächlich die X-Koordinate, da sich die X-Koordinate mit dem ViewPager bewegt. Wie erhalten wir also die Bewegungsentfernung? Es gibt einen PageChangeListener mit einer onPageScrolled-Methode. Diese Methode ruft positionOffset und positionOffsetPixels auf. Wir können dem folgen, um die Position von x zu steuern.
LinearLayout-Verknüpfung: Wenn der aktuelle Tab auf den letzten sichtbaren Tab verschoben wird, lassen wir unser Linearlayout weiterhin scrollXTo basierend auf dem von onPageScrolled bereitgestellten positionOffset ausführen~~
Okay, alles, was analysiert werden muss~~ Es muss Hier sei erklärt, dass benutzerdefinierte Steuerelemente manchmal großartige Ergebnisse erzielen können, indem sie vorhandene Steuerelemente kombinieren. ~~ Da es ein passendes gibt, warum sollte man sich dann die Mühe machen, selbst etwas von Grund auf neu zu erstellen ~~

3. Methode verwenden
Bevor Sie den Code schreiben, ist es besser, zuerst die Verwendungsmethode zu veröffentlichen, damit jeder zuerst ein sensorisches Verständnis haben und dieses Verständnis dann nutzen kann, um den Implementierungsprozess im Code zu erkunden~~

(1) Layoutdatei

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  xmlns:zhy="http://schemas.android.com/apk/res/com.example.demo_zhy_mms_miui" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="#ffffffff" 
  android:orientation="vertical" > 
 
  <com.example.demo_zhy_mms_miui.ViewPagerIndicator 
    android:id="@+id/id_indicator" 
    android:layout_width="match_parent" 
    android:layout_height="45dp" 
    android:background="@drawable/title_bar_bg_one_row" 
    android:orientation="horizontal" 
    zhy:item_count="3" > 
    
  </com.example.demo_zhy_mms_miui.ViewPagerIndicator> 
 
  <android.support.v4.view.ViewPager 
    android:id="@+id/id_vp" 
    android:layout_width="match_parent" 
    android:layout_height="0dp" 
    android:layout_weight="1" > 
  </android.support.v4.view.ViewPager> 
 
</LinearLayout>

Zuerst deklarieren wir in der Layoutdatei, dass einer der ViewPagerIndicator und der andere unser ViewPager ist.

(2) MainActivity

package com.example.demo_zhy_mms_miui; 
 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 
 
import android.os.Bundle; 
import android.support.v4.app.Fragment; 
import android.support.v4.app.FragmentActivity; 
import android.support.v4.app.FragmentPagerAdapter; 
import android.support.v4.view.ViewPager; 
import android.view.Window; 
 
public class MainActivity extends FragmentActivity 
{ 
  private List<Fragment> mTabContents = new ArrayList<Fragment>(); 
  private FragmentPagerAdapter mAdapter; 
  private ViewPager mViewPager; 
// private List<String> mDatas = Arrays.asList("短信1", "短信2", "短信3", "短信4", 
//     "短信5", "短信6", "短信7", "短信8", "短信9"); 
  private List<String> mDatas = Arrays.asList("短信", "收藏", "推荐"); 
 
  private ViewPagerIndicator mIndicator; 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) 
  { 
    super.onCreate(savedInstanceState); 
    requestWindowFeature(Window.FEATURE_NO_TITLE); 
    setContentView(R.layout.vp_indicator); 
 
    initView(); 
    initDatas(); 
    //设置Tab上的标题 
    mIndicator.setTabItemTitles(mDatas); 
    mViewPager.setAdapter(mAdapter); 
    //设置关联的ViewPager 
    mIndicator.setViewPager(mViewPager,0); 
 
  } 
 
  private void initDatas() 
  { 
 
    for (String data : mDatas) 
    { 
      VpSimpleFragment fragment = VpSimpleFragment.newInstance(data); 
      mTabContents.add(fragment); 
    } 
 
    mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) 
    { 
      @Override 
      public int getCount() 
      { 
        return mTabContents.size(); 
      } 
 
      @Override 
      public Fragment getItem(int position) 
      { 
        return mTabContents.get(position); 
      } 
    }; 
  } 
 
  private void initView() 
  { 
    mViewPager = (ViewPager) findViewById(R.id.id_vp); 
    mIndicator = (ViewPagerIndicator) findViewById(R.id.id_indicator); 
  } 
  
}

Bezüglich der Verwendung unseres ViewPagerIndicators gibt es nur zwei Zeilen:


//设置Tab上的标题
mIndicator.setTabItemTitles(mDatas);
//设置关联的ViewPager
mIndicator.setViewPager(mViewPager,0);

其他代码都是初始化ViewPager神马的~~可见,我们的控件写好之后使用起来极其简单~~
好了,大家注意下,布局文件里面有个设置可见Tab个数的属性:zhy:item_count="3"  ;
比如:当item_count=3,而给的TabTitle的Listf7e83be87db5cd2d9a8a0b8117b38cd4的size也是3的话,就是效果图1的效果~~~
           当item_count=4,而给的TabTitle的Listf7e83be87db5cd2d9a8a0b8117b38cd4的size大于4的话,就是效果图2的效果~~~
其实,我们也支持直接在布局中书写我们的Tab,你完全可以不使用mIndicator.setTabItemTitles(mDatas);取而代之,你可以在布局中定义几个TextView,固定好文本,样式什么的~~其实别的控件我们也是支持的~~~
贴一下Fragment代码~

package com.example.demo_zhy_mms_miui; 
 
import android.os.Bundle; 
import android.support.v4.app.Fragment; 
import android.view.Gravity; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.TextView; 
 
public class VpSimpleFragment extends Fragment 
{ 
  public static final String BUNDLE_TITLE = "title"; 
  private String mTitle = "DefaultValue"; 
 
  @Override 
  public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) 
  { 
    Bundle arguments = getArguments(); 
    if (arguments != null) 
    { 
      mTitle = arguments.getString(BUNDLE_TITLE); 
    } 
 
    TextView tv = new TextView(getActivity()); 
    tv.setText(mTitle); 
    tv.setGravity(Gravity.CENTER); 
 
    return tv; 
  } 
 
  public static VpSimpleFragment newInstance(String title) 
  { 
    Bundle bundle = new Bundle(); 
    bundle.putString(BUNDLE_TITLE, title); 
    VpSimpleFragment fragment = new VpSimpleFragment(); 
    fragment.setArguments(bundle); 
    return fragment; 
  } 
}

好了,看完使用方式,有木有一点小激动~~

4、自定义ViewPagerIndicator的实现
(1)自定义属性
其实可抽取为自定义的属性很多哈~这里我们就写了一个,就是tab的数量。你完全可以把指示器颜色,文本颜色神马可定制的属性全搞出来~~
我们的控件名称叫做:ViewPagerIndicator
所以我们在values/attr.xml中这么写:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
 
  <attr name="item_count" format="integer"></attr> 
 
  <declare-styleable name="ViewPagerIndicator"> 
    <attr name="item_count" /> 
  </declare-styleable> 
 
</resources>

定义好了,肯定得用,怎么用?在哪用?就不用说了吧。上面的用法已经贴过布局文件了~~记得自定义属性的命名空间要注意哈~~~
首先看什么,肯定要有哪些成员变量,和构造里面做了些什么~

(2)构造方法及成员变量

public class ViewPagerIndicator extends LinearLayout 
{ 
  /** 
   * 绘制三角形的画笔 
   */ 
  private Paint mPaint; 
  /** 
   * path构成一个三角形 
   */ 
  private Path mPath; 
  /** 
   * 三角形的宽度 
   */ 
  private int mTriangleWidth; 
  /** 
   * 三角形的高度 
   */ 
  private int mTriangleHeight; 
   
  /** 
   * 三角形的宽度为单个Tab的1/6 
   */ 
  private static final float RADIO_TRIANGEL = 1.0f / 6; 
  /** 
   * 三角形的最大宽度 
   */ 
  private final int DIMENSION_TRIANGEL_WIDTH = (int) (getScreenWidth() / 3 * RADIO_TRIANGEL); 
   
   
  /** 
   * 初始时,三角形指示器的偏移量 
   */ 
  private int mInitTranslationX; 
  /** 
   * 手指滑动时的偏移量 
   */ 
  private float mTranslationX; 
 
  /** 
   * 默认的Tab数量 
   */ 
  private static final int COUNT_DEFAULT_TAB = 4; 
  /** 
   * tab数量 
   */ 
  private int mTabVisibleCount = COUNT_DEFAULT_TAB; 
 
  /** 
   * tab上的内容 
   */ 
  private List<String> mTabTitles; 
  /** 
   * 与之绑定的ViewPager 
   */ 
  public ViewPager mViewPager; 
   
  /** 
   * 标题正常时的颜色 
   */ 
  private static final int COLOR_TEXT_NORMAL = 0x77FFFFFF; 
  /** 
   * 标题选中时的颜色 
   */ 
  private static final int COLOR_TEXT_HIGHLIGHTCOLOR = 0xFFFFFFFF; 
 
  public ViewPagerIndicator(Context context) 
  { 
    this(context, null); 
  } 
 
  public ViewPagerIndicator(Context context, AttributeSet attrs) 
  { 
    super(context, attrs); 
 
    // 获得自定义属性,tab的数量 
    TypedArray a = context.obtainStyledAttributes(attrs, 
        R.styleable.ViewPagerIndicator); 
    mTabVisibleCount = a.getInt(R.styleable.ViewPagerIndicator_item_count, 
        COUNT_DEFAULT_TAB); 
    if (mTabVisibleCount < 0) 
      mTabVisibleCount = COUNT_DEFAULT_TAB; 
    a.recycle(); 
 
    // 初始化画笔 
    mPaint = new Paint(); 
    mPaint.setAntiAlias(true); 
    mPaint.setColor(Color.parseColor("#ffffffff")); 
    mPaint.setStyle(Style.FILL); 
    mPaint.setPathEffect(new CornerPathEffect(3)); 
 
  }

看起来成员变量挺多的,其实主要就几类:
最前面的6个都是和绘制那个三角形相关的,画笔决定了三角形的样式(颜色等),Path用于构造这个三角形(其实就是3条线的封闭合),然后就是三角形的宽度什么的。
接下来的两个:都带Translation,肯定是和三角形的位置相关的了~
剩下的就是Tab内容、数量神马的~~
看看我们构造方法里面:获得了自定义属性,即可见的Tab的数量,初始化了我们的画笔,这里设置了setPathEffect,就是为了画的线的连接处,有点圆角~~

(3)onFinishInflate和onSizeChanged
我们的一些初始化工作,会在这两个方法里面做~~尺寸相关的,会在onSizeChanged回调里面进行设置~

/** 
   * 设置布局中view的一些必要属性;如果设置了setTabTitles,布局中view则无效 
   */ 
  @Override 
  protected void onFinishInflate() 
  { 
    Log.e("TAG", "onFinishInflate"); 
    super.onFinishInflate(); 
 
    int cCount = getChildCount(); 
 
    if (cCount == 0) 
      return; 
 
    for (int i = 0; i < cCount; i++) 
    { 
      View view = getChildAt(i); 
      LinearLayout.LayoutParams lp = (LayoutParams) view 
          .getLayoutParams(); 
      lp.weight = 0; 
      lp.width = getScreenWidth() / mTabVisibleCount; 
      view.setLayoutParams(lp); 
    } 
    // 设置点击事件 
    setItemClickEvent(); 
 
  }

这个其实是获取在布局文件中直接写好Tab的~~如果你在这写好了,就不需要去调用mIndicator.setTabItemTitles(mDatas);了~~
大家可以下载文末的代码后,把mIndicator.setTabItemTitles(mDatas);这行代码注释进行测试~~不过注意下定义的Tab和ViewPager的页面数量最好一致。
代码很简单,就是获取ChildView,然后显示的重置一个宽度为getScreenWidth() / mTabVisibleCount;接下来设置一下点击事件。

/** 
   * 设置点击事件 
   */ 
  public void setItemClickEvent() 
  { 
    int cCount = getChildCount(); 
    for (int i = 0; i < cCount; i++) 
    { 
      final int j = i; 
      View view = getChildAt(i); 
      view.setOnClickListener(new OnClickListener() 
      { 
        @Override 
        public void onClick(View v) 
        { 
          mViewPager.setCurrentItem(j); 
        } 
      }); 
    } 
  }

这个就更简单了~~就是mViewPager.setCurrentItem(j);
下面看看onSizeChanged

/** 
   * 初始化三角形的宽度 
   */ 
  @Override 
  protected void onSizeChanged(int w, int h, int oldw, int oldh) 
  { 
    super.onSizeChanged(w, h, oldw, oldh); 
    mTriangleWidth = (int) (w / mTabVisibleCount * RADIO_TRIANGEL);// 1/6 of 
                                    // width 
    mTriangleWidth = Math.min(DIMENSION_TRIANGEL_WIDTH, mTriangleWidth); 
 
    // 初始化三角形 
    initTriangle(); 
 
    // 初始时的偏移量 
    mInitTranslationX = getWidth() / mTabVisibleCount / 2 - mTriangleWidth 
        / 2; 
  } 
   
  /** 
   * 初始化三角形指示器 
   */ 
  private void initTriangle() 
  { 
    mPath = new Path(); 
 
    mTriangleHeight = (int) (mTriangleWidth / 2 / Math.sqrt(2)); 
    mPath.moveTo(0, 0); 
    mPath.lineTo(mTriangleWidth, 0); 
    mPath.lineTo(mTriangleWidth / 2, -mTriangleHeight); 
    mPath.close(); 
  }

onSizeChanged,我们主要是确定三角形的宽度和Path去构造这个三角形。
默认的我们的三角形的底边的宽度为,每个Tab宽度的1/6;当然有个上限是 (int) (getScreenWidth() / 3 * RADIO_TRIANGEL);【RADIO_TRIANGEL = 1.0f / 6】
这个其实无所谓,主要为了屏幕适配,你可以抽取为自定义属性让用户去设置;
initTriangle()中用Path去构造了一个三角形,这个很简单了~~
这里还初始化了mInitTranslationX,因为一开始显示的就在第一个Tab的中间位置。
三角形初始化完成了,是不是应该去看看它在哪进行绘制的~~

(4)dispatchDraw

/** 
 * 绘制指示器 
 */ 
@Override 
protected void dispatchDraw(Canvas canvas) 
{ 
  canvas.save(); 
  // 画笔平移到正确的位置 
  canvas.translate(mInitTranslationX + mTranslationX, getHeight() + 1); 
  canvas.drawPath(mPath, mPaint); 
  canvas.restore(); 
 
  super.dispatchDraw(canvas); 
}

在绘制子View之前,我们先绘制我们的三角形指示器~~
可以看到,我们通过canvas.translate移动画布,来把指示器画到了指定的位置~~当然了,记得save和restore.
看到,我们这里还有个mTranslationX,这个是动态变化的,后面会介绍~~

三角形绘制完成了,应该到了,跟随ViewPager移动了把~~当然了,这里肯定得先绑定ViewPager,不然怎么跟随

(5)setViewPager

// 设置关联的ViewPager 
public void setViewPager(ViewPager mViewPager, int pos) 
{ 
  this.mViewPager = mViewPager; 
 
  mViewPager.setOnPageChangeListener(new OnPageChangeListener() 
  { 
    @Override 
    public void onPageSelected(int position) 
    { 
      // 设置字体颜色高亮 
      resetTextViewColor(); 
      highLightTextView(position); 
 
      // 回调 
      if (onPageChangeListener != null) 
      { 
        onPageChangeListener.onPageSelected(position); 
      } 
    } 
 
    @Override 
    public void onPageScrolled(int position, float positionOffset, 
        int positionOffsetPixels) 
    { 
      // 滚动 
      scroll(position, positionOffset); 
 
      // 回调 
      if (onPageChangeListener != null) 
      { 
        onPageChangeListener.onPageScrolled(position, 
            positionOffset, positionOffsetPixels); 
      } 
 
    } 
 
    @Override 
    public void onPageScrollStateChanged(int state) 
    { 
      // 回调 
      if (onPageChangeListener != null) 
      { 
        onPageChangeListener.onPageScrollStateChanged(state); 
      } 
 
    } 
  }); 
  // 设置当前页 
  mViewPager.setCurrentItem(pos); 
  // 高亮 
  highLightTextView(pos); 
}

很简单的代码,我们关联上ViewPager以后,立刻注册setOnPageChangeListener,关于指示器的跟随移动,核心代码是:onPageScrolled中的

// 滚动
scroll(position, positionOffset);

这行后面介绍~
这里注意下,我们不是把setOnPageChangeListener用了么,但是用户可能也需要监听这个接口,去干一些事,那么我们就需要给用户解决,于是我们自己定义一个类似的接口公布给用户:

/** 
   * 对外的ViewPager的回调接口 
   * 
   * @author zhy 
   * 
   */ 
  public interface PageChangeListener 
  { 
    public void onPageScrolled(int position, float positionOffset, 
        int positionOffsetPixels); 
 
    public void onPageSelected(int position); 
 
    public void onPageScrollStateChanged(int state); 
  } 
 
  // 对外的ViewPager的回调接口 
  private PageChangeListener onPageChangeListener; 
 
  // 对外的ViewPager的回调接口的设置 
  public void setOnPageChangeListener(PageChangeListener pageChangeListener) 
  { 
    this.onPageChangeListener = pageChangeListener; 
  }

如果用户需要回调,请使用我们的mIndicator.setOnPageChangeListener,回调的方法和原本的listener一模一样~~
ps:不要问我,这里用了mViewPager.setOnPageChangeListener我还想监听咋办,以及我设置了mViewPager.setOnPageChangeListener指示器怎么不动了,请仔细看上文
当然了,还有个高亮文本和重置文本颜色的代码,其实就是简单改变下当前选择的Tab的文本的颜色。

/** 
   * 高亮文本 
   * 
   * @param position 
   */ 
  protected void highLightTextView(int position) 
  { 
    View view = getChildAt(position); 
    if (view instanceof TextView) 
    { 
      ((TextView) view).setTextColor(COLOR_TEXT_HIGHLIGHTCOLOR); 
    } 
 
  } 
 
  /** 
   * 重置文本颜色 
   */ 
  private void resetTextViewColor() 
  { 
    for (int i = 0; i < getChildCount(); i++) 
    { 
      View view = getChildAt(i); 
      if (view instanceof TextView) 
      { 
        ((TextView) view).setTextColor(COLOR_TEXT_NORMAL); 
      } 
    } 
  }

接下来就到scroll登场了~

(6)scroll

/** 
   * 指示器跟随手指滚动,以及容器滚动 
   * 
   * @param position 
   * @param offset 
   */ 
  public void scroll(int position, float offset) 
  { 
    /** 
     * <pre class="brush:php;toolbar:false"> 
     * 0-1:position=0 ;1-0:postion=0; 
     * 
*/ // 不断改变偏移量,invalidate mTranslationX = getWidth() / mTabVisibleCount * (position + offset); int tabWidth = getScreenWidth() / mTabVisibleCount; // 容器滚动,当移动到倒数最后一个的时候,开始滚动 if (offset > 0 && position >= (mTabVisibleCount - 2) && getChildCount() > mTabVisibleCount) { if (mTabVisibleCount != 1) { this.scrollTo((position - (mTabVisibleCount - 2)) * tabWidth + (int) (tabWidth * offset), 0); } else // 为count为1时 的特殊处理 { this.scrollTo( position * tabWidth + (int) (tabWidth * offset), 0); } } invalidate(); }

看完之后,有没有一种,卧槽,就这几行代码就实现了,指示器跟随滚动和我们的Tab跟随滚动~~
嗯,其实指示器跟随滚动上面说了,依赖mTranslationX,然后借着canvas.translate实现的~~也就是说,就一行去确定当前应该的偏移即可。
比如:从第0个Tab滑向第1个Tab:position为0,offset会0.0~1.0这么变化~我们的偏移量实际也就是增加 offset * 每个Tab的宽度~
好了,下面说容器滚动,其实容器滚动的x也是 offset * 每个Tab的宽度~;只不过,有个前提就是当前滑动的是可见的倒数第二个到最后一个,所以我们有个判断:
 position >= (mTabVisibleCount - 2) ; 于是乎,我们在偏移的时候也有:(position - (mTabVisibleCount - 2)) * tabWidth ;如当前恰好是可见的倒数第二个到最后一个,
那么position - (mTabVisibleCount - 2)为0,偏移量也就是(tabWidth * offset)~~
当可见为0的时候,我们需要特殊处理下,也就是我们的else~
最后记得invalidate~~
好了,到此核心的方法介绍完了~~剩下些杂七杂八的~~

(7)剩余的方法

/** 
   * 设置可见的tab的数量 
   * 
   * @param count 
   */ 
  public void setVisibleTabCount(int count) 
  { 
    this.mTabVisibleCount = count; 
  } 
 
  /** 
   * 设置tab的标题内容 可选,可以自己在布局文件中写死 
   * 
   * @param datas 
   */ 
  public void setTabItemTitles(List<String> datas) 
  { 
    // 如果传入的list有值,则移除布局文件中设置的view 
    if (datas != null && datas.size() > 0) 
    { 
      this.removeAllViews(); 
      this.mTabTitles = datas; 
 
      for (String title : mTabTitles) 
      { 
        // 添加view 
        addView(generateTextView(title)); 
      } 
      // 设置item的click事件 
      setItemClickEvent(); 
    } 
 
  }

其实就是你可以在onCreate里面去设置tab显示的内容,以及可见的Tab数量,大家猜一猜,如果在布局和onCreate里面都写了数量,哪个有效呢(自己去实验)~~
记得如果是代码控制,setVisibleTabCount在setTabItemTitles之前调用。

ok,基本完工了~~~
有兴趣的,把三角形改成我们的下划线指示器玩一玩~~估计改几行代码即可~~

更多Android App中制作仿MIUI的Tab切换效果的实例分享相关文章请关注PHP中文网!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn