自定义ViewPage+底部指示器(广告图片轮播)
2015-11-26 00:14
357 查看
自定义ViewPage+底部指示器(广告图片轮播)
有不少项目都用到了广告图片的轮播,通过ViewPage来实现。每次都写一遍总感觉特别麻烦,今天就自己封装一个自带底部指示器的广告图片轮播CustomerViewPage。
整体View是是在一个RelativeLayout中布局,由ViewPage(广告View)+LinearLayout(底部指示器)组成。如下图
底部指示器轮播是通过一个线程来控制,所以实现了Runnable接口。如下代码解析:
CustomerViewPage的成员变量
dot_focused.xml
源码地址:http://download.csdn.net/detail/tianzhaoai/9300821
GitHub源码地址:https://github.com/xyTianZhao/CustomerViewPage
有不少项目都用到了广告图片的轮播,通过ViewPage来实现。每次都写一遍总感觉特别麻烦,今天就自己封装一个自带底部指示器的广告图片轮播CustomerViewPage。
整体View是是在一个RelativeLayout中布局,由ViewPage(广告View)+LinearLayout(底部指示器)组成。如下图
底部指示器轮播是通过一个线程来控制,所以实现了Runnable接口。如下代码解析:
CustomerViewPage的成员变量
<span style="font-size:14px;">public class CustomerViewPage extends RelativeLayout implements Runnable { /** * 要显示的ViewPage对象 */ private ViewPager viewPager; /** * 放置底部指示物的子视图 */ private LinearLayout viewDots; /** * viewDots上的指示物 */ private List<ImageView> dots; /** * ViewPage项 */ private List<View> views; /** * 当前显示第几张图 */ private int position = 0; /** * 可不可以自动轮转(为true当手触摸时不轮转) */ private boolean isContinue = true; /** * 切换时间/ms */ private long changeTime = 1500; /** * 底部指示物之间的间距,默认2dp */ private int dotsSpacing = 2; /** * 底部指示物大小,默认7dp */ private int circleRadio = 10; /** * 轮播图片线程是否存活 */ private boolean isAlive = true; /** * 底部指示物在父View(即viewDots视图)的gravity属性,默认在中间 * Gravity.RIGHT | Gravity.LEFT | Gravity.CENTER */ private int gravity = Gravity.RIGHT; }</span>这里有三个构造函数,以防在代码中动态添加使用:
public CustomerViewPage(Context context) { this(context, null); } public CustomerViewPage(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomerViewPage(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(); }初始化ViewPage和底部LinearLayout:
private void initView() { /** * 初始化ViewPage视图 */ viewPager = new ViewPager(getContext()); LayoutParams viewPagerlp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); addView(viewPager, viewPagerlp); /** * 初始化底部指示器视图 */ viewDots = new LinearLayout(getContext()); LayoutParams viewDotslp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); viewDotslp.addRule(ALIGN_PARENT_BOTTOM); viewDotslp.bottomMargin = dpTopx(5); viewDots.setGravity(gravity); addView(viewDots, viewDotslp); }在内部有一个CustomerViewPageAdapter内部适配器:
class CustomerViewPageAdapter extends PagerAdapter { @Override public int getCount() { return views == null ? 0 : views.size(); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public Object instantiateItem(ViewGroup container, int position) { if (views.get(position).getParent() != null) { ((ViewGroup) views.get(position).getParent()).removeView(views .get(position)); } container.addView(views.get(position)); return views.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(views.get(position)); } }这里特别要注意instantiateItem这个方法。里面一定要写上下面这段代码,如果不写的话,有可能会抛出非法异常操作。如果只是一个Activity中使用不会有这个异常,但是超过两个地方以上使用这个CustomerViewPage的话就回抛出illegalStateException(非法操作异常),这是因为第一次将ViewPage中的VIew添加进去的时候,这个View是没有父控件的,但是当第二次添加的时候,该View已经有了一个父控件了,不允许再有其他的父控件,所以如果该View的父控件不为null,就必须先将该View的从父控件中remove掉,再添加进去。否则就回抛出illegalStateException
if (views.get(position).getParent() != null) { ((ViewGroup) views.get(position).getParent()).removeView(views .get(position)); }控制底部指示器轮播的线程:
Handler pagerHandler = new Handler() { public void handleMessage(android.os.Message msg) { viewPager.setCurrentItem(msg.what); super.handleMessage(msg); }; }; @Override public void run() { while (isAlive) { if (isContinue) { pagerHandler.sendEmptyMessage(position); position = (position + 1) % views.size(); try { Thread.sleep(changeTime); } catch (InterruptedException e) { } } } }这个就是暴露在外部的一个接口,设置ViewPage的View:
public void setViewPageViews(List<View> views) { this.views = views; // 初始化底部的圆 addDots(views.size()); viewPager.setAdapter(new CustomerViewPageAdapter()); viewPager.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int index) { position = index; for (int i = 0; i < dots.size(); i++) { if (position == i) { dots.get(i).setBackgroundResource( R.drawable.dot_focused); } else { dots.get(i) .setBackgroundResource(R.drawable.dot_normal); } } } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }); viewPager.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: isContinue = false; break; case MotionEvent.ACTION_UP: isContinue = true; break; default: isContinue = true; break; } return false; } }); new Thread(this).start(); } private void addDots(int size) { dots = new ArrayList<ImageView>(); for (int i = 0; i < size; i++) { ImageView dot = new ImageView(getContext()); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( dpTopx(circleRadio), dpTopx(circleRadio)); params.setMargins(dpTopx(dotsSpacing), 0, dpTopx(dotsSpacing), 0); dot.setLayoutParams(params); if (i == 0) { dot.setBackgroundResource(R.drawable.dot_focused); } else { dot.setBackgroundResource(R.drawable.dot_normal); } dots.add(dot); viewDots.addView(dot); } }当该页面不可见的时候可以调用stop()方法停止掉该线程,否则她还是会一直跑下去。。。
public void stop() { isAlive = false; }将dp转化为px:
private int dpTopx(int dp) { float scale = getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); }基本上就没什么了,代码中都有解释,下面贴上CustomerViewPage整个代码:
package com.tz.viewpagedemo; import java.util.ArrayList; import java.util.List; import android.annotation.SuppressLint; import android.content.Context; import android.os.Handler; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; /** * * @author TianZhao * Time 2015年11月26日 * xiyouMobile */ @SuppressLint("HandlerLeak") public class CustomerViewPage extends RelativeLayout implements Runnable { /** * 要显示的ViewPage对象 */ private ViewPager viewPager; /** * 放置底部指示物的子视图 */ private LinearLayout viewDots; /** * viewDots上的指示物 */ private List<ImageView> dots; /** * ViewPage项 */ private List<View> views; /** * 当前显示第几张图 */ private int position = 0; /** * 可不可以自动轮转(为true当手触摸时不轮转) */ private boolean isContinue = true; /** * 切换时间/ms */ private long changeTime = 1500; /** * 底部指示物之间的间距,默认2dp */ private int dotsSpacing = 2; /** * 底部指示物大小,默认7dp */ private int circleRadio = 10; /** * 轮播图片线程是否存活 */ private boolean isAlive = true; /** * 底部指示物在父View(即viewDots视图)的gravity属性,默认在中间 * Gravity.RIGHT | Gravity.LEFT | Gravity.CENTER */ private int gravity = Gravity.RIGHT; public CustomerViewPage(Context context) { this(context, null); } public CustomerViewPage(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomerViewPage(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(); } private void initView() { /** * 初始化ViewPage视图 */ viewPager = new ViewPager(getContext()); LayoutParams viewPagerlp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); addView(viewPager, viewPagerlp); /** * 初始化底部指示器视图 */ viewDots = new LinearLayout(getContext()); LayoutParams viewDotslp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); viewDotslp.addRule(ALIGN_PARENT_BOTTOM); viewDotslp.bottomMargin = dpTopx(5); viewDots.setGravity(gravity); addView(viewDots, viewDotslp); } public void setViewPageViews(List<View> views) { this.views = views; // 初始化底部的圆 addDots(views.size()); viewPager.setAdapter(new CustomerViewPageAdapter()); viewPager.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int index) { position = index; for (int i = 0; i < dots.size(); i++) { if (position == i) { dots.get(i).setBackgroundResource( R.drawable.dot_focused); } else { dots.get(i) .setBackgroundResource(R.drawable.dot_normal); } } } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }); viewPager.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: isContinue = false; break; case MotionEvent.ACTION_UP: isContinue = true; break; default: isContinue = true; break; } return false; } }); new Thread(this).start(); } private void addDots(int size) { dots = new ArrayList<ImageView>(); for (int i = 0; i < size; i++) { ImageView dot = new ImageView(getContext()); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( dpTopx(circleRadio), dpTopx(circleRadio)); params.setMargins(dpTopx(dotsSpacing), 0, dpTopx(dotsSpacing), 0); dot.setLayoutParams(params); if (i == 0) { dot.setBackgroundResource(R.drawable.dot_focused); } else { dot.setBackgroundResource(R.drawable.dot_normal); } dots.add(dot); viewDots.addView(dot); } } class CustomerViewPageAdapter extends PagerAdapter { @Override public int getCount() { return views == null ? 0 : views.size(); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public Object instantiateItem(ViewGroup container, int position) { if (views.get(position).getParent() != null) { ((ViewGroup) views.get(position).getParent()).removeView(views .get(position)); } container.addView(views.get(position)); return views.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(views.get(position)); } } Handler pagerHandler = new Handler() { public void handleMessage(android.os.Message msg) { viewPager.setCurrentItem(msg.what); super.handleMessage(msg); }; }; @Override public void run() { while (isAlive) { if (isContinue) { pagerHandler.sendEmptyMessage(position); position = (position + 1) % views.size(); try { Thread.sleep(changeTime); } catch (InterruptedException e) { } } } } private int dpTopx(int dp) { float scale = getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } public void stop() { isAlive = false; } }底部的小圆点(指示物)是通过xml加载的:
dot_focused.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" > <solid android:color="#aaFFFFFF" /> <corners android:radius="10dip" /> </shape>dot_normal.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" > <solid android:color="#33000000" /> <corners android:radius="10dip" /> </shape>接下来就是使用了:
package com.tz.viewpagedemo; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.View; import android.widget.ImageView; /** * * @author TianZhao * Time 2015年11月26日 * xiyouMobile */ public class MainActivity extends Activity { private CustomerViewPage viewPage; private List<View> views; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPage = (CustomerViewPage) findViewById(R.id.vp); initViews(); viewPage.setViewPageViews(views); } private void initViews() { views = new ArrayList<>(); ImageView imageView1 = new ImageView(this); ImageView imageView2 = new ImageView(this); ImageView imageView3 = new ImageView(this); ImageView imageView4 = new ImageView(this); ImageView imageView5 = new ImageView(this); imageView1.setBackgroundColor(Color.parseColor("#123456")); views.add(imageView1); imageView2.setBackgroundColor(Color.parseColor("#145826")); views.add(imageView2); imageView3.setBackgroundColor(Color.parseColor("#874592")); views.add(imageView3); imageView4.setBackgroundColor(Color.parseColor("#658415")); views.add(imageView4); imageView5.setBackgroundColor(Color.parseColor("#845163")); views.add(imageView5); } }下面是运行效果图:
源码地址:http://download.csdn.net/detail/tianzhaoai/9300821
GitHub源码地址:https://github.com/xyTianZhao/CustomerViewPage
相关文章推荐
- org.jsoup.select.Selector
- LightOJ 1046 Rider
- org.jsoup.select.Selector
- JS开发者常用的10个Sublime Text插件
- const的使用
- .net 估计要死在你手里了
- Tomcat+Apache 负载均衡
- web后端 文件上传
- hdoj2073(无限的路
- 制作initrd(2):update-initramfs和mkinitramfs脚本分析
- js的 style.width 取不到元素的宽度值
- 操作系统核心原理-1.操作系统导论
- 将两个矩阵相乘,A为x行y列的矩阵,B为y行z列的矩阵,A*B
- Windows上简单的Apache守护进程
- qt中文乱码问题
- ubuntu下django和apache的部署
- Apache Spark大数据分析入门(一)
- cdoj 574 High-level ancients dfs序+线段树
- Sublime text 2/3 中 Package Control 的安装与使用方法
- 1.环境搭建与命令入门