最近由于项目需要需要实现左右无限滑动展示信息的功能,在网上找到了这篇文章转载给朋友们
因此第一反应就是使用viewpager,但是Android原生的viewpager不支持左右无限滑动,在网上也找了好多资料都不是很满意,有的说在适配器中把返回的大小设置为很大,然后初始化时再取中间位,因为一般人不会那么无聊的一直向右或左滑到尽头,但是我觉得这种方法不是很好,浪费资源,也容易报错。因此就viewpager的特性想出了一个简单而实用的方法,完美的实现了这一需求。
我们先看看效果再说原理:
1、这是初始化的第一张:
2、这是第二张(在第一张的基础上再向做滑动的效果,到了最后一张):
3、这是最后一张:
4、这是最后一张再向右滑动(到了第一张):
是不是满足了需求了,下面我们来看看实现的原理:
首先初始化图片:比如我们要展示A B C D四张图片,为了滑到第一张或左后一张还能继续滑动,所以我们需要在原来的图片前后各再构造一张图片即:D A B C D A。第一张图片A再向做滑动,应该是图片D展示出来;最后一张D图片再向右滑动,应该是图片A展示出来。这样就实现了滑到第一张和左后一张时都还可以再向左或向右滑动。
然后重点来了:当滑到A前面的D图片时,此时再向左滑动是不能滑动的,因为到了viewpager的第一张(索引为0),按照逻辑如果还能滑动的话,再向左滑动的下一张会是C,因此我们需要重写方法onPageScrolled(int arg0, float arg1, int arg2),当从第一张A完全滑动到D图片时,参数arg1为0.0,此时表示滑动已经完成,这时为了还能继续向做滑动,是不是应该把viewpager的当前页设置到倒数第二个D的位置?关键点就是这样的,此时用方法setCurrentItem(int, boolean)设置viewpager跳转到倒数第二个D那一页,其中boolean参数设置为false,viewpager就会瞬间跳转到倒数第二个D,在我们的视觉效果来看,完全看不出任何变化,所以此时的页面实际就是倒数第二个D那一页,所以再向左边滑动时,B就显示出来了,给人的感觉就是,可以无限向左滑动,就这样实现了无限左右滑动。以此类推,向右滑动也一样,到了最后一个A的时候,跳转到正数第二个A,然后继续向右滑动就会显示B,也实现了无限向右滑动。此时向左向右无限循环滑动已经实现了。
下面是示例代码(已经写了详细的注释):
布局文件(activity_main.xml):
<pre name="code" class="html"><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="${relativePackage}.${activityClass}" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="250dip"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/circlulate_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
<TextView
android:id="@+id/page"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"/>
</LinearLayout>
ViewPager适配器(CirculatePagerAdapter.java):
</pre><pre name="code" class="java">package com.ywl5320.circulateviewpager;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
/**
* viewpager适配器
* @author ywl
*
*/
public class CirculatePagerAdapter extends PagerAdapter {
private ImageView[] imageViews;
private int size;
private Context context;
public CirculatePagerAdapter(Context context, ImageView[] imageViews) {
this.context = context;
this.imageViews = imageViews;
size = imageViews.length;
}
@Override
public int getCount() {
return imageViews.length;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((View) object);// 完全溢出view,避免数据多时出现重复现象
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView((ImageView)imageViews[position], 0);
return (ImageView)imageViews[position];
}
/*
* 这个方法可用来异步获取网路图片时,更新viewpager中的图片(此处没用,使用时需格式化一下代码) public void refresh()
* { if(imageViews[size - 2].getDrawable() != null &&
* imageViews[0].getDrawable() == null) { ImageView imageView = new
* ImageView(context); imageView.setBackgroundDrawable(imageViews[size -
* 2].getDrawable()); imageViews[0] = imageView; }
* if(imageViews[1].getDrawable() != null && imageViews[size -
* 1].getDrawable() == null) { ImageView imageView = new ImageView(context);
* imageView.setBackgroundDrawable(imageViews[1].getDrawable());
* imageViews[size - 1] = imageView; } notifyDataSetChanged(); }
*/
}
主Activity(MainActivity.java):
package com.ywl5320.circulateviewpager;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements OnPageChangeListener{
private ViewPager mvPager;
private TextView mTextView;
private ImageView[] mImageViews;// 要循环显示的图片资源(也可以是其他的view)
private CirculatePagerAdapter mCirculatePagerAdapter;// viewpager适配器
private static int [] imgs = {R.drawable.zx,R.drawable.tm,R.drawable.kp,R.drawable.js};//要显示的图片资源
private int imgsize = 0;
private ScheduledExecutorService scheduledExecutorService;// 定时周期执行指定任务
private int currentIndex = 0;// (自动播放时)定时周期要显示的图片的索引(viewpager中的图片位置)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mvPager = (ViewPager) findViewById(R.id.circlulate_pager);
mTextView = (TextView) findViewById(R.id.page);
initImg();
setAdapter();
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleWithFixedDelay(new ViewPagerTask(),
10, 10, TimeUnit.SECONDS);
}
/**
* 初始化图片资源 为数组里的每一个Imageview分配内存
*/
public void initImg()
{
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
int length = imgs.length + 2;
mImageViews = new ImageView[length];
for(int i = 0; i < length; i++)
{
ImageView imageView = new ImageView(this);
imageView.setLayoutParams(params);
imageView.setScaleType(ScaleType.FIT_XY);
mImageViews[i] = imageView;
}
setImg(length);
}
/**
* 除去第一个和最后一个imageview,其他的imageview依次设置相应的图片,顺序与imgs里的一样
* 最后再把第一个imageview的图片设置为imgs中的最后一张图片
* ,把最后一个imageview的图片设置为imgs中的第一张图片(因为向做滑动到第一张时
* ,再向左滑动就到了最后一张;向右滑动一样,到了最后一张,再向右滑动就到了第一张) 比如:要显示的图片为: A B C
* D四张图片,此时我们要把它们构造成: D {A B C D}
* A中间大括号里的就是要显示的图片,第一个D和最后一个A就是滑动到头时继续再滑动时逻辑上要展示的图片
*
* @param length
*/
public void setImg(int length)
{
imgsize = length;
for(int i = 0; i < length; i++)
{
if(i < length - 2)
{
final int index = i;
mImageViews[i + 1].setBackgroundDrawable(getResources().getDrawable(imgs[i]));
mImageViews[i + 1].setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 为每一张图片添加点击事件
if(index + 1 == 1)
{
Toast.makeText(MainActivity.this, "赵信", Toast.LENGTH_SHORT).show();
}
else if(index + 1 == 2)
{
Toast.makeText(MainActivity.this, "提莫", Toast.LENGTH_SHORT).show();
}
else if(index + 1 == 3)
{
Toast.makeText(MainActivity.this, "卡牌", Toast.LENGTH_SHORT).show();
}
else if(index + 1 == 4)
{
Toast.makeText(MainActivity.this, "剑圣", Toast.LENGTH_SHORT).show();
}
}
});
}
}
mImageViews[0].setBackgroundDrawable(getResources().getDrawable(imgs[imgs.length - 1]));
mImageViews[length - 1].setBackgroundDrawable(getResources().getDrawable(imgs[0]));
}
/**
* 初始化和给viewpager添加适配器
*/
public void setAdapter()
{
mCirculatePagerAdapter = new CirculatePagerAdapter(this, mImageViews);
mvPager.setAdapter(mCirculatePagerAdapter);
mvPager.setOnPageChangeListener(this);
mvPager.setCurrentItem(1);//初始化时设置显示第一页(ViewPager中索引为1)
}
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
/**
* 这是重点: 此方法会监听viewpager的滑动过程,可获取滑动的百分比(arg1)参数。
* 这里判断的方法是:当滑动到第索引为0的那一页时(即:在逻辑上是到了第一张图片
* (即赵信那张图片)后,再向左滑动,此时viewpager会显示索引为0的那张图片
* (即在视觉效果上是左后一张的图片,因为,第一张过了再向左滑动,就是最后一张
* )。如果再这里不做相应的处理,再向左滑动就滑不动了,因为已经到了viewpager的第一张
* (索引为0),此时我们就要依靠参数arg1的值来判断是否已经完成了滑动到第一张
* ,当arg1的值为0.0时,即已经滑动完成,此时我们就把viewpager的页面跳转到viewpager的倒数第二张页面上
* ,使用setcurrentItem
* (Int,boolean)方法,当Boolean取值为FALSE时,就没有滑动效果,直接跳转过去,由于当前页的图片和要跳转到的页面一样
* ,所以在视觉效果上看不出闪烁
* ,这样就很自然的跳转到了倒数第二张,然后继续向左滑动,就可以继续滑动了。给人的感觉就是能一直无限向左滑动。)向右滑动也类似
* ,当滑动到最后一张时就跳到索引为1的那张,然后继续向右还可以滑动,这样就实现了左右循环无限滑动的效果。哈哈
*/
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
if(arg1 == 0.0)
{
if(arg0 == 0)
{
mvPager.setCurrentItem(imgsize - 2, false);
System.out.println("ok");
}
else if(arg0 == imgsize - 1)
{
mvPager.setCurrentItem(1, false);
}
}
}
/**
* 显示滑到了第几张(不是必须的)
*/
@Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
currentIndex = arg0;// 把当前页的索引记住,方便跳转到下一页(这是必须的)
if(arg0 == 1)
{
mTextView.setText(arg0 + "/" + (imgsize - 2));
}
else if(arg0 == 2)
{
mTextView.setText(arg0 + "/" + (imgsize - 2));
}
else if(arg0 == 3)
{
mTextView.setText(arg0 + "/" + (imgsize - 2));
}
else if(arg0 == 4)
{
mTextView.setText(arg0 + "/" + (imgsize - 2));
}
}
/**
* 继承runnable一个新线程,来定时播放图片
*
* @author ywl
*
*/
private class ViewPagerTask implements Runnable
{
@Override
public void run() {
// TODO Auto-generated method stub
currentIndex++;
handler.obtainMessage().sendToTarget();
}
}
private Handler handler = new Handler(){
public void handleMessage(Message msg)
{
// 使viewpager跳转到指定页(true:带有滑动效果)
mvPager.setCurrentItem(currentIndex, true);
};
};
}