ViewPager2在 Android 中的正确实现

我开始了解 ViewPager2并试图实现它,但没有找到任何适当的例子。

有人能告诉我怎么用吗。

我在寻找正确的用法,而不是一个例子。

183068 次浏览

ViewPager2在 Android 中的使用

发展商网站台提到过

空气污染指数变化

FragmentStateAdapter 取代 FragmentStatePagerAdapter

适配器取代 PagerAdapter

RegisterOnPageChangeCallback 取代 addPageChangeListener

在简单的词汇,他们使它查看寻呼机适配器工作喜欢回收查看适配器。

注意:-我们不需要在视图寻呼机2中使用片段。它完全依赖于回收视图。适配器膨胀布局。

这里是样本 gitHub 回购 林克

例子:-

MainActivity.class

public class MainActivity extends AppCompatActivity {
    

private ViewPager2 mPager;
    

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().setTitle("View Pager 2");
mPager = findViewById(R.id.pager);
mPager.setAdapter(new MyViewPagerAdapter(this));
}
    

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
    

@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (R.id.change == item.getItemId()) {
mPager.setOrientation(mPager.getOrientation() != ViewPager2.ORIENTATION_VERTICAL ? ViewPager2.ORIENTATION_VERTICAL : ViewPager2.ORIENTATION_HORIZONTAL);
}
return super.onOptionsItemSelected(item);
}
}

Activity _ main. xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
tools:context=".MainActivity">


<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />


</android.support.constraint.ConstraintLayout>

public class MyViewPagerAdapter extends RecyclerView.Adapter<MyHolder> {
    

private Context context;
    

public MyViewPagerAdapter(Context context) {
this.context=context;
}
    

@NonNull
@Override
public MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyHolder(LayoutInflater.from(context).inflate(R.layout.cell_item, parent, false));
}
    

@Override
public void onBindViewHolder(@NonNull MyHolder holder, int position) {
holder.mText.setText("Page "+(position+1));
}
    

@Override
public int getItemCount() {
return 10;
}
}

Cell _ item. xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">


<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Page 1"
android:textSize="20sp" />


</android.support.constraint.ConstraintLayout>

MyHolder 同学

class MyHolder extends RecyclerView.ViewHolder {
    

public TextView mText;
    

public MyHolder(@NonNull View itemView) {
super(itemView);
mText = itemView.findViewById(R.id.text);
}
}

产出:

enter image description here

更新7

检查: 从 ViewPager 迁移到 ViewPager2

检查: 使用 ViewPager2创建带选项卡的滑动视图

更新6

看看 如果你想使用 View Pager2实现 Carousel,我的回答是

更新5

如何在 ViewPager2中使用 TabLayout

样本代码

使用下面的 dependencies

implementation 'com.google.android.material:material:1.1.0-alpha08'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'

样本代码

XML 布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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">


<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">


<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>


<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.appbar.AppBarLayout>


<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
app:layout_anchor="@id/tabs"
app:layout_anchorGravity="bottom"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>




</androidx.coordinatorlayout.widget.CoordinatorLayout>

活动

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import com.google.android.material.tabs.TabLayoutMediator


import com.google.android.material.tabs.TabLayout




class MainActivity : AppCompatActivity() {


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)


//        setSupportActionBar(toolbar)
viewpager.adapter = AppViewPagerAdapter(supportFragmentManager, lifecycle)


TabLayoutMediator(tabs, viewpager, object : TabLayoutMediator.OnConfigureTabCallback {
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
// Styling each tab here
tab.text = "Tab $position"
}
}).attach()




}
}

输出

使用 ViewPager2的 TabLayout

文件

ViewPager2

新功能

  • 右向左(RTL)布局支持
  • 垂直方向支撑
  • NotifyDataSetChanged 完全功能化

空气污染指数变化

  • FragmentStateAdapter代替 FragmentStatePagerAdapter
  • RecyclerView.Adapter代替 PagerAdapter
  • registerOnPageChangeCallback代替 addPageChangeListener

样本代码

ViewPager2添加最新的 dependencies

implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha01'

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">


<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />


</LinearLayout>

活动

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;


import java.util.ArrayList;


public class MyActivity extends AppCompatActivity {


ViewPager2 myViewPager2;
MyAdapter MyAdapter;
private ArrayList<String> arrayList = new ArrayList<>();


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);


myViewPager2 = findViewById(R.id.view_pager);


arrayList.add("Item 1");
arrayList.add("Item 2");
arrayList.add("Item 3");
arrayList.add("Item 4");
arrayList.add("Item 5");


MyAdapter = new MyAdapter(this, arrayList);




myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);


myViewPager2.setAdapter(MyAdapter);
}
}

MyAdapter

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;


import java.util.ArrayList;


public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {


private Context context;
private ArrayList<String> arrayList = new ArrayList<>();


public MyAdapter(Context context, ArrayList<String> arrayList) {
this.context = context;
this.arrayList = arrayList;
}


@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
return new MyViewHolder(view);
}


@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.tvName.setText(arrayList.get(position));
}


@Override
public int getItemCount() {
return arrayList.size();
}


public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tvName;


public MyViewHolder(@NonNull View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tvName);
}
}
}

新功能

现在我们需要使用 ViewPager2.OnPageChangeCallback()得到 ViewPager2的滑动事件

样本代码

    myViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}


@Override
public void onPageSelected(int position) {
super.onPageSelected(position);


Log.e("Selected_Page", String.valueOf(position));
}


@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});

我们可以使用 myViewPager2.setOrientation()设置方向

样本代码

用于 HORIZONTAL Orientation

myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);

用于 VERTICAL Orientation

myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);

我们可以像在 RecyclerView.Adapter中一样使用 notifyDataSetChanged

添加新项的示例代码

    btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
arrayList.add("New ITEM ADDED");
MyAdapter.notifyDataSetChanged();
}
});

删除新项的样本代码

    btnRemove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
arrayList.remove(3);
MyAdapter.notifyItemRemoved(3);
}
});

更新

如果您想将 FragmentViewPager2一起使用,请尝试这样做

首先创建一个扩展 FragmentStateAdapterViewPagerFragmentAdapter

import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager2.adapter.FragmentStateAdapter;


public class ViewPagerFragmentAdapter extends FragmentStateAdapter {


private ArrayList<Fragment> arrayList = new ArrayList<>();


public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager) {
super(fragmentManager);
}


@NonNull
@Override
public Fragment getItem(int position) {
return arrayList.get(position);
}


public void addFragment(Fragment fragment) {
arrayList.add(fragment);
}


@Override
public int getItemCount() {
return arrayList.size();
}
}

现在像这样在你的活动中使用

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;


public class MainActivity extends AppCompatActivity {


ViewPager2 myViewPager2;
ViewPagerFragmentAdapter myAdapter;


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


myViewPager2 = findViewById(R.id.view_pager);


myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager());


// add Fragments in your ViewPagerFragmentAdapter class
myAdapter.addFragment(new FragmentOne());
myAdapter.addFragment(new Fragmenttwo());
myAdapter.addFragment(new FragmentThree());


// set Orientation in your ViewPager2
myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);


myViewPager2.setAdapter(myAdapter);


}


}

更多信息请看这个

更新2

版本1.0.0-alpha02

新功能

  • 禁用用户输入的能力(setUserInputEnabledisUserInputEnabled)

空气污染指数变化

  • ViewPager2级期末考试

修复漏洞

  • FragmentStateAdapter稳定性修复

禁止在 viewpager2中滑动的样本代码

myViewPager2.setUserInputEnabled(false);// SAMPLE CODE to disable swiping in viewpager2




myViewPager2.setUserInputEnabled(true);//SAMPLE CODE to enable swiping in viewpager2

更新3

版本1.0.0-alpha03

新功能

  • 能够以编程方式滚动 ViewPager2: FakeDragBy (offsetPx)

空气污染指数变化

  • FragmentStateAdapter现在需要一个 Lifecycle对象。添加了两个实用程序构造函数,以便从主机 FragmentActivity或主机片段获取它

样本代码

ViewPagerFragmentAdapter

import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;


public class ViewPagerFragmentAdapter extends FragmentStateAdapter {


private ArrayList<Fragment> arrayList = new ArrayList<>();




public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}


@NonNull
@Override
public Fragment getItem(int position) {
return arrayList.get(position);
}


public void addFragment(Fragment fragment) {
arrayList.add(fragment);
}


@Override
public int getItemCount() {
return arrayList.size();
}
}

主活动代码

import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;


public class MainActivity extends AppCompatActivity {


ViewPager2 myViewPager2;
ViewPagerFragmentAdapter myAdapter;


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


myViewPager2=findViewById(R.id.view_pager);
myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager(), getLifecycle());


// add Fragments in your ViewPagerFragmentAdapter class
myAdapter.addFragment(new FragmentOne());
myAdapter.addFragment(new Fragmenttwo());
myAdapter.addFragment(new FragmentThree());


myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);


myViewPager2.setAdapter(myAdapter);
}
}

更新4

Version 1.0.0-alpha05 新功能

  • 引入的 ItemDecorator的行为与 RecyclerView一致。
  • 引入了 MarginPageTransformer,以提供在页面之间创建空间(页面插入之外)的能力。
  • 引入 CompositePageTransformer是为了提供组合多个 PageTransformers的能力

空气污染指数变化

  • FragmentStateAdapter#getItem方法重命名为 FragmentStateAdapter#createFragment-以前的方法名称已被证明是一个错误的来源在过去。
  • OFFSCREEN_PAGE_LIMIT_DEFAULT值从0更改为 -1。如果使用 OFFSCREEN_PAGE_LIMIT_DEFAULTconstant,则不需要更改客户端代码。

样本代码

行动代码

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.MarginPageTransformer;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;


public class MainActivity extends AppCompatActivity {


ViewPager2 myViewPager2;
ViewPagerFragmentAdapter myAdapter;
private ArrayList<Fragment> arrayList = new ArrayList<>();


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


myViewPager2 = findViewById(R.id.myViewPager2);


// add Fragments in your ViewPagerFragmentAdapter class
arrayList.add(new FragmentOne());
arrayList.add(new Fragmenttwo());
arrayList.add(new FragmentThree());


myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager(), getLifecycle());
// set Orientation in your ViewPager2
myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);


myViewPager2.setAdapter(myAdapter);


myViewPager2.setPageTransformer(new MarginPageTransformer(1500));




}
}

ViewPagerFragmentAdapter

import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;


public class ViewPagerFragmentAdapter extends FragmentStateAdapter {


private ArrayList<Fragment> arrayList = new ArrayList<>();




public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}


@NonNull
@Override
public Fragment createFragment(int position) {
switch (position) {
case 0:
return new FragmentOne();
case 1:
return new Fragmenttwo();
case 2:
return new FragmentThree();


}
return null;
}


@Override
public int getItemCount() {
return 3;
}
}

实际上,现在有一个官方的样品回购 ViewPager2(链接如下)

回购包含以下样品(引自下面的回购自述)

样本

  • 视图 ViewPager2-演示如何设置视图为页面的 ViewPager2
  • 使用片段的 ViewPager2-演示如何设置将片段作为页面的 ViewPager2
  • 带有可变集合(视图)的 ViewPager2-演示如何使用 ViewPager2将视图作为页面并在页面适配器中进行变换
  • 可变集合(片段)的 ViewPager2-演示了将片段用作页面的 ViewPager2的使用,以及 页面适配器
  • 使用 TabLayout (视图)的 ViewPager2-演示如何设置将视图作为页面的 ViewPager2,并将其链接到 TabLayout

在 Kotlin 使用碎片的 ViewPager2的简单例子

Activity _ main. xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">


<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager2_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>


</LinearLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)




val viewPager2 = findViewById<ViewPager2>(R.id.pager2_container)


val fragmentList = arrayListOf(
FirstFragment.newInstance(),
SecondFragment.newInstance(),
ThirdFragment.newInstance()
)
viewPager2.adapter = ViewPagerAdapter(this, fragmentList)
}
}

FirstFragment.kt (SecondFragment.ktThirdFragment.kt看起来类似于 FirstFragment.kt)

class FirstFragment : Fragment() {


override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_first, container, false)
}


companion object{
fun newInstance() = FirstFragment()
}
}

ViewPagerAdapter.kt

class ViewPagerAdapter(fa:FragmentActivity, private val fragments:ArrayList<Fragment>): FragmentStateAdapter(fa) {


override fun getItemCount(): Int = fragments.size


override fun createFragment(position: Int): Fragment = fragments[position]


}

其他一些有用的资源:

以下是我的解决方案(Android Studio 3.6) :

在 app/build.gradle 中:

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') { transitive = true; }
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.1.0-beta01'
implementation 'org.altbeacon:android-beacon-library:2.16.3'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta05'


testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "androidx.core:core-ktx:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

我的活动如下:

import android.os.Bundle;


import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;


import java.util.ArrayList;
import java.util.List;


import com.myproject.android.R;
import com.myproject.android.adapter.CustomFragmentStateAdapter;
import com.myproject.android.ui.fragment.BluetoothPageFragment;
import com.myproject.android.ui.fragment.QrPageFragment;


public class QRBluetoothSwipeActivity extends AppCompatActivity {
private ViewPager2 myViewPager2;
private CustomFragmentStateAdapter myAdapter;


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// Make sure this is before calling super.onCreate
setTheme(R.style.AppTheme); // show splash screen
super.onCreate(savedInstanceState);
setContentView(R.layout.qr_bluetooth_swipe_activity);
init();
}


private void init() {
List<Fragment> fragmentList = new ArrayList<Fragment>();
QrPageFragment m1 = new QrPageFragment();
BluetoothPageFragment m2 = new BluetoothPageFragment();
myViewPager2 = findViewById(R.id.viewPager2);
fragmentList.add(m2);
fragmentList.add(m1);
myAdapter = new CustomFragmentStateAdapter(this, fragmentList);
myViewPager2.setAdapter(myAdapter);
}
}

这里的布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".ui.actviity.SplashDelayActivity">


<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="match_parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

这是我的 CustomFragmentStateAdapter

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;


import org.jetbrains.annotations.NotNull;


import java.util.ArrayList;
import java.util.List;


public class CustomFragmentStateAdapter extends FragmentStateAdapter {
private List<Fragment> listFragment = new ArrayList<>();


public CustomFragmentStateAdapter(FragmentActivity fa, List<Fragment> list) {
super(fa);
listFragment = list;
}


@NotNull
@Override
public Fragment createFragment(int position) {
return listFragment.get(position);
}


@Override
public int getItemCount() {
return 2;
}
}

这是我的片段:

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;


import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;


import com.myproject.android.R;


public class BluetoothPageFragment extends Fragment {


@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.bluetooth_page_fragment, container, false);
}


}

第二部分:

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;


import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;


import com.myproject.android.R;


public class QrPageFragment extends Fragment {


@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.qr_page_fragment, container, false);
}


}

因此,现在我使用 androidx.viewpager2.widget.ViewPager2和我的定制片段。

是工作! ! !

不错嘛。

附注: 另一种工具:

public class CustomFragmentStateAdapter extends FragmentStateAdapter {
private ArrayList<Fragment> arrayList = new ArrayList<>();


public CustomFragmentStateAdapter (FragmentActivity fa) {
super(fa);
}


public void addFragment(Fragment fragment) {
arrayList.add(fragment);
}


@Override
public int getItemCount() {
return arrayList.size();
}


@NonNull
@Override
public Fragment createFragment(int position) {
// return your fragment that corresponds to this 'position'
return arrayList.get(position);
}
}

像这样使用(在活动中) :

 myViewPager2 = findViewById(R.id.viewPager2);
myAdapter = new CustomFragmentStateAdapter (this);
myAdapter.addFragment(new QrPageFragment());
myAdapter.addFragment(new BluetoothPageFragment());
myViewPager2.setAdapter(myAdapter);

下面是我用 TabLayout 实现 ViewPager2的3个片段完整示例:

布局包含 ViewPager2TabLayout:

 <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/include3">


<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:background="@color/colorPrimary"
app:tabTextColor="@color/tab_dismiss_color"
app:tabSelectedTextColor="@color/green"
android:layout_height="wrap_content" />


<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:background="#e4e4e4" />


<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

Init ViewPager2并设置 Tabs Name:

 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_report);
ButterKnife.bind(this);
actionBarTitleId.setText(R.string.reports);


viewPager.setAdapter(new ViewPagerAdapter(this));


new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
switch (position) {
case 0:
tab.setText(R.string.financial_duty_str);
break;
case 1:
tab.setText(R.string.financial_unpaid_str);
break;
case 2:
tab.setText(R.string.financial_paid_str);
break;


}
}).attach();


}

ViewPager2适配器:

public class ViewPagerAdapter extends FragmentStateAdapter {




public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}


@NonNull
@Override
public Fragment createFragment(int position) {
switch (position) {
case 0:
return new FinancialFragment();
case 1:
return new FinancialUnPaidFragment();
case 2:
return new FinancialPaidFragment();
default:
return null;


}
}


@Override
public int getItemCount() {
return 3;
}

使用依赖关系:

implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation 'com.google.android.material:material:1.1.0-alpha10'

如何使用它解释得很清楚。让我给出一些关于 ViewPager2的小的但是非常重要的提示和细节,特别是如果它在一个关于如何防止内存泄漏的片段中

  1. 不要使用接受片段的构造函数,特别是在使用 TabLayout时。

    public FragmentStateAdapter(@NonNull Fragment fragment) {
    this(fragment.getChildFragmentManager(), fragment.getLifecycle());
    }
    

因为它存在 给你描述的内存泄漏风险

取而代之的是使用一个带有 FragmentManager 和 LifeCycle 的。并且不要发送片段的 lifeCycle作为参数,使用 viewLifeCycleOwner的生命周期,因为 viewLifeCycleOwner 代表片段的 view的生命周期。

 class NavigableFragmentStateAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, lifecycle) {




}

并在 onCreateView中设置适配器

viewPager.adapter =
NavigableFragmentStateAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
  1. 如果当 ViewPager2在片段中时使用 TabLayout,不要忘记分离 TabLayout 并将 ViewPager2的适配器设置为

DetachTabLayoutMediator,因为它在片段中时会导致内存泄漏

Https://stackoverflow.com/questions/61779776/leak-canary-detects-memory-leaks-for-tablayout-with-viewpager2

片段中的 ViewPager2在通过导航组件导航替换片段后会泄漏

TabLayoutMediator(tabLayout, viewPager2, tabConfigurationStrategy).detach()
viewPager2.adapter = null

片段内 onDestroyView

  1. 如果您希望使用 ViewPager 页面作为带导航组件的 navHostFragment,以导航回到子片段,请将 FragmentStateAdapter中的 FragmentTransactionCallback 注册为

    private val fragmentTransactionCallback =
    object : FragmentStateAdapter.FragmentTransactionCallback() {
    override fun onFragmentMaxLifecyclePreUpdated(
    fragment: Fragment,
    maxLifecycleState: Lifecycle.State
    ) = if (maxLifecycleState == Lifecycle.State.RESUMED) {
    
    
    // This fragment is becoming the active Fragment - set it to
    // the primary navigation fragment in the OnPostEventListener
    OnPostEventListener {
    fragment.parentFragmentManager.commitNow {
    setPrimaryNavigationFragment(fragment)
    }
    }
    } else {
    super.onFragmentMaxLifecyclePreUpdated(fragment, maxLifecycleState)
    }
    }
    
    
    init {
    // Add a FragmentTransactionCallback to handle changing
    // the primary navigation fragment
    registerFragmentTransactionCallback(fragmentTransactionCallback)
    }
    

此外,如果您希望看到一些示例如何使用导航组件,动态功能模块 ViewPager2和 BottomNavigationView 相结合,您可以查看教程 在这个 Github 回收站。

我首先创建了一个视图页导航器,然后使用以下指令迁移到了视图页导航器2: Https://developer.android.com/training/animation/vp2-migration

这是正确的实现!

typealias FragmentBuilder = () -> Fragment


class MyAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, lifecycle) {


private val fragmentBuilders = mutableListOf<FragmentBuilder>()


fun add(fragmentBuilder: FragmentBuilder) {
fragmentBuilders.add(fragmentBuilder)
}


/**
* Dynamic replacement of fragments
*/
fun set(position: Int, fragmentBuilder: FragmentBuilder) {
fragmentBuilders[position] = fragmentBuilder
}


override fun getItemCount() = fragmentBuilders.size


override fun createFragment(position: Int) = fragmentBuilders[position].invoke()


}

别谢我

以下是@sushildlh answer 在 Kotlin 的 Kotlin 版本实现。
在这段代码中,我实现 viewpager2与回收视图查看图像。 而且我正在使用视图绑定

Food _ Details. xml

<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".presentation.recipeitem.RecipeDetailsFragment">




<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2"
android:layout_width="0dp"
android:layout_height="300dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
...

膨胀这个 xml 的片段

@AndroidEntryPoint
class RecipeDetailsFragment : Fragment(R.layout.fragment_recipe_details) {


private val viewModel: RecipeItemViewModel by viewModels()
private val viewBinding: FragmentRecipeDetailsBinding by viewBinding()


override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bindRecipeData(//object of data that is to be desplayed)
}


private fun bindRecipeData(recipeDetailedInfo: RecipeDetailedInfo?) {
recipeDetailedInfo?.let {
with(viewBinding) {
viewPager2.adapter = ViewPagerAdapter(it.images)
viewPager2.setPageTransformer(ZoomOutPageTransformer())
lifecycleScope.launchWhenCreated {
delay(500)
viewPager2.setCurrentItem(if (it.images.size >= 2) 1 else 0, true)
}
}
}
}....

在片段中,我创建适配器的对象,并直接发送包含图像 URL 的字符串列表

这是视图页面适配器,它基本上是一个普通的回收视图适配器

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.blogspot.soyamr.recipes2.databinding.ImageviewBinding
import com.squareup.picasso.Picasso


class ViewPagerAdapter(private val images: List<String>) :
RecyclerView.Adapter<ViewPagerAdapter.ImageViewHolder>() {




class ImageViewHolder(private val imageViewBinding: ImageviewBinding) :
RecyclerView.ViewHolder(imageViewBinding.root) {
fun bind(imageLink: String) {
Picasso.get().load(imageLink).into(imageViewBinding.root)
}


companion object {
fun from(parent: ViewGroup): ImageViewHolder {
val itemBinding =
ImageviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ImageViewHolder(itemBinding)
}
}
}


override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
return ImageViewHolder.from(parent)
}


override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
holder.bind(images[position])
}


override fun getItemCount(): Int = images.size


}

在回收视图中,我正在膨胀这个 imageview.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.imageview.ShapeableImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:scaleType="centerCrop"
android:theme="@style/roundedImageView"
/>

如果需要,可以使用复杂的 xml 视图

简单的例子,两个不同的片段,usin 制表符。主要布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/mainConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">


<androidx.viewpager2.widget.ViewPager2
android:id="@+id/mainViewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mainTabLayout">


</androidx.viewpager2.widget.ViewPager2>


<com.google.android.material.tabs.TabLayout
android:id="@+id/mainTabLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height"
android:background="@color/brown_normal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tabIndicatorColor="@color/yellow_light"
app:tabIndicatorHeight="3dp"
app:tabSelectedTextColor="@color/yellow_light"
app:tabTextColor="@color/yellow_normal"
tools:ignore="SpeakableTextPresentCheck" />
</androidx.constraintlayout.widget.ConstraintLayout>

适配器:

public class MainToolsAdapter extends FragmentStateAdapter
{
// The quantity of tab is fixed
private static final int FRAGMENT_COUNT = 2;
// Titles for each tab
private final String[] titles = new String[FRAGMENT_COUNT];


public MainToolsAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, Context context)
{
super(fragmentManager, lifecycle);
// Load titles for tab from resourses
titles[0] = context.getResources().getString(R.string.tab_1);
titles[1] = context.getResources().getString(R.string.tab_2);
}


@NonNull
@Override
public Fragment createFragment(int position)
{
// Create fragments according to position
if(position == 0)
{
return new FragmentTabOne();
}
return new FragmentTabTwo();
}


@Override
public int getItemCount()
{
return FRAGMENT_COUNT;
}


public String getItemTitle(int position)
{
if(position == 0)
{
return titles[0];
}
return titles[1];
}
}

主要活动:

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


// Create adapter for ViewPager
mainToolsAdapter = new MainToolsAdapter(getSupportFragmentManager(), getLifecycle(), this);


// Set adapter to the ViewPager
viewPager = findViewById(R.id.mainViewPager);
viewPager.setAdapter(mainToolsAdapter);


tabLayout = findViewById(R.id.mainTabLayout);


// Create the TabLayoutMediator to asociate ViewPager2 to TabLayout
TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.TabConfigurationStrategy()
{
@Override
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position)
{
// When a tab is created this is called, then we can set tab properties, in this case the text
tab.setText(mainToolsAdapter.getItemTitle(position));
}
});
// After configure we need to realice attach then Tab and ViewPager2 are asociated
tabLayoutMediator.attach();
}

分级:

plugins {
id 'com.android.application'
}


android {
compileSdk 30


defaultConfig {
applicationId "com.xxxx.yyyy"
minSdk 19
targetSdk 30
versionCode 1
versionName "1.0"


testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}


buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}


dependencies
{
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')


implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.13.2'


androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

以防有人想听 ViewPager2.OnPageChangeCallback事件:

private final ViewPager2.OnPageChangeCallback onPageChangeListener = new ViewPager2.OnPageChangeCallback() {


/**
* This method will be invoked when the current page is scrolled, either as part
* of a programmatically initiated smooth scroll or a user initiated touch scroll.
* @param position             Position index of the first page currently being displayed.
*                             Page position+1 will be visible if positionOffset is nonzero.
* @param positionOffset       Value from [0, 1) indicating the offset from the page at position.
* @param positionOffsetPixels Value in pixels indicating the offset from position.
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}


/**
* This method will be invoked when a new page becomes selected.
* Animation is not necessarily complete.
* @param position             Position index of the first page currently being displayed.
*                             Page position+1 will be visible if positionOffset is nonzero.
*/
@Override
public void onPageSelected (int position) {
super.onPageSelected(position);
if (position == 2) { // SomeFragment
Log.d(LOG_TAG, "ViewPager2.onPageSelected( " + position + " )");
SomePagerAdapter adapter = (SomePagerAdapter) viewpager.getAdapter();
if (adapter != null) {
SomeFragment fragment = (SomeFragment) adapter.getItem(position);
fragment.onLateInit();
}
}
}
};

应用于 ViewPager2.registerOnPageChangeCallback():

viewpager.registerOnPageChangeCallback(this.onPageChangeListener);

FragmentStateAdapter需要保存一个 ArrayList<Fragment> mItems,这样就可以访问提前构建的实例。SomeFragment暴露方法 public void onLateInit(),因为 @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)可能不能被使用(取决于 Fragment)。

public Fragment getItem(int position) {
return this.mItems.get(position);
}

与这个问题类似,在正确初始化视图之前很长一段时间内,Fragment都在构建什么,例如在前一个 Fragment中输入数据。对于大量的 Fragment(...)来说,这可能不是最佳选择,但是对于少数人来说,它的效果非常好。

我个人在一个片段中使用了 ViewPager2,我是这样做的。 GitHub 上的示例代码[ https://GitHub.com/codebyjames/Example-using-ViewPager2-slide-page-adapter ]

首先是 onCreate

        // pager adapter
val pagerAdapter = ScreenSlidePageAdapter(this@ManagerFragment)
viewPager.adapter = pagerAdapter
viewPager.setPageTransformer(ZoomOutPageTransformer())

ViewPager 适配器

class ScreenSlidePageAdapter(val fragment: Fragment): FragmentStateAdapter(fragment) {


val fragments = listOf(WalkThroughFragment(), PermissionsFragment(), DatastoreFragment())


override fun getItemCount(): Int {
return fragments.size
}


override fun createFragment(position: Int): Fragment {
return fragments[position]
}

}

ViewPager2的转换-可选

class ZoomOutPageTransformer : ViewPager2.PageTransformer {


override fun transformPage(view: View, position: Float) {
view.apply {
val pageWidth = width
val pageHeight = height
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
alpha = 0f
}
position <= 1 -> { // [-1,1]
// Modify the default slide transition to shrink the page as well
val scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position))
val vertMargin = pageHeight * (1 - scaleFactor) / 2
val horzMargin = pageWidth * (1 - scaleFactor) / 2
translationX = if (position < 0) {
horzMargin - vertMargin / 2
} else {
horzMargin + vertMargin / 2
}


// Scale the page down (between MIN_SCALE and 1)
scaleX = scaleFactor
scaleY = scaleFactor


// Fade the page relative to its size.
alpha = (MIN_ALPHA +
(((scaleFactor - MIN_SCALE) / (1 - MIN_SCALE)) * (1 - MIN_ALPHA)))
}
else -> { // (1,+Infinity]
// This page is way off-screen to the right.
alpha = 0f
}
}
}
}

}

我的 ManagerFragment 布局(包含 ViewPager2)

<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />