仿支付宝里面饿了么H5的红包特效
年底了,公司需求给APP搞个红包.要特效像支付宝里那个订餐进去的红包差不多. 我进去一看,那个是饿了么的H5实现的动画.有点像Android里的阻尼动画效果.看了一下Google的开源 ,和facebook的开源,发现低版本的还不行……这就尴尬了.为了兼容下面,只能放弃阻尼动画了.
既然是动画,那就可以用基础的来实现嘛.基础的平移 /放大/缩小.翻转.这些实现不了贝塞尔曲线那么狠的动画,但是仔细看看,很荣幸,还是可以模拟阻尼动画的,哈哈.阻尼动画——>用基础动画实现[由小放大(同时由透明到显示)—->接着由大到小]就可以实现了.那么红包在Activity顶层显示 ,那么我们就可以用一个PoPupWindow来放这个红包 .下来就可以开始我们的实现之路了.
PoPupWindow封装
采用链试调用,更方便,快捷.
package com.townwang.popupwindowdemo;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupWindow;
/**
* @author Town
* @created at 2018/1/11 14:25
* @Last Modified by: Town
* @Last Modified time: 2018/1/11 14:25
* @Remarks 自定义Popupwindow
*/
public class PWindow {
private Context mContext;
private int mWidth;
private int mHeight;
private boolean mIsFocusable = true;
private boolean mIsOutside = true;
private int mResLayoutId = -1;
private View mContentView;
private PopupWindow mPopupWindow;
private int mAnimationStyle = -1;
private boolean mClippEnable = true;//default is true
private boolean mIgnoreCheekPress = false;
private int mInputMode = -1;
private PopupWindow.OnDismissListener mOnDismissListener;
private int mSoftInputMode = -1;
private boolean mTouchable = true;//default is ture
private View.OnTouchListener mOnTouchListener;
public PWindow(Context context){
mContext = context;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
/**
*
* @param anchor
* @param xOff
* @param yOff
* @return
*/
public PWindow showAsDropDown(View anchor, int xOff, int yOff){
if(mPopupWindow!=null){
mPopupWindow.showAsDropDown(anchor,xOff,yOff);
}
return this;
}
public PWindow showAsDropDown(View anchor){
if(mPopupWindow!=null){
mPopupWindow.showAsDropDown(anchor);
}
return this;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public PWindow showAsDropDown(View anchor, int xOff, int yOff, int gravity){
if(mPopupWindow!=null){
mPopupWindow.showAsDropDown(anchor,xOff,yOff,gravity);
}
return this;
}
/**
* 相对于父控件的位置(通过设置Gravity.CENTER,下方Gravity.BOTTOM等 ),可以设置具体位置坐标
* @param parent
* @param gravity
* @param x the popup's x location offset
* @param y the popup's y location offset
* @return
*/
public PWindow showAtLocation(View parent, int gravity, int x, int y){
if(mPopupWindow!=null){
mPopupWindow.showAtLocation(parent,gravity,x,y);
}
return this;
}
/**
* 添加一些属性设置
* @param popupWindow
*/
private void apply(PopupWindow popupWindow){
popupWindow.setClippingEnabled(mClippEnable);
if(mIgnoreCheekPress){
popupWindow.setIgnoreCheekPress();
}
if(mInputMode!=-1){
popupWindow.setInputMethodMode(mInputMode);
}
if(mSoftInputMode!=-1){
popupWindow.setSoftInputMode(mSoftInputMode);
}
if(mOnDismissListener!=null){
popupWindow.setOnDismissListener(mOnDismissListener);
}
if(mOnTouchListener!=null){
popupWindow.setTouchInterceptor(mOnTouchListener);
}
popupWindow.setTouchable(mTouchable);
}
private PopupWindow build(){
if(mContentView == null){
mContentView = LayoutInflater.from(mContext).inflate(mResLayoutId,null);
}
if(mWidth != 0 && mHeight!=0 ){
mPopupWindow = new PopupWindow(mContentView,mWidth,mHeight);
}else{
mPopupWindow = new PopupWindow(mContentView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,true);
}
if(mAnimationStyle!=-1){
mPopupWindow.setAnimationStyle(mAnimationStyle);
}
apply(mPopupWindow);//设置一些属性
mPopupWindow.setFocusable(mIsFocusable);
mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mPopupWindow.setOutsideTouchable(mIsOutside);
if(mWidth == 0 || mHeight == 0){
mPopupWindow.getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
//如果外面没有设置宽高的情况下,计算宽高并赋值
mWidth = mPopupWindow.getContentView().getMeasuredWidth();
mHeight = mPopupWindow.getContentView().getMeasuredHeight();
}
mPopupWindow.update();
return mPopupWindow;
}
/**
* 关闭popWindow
*/
public void dissmiss(){
if(mPopupWindow!=null){
mPopupWindow.dismiss();
}
}
public static class PopupWindowBuilder{
private PWindow mCustomPopWindow;
public PopupWindowBuilder(Context context){
mCustomPopWindow = new PWindow(context);
}
public PopupWindowBuilder size(int width,int height){
mCustomPopWindow.mWidth = width;
mCustomPopWindow.mHeight = height;
return this;
}
public PopupWindowBuilder setFocusable(boolean focusable){
mCustomPopWindow.mIsFocusable = focusable;
return this;
}
public PopupWindowBuilder setView(int resLayoutId){
mCustomPopWindow.mResLayoutId = resLayoutId;
mCustomPopWindow.mContentView = null;
return this;
}
public PopupWindowBuilder setView(View view){
mCustomPopWindow.mContentView = view;
mCustomPopWindow.mResLayoutId = -1;
return this;
}
public PopupWindowBuilder setOutsideTouchable(boolean outsideTouchable){
mCustomPopWindow.mIsOutside = outsideTouchable;
return this;
}
/**
* 设置弹窗动画
* @param animationStyle
* @return
*/
public PopupWindowBuilder setAnimationStyle(int animationStyle){
mCustomPopWindow.mAnimationStyle = animationStyle;
return this;
}
public PopupWindowBuilder setClippingEnable(boolean enable){
mCustomPopWindow.mClippEnable =enable;
return this;
}
public PopupWindowBuilder setIgnoreCheekPress(boolean ignoreCheekPress){
mCustomPopWindow.mIgnoreCheekPress = ignoreCheekPress;
return this;
}
public PopupWindowBuilder setInputMethodMode(int mode){
mCustomPopWindow.mInputMode = mode;
return this;
}
public PopupWindowBuilder setOnDissmissListener(PopupWindow.OnDismissListener onDissmissListener){
mCustomPopWindow.mOnDismissListener = onDissmissListener;
return this;
}
public PopupWindowBuilder setSoftInputMode(int softInputMode){
mCustomPopWindow.mSoftInputMode = softInputMode;
return this;
}
public PopupWindowBuilder setTouchable(boolean touchable){
mCustomPopWindow.mTouchable = touchable;
return this;
}
public PopupWindowBuilder setTouchIntercepter(View.OnTouchListener touchIntercepter){
mCustomPopWindow.mOnTouchListener = touchIntercepter;
return this;
}
public PWindow create(){
//构建PopWindow
mCustomPopWindow.build();
return mCustomPopWindow;
}
}
}
这就是封装后的类了.值得注意的是:如果
new PopupWindow(mContentView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,true);
是这个属性.那么你设置
mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mPopupWindow.setOutsideTouchable(mIsOutside);
都不起作用! 很尴尬.我这里红包不让点击外面取消,所以才设置ViewGroup.LayoutParams.MATCH_PARENT.
红包承载写完了.接下来是动画
模仿阻尼动画效果
- style
<style name="CustomPopWindowStyle">
<item name="android:windowEnterAnimation">@anim/popwindow_anim_in</item>
<item name="android:windowExitAnimation">@anim/popwindow_anim_out</item>
</style>
没错,你没看错.我们采用视图进入和退出动画来改动完成
接下来是进入动画
- 进入动画@anim/popwindow_anim_in
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="100"
/>
<scale
android:interpolator= "@android:anim/decelerate_interpolator"
android:duration="100"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="1"
android:repeatMode="reverse"
android:startOffset="0"
android:toXScale="1.5"
android:toYScale="1.5" />
</set>
说明:
interpolator指定动画插入器,常见的有加速减速插入器accelerate_decelerate_interpolator,加速插入器elerate_interpolator,减速插入器decelerate_interpolator。
fromXScale,fromYScale,动画开始前X,Y的缩放,0.0为不显示,1.0为正常大小
toXScale,toYScale,动画最终缩放的倍数,1.0为正常大小,大于1.0放大
pivotX,pivotY动画起始位置,相对于屏幕的百分比,两个都为50%表示动画从屏幕中间开始
startOffset,动画多次执行的间隔时间,如果只执行一次,执行前会暂停这段时间,单位毫秒 duration,一次动画效果消耗的时间,单位毫秒,值越小动画速度越快
repeatCount,动画重复的计数,动画将会执行该值+1次
repeatMode,动画重复的模式,reverse为反向,当第偶次执行时,动画方向会相反。restart为重新执行,方向不变
3.退出动画@anim/popwindow_anim_out
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="100"
/>
<scale
android:interpolator= "@android:anim/decelerate_interpolator"
android:duration="100"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="1"
android:repeatMode="reverse"
android:startOffset="0"
android:toXScale="2.0"
android:toYScale="2.0" />
</set>
接下来看我们链式调用大法
@Override
public void onClick(View view) {
View contentView = LayoutInflater.from(this).inflate(R.layout.meny_window, null);
View view1 = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
new PWindow.PopupWindowBuilder(this)
.setAnimationStyle(R.style.CustomPopWindowStyle)
.setView(contentView).setFocusable(true)
.setOutsideTouchable(true)
.create()
.showAsDropDown(view1);
}
至此..完成 项目地址 : 点击下载