視頻頁面:ViewPager
ViewPager 是一種可以讓用戶通過左右滑動來切換頁面的控件,通過它我們可以展示超過屏幕尺寸大小的內(nèi)容,在某種程度上它可以說是實現(xiàn)多頁面的最佳方式,同時 ViewPager 還支持任意動態(tài)的添加/刪除頁面。比如我們可以將不同的類別的內(nèi)容分別放在不同頁面當中,然后通過滑動切換不同的類別從而給用戶展示不同的頁面,這個在類似百度App等新聞類App中非常適用。在 ViewPager 中插入“娛樂”、“國際”、“體育”、“星座”等等新聞類別,然后在不同的 View 中展示不同的新聞內(nèi)容,還可以根據(jù)用戶的喜好動態(tài)增加/刪除某些頁面,接下來就一起來看看如何完成多頁視圖。
1. ViewPager 的特性
大家在使用 Android 手機的時候一定都見過下圖的效果:
沒錯,這個就是今天的主角——ViewPager 了。在實際開發(fā)過程中,我們大多數(shù)時候會采用 Fragment 來展示一個頁面而不會直接采用 View,在后面的章節(jié)學完 Fragment 之后就會知道,F(xiàn)ragment 可以封裝 UI 和邏輯,并且會維護自己的生命周期,所以通過 Fragment 我們可以實現(xiàn)更豐富生動的效果,當然對于 ViewPager 的使用而言其實二者幾乎沒什么差別,我們現(xiàn)在還是把重點放在 ViewPager 上,在后面學完 Fragment 之后只需要做一些簡單的改動即可將 View 替換成 Fragment。ViewPager 和前面所學的 ListView/GridView 類似,也需要一個適配器來完成數(shù)據(jù)的適配,不同的是 ViewPager 有一個專門的適配器——PagerAdapter,所以我們很多的工作也是圍繞著 PagerAdapter 展開。
2. PagerAdapter 的使用方法
類似前面所講的 BaseAdapter 的四個回調(diào)接口(不記得的同學可以返回前面章節(jié)回顧一下),PagerAdapter 同樣也有類似的回調(diào)接口,如下:
- public Object instantiateItem(ViewGroup container, int position):
根據(jù)傳入的 position 創(chuàng)建一個 Page,適配器需要在這個回調(diào)里網(wǎng) container 里面添加 View,如下:
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.view_pager, container, false);
TextView textView = itemView.findViewById(R.id.TextView);
textView.setText("Imooc Android");
container.addView(itemView);
return itemView;
}
- public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object):
根據(jù)傳入的 position 移除一個 Page
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
- gpublic int getCount():
返回當前 ViewPager 中可用 View 的數(shù)量
public int getCount() {
return mList.length;
}
- public boolean isViewFromObject(@NonNull View view, @NonNull Object object):
通過instantiateItem()
返回的對象可以看做是一個 key,這個方法用來判斷傳入的 View 是否是之前創(chuàng)建的 key,如下:
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
常用的一般就是以上四種回調(diào)方法,理解起來都比較簡單,其中要注意的是getCount
和isViewFromObject
這兩個是必須實現(xiàn)的,而instantiateItem()
和destroyItem()
是可選的,不過大多數(shù)場景還是推薦大家實現(xiàn) 4 個回調(diào)方法。
3. ViewPager 完整示例
本節(jié)將通過一個簡單的例子學習 ViewPager 的使用,每一個 Page 將會通過一個 View 來實現(xiàn)(在學習了 Fragment 之后,可以嘗試將 View 替換成 Fragment)。例子中的每一個 Page 表示一種類別,在切換過程中我們會接收切換的狀態(tài)回調(diào),同步更新類別標題。
3.1 整體布局
由于每個頁面都在 ViewPager 中,所以整體的布局非常簡單,我們只需要放置一個 ViewPager 以及一個 TextView 用來顯示當前 Page 的標題即可。
<?xml version="1.0" encoding="utf-8"?>
<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:showIn="@layout/activity_main">
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:text="Num"
android:textSize="100sp"
android:id="@+id/text"
android:layout_marginTop="50dp"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
3.2 Page 頁面的布局編寫
對于整體布局而言,主要的頁面都在 ViewPager 當中,所以我們需要為不同結(jié)構(gòu)的 Page 編寫不同的頁面,由于本例中每個 Page 的頁面結(jié)構(gòu)都一樣,所以可以直接復用一套,直接在里面放置一個 ImageView 用于展示類別圖片。
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3.3 適配器的編寫
適配這一塊主要就是對四個回調(diào)接口的實現(xiàn),其實在第 2 小節(jié)的描述中已經(jīng)展示了各個方法的實現(xiàn)方式。
package com.emercy.myapplication;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import java.util.List;
public class MyAdapter extends PagerAdapter {
private final List<View> mView;
public MyAdapter(List<View> view) {
mView = view;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
container.addView(mView.get(position));
return mView.get(position);
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView(mView.get(position));
}
@Override
public int getCount() {
return mView.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
}
3.4 MainActivity 主邏輯編寫
主邏輯主要的任務(wù)就是將前面的布局都用上,并通過 Adapter 將數(shù)據(jù)和布局串聯(lián)起來,所以我們需要獲取到 ViewPager 中每個 View 的實例,設(shè)置類別之后傳遞給 Adpater,綁定的任務(wù)就交由Adpater完成。接下來再監(jiān)聽 ViewPager 的滑動狀態(tài)從而判斷當前切換的位置,從而實現(xiàn)同步更新類別標題,整體代碼如下:
package com.emercy.myapplication;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;
import androidx.viewpager.widget.ViewPager;
import org.w3c.dom.Text;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity {
private ViewPager mViewPager;
private String[] mTitle = new String[]{"蘋果", "香蕉", "荔枝"};
private List<View> mView = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tv = findViewById(R.id.text);
mViewPager = findViewById(R.id.view_pager);
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
tv.setText(mTitle[position]);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
ImageView view1 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
view1.setBackgroundColor(Color.RED);
view1.setImageResource(R.drawable.apple);
mView.add(view1);
ImageView view2 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
view2.setBackgroundColor(Color.GREEN);
view2.setImageResource(R.drawable.banana);
mView.add(view2);
ImageView view3 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
view3.setBackgroundColor(Color.BLUE);
view3.setImageResource(R.drawable.lychee);
mView.add(view3);
mViewPager.setAdapter(new MyAdapter(mView));
tv.setText(mTitle[0]);
}
}
編譯之后效果如下:
通過左右滑動可以切換不同的頁面,每個頁面對應的一種水果類別,這樣就通過 ViewPager 實現(xiàn)了一個簡單的頁面切換效果。
4. 小節(jié)
本節(jié)介紹了 ViewPager 的特點及使用場景,并講解了 ViewPager 的專屬適配器——PagerAdapter 的幾個回調(diào)函數(shù)的使用方法,最后采用 ViewPager 實現(xiàn)了一個簡單的例子用于切換不同的頁面從而展示不同的類別。這一節(jié)中是直接采用 View 來承載每一個 Page,而在實際開發(fā)中大多數(shù)場景會采用 Fragemnt 來承載 Page,不過對于 ViewPager 的使用到同小異,針對 Fragment 系統(tǒng)提供了兩種 Adapter:FragmentPageAdapter
和FragmentStatePagerAdapter
,在學完 Fragement之后大家可以自行修改本節(jié)的例子,通過 Fragment 來實現(xiàn)本例的效果。