自定义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

相关文章

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

使用Python实现无损放大图片功能

《使用Python实现无损放大图片功能》本文介绍了如何使用Python的Pillow库进行无损图片放大,区分了JPEG和PNG格式在放大过程中的特点,并给出了示例代码,JPEG格式可能受压缩影响,需先... 目录一、什么是无损放大?二、实现方法步骤1:读取图片步骤2:无损放大图片步骤3:保存图片三、示php

聊聊springboot中如何自定义消息转换器

《聊聊springboot中如何自定义消息转换器》SpringBoot通过HttpMessageConverter处理HTTP数据转换,支持多种媒体类型,接下来通过本文给大家介绍springboot中... 目录核心接口springboot默认提供的转换器如何自定义消息转换器Spring Boot 中的消息

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2

Linux中的自定义协议+序列反序列化用法

《Linux中的自定义协议+序列反序列化用法》文章探讨网络程序在应用层的实现,涉及TCP协议的数据传输机制、结构化数据的序列化与反序列化方法,以及通过JSON和自定义协议构建网络计算器的思路,强调分层... 目录一,再次理解协议二,序列化和反序列化三,实现网络计算器3.1 日志文件3.2Socket.hpp

C语言自定义类型之联合和枚举解读

《C语言自定义类型之联合和枚举解读》联合体共享内存,大小由最大成员决定,遵循对齐规则;枚举类型列举可能值,提升可读性和类型安全性,两者在C语言中用于优化内存和程序效率... 目录一、联合体1.1 联合体类型的声明1.2 联合体的特点1.2.1 特点11.2.2 特点21.2.3 特点31.3 联合体的大小1