很久很久以前,當我第一次要做一系列圖片的橫向滑動展示時,使用的是 Gallery 這個類別,之後過了很久幸福快樂日子,某一天!Eclipse 上竟然出現了「This class was deprecated.」之類的訊息,嚇得我立即關閉專案,馬上逃走並灌了三瓶啤酒壓壓驚。無視了一陣子之後,想說這樣下去也不是辦法,決定來找找新的替代方法,就找到了親愛的 ViewPager。
Gallery 在 API 的第一行就寫道「This class was deprecated in API level 16.」並同時提供兩個 Gallery 的替代類別:HorizontalScrollView 及 ViewPager。HorizontalScrollView 是 FrameLayout 的子類別,意謂者只允許擁有一個子類別,並且它只支援水平捲動,如果要同時做垂直及水平的捲動,應該用 ScrollView。看起來並不符合我的需求,所以不考慮。
ViewPager 介紹
ViewPager 允許使用者左右翻動頁面,你必須實作 PagerAdapter 來產生被顯示的頁面。ViewPager 通常搭配 Fragment 一起使用,便於管理每個頁面的生命週期。PagerAdapter 有兩種類型可以使用:FragmentPagerAdapter 和 FragmentStatePagerAdapter
FragmentPagerAdapter 適合頁面數量少的情況,因為每個 Fragment 頁面會持續的被保留在記憶體中,在頁面不可見(not visible)的情況下,view 會被銷毀,但整個 Fragment 仍然存在。
如果頁面數量很多的情況,必須改用 FragmentStatePagerAdapter,唯一不同的是,在頁面不可見(not visible)的情況下,整個 Fragment 都會被銷毀,只保留狀態 (state),因此不會佔用大量的記憶體。
ViewPagerDemo 範例程式碼
主要有三個檔案:- MainActivity.java:主要的 Activity
- PageFragment.java:頁面內容的 Fragment
- PagerAdapter.java:用來產生頁面
MainActivity.java
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initToolbar();
initViewPager();
}
private void initToolbar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionbar = getSupportActionBar();
actionbar.setTitle("ViewPager Demo");
}
private void initViewPager(){
List<Fragment> fragments = getFragments();
PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager(), fragments);
ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
pager.setAdapter(adapter);
}
private List<Fragment> getFragments() {
List<Fragment> fragments = new ArrayList<Fragment>();
fragments.add(PageFragment.newInstance("Fragment 1", R.drawable.wallpaper_1));
fragments.add(PageFragment.newInstance("Fragment 2", R.drawable.wallpaper_2));
fragments.add(PageFragment.newInstance("Fragment 3", R.drawable.wallpaper_3));
fragments.add(PageFragment.newInstance("Fragment 4", R.drawable.wallpaper_4));
fragments.add(PageFragment.newInstance("Fragment 5", R.drawable.wallpaper_5));
fragments.add(PageFragment.newInstance("Fragment 6", R.drawable.wallpaper_6));
return fragments;
}
}
說明:整個流程很簡單,先建立要顯示的 Fragment 的實體,送給 PagerAdapter,再把 PagerAdapter 送給 ViewPager 來顯示。
PageFragment.java
public class PageFragment extends Fragment {
private final String TAG = "PageFragment";
public static final String TITLE = "TITLE";
public static final String IMAGE = "IMAGE";
private String title = "";
private int resImageId = 0;
public static final PageFragment newInstance(String title, int resImageId){
PageFragment f = new PageFragment();
Bundle bdl = new Bundle(1);
bdl.putString(TITLE, title);
bdl.putInt(IMAGE, resImageId);
f.setArguments(bdl);
return f;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d(TAG, "onAttach");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
title = getArguments().getString(TITLE);
resImageId = getArguments().getInt(IMAGE);
Log.d(TAG, title + " - onCreate");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, title + " - onCreateView");
return inflater.inflate(R.layout.myfragment, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, title + " - onViewCreated");
TextView txtTitle = (TextView) view.findViewById(R.id.txtTitle);
ImageView img = (ImageView) view.findViewById(R.id.imageView1);
txtTitle.setText(title);
img.setImageResource(resImageId);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, title + " - onActivityCreated");
Toast.makeText(getActivity(), "Create View " + title, Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroyView() {
Log.d(TAG, title + " - onDestroyView");
super.onDestroyView();
}
@Override
public void onDestroy() {
Log.d(TAG, title + " - onDestroy");
super.onDestroy();
}
}
說明:如果有要傳值給 Fragment 的話,必須在 newInstance() 方法中,利用 Bundle 來包裹值,並透過 setArguments() 方法指定給 Fragment,當它被建立(onCreate)時,則可以利用 getArguments() 來取得值,即 Bundle。
在 onCreate() 中 getArguments();在 onCreateView() 中指定 layout;在 onViewCreated() 中可以對 view 做動作;如果需要 Activity 的資源,則在 onActivityCreated() 方法中取用。
你可以在 LogCat 中觀查 Fragment 的生命週期,Adapter 會預先載入下一個 page 的 fragment,並且會把超過目前頁面往前/後加1之外的頁面移除,也就是保持目前所在頁面的前後各一個頁面。但它並沒有被 onDestroy(),而只是被 onDestroyView()。onDestroy() 只會發生在所屬的 Activity 被 destroy 時。
PagerAdapter.java
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public PagerAdapter(FragmentManager fm, List<Fragment> fragments){
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
說明:沒什麼特別的,繼承 FragmentPagerAdapter,並且覆寫 getItem() 及 getCount() 方法。
main.xml
<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=".MainActivity" >
<include
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
layout="@layout/toolbar" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar" />
</RelativeLayout>
說明:MainActivity 的 layout,使用 ViewPager。
fragment_page.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/wallpaper_1" />
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:gravity="center_horizontal"
android:text="Title"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
說明:每個頁面都顯示一張圖及一行字。
最後完成的結果: 範例程式碼:github - tony915/ViewPagerDemo
!備註:範例專案的 support library 部份,是用 library 的方法加入,所以記得調整你的 librayr 目錄。(專案按 右鍵 -> properties -> Android,如果你的 library 路徑位置和我的不同,會打叉)。或是你也可以直接複製 v4,v7 的 jar 到 libs 目錄下,只是這個做法不太建議。
樓主您好,我將您的範例下載下來後
回覆刪除照著您的方法更改了Library,但是卻發生你專案中的.java檔內的R檔錯誤,顯示R cannot be resolved to a variable
還有values資料夾中的theme_material.xml item錯誤,顯示error: Error: No resource found that matches the given name: attr 'colorAccent'.
請問有什麼方式可以成功編譯專案呢?
先確定 Androidmanifest.xml 中的 targetSDKVersion是不是設定大於21,
刪除再確定support library是否有設定正確
老師您好,我匯入專案之後出現project sdk is not defind,有出現setup sdk,所以我設置android api platform,每個版本都是過了還是出現http://ppt.cc/Sj4Uu,請問有什麼方法解決嗎?謝謝您
回覆刪除抱歉!沒有說明問題是沒辦法執行該app
刪除這個範例是用舊的Eclipse寫的,可能是沒有 gradle 檔的關係,
刪除你用 AS 匯入的時候要選擇匯入 eclipse 專案的選項,
不確定會不會自動建立 gradle 檔,
如果沒有的話要自行加入,這樣應該就能執行。
補充一下..如果想用Android.app.fragment 可以下載android.support.v13 ,謝謝樓主..
回覆刪除