Android 教你创造炫酷的ViewPagerIndicator 不仅仅是高仿MIUI-Android-优质IT资源分享社区

admin
管理员
管理员
  • UID1
  • 粉丝29
  • 关注4
  • 发帖数581
  • 社区居民
  • 忠实会员
  • 原创写手
阅读:303回复:0

  Android 教你创造炫酷的ViewPagerIndicator 不仅仅是高仿MIUI

楼主#
更多 发布于:2016-05-22 14:54


哈,今日给咱们带来一个ViewPagerIndicator的制作,信任咱们在做tabIndicator的时分,大多数人都用过TabPageIndicator,而且许多知名APP都运用过这个开源的指示器。咱们有没有想过怎样自个去完成这么的一个指示器,而且代码会有多杂乱呢~~~今日,我就带领咱们来从无到有的完成这么一个指示器,当然了,不预备一模相同,搞得没有立异似的,再看标题,跟MIUI有关,所以咱们预备做一个特性与TabPageIndicator共同的,可是姿态和MIUI的Tab相同的~~
首要仿MIUI对比简略,咱们看看作用图:

可是呢,MIUI中所有的Tab的数量根本维持在两个到四个,可是呢,咱们也许节目对比多,假定咱们10来个Tab咋办,总不能把屏幕均分10份吧,这我5.3的视力我也不能承受~~所以咱们需求做相似TabPageIndicator特性,显现出几个,然后剩余的,能够Tab跟从ViewPager滑动时联动,作用图是这么的:

擦,多看一会,是联动的,上面的Tab也是支撑点击的~
本来学完这个,假如你衷与下面是下划线的,简略修正几行代码就o了~不管你信或不信,横竖我是信了。
ok,完成前谢谢下这位哥们,博客地址:http://blog.csdn.net/qibin0506/article/details/42046559
 ,没看到这篇博客,也不会有此篇了(目测博主群里的)。
2、完成前的剖析
对于这么的指示器,咱们首要剖析下怎样制作。
内容区域咱们根本不必考虑,ViewPager+FragmentPagerAdapter即可。
首要是顶部的Tab区域:
首要虽然是自界说控件,可是咱们只需求采用组合的办法就行:
控件的挑选:外层规划我预备运用LinearLayout,设置个方向水平就行了,内部的标题呢,默许的话,我决议运用TextView。
自界说特点:由于咱们可视的Tab特点应当是能够由用户来定制的,所以咱们对外发布一个自界说的特点,由用户来设置,每个TextView的width即ScreenWidth/mVisibleTab。
三角形的TabIndicator制作:不管是制作三角形仍是下划线的指示器,咱们肯定是在Tab的外层规划中进行制作,那咱们初始化时制作一个三角形,终究中dispatchDraw中,根
据三角形的方位,直接制作。
三角形指示器的方位:方位的y坐标对比简略核算,这儿不赘述。首要是x坐标,由于x坐标跟从ViewPager移动,那么咱们怎样取得移动的间隔呢?有个PageChangeListener
里边有个onPageScrolled办法,这个办法回调positionOffset和positionOffsetPixels,咱们能够跟从这个进行操控x的方位。
LinearLayout的联动,当时Tab假如是移动到可见Tab的最终一个,咱们依然是依据onPageScrolled供给的positionOffset,让咱们的Linearlayout进行scrollXTo~~
好了,该剖析的都剖析了~~这儿需求说明,自界说控件有时分组合现已有的控件完成的作用也是棒棒哒~~已然有合适的,何须自个去从无到有呢~~
3、运用办法
在编写代码前,仍是先贴下运用办法,让咱们先有个感官上的认识,然后再依据这个认识,去探究代码中的完成进程~~
1、规划文件

首要咱们在规划文件中声明下,一个是ViewPagerIndicator
,一个是咱们的ViewPager.
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 ListmTabContents =
new ArrayList();      private FragmentPagerAdapter mAdapter;    
 private ViewPager mViewPager;      //  private ListmDatas =
Arrays.asList("短信1", "短信2", "短信3", "短信4",      //          "短信5", "短信6", "短信7",
"短信8", "短信9");      private ListmDatas = 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);      
 }}
对于咱们的ViewPagerIndicator的运用,就两行:
//设置Tab上的标题mIndicator.setTabItemTitles(mDatas);//设置有关的ViewPagermIndicator.setViewPager(mViewPager,0);
其他代码都是初始化ViewPager神马的~~可见,咱们的控件写好今后运用起来极端简略~~
好了,咱们留意下,规划文件里边有个设置可见Tab个数的特点:zhy:item_count=”3″
 ;
比方:当item_count=3,而给的TabTitle的List的size也是3的话,即是作用图1的作用~~~
当item_count=4,而给的TabTitle的List的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中这么写:

界说好了,肯定得用,怎样用?在哪用?就不必说了吧。上面的用法现已贴过规划文件了~~记住自界说特点的命名空间要留意哈~~~
首要看啥,肯定要有哪些成员变量,和结构里边做了些啥~
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 ListmTabTitles;      /**        *
与之绑定的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则无效*/@Overrideprotected 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
/*** 初始化三角形的宽度*/@Overrideprotected 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
/*** 制作指示器*/@Overrideprotected 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
// 设置有关的ViewPagerpublic 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){     /**       *
       *  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(Listdatas){     // 假如传入的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,根本竣工了~~~
有爱好的,把三角形改成咱们的下划线指示器玩一玩~~估计改几行代码即可~~






[font=Tahoma  ]

优质IT资源分享社区为你提供此文。
站有大量优质android教程视频,资料等资源,包含android基础教程,高级进阶教程等等,教程视频资源涵盖传智播客,极客学院,达内,北大青鸟,猎豹网校等等IT职业培训机构的培训教学视频,价值巨大。欢迎点击下方链接查看。

android教程视频
优质IT资源分享社区(www.itziyuan.top)
一个免费,自由,开放,共享,平等,互助的优质IT资源分享网站。
专注免费分享各大IT培训机构最新培训教学视频,为你的IT学习助力!

!!!回帖受限制请看点击这里!!!
!!!资源失效请在此版块发帖说明!!!

[PS:按 CTRL+D收藏本站网址~]

——“优质IT资源分享社区”管理员专用签名~

本版相似帖子

游客