科技网站首页,电子商务具体干嘛的,虚拟主机怎么建设网站,推广论坛有哪些1、效果图 2、前言
1、页面由 MagicIndicator ViewPager2 Fragment 实现#xff1b;
2、下拉框是基于WindowManager实现#xff1b;
3、我使用PopupWindow实现下拉框时#xff0c;发现一个问题#xff0c;PopupWindow 在窗口显示的情况下#xff0c;无法直接从外部修…1、效果图 2、前言
1、页面由 MagicIndicator ViewPager2 Fragment 实现
2、下拉框是基于WindowManager实现
3、我使用PopupWindow实现下拉框时发现一个问题PopupWindow 在窗口显示的情况下无法直接从外部修改布局必须先dismiss
PopupWindow源码
public void setContentView(View contentView) {if (isShowing()) {return;}... ...
}public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {if (isShowing() || !hasContentView()) {return;}... ...
}
4、如果先dismiss再添加属于重新创建布局切换生硬会出现闪烁影响用户体验就像这样 那就没办法了自己实现 观摩PopupWindow源码发现它是基于windowManager实现的照葫芦画瓢自定义一个 3、自定义下拉框
AffiliatedBottomWindow
package com.example.myapplication.common;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;import androidx.annotation.NonNull;import com.example.myapplication.util.BarUtils;
import com.example.myapplication.util.CustomizeUtils;
import com.example.myapplication.util.ScreenUtils;/*** 挂靠在某个view下面的悬浮窗* p* 和PopupWindow一样都是基于WindowManager实现的* p** 和PopupWindow区别* PopupWindow 在窗口显示的情况下无法直接从外部修改布局必须先dismiss* 再重新创建切换会出现闪烁用户体验差* p* PopupWindow源码* public void setContentView(View contentView) {* if (isShowing()) {* return;* }* ... ...* }** p* 为了避免这种闪烁基于WindowManager写了这个 AffiliatedBottomWindow 底部挂靠悬浮框* p*** 注意单例使用完成后一定要清空不然无法创建实例 if (instance null) {}* public void clear() {* instance null;* }*/
public class AffiliatedBottomWindow {private final Context context;private static CustomizeUtils.AntiShake antiShake;private WindowManager windowManager;SuppressLint(StaticFieldLeak)private static AffiliatedBottomWindow instance;// 根viewSuppressLint(StaticFieldLeak)private static ViewGroup rootView;// 根view中的子viewprivate ViewGroup rootChildView;// true使用 根view中的子view 作为内容布局的容器private boolean useRootChildView false;// 显示/隐藏 过渡动画时长public static int animatorDuration 200;private WindowManager.LayoutParams windowLayoutParams;private AffiliatedBottomWindow(Context context) {this.context context;createWindowManager();antiShake new CustomizeUtils.AntiShake(500);}public WindowManager getWindowManager() {return windowManager;}public void setWindowManager(WindowManager windowManager) {this.windowManager windowManager;}public AffiliatedBottomWindow getInstance() {return instance;}public WindowManager.LayoutParams getWindowLayoutParams() {return windowLayoutParams;}public void setWindowLayoutParams(WindowManager.LayoutParams windowLayoutParams) {this.windowLayoutParams windowLayoutParams;}public ViewGroup getRootView() {return rootView;}public void setRootView(ViewGroup rootView) {AffiliatedBottomWindow.rootView rootView;// 初始化隐藏布局AffiliatedBottomWindow.rootView.setVisibility(View.GONE);}public boolean isUseRootChildView() {return useRootChildView;}public void setUseRootChildView(boolean useRootChildView) {this.useRootChildView useRootChildView;}// 显示 和 隐藏 过渡透明度动画public static void alphaAnimation(boolean show) {if (antiShake.isFastClick()) {ValueAnimator animator;if (show) {if (rootView.getVisibility() View.VISIBLE) {return;}rootView.setAlpha(0);rootView.setVisibility(View.VISIBLE);//显示animator ValueAnimator.ofFloat(0f, 1f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {Overridepublic void onAnimationUpdate(NonNull ValueAnimator animation) {float progress (float) animation.getAnimatedValue();rootView.setAlpha(progress);}});animator.setDuration(animatorDuration);animator.start();} else {if (rootView.getVisibility() View.GONE) {return;}//隐藏animator ValueAnimator.ofFloat(1f, 0f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {Overridepublic void onAnimationUpdate(NonNull ValueAnimator animation) {float progress (float) animation.getAnimatedValue();rootView.setAlpha(progress);}});animator.addListener(new AnimatorListenerAdapter() {Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);rootView.setVisibility(View.GONE);}});animator.setDuration(animatorDuration);animator.start();}}}/*** 内容布局插入到 根View中** param context* param rootView 根布局* param affiliatedView 挂靠的View悬浮框会出现在这个View下面*/public static AffiliatedBottomWindow createInstance(Context context,ViewGroup rootView,View affiliatedView) {if (instance null) {instance new AffiliatedBottomWindow(context);// 先设置根viewinstance.setRootView(rootView);// 再设置挂靠view顺序不能乱instance.setAffiliatedView(affiliatedView);}return instance;}/*** 内容布局插入到 根View中的 子View中** param context* param rootView 根布局* param affiliatedView 挂靠的View悬浮框会出现在这个View下面* param rootChildView 根view中的子view* param useChildContainer 是否将 根view中的子view 作为内容布局的容器*/public static AffiliatedBottomWindow createInstance(Context context,ViewGroup rootView,View affiliatedView,ViewGroup rootChildView,boolean useChildContainer) {if (instance null) {instance new AffiliatedBottomWindow(context);instance.setUseRootChildView(useChildContainer);instance.setRootChildView(rootChildView);// 先设置根viewinstance.setRootView(rootView);// 再设置挂靠view顺序不能乱instance.setAffiliatedView(affiliatedView);}return instance;}public void setAffiliatedView(View affiliatedView) {// 设置悬浮框宽/高windowLayoutParams.width affiliatedView.getWidth();// 剩余空间高度windowLayoutParams.height ScreenUtils.getScreenHeight(context) - affiliatedView.getBottom();// 设置悬浮框位置windowLayoutParams.gravity Gravity.TOP;// 减去状态栏高度沉浸式布局// 如果不是沉浸式布局扩展重写此方法windowLayoutParams.y affiliatedView.getBottom() - BarUtils.getStatusBarHeight(context);// 显示当前根视图隐藏了所以不显示windowManager.addView(rootView, windowLayoutParams);}public void setRootChildView(ViewGroup rootChildView) {this.rootChildView rootChildView;}private void createWindowManager() {if (windowManager ! null) {return;}// 创建 windowManager对象windowManager (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);// 创建布局参数windowLayoutParams new WindowManager.LayoutParams();// 设置窗口类型windowLayoutParams.type WindowManager.LayoutParams.TYPE_APPLICATION;// 设置悬浮框不可触摸默认接收事件会导致底层view接收不到事件windowLayoutParams.flags WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 背景颜色设置为透明windowLayoutParams.format PixelFormat.TRANSPARENT;}// 插入布局public void insertViewLayout(View view) {if (useRootChildView) {if (rootChildView.getChildCount() 0) {rootChildView.addView(view);} else {rootChildView.removeAllViews();rootChildView.addView(view);}} else {if (rootView.getChildCount() 0) {rootView.addView(view);} else {rootView.removeAllViews();rootView.addView(view);}}alphaAnimation(true);}// 隐藏窗口public static void dismiss() {alphaAnimation(false);}// 单例使用完成后一定要清空// 不然 无法创建实例 if (instance null) {}public void clear() {instance null;}
}4、源码
demo东西比较多是从自己项目里摘录出来的扩展了MagicIndicator
核心类AffiliatedBottomWindow
https://github.com/LanSeLianMa/CustomizeBottomWindow