Android ViewPager实现轻量级Banner
来源:互联网 发布:淘宝直通车效果怎么样 编辑:程序博客网 时间:2024/05/29 18:24
发现好多人提到banner,第一个想法就是撸个第三方依赖。然后出bug了,打开三方代码,一堆文件无从下手,改了又担心出现新bug,然后又替换了第二个三方…
一个ViewPager能实现的功能,何必求助第三方。
Banner的实现技术点主要在于
1 无限循环,当banner滑到最后一张后继续滑动,要滑回第一张
2 自动轮播
Adapter代码
import android.content.Context;import android.support.v4.view.PagerAdapter;import android.support.v4.view.ViewPager;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.Toast;import com.bumptech.glide.Glide;import java.util.List;/** * Created by KID on 2017/8/11 0011. */public class PagerBannerAdapter extends PagerAdapter { private Context context; private List<String> imgUrls; private ViewPager mViewPager; private boolean isPlay=false; public PagerBannerAdapter(Context context, List<String> imgUrls,ViewPager mViewPager) { this.context = context; this.imgUrls = imgUrls; this.mViewPager=mViewPager; } //是否自动播放第一张图片到第二张 public void setPlayingFirstItem(boolean isPlay){ this.isPlay=isPlay; } @Override public Object instantiateItem(ViewGroup container, final int position) { final int pos = position%imgUrls.size(); View view = LayoutInflater.from(context).inflate(R.layout.item_vp, container, false); ImageView imageView = (ImageView) view.findViewById(R.id.img_item); Glide.with(context).load(imgUrls.get(pos)).into(imageView); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context,"当前看到的要做点击事件的position="+pos+"-----实际position="+position,Toast.LENGTH_SHORT).show(); } }); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); Log.d("BannerView","destroyItem position======"+position); } @Override public int getCount() { //理论上当图片不为1张时,getCount可以设置无穷大,这里设置成图片4倍只是想看会不会滑到头,实际上3倍就够了,设2倍的话,当图片只有2张时可能会碰到头 return imgUrls.size()==1?imgUrls.size():imgUrls.size()*4; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } //当显示界面加载完时调用该方法 @Override public void finishUpdate(ViewGroup container) { Log.d("BannerView","finishUpdate position======"+mViewPager.getCurrentItem()); int position = mViewPager.getCurrentItem(); if(imgUrls.size()==1){ //TODO 但轮播图片只有一张的时候,什么都不做,或者隐藏小圆点,提示文本之类。当然,如果你只有一张图片,你还想重复滑出这张图片的话,在这里开始你的骚操作 } else { //TODO 但轮播图片超过3张时,每当图片的position超过图片数量时,切换viewpager当前选择item(去除切换动画效果) if (position == 0){ position = imgUrls.size(); //TODO 自动轮播的时候,需要这部判断来让第一张和第二张平滑过渡 if(!isPlay){ mViewPager.setCurrentItem(position,false); } } else if(position>imgUrls.size()+1){ mViewPager.setCurrentItem(position%imgUrls.size(),false); } } }}
Activity代码
import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.support.v4.view.ViewPager;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import butterknife.Bind;import butterknife.ButterKnife;/** * Created by KID on 2017/8/16 0016. * Handler实现图片自动轮播 */public class PlayBannerActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener { @Bind(R.id.view_pager) ViewPager viewPager; @Bind(R.id.tv_page) TextView pageTv; List<String> imgUrls=new ArrayList<>(); PagerBannerAdapter adapter; private boolean isStill;//是否静止 int pos ;//看到的position @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_banner); ButterKnife.bind(this); initData(); } private void initData() { imgUrls.add("http://img2.91.com/uploads/allimg/140417/59-14041GQ2040-L.jpg"); imgUrls.add("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1205/25/c2/11755122_1337938898578_800x600.jpg"); imgUrls.add("http://img04.tooopen.com/images/20130114/tooopen_22372502.jpg"); imgUrls.add("http://img3.iqilu.com/data/attachment/forum/201308/21/100932s9pwjxmm4h8jy704.jpg"); imgUrls.add("http://images.ali213.net/picfile/pic/2013/02/25/927_48.jpg"); adapter=new PagerBannerAdapter(PlayBannerActivity.this,imgUrls,viewPager); viewPager.setAdapter(adapter); viewPager.addOnPageChangeListener(this); //第一次进入页面开始轮播图的间隔要久一点,要等图片,还有你其他的页面数据加载出来以后才轮播,提高用户体验 mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,4000); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(positionOffset==0&&positionOffsetPixels==0){ isStill=true; }else { isStill=false; mHandler.removeMessages(MESSAGE_PLAY_IMAGE); } } @Override public void onPageSelected(int position) { //打印position为真实的position pos=position%imgUrls.size(); pageTv.setText(pos+1+"/"+imgUrls.size()); } @Override public void onPageScrollStateChanged(int state) { if(state==0){//静止 isStill=true; mHandler.removeMessages(MESSAGE_PLAY_IMAGE); mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,PLAY_DELAY); } } /** * 消息处理 */ //轮播下一张图片 private static final int MESSAGE_PLAY_IMAGE=1001; //轮播间隔时间 private static final long PLAY_DELAY=2000; private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { /**滑动中,同步播放进度*/ case MESSAGE_PLAY_IMAGE: if(isStill){ if(pos==0){ adapter.setPlayingFirstItem(true); viewPager.setCurrentItem(0,false); viewPager.setCurrentItem(pos+1); adapter.setPlayingFirstItem(false); }else { viewPager.setCurrentItem(pos+1); adapter.setPlayingFirstItem(false); } } break; } } };}
核心代码
//当显示界面加载完时调用该方法 @Override public void finishUpdate(ViewGroup container) { Log.d("BannerView","finishUpdate position======"+mViewPager.getCurrentItem()); int position = mViewPager.getCurrentItem(); if(imgUrls.size()==1){ //TODO 但轮播图片只有一张的时候,什么都不做,或者隐藏小圆点,提示文本之类。当然,如果你只有一张图片,你还想重复滑出这张图片的话,在这里开始你的骚操作 } else { //TODO 但轮播图片超过3张时,每当图片的position超过图片数量时,切换viewpager当前选择item(去除切换动画效果) if (position == 0){ position = imgUrls.size(); //TODO 自动轮播的时候,需要这部判断来让第一张和第二张平滑过渡 if(!isPlay){ mViewPager.setCurrentItem(position,false); } } else if(position>imgUrls.size()+1){ mViewPager.setCurrentItem(position%imgUrls.size(),false); } } }
将getCount return 无穷大,当图片显示在第一张位置(position=0)的时候,我们要让viewpager左边也有图片,所以将position的索引加上图片数量。其实到这里,我们就已经能实现图片无限轮播了,再加上hanlder延迟执行,自动轮播效果就出来了。但position的数量无限增加,item的数量也会一直增加,哪怕viewpager默认只会保存当前和相邻两页,那些被划过的pager占用的内存也不会马上释放掉。所以我们不能让position,也就是item无限增加。 ViewPager的切换中,可以设置去除切换效果setCurrentItem(position,false);
而我们需要找准一个时机,让viewpager切回position最小时的状态 mViewPager.setCurrentItem(position%imgUrls.size(),false);
handler自动轮播的代码就比较简单了,通过 mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,PLAY_DELAY);
和mHandler.removeMessages(MESSAGE_PLAY_IMAGE)来实现。当viewpager处于静止状态时,抛出一个延迟 n秒执行的消息去让viewpager切换到下一页,viewpager切换到下一页后,又会处于静止状态,再次抛出n秒执行的消息。当延迟消息执行前,viewpager状态不是静止了,就把延迟消息取消掉。
至此,一个自动轮播的banner就出来了。
可能你看着,感觉代码一堆,没第三方写起来简洁- -!
ok,让我们稍微封装一下
import android.content.Context;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.support.v4.view.PagerAdapter;import android.support.v4.view.ViewPager;import android.util.AttributeSet;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.FrameLayout;import android.widget.ImageView;import com.bumptech.glide.Glide;import java.util.List;/** * Created by KID on 2017/8/17 0017. */public class BannerView extends FrameLayout implements ViewPager.OnPageChangeListener { //轮播下一张图片 private static final int MESSAGE_PLAY_IMAGE=1001; //轮播间隔时间 private static final long PLAY_DELAY=2000L; //第一张图片间隔多久开始轮播 private static final long FIRST_DELAY=4000L; private ViewPager viewPager; private Context context; private List<String> imgUrls; private BannerPageAdapter bannerPageAdapter; private int cusPosition;//当前看到的选中位置 private boolean isAutoPlay=false; private BannerListener bannerListener; public interface BannerListener{ void OnBannerSelect(int position); void OnBannerClick(int position); } public void setBannerListener(BannerListener bannerListener){ this.bannerListener=bannerListener; } private boolean isStill;//是否静止 int pos ;//看到的position public BannerView(Context context) { super(context); this.context=context; } public BannerView(Context context, AttributeSet attrs) { super(context, attrs); this.context=context; init(context); } public BannerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context=context; } /** * @param isAutoPlay 是否自动播放 ,实现比较暴力,直接不让handler消息执行 */ public void setAutoPlay(boolean isAutoPlay){ this.isAutoPlay=isAutoPlay; } /** * 设置图片 * @param imageList */ public void setImageList(List<String>imageList){ imgUrls=imageList; bannerPageAdapter=new BannerPageAdapter(); viewPager.setAdapter(bannerPageAdapter); viewPager.addOnPageChangeListener(this); //第一次进入页面开始轮播图的间隔要久一点,要等图片,还有你其他的页面数据加载出来以后才轮播,提高用户体验 mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,FIRST_DELAY); } private void init(Context context) { LayoutInflater.from(context).inflate(R.layout.view_banner, this); viewPager= (ViewPager) findViewById(R.id.view_pager); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(positionOffset==0&&positionOffsetPixels==0){ isStill=true; }else { isStill=false; mHandler.removeMessages(MESSAGE_PLAY_IMAGE); } } @Override public void onPageSelected(int position) { //打印position为真实的position pos=position%imgUrls.size(); if(cusPosition!=pos){//引入自动播放后,会在某一位置做一个无动画效果的页面切换,加入这步判断才回调防止重复调用 bannerListener.OnBannerSelect(pos); } cusPosition=pos; } @Override public void onPageScrollStateChanged(int state) { if(state==0){//静止 isStill=true; mHandler.removeMessages(MESSAGE_PLAY_IMAGE); mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,PLAY_DELAY); } } class BannerPageAdapter extends PagerAdapter { private boolean isPlay=false; public BannerPageAdapter() { } //是否自动播放第一张图片到第二张 public void setPlayingFirstItem(boolean isPlay){ this.isPlay=isPlay; } @Override public Object instantiateItem(ViewGroup container, final int position) { final int pos = position%imgUrls.size(); View view = LayoutInflater.from(context).inflate(R.layout.item_vp, container, false); ImageView imageView = (ImageView) view.findViewById(R.id.img_item); Glide.with(context).load(imgUrls.get(pos)).into(imageView); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { bannerListener.OnBannerClick(pos); } }); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } @Override public int getCount() {// 理论上当图片不为1张时,getCount可以设置无穷大,这里设置成图片4倍只是想看会不会滑到头,实际上3倍就够了,设2倍的话,当图片只有2张时可能会碰到头// return Integer.MAX_VALUE; return imgUrls.size()==1?imgUrls.size():imgUrls.size()*4; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } //当显示界面加载完时调用该方法 @Override public void finishUpdate(ViewGroup container) { Log.d("BannerView","finishUpdate position======"+viewPager.getCurrentItem()); int position = viewPager.getCurrentItem(); if(imgUrls.size()==1){ //TODO 但轮播图片只有一张的时候,什么都不做,或者隐藏小圆点,提示文本之类。当然,如果你只有一张图片,你还想重复滑出这张图片的话,在这里开始你的骚操作 } else { //TODO 但轮播图片超过3张时,每当图片的position超过图片数量时,切换viewpager当前选择item(去除切换动画效果) if (position == 0){ position = imgUrls.size(); //TODO 自动轮播的时候,需要这部判断来让第一张和第二张平滑过渡 if(!isPlay){ viewPager.setCurrentItem(position,false); } } else if(position>imgUrls.size()+1){ viewPager.setCurrentItem(position%imgUrls.size(),false); } } } } /** * 消息处理 */ private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { /**滑动中,同步播放进度*/ case MESSAGE_PLAY_IMAGE: if(isStill&&isAutoPlay){ if(pos==0){ bannerPageAdapter.setPlayingFirstItem(true); viewPager.setCurrentItem(0,false); viewPager.setCurrentItem(pos+1); bannerPageAdapter.setPlayingFirstItem(false); }else { viewPager.setCurrentItem(pos+1); bannerPageAdapter.setPlayingFirstItem(false); } } break; } } }; /** * 退出页面后防止handler还执行 */ public void stopPlay(){ if(mHandler!=null)mHandler.removeMessages(MESSAGE_PLAY_IMAGE); }}
使用
import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;import android.widget.Toast;import java.util.ArrayList;import java.util.List;import butterknife.Bind;import butterknife.ButterKnife;public class CusBannerActivity extends AppCompatActivity { @Bind(R.id.bannerView) BannerView bannerView; @Bind(R.id.tv_page) TextView pageTv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cus_banner); ButterKnife.bind(this); final List<String>list=new ArrayList<>(); list.add("http://img2.91.com/uploads/allimg/140417/59-14041GQ2040-L.jpg"); list.add("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1205/25/c2/11755122_1337938898578_800x600.jpg"); list.add("http://img04.tooopen.com/images/20130114/tooopen_22372502.jpg"); list.add("http://img3.iqilu.com/data/attachment/forum/201308/21/100932s9pwjxmm4h8jy704.jpg"); list.add("http://images.ali213.net/picfile/pic/2013/02/25/927_48.jpg"); bannerView.setImageList(list); bannerView.setAutoPlay(true); bannerView.setBannerListener(new BannerView.BannerListener() { @Override public void OnBannerSelect(int position) { pageTv.setText(position+1+"/"+list.size()); Toast.makeText(CusBannerActivity.this, "选中======="+position, Toast.LENGTH_SHORT).show(); } @Override public void OnBannerClick(int position) { Toast.makeText(CusBannerActivity.this, "点击======="+position, Toast.LENGTH_SHORT).show(); } }); pageTv.setText(1+"/"+list.size()); } @Override protected void onDestroy() { super.onDestroy(); bannerView.stopPlay(); }}
在onDestory里,我们需要让hanlder不再执行。PS,有些手机退出界面后,onDestory迟迟不执行,这时候就要结合实际情况,在onPause或者监听返回键,你自己项目的返回按钮执行bannerView.stopPlay()这步操作了。很多三方库封装的banner,定时器没有关闭操作,在页面finish后,延迟执行的操作里,找不到之前被关闭页面的view,导致App崩溃。
代码传送门http://download.csdn.net/download/qq_31390699/9944176
- Android ViewPager实现轻量级Banner
- Android 用ViewPager实现可自动循环的Banner图
- Android 中ViewPager 实现banner无限轮播效果
- Android顶部banner轮播图的两种实现(ViewPager+ViewPagerIndicator/banner)
- 三层继承ViewPager实现无限轮播图Banner
- viewPager 实现banner图 自动轮播
- ViewPager实现Banner图的展示
- Banner+Viewpager+GridView实现首页简单布局
- Android广告Banner实现
- android banner 实现轮播图
- Android Banner 简单实现
- Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果
- Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果
- Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果
- Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果
- Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果
- Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果
- Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果
- MyEclipse的字体调整
- TCP状态测试(CS模型改进3)
- 修改mysql数据库root密码
- 微信支付服务商APP支付申请H5支付申请
- 使用atof需小心
- Android ViewPager实现轻量级Banner
- 区块链基本加密概念
- python
- rsync参数详解
- Android开发规范
- WIN7无法卸载掉中文繁体注音输入法
- Hibernate面试题
- www
- UVA10366(细节模拟)