Android 仿网易新闻 ViewPager 实现图片自动轮播

前言

新闻 App 首页最上方一般会循环播放热点图片,如下图所示。

http://blog.csdn.net/never_cxb

本文主要介绍了利用 ViewPager 实现轮播图片,图片下方加上小圆点指示器标记当前位置,并利用 Timer+Handler 实现了自动轮播播放。

本文链接 http://blog.csdn.net/never_cxb/article/details/50515216 转载请注明出处

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">

    <!--ViewPager 热门文章图片展示-->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="@color/gray_light">

        <android.support.v4.view.ViewPager
            android:id="@+id/vp_hottest"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/colorPrimary" />

        <LinearLayout
            android:id="@+id/ll_hottest_indicator"
            android:layout_width="wrap_content"
            android:layout_height="20dp"
            android:layout_gravity="bottom|right"
            android:layout_marginBottom="5dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="5dp"
            android:gravity="center"
            android:orientation="horizontal" />
    </FrameLayout>

</LinearLayout>

FrameLayout里面包含了ViewPager和LinearLayout,ViewPager 显示图片,LinearLayout是小圆点指示器区域,标记现在滑到哪张图片。

查看 xml 预览图,由于没有图片内容,当前只显示出红色矩形区域。

http://blog.csdn.net/never_cxb

新建javabean

首页的图片地址是新闻的一个属性,我们新建一个ItemArticle类。

public class ItemArticle {
    // 新闻的 id
    private int index;
    // 新闻里的图片 url
    private String imageUrl;

    public ItemArticle(int index, String imageUrl) {
        this.index = index;
        this.imageUrl = imageUrl;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

适配器 PagerAdapter

继承自 android.support.v4.view.PagerAdapter,复写4个方法

  • instantiateItem(ViewGroup, int)
  • destroyItem(ViewGroup, int, Object)
  • getCount()
  • isViewFromObject(View, Object)
public class HeaderAdapter extends PagerAdapter {
    private static final String LOG = "NEWS_LOG";

    private Activity context;
    private List<ItemArticle> articles;
    private List<SimpleDraweeView> images = new ArrayList<SimpleDraweeView>();


    public HeaderAdapter(Activity context, List<ItemArticle> articles) {
        this.context = context;
        if (articles == null || articles.size() == 0) {
            this.articles = new ArrayList<>();
        } else {
            this.articles = articles;
        }

        for (int i = 0; i < articles.size(); i++) {
            SimpleDraweeView image = new SimpleDraweeView(context);
            Uri uri = Uri.parse(articles.get(i).getImageUrl());
            image.setImageURI(uri);
            images.add(image);
        }
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        container.addView(images.get(position));
        return images.get(position);
    }

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

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

    @Override
    public boolean isViewFromObject(View view, Object object) {
        Log.i(LOG, "in isViewFromObject view: " + view + " object: "
                + object + " equal: " + (view == (View) object));
        return view == (View) object;
    }
}

深入解析 isViewFromObject 方法

isViewFromObject(View view, Object object)的通用写法是return view == (View) object;
其中(View) object可根据具体情形替换成LinearLayout等等。

查看 ViewPager 源代码(戳这里

isViewFromObject是在infoForChild里被调用的,而且在该方法内会被调用mItems.size()次,mItems.size()是 ViewPager 里面图片的个数。

static class ItemInfo {
    Object object;
    int position;
    boolean scrolling;
}

private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();

ItemInfo infoForChild(View child) {
    for (int i=0; i<mItems.size(); i++) {
        ItemInfo ii = mItems.get(i);
        if (mAdapter.isViewFromObject(child, ii.object)) {
            return ii;
        }
    }
    return null;
}

ViewPager里面用了一个mItems 存储每个page的信息(ItemInfo),当界面要展示或者发生变化时,需要依据page的当前信息来调整,但此时只能通过view来查找,遍历mItems通过比较view和object来找到对应的ItemInfo。

Log.i(LOG, "in isViewFromObject view: " + view + " object: "
        + object + " equal: " + (view == (View) object));

所以我们如果打印出 Log 的话,会看到isViewFromObject()被调用多次,只有1次返回 true (表示找到了对应的ItemInfo),其他返回 false。

01-17 10:15:21.207 I/NEWS_LOG﹕ in isViewFromObject 
view: SimpleDraweeView{holder=DraweeHolder{...} 
object: SimpleDraweeView{holder=DraweeHolder{...} 
equal: false

01-17 10:15:21.207 I/NEWS_LOG﹕ in isViewFromObject 
view: SimpleDraweeView{holder=DraweeHolder{...} 
object: SimpleDraweeView{holder=DraweeHolder{...} 
equal: false

01-17 10:15:21.207 I/NEWS_LOG﹕ in isViewFromObject 
view: SimpleDraweeView{holder=DraweeHolder{...} 
object: SimpleDraweeView{holder=DraweeHolder{...} 
equal: false

01-17 10:15:21.207 I/NEWS_LOG﹕ in isViewFromObject 
view: SimpleDraweeView{holder=DraweeHolder{...} 
object: SimpleDraweeView{....}} 
equal: true

增加底部小圆点指示器

轮播图片的底部都会加上小圆点,指示当前访问图片的位置。

http://blog.csdn.net/never_cxb

private ImageView[] mBottomImages;//底部只是当前页面的小圆点


//创建底部指示位置的导航栏
mBottomImages = new ImageView[headerArticles.size()];

for (int i = 0; i < mBottomImages.length; i++) {
    ImageView imageView = new ImageView(mAct);
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(10, 10);
    params.setMargins(5, 0, 5, 0);
    imageView.setLayoutParams(params);
    if (i == 0) {
        imageView.setBackgroundResource(R.drawable.indicator_select);
    } else {
        imageView.setBackgroundResource(R.drawable.indicator_not_select);
    }

    mBottomImages[i] = imageView;
    //把指示作用的原点图片加入底部的视图中
    llHottestIndicator.addView(mBottomImages[i]);

}

上面这段代码是小圆点的初始步骤,最开始是第0张图片被选中,所以是第0张小圆点是蓝色,其他小圆点是灰色。

addOnPageChangeListener 使得小圆点动态变化

切换图片的时候,小圆点也要随着改变,这需要利用ViewPager.OnPageChangeListener,主要是下面这个方法:

public abstract void onPageSelected (int position)

This method will be invoked when a new page becomes selected. Animation is not necessarily complete.

Parameters
position    Position index of the new selected page.
vpHottest.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {    
    //图片左右滑动时候,将当前页的圆点图片设为选中状态
    @Override
    public void onPageSelected(int position) {
        // 一定几个图片,几个圆点,但注意是从0开始的
        int total = mBottomImages.length;
        for (int j = 0; j < total; j++) {
            if (j == position) {
                mBottomImages[j].setBackgroundResource(R.drawable.indicator_select);
            } else {
                mBottomImages[j].setBackgroundResource(R.drawable.indicator_not_select);
            }
        }
    }

    @Override
    public void onPageScrolled(int i, float v, int i1) {    
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }
});

onPageSelected()中,利用 for 循环,将当前选中位置对应的小圆点置为蓝色,其他小圆点置为灰色。

自动播放

先定义一个 Handler,在主线程里面更新 UI

//定时轮播图片,需要在主线程里面修改 UI
private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case UPTATE_VIEWPAGER:
                if (msg.arg1 != 0) {
                    vpHottest.setCurrentItem(msg.arg1);
                } else {
                    //false 当从末页调到首页是,不显示翻页动画效果,
                    vpHottest.setCurrentItem(msg.arg1, false);
                }
                break;
        }
    }
};

利用 Timer 实现每隔 5s 向 Handler 发送message来更新图片

// 设置自动轮播图片,5s后执行,周期是5s
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        Message message = new Message();
        message.what = UPTATE_VIEWPAGER;
        if (autoCurrIndex == headerArticles.size() - 1) {
            autoCurrIndex = -1;
        }
        message.arg1 = autoCurrIndex + 1;
        mHandler.sendMessage(message);
    }
}, 5000, 5000);

为了使得滑到最后一页后能滑到首页,我们对于autoCurrIndex == headerArticles.size() - 1进行了处理。

完整代码

基于上面的分析,我们实现了自动轮播图片

http://blog.csdn.net/never_cxb

public class MasterArticleFragment extends Fragment {

    private static final String ARTICLE_LATEST_PARAM = "param";

    private static final int UPTATE_VIEWPAGER = 0;

    //轮播的最热新闻图片
    @InjectView(R.id.vp_hottest)
    ViewPager vpHottest;
    //轮播图片下面的小圆点
    @InjectView(R.id.ll_hottest_indicator)
    LinearLayout llHottestIndicator;

    //存储的参数
    private String mParam;

    //获取 fragment 依赖的 Activity,方便使用 Context
    private Activity mAct;

    //设置当前 第几个图片 被选中
    private int autoCurrIndex = 0;

    private ImageView[] mBottomImages;//底部只是当前页面的小圆点 

    private Timer timer = new Timer(); //为了方便取消定时轮播,将 Timer 设为全局

    //定时轮播图片,需要在主线程里面修改 UI
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPTATE_VIEWPAGER:
                    if (msg.arg1 != 0) {
                        vpHottest.setCurrentItem(msg.arg1);
                    } else {
                        //false 当从末页调到首页是,不显示翻页动画效果,
                        vpHottest.setCurrentItem(msg.arg1, false);
                    }
                    break;
            }
        }
    };

    public static MasterArticleFragment newInstance(String param) {
        MasterArticleFragment fragment = new MasterArticleFragment();
        Bundle args = new Bundle();
        args.putString(ARTICLE_LATEST_PARAM, param);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mParam = savedInstanceState.getString(ARTICLE_LATEST_PARAM);
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_one_master, container, false);
        mAct = getActivity();
        ButterKnife.inject(this, view);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        new ImageTask().execute();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        ButterKnife.reset(this);
    }


    private void setUpViewPager(final List<ItemArticle> headerArticles) {
        HeaderAdapter imageAdapter = new HeaderAdapter(mAct, headerArticles);
        vpHottest.setAdapter(imageAdapter);

        //创建底部指示位置的导航栏
        mBottomImages = new ImageView[headerArticles.size()];

        for (int i = 0; i < mBottomImages.length; i++) {
            ImageView imageView = new ImageView(mAct);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(10, 10);
            params.setMargins(5, 0, 5, 0);
            imageView.setLayoutParams(params);
            if (i == 0) {
                imageView.setBackgroundResource(R.drawable.indicator_select);
            } else {
                imageView.setBackgroundResource(R.drawable.indicator_not_select);
            }

            mBottomImages[i] = imageView;
            //把指示作用的原点图片加入底部的视图中
            llHottestIndicator.addView(mBottomImages[i]);

        }

        vpHottest.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

              //图片左右滑动时候,将当前页的圆点图片设为选中状态
              @Override
              public void onPageSelected(int position) {
                  // 一定几个图片,几个圆点,但注意是从0开始的
                  int total = mBottomImages.length;
                  for (int j = 0; j < total; j++) {
                      if (j == position) {
                          mBottomImages[j].setBackgroundResource(R.drawable.indicator_select);
                      } else {
                          mBottomImages[j].setBackgroundResource(R.drawable.indicator_not_select);
                      }
                  }

                  //设置全局变量,currentIndex为选中图标的 index
                  autoCurrIndex = position;
              }

              @Override
              public void onPageScrolled(int i, float v, int i1) {
              }

              @Override
              public void onPageScrollStateChanged(int state) {
              }
          }
        );

        // 设置自动轮播图片,5s后执行,周期是5s
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = UPTATE_VIEWPAGER;
                if (autoCurrIndex == headerArticles.size() - 1) {
                    autoCurrIndex = -1;
                }
                message.arg1 = autoCurrIndex + 1;
                mHandler.sendMessage(message);
            }
        }, 5000, 5000);
    }


    class ImageTask extends AsyncTask<String, Void, List<ItemArticle>> {
        @Override
        protected List<ItemArticle> doInBackground(String... params) {
            List<ItemArticle> articles = new ArrayList<ItemArticle>();
            articles.add(
                    new ItemArticle(1123, "http://***20151231105648_11790.jpg"));
            articles.add(
                    new ItemArticle(1123, "http://***20151230152544_36663.jpg"));
            articles.add(
                    new ItemArticle(1123, "http://***20151229204329_75030.jpg"));
            articles.add(
                    new ItemArticle(1123, "http://***20151221151031_36136.jpg"));
            return articles;
        }

        @Override
        protected void onPostExecute(List<ItemArticle> articles) {
            //这儿的 是 url 的集合
            super.onPostExecute(articles);
            setUpViewPager(articles);

        }
    }
}

一些知识点

schedule和scheduleAtFixedRate方法

(1)schedule方法:下一次执行时间相对于 上一次 实际执行完成的时间点 ,因此执行时间会不断延后。保持间隔时间的稳定
(2)scheduleAtFixedRate方法:下一次执行时间相对于上一次开始的 时间点 ,因此执行时间不会延后,存在并发性 。保持执行频率的稳定。

参考文章

  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
实现ViewPager的自动轮播,你可以按照以下步骤进行操作: 1. 首先,在布局文件中添加ViewPager控件,并设置其相关属性,如布局宽高、指示器等。 2. 在代码中,创建一个Handler对象和一个Runnable对象,用于实现自动轮播的定时任务。 ```java private ViewPager viewPager; private int currentPage = 0; private Timer timer; private final long DELAY_MS = 500; // 延迟时间 private final long PERIOD_MS = 3000; // 滚动间隔时间 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.viewPager); // 设置ViewPager的Adapter以及其他属性 final Handler handler = new Handler(); final Runnable runnable = new Runnable() { public void run() { if (currentPage == NUM_PAGES) { currentPage = 0; } viewPager.setCurrentItem(currentPage++, true); } }; timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { handler.post(runnable); } }, DELAY_MS, PERIOD_MS); } ``` 3. 在Activity的`onDestroy`方法中,取消定时任务,避免内存泄漏。 ```java @Override protected void onDestroy() { super.onDestroy(); if (timer != null) { timer.cancel(); timer = null; } } ``` 这样,ViewPager就会自动进行轮播了。注意,上述代码中的`NUM_PAGES`是ViewPager中页面的数量,你需要根据实际情况进行替换。另外,你还可以根据需求添加页面切换的动画效果、手势滑动等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值