ViewPager系列之 仿魅族应用的广告BannerView

2024-02-18 19:40

本文主要是介绍ViewPager系列之 仿魅族应用的广告BannerView,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

Banner广告位是APP 中的一个非常重要的位置,为什么呢?因为它能带来money。是一个公司很重要的一个营收点。像那种用户数基数特别大的产品,如facebook、twitter、QQ、微信等等。Banner广告位日营收估计得上千万美刀(猜的,不知道具体数据)。一个漂亮的Banner往往能够吸引用户的眼球,引导用户点击,从而提高转化率。遗憾的是现在的大多数产品的Banner都是千篇一律的,没有什么亮点可言。但是前几天在魅族手机上发现了一个效果不错的Banner,魅族所有自家的APP所用的Banner 引起了我的注意。效果是这样子的:


meizuapp.gif

看到这个Banner 第一眼就吸引了我,随后就反复的体验了几次了,感觉这种Banner的效果还不错。最后想着高仿一个和这种效果差不多的BannerView 。那么本文就讲一下如何实现这样一个BannerView。最终实现的效果如下:


MZBannerView.gif

目录

本文会讲实现仿魅族Banner效果所要用到的一些关键知识点,目录如下图所示。所有的效果已经封装成一个库。详细代码请看github: https://github.com/pinguo-zhouwei/MZBannerView


本文目录.png

仿魅族Banner 效果

在开始实现魅族Banner效果之前,我们先来整理一下实现一个BannerView的思路,首先需要用ViewPager,其次让ViewPager无限轮播。其实BannerView就是一个无限轮播的ViewPager,然后做一些封装处理,让使用更加简单就ok。

现在我们在来看一下魅族的这个Banner。他与普通的banner的区别是当前页显示了前一页和后一页的部分内容。


ViewPager展示多页.png

抛开切换时的动画先不说,要实现这个效果的第一步就是要让ViewPager在一个页面显示多页的内容(当前页+前后页部分)。

1 . ViewPager展示多页
要让ViewPager页面展示多页的内容,就要用到ViewGroup的一个强大的属性。这个属性虽然强大,但是也不常用,可能有些小伙伴不知道(之前我也没用过...),那就是clipChildren属性。这个属性有什么作用呢,我们看一下它的文档介绍:

/*** By default, children are clipped to their bounds before drawing. This* allows view groups to override this behavior for animations, etc.** @param clipChildren true to clip children to their bounds,*        false otherwise* @attr ref android.R.styleable#ViewGroup_clipChildren*/

clipChildren: 默认值为true, 子View 的大小只能在父View规定的范围之内,比如父View的高为50,子View的高为60 ,那么多处的部分就会被裁剪。如果我们设置这个值为false的话,那么多处的部分就不会被裁剪了。

这里我们就可以利用这个属性来实现了这个效果了,我们设置ViewPager的父布局的clipChildren为false。然后设置ViewPager 左右一定的边距,那么左右就空出了一定的区域,利用clipChildren 属性,就能让前后页面的部分显示在当前页了。布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"android:clipChildren="false"android:orientation="vertical"><android.support.v4.view.ViewPagerandroid:id="@+id/view_pager"android:layout_width="match_parent"android:layout_height="200dp"android:layout_marginLeft="30dp"android:layout_marginRight="30dp"/>
</LinearLayout>

这样就能实现ViewPager 展示前后页面的部分内容。

2 . 自定义ViewPager.PageTransformer动画
上面实现了ViewPager当前页面显示前后页的部分内容,但是从最开始魅族的Banner效果我们可以看出,滑动的时候是有 一个放大缩小的动画的。左右显示的部分有一定比例的缩小。这就要用到ViewPager.PageTransformer了。

ViewPager.PageTransformer 干什么的呢?ViewPager.PageTransformer 是用来做ViewPager切换动画的,它是一个接口,里面只有一个方法transformPage

 public interface PageTransformer {/*** Apply a property transformation to the given page.** @param page Apply the transformation to this page* @param position Position of page relative to the current front-and-center*                 position of the pager. 0 is front and center. 1 is one full*                 page position to the right, and -1 is one page position to the left.*/void transformPage(View page, float position);}

虽然只有一个方法,但是它很强大,它能反映出在ViewPager滑动过程中,各个View的位置变化。我们拿到了这些位置变化,就能在这个过程中对View做各种各样的动画了。

要自定义动画,我们就来需要知道positon这个值的变化区间。从官方给的ViewPager的两个示例我们知道,position的变换有三个区间,[-Infinity,-1),[-1,1],(1.Infinity)。
[-Infinity,-1):已经在屏幕之外,看不到了
(1.Infinity): 已经在屏幕之外,看不到了。
[-1,1]: 这个区间是我门操作View动画的重点区间。
我们来看一下官方对于position的解释:

官方的解释:The position parameter indicates where a given page is located relative to the center of the screen. It is a dynamic property that changes as the user scrolls through the pages. When a page fills the screen, its position value is 0. When a page is drawn just off the right side of the screen, its position value is 1. If the user scrolls halfway between pages one and two, page one has a position of -0.5 and page two has a position of 0.5.

根据解释,也就是说当前停留的页面的位置为 0,右边屏幕之外绘制的这个页面位置为 1。那么,A 页面滑到 B 页面有 2 种情况:第一种:左边划出屏幕,那么 A:0 -> -1,B :1 -> 0。第二种:右边划出屏幕,A:0->1, B :-1-> 0

了解了这个方法的变化后,我们就来自定义我们的切换动画,这里很简单,我们只需要一个scale动画。代码如下:

/*** Created by zhouwei on 17/5/26.*/public class CustomTransformer implements ViewPager.PageTransformer {private static final float MIN_SCALE = 0.9F;@Overridepublic void transformPage(View page, float position) {if(position < -1){page.setScaleY(MIN_SCALE);}else if(position<= 1){//float scale = Math.max(MIN_SCALE,1 - Math.abs(position));page.setScaleY(scale);/*page.setScaleX(scale);if(position<0){page.setTranslationX(width * (1 - scale) /2);}else{page.setTranslationX(-width * (1 - scale) /2);}*/}else{page.setScaleY(MIN_SCALE);}}}

效果图是这样的:


仿魅族Banner效果图.png

到此,我们仿魅族Banner的静态效果就实现了。接下来我们就要让Banner动起来,实现无限轮播效果。

图片轮播

上面我们已经实现了Bannerd的静态展示和切换动画,那么我们现在就需要让Banner动起来,实现无限轮播。

ViewPager实现Banner无效轮播效果有2种方案,第一种是:在列表的最前面插入最后一条数据,在列表末尾插入第一个数据,造成循环的假象。第二种方案是:采用getCount 返回 Integer.MAX_VALUE。结下来分别看一下这两种方案。

1 . 在列表的最前面插入最后一条数据,在列表末尾插入第一个数据,造成循环的假象。

这种方法是怎么做的呢?,是这样的:假如我们的列表有3条数据,用三个页面展示,分别编号为1,2,3。我们再创建一个新的列表,长度为真实列表的长度+2,也就是5。在最前面插入最后一条数据,然后在末尾插入第一条数据。新列表就变成了这样了,3-1-2-3-1。如果当前滑到的是0位置(页面3),那就通过ViewPager的setCurrentItem(int item, boolean smoothScroll)方法神不知鬼不觉的切换到3位置(页面3),当滑到4的位置时(页面1),也用这个方法滑到1位置(页面1)。这样给我们的感觉就是无限轮播了。来一张图辅助理解一下。


轮播切换示意图.png

2 . 采用getCount 返回 Integer.MAX_VALUE

让ViewPager 的Adapter getCount 方法返回一个很大的数(这里用Integer.MAX_VALUE),理论上可以无限滑动。当显示完一个真实列表的周期后,又从真实列表的0位置显示数据,造成无限循环轮播的假象。开始时调用 mViewPager.setCurrentItem(Integer.MAX_VALUE /2)设置选中中间位置,这样最开始就可以向左滑动。关键代码:

 int currentItem = getStartSelectItem();//设置当前选中的ItemmViewPager.setCurrentItem(currentItem);private int getStartSelectItem(){// 我们设置当前选中的位置为Integer.MAX_VALUE / 2,这样开始就能往左滑动// 但是要保证这个值与getRealPosition 的 余数为0,因为要从第一页开始显示int currentItem = Integer.MAX_VALUE / 2;if(currentItem % getRealCount()  ==0 ){return currentItem;}// 直到找到从0开始的位置while (currentItem % getRealCount()  != 0){currentItem++;}return currentItem;}

3 . 两种方案选哪一种?

两种方案我都试了一下,都可以实现轮播,但是第一种 方案在有切换动画的时候是有问题的,因为上面我们说了滑动到最后一页切换到第一页时,用的是ViewPager的setCurrentItem(int item, boolean smoothScroll)方法,smoothScroll 的值为false,这样界面就感觉不到我们偷偷的切换。但是这样切换就没有了动画。这样每次切换就会很生硬,因此就抛弃这种方法。选择第二种方案。

轮播我们采用Hanlder的postDelayed方法,关键代码如下:

    private final Runnable mLoopRunnable = new Runnable() {@Overridepublic void run() {if(mIsAutoPlay){mCurrentItem = mViewPager.getCurrentItem();mCurrentItem++;if(mCurrentItem == mAdapter.getCount() - 1){mCurrentItem = 0;mViewPager.setCurrentItem(mCurrentItem,false);mHandler.postDelayed(this,mDelayedTime);}else{mViewPager.setCurrentItem(mCurrentItem);mHandler.postDelayed(this,mDelayedTime);}}else{mHandler.postDelayed(this,mDelayedTime);}}};

在Adapter instantiateItem(ViewGroup container, final int position) 中,现在的这个position是一个很大的数字,我们需要将它转换成一个真实的position,否则会越界报错。

 final int realPosition = position % getRealCount();
        /*** 获取真实的Count* @return*/private int getRealCount(){return  mDatas==null ? 0:mDatas.size();}

通过以上就实现了仿魅族的BannerView,但是这还没完,虽然功能实现了,要想在任何地方拿来就可以使用,简单方便,我们还需要进一步的封装。

封装轮子:MZBannerView

通过上面几步就可以实现仿魅族的BannerView,但是为了使用方便,我们将它封装成一个库,前面一篇文章讲了,如何封装一个通用的ViewPager(文章地址:ViewPager系列之 打造一个通用的ViewPager)。既然要想Banner使用方便,我们也需要封装得通用,可扩展。因为我们的Banner也是用ViewPager 实现的,因此,我们可用上一篇文章的方法,封装一个通用的BannerView。

MZBannerView 有以下功能:
1 . 仿魅族BannerView 效果。
2 . 当普通Banner 使用
3 . 当普通ViewPager 使用。
4 . 当普通ViewPager使用(有魅族Banner效果)

自定义属性

属性名 属性意义 取值
open_mz_mode 是否开启魅族模式 true 为魅族Banner效果,false 则普通Banner效果
canLoop 是否轮播 true 轮播,false 则为普通ViewPager
indicatorPaddingLeft 设置指示器距离左侧的距离 单位为 dp 的值
indicatorPaddingRight 设置指示器距离右侧的距离 单位为 dp 的值
indicatorAlign 设置指示器的位置 有三个取值:left 左边,center 剧中显示,right 右侧显示

通过open_mz_modecanLoop这两个属性来控制MZBannerView 是用作Banner还是普通ViewPager,有4种组合方式
1,仿魅族BannerView(默认的模式)

 app:open_mz_mode="true"app:canLoop="true"

2, 普通BannerView

 app:open_mz_mode="false"app:canLoop="true"

3 ,普通ViewPager (有魅族Banner的切换动画)

 app:open_mz_mode="true"app:canLoop="false"

4, 普通ViewPager

 app:open_mz_mode="false"app:canLoop="false"

使用方法:
1 . xml 布局文件

 <com.zhouwei.mzbanner.MZBannerViewandroid:id="@+id/banner"android:layout_width="match_parent"android:layout_height="200dp"android:layout_marginTop="10dp"app:open_mz_mode="true"app:canLoop="true"app:indicatorAlign="center"app:indicatorPaddingLeft="10dp"/>

2 . activity中代码:

        mMZBanner = (MZBannerView) view.findViewById(R.id.banner);// 设置页面点击事件mMZBanner.setBannerPageClickListener(new MZBannerView.BannerPageClickListener() {@Overridepublic void onPageClick(View view, int position) {Toast.makeText(getContext(),"click page:"+position,Toast.LENGTH_LONG).show();}});List<Integer> list = new ArrayList<>();for(int i=0;i<RES.length;i++){list.add(RES[i]);}// 设置数据mMZBanner.setPages(list, new MZHolderCreator<BannerViewHolder>() {@Overridepublic BannerViewHolder createViewHolder() {return new BannerViewHolder();}});public static class BannerViewHolder implements MZViewHolder<Integer> {private ImageView mImageView;@Overridepublic View createView(Context context) {// 返回页面布局文件View view = LayoutInflater.from(context).inflate(R.layout.banner_item,null);mImageView = (ImageView) view.findViewById(R.id.banner_image);return view;}@Overridepublic void onBind(Context context, int position, Integer data) {// 数据绑定mImageView.setImageResource(data);}}

3 .如果是当Banner使用,注意在onResume 中调用start()方法,在onPause中调用 pause() 方法。如果当普通ViewPager使用,则不需要。

 @Overridepublic void onPause() {super.onPause();mMZBanner.pause();//暂停轮播}@Overridepublic void onResume() {super.onResume();mMZBanner.start();//开始轮播}

其他对外API

    /******************************************************************************************************//**                             对外API                                                               **//******************************************************************************************************///开始轮播start()//停止轮播pause()//设置BannerView 的切换时间间隔setDelayedTime(int delayedTime)// 设置页面改变监听器addPageChangeLisnter(ViewPager.OnPageChangeListener onPageChangeListener)//添加Page点击事件setBannerPageClickListener(BannerPageClickListener bannerPageClickListener)//设置是否显示IndicatorsetIndicatorVisible(boolean visible)// 获取ViewPagerViewPager getViewPager()// 设置 Indicator资源setIndicatorRes(int unSelectRes,int selectRes)//设置页面数据setPages(List<T> datas,MZHolderCreator mzHolderCreator)//设置指示器显示位置setIndicatorAlign(IndicatorAlign indicatorAlign)//设置ViewPager(Banner)切换速度setDuration(int duration)

因为是对ViewPager的包装,所有要设置某些ViewPager的属性,可以通过getViewPager 获取到ViewPager再设置对应属性

效果图:
1, BannerView 轮播效果图:


仿魅族Banner效果.gif

2 普通ViewPager效果图:


MZBanner普通ViewPager效果.gif

总结

本文讲了如何实现一个仿魅族Banner效果。其中讲了一些关键的点和关键代码。其实普通的BannerView 是一样的,只是少了动画而已。最后,将这些功能封装成了一个通用的BannerView 控件。这个控件既有仿魅族Banner的效果,又可以当普通Banner使用。而且还可以当作一个普通的ViewPager使用。

这篇关于ViewPager系列之 仿魅族应用的广告BannerView的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

5分钟获取deepseek api并搭建简易问答应用

《5分钟获取deepseekapi并搭建简易问答应用》本文主要介绍了5分钟获取deepseekapi并搭建简易问答应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1、获取api2、获取base_url和chat_model3、配置模型参数方法一:终端中临时将加

JavaScript中的isTrusted属性及其应用场景详解

《JavaScript中的isTrusted属性及其应用场景详解》在现代Web开发中,JavaScript是构建交互式应用的核心语言,随着前端技术的不断发展,开发者需要处理越来越多的复杂场景,例如事件... 目录引言一、问题背景二、isTrusted 属性的来源与作用1. isTrusted 的定义2. 为

Python调用另一个py文件并传递参数常见的方法及其应用场景

《Python调用另一个py文件并传递参数常见的方法及其应用场景》:本文主要介绍在Python中调用另一个py文件并传递参数的几种常见方法,包括使用import语句、exec函数、subproce... 目录前言1. 使用import语句1.1 基本用法1.2 导入特定函数1.3 处理文件路径2. 使用ex

将Python应用部署到生产环境的小技巧分享

《将Python应用部署到生产环境的小技巧分享》文章主要讲述了在将Python应用程序部署到生产环境之前,需要进行的准备工作和最佳实践,包括心态调整、代码审查、测试覆盖率提升、配置文件优化、日志记录完... 目录部署前夜:从开发到生产的心理准备与检查清单环境搭建:打造稳固的应用运行平台自动化流水线:让部署像

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

Go信号处理如何优雅地关闭你的应用

《Go信号处理如何优雅地关闭你的应用》Go中的优雅关闭机制使得在应用程序接收到终止信号时,能够进行平滑的资源清理,通过使用context来管理goroutine的生命周期,结合signal... 目录1. 什么是信号处理?2. 如何优雅地关闭 Go 应用?3. 代码实现3.1 基本的信号捕获和优雅关闭3.2