ViewPager实现轮播图(可无限向左或向右滑动)

#1 效果图

ViewPager实现轮播图(可无限向左或向右滑动)


#2 Demo地址

http://download.csdn.net/detail/baopengjian/9901113


#3 原理

1) ViewPager实现左右滑动

2)handler实现定时消息

3)数据集List第0个添加真实数据最后一个,最后一个添加真实数据的第0个(List = 真实数据最后一个+真实数据集合+真实数据第一个)实现缓存的数据结构;

4)ViewPager的无动画切换View的API:setCurrentItem(position,false)实现 当最后或第一个时切换到相同视图,从而实现“无限轮播”的错觉


#4 Demo特点:

1)封装性强,集成速度快(关键代码2行,xml中引入封装Fragment即可)

2)轮播图的停止,启动自维护;

3)支持异步

4)修改方便,点击事件、轮播图的Pager均可修改


#5 代码

1)使用轮播图的MainActivity

xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="com.bpj.demo.MainActivity">


    <fragment
        android:id="@+id/fragment_viewpager_cycle"
        android:name="com.bpj.demo.ViewPagerCycleFragment"
        android:layout_width="match_parent"
        android:layout_height="180dp"/>


    <Button
        android:onClick="refreshData"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp"
        android:layout_below="@id/fragment_viewpager_cycle"
        android:text="refreshData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>


MainActivity

public class MainActivity extends AppCompatActivity {

    private ViewPagerCycleFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mFragment = (ViewPagerCycleFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_viewpager_cycle);
        Log.i("MainActivity", ">>>> ThreadId =" + Thread.currentThread().getId());
        initData();
    }

    public void refreshData(View v) {
        initData();
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mFragment.setData(list, new ViewPagerCycleFragment.CycleViewClickListener() {
                @Override
                public void onImageClick(ViewPagerCycleBean info, int position, View imageView) {
                    Toast.makeText(MainActivity.this, " >>> position" + position, Toast.LENGTH_SHORT).show();
                }
            });
            mFragment.startRoll(3000);
        }
    };


    @Override
    protected void onDestroy() {
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }

    List<ViewPagerCycleBean> list;

    private List<ViewPagerCycleBean> initData() {
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(1000);
                    list = new ArrayList<>();
                    Random r = new Random();
                    int size = r.nextInt(10);
                    Log.i("MainActivity", ">>> size " + size);
//                    int size = 1;
                    for (int i = 0; i < size; i++) {
                        ViewPagerCycleBean bean = new ViewPagerCycleBean(i);
                        list.add(bean);
                    }
                    mHandler.sendEmptyMessage(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
        return null;
    }
}

2)封装了轮播图的Fragment


public class ViewPagerCycleFragment extends Fragment implements ViewPager.OnPageChangeListener {
    private static final int ROLL = 100; // 转动
    private static final int WAIT = 101; // 等待

    private ViewPager viewPager;
    private LinearLayout indicatorLayout;
    private List<View> mViews = new ArrayList<>();
    private boolean isScrolling = false;
    private int time = 3000; // 默认轮播时间
    private int currentPosition = 0; // 轮播当前位置
    private long releaseTime = 0; //  手指松开、页面不滚动时间,防止手机松开后短时间进行切换
    private CycleViewClickListener mListener;
    private List<ViewPagerCycleBean> mDatas;
    private ViewPagerAdapter mAdapter;
    private ImageView[] indicators;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.i("Fragment",">>> handleMessage");
            if (msg.what == ROLL && mViews.size() != 0) {
                if (!isScrolling) {
                    int max = mViews.size() + 1;
                    int position = (currentPosition + 1) % mViews.size();
                    viewPager.setCurrentItem(position, true);
                    if (position == max) { // 最后一页时回到第一页
                        viewPager.setCurrentItem(1, false);
                    }
                }
               releaseTime = System.currentTimeMillis();
                mHandler.removeCallbacks(runnable);
                mHandler.postDelayed(runnable, time);
                return;
            }
            if (msg.what == WAIT && mViews.size() != 0) {
                mHandler.removeCallbacks(runnable);
                mHandler.postDelayed(runnable, time);
            }
        }
    };

    final Runnable runnable = new Runnable() {
        @Override
        public void run() {
            if (getActivity() != null && !getActivity().isFinishing()) {
                long now = System.currentTimeMillis();
                // 检测上一次滑动时间与本次之间是否有触击(手滑动)操作,有的话等待下次轮播
                if (now - releaseTime > time - 500) {
                    mHandler.sendEmptyMessage(ROLL);
                } else {
                    mHandler.sendEmptyMessage(WAIT);
                }
            }
        }
    };

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = LayoutInflater.from(getActivity()).inflate(
                R.layout.view_cycle_viewpager_contet, null);

        viewPager = view.findViewById(R.id.viewPager);
        viewPager.setOffscreenPageLimit(3);
        indicatorLayout = view.findViewById(R.id.layout_viewpager_indicator);
        setIndicatorCenter();
        return view;
    }

    public void setIndicatorCenter() {
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        params.addRule(RelativeLayout.CENTER_HORIZONTAL);
        indicatorLayout.setLayoutParams(params);
    }

    public void setData(List<ViewPagerCycleBean> datas,CycleViewClickListener listener) {
        Activity activity = getActivity();
        if(activity == null || activity.isFinishing() || !isVisible()){
            return;
        }
        if (datas == null || datas.isEmpty()) {
            return;
        }
        mListener = listener;
        mViews = getCycleViews(datas);
        // 设置指示器
        indicators = new ImageView[datas.size()];
        indicatorLayout.removeAllViews();
        for (int i = 0; i < datas.size(); i++) {
            View view = LayoutInflater.from(getActivity()).inflate(
                    R.layout.view_cycle_viewpager_indicator, null);
            indicators[i] = view.findViewById(R.id.image_indicator);
            indicatorLayout.addView(view);
        }

        mAdapter = new ViewPagerAdapter();
        // 默认指向第一项,下方viewPager.setCurrentItem将触发重新计算指示器指向
        setIndicator(0);

        viewPager.setOffscreenPageLimit(3);
        viewPager.setOnPageChangeListener(this);
        viewPager.setAdapter(mAdapter);
        viewPager.setCurrentItem(1);
    }


    private List<View> getCycleViews(List<ViewPagerCycleBean> datas) {
        mDatas = new ArrayList<>();
        if(datas.size()>1){
            mDatas.add(datas.get(datas.size() - 1));
            mDatas.addAll(datas);
            mDatas.add(datas.get(0));
        }else{
            mDatas.addAll(datas);
        }


        List<View> list = new ArrayList<>();
        for (ViewPagerCycleBean bean : mDatas) {
            TextView tv = new TextView(getContext());
            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            tv.setLayoutParams(params);
            tv.setGravity(Gravity.CENTER);
            tv.setBackgroundColor(Color.GREEN);
            tv.setText(String.valueOf(bean.getPosition()));
            tv.setTextSize(40);
            tv.setTextColor(Color.RED);
            list.add(tv);
        }
        return list;
    }


    private boolean isRooll = false;


    public void startRoll(int time) {
        if(mAdapter == null ||mViews == null ||mViews.size() <= 1){
            return;
        }
        this.time = time;
        releaseTime = 0;
        if(isRooll){
            return;
        }else{
            mHandler.postDelayed(runnable, time);
            isRooll = true;
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        stopRoll();
    }


    @Override
    public void onResume() {
        super.onResume();
        startRoll(time);
    }

    public void stopRoll(){
        if(isRooll){
            mHandler.removeCallbacksAndMessages(null);
        }
        isRooll = false;
    }


    /**
     * 设置指示器
     *
     * @param selectedPosition 默认指示器位置
     */
    private void setIndicator(int selectedPosition) {
        for (int i = 0; i < indicators.length; i++) {
            indicators[i].setBackgroundResource(R.drawable.icon_point);
        }
        if (indicators.length > selectedPosition)
            indicators[selectedPosition].setBackgroundResource(R.drawable.icon_point_pre);
    }


    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }


    @Override
    public void onPageSelected(int position) {
        int max = mViews.size() - 1;
        currentPosition = position;
        if (position == 0) {
            currentPosition = max - 1;
        } else if (position == max) {
            currentPosition = 1;
        }
        int position1 = currentPosition - 1;
        setIndicator(position1);
    }


    @Override
    public void onPageScrollStateChanged(int state) {
        if (state == 1) { // viewPager在滚动
            isScrolling = true;
            return;
        } else if (state == 0) { // viewPager滚动结束
            releaseTime = System.currentTimeMillis();
            viewPager.setCurrentItem(currentPosition, false);
        }
        isScrolling = false;
    }

    private class ViewPagerAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return mViews.size();
        }

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == arg1;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public View instantiateItem(ViewGroup container, final int position) {
            View v = mViews.get(position);
            if (mListener != null) {
                v.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        ViewPagerCycleBean bean = mDatas.get(position);
                        mListener.onImageClick(bean, bean.getPosition(), v);
                    }
                });
            }
            container.addView(v);
            return v;
        }

        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }
    }

    /**
     * 轮播控件的监听事件
     *
     * @author minking
     */
    public static interface CycleViewClickListener {

        /**
         * 单击图片事件
         *
         * @param position  a
         * @param imageView
         */
        public void onImageClick(ViewPagerCycleBean info, int position, View imageView);
    }
}

view_cycle_viewpager_contet.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_viewager_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
 >


    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>


    <LinearLayout
        android:id="@+id/layout_viewpager_indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_margin="1dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingBottom="6dp"
        android:paddingRight="6dp"></LinearLayout>
</RelativeLayout>

view_cycle_viewpager_indicator.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView 
         android:id="@+id/image_indicator"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:scaleType="fitXY"/>

</LinearLayout>