Android底部弹窗的实现示例代码
本文主要是介绍android中实现底部弹窗的的正确姿势,如果你在实现底部弹窗时遇到了一些问题,那么请仔细阅读本文,相信文章会对你有所帮助。
收获早知道
阅读完本文后,你可以有以下收获
- 利用popupwindow实现底部弹窗
- popupwindow实现底部弹窗时的缺点
- 解决利用popupwindow实现底部弹窗,无法覆盖状态栏的问题
- 利用dialog实现底部弹窗
- 利用dialogfragment实现底部弹窗
实现底部弹窗的方式
由于本人水平有限,只知道一下几种实现底部弹窗的方式
- 利用popupwindow实现底部弹窗。
- 利用dialog实现底部弹窗。
- 利用dialogfragment实现底部弹窗。
下面,就利用以上三种方式分别实现android中的底部弹窗。
利用popwindow实现底部弹窗
因为本文主要是介绍实现底部弹窗的方式,所以,不会对popupwindow进行具体的讲解,大家可以到这里了解popupwindow。
直接进入主题,按照套路,一步步实现利用popupwindow实现底部弹窗。首先,写一个布局文件作为popupwindow中的内容,布局文件如下
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:background="#553b3a3a" android:layout_height="match_parent"> <linearlayout android:layout_width="match_parent" android:layout_alignparentbottom="true" android:orientation="vertical" android:id="@+id/content" android:background="@android:color/white" android:layout_height="wrap_content"> <textview android:layout_width="match_parent" android:textcolor="#333" android:text="相机" android:padding="8dp" android:id="@+id/open_from_camera" android:gravity="center" android:textsize="15sp" android:layout_height="40dp" /> <textview android:layout_margintop="1dp" android:id="@+id/open_album" android:layout_width="match_parent" android:textcolor="#333" android:text="打开图库" android:padding="8dp" android:gravity="center" android:textsize="15sp" android:layout_height="40dp" /> <textview android:layout_margintop="1dp" android:id="@+id/cancel" android:layout_width="match_parent" android:textcolor="#333" android:text="取消" android:padding="8dp" android:gravity="center" android:textsize="15sp" android:layout_height="40dp" /> </linearlayout> </relativelayout>
注:这里使用的是填充父窗口的方式,如果不这样做的话,就不能看出遮住后面的效果,看下图更容易理解,左图为填充父布局的方式,右图为
自适应的方式
注:因为采用填充父布局的方式,这里弹出的窗口都是popupwindow,所以点击左图中的阴影弹窗不会消失,因为阴影也是popupwindow呀!解决方法就是,把左图中的阴影部分用一个textview控件填充,然后为这个textview设置点击事件,点击textview时让popupwindow消失就行了。
下面看下利用popupwindow实现底部弹窗的代码,重要的方法我会具体讲解
private void initpopupwindow() { //要在布局中显示的布局 contentview = layoutinflater.from(this).inflate(r.layout.popup_layout, null, false); //实例化popupwindow并设置宽高 popupwindow = new popupwindow(contentview, linearlayout.layoutparams.match_parent, linearlayout.layoutparams.match_parent); popupwindow.setbackgrounddrawable(new bitmapdrawable()); //点击外部消失,这里因为popupwindow填充了整个窗口,所以这句代码就没用了 popupwindow.setoutsidetouchable(true); //设置可以点击 popupwindow.settouchable(true); //进入退出的动画 popupwindow.setanimationstyle(r.style.mypopwindowanim); } private void showpopwindow() { view rootview = layoutinflater.from(mainactivity.this).inflate(r.layout.activity_main, null); popupwindow.showatlocation(rootview, gravity.bottom, 0, 0); }
重点看一下这句代码
popupwindow.showatlocation(rootview, gravity.bottom, 0, 0);
这句代码是设置弹出窗口从哪里弹出, void showatlocation (view parent,int gravity,int x,int y) 方法有四个参数,第一个参数是父布局,第二个为从父布局的哪里弹出,x和y是相对于父布局弹出位置的偏移量。由于,我们要将mpopwindow放在整个屏幕的最低部,所以我们将r.layout.activity_main做为它的父容器,将其显示在bottom的位置。
再仔细看下上图,利用popupwindow实现从底部的弹窗并不能覆盖到状态栏,下面就来解决这个问题。
解决popupwindow弹出的窗口不能覆盖状态栏问题
想要覆盖到状态栏还需要添以下代码
//弹出的窗口是否覆盖状态栏 public void fitpopupwindowoverstatusbar(boolean needfullscreen) { if (build.version.sdk_int >= build.version_codes.lollipop) { try { //利用反射重新设置mlayoutinscreen的值,当mlayoutinscreen为true时则popupwindow覆盖全屏。 field mlayoutinscreen = popupwindow.class.getdeclaredfield("mlayoutinscreen"); mlayoutinscreen.setaccessible(true); mlayoutinscreen.set(popupwindow, needfullscreen); } catch (nosuchfieldexception e) { e.printstacktrace(); } catch (illegalaccessexception e) { e.printstacktrace(); } } }
再改变一下显示popupwindow的代码,如下
//设置是否遮住状态栏 fitpopupwindowoverstatusbar(true); view rootview = layoutinflater.from(mainactivity.this).inflate(r.layout.activity_main, null); popupwindow.showatlocation(rootview, gravity.bottom, 0, 0);
再看下效果
好了,到此完美解决问题,可以发现利用popupwindow实现底部弹窗其实还是挺麻烦的。
利用dialog实现底部弹窗
先看下代码,然后在讲解
public class dialogfrombottom extends dialog{ private final static int manimationduration = 200; // 持有 contentview,为了做动画 private view mcontentview; private boolean misanimating = false; private onbottomsheetshowlistener monbottomsheetshowlistener; public dialogfrombottom(@nonnull context context) { super(context, r.style.apptheme_bottomsheet); } @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); getwindow().getdecorview().setpadding(0, 0, 0, 0); // 在底部,宽度撑满 windowmanager.layoutparams params = getwindow().getattributes(); params.height = viewgroup.layoutparams.wrap_content; params.gravity = gravity.bottom | gravity.center;//dialog从哪里弹出 //弹出窗口的宽高 int screenwidth = qmuidisplayhelper.getscreenwidth(getcontext()); int screenheight = qmuidisplayhelper.getscreenheight(getcontext()); params.width = screenwidth < screenheight ? screenwidth : screenheight; getwindow().setattributes(params); setcanceledontouchoutside(true); } //设置弹出dialog中的layout @override public void setcontentview(int layoutresid) { mcontentview = layoutinflater.from(getcontext()).inflate(layoutresid, null); super.setcontentview(mcontentview); } @override public void setcontentview(@nonnull view view) { mcontentview = view; super.setcontentview(view); } @override public void setcontentview(@nonnull view view, viewgroup.layoutparams params) { mcontentview = view; super.setcontentview(view, params); } /** * bottomsheet升起动画 */ private void animateup() { if (mcontentview == null) { return; } translateanimation translate = new translateanimation( animation.relative_to_self, 0f, animation.relative_to_self, 0f, animation.relative_to_self, 1f, animation.relative_to_self, 0f ); alphaanimation alpha = new alphaanimation(0, 1); animationset set = new animationset(true); set.addanimation(translate); set.addanimation(alpha); set.setinterpolator(new decelerateinterpolator()); set.setduration(manimationduration); set.setfillafter(true); mcontentview.startanimation(set); } /** * bottomsheet降下动画 */ private void animatedown() { if (mcontentview == null) { return; } translateanimation translate = new translateanimation( animation.relative_to_self, 0f, animation.relative_to_self, 0f, animation.relative_to_self, 0f, animation.relative_to_self, 1f ); alphaanimation alpha = new alphaanimation(1, 0); animationset set = new animationset(true); set.addanimation(translate); set.addanimation(alpha); set.setinterpolator(new decelerateinterpolator()); set.setduration(manimationduration); set.setfillafter(true); set.setanimationlistener(new animation.animationlistener() { @override public void onanimationstart(animation animation) { misanimating = true; } @override public void onanimationend(animation animation) { misanimating = false; /** * bugfix: attempting to destroy the window while drawing! */ mcontentview.post(new runnable() { @override public void run() { // java.lang.illegalargumentexception: view=com.android.internal.policy.phonewindow$decorview{22dbf5b v.e...... r......d 0,0-1080,1083} not attached to window manager // 在dismiss的时候可能已经detach了,简单try-catch一下 try { dialogfrombottom.super.dismiss(); } catch (exception e) { //这里处理异常 } } }); } @override public void onanimationrepeat(animation animation) { } }); mcontentview.startanimation(set); } @override public void show() { super.show(); animateup(); if (monbottomsheetshowlistener != null) { monbottomsheetshowlistener.onshow(); } } @override public void dismiss() { if (misanimating) { return; } animatedown(); } public interface onbottomsheetshowlistener { void onshow(); } }
额,代码有点长,其实很容易理解,这里主要说下oncreate方法中的内容,可以仔细看下注释。
@override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); getwindow().getdecorview().setpadding(0, 0, 0, 0);//把父布局的padding都设为0,目的是可以dialog撑满全屏。 // 在底部,宽度撑满 windowmanager.layoutparams params = getwindow().getattributes(); params.height = viewgroup.layoutparams.wrap_content; params.gravity = gravity.bottom | gravity.center;//dialog从底部弹出 //弹出窗口的宽高,displayhelper.getscreenwidth(getcontext());和displayhelper.getscreenheight(getcontext());是拿到屏幕的宽高。 int screenwidth = displayhelper.getscreenwidth(getcontext()); int screenheight = displayhelper.getscreenheight(getcontext()); params.width = screenwidth < screenheight ? screenwidth : screenheight;//适配手机横屏 getwindow().setattributes(params);//重新设置dialog的属性 setcanceledontouchoutside(true);//设置触摸dialog以外,dialog是否消失 }
利用dialog实现底部弹窗就是继承系统dialog然后重写了oncreate方法,设置dialog从底部弹出。因为是继承dialog,所以有dialog的特性,既触摸底部弹窗以外的部分,弹窗会自动消失,这里就不在演示,可以在文末获取源码,自己实验一下就知道了。
利用dialogfragment实现底部弹窗
在实现弹窗之前,先了解一下dialogfragment
dialogfragment在android 3.0时被引入。是一种特殊的fragment,用于在activity的内容之上展示一个模态的对话框。
使用dialogfragment至少需要实现oncreateview或者oncreatedialog方法。oncreateview即使用定义的xml布局文件展示dialog。oncreatedialog即利用alertdialog或者dialog创建出dialog。下面通过实现oncreateview方法来实现底部弹窗。
@nullable @override public view oncreateview(layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { view view = inflater.inflate(r.layout.dialog_layout, container, false); return view; } @override public void onstart() { super.onstart(); initparams();//初始化弹窗的参数 } private void initparams() { window window = getdialog().getwindow(); if (window != null) { windowmanager.layoutparams lp = window.getattributes(); //调节灰色背景透明度[0-1],默认0.5f lp.dimamount = dimamount; //是否在底部显示 if (showbottom) { lp.gravity = gravity.bottom; if (animstyle == 0) { animstyle = r.style.defaultanimation; } } //设置dialog宽度 if (width == 0) { lp.width = displayhelper.getscreenwidth(getactivity()) - 2 * displayhelper.dp2px(getactivity(), margin); } else { lp.width = displayhelper.dp2px(getactivity(), width); } //设置dialog高度 if (height == 0) { lp.height = windowmanager.layoutparams.wrap_content; } else { lp.height = displayhelper.dp2px(getactivity(), height); } //设置dialog进入、退出的动画 window.setwindowanimations(animstyle); window.setattributes(lp); } setcancelable(outcancel);//设置点击外部是否消失 }
因为dialogfragment也是fragment,所以,dialogfragment有和fragment一样的生命周期,在onstart方法中初始化弹窗的数据,在oncreateview中加载布局,同样,和fragment使用方法也是一样的,下面看下在activity中的使用
void showdialog() { fragmenttransaction ft = getfragmentmanager().begintransaction(); // create and show the dialog. dialogfragmentfrombottom newfragment = new dialogfragmentfrombottom(); newfragment.show(ft, "dialog"); }
结束语
好了,到这里三种实现底部弹窗的方式已经讲完了,大家可以下载源码研究一下, 源码在这里 ,在做项目时选择最适合的就好,在这里还是推荐使用dialogfragment,这种方式可定制性很高,实现弹窗的方式也比较优雅。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。