前提
Miwok 应用现在外观已经很美观惊艳!看起来已经创建完毕了,为何还要更改呢?
这在应用开发团队中是有可能发生的。已经构建了第 1 个版本的应用,然后团队决定重新设计并提供更好的用户体验。然后就开始构建第 2 个版本的应用!
作为开发者,需要掌握的重要技能是能够重构代码。在应用中保留相同的功能,但是外观上却看起来不同。在采用新的设计模式的时候,不能破坏任何现有的功能(例如,不能丢失图片或音频播放功能)。当破坏了某些功能,用户无法再像在之前的版本中那样执行某些任务的话,就称之为退步。
为了确保我们没有破坏任何功能,我们可以一次完成一个小小的阶段。尽可能多次在设备上运行下应用。你肯定不希望花 5 天的时间编写出新的代码,然后发现应用不能在设备上运行了。
以下是新的设计模式。我们希望能在单词列表之间滑动。这样我们就不用再点按一次,才能打开单词列表。当我们启动应用后,就可以立即看到单词列表。
但在重构 Miwok 应用之前,将构建一个小型且具有单一的功能的应用,用于测试 新功能,同时了解下 ViewPager 在简单情形下是如何工作的。
在本文结束后,构建的应用应该与下面这个类似:
ViewPager
Viewpager是android开发的一个非常好的特性,它通过水平滑动屏幕来帮助用户从一个视图移动到另一个视图。
ViewPager 是一个布局管理器,它允许用户左右翻转数据页面。它主要出现在 Youtube、Snapchat 等应用中,用户在这些应用中左右切换切换到屏幕。使用的不是 Activity,而是 Fragment。当用户第一次启动应用程序时,它也用于引导用户通过应用程序。
ViewPager 在Android 中 使用片段 Fragment
实现 viewpager 的步骤:
-
将ViewPager小部件添加到XML布局(通常是 main_layout )。
-
通过扩展 FragmentPagerAdapter 或 FragmentStatePagerAdapter 类来创建适配器。
适配器填充Viewpager中的页面。PagerAdapter是由FragmentPagerAdapter和FragmentStatePagerAdapter扩展的基类。
FragmentPagerAdapter和FragmentStatePagerAdapter的区别:
FragmentStatePagerAdapter :只将屏幕上显示的当前片段保存在内存中。这是有效的内存,应该在具有动态片段的应用程序中使用。(碎片的数量是不固定的。)
FragmentPagerAdapter :当片段的数量固定时,应该使用这个适配器。应用程序有3个选项卡,在应用程序运行期间不会改变。
关于 ViewPager 的更多细节本文将不再解释,更多可以参考 文末 参考链接中的 相关文章。
示例编写
1、创建项目
创建一个新的项目在Android Studio从文件 ==> 新项目 ==> 选择 Empty Activity 以此来创建一个具有空模板的 Android项目。
最终,项目目录应该如下所示:
2、ViewPager 布局
ViewPager 的布局一共需要三个小部件,AppBarLayout
用于托管 TabLayout
, TabLayout
负责显示页面标题。ViewPager
布局,将存放不同的片段。下图解释了要设置的重要参数,以使应用程序正常工作。
TabLayout 提供了一个水平布局来显示选项卡。如果使用 TabLayout,那么也使用 Fragment,因为 Fragment 是轻量级的,如果添加更多的 Fragment,应用程序可以在一个屏幕上有更多的功能。每当用户单击标签时,它将导致一个片段到另一个片段的切换。ViewPager用于在选项卡之间滑动。WhatsApp、Facebook等都是使用ViewPager进行 TabLayout的很好的例子。这就是 TabLayout的样子。
在 TabLayout
中,需要添加 tabmode = "fixed"
参数,这将告诉android系统,在我们的应用程序中将有一个固定数量的标签。最终的 activity_main.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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:tabGravity="fill"
app:tabMode="fixed" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
3、创建碎片
在 Android Studio 中创建碎片 Fragment ,File => New => Fragment => Fragment (Blank) => 填写恰当的 Fragment 名称 => Finish,创建步骤如下:
在本文中,将使用三个页面(Fragments),分别为 FragmentFirst
、FragmentSecond
、FragmentThird
,完成后的项目结构应该如下所示:
下面是 FragmentFirst.java
、FragmentSecond.java
、FragmentThird.java
中的代码:
FragmentFirst.java
:
package com.example.viewpagerdemo;
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 FragmentFirst extends Fragment {
public FragmentFirst() {
// required empty public constructor.
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_first, container, false);
}
}
FragmentSecond.java
:
package com.example.viewpagerdemo;
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 FragmentSecond extends Fragment {
public FragmentSecond() {
// required empty public constructor.
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_second, container, false);
}
}
FragmentThird.java
:
package com.example.viewpagerdemo;
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 FragmentThird extends Fragment {
public FragmentThird() {
// required empty public constructor.
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_third, container, false);
}
}
方法描述:
- FragmentFirst(): 空构造函数(默认结构)
- onCreateView( onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState): 该方法负责扩展(解析)各自的XML文件,并返回添加到 ViewPager 适配器的视图。
- onCreate(Bundle SaveInstanceState): 此方法类似于 Activity 当中的
OnCreate()
方法。
设计页面XML文件。所有的片段XML布局都有相同的设计。有一个 TextView 在中心显示相应的 Fragment 的名称,这里使用的根容器是帧布局。
下面是布局文件 fragment_first.xml
、fragment_second.xml
、fragment_third.xml
中的代码。
fragment_first.xml
文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".FragmentFirst">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="First Fragment"
android:textColor="@color/black"
android:textSize="24sp" />
</FrameLayout>
下面是 fragment_second.xml
文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".FragmentSecond">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Second Fragment"
android:textColor="@color/black"
android:textSize="24sp" />
</FrameLayout>
下面是 fragment_third.xml
文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".FragmentThird">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Third Fragment"
android:textColor="@color/black"
android:textSize="24sp" />
</FrameLayout>
4、创建适配器
向项目结构中添加一个名为 ViewPaqerAdapter
的 java类。项目结构如下所示。
ViewPagerAdapter.java
文件的代码如下:
package com.example.viewpagerdemo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import java.util.ArrayList;
import java.util.List;
public class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragments = new ArrayList<>();
private final List<String> fragmentTitle = new ArrayList<>();
public ViewPagerAdapter(@NonNull FragmentManager fm) {
super(fm);
}
public void add(Fragment fragment, String title) {
fragments.add(fragment);
fragmentTitle.add(title);
}
@NonNull
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return fragmentTitle.get(position);
}
}
这个自定义的适配器有两个成员变量,fragments
和 fragmentTitle
分别应用存储 碎片(Fragment 对象)和 每个对象的名称。
add(Fragment fragment, String title)
方法用于 向适配器添加一个 碎片以及该碎片的名称。
5、处理 MainActivity
在MainActivity中,我们需要执行以下步骤。
- 初始化 ViewPager、TabLayout和适配器。
- 在标题旁边加上页码(片段)
- 使用 setupWithiewPager 方法将 TabLayout 链接到Viewpager。
Syntax: TabLayout.setupWithiewPager(ViewPager pager).
Description: The tablayout with the viewpager. The titles of each pager now appears on the tablayout. The user can also navigate throught the fragments by clicking on the tabs.
Parameter:
Viewpager: Used to display the fragments.
下面是 MainActivity.java
文件的代码。
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
public class MainActivity extends AppCompatActivity {
private ViewPagerAdapter viewPagerAdapter;
private ViewPager viewPager;
private TabLayout tabLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.viewpager);
// setting up the adapter
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
// add the fragments
viewPagerAdapter.add(new FragmentFirst(), "First");
viewPagerAdapter.add(new FragmentSecond(), "Second");
viewPagerAdapter.add(new FragmentThird(), "Third");
// Set the adapter
viewPager.setAdapter(viewPagerAdapter);
// The Page (fragment) titles will be displayed in the
// tabLayout hence we need to set the page viewer
// we use the setupWithViewPager().
tabLayout = findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
}
}
- 20 行,ViewPager 视图
- 23,初始化 ViewPagerAdapter 适配器,需要传入一个 FragmentManager 对象,这个对象从
getSupportFragmentManager()
方法获得 - 26 - 28,添加三个 Fragment 对象 给适配器,以及它们的名称
- 31 行,为 ViewPager 视图 绑定适配器
- 36 - 37 行,水平选项卡,用于显示 每个 碎片的标题
6、运行
最终运行效果如下:
总结
ViewPager 的运作方式是从 FragmentPagerAdapter 适配器那获取数据。
对我们来说,我们需要自定义该适配器,以便显示我们自己的 Fragment,因此我们需要使用 FragmentPagerAdapter 的子类。通过继承,我们免费获得了 FragmentPagerAdapter 的所有功能,并且可以在上面添加我们的自定义功能。我们创建一个 ViewPagerAdapter 类,并继承自 FragmentPagerAdapter 类。
当你在设备上启动该应用时,首先 ViewPager 会询问该适配器将有多少页面。对我们来说, 适配器会说有 3 个页面。请参阅 ViewPagerAdapter getCount()
方法。
为了让 ViewPager 能够显示第 0 页,ViewPager 会向适配器请求第 0 个 Fragment。 请参阅 ViewPagerAdapter getItem(int position)
方法。当用户向左 滑动时,我们移到第 1 页,表示 ViewPager 向适配器寻求位置 1 的 Fragment。当我们 转到第 2 页时,ViewPager 会向适配器寻求位置 2 的 Fragment。因此,根据用户滑到的 页面(亦成为位置),应用就会显示相应的 Fragment。
参考
ViewPager | Android Developers
Create swipe views with tabs using ViewPager | Android …
Slide between fragments using ViewPager | Android Developers
Android ViewPager Example Tutorial - JournalDev
ViewPager Using Fragmentsbin Android with Example …
Android ViewPager | ViewPager Android Example - Technxt …
How to implement a TabLayout in Android using ViewPager and Fragments
ViewPager 示例 Github 链接