1. 程式人生 > >ViewPager 詳解(二)---詳解四大函式

ViewPager 詳解(二)---詳解四大函式

前言:上篇中我們講解了如何快速實現了一個滑動頁面,但問題在於,PageAdapter必須要重寫的四個函式,它們都各有什麼意義,在上節的函式內部為什麼要這麼實現,下面我們就結合android的API說明,詳細講解一下。

相關文章:

這篇涉及到內容比較多,因為有英文文件和中文文件,還有示例,在排版上很難駕馭(因為本人語文太爛……),所以排版有點非常的不賞心悅目,所以只能靠大家耐著性子慢慢看了……,我覺得大家看完之後應該會有所收穫,謝謝。

一、SDK講解

1、官方文件:(看不懂沒關係,下面有翻譯)

Class Overview

Base class providing the adapter to populate pages inside of a . You will most likely want to use a more specific implementation of this, such as  or.

When you implement a PagerAdapter, you must override the following methods at minimum:

PagerAdapter is more general than the adapters used for . Instead of providing a View recycling mechanism directly ViewPager uses callbacks to indicate the steps taken during an update. A PagerAdapter may implement a form of View recycling if desired or use a more sophisticated method of managing page Views such as Fragment transactions where each page is represented by its own Fragment.

ViewPager associates each page with a key Object instead of working with Views directly. This key is used to track and uniquely identify a given page independent of its position in the adapter. A call to the PagerAdapter method  indicates that the contents of the ViewPager are about to change. One or more calls to  and/or will follow, and the end of an update will be signaled by a call to . By the time  returns the views associated with the key objects returned by  should be added to the parent ViewGroup passed to these methods and the views associated with the keys passed to  should be removed. The method  identifies whether a page View is associated with a given key object.

A very simple PagerAdapter may choose to use the page Views themselves as key objects, returning them from  after creation and adding them to the parent ViewGroup. A matching  implementation would remove the View from the parent ViewGroup and  could be implemented as return view == object;.

PagerAdapter supports data set changes. Data set changes must occur on the main thread and must end with a call to  similar to AdapterView adapters derived from. A data set change may involve pages being added, removed, or changing position. The ViewPager will keep the current page active provided the adapter implements the method.


2、對應翻譯

Class Overview

當你實現一個PagerAdapter時,至少需要覆蓋以下幾個方法:

PagerAdapter比AdapterView的使用更加普通.ViewPager使用回撥函式來表示一個更新的步驟,而不是使用一個檢視回收機制。在需要的時候pageradapter也可以實現檢視的回收或者使用一種更為巧妙的方法來管理檢視,比如採用可以管理自身檢視的fragment。

viewpager不直接處理每一個檢視而是將各個檢視與一個鍵聯絡起來。這個鍵用來跟蹤且唯一代表一個頁面,不僅如此,該鍵還獨立於這個頁面所在adapter的位置。當pageradapter將要改變的時候他會呼叫startUpdate函式,接下來會呼叫一次或多次的instantiateItem或者destroyItem。最後在更新的後期會呼叫finishUpdate。當finishUpdate返回時 instantiateItem返回的物件應該新增到父ViewGroup destroyItem返回的物件應該被ViewGroup刪除。methodisViewFromObject(View, Object)代表了當前的頁面是否與給定的鍵相關聯。對於非常簡單的pageradapter或許你可以選擇用page本身作為鍵,在建立並且新增到viewgroup後instantiateItem方法裡返回該page本身即可destroyItem將會將該page從viewgroup裡面移除。isViewFromObject方法裡面直接可以返回view == object。pageradapter支援資料集合的改變,資料集合的改變必須要在主執行緒裡面執行,然後還要呼叫notifyDataSetChanged方法。和baseadapter非常相似。資料集合的改變包括頁面的新增刪除和修改位置。viewpager要維持當前頁面是活動的,所以你必須提供getItemPosition方法。

3、解析

看上面的翻譯,與我們相關只有這兩段話:

viewpager不直接處理每一個檢視而是將各個檢視與一個鍵聯絡起來。這個鍵用來跟蹤且唯一代表一個頁面,不僅如此,該鍵還獨立於這個頁面所在adapter的位置。當pageradapter將要改變的時候他會呼叫startUpdate函式,接下來會呼叫一次或多次的instantiateItem或者destroyItem。最後在更新的後期會呼叫finishUpdate。當finishUpdate返回時 instantiateItem返回的物件應該新增到父ViewGroup destroyItem返回的物件應該被ViewGroup刪除。methodisViewFromObject(View, Object)代表了當前的頁面是否與給定的鍵相關聯。

對於非常簡單的pageradapter或許你可以選擇用page本身作為鍵,在建立並且新增到viewgroup後instantiateItem方法裡返回該page本身即可destroyItem將會將該page從viewgroup裡面移除。isViewFromObject方法裡面直接可以返回view == object。

對於上面兩段話,我這裡有兩點要著重講一下:

1、第一段說明了,鍵(Key)的概念,首先這裡要清楚的一點是,每個滑動頁面都對應一個Key,而且這個Key值是用來唯一追蹤這個頁面的,也就是說每個滑動頁面都與一個唯一的Key一一對應。大家先有這個概念就好,關於這個Key是怎麼來的,下面再講。

2、第二段簡單講了一個應用,即將當前頁面本身的View作為Key。其實這個應用就是我們前一章講的例子應用。不太理解?沒關係,下面細講。下面我們講講Key的問題

4、關於Key

首先:destroyItem()

public void destroyItem (ViewGroup container, int position, Object object)

Remove a page for the given position. The adapter is responsible for removing the view from its container, although it only must ensure this is done by the time it returns from.

Parameters
containerThe containing View from which the page will be removed.
positionThe page position to be removed.
該方法實現的功能是移除一個給定位置的頁面。介面卡有責任從容器中刪除這個檢視。這是為了確保在finishUpdate(viewGroup)返回時檢視能夠被移除。
@Override
public void destroyItem(ViewGroup container, int position,
		Object object) {
	// TODO Auto-generated method stub
	container.removeView(viewList.get(position));
}
果不其然,我們將給定位置的檢視從container中移除了……

然後看getCount ()

public abstract int getCount ()

Return the number of views available.

返回當前有效檢視的個數。

在上一章例子中,我們是這麼做的:

@Override
public int getCount() {
	// TODO Auto-generated method stub
	return viewList.size();
}
返回了當前要滑動檢視的個數,與SDK說明一致。

最難的兩個來了

instantiateItem (ViewGroup container, int position)

instantiateItem (ViewGroup container, int position)

Create the page for the given position. The adapter is responsible for adding the view to the container given here, although it only must ensure this is done by the time it returns from.

Parameters
containerThe containing View in which the page will be shown.
positionThe page position to be instantiated.
Returns
  • Returns an Object representing the new page. This does not need to be a View, but can be some other container of the page.
這個函式的實現的功能是建立指定位置的頁面檢視。介面卡有責任增加即將建立的View檢視到這裡給定的container中,這是為了確保在finishUpdate(viewGroup)返回時this is be done!

返回值:返回一個代表新增檢視頁面的Object(Key),這裡沒必要非要返回檢視本身,也可以這個頁面的其它容器。其實我的理解是可以代表當前頁面的任意值,只要你可以與你增加的View一一對應即可,比如position變數也可以做為Key(最後我們舉個例子試試可不可行)

心得 :

1、從說明中可以看到,在程式碼中,我們的責任是將指定position的檢視新增到conatiner中

2、Key的問題:從這個函式就可以看出,該函式返回值就是我們根據引數position增加到conatiner裡的View的所對應的Key!!!!!!!

3、“it only must ensure this is done by the time it returns fromfinishUpdate(ViewGroup).”這句話在destroyItem()的函式說明中同樣出現過,這說明在 finishUpdate(viewGroup)執行完後,有兩個操作,一個是原檢視的移除(不再顯示的檢視),另一個是新增顯示檢視(即將顯示的檢視)

在上一章的程式碼中,我們是這樣做的:

@Override
public Object instantiateItem(ViewGroup container, int position) {
	// TODO Auto-generated method stub
		container.addView(viewList.get(position));
		
		
		return viewList.get(position);
	}
};
在這裡,我們做了兩件事

第一:將引數裡給定的position的檢視,增加到conatiner中,供其建立並顯示、。

第二:返回當前position的View做為此檢視的Key。還記得API官方文件中下面這段話麼?
對於非常簡單的pageradapter或許你可以選擇用page本身作為鍵,在建立並且新增到viewgroup後instantiateItem方法裡返回該page本身即可destroyItem將會將該page從viewgroup裡面移除。isViewFromObject方法裡面直接可以返回view == object。

這裡就把當前的View當作Key傳過出去!!!!

最後一個: isViewFromObject (View view, Object object)

public abstract boolean isViewFromObject (View view, Object object)

Determines whether a page View is associated with a specific key object as returned by . This method is required for a PagerAdapter to function properly.

Parameters
viewPage View to check for association with object
objectObject to check for association with view
Returns
  • true if view is associated with the key object object
功能:該函式用來判斷instantiateItem(ViewGroup, int)函式所返回來的Key與一個頁面檢視是否是代表的同一個檢視(即它倆是否是對應的,對應的表示同一個View)

返回值:如果對應的是同一個View,返回True,否則返回False。

在上章節的例子中,我們這樣做的:

@Override
public boolean isViewFromObject(View arg0, Object arg1) {
	// TODO Auto-generated method stub
	return arg0 == arg1;
}
由於在instantiateItem()中,我們作為Key返回來的是當前的View,所以在這裡判斷時,我們直接將Key與View看是否相等來判斷是否是同一個View。

二、自定義Key例項

經過上面的講解,想必大家給Key的概念應該有個清楚的理解,下面舉個例子來說明Key與View的關係,由於Key與View要一一對應,所以我把每個檢視所處的位置Position作為Key,在上章例子的基礎上更改的,下面先看全部程式碼,然後看部分講解:

package com.example.testviewpage_2;
/**
 * @author  harvic
 * @date 2014.8.11
 */
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MainActivity extends Activity {

	private View view1, view2, view3;
	private List<View> viewList;// view陣列
	private ViewPager viewPager; // 對應的viewPager
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		viewPager = (ViewPager) findViewById(R.id.viewpager);
		LayoutInflater inflater = getLayoutInflater();
		view1 = inflater.inflate(R.layout.layout1, null);
		view2 = inflater.inflate(R.layout.layout2, null);
		view3 = inflater.inflate(R.layout.layout3, null);

		viewList = new ArrayList<View>();// 將要分頁顯示的View裝入陣列中
		viewList.add(view1);
		viewList.add(view2);
		viewList.add(view3);

		PagerAdapter pagerAdapter = new PagerAdapter() {

			@Override
			public boolean isViewFromObject(View arg0, Object arg1) {
				// TODO Auto-generated method stub
				//根據傳來的key,找到view,判斷與傳來的引數View arg0是不是同一個檢視
				return arg0 == viewList.get((int)Integer.parseInt(arg1.toString()));
			}

			@Override
			public int getCount() {
				// TODO Auto-generated method stub
				return viewList.size();
			}

			@Override
			public void destroyItem(ViewGroup container, int position,
					Object object) {
				// TODO Auto-generated method stub
				container.removeView(viewList.get(position));
			}

			@Override
			public Object instantiateItem(ViewGroup container, int position) {
				// TODO Auto-generated method stub
				container.addView(viewList.get(position));

				//把當前新增檢視的位置(position)作為Key傳過去
				return position;
			}
		};

		viewPager.setAdapter(pagerAdapter);

	}

}
在這裡更改了兩個地方:

1、先看Key的產生的位置instantiateItem()

@Override
public Object instantiateItem(ViewGroup container, int position) {
	// TODO Auto-generated method stub
	container.addView(viewList.get(position));

	//把當前新增檢視的位置(position)作為Key傳過去
	return position;
}

我們在上講也講了在這個函式中Key是作為返回值與當前裝入Container中的檢視對應起來的。所以在這裡我們返回postion與container.addView(viewList.get(position));裡的viewList.get(position)這個檢視對應起來。
2、isViewFromObject ()

@Override
public boolean isViewFromObject(View arg0, Object arg1) {
	// TODO Auto-generated method stub
	//根據傳來的key,找到view,判斷與傳來的引數View arg0是不是同一個檢視
	return arg0 == viewList.get((int)Integer.parseInt(arg1.toString()));
}

判斷從instantiateItem()返回來的Key與當前的View是否能對應起來,我們知道從instantiateItem傳過來的其實是position,所以我們要根據position找到View,然後跟引數中的View arg0判斷。

但在真正操作時出現了問題,我們要先將obect對應轉換為int型別:(int)Integer.parseInt(arg1.toString());然後再根據position找到對應的View;

效果圖:三個View之間的滑動切換

  

這裡只所以與上章不一樣,僅僅只有上部分一部分的地方才有滑動切換,是因為我更改了佈局檔案:

<RelativeLayout 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"
    tools:context="com.example.testviewpage_2.MainActivity" >

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="wrap_content"
        android:layout_height="200dip"
        android:layout_gravity="center" />

</RelativeLayout>
這裡將layout_height更改為200dip,只所以這麼做,是為了告訴大家,只要在想要實現滑動切換的地方新增上<android.support.v4.view.ViewPager />就可以實現切換,無所謂位置和大小,跟普通控制元件一樣!!!!!!