Android 轮播图的实现
MarianaPham
8年前
<h2><strong>环境准备</strong></h2> <h3><strong>开发环境</strong></h3> <ul> <li> <p>Android Studio 2.2.1</p> </li> <li> <p>JDK1.7</p> </li> <li> <p>API 24</p> </li> <li> <p>Gradle 2.2.1</p> </li> </ul> <h2><strong>开发开始</strong></h2> <h3><strong>先上效果预览</strong></h3> <p style="text-align:center"><img src="https://simg.open-open.com/show/cbf7c0d6b8a273cd7d121928cba72836.gif"></p> <p style="text-align:center">效果预览</p> <h3><strong>案例分析</strong></h3> <p>这个案例网上也很多, 质量参差不齐, 我也就根据自己的理解来分析分析需要实现的几个功能点:</p> <ul> <li> <p>轮播图有n张图片和相对应的n个小圆点(指示器 indicator) 实现联动</p> </li> <li> <p>除了可以手动滑动外, 也可以自动滚动(轮播) 可以考虑使用Handler实现</p> </li> <li> <p>实现无限轮回滚动</p> </li> <li> <p>当手指按下图片后不再自动滚动</p> </li> </ul> <p>根据上述分析进行开发</p> <h3><strong>接下来搭建布局</strong></h3> <pre> <code class="language-java">**activity_main.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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.lulu.shufflingpicdemo.MainActivity"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="275dp"> <FrameLayout android:layout_width="match_parent" android:layout_height="220dp"> <!--轮播图位置--> <android.support.v4.view.ViewPager android:id="@+id/live_view_pager" android:layout_width="match_parent" android:layout_height="match_parent"/> <!--右下角小圆点--> <LinearLayout android:layout_marginRight="5dp" android:layout_gravity="bottom|right" android:id="@+id/live_indicator" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="10dp"/> </FrameLayout> </LinearLayout> </RelativeLayout></code></pre> <p>指示器 小点绘制文件</p> <p>indicator_select.xml</p> <pre> <code class="language-java"><?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="20dp" android:height="20dp"/> <solid android:color="#c213b7"/> </shape></code></pre> <p>indicator_no_select.xml</p> <pre> <code class="language-java"><?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="20dp" android:height="20dp"/> <solid android:color="#fff"/> </shape></code></pre> <h3><strong>ViewPager的实现</strong></h3> <p>MyPagerAdapter.java</p> <pre> <code class="language-java">public class MyPagerAdapter extends PagerAdapter { public static final int MAX_SCROLL_VALUE = 10000; private List<ImageView> mItems; private Context mContext; private LayoutInflater mInflater; public MyPagerAdapter(List<ImageView> items, Context context) { mContext = context; mInflater = LayoutInflater.from(context); mItems = items; } /** * @param container * @param position * @return 对position进行求模操作 * 因为当用户向左滑时position可能出现负值,所以必须进行处理 */ @Override public Object instantiateItem(ViewGroup container, int position) { View ret = null; //对ViewPager页号求摸取出View列表中要显示的项 position %= mItems.size(); Log.d("Adapter", "instantiateItem: position: " + position); ret = mItems.get(position); //如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。 ViewParent viewParent = ret.getParent(); if (viewParent != null) { ViewGroup parent = (ViewGroup) viewParent; parent.removeView(ret); } container.addView(ret); return ret; } /** * 由于我们在instantiateItem()方法中已经处理了remove的逻辑, * 因此这里并不需要处理。实际上,实验表明这里如果加上了remove的调用, * 则会出现ViewPager的内容为空的情况。 * * @param container * @param position * @param object */ @Override public void destroyItem(ViewGroup container, int position, Object object) { //警告:不要在这里调用removeView, 已经在instantiateItem中处理了 } @Override public int getCount() { int ret = 0; if (mItems.size() > 0) { ret = MAX_SCROLL_VALUE; } return ret; } @Override public boolean isViewFromObject(View view, Object object) { return view == (View) object; } }</code></pre> <p>Note: 一定不要在destroyItem中再调用removeView了, 因为咱们已经instantiateItem中做了处理</p> <p>在MainActivity.java中给ViewPager设置Adapter</p> <pre> <code class="language-java">mItems = new ArrayList<>(); mViewPager.setAdapter(mAdapter); addImageView(); mAdapter.notifyDataSetChanged();</code></pre> <pre> <code class="language-java">private void addImageView(){ ImageView view0 = new ImageView(this); view0.setImageResource(R.mipmap.pic0); ImageView view1 = new ImageView(this); view1.setImageResource(R.mipmap.pic1); ImageView view2 = new ImageView(this); view2.setImageResource(R.mipmap.pic2); view0.setScaleType(ImageView.ScaleType.CENTER_CROP); view1.setScaleType(ImageView.ScaleType.CENTER_CROP); view2.setScaleType(ImageView.ScaleType.CENTER_CROP); mItems.add(view0); mItems.add(view1); mItems.add(view2); }</code></pre> <p>Note: 因为咱们做的是Demo, 所以我们传入的是一个ImageView的集合, 真正开发时, 需要传入含有图片url的实体类, 在Adapter中可以使用加载图片的类库加载</p> <h3><strong>实现右下角指示器</strong></h3> <p>添加指示器</p> <p>在onCreate中添加</p> <pre> <code class="language-java">//获取指示器(下面三个小点) mBottomLiner = (LinearLayout) findViewById(R.id.live_indicator); //右下方小圆点 mBottomImages = new ImageView[mItems.size()]; for (int i = 0; i < mBottomImages.length; i++) { ImageView imageView = new ImageView(this); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(20, 20); params.setMargins(5, 0, 5, 0); imageView.setLayoutParams(params); //如果当前是第一个 设置为选中状态 if (i == 0) { imageView.setImageResource(R.drawable.indicator_select); } else { imageView.setImageResource(R.drawable.indicator_no_select); } mBottomImages[i] = imageView; //添加到父容器 mBottomLiner.addView(imageView); }</code></pre> <h3><strong>实现联动</strong></h3> <p>添加ViewPager的监听事件, 实现ViewPager.OnPageChangeListener接口</p> <pre> <code class="language-java">mViewPager.addOnPageChangeListener(this);</code></pre> <p>回调事件</p> <pre> <code class="language-java">/////////////////////////////////////////////////////////////////////////// // ViewPager的监听事件 /////////////////////////////////////////////////////////////////////////// @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { currentViewPagerItem = position; if (mItems != null) { position %= mBottomImages.length; int total = mBottomImages.length; for (int i = 0; i < total; i++) { if (i == position) { mBottomImages[i].setImageResource(R.drawable.indicator_select); } else { mBottomImages[i].setImageResource(R.drawable.indicator_no_select); } } } } @Override public void onPageScrollStateChanged(int state) { }</code></pre> <h3><strong>实现自动滚动</strong></h3> <p>在mBottomImages初始化之后 开启一个线程 进行定时发送一个空消息给Handler处理, 由Handler决定切换到下一页</p> <pre> <code class="language-java">//让其在最大值的中间开始滑动, 一定要在 mBottomImages初始化之前完成 int mid = MyPagerAdapter.MAX_SCROLL_VALUE / 2; mViewPager.setCurrentItem(mid); currentViewPagerItem = mid; //定时发送消息 mThread = new Thread(){ @Override public void run() { super.run(); while (true) { mHandler.sendEmptyMessage(0); try { Thread.sleep(MainActivity.VIEW_PAGER_DELAY); } catch (InterruptedException e) { e.printStackTrace(); } } } }; mThread.start();</code></pre> <p>自定义Handler</p> <pre> <code class="language-java">/////////////////////////////////////////////////////////////////////////// // 为防止内存泄漏, 声明自己的Handler并弱引用Activity /////////////////////////////////////////////////////////////////////////// private static class MyHandler extends Handler { private WeakReference<MainActivity> mWeakReference; public MyHandler(MainActivity activity) { mWeakReference = new WeakReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 0: MainActivity activity = mWeakReference.get(); if (activity.isAutoPlay) { activity.mViewPager.setCurrentItem(++activity.currentViewPagerItem); } break; } } }</code></pre> <p>Note: 其中isAutoPlay是一个用来判断当前是否是自动轮播的boolean值变量, 主要用于实现我们接下来说的 <strong>当手指按下图片后不再滚动</strong></p> <h3><strong>实现当手指按下图片后不再滚动</strong></h3> <p>思路: 我们可以考虑对ViewPager的触摸事件进行监听, 然后设置一个上节说到的isAutoPlay的boolean变量用来让Handler判断是否进行轮播滚动</p> <p>代码实现:</p> <p>ViewPager设置监听</p> <pre> <code class="language-java">mViewPager.setOnTouchListener(this);</code></pre> <p>事件回调</p> <pre> <code class="language-java">@Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: isAutoPlay = false; break; case MotionEvent.ACTION_UP: isAutoPlay = true; break; } return false; }</code></pre> <p>注:细心的同学可能会看出来, 我们没有单独说 无限循环 如何实现, 其实, 它的实现已经隐藏在了代码中, 在这里我简单的说一下思路:</p> <p>给ViewPager的条目个数设置个较大值, 该案例中为10000, 然后我们刚进入时选中的位置为 10000/2=5000, 也就是说我们可以向左或向右滑动约5000多张图片, 但这是不现实的, 所以就给用户造成了无限循环的假象</p> <p> </p> <p> </p> <p>来自:http://www.jianshu.com/p/24b30a3d052f</p> <p> </p>