我们常用的app中,首页多半都会展示广告的地方(特别是电商产品),比如下面这张图中被圈出的部分,就是我所说的广告栏
广告栏的需求就是:展示活动的广告图,
(1)用户可以左右滑动来选择;
(2)广告可以自动轮流播放,即过一定时间,展示下一张广告;
(3)当用户手指触摸到广告栏时,自动播放停止
(5)图片右下角的圆点和广告图播放位置显示一致,即:播放第一种,第一个圆点亮,其他都暗
(6)圆点可以点击,即:点第三个圆点,展示第三张广告图
实现
看到需求1,首先想到控件ViewPager,看到需求6,想到的是右下角的圆点是RadioButton,这样很容易就控制了单选。先满足这两个要求的实现吧!
广告栏的布局:
main_activity.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="400dp" android:background="#696969" /> <include layout="@layout/radio_layout" /> </RelativeLayout>radio_layout.xml就是单选按钮的布局:
<?xml version="1.0" encoding="utf-8"?> <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RadioGroup" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/viewpager" android:layout_alignParentRight="true" android:layout_marginBottom="5dp" android:orientation="horizontal"> <RadioButton android:id="@+id/radio1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:button="@null" android:checked="true" android:drawableLeft="@drawable/radiobutton_selector" android:padding="2dp" /> <RadioButton android:id="@+id/radio2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:button="@null" android:drawableLeft="@drawable/radiobutton_selector" android:padding="2dp" /> <RadioButton android:id="@+id/radio3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:button="@null" android:drawableLeft="@drawable/radiobutton_selector" android:padding="2dp" /> <RadioButton android:id="@+id/radio4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:button="@null" android:drawableLeft="@drawable/radiobutton_selector" android:padding="2dp" /> </RadioGroup>
public class ViewPagerAdapter extends PagerAdapter { //界面列表 private List<View> views; public ViewPagerAdapter (List<View> views){ this.views = views; } @Override public int getCount() { return views==null?0:views.size(); } @Override public void destroyItem(View container, int position, Object arg2) { ((ViewPager)container).removeView(views.get(position)); } @Override public Object instantiateItem(View container, int position) { ((ViewPager)container).addView(views.get(position), 0); return views.get(position); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return (arg0 == arg1); }ViewPager的初始化,和setAdapter代码:@Override // public void destroyItem(ViewGroup container, int position, Object object) { // container.removeView((View) object); // } // // @Override // public Object instantiateItem(ViewGroup container, int position) { // /** // * 设置具体需要填充的View对象 // */ // int i = position % views.size(); // container.addView(views.get(i)); // return views.get(i); // }}
views = new ArrayList<>(); LinearLayout.LayoutParams mParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); for (int i = 0; i < picPath.length; i++) { ImageView iv = new ImageView(this); iv.setLayoutParams(mParams); iv.setImageResource(pics[i]); iv.setScaleType(ScaleType.FIT_XY); views.add(iv); } vpAdapter = new ViewPagerAdapter(views); vp.setAdapter(vpAdapter);上面这种情况就是我们一般使用的ViewPager,我们的list有多少数据就传过去多少数据,在Adapter的getCount()方法里面,list.size()来决定我们ViewPager显示的数据的条数,这种情况下,当 你已经右滑到最后一张的时候,就不能再右滑动,左滑动到第一张的时候,就不能再向左滑动了。
public class ViewPagerAdapter extends PagerAdapter { //界面列表 private List<View> views; public ViewPagerAdapter (List<View> views){ this.views = views; } @Override public int getCount() { return Integer.MAX_VALUE; //设置成最大,使用户看不到边界 // return views==null?0:views.size(); } @Override public void destroyItem(View container, int position, Object arg2) { ((ViewPager)container).removeView(views.get(position % views.size())); // ((ViewPager)container).removeView(views.get(position)); } @Override public Object instantiateItem(View container, int position) { ((ViewPager)container).addView(views.get(position % views.size()), 0); return views.get(position % views.size()); // ((ViewPager)container).addView(views.get(position), 0); // return views.get(position); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return (arg0 == arg1); } }上面的代码中,注释的地方就是不循环的代码,注意:我们在getCount()里面给的是最大的条数,这样保证ViewPager可以一直左滑动右滑动,让数据循环显示重点在views.get(position%views.size())这句代码上,给用户的感觉就是在循环显示,其实呢,不是循环,你打印position就知道,它一直在递增,我们只是让views.get()出来的数据是循环的(views.get出来的数据:1,2,3,......list.size(),1,2,3.....list.size()这样实现循环的)。而要实现一开始就可以左滑动,网友的办法是 vp.setCurrentItem(views.size()*10);这样只是一个障眼法,让用户看到的第一张position就不是0开始的。参考
vp.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int position) { position=position%views.size();//循环时,一定要有这句,不然会越界异常 radioButton = (RadioButton) group.getChildAt(position); radioButton.setChecked(true); } @Override public void onPageScrolled(int positon, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int state) { } });
RadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkId) { switch (checkId) { case R.id.radio1: vp.setCurrentItem(0); break; case R.id.radio2: vp.setCurrentItem(1); break; case R.id.radio3: vp.setCurrentItem(2); break; case R.id.radio4: vp.setCurrentItem(3); break; default: break; } } }); }
一般情况下,网页版的会考虑上面的需求(6),手机端很少有app会这样做,可能是手机页面本身就小,圆点就更小了,让用户可点的话,用户很容易就会点击到广告图,所以这种效果不佳。所以我们去掉需求6,那么圆点就是图片实现,每次ViewPager显示的图片改变的时候,都要调用下面的setCurPage(intpage, intcount)方法,去改变圆点的显示效果,page是当前页,count是广告图片数量,这样还有一个好处就是,可以根据接口获得的广告图片数量,来决定圆点的数量,比直接在布局中写死灵活很多。
这样广告栏的布局变成:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="400dp" android:background="#696969" /> <LinearLayout android:id="@+id/three_tv_viewpager_dot1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerInParent="true" android:orientation="horizontal" android:layout_marginBottom="12dp"/> </RelativeLayout>
LinearLayout dot = (LinearLayout) view.findViewById(R.id.three_tv_viewpager_dot1);
public void setCurPage(int page, int count) { try { dot.removeAllViews(); for (int i = 0; i < count; i++) { ImageView imgCur = new ImageView(getActivity()); imgCur.setBackgroundResource(R.drawable.news_dot1); imgCur.setId(i); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( -2, -2); lp.rightMargin = 5; if (imgCur.getId() == page) { imgCur.setBackgroundResource(R.drawable.news_dot); } dot.addView(imgCur, lp); } } catch (Exception e) { // TODO: handle exception } }
(1)定义一个全局变量:private int currentItem; // 当前页面
(这是为了记录当前页面,这样自动播放的时候才会知道该显示哪一页,所以单选按钮切换的时候viewpager.setCurrentItem(x);更改当前页面的currentItem 值:currentItem = x,滑动改变页面的时候也要写:currentItem = position;)
(2)自动滚动肯定要开线程,线程做的就是停一点时间(比如2秒),完了之后告诉Hanlder去改变显示的页面:
private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { // 设置当前页面 vp.setCurrentItem(currentItem); }; };
(3)在onStart()里面开启ScheduledExecutorService,再onPause()里面关闭ScheduledExecutorService
private ScheduledExecutorService scheduledExecutorService; @Override protected void onStart() { super.onStart(); scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); // 每隔2秒钟切换一张图片 scheduledExecutorService.scheduleWithFixedDelay(new ViewPagerTask(), 2, 2, TimeUnit.SECONDS); // scheduleAtFixedRate(command, initialDelay, period, unit); // command:执行线程 initialDelay:初始化延时 period:前一次执行结束到下一次执行开始的间隔时间(间隔执行延迟时间) // unit:计时单位 } @Override protected void onStop() { if (scheduledExecutorService!=null){ scheduledExecutorService.shutdown(); } super.onStop(); } // 切换图片 private class ViewPagerTask implements Runnable { @Override public void run() { currentItem = (currentItem + 1) % pics.length; // 更新界面 handler.obtainMessage().sendToTarget(); // message对象sendToTarget(),handler对象sendMessage(); } }
这就能实现图片像广告一样自动播放,也可以滑动选择。
实现暂停一段时间再显示,这个功能有很多方法,上面讲到的ScheduledExecutorService可以,Timer计时器也可以,或者直接用handler设置延迟时间有可以做到。
private int currentItem=0; // 当前页面 /** * 焦点图自动滚动方法 * * @param delayTimeInMills */ private final int SCROLL_WHAT = 1; private boolean mIsStop = false;// 焦点图触摸暂停监听 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case SCROLL_WHAT: if (currentItem == views.size()) { currentItem = 0; } //点击图片时,自动切换暂停 if (!mIsStop) { currentItem++; vp.setCurrentItem(currentItem % views.size()); } sendScrollMessage(4000); break; } } }; private void sendScrollMessage(long delayTimeInMills) { /** remove messages before, keeps one message is running at most **/ handler.removeMessages(SCROLL_WHAT); handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills); }
setAdapter之后就开启自动切换,
vp.setAdapter(vpAdapter); vp.setCurrentItem(0);// 默认显示第一张 sendScrollMessage(300);// 自动切换启动
ViewPager触摸监听,实现触摸的时候,暂停自动切换,即功能3
//ViewPager 触摸事件 vp.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View arg0, MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { mIsStop = true; } else if (action == MotionEvent.ACTION_UP) { mIsStop = false; } return false; } });
目前:手动循环滑动加上自动播放共同的效果,会出现异常,有待解决
左右滑动,不支持循环滑动,自动播放,可以点击右下角切换ViewPager显示图的:
源代码:http://download.csdn.net/detail/qq_30716173/9273873