转载请标明出处:一片枫叶的专栏
上一篇文章中讲解了我最近写的一个快速集成二维码扫描库,其核心的实现扫描的功能,是通过调用ZXing库实现的。由于在实现二维码扫描功能的时候发现集成二维码扫描功能并不是特别方便,于是有了将其制作成标准库的想法,这个二维码库能够快速,方便的集成二维码扫描功能,项目地址是在:android-zxingLibrary**,在项目开源后有不少同学提出了许多不错的意见,目前也在不断的迭代中,自己也学到了很多。
本文我们将讲解一个简单,强大的广告活动弹窗控件。不少App在打开的时候需要弹出一个广告活动弹窗,点击广告活动弹窗中的图片就会跳转到一个H5页面,加载显示具体的活动内容等,为了方便大家的操作,我将其做成了一个标准控件:android-adDialog。需要说明的是,虽然其名称为android-adDialog,并且表现形式也和Dialog类似,但是这里并不是通过Dialog实现的,而是自定义的View。
本项目的github地址:android-adDialog,欢迎star和follow。
在介绍具体的使用说明之前,我们先看一下简单的实现效果:
使用说明
显示一个默认广告弹窗,支持单广告活动、多广告活动,当弹窗显示多广告是默认显示底部小圆圈,当显示单活动时默认不显示底部小圆圈;
默认支持弹窗从上,下,左,右,左上,右上,左下,右下等八个方向弹出,更可以设置从任意指定的角度(0-360)弹出;
默认继承了弹性动画效果,可以设定弹性动画弹性参数,动画速度等;
支持对弹窗背景透明色的更改,支持对弹窗透明,支持设置弹窗背景全屏显示或者是只在内容区域显示;
支持设定弹窗宽高比,支持设置弹窗距离屏幕两侧距离,支持设置关闭按钮是否显示;
支持对关闭按钮点击事件的回调,对每一项广告活动点击事件的回调等;
支持对ViewPager滑动动画效果的设定,支持自定义ViewPager滑动动画效果;
API说明:
所有的设置参数均返回AdManager对象,所以可以直接链式调用。
AdManager adManager = new AdManager(MainActivity.this, advList);
aDMamager.
/**
* 设置弹窗背景全屏显示还是在内容区域显示
*/
.setOverScreen(true)
/**
* 设置ViewPager的滑动动画
*/
.setPageTransformer(new DepthPageTransformer())
/**
* 设置弹窗距离屏幕两侧的距离(单位dp)
*/
.setPadding(100)
/**
* 设置弹窗的宽高比
*/
.setWidthPerHeight(0.75f)
/**
* 设置弹窗的背景色(当弹窗背景设置透明时,此设置失效)
*/
.setBackViewColor(Color.parseColor("#AA333333"))
/**
* 设置弹窗背景是否透明
*/
.setAnimBackViewTransparent(true)
/**
* 设置弹窗关闭图标是否可见
*/
.setDialogCloseable(false)
/**
* 设置弹窗弹性滑动弹性值
*/
.setBounciness(15)
/**
* 设置弹窗弹性滑动速度值
*/
.setSpeed(5)
/**
* 设定弹窗点击事件回调
*/
.setOnImageClickListener(new AdManager.OnImageClickListener() {})
/**
* 设定关闭按钮点击事件回调
*/
.setOnCliseClickListener(new OnClickListener() {})
/**
* 开始执行弹窗的显示操作,可传值为0-360,0表示从右开始弹出,逆时针方向,也可以传入自定义的方向值
*/
.showAdDialog(AdConstant.ANIM_UP_TO_DOWN)
使用方式:
compile 'cn.yipianfengye.android:ad-library:1.0'
/**
* 初始化数据
*/
private void initData() {
advList = new ArrayList<>();
AdInfo adInfo = new AdInfo();
adInfo.setActivityImg("https://raw.githubusercontent.com/yipianfengye/android-adDialog/master/images/testImage1.png");
advList.add(adInfo);
adInfo = new AdInfo();
adInfo.setActivityImg("https://raw.githubusercontent.com/yipianfengye/android-adDialog/master/images/testImage2.png");
advList.add(adInfo);
}
这里只要是初始化图片的UI地址信息,方便我们的后续下载操作。
/**
* 创建广告活动管理对象
*/
AdManager adManager = new AdManager(MainActivity.this, advList);
adManager.setOverScreen(true)
.setPageTransformer(new DepthPageTransformer());
/**
* 执行弹窗的显示操作
*/
adManager.showAdDialog(AdConstant.ANIM_DOWN_TO_UP);
怎么样是不是很简单?下面我们可以来看一下具体API。
具体的API说明:
在执行AdManager的showAdDialog方法时,需要传递一个int型的animType参数,我们默认定义了八个该类型的参数,默认如下:
// ####################### 弹出动画效果 ###########################
/**
* 广告活动弹窗动画-从上至下
*/
public static final int ANIM_UP_TO_DOWN = -11;
/**
* 广告活动弹窗动画-从下至上
*/
public static final int ANIM_DOWN_TO_UP = -12;
/**
* 广告活动弹窗动画-从左至右
*/
public static final int ANIM_LEFT_TO_RIGHT = -13;
/**
* 广告活动弹窗动画-从右至左
*/
public static final int ANIM_RIGHT_TO_LEFT = -14;
/**
* 广告活动弹窗动画-从左上弹出
*/
public static final int ANIM_UPLEFT_TO_CENTER = -15;
/**
* 广告活动弹窗动画-从右上弹出
*/
public static final int ANIM_UPRIGHT_TO_CENTER = -16;
/**
* 广告活动弹窗动画-从左下弹出
*/
public static final int ANIM_DOWNLEFT_TO_CENTER = -17;
/**
* 广告活动弹窗动画-从右下弹出
*/
public static final int ANIM_DOWNRIGHT_TO_CENTER = -18;
好吧,如果你觉得还不够好,我想让弹窗从右上侧30度角的弹出可以么?这也是支持的,只需要你传递的int型的animType的时候传递30就好了,如下:
/**
* 执行弹窗的显示操作(参数的范围:0-360,0表示从右侧弹出,逆时针旋转)
*/
adManager.showAdDialog(30);
那么我们看一下执行效果呢:
/**
* 自定义设置广告活动弹窗距离屏幕两侧距离以及宽高比
*/
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AdManager adManager = new AdManager(MainActivity.this, advList);
adManager.setOnImageClickListener(new AdManager.OnImageClickListener() {
@Override
public void onImageClick(View view, AdInfo advInfo) {
Toast.makeText(MainActivity.this, "您点击了ViewPagerItem...", Toast.LENGTH_SHORT).show();
}
})
.setPadding(100)
.setWidthPerHeight(0.5f)
.showAdDialog(AdConstant.ANIM_UP_TO_DOWN);
}
});
然后我们看一下执行效果:
怎么样?是不是发现弹窗的宽高比和距离屏幕两侧的距离发生了变化?
/**
* 自定义弹窗背景颜色,弹窗是否覆盖全屏,关闭按钮是否显示等
*/
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AdManager adManager = new AdManager(MainActivity.this, advList);
adManager.setOnImageClickListener(new AdManager.OnImageClickListener() {
@Override
public void onImageClick(View view, AdInfo advInfo) {
Toast.makeText(MainActivity.this, "您点击了ViewPagerItem...", Toast.LENGTH_SHORT).show();
}
})
.setBackViewColor(Color.parseColor("#AA333333"))
.setDialogCloseable(false)
.showAdDialog(AdConstant.ANIM_UP_TO_DOWN);
}
});
然后我们来看一下具体的实现效果:
可以看到我们更改了弹窗的背景颜色以及未显示关闭按钮
/**
* 自定义设定弹窗弹性参数和速度参数
*/
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AdManager adManager = new AdManager(MainActivity.this, advList);
adManager.setOnImageClickListener(new AdManager.OnImageClickListener() {
@Override
public void onImageClick(View view, AdInfo advInfo) {
Toast.makeText(MainActivity.this, "您点击了ViewPagerItem...", Toast.LENGTH_SHORT).show();
}
})
.setBounciness(20)
.setSpeed(4)
.showAdDialog(AdConstant.ANIM_UP_TO_DOWN);
}
});
为了明显的展示出动画效果,我们设置的弹性参数和速度参数比较大,是不是比较出来了这两个参数的作用?
/**
* 自定义设置弹窗ViewPager滑动动画
*/
button5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AdManager adManager = new AdManager(MainActivity.this, advList);
adManager.setOnImageClickListener(new AdManager.OnImageClickListener() {
@Override
public void onImageClick(View view, AdInfo advInfo) {
Toast.makeText(MainActivity.this, "您点击了ViewPagerItem...", Toast.LENGTH_SHORT).show();
}
})
.setPageTransformer(new RotateDownPageTransformer())
.showAdDialog(AdConstant.ANIM_UP_TO_DOWN);
}
});
然后我们看一下弹窗的滑动动画:
这里我内置了三种ViewPager的滑动动画效果:
DepthPageTransformer;
RotateDownPageTransformer;
ZoomOutPageTransformer;
我们还可以自定义实现:PageTransformer自定义出自己的滑动动画效果,更多关于PageTransformer的知识,可参考鸿洋大神的: Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
/**
* 自定义设置弹窗ViewPagerItem点击事件,关闭按钮点击事件回调
*/
button6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AdManager adManager = new AdManager(MainActivity.this, advList);
adManager.setOnImageClickListener(new AdManager.OnImageClickListener() {
@Override
public void onImageClick(View view, AdInfo advInfo) {
Toast.makeText(MainActivity.this, "您点击了ViewPagerItem...", Toast.LENGTH_SHORT).show();
}
})
.setOnCloseClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "您点击了关闭按钮...", Toast.LENGTH_SHORT).show();
}
})
.showAdDialog(AdConstant.ANIM_UP_TO_DOWN);
}
});
好吧,以上就是广告活动弹窗的API,除了以上还可以添加其他的一些API,欢迎提出。
自定义广告活动弹窗的实现过程:
主要用于管理自定义View,ViewPager等对象;这里我们可以看一下弹窗展示方法的实现:
/**
* 开始执行显示广告弹窗的操作
* @param animType
*/
public void showAdDialog(final int animType) {
contentView = LayoutInflater.from(context).inflate(R.layout.ad_dialog_content_layout, null);
adRootContent = (RelativeLayout) contentView.findViewById(R.id.ad_root_content);
viewPager = (ViewPager) contentView.findViewById(R.id.viewPager);
mIndicator = (FlycoPageIndicaor) contentView.findViewById(R.id.indicator);
adAdapter = new AdAdapter();
viewPager.setAdapter(adAdapter);
if (pageTransformer != null) {
viewPager.setPageTransformer(true, pageTransformer);
}
mIndicator.setViewPager(viewPager);
isShowIndicator();
animDialogUtils = AnimDialogUtils.getInstance(context)
.setAnimBackViewTransparent(isAnimBackViewTransparent)
.setDialogCloseable(isDialogCloseable)
.setDialogBackViewColor(backViewColor)
.setOnCloseClickListener(onCloseClickListener)
.setOverScreen(isOverScreen)
.initView(contentView);
setRootContainerHeight();
// 延迟1s展示,为了避免ImageLoader还为加载完缓存图片时就展示了弹窗的情况
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
animDialogUtils.show(animType, bounciness, speed);
}
}, 1000);
}
在广告活动弹窗对象中我们调用了:
AnimDialogUtils.getInstance(context)
.setAnimBackViewTransparent(isAnimBackViewTransparent)
.setDialogCloseable(isDialogCloseable)
.setDialogBackViewColor(backViewColor)
.setOnCloseClickListener(onCloseClickListener)
.setOverScreen(isOverScreen)
.initView(contentView);
而这里的AnimDialogUtils主要用于实现类似Dialog的效果,这里我们看一下自定义弹窗展示方法的具体实现:
/**
* 初始化弹窗中的界面,添加传入的customView界面,并监听关闭按钮点击事件
*
* @param customView
* @return
*/
public AnimDialogUtils initView(final View customView) {
if (isOverScreen) {
androidContentView = (ViewGroup) context.getWindow().getDecorView();
} else {
androidContentView = (ViewGroup) context.getWindow().findViewById(Window.ID_ANDROID_CONTENT);
}
rootView = LayoutInflater.from(context).inflate(R.layout.anim_dialog_layout, null);
rootView.setTag(ANIM_DIALOG_TAG);
animBackView = (RelativeLayout) rootView.findViewById(R.id.anim_back_view);
animContainer = (RelativeLayout) rootView.findViewById(R.id.anim_container);
animContainer.setVisibility(View.INVISIBLE);
flContentContainer = (FrameLayout) rootView.findViewById(R.id.fl_content_container);
ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
flContentContainer.addView(customView, contentParams);
ivClose = (ImageView) rootView.findViewById(R.id.iv_close);
return this;
}
/**
* 开始执行弹窗的展示动画
* @param animType
*/
public void show(int animType, double bounciness, double speed) {
// 判断是否设置背景透明
if (isAnimBackViewTransparent) {
backViewColor = Color.TRANSPARENT;
}
// 判断背景颜色
animBackView.setBackgroundColor(backViewColor);
// 判断弹窗是否可关闭
if (isDialogCloseable) {
ivClose.setVisibility(View.VISIBLE);
ivClose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (onCloseClickListener != null) {
onCloseClickListener.onClick(view);
}
dismiss(AdConstant.ANIM_STOP_TRANSPARENT);
}
});
} else {
ivClose.setVisibility(View.GONE);
}
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
androidContentView.addView(rootView, params);
AnimSpring.getInstance().startAnim(animType, animContainer, bounciness, speed);
isShowing = true;
}
在上面的弹窗展示方法中我们最终调用了:
AnimSpring.getInstance().startAnim(animType, animContainer, bounciness, speed);
在实现弹窗的弹性滑动过程中使用了Facebook的rebound框架,自定义了一个弹性滑动工具类,在其中初始化rebound的SpringSystem对象,并实现了弹窗的各个角度的弹性滑动效果:
/**
* 开始动画-定义动画开始角度
* @param animType
* @param animContainer
*/
public void startCircleAnim(final int animType, final RelativeLayout animContainer) {
double radius = Math.sqrt(DisplayUtil.screenhightPx * DisplayUtil.screenhightPx + DisplayUtil.screenWidthPx * DisplayUtil.screenWidthPx);
double heightY = - Math.sin(Math.toRadians(animType)) * radius;
double widthX = Math.cos(Math.toRadians(animType)) * radius;
Spring tranSpring = springSystem.createSpring();
Spring tranSpring1 = springSystem.createSpring();
tranSpring.addListener(new SimpleSpringListener() {
@Override
public void onSpringActivate(Spring spring) {
animContainer.setVisibility(View.VISIBLE);
}
@Override
public void onSpringUpdate(Spring spring) {
animContainer.setTranslationX((float) spring.getCurrentValue());
}
});
tranSpring1.addListener(new SimpleSpringListener() {
@Override
public void onSpringActivate(Spring spring) {
animContainer.setVisibility(View.VISIBLE);
}
@Override
public void onSpringUpdate(Spring spring) {
animContainer.setTranslationY((float) spring.getCurrentValue());
}
});
tranSpring.setSpringConfig(springConfig);
tranSpring1.setSpringConfig(springConfig);
tranSpring.setCurrentValue(widthX);
tranSpring.setEndValue(0);
tranSpring1.setCurrentValue(heightY);
tranSpring1.setEndValue(0);
}
这样就实现了对弹窗的初始化,展示,动画效果的实现等操作。
在我们的ViewPager初始化图片的时候是通过Fresco类库实现对图片URL的下载操作的:
@Override
public Object instantiateItem(ViewGroup container, int position) {
/**
* 初始化ViewPager Item内容
*/
AdInfo advInfo = advInfoListList.get(position);
View rootView = context.getLayoutInflater().inflate(R.layout.viewpager_item, null);
final ViewGroup errorView = (ViewGroup) rootView.findViewById(R.id.error_view);
final ViewGroup loadingView = (ViewGroup) rootView.findViewById(R.id.loading_view);
final SimpleDraweeView simpleDraweeView = (SimpleDraweeView) rootView.findViewById(R.id.simpleDraweeView);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
container.addView(rootView, params);
simpleDraweeView.setTag(advInfo);
simpleDraweeView.setOnClickListener(imageOnClickListener);
/**
* 监听图片下载动作
*/
ControllerListener controllerListener = new BaseControllerListener() {
@Override
public void onFinalImageSet(
String id,
@Nullable ImageInfo imageInfo,
@Nullable Animatable anim) {
if (imageInfo == null) {
return;
}
errorView.setVisibility(View.GONE);
loadingView.setVisibility(View.GONE);
simpleDraweeView.setVisibility(View.VISIBLE);
}
@Override
public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
Log.i("##########", "onIntermediateImageSet()");
}
@Override
public void onFailure(String id, Throwable throwable) {
errorView.setVisibility(View.VISIBLE);
loadingView.setVisibility(View.GONE);
simpleDraweeView.setVisibility(View.GONE);
}
};
Uri uri = Uri.parse(advInfo.getActivityImg());
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setControllerListener(controllerListener)
.setUri(uri)
.build();
simpleDraweeView.setController(controller);
return rootView;
}
当然更具体的关于广告活动弹窗控件的实现可以下载源码参考。
总结:
以上就是我实现的这个自定义广告活动弹窗控件。其还可以添加一些其他的API,欢迎提出,对于源码有兴趣的同学可以到github上看一下具体实现。项目地址:android-adDialog
另外对github项目,开源项目解析感兴趣的同学可以参考我的:
Github项目解析(一)–>上传Android项目至github
Github项目解析(二)–>将Android项目发布至JCenter代码库
Github项目解析(三)–>Android内存泄露监测之leakcanary
Github项目解析(四)–>动态更改TextView的字体大小
Github项目解析(五)–>Android日志框架
Github项目解析(六)–>自定义实现ButterKnife框架
Github项目解析(七)–>防止按钮重复点击
Github项目解析(八)–>Activity启动过程中获取组件宽高的五种方式
Github项目解析(九)–>实现Activity跳转动画的五种方式
Github项目解析(十)–>几行代码快速集成二维码扫描库