仿网易新闻 ViewPager 实现图片自动轮播
新闻 App 首页最上方一般会循环播放热点图片,如下图所示。

本文主要介绍了利用 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 预览图,由于没有图片内容,当前只显示出红色矩形区域。

新建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
增加底部小圆点指示器
轮播图片的底部都会加上小圆点,指示当前访问图片的位置。

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进行了处理。
完整代码
基于上面的分析,我们实现了自动轮播图片

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方法:下一次执行时间相对于上一次开始的 时间点 ,因此执行时间不会延后,存在并发性 。保持执行频率的稳定。
参考文章
LoremPixel 图片资源网站,随机生成图片 http://lorempixel.com/
Android Image Slideshow using ViewPager with PagerAdapter http://codetheory.in/android-image-slideshow-using-viewpager-pageradapter/
安卓PagerAdapter中的isViewFromObject()方法有什么用? http://segmentfault.com/q/1010000000484617
Timer的schedule和scheduleAtFixedRate方法的区别解析 http://blog.csdn.net/gtuu0123/article/details/6040159
Android 仿首页广告轮播效果 http://blog.csdn.net/xiyou_android/article/details/45566129
Android Material Design之Toolbar与Palette实践 http://blog.csdn.net/bbld_/article/details/41439715
方法二:
GitHub 上面介绍的更详细:https://github.com/CodingForAndroid/CircleViewPager
2016.8.30 更新 : 添加两种切换效果
CircleViewPager
轮播图目前支持三种效果
//通过ImageCycleView.CYCLE_T选择切换类型 效果如下图所示
//CYCLE_VIEW_NORMAL 、 CYCLE_VIEW_THREE_SCALE 、 CYCLE_VIEW_ZOOM_IN
可以通过设置imageCycleView.setCycle_T(ImageCycleView.CYCLE_T.CYCLE_VIEW_THREE_SCALE);随意选择
自动轮播图
CYCLE_VIEW_NORMAL:

CYCLE_VIEW_THREE_SCALE:
CYCLE_VIEW_ZOOM_IN:
Android 自动轮播图,接入方便 ,欢迎使用~
使用说明:
布局文件
<com.jorge.circlelibrary.ImageCycleView
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/cycleView"/>
相关Activity中
ImageCycleView imageCycleView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/** 找到轮播控件*/
imageCycleView= (ImageCycleView) findViewById(R.id.cycleView);
// 选择切换类型
//ImageCycleView.CYCLE_T 有三种类型 ,效果如上图所示
//CYCLE_VIEW_NORMAL CYCLE_VIEW_THREE_SCALE CYCLE_VIEW_ZOOM_IN 可以随意选择
imageCycleView.setCycle_T(ImageCycleView.CYCLE_T.CYCLE_VIEW_THREE_SCALE);
/**装在数据的集合 文字描述*/
ArrayList<String> imageDescList=new ArrayList<>();
/**装在数据的集合 图片地址*/
ArrayList<String> urlList=new ArrayList<>();
/**添加数据*/
urlList.add("http://attach.bbs.miui.com/forum/month_1012/101203122706c89249c8f58fcc.jpg");
urlList.add("http://bbsdown10.cnmo.com/attachments/201308/06/091441rn5ww131m0gj55r0.jpg");
// urlList.add("http://kuoo8.com/wall_up/hsf2288/200801/2008012919460743597.jpg");
urlList.add("http://attach.bbs.miui.com/forum/201604/05/001754vp6j0vmcj49f0evc.jpg.thumb.jpg");
urlList.add("http://d.3987.com/taiqiumein_141001/007.jpg");
urlList.add("http://attach.bbs.miui.com/forum/201604/05/100838d2b99k6ihk32a36a.jpg.thumb.jpg");
imageDescList.add("小仓柚子");
imageDescList.add("抚媚妖娆性感美女");
imageDescList.add("热血沸腾 比基尼");
imageDescList.add(" 台球美女");
imageDescList.add("身材妙曼");
initCarsuelView(imageDescList, urlList);
}
/**初始化轮播图*/
public void initCarsuelView(ArrayList<String> imageDescList,ArrayList<String>urlList) {
LinearLayout.LayoutParams cParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, getScreenHeight(MainActivity.this) * 3 / 10);
imageCycleView.setLayoutParams(cParams);
ImageCycleView.ImageCycleViewListener mAdCycleViewListener = new ImageCycleView.ImageCycleViewListener() {
@Override
public void onImageClick(int position, View imageView) {
/**实现点击事件*/
Toast.makeText(MainActivity.this,"position="+position,Toast.LENGTH_SHORT).show();
}
@Override
public void displayImage(String imageURL, ImageView imageView) {
/**在此方法中,显示图片,可以用自己的图片加载库,也可以用本demo中的(Imageloader)*/
ImageLoaderHelper.getInstance().loadImage(imageURL, imageView);
}
};
/**设置数据*/
imageCycleView.setImageResources(imageDescList,urlList, mAdCycleViewListener);
imageCycleView.startImageCycle();
}
/**
* 得到屏幕的高度
* @param context
* @return
*/
public static int getScreenHeight(Context context){
if (null == context) {
return 0;
}
DisplayMetrics dm = new DisplayMetrics();
dm = context.getApplicationContext().getResources().getDisplayMetrics();
return dm.heightPixels;
}
内部封装,外部调用起来比较方便 可通过 build.gradle 直接导入
dependencies { compile 'com.jorge.circleview:circlelibrary:1.0.1' }
---------------欢迎各位大神加群
----------------android交流群:230274309
-----------------------------期待大神们的到来
------------------------一---起分享,一起进步!需要你们
仿网易新闻 ViewPager 实现图片自动轮播的更多相关文章
- Android 开源框架ActionBarSherlock 和 ViewPager 仿网易新闻客户端
转载请注明出处:http://blog.csdn.net/xiaanming/article/details/9971721 大家都知道Android的ActionBar是在3.0以上才有的,那么在3 ...
- Android Studio精彩案例(一)《ActionBar和 ViewPager版仿网易新闻客户端》
转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 为了能更好的分享高质量的文章,所以开设了此专栏.文章代码都以Android Studio亲测运行,读者朋友可在后面直接下载源码.该专栏 ...
- Android应用经典主界面框架之二:仿网易新闻client、CSDN client (Fragment ViewPager)
另外一种主界面风格则是以网易新闻.凤凰新闻以及新推出的新浪博客(阅读版)为代表.使用ViewPager+Fragment,即ViewPager里适配器里放的不是一般的View.而是Fragment.所 ...
- 类似掌盟的Tab页 Android 开源框架ViewPageIndicator 和 ViewPager 仿网易新闻客户端Tab标签 (转)
原博客地址 :http://blog.csdn.net/xiaanming/article/details/10766053 本文转载,记录学习用,如有需要,请到原作者网站查看(上面这个网址) 之前 ...
- Android 开源框架ViewPageIndicator 和 ViewPager 仿网易新闻clientTab标签
之前用JakeWharton的开源框架ActionBarSherlock和ViewPager实现了对网易新闻clientTab标签的功能,ActionBarSherlock是在3.0下面的机器支持Ac ...
- Android 开源框架ViewPageIndicator 和 ViewPager 仿网易新闻客户端Tab标签
转载请注明出处:http://blog.csdn.net/xiaanming/article/details/10766053 之前用JakeWharton的开源框架ActionBarSherlock ...
- iOS界面-仿网易新闻左侧抽屉式交互 续(添加新闻内容页和评论页手势)
本文转载至 http://blog.csdn.net/totogo2010/article/details/8637430 1.介绍 有的博友看了上篇博文iOS界面-仿网易新闻左侧抽屉 ...
- iOS仿网易新闻栏目拖动重排添加删除效果
仿网易新闻栏目选择页面的基本效果,今天抽了点时间教大家如何实现UICollectionView拖动的效果! 其实实现起来并不复杂,这里只是基本的功能,没有实现细节上的修改,连UI都是丑丑的样子,随手画 ...
- 仿网易新闻app下拉标签选择菜单
仿网易新闻app下拉标签选择菜单 仿网易新闻app下拉标签选择菜单,长按拖动排序,点击增删标签控件 ##示例 ##EasyTagDragView的使用 在layout布局里添加:
随机推荐
- Hyper-V~双网卡设置
Windows: Win10 有线网卡+无线网卡各一块 Hyper-V: 10.0.10240.16384 公司网络服务器180网段,公网192.168.0.*网段 家里网络:192.168.1.*网 ...
- 60个有用CSS代码片段
1.垂直对齐 如果你用CSS,则你会有困惑:我该怎么垂直对齐容器中的元素?现在,利用CSS3的Transform,可以很优雅的解决这个困惑: .verticalcenter{ position: re ...
- 学习笔记---C/C++语法
一.char *s char s[] 区别 char *s中的s是指针,而指针是指向一块内存区域,它指向的内存区域的大小可以随时改变,而且当指针指向常量字符串时,它的内容是不可以被修改的,否则在运行时 ...
- Apache 配置多端口
Apache 配置多端口,主要是以下步骤 1. 如果电脑是64位的,官网上下载WampServe,装的过程中如果出现msvcp110.dll丢失的话,解决办法如下: 1.1 首先是打开浏览器,在浏览器 ...
- 在.net中调用Delphi dll的Pchar转换
Pchar是非托管代码,要在.net中调用Delphi dll中的功能,请使用MarshalAs属性告知.net调用PInvoke去转换.net中标准的string类型.如果Delphi dll是De ...
- Java中的Atomic包使用指南
Atomic包介绍 在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段.Atomic包里的类基本都是使用Unsafe实现的包装类. 原 ...
- Virtualbox虚拟机配置CentOS7.0静态网络
1.首先在Virtualbox下将网络设置为Bridged Adapter,此项设置可以使本机与虚拟机进行互访,而NAT模式只能进行单项访问: 2.先切换到root,配置网卡信息需要用到root,然后 ...
- Selenium 功能总结大集合
slenium自动化测试的一个利器: 总结了部分功能,成图,方便学习: 这是一张大图,大家看起来可能比较麻烦: 可以在我的github下载:selenium大图.xmind格式
- xl2tp部署
参考 http://blog.51yip.com/linux/1795.html 说到VPN,就会想到google,满心的疼.以前写过一篇关于vpn的文单,请参考:centos5.5 vpn 安装配置 ...
- vs2010项目使用vs2013编译报错 无法打开包括文件:“winapifamily.h”
我的老项目是vs2010下的项目.最近安装vs2013后,打开sln解决方案,调试运行报错 C:\Program Files (x86)\Windows Kits\8.0\Include\um\win ...