ViewPager是Android扩展包v4包中的一个类,允许用户左右滑动切换显示不同的view。类似RecyclerView,需要一个PagerAdapter适配器提供显示的数据,通常可以和Fragment一起使用,即碎片,且专门提供了FragmentPagerAdapter和FragmentStatePagerAdapter类给Fragment使用。
通过一个简单demo看看具体是怎么使用的。
首先修改activity_main.xml,添加一个ViewPager控件。
<android.support.v4.view.ViewPager
android:id="@+id/ShowView"
android:layout_width="match_parent"
android:layout_height="match_parent">android.support.v4.view.ViewPager>
创建布局文件viewpager_item.xml,由于只是一个简单demo,因此只需要一个TextView控件即可。
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/ViewContent"
android:layout_gravity="center"
android:gravity="center"
android:text="ss"
xmlns:android="http://schemas.android.com/apk/res/android" />
创建适配器类MyViewPagerAdapter,这个适配器也比较简单,主要就是instantiateItem()方法,在这里面对View子项的内容进行设置,我这里是先加载布局,然后获取TextView控件,设置文字然后add到container中,返回view即可。
public class MyPagerAdapter extends PagerAdapter {
private Context MyContext;
private List<String> MyTextList;
public MyPagerAdapter(Context context,List<String> TextList){
this.MyContext=context;
this.MyTextList=TextList;
}
@Override
public int getCount() {
return MyTextList.size();
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view=View.inflate(MyContext,R.layout.viewpager_item,null);
TextView ViewContentTextView=view.findViewById(R.id.ViewContent);
ViewContentTextView.setText(MyTextList.get(position));
container.addView(view);
return view;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
最后修改MainActivity.java中的onCreate()方法,就是获取ViewPager实例,然后实例化适配器传入桉树,设置适配器即可。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<String> m= new ArrayList<>();
for (int i=0;i<5;i++){
m.add("第 "+i+" 个view");
}
ViewPager MyViewPager=findViewById(R.id.ShowView);
MyViewPager.setAdapter(new MyPagerAdapter(this,m));
}
新建MyPagerTransformer继承PagerTransformer类
public class MyPagerTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
// [-Infinity,-1)
if (position < -1) {
// This page is way off-screen to the left.
view.setAlpha(0);
}
// [-1,0]
else if (position <= 0) {
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
}
// (0,1]
else if (position <= 1) {
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
}
// (1,+Infinity]
else {
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
在MainActivity.java中设置一下,加一行就行了
MyViewPager.setPageTransformer(false,new MyPagerTransformer());
运行程序后可以看到动画效果了,淡入淡出的动画效果。
这个动画好像有些开源框架提供好多种动画效果。
调用SetOnPageChangeListener()方法设置监听,但是我的as显示这个方法已经过时了,改成addOnPageChangeListener即可,可以监听滑动的整个过程、滑动的每个状态以及改变的是哪个Page。
新建一个自定义过滤器就可以更方便地看到日志输出。
MyViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.d("ViewPagerListener", "滑动中 position:" + position + " positionOffset:" + positionOffset + " positionOffsetPixels:" + positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
Log.d("ViewPagerListener","改变了 "+position);
}
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
case ViewPager.SCROLL_STATE_IDLE:
Log.d("ViewPagerListener", "静止中");
break;
case ViewPager.SCROLL_STATE_DRAGGING:
Log.d("ViewPagerListener", "滑动中");
break;
case ViewPager.SCROLL_STATE_SETTLING:
Log.d("ViewPagerListener", "结束");
break;
}
}
});
假设当前有3个View,无限滚动即在两边各添加一个View,比如原有123,在左右添加一个View,当滑到最左边的时候,切换到3,滑到最右边的时候,切换到position=0的view,就实现无限滚动了。
因为是切换view成功后才会切换到第一个或最后一个view,因此最好在左侧添加最后一个view,右侧添加第一个view,这样可以在视觉上有更好的效果。
即view3 - view1 - view2 - view3 - view1
...
if (position==4)
MyViewPager.setCurrentItem(1,false);
...
if (position==0)
MyViewPager.setCurrentItem(3,false);
...
自动滚动的逻辑大概是如下两条
这里我使用的是Handler处理定时任务,在MainActivity.java中编写内部类ViewPagerTimer类,继承自Runnable类,其逻辑主要就是获取当前view编号,延迟一定时间,然后切换到下一个view。
调用Handler的postDelayed()方法进行延迟,时间单位是毫秒。
要记得在MainActivity.java中的onDestroy()方法中设置ViewPagerTimerHandler=null
。
Handler ViewPagerTimerHandler;
int DelayTime=2000;
ViewPagerTimerHandler=new Handler();
ViewPagerTimerHandler.postDelayed(new ViewPagerTimer(),DelayTime);
//滚动定时器
class ViewPagerTimer implements Runnable{
public ViewPagerTimer(){
}
@Override
public void run() {
int CurrentViewPager=MyViewPager.getCurrentItem();
MyViewPager.setCurrentItem(CurrentViewPager+1);
if (ViewPagerTimerHandler!=null){
ViewPagerTimerHandler.postDelayed(this,DelayTime);
}
}
}
没解决的问题
这里我还没想到怎么监听手动滚动的时候暂停自动滚动,而且动画滚动时间有点快,似乎也是可以设置的。
小白点的话,主要就是在ViewPager控件下面放置一个LinearLayout,在这个布局中添加小圆点,用View创建小圆点,形状和颜色通过background背景设置。
在activity_main.xml中添加一个LinearLayout用于放置小白点,如果真用了可以把这两个控件放在一个LinearLayout中,就方便使用了。
<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="com.example.k.androidpractie.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/ShowView"
android:layout_width="match_parent"
android:background="@color/burlywood"
android:layout_height="60dp">android.support.v4.view.ViewPager>
<LinearLayout
android:id="@+id/WhitePoints"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignBottom="@+id/ShowView">
LinearLayout>
RelativeLayout>
编写Background文件,这里用到了三个文件,一个是selector文件,其余两个是item,均位于drawable文件夹中。
selector是存放在drawable文件夹中 用来设置控件背景和字体颜色的。通过android:background属性设置selector,可以对控件的一些属性进行设置。我觉得其实有点类似WPF的触发器,满足相应条件就会应用相应属性
android:state_enabled属性值为true或者false,若控件设置view.setEnabled(true),则会使用@drawable/white的属性,设置view.setEnabled(false),则会使用@drawable/gray的属性。
filename:whitepoint_background.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/white" android:state_enabled="true" />
<item android:drawable="@drawable/gray" android:state_enabled="false" />
selector>
filename:white.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#FF3030" />
<corners android:radius="10dp" />
shape>
filename:gray.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/darker_gray" />
<corners android:radius="10dp" />
shape>
在MainActivity.java中编写getWhitePoint()方法,在onCreate()方法中初始化的时候调用就好了。
在getWhitePoint()中,由于我有五个view,因此需要五个白点,这里的白点直接使用View,然后设置background背景,先给所有的View设为false,通过LinearLayout.LayoutParams设置控件的参数属性,如果不是第一个点,则设左边距为30,然后调用addView()方法添加到我的小白点布局WhitePointLinearLayout中。
LinearLayout WhitePontLinearLayout;
protected void onCreate(Bundle savedInstanceState) {
...
WhitePontLinearLayout=findViewById(R.id.WhitePoints);
getWhitePoint();
...
}
private void getWhitePoint(){
View view;
for (int i=0;i<5;i++){
view=new View(this);
view.setBackgroundResource(R.drawable.whitepoint_background);
view.setEnabled(false);
LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(30,30);
if (i!=0)
params.leftMargin=30;
WhitePontLinearLayout.addView(view,params);
}
WhitePontLinearLayout.getChildAt(0).setEnabled(true);
}
然后运行程序,就可以看到底下的小白点的,点的大小和颜色都可以在background的item那几个xml中自己设置。
通过继承OnTouchListener接口,实现onTouch()方法,这个方法中可以获取到手指滑动的坐标,处理开始和停止时的坐标,通过对坐标的处理,可以判断出是往什么方向滑动,响应相应的操作,比如左滑或者右滑。
可以通过OnGestureListener实现,但是需要实现很多的方法。
另一个方法,继承GestureDetector.SimpleOnGestureListener类,实现onFling()方法,获取手指按下和松开的坐标,判断滑动状态,进行相应操作。
这里的点击事件就是点击轮播的时候响应的事件,比如跳转之类的,比较简单,主要就是给View设置点击监听,我是在适配器中给View设置的监听。
由于我们使用了无限滚动,因此左右会各多出一个view,要搞清楚每个view和对应的position。
filename:MyPagerAdapter.java
...
public Object instantiateItem(ViewGroup container, final int position) {
View view=View.inflate(MyContext,R.layout.viewpager_item,null);
TextView ViewContentTextView=view.findViewById(R.id.ViewContent);
final String Content=MyTextList.get(position);
ViewContentTextView.setText(Content);
container.addView(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MyContext,"this is view :"+MyTextList.get(position-1>0?position-1:0),Toast.LENGTH_SHORT).show();
}
});
return view;
}
...
运行程序后,点击轮播内容,会弹出Toast,显示当前轮播TextView的文字。
如果是轮播的图片的话,可以直接对ImageView设置监听事件。
这个控件可以很好的实现类似垂直滚动广告条的功能。
在activity_main.xml中编写一个布局,就只有一个控件
<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="com.example.k.androidpractie.MainActivity">
<ViewFlipper
android:id="@+id/ViewShow"
android:layout_width="match_parent"
android:layout_height="wrap_content">ViewFlipper>
RelativeLayout>
编写子项布局viewflipper_item.xml,水平布局,左边一个ImageView,右边一个TextView,垂直滚动。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ViewShowImage"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/ViewShowText"
android:layout_weight="1"
android:gravity="center"
android:textSize="25dp"/>
LinearLayout>
在MainActivity.java中设置显示的内容。调用setFlipInterval()设置滚动的时间间隔,调用startFlipping()方法启动。
public class MainActivity extends AppCompatActivity {
ViewFlipper MyViewFlipper;
ImageView TempImageView;
TextView TempTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyViewFlipper=findViewById(R.id.ViewShow);
for (int i=0;i<3;i++){
View view= LayoutInflater.from(this).inflate(R.layout.viewflipper_item,null);
TempImageView=view.findViewById(R.id.ViewShowImage);
TempTextView=view.findViewById(R.id.ViewShowText);
TempImageView.setImageResource(R.drawable.diamond);
TempTextView.setText("abcdefgh "+i);
MyViewFlipper.addView(view);
}
MyViewFlipper.setFlipInterval(1000);
MyViewFlipper.startFlipping();
}
}
运行程序,可以看到
此时的效果和ViewPager控件的无动画效果差不多。
在res文件夹中new一个安卓资源文件夹
右键res -> New -> Android resource directory -> anim
动画效果通过set标签设置,这个xml文件只能放在anim这种文件夹,不能放到drawable之类的文件夹。
此时需要将setFlipInterval(1000);参数改为setFlipInterval(2000);,如果是1000的话太快了重叠了,有兴趣可以自己试试。
filename:in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000">
<translate android:fromYDelta="100%p" android:toYDelta="0"/>
set>
filename:out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000">
<translate android:fromYDelta="0" android:toYDelta="-100%p" />
set>
最后修改一下ViewFlipper控件,将这两个出入动画写到属性里面
<ViewFlipper
android:id="@+id/ViewShow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inAnimation="@anim/in"
android:outAnimation="@anim/out">ViewFlipper>
我不会截gif,找一个间隙接涂,可以看到垂直滚动很顺滑。修改动画xml,Y改成X应该能实现水平动画。
添加点击事件的话,只需要在MainActivity.java中对view设置OnClockListener鼠标点击监听即可。