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

相关文章

C#实现将Excel表格转换为图片(JPG/ PNG)

《C#实现将Excel表格转换为图片(JPG/PNG)》Excel表格可能会因为不同设备或字体缺失等问题,导致格式错乱或数据显示异常,转换为图片后,能确保数据的排版等保持一致,下面我们看看如何使用C... 目录通过C# 转换Excel工作表到图片通过C# 转换指定单元格区域到图片知识扩展C# 将 Excel

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效

Python实现AVIF图片与其他图片格式间的批量转换

《Python实现AVIF图片与其他图片格式间的批量转换》这篇文章主要为大家详细介绍了如何使用Pillow库实现AVIF与其他格式的相互转换,即将AVIF转换为常见的格式,比如JPG或PNG,需要的小... 目录环境配置1.将单个 AVIF 图片转换为 JPG 和 PNG2.批量转换目录下所有 AVIF 图

详解如何通过Python批量转换图片为PDF

《详解如何通过Python批量转换图片为PDF》:本文主要介绍如何基于Python+Tkinter开发的图片批量转PDF工具,可以支持批量添加图片,拖拽等操作,感兴趣的小伙伴可以参考一下... 目录1. 概述2. 功能亮点2.1 主要功能2.2 界面设计3. 使用指南3.1 运行环境3.2 使用步骤4. 核

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当