ViewPager实现真正的左右无限循环滑动

最近由于项目需要需要实现左右无限滑动展示信息的功能,在网上找到了这篇文章转载给朋友们

因此第一反应就是使用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);
        };
    };
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现数据无限循环可以通过以下步骤: 1. 在Adapter中重写getCount()方法,将其返回一个较大的值,比如Integer.MAX_VALUE,这样就可以实现数据循环的效果。 2. 在Adapter中重写getItem()方法,将其返回数据集中指定位置的数据,但是要注意,位置需要取余数,即position % data.size(),这样可以保证数据循环。 3. 在ViewPager中重写onPageSelected()方法,当用户滑动到第一个或最后一个数据时,将ViewPager设置为对应的位置,这样就可以实现无限循环的效果。 以下是示例代码: ``` public class MyPagerAdapter extends PagerAdapter { private List<Data> mDataList; public MyPagerAdapter(List<Data> dataList) { mDataList = dataList; } @Override public int getCount() { return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { View itemView = LayoutInflater.from(container.getContext()).inflate(R.layout.item_viewpager, container, false); TextView textView = itemView.findViewById(R.id.tv_data); Data data = mDataList.get(position % mDataList.size()); textView.setText(data.getData()); container.addView(itemView); return itemView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } } public class MainActivity extends AppCompatActivity { private ViewPager mViewPager; private List<Data> mDataList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initViewPager(); } private void initData() { for (int i = 0; i < 10; i++) { mDataList.add(new Data("Data " + i)); } } private void initViewPager() { mViewPager = findViewById(R.id.view_pager); mViewPager.setAdapter(new MyPagerAdapter(mDataList)); mViewPager.setCurrentItem(Integer.MAX_VALUE / 2); mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { if (position == 0) { mViewPager.setCurrentItem(Integer.MAX_VALUE / 2); } else if (position == Integer.MAX_VALUE - 1) { mViewPager.setCurrentItem(Integer.MAX_VALUE / 2); } } @Override public void onPageScrollStateChanged(int state) { } }); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值