自定义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实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

Python基于微信OCR引擎实现高效图片文字识别

《Python基于微信OCR引擎实现高效图片文字识别》这篇文章主要为大家详细介绍了一款基于微信OCR引擎的图片文字识别桌面应用开发全过程,可以实现从图片拖拽识别到文字提取,感兴趣的小伙伴可以跟随小编一... 目录一、项目概述1.1 开发背景1.2 技术选型1.3 核心优势二、功能详解2.1 核心功能模块2.

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个

如何自定义一个log适配器starter

《如何自定义一个log适配器starter》:本文主要介绍如何自定义一个log适配器starter的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求Starter 项目目录结构pom.XML 配置LogInitializer实现MDCInterceptor

使用Python实现base64字符串与图片互转的详细步骤

《使用Python实现base64字符串与图片互转的详细步骤》要将一个Base64编码的字符串转换为图片文件并保存下来,可以使用Python的base64模块来实现,这一过程包括解码Base64字符串... 目录1. 图片编码为 Base64 字符串2. Base64 字符串解码为图片文件3. 示例使用注意

c/c++的opencv实现图片膨胀

《c/c++的opencv实现图片膨胀》图像膨胀是形态学操作,通过结构元素扩张亮区填充孔洞、连接断开部分、加粗物体,OpenCV的cv::dilate函数实现该操作,本文就来介绍一下opencv图片... 目录什么是图像膨胀?结构元素 (KerChina编程nel)OpenCV 中的 cv::dilate() 函

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

使用Python实现调用API获取图片存储到本地的方法

《使用Python实现调用API获取图片存储到本地的方法》开发一个自动化工具,用于从JSON数据源中提取图像ID,通过调用指定API获取未经压缩的原始图像文件,并确保下载结果与Postman等工具直接... 目录使用python实现调用API获取图片存储到本地1、项目概述2、核心功能3、环境准备4、代码实现

Java实现图片淡入淡出效果

《Java实现图片淡入淡出效果》在现代图形用户界面和游戏开发中,**图片淡入淡出(FadeIn/Out)**是一种常见且实用的视觉过渡效果,它可以用于启动画面、场景切换、轮播图、提示框弹出等场景,通过... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细