自定义ViewPage+底部指示器(广告图片轮播)

2024-04-07 07:32

本文主要是介绍自定义ViewPage+底部指示器(广告图片轮播),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

自定义ViewPage+底部指示器(广告图片轮播)

     有不少项目都用到了广告图片的轮播,通过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 {@Overridepublic int getCount() {return views == null ? 0 : views.size();}@Overridepublic boolean isViewFromObject(View arg0, Object arg1) {return arg0 == arg1;}@Overridepublic 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);}@Overridepublic 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);};};@Overridepublic 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() {@Overridepublic 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);}}}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {}@Overridepublic 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() {@Overridepublic 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);}}}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {}@Overridepublic 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 {@Overridepublic int getCount() {return views == null ? 0 : views.size();}@Overridepublic boolean isViewFromObject(View arg0, Object arg1) {return arg0 == arg1;}@Overridepublic 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);}@Overridepublic 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);};};@Overridepublic 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;@Overrideprotected 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









这篇关于自定义ViewPage+底部指示器(广告图片轮播)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/881992

相关文章

Python利用PIL进行图片压缩

《Python利用PIL进行图片压缩》有时在发送一些文件如PPT、Word时,由于文件中的图片太大,导致文件也太大,无法发送,所以本文为大家介绍了Python中图片压缩的方法,需要的可以参考下... 有时在发送一些文件如PPT、Word时,由于文件中的图片太大,导致文件也太大,无法发送,所有可以对文件中的图

java获取图片的大小、宽度、高度方式

《java获取图片的大小、宽度、高度方式》文章介绍了如何将File对象转换为MultipartFile对象的过程,并分享了个人经验,希望能为读者提供参考... 目China编程录Java获取图片的大小、宽度、高度File对象(该对象里面是图片)MultipartFile对象(该对象里面是图片)总结java获取图片

Java实战之自助进行多张图片合成拼接

《Java实战之自助进行多张图片合成拼接》在当今数字化时代,图像处理技术在各个领域都发挥着至关重要的作用,本文为大家详细介绍了如何使用Java实现多张图片合成拼接,需要的可以了解下... 目录前言一、图片合成需求描述二、图片合成设计与实现1、编程语言2、基础数据准备3、图片合成流程4、图片合成实现三、总结前

使用Python实现图片和base64转换工具

《使用Python实现图片和base64转换工具》这篇文章主要为大家详细介绍了如何使用Python中的base64模块编写一个工具,可以实现图片和Base64编码之间的转换,感兴趣的小伙伴可以了解下... 简介使用python的base64模块来实现图片和Base64编码之间的转换。可以将图片转换为Bas

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...

css实现图片旋转功能

《css实现图片旋转功能》:本文主要介绍了四种CSS变换效果:图片旋转90度、水平翻转、垂直翻转,并附带了相应的代码示例,详细内容请阅读本文,希望能对你有所帮助... 一 css实现图片旋转90度.icon{ -moz-transform:rotate(-90deg); -webkit-transfo

C#实现添加/替换/提取或删除Excel中的图片

《C#实现添加/替换/提取或删除Excel中的图片》在Excel中插入与数据相关的图片,能将关键数据或信息以更直观的方式呈现出来,使文档更加美观,下面我们来看看如何在C#中实现添加/替换/提取或删除E... 在Excandroidel中插入与数据相关的图片,能将关键数据或信息以更直观的方式呈现出来,使文档更

C#中图片如何自适应pictureBox大小

《C#中图片如何自适应pictureBox大小》文章描述了如何在C#中实现图片自适应pictureBox大小,并展示修改前后的效果,修改步骤包括两步,作者分享了个人经验,希望对大家有所帮助... 目录C#图片自适应pictureBox大小编程修改步骤总结C#图片自适应pictureBox大小上图中“z轴

使用Python将长图片分割为若干张小图片

《使用Python将长图片分割为若干张小图片》这篇文章主要为大家详细介绍了如何使用Python将长图片分割为若干张小图片,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果1. Python需求

SpringBoot 自定义消息转换器使用详解

《SpringBoot自定义消息转换器使用详解》本文详细介绍了SpringBoot消息转换器的知识,并通过案例操作演示了如何进行自定义消息转换器的定制开发和使用,感兴趣的朋友一起看看吧... 目录一、前言二、SpringBoot 内容协商介绍2.1 什么是内容协商2.2 内容协商机制深入理解2.2.1 内容