花了很长时间研究ViewPager,这篇接上一篇文章,现在我来讲述一下关于ViewPager的标题,ViewPager的标题有两个,PagerTitleStrip和PagerTabStrip,我先上一下两个的区别效果图:
PagerTitleStrip:
PagerTabStrip:
唯一的区别就是下面的那个光标,而使用的方法则都是一样的,首先,我们在ViewPager下面给它添加子控件:
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"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" > <android.support.v4.view.PagerTabStrip android:id="@+id/pagertitle" android:layout_width="wrap_content" android:layout_height="wrap_content"> android.support.v4.view.PagerTabStrip> android.support.v4.view.ViewPager> android.support.constraint.ConstraintLayout>
然后我们紧接着使用上一篇的代码:
package com.example.administrator.testviewpager; import android.graphics.Color; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.view.PagerAdapter; import android.support.v4.view.PagerTitleStrip; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import java.util.ArrayList; import java.util.List; import java.util.zip.Inflater; public class MainActivity extends AppCompatActivity { private ViewPager viewPager; private List<Integer> list = new ArrayList<>(); private List<String> stringList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.viewpager); String[] q = {"1", "2", "3", "4", "5"}; for (String b : q) { stringList.add(b); } list.add(0, Color.BLACK); list.add(1, Color.RED); list.add(2, Color.GRAY); list.add(3, Color.GREEN); list.add(4, Color.MAGENTA); viewPager.setAdapter(new PagerAdapter()); } private class PagerAdapter extends android.support.v4.view.PagerAdapter { @Override public int getCount() { return list.size(); } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { View view = LayoutInflater.from(container.getContext()).inflate(R.layout.layout_one, container, false); RelativeLayout relativeLayout = view.findViewById(R.id.layout); relativeLayout.setBackgroundColor(list.get(position)); Log.d("times", String.valueOf(position)); container.addView(view); return view; } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView((View) object); } @Nullable @Override public CharSequence getPageTitle(int position) { return stringList.get(position); } } }
大家可以发现,我这里只是多了两个地方,第一个就是增加了一个Stringlist,用来储存标题的文字,另一个就是getPagerTitle这个方法,用法和destroyItem的原理一样,然后就可以了
有同学会问了,你这里只介绍了PagerTabStrip啊。还有一个PagerTitleStrip呢?好了别慌。。。要使用PagerTitleStrip,直接把布局文件里面的PagerTabStrip改成PagerTitleStrip就好了,代码都一样,嗯,就这么简单。
然后我们发现,其实在项目中,我们根本不会用这种啊0.0
那么问题来了,我们如何自定义一个title(指示器)呢?我在网上找了一下,有很多方法,但是我都觉得太麻烦,这里给大家介绍一个方法,叫做addOnPageChangeListener,顾名思义是当页面切换时候的监听器,里面有三个方法,先上效果图,后上代码:
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
这个方法适用于当你的页面还是滑动的时候,第一个参数是你滑动页面的下标,第二个是滑动的偏移位置(左右滑动),第三个是偏移量。
public void onPageSelected(int position)
这个方法是当页面滑动完成之后(前一个页面完全消失的时候)
public void onPageScrollStateChanged(int state)
当滑动的值改变的时候
这里我们要使用的当然就是第二个onPageSelected方法,在这之前,我们先在原代码基础上加一点东西:
xml version="1.0" encoding="utf-8"?> <RelativeLayout 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"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"> <android.support.v4.view.PagerTabStrip android:id="@+id/pagertitle" android:layout_width="wrap_content" android:layout_height="wrap_content"> android.support.v4.view.PagerTabStrip> android.support.v4.view.ViewPager> <LinearLayout android:id="@+id/select_system" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:layout_marginEnd="20dp" android:layout_marginStart="20dp" android:orientation="horizontal">LinearLayout> RelativeLayout>
这里我们增加了一个LinearLayout,放在主布局的最下方。
然后:
package com.example.administrator.testviewpager; import android.graphics.Color; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.view.PagerAdapter; import android.support.v4.view.PagerTitleStrip; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import java.util.ArrayList; import java.util.List; import java.util.zip.Inflater; public class MainActivity extends AppCompatActivity { private ViewPager viewPager; private List<Integer> list = new ArrayList<>(); private List<String> stringList = new ArrayList<>(); private LinearLayout linearLayout; private List<View> viewList = new ArrayList<>(); private ImageView imageView; private int oldposition; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.viewpager); linearLayout = findViewById(R.id.select_system); String[] q = {"1", "2", "3", "4", "5"}; for (String b : q) { stringList.add(b); } list.add(0, Color.BLACK); list.add(1, Color.RED); list.add(2, Color.GRAY); list.add(3, Color.GREEN); list.add(4, Color.MAGENTA); for (int i = 0; i < list.size(); i++) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(10, 0, 10, 0); imageView = new ImageView(MainActivity.this); imageView.setLayoutParams(params); imageView.setImageResource(R.drawable.gray_nodown); viewList.add(imageView); linearLayout.addView(imageView); } viewPager.setAdapter(new PagerAdapter()); viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { Log.d("position", String.valueOf(position)); viewList.get(oldposition).setBackgroundResource(R.drawable.gray_nodown); viewList.get(position).setBackgroundResource(R.drawable.green_down); oldposition = position; } @Override public void onPageScrollStateChanged(int state) { } }); } private class PagerAdapter extends android.support.v4.view.PagerAdapter { @Override public int getCount() { return list.size(); } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { View view = LayoutInflater.from(container.getContext()).inflate(R.layout.layout_one, container, false); RelativeLayout relativeLayout = view.findViewById(R.id.layout); relativeLayout.setBackgroundColor(list.get(position)); Log.d("times", String.valueOf(position)); container.addView(view); return view; } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView((View) object); } @Nullable @Override public CharSequence getPageTitle(int position) { return stringList.get(position); } } }
我新加了一个数据源:viewList,这个list用来储存左下角的绿色下标;每个绿色下表都是一个ImageView(这里直接用的原来的代码,所以写的很乱。但是道理很简单,能看懂就好)
首先我们初始化我们创建的LinearLayout,然后我们写(这里代码不是很多,就直接写在onCreat里面):
for (int i = 0; i < list.size(); i++) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(10, 0, 10, 0); imageView = new ImageView(MainActivity.this); imageView.setLayoutParams(params); imageView.setImageResource(R.drawable.gray_nodown); viewList.add(imageView); linearLayout.addView(imageView); }
这里添加与页面数量相同的绿色下标,并且把它们添加到ViewList与linearlayout里面,然后我们在addOnPageChangeListener里面进行设置:
public void onPageSelected(int position) { Log.d("position", String.valueOf(position)); viewList.get(oldposition).setBackgroundResource(R.drawable.gray_nodown); viewList.get(position).setBackgroundResource(R.drawable.green_down); oldposition = position; }
这里的oldposition用于储存上一个imageView的下标(这里简单解释一下:这个oldposition的初始值是空的,每当你滑动一次界面,position就会+1,然后当前的下标变成绿色,给oldposition赋值为现在的position,当你再次滑动的时候,前一个已经变成了绿色的下标就会执行将图片变成未选中状态的操作,然后当前的下标继续变成绿色,以此类推)
这样的话就实现了滑动下标指示器的问题,但是!我们又发现一个东西,刚刚进入程序的时候,第一个界面的下标不会变成选中状态,只有在第二次开始时才会变成选中,效果如下:
其实出现这种情况,我们用log打印出来,发现每次进入程序的时候,position根本不会等于0,当你滑动的时候,position才会开始有值,这个时候,我们将oldposition提为全局变量,然后我们在
public Object instantiateItem(@NonNull ViewGroup container, int position) { View view = LayoutInflater.from(container.getContext()).inflate(R.layout.layout_one, container, false); RelativeLayout relativeLayout = view.findViewById(R.id.layout); relativeLayout.setBackgroundColor(list.get(position)); Log.d("times", String.valueOf(position)); container.addView(view); return view; }
这里加一句:
viewList.get(oldposition).setBackgroundResource(R.drawable.green_down);
就会发现问题解决了,至于原因嘛,你在上面这行代码已经添加了的情况下把
Log.d("position", String.valueOf(position)); viewList.get(oldposition).setBackgroundResource(R.drawable.gray_nodown); viewList.get(position).setBackgroundResource(R.drawable.green_down); oldposition = position;
注视掉,再运行程序就知道了。
是不是发现为什么当前页面左右两个页面的下标都亮了?这是因为PagerAdapter的缓存机制,举个例子:
比如我这里有12345五个页面,当我进入程序的时候,我在第一个界面,但是其实系统已经加载了2,如果你在2,系统则加载了1和3,以此类推,所以你会发现原来打印出来position的值与你当前所在的页面居然不同,也就是因为这个原因啦!而我们在onPageSelected方法中将之前的背景掩盖掉了,所以问题到此就解决啦!
下面我们就要看看如何无限循环(可以说是假无限循环)ViewPager:
在项目中我们都经常看到当ViewPager划到最后一页的时候,再次滑动自动回到第一页,然后当真是自动回到第一页了吗?这个例子在网上很常见,我把它称为假无限循环,其原理就是将getCount中的list.size改为integer.max_value,并且将我们的复制的inedx修改一下:
定义全局变量
private int newposition;
然后修改onPageSelected:
public void onPageSelected(int position) { newposition=position%list.size(); Log.d("position", String.valueOf(position)); viewList.get(oldposition).setBackgroundResource(R.drawable.gray_nodown); viewList.get(newposition).setBackgroundResource(R.drawable.green_down); oldposition = newposition; }
然后:
public Object instantiateItem(@NonNull ViewGroup container, int position) { newposition=position%list.size(); View view = LayoutInflater.from(container.getContext()).inflate(R.layout.layout_one, container, false); RelativeLayout relativeLayout = view.findViewById(R.id.layout); viewList.get(oldposition).setBackgroundResource(R.drawable.green_down); relativeLayout.setBackgroundColor(list.get(newposition)); Log.d("times", String.valueOf(position)); container.addView(view); return view; }
最后:
public CharSequence getPageTitle(int position) { newposition=position%list.size(); return stringList.get(newposition); }
,就可以实现(假)无限循环啦!但是!这个并不是无限循环,因为int的最大值2147483647,所以当你想划到第2147483648个页面的时候,你会发现划不动了(这里有朋友估计会说,博主你是XX?谁特么没事划这么多次),当然我们正常人不会划这么多次,但是为了追求完善,我将会在实践之后在后面的代码中更新真的无限循环方法。
我是分割线---------------------------------------------------------------------------------------------------------------------------------------
昨天尝试了一下真的无限循环方式,但结果却让人很不满意,先公布效果图:
这里因为索引越界问题避免麻烦我将下标的代码屏蔽了,大家可以看到这里出现了一个问题,当我第二次划到第一个页面的时候,ViewPager自动跳转到了第一个页面去,这个不友好跳转让体验特别差,但这应该是ViewPager的机制问题,我先来说一下思路,
首先我们将最后一页添加到第一页,再将第一页(现在的第二页)添加到最后一页,画个图应该是这样的:
这样的话实际上是有五个界面的,当我们处于黑色的第三个界面时(position等于3时)使用viewpager的setcurrentItem(0),将其跳转到红色的3界面上去,当界面处于黑色1的时候,也使用setcurrentItem(4)跳转到红色1上面去,虽然这样是实现了无限循环,可是页面会出现刚才那样的自动返回情况。代码这里就不发布了。