原文地址:http://www.cnblogs.com/kross/p/3372987.htmlhtml
咱们实现一个上面是一个能够左右滑动的页面,下面是三个可点击切换的tab按钮,tab按钮上还有一个激活条。效果以下图所示:java
----------------我是分割线--------------------android
--------------------------我是分割线,下面的图片表示往右滑动,白条中的小机器人往右滑动-------------------------数组
--------------------------我是分割线,切换到第二个页面,tab中的第二个被激活,字呈白色-----------------------框架
------------------我是分割线-------------------dom
OK,效果就是如上所示的效果。如今开始一步一步的介绍如何实现。ide
1.建立好基础的布局文件。函数
整个页面是一个LinearLayout,若是你用其余的布局也是能够的,在下只是图一个方便了。布局
根据最终效果,咱们将整个布局分为三个部分:post
第一部分是上面那一大块有颜色的区域,这是一个ViewPager,里面的内容是经过Fragment加载进来的。
第二部分是那个在白条中间的小机器人这一块,这个做为激活条(本身编的名词,就是表名当前的tab是被激活的),就是一个ImageView。
第三部分是最底下的三个tab,是一个水平的LinearLayout,里面每个tab都是ImageView+TextView构成的。
注意这里tab这个布局是被重复使用的,由于有三个嘛,为了代码重用,咱们可使用<include>标签。
因此,咱们先写好每一个小tab的布局(/res/layout/tab.xml),代码以下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView android:id="@+id/tab_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/ic_launcher"/> <TextView android:id="@+id/tab_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/tab_text" android:gravity="center" android:textColor="#cccccc"/> </LinearLayout>
为了方便,就直接使用启动图标的图片了,啊哈哈……啊哈哈哈……
接下来咱们去写主体页面的布局(/res/layout/activity_main.xml),代码以下:
注意<include>标签的使用哦~
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <!-- ViewPager的使用必须是完整的名字哦 --> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1"/> <!-- 这是所谓的激活条,为了方便,也直接使用启动图标的图片了 --> <ImageView android:id="@+id/active_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="matrix" android:src="@drawable/ic_launcher" android:background="#ffffff"/> <!-- 下面的线性布局是三个tab放置的区域 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <!-- 使用include标签,能够直接重用xml,这是我在《Android UI 基础教程》中发现的~\(^o^)/~ --> <include android:id="@+id/tab1" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1" layout="@layout/tab"/> <include android:id="@+id/tab2" android:layout_weight="1" android:layout_width="0dip" android:layout_height="wrap_content" layout="@layout/tab"/> <include android:id="@+id/tab3" android:layout_weight="1" android:layout_width="0dip" android:layout_height="wrap_content" layout="@layout/tab"/> </LinearLayout> </LinearLayout>
OK,这样咱们整个页面的框架就有了。
2.给ViewPager添加页面
OKOK,在这一步呢,咱们要作的是给ViewPager这个控件添加页面,咱们从一开始就打算添加三个页面的,因此上面才有3个tab,若是你须要更多,都是同样的道理。
ViewPager这个控件使用起来是很是方便的,在给它添加好页面后,它自身能够实现左右滑动切换的效果,很是滴好用啊。
ViewPager最重要的是给它设置适配器(setAdapter),咱们指望的结果是,每一个页面里,都有本身的控件,操做这些控件的方法均可以分别写在各自的Fragment中,而不是所有都写在MainActivity中。因此,咱们要让ViewPager里面装载的是Fragment,所以,咱们须要去拓展(extends)一个FragmentPagerAdapter。
实现FragmentPagerAdapter又须要事先准备好三个Fragment,三个Fragment又须要三个独立的布局。
所以咱们须要作的第一件事是准备三个咱们指望的布局。
为了简单起见,咱们准备layout一、layout二、layout3(/res/layout/layout*.xml)这样三个布局,这三个布局的背景颜色都是不同的,里面都放了一个按钮,为了可以在后面实现每一个页面独立的功能。
布局文件的代码以下:
<?xml version="1.0" encoding="utf-8"?> <!-- 这是layout1.xml的布局代码,另外两个布局文件就是背景颜色和按钮的id,text改变了而已 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#ff0000"> <Button android:id="@+id/button1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="button1"/> </LinearLayout>
接下来,咱们要拓展(extends)三个Fragment,分别是Fragment1.java、Fragment2.java、Fragment3.java(/src/fragment/Fragment*.java)。代码以下:
推荐不一样功能的类,创建不一样的包来进行分类管理。
public class Fragment1 extends Fragment { private View layout = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { this.layout = inflater.inflate(R.layout.layout1, null); //从这里到return之间,你能够为这个Fragment添加其功能。 Button b = (Button)this.layout.findViewById(R.id.button1); b.setOnClickListener(new ButtonListener()); return this.layout; } public class ButtonListener implements OnClickListener { @Override public void onClick(View v) { Toast.makeText(getActivity(), "button1", Toast.LENGTH_SHORT).show(); } } }
OKOK,三个Fragment也准备好了,接下来实现一个FragmentPagerAdapter(/src/adapter/FragmentAdapter.java),代码以下:
public class FragmentAdapter extends FragmentPagerAdapter{ //这个是存放三个Fragment的数组,待会从MainActivity中传过来就好了 private ArrayList<Fragment> fragmentArray; //本身添加一个构造函数从MainActivity中接收这个Fragment数组 public FragmentAdapter(FragmentManager fm, ArrayList<Fragment> fragmentArray) { this(fm); this.fragmentArray = fragmentArray; } public FragmentAdapter(FragmentManager fm) { super(fm); } //这个函数的做用是当切换到第arg0个页面的时候调用。 @Override public Fragment getItem(int arg0) { return this.fragmentArray.get(arg0); } @Override public int getCount() { return this.fragmentArray.size(); } }
实现好Adapter后,咱们就去MainActivity(src/com.kross.test/MainActivity.java)中配置一下。
在MainActivity中,咱们须要作以下几件事情:
1.准备好Fragment,把它们添加进数组。
2.获取到ViewPager的实例,为其设置适配器。
3.设置ViewPager当前显示第一页。
由于在FragmentAdapter的构造函数中须要一个FragmentManager,因此,MainActivity应该继承FragmentActivity。
代码以下:
public class MainActivity extends FragmentActivity { private ViewPager viewPager = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);this.initViewPager(); }
private void initViewPager() { //获取到ViewPager的实例 this.viewPager = (ViewPager)findViewById(R.id.viewpager); //构造好存放Fragment的数组 ArrayList<Fragment> fragmentArray = new ArrayList<Fragment>(); fragmentArray.add(new Fragment1()); fragmentArray.add(new Fragment2()); fragmentArray.add(new Fragment3()); //为ViewPager设置适配器 this.viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentArray)); //设置当前显示的页面 this.viewPager.setCurrentItem(0); } }
3.为下面的三个tab添加点击事件,并设置好相应的激活效果。
完成上面两步,应该能够实现三个页面滑动的效果。但下面的三个Tab是没有被用上的。咱们这步要作到的事情是:点击下面的tab,切换到相应的页面,并激活tab,滑动上面的页面,并激活tab。
这里须要使用到一个接口OnPageChangeListener。这个接口中有三个方法,都是在页面滑动的时候会被调用。咱们须要使用这个接口,让页面滑动的时候激活相应的tab。
如今来实现该接口PagerListener(/src/listener/PagerListener.java),代码以下:
public class PagerListener implements OnPageChangeListener { private ArrayList<View> tabArray = null;private final static String TAG = "PagerListener"; public PagerListener(ArrayList<View> tabArray) { this.tabArray = tabArray; } @Override //这个…… public void onPageScrollStateChanged(int arg0) { Log.i(TAG, "onPageScrollStateChanged:" + Integer.toString(arg0)); } @Override //这个方法是页面滚动的时候被调用,咱们将在这个方法里完成对激活条的操做 public void onPageScrolled(int arg0, float arg1, int arg2) { Log.i(TAG, "onPageScrolled"); } @Override //这个方法是当页面切换完毕后被调用, public void onPageSelected(int arg0) { Log.i(TAG, "onPageSelected"); //先将全部的tab设置成灰色的 for (View item : this.tabArray) { ((TextView)item.findViewById(R.id.tab_text)).setTextColor(Color.rgb(20, 20, 20)); } //再将当前的tab设置成白色的,表示激活状态 ((TextView)this.tabArray.get(arg0).findViewById(R.id.tab_text)).setTextColor(Color.WHITE); } }
接下来,咱们须要实现一个OnClickListener。
TabListener(src/listener/TabListener.java),代码以下:
public class TabListener implements OnClickListener { private ViewPager viewPager = null; public TabListener(ViewPager v) { this.viewPager = v; } @Override public void onClick(View arg0) { switch (arg0.getId()) { case R.id.tab1: this.viewPager.setCurrentItem(0); break; case R.id.tab2: this.viewPager.setCurrentItem(1); break; case R.id.tab3: this.viewPager.setCurrentItem(2); break; } } }
OKOK,接下来,咱们只须要在MainActivity中获取tab的实例,给tab设置监听器,给ViewPager设置监听器就OK了。
MainActivity的代码变成以下这样:
public class MainActivity extends FragmentActivity { private ViewPager viewPager = null; //三个tab private View tab1 = null; private View tab2 = null; private View tab3 = null;//存放tab的数组 private ArrayList<View> tabArray = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);this.initViewPager(); this.initTab(); this.setListener(); }private void initTab() { this.tab1 = findViewById(R.id.tab1); this.tab2 = findViewById(R.id.tab2); this.tab3 = findViewById(R.id.tab3); this.tabArray = new ArrayList<View>(); this.tabArray.add(tab1); this.tabArray.add(tab2); this.tabArray.add(tab3); } private void initViewPager() { //获取到ViewPager的实例 this.viewPager = (ViewPager)findViewById(R.id.viewpager); //构造好存放Fragment的数组 ArrayList<Fragment> fragmentArray = new ArrayList<Fragment>(); fragmentArray.add(new Fragment1()); fragmentArray.add(new Fragment2()); fragmentArray.add(new Fragment3()); //为ViewPager设置适配器 this.viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentArray)); //设置当前显示的页面 this.viewPager.setCurrentItem(0); } private void setListener() { TabListener tl = new TabListener(this.viewPager); this.tab1.setOnClickListener(tl); this.tab2.setOnClickListener(tl); this.tab3.setOnClickListener(tl); this.viewPager.setOnPageChangeListener(new PagerListener(this.tabArray); } }
完成上面的三步,就基本上实现了大部分的功能了。页面能够切换,tab也能够点,有激活效果。
4.使用Matrix让激活条随页面的滑动而滑动。
激活条的滑动,须要使用到OnPageChangeListener接口中的onPageScrolled方法,
该方法有会在页面被滑动的时候调用,将会获取到三个参数,第一个参数为当前页面的下标,第二个参数为滑动距离占屏幕宽度的百分比,第三个参数为实际滑动的像素。
假设咱们当前处在0下标的页面,咱们从右往左滑动,滑向1下标的页面,在滑动的时候,该方法会一直被调用。
在没有完全切换到1下标的页面时,第一个参数一直是0,直到完全切换到1下标的页面。
第二个参数是个[0,1)的浮点数,一滑动,该参数就开始变大 0.12313---->0.23321---->0.31233,当页面快完成完全切换的时候,也就是该参数将无限逼近1,但不会是1,而后忽然变成了0,完成的页面的切换。
第三个参数就很好理解了,个人屏幕宽度是720px,我滑到通常,它的值就是360。
知道了这些,咱们须要获取激活条须要移动的长度,以及屏幕的宽度。
tab有三个,因此咱们用屏幕宽度/3,这是每一个tab的宽度,tab的宽度 - 图片的宽度,这是剩余的空间,剩余的空间 / 2,就是小图片的偏移量(offset)。
激活条图片默认会出如今所在控件位置的最左边,初始的时候,咱们就须要将它的位置设置在一个offset以后。
所以MainActivity的代码变成以下这样:
public class MainActivity extends FragmentActivity { private ViewPager viewPager = null; //三个tab private View tab1 = null; private View tab2 = null; private View tab3 = null; //偏移量 private int offset; //图片宽度 private int imageWidth; //存放tab的数组 private ArrayList<View> tabArray = null; private ImageView activeBar = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取激活条控件 this.activeBar = (ImageView)findViewById(R.id.active_bar); //获取该激活条的宽度 this.imageWidth = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher).getWidth(); //获取屏幕的宽度 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int screenWidth = dm.widthPixels; //计算出偏移量 this.offset = (screenWidth / 3 - imageWidth) / 2; this.initViewPager(); this.initTab(); this.setListener(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } private void initTab() { this.tab1 = findViewById(R.id.tab1); this.tab2 = findViewById(R.id.tab2); this.tab3 = findViewById(R.id.tab3); this.tabArray = new ArrayList<View>(); this.tabArray.add(tab1); this.tabArray.add(tab2); this.tabArray.add(tab3); } private void initViewPager() { //获取到ViewPager的实例 this.viewPager = (ViewPager)findViewById(R.id.viewpager); //构造好存放Fragment的数组 ArrayList<Fragment> fragmentArray = new ArrayList<Fragment>(); fragmentArray.add(new Fragment1()); fragmentArray.add(new Fragment2()); fragmentArray.add(new Fragment3()); //为ViewPager设置适配器 this.viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentArray)); //设置当前显示的页面 this.viewPager.setCurrentItem(0); } private void setListener() { TabListener tl = new TabListener(this.viewPager); this.tab1.setOnClickListener(tl); this.tab2.setOnClickListener(tl); this.tab3.setOnClickListener(tl); //注意这里的构造函数 this.viewPager.setOnPageChangeListener(new PagerListener(this.tabArray, this.activeBar, this.offset, this.imageWidth)); } }
须要注意给ViewPager设置监听器的时候,传入了激活条的句柄,偏移量以及图片的宽度。
接下来咱们须要修改下PagerListener的代码:
public class PagerListener implements OnPageChangeListener { private ArrayList<View> tabArray = null; private ImageView activeBar = null; private int offset; private int imageWidth; private final static String TAG = "PagerListener"; public PagerListener(ArrayList<View> tabArray, ImageView activeBar, int offset, int iw) { this.tabArray = tabArray; this.activeBar = activeBar; this.offset = offset; this.imageWidth = iw; } @Override //这个…… public void onPageScrollStateChanged(int arg0) { Log.i(TAG, "onPageScrollStateChanged:" + Integer.toString(arg0)); } @Override //这个方法是页面滚动的时候被调用,咱们将在这个方法里完成对激活条的操做 public void onPageScrolled(int arg0, float arg1, int arg2) { Log.i(TAG, "onPageScrolled"); //new一个矩阵 Matrix matrix = new Matrix(); //设置激活条的最终位置 switch (arg0) { case 0: //使用set直接设置到那个位置 matrix.setTranslate(this.offset, 0); break; case 1: matrix.setTranslate(this.offset * 3 + this.imageWidth, 0); break; case 2: matrix.setTranslate(this.offset * 5 + this.imageWidth * 2, 0); break; } //在滑动的过程当中,计算出激活条应该要滑动的距离 float t = (this.offset * 2 + this.imageWidth) * arg1; //使用post追加数值 matrix.postTranslate(t, 0); this.activeBar.setImageMatrix(matrix); } @Override //这个方法是当页面切换完毕后被调用, public void onPageSelected(int arg0) { Log.i(TAG, "onPageSelected"); //先将全部的tab设置成灰色的 for (View item : this.tabArray) { ((TextView)item.findViewById(R.id.tab_text)).setTextColor(Color.rgb(20, 20, 20)); } //再将当前的tab设置成白色的,表示激活状态 ((TextView)this.tabArray.get(arg0).findViewById(R.id.tab_text)).setTextColor(Color.WHITE); } }
OKOK,完成上面全部的代码,就OK了。这样就实现了全部的效果。真是辛苦啊……
若是还有疑惑的话,能够来微博上问我:http://weibo.com/KrossFord