一、什么是ViewPager?
布局管理器允许左右翻转带数据的页面,你想要显示的视图可以通过实现PagerAdapter来显示。这个类其实是在早期设计和开发的,它的API在后面的更新之中可能会被改变,当它们在新版本之中编译的时候可能还会改变源码。
ViewPager经常用来连接Fragment,它很方便管理每个页面的生命周期,使用ViewPager管理Fragment是标准的适配器实现。最常用的实现一般有FragmentPagerAdapter和FragmentStatePagerAdapter。
ViewPager是android扩展包v4包中的类,这个类可以让我们左右切换当前的view。我们先来聊聊ViewPager的几个相关知识点:
- 1、ViewPager类直接继承了ViewGroup类,因此它一个容器类,可以添加其他的view类
- 2、ViewPager类需要一个PagerAdapter适配器类给它提供数据(这点跟ListView一样需要数据适配器Adater)
- 3、ViewPager经常和Fragment一起使用,并且官方还提供了专门的FragmentPagerAdapterFragmentStatePagerAdapter类供Fragment中的ViewPager使用
二、什么是ViewPager2
原理: viewpager2 内部实现原理是使用recycleview加LinearLayoutManager实现竖直滚动,其实可以理解为对recyclerview的二次封装
private void initialize(Context context, AttributeSet attrs) {
mAccessibilityProvider = sFeatureEnhancedA11yEnabled
? new PageAwareAccessibilityProvider()
: new BasicAccessibilityProvider();
// 实例化recycleview对象
mRecyclerView = new RecyclerViewImpl(context);
mRecyclerView.setId(ViewCompat.generateViewId());
mRecyclerView.setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
// 实例化LinearLayoutManager对象
mLayoutManager = new LinearLayoutManagerImpl(context);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);
setOrientation(context, attrs);
mRecyclerView.setLayoutParams(
new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mRecyclerView.addOnChildAttachStateChangeListener(enforceChildFillListener());
// Create ScrollEventAdapter before attaching PagerSnapHelper to RecyclerView, because the
// attach process calls PagerSnapHelperImpl.findSnapView, which uses the mScrollEventAdapter
mScrollEventAdapter = new ScrollEventAdapter(this);
// Create FakeDrag before attaching PagerSnapHelper, same reason as above
mFakeDragger = new FakeDrag(this, mScrollEventAdapter, mRecyclerView);
mPagerSnapHelper = new PagerSnapHelperImpl();
mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
// Add mScrollEventAdapter after attaching mPagerSnapHelper to mRecyclerView, because we
// don't want to respond on the events sent out during the attach process
mRecyclerView.addOnScrollListener(mScrollEventAdapter);
mPageChangeEventDispatcher = new CompositeOnPageChangeCallback(3);
mScrollEventAdapter.setOnPageChangeCallback(mPageChangeEventDispatcher);
// Callback that updates mCurrentItem after swipes. Also triggered in other cases, but in
// all those cases mCurrentItem will only be overwritten with the same value.
final OnPageChangeCallback currentItemUpdater = new OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
if (mCurrentItem != position) {
mCurrentItem = position;
mAccessibilityProvider.onSetNewCurrentItem();
}
}
@Override
public void onPageScrollStateChanged(int newState) {
if (newState == SCROLL_STATE_IDLE) {
updateCurrentItem();
}
}
};
// Prevents focus from remaining on a no-longer visible page
final OnPageChangeCallback focusClearer = new OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
clearFocus();
if (hasFocus()) { // if clear focus did not succeed
mRecyclerView.requestFocus(View.FOCUS_FORWARD);
}
}
};
// Add currentItemUpdater before mExternalPageChangeCallbacks, because we need to update
// internal state first
mPageChangeEventDispatcher.addOnPageChangeCallback(currentItemUpdater);
mPageChangeEventDispatcher.addOnPageChangeCallback(focusClearer);
// Allow a11y to register its listeners after currentItemUpdater (so it has the
// right data). TODO: replace ordering comments with a test.
mAccessibilityProvider.onInitialize(mPageChangeEventDispatcher, mRecyclerView);
mPageChangeEventDispatcher.addOnPageChangeCallback(mExternalPageChangeCallbacks);
// Add mPageTransformerAdapter after mExternalPageChangeCallbacks, because page transform
// events must be fired after scroll events
mPageTransformerAdapter = new PageTransformerAdapter(mLayoutManager);
mPageChangeEventDispatcher.addOnPageChangeCallback(mPageTransformerAdapter);
attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams());
}
ViewPager2的出现是为了替代ViewPager,它有以下几个优势:
- 支持RTL布局,
- 支持竖向滚动
- 支持notifyDataSetChanged
RTL布局是Right To Left布局也就是从右往左的布局,大家知道我们平常写的布局都是从左往右,但是如果你适配阿拉伯语等环境的UI布局,他们的写法是从右往左的,具体这里不做研究。
API的变动:
FragmentStateAdapter替换了原来的 FragmentStatePagerAdapter
RecyclerView.Adapter替换了原来的 PagerAdapter
registerOnPageChangeCallback替换了原来的 addPageChangeListener
FragmentStateAdapter和FragmentStatePagerAdapter作用相同, 可以用viewpager来管理fragment, 区别在于viewpager2的FragmentStateAdapter与recycleview的生命周期绑定
另外viewpager2的Adapter是继承自recyclerview的adapter, 支持除了notifyDataSetChanged()以外的notifyItemChanged(int position)等方式, 使用上更加的便捷
三、ViewPager2 结合 Fragment实际demo
在app模块的build.gradle里要加上这两个依赖
implementation "androidx.viewpager2:viewpager2:1.1.0-beta02"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
myfragment.xml
一个显示在屏幕正中央的文字mode1
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF9800"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="mode1"
android:textSize="30sp"/>
</LinearLayout>
myfragment2.xml
一个显示在屏幕正中央的文字mode2
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF0000"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="mode2"
android:textSize="30sp"/>
</LinearLayout>
activity_main.xml
一个TabLayout和一个ViewPager2组件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.google.android.material.tabs.TabLayout
android:id="@+id/my_tab"
android:layout_width="match_parent"
android:layout_height="60dp"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/my_pager2"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
MyAdapter.java
重写MyAdapter、createFragment、getItemCount三个方法
package com.example.viewpager2;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.lifecycle.Lifecycle;
import java.util.List;
public class MyAdapter extends FragmentStateAdapter {
List<Fragment> fragments;
public MyAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, List<Fragment> fragments) {
super(fragmentManager, lifecycle);
this.fragments = fragments;
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragments.get(position);
}
@Override
public int getItemCount() {
return fragments.size();
}
}
MyFragment.java
实现第一个fragment页面
package com.example.viewpager2;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class MyFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.myfragment, null);
}
}
MySecondFragment.java
实现第二个fragment页面
package com.example.viewpager2;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class MySecondFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.myfragment2, null);
}
}
MainActivity.java
进来的activity
package com.example.viewpager2;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private TabLayout myTab;
private ViewPager2 myPager2;
List<String> titles=new ArrayList<>();
List<Fragment> fragments=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTab = findViewById(R.id.my_tab);
myPager2 = findViewById(R.id.my_pager2);
//添加标题
titles.add("1");
titles.add("2");
//添加Fragment进去
fragments.add(new MyFragment());
fragments.add(new MySecondFragment());
//实例化适配器
MyAdapter myAdapter=new MyAdapter(getSupportFragmentManager(),getLifecycle(),fragments);
//设置适配器
myPager2.setAdapter(myAdapter);
//TabLayout和Viewpager2进行关联
new TabLayoutMediator(myTab, myPager2, new TabLayoutMediator.TabConfigurationStrategy() {
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
tab.setText(titles.get(position));
}
}).attach();
}
}
ViewPager + Fragment 实现tab页切换不同页面内容的效果
参考:【精选】Android:ViewPager \ViewPager2 简单介绍 & 使用方法解析_viewpager2与viewpager-CSDN博客