視頻頁(yè)面:ViewPager
ViewPager 是一種可以讓用戶通過左右滑動(dòng)來切換頁(yè)面的控件,通過它我們可以展示超過屏幕尺寸大小的內(nèi)容,在某種程度上它可以說是實(shí)現(xiàn)多頁(yè)面的最佳方式,同時(shí) ViewPager 還支持任意動(dòng)態(tài)的添加/刪除頁(yè)面。比如我們可以將不同的類別的內(nèi)容分別放在不同頁(yè)面當(dāng)中,然后通過滑動(dòng)切換不同的類別從而給用戶展示不同的頁(yè)面,這個(gè)在類似百度App等新聞?lì)怉pp中非常適用。在 ViewPager 中插入“娛樂”、“國(guó)際”、“體育”、“星座”等等新聞?lì)悇e,然后在不同的 View 中展示不同的新聞內(nèi)容,還可以根據(jù)用戶的喜好動(dòng)態(tài)增加/刪除某些頁(yè)面,接下來就一起來看看如何完成多頁(yè)視圖。
1. ViewPager 的特性
大家在使用 Android 手機(jī)的時(shí)候一定都見過下圖的效果:
沒錯(cuò),這個(gè)就是今天的主角——ViewPager 了。在實(shí)際開發(fā)過程中,我們大多數(shù)時(shí)候會(huì)采用 Fragment 來展示一個(gè)頁(yè)面而不會(huì)直接采用 View,在后面的章節(jié)學(xué)完 Fragment 之后就會(huì)知道,F(xiàn)ragment 可以封裝 UI 和邏輯,并且會(huì)維護(hù)自己的生命周期,所以通過 Fragment 我們可以實(shí)現(xiàn)更豐富生動(dòng)的效果,當(dāng)然對(duì)于 ViewPager 的使用而言其實(shí)二者幾乎沒什么差別,我們現(xiàn)在還是把重點(diǎn)放在 ViewPager 上,在后面學(xué)完 Fragment 之后只需要做一些簡(jiǎn)單的改動(dòng)即可將 View 替換成 Fragment。ViewPager 和前面所學(xué)的 ListView/GridView 類似,也需要一個(gè)適配器來完成數(shù)據(jù)的適配,不同的是 ViewPager 有一個(gè)專門的適配器——PagerAdapter,所以我們很多的工作也是圍繞著 PagerAdapter 展開。
2. PagerAdapter 的使用方法
類似前面所講的 BaseAdapter 的四個(gè)回調(diào)接口(不記得的同學(xué)可以返回前面章節(jié)回顧一下),PagerAdapter 同樣也有類似的回調(diào)接口,如下:
- public Object instantiateItem(ViewGroup container, int position):
根據(jù)傳入的 position 創(chuàng)建一個(gè) Page,適配器需要在這個(gè)回調(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 移除一個(gè) Page
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
- gpublic int getCount():
返回當(dāng)前 ViewPager 中可用 View 的數(shù)量
public int getCount() {
return mList.length;
}
- public boolean isViewFromObject(@NonNull View view, @NonNull Object object):
通過instantiateItem()
返回的對(duì)象可以看做是一個(gè) key,這個(gè)方法用來判斷傳入的 View 是否是之前創(chuàng)建的 key,如下:
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
常用的一般就是以上四種回調(diào)方法,理解起來都比較簡(jiǎn)單,其中要注意的是getCount
和isViewFromObject
這兩個(gè)是必須實(shí)現(xiàn)的,而instantiateItem()
和destroyItem()
是可選的,不過大多數(shù)場(chǎng)景還是推薦大家實(shí)現(xiàn) 4 個(gè)回調(diào)方法。
3. ViewPager 完整示例
本節(jié)將通過一個(gè)簡(jiǎn)單的例子學(xué)習(xí) ViewPager 的使用,每一個(gè) Page 將會(huì)通過一個(gè) View 來實(shí)現(xiàn)(在學(xué)習(xí)了 Fragment 之后,可以嘗試將 View 替換成 Fragment)。例子中的每一個(gè) Page 表示一種類別,在切換過程中我們會(huì)接收切換的狀態(tài)回調(diào),同步更新類別標(biāo)題。
3.1 整體布局
由于每個(gè)頁(yè)面都在 ViewPager 中,所以整體的布局非常簡(jiǎn)單,我們只需要放置一個(gè) ViewPager 以及一個(gè) TextView 用來顯示當(dāng)前 Page 的標(biāo)題即可。
<?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 頁(yè)面的布局編寫
對(duì)于整體布局而言,主要的頁(yè)面都在 ViewPager 當(dāng)中,所以我們需要為不同結(jié)構(gòu)的 Page 編寫不同的頁(yè)面,由于本例中每個(gè) Page 的頁(yè)面結(jié)構(gòu)都一樣,所以可以直接復(fù)用一套,直接在里面放置一個(gè) 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 適配器的編寫
適配這一塊主要就是對(duì)四個(gè)回調(diào)接口的實(shí)現(xiàn),其實(shí)在第 2 小節(jié)的描述中已經(jīng)展示了各個(gè)方法的實(shí)現(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 中每個(gè) View 的實(shí)例,設(shè)置類別之后傳遞給 Adpater,綁定的任務(wù)就交由Adpater完成。接下來再監(jiān)聽 ViewPager 的滑動(dòng)狀態(tài)從而判斷當(dāng)前切換的位置,從而實(shí)現(xiàn)同步更新類別標(biāo)題,整體代碼如下:
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]);
}
}
編譯之后效果如下:
通過左右滑動(dòng)可以切換不同的頁(yè)面,每個(gè)頁(yè)面對(duì)應(yīng)的一種水果類別,這樣就通過 ViewPager 實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的頁(yè)面切換效果。
4. 小節(jié)
本節(jié)介紹了 ViewPager 的特點(diǎn)及使用場(chǎng)景,并講解了 ViewPager 的專屬適配器——PagerAdapter 的幾個(gè)回調(diào)函數(shù)的使用方法,最后采用 ViewPager 實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的例子用于切換不同的頁(yè)面從而展示不同的類別。這一節(jié)中是直接采用 View 來承載每一個(gè) Page,而在實(shí)際開發(fā)中大多數(shù)場(chǎng)景會(huì)采用 Fragemnt 來承載 Page,不過對(duì)于 ViewPager 的使用到同小異,針對(duì) Fragment 系統(tǒng)提供了兩種 Adapter:FragmentPageAdapter
和FragmentStatePagerAdapter
,在學(xué)完 Fragement之后大家可以自行修改本節(jié)的例子,通過 Fragment 來實(shí)現(xiàn)本例的效果。