ViewPager、Fragment、Matrix綜合使用實現Tab滑頁效果


原文地址:http://www.cnblogs.com/kross/p/3372987.html

我們實現一個上面是一個可以左右滑動的頁面,下面是三個可點擊切換的tab按鈕,tab按鈕上還有一個激活條。效果如下圖所示:

----------------我是分割線--------------------

--------------------------我是分割線,下面的圖片表示往右滑動,白條中的小機器人往右滑動-------------------------

--------------------------我是分割線,切換到第二個頁面,tab中的第二個被激活,字呈白色-----------------------

------------------我是分割線-------------------

OK,效果就是如上所示的效果。現在開始一步一步的介紹如何實現。

1.創建好基礎的布局文件。

整個頁面是一個LinearLayout,如果你用其他的布局也是可以的,在下只是圖一個方便了。

根據最終效果,我們將整個布局分為三個部分:

第一部分是上面那一大塊有顏色的區域,這是一個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又需要三個獨立的布局。

因此我們需要做的第一件事是准備三個我們期望的布局。

為了簡單起見,我們准備layout1、layout2、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

原文地址:http://www.cnblogs.com/kross/p/3372987.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM