Android仿QQ消息提示实现弹出式对话框

本文在《7种形式的Android Dialog使用实例》在这篇文章的基础进行学习,具体内容如下

1.概述

  android原生控件向来以丑著称(新推出的Material Design当另说),因此几乎所有的应用都会特殊定制自己的UI样式。而其中弹出式提示框的定制尤为常见,本篇我们将从模仿QQ退出提示框来看一下常见的几种自定义提示框的实现方式。
  这里使用的几种弹出框实现方法概括为以下几种:

自定义Dialog
自定义PopupWindow
自定义Layout View
Activity的Dialog样式
FragmentDialog

先看下最终的效果图:

2.实践

  前面提到几种实现方式均可以达到同样的演示效果,但其中又是各有不同。这里先逐一列举各种具体实现,最后加以综述总结和归纳吧。
  在此之前呢,先看一下这里实现的对话框共用布局layout/confirm_dialog.xml 。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_marginLeft="5dp"
 android:layout_marginRight="5dp"
 android:background="@drawable/confirm_dialog_bg"
 android:orientation="vertical">

 <LinearLayout
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:background="@android:color/transparent"
  android:orientation="vertical" >

  <TextView
   android:id="@+id/title_name"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:gravity="center_horizontal"
   android:paddingBottom="10dp"
   android:paddingTop="15dp"
   android:text="Message Title"
    android:textColor="@android:color/black"
   android:textSize="20sp"
   android:visibility="visible" />
 </LinearLayout>

 <LinearLayout
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@android:color/transparent"
  android:orientation="vertical" >

  <TextView
   android:id="@+id/text_view"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:layout_marginLeft="15dp"
   android:layout_marginRight="15dp"
   android:layout_marginTop="10dp"
   android:textColor="@android:color/black"
   android:text="this is message content"
   android:textSize="16dip"/>

  <View
   android:layout_width="match_parent"
   android:layout_height="1px"
   android:layout_marginTop="15dip"
   android:background="#c5c5c5" />

  <LinearLayout
   android:layout_width="fill_parent"
   android:layout_height="50dip"
   android:background="@android:color/transparent"
   android:gravity="center_horizontal"
   android:orientation="horizontal" >

   <!-- 取消按钮 -->
   <Button
    android:id="@+id/btn_cancel"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:text="Cancel"
    android:textStyle="bold"
    android:textColor="#0072c6"
    android:background="@drawable/confirm_dialog_cancel_selector"
    android:textSize="15sp" />

   <!-- 确认按钮 -->

   <View
    android:layout_width="1px"
    android:layout_height="match_parent"
    android:layout_gravity="center_horizontal"
    android:background="#c5c5c5"/>

   <Button
    android:id="@+id/btn_ok"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:text="OK"
    android:textStyle="bold"
    android:textColor="#0072c6"
    android:background="@drawable/confirm_dialog_ok_selector"
    android:textSize="15sp" />
  </LinearLayout>
 </LinearLayout>

</LinearLayout>

仅仅通过布局预览就可以看到效果了:

下边我们分别通过上述几种方式来使用这个布局展示消息提示框。   

2.1 Dialog 

这个是最基本也最常见的非阻塞式对话框。具体形式可分为七种,详细参见网上各种文章,随便引用一篇7种形式的Android Dialog使用举例。
(注:官方在fragmentDialog推出后就不在推荐直接使用Dialog来创建对话框,这是后话)

我们这里自定义的提示框ConfirmDialog继承自Dialog,使用confirm_dialog.xml 初始化布局,绑定相应事件。

public class ConfirmDialog extends Dialog {
 private Context context;
 private TextView titleTv,contentTv;
 private View okBtn,cancelBtn;
 private OnDialogClickListener dialogClickListener;

 public ConfirmDialog(Context context) {
  super(context);
  this.context = context;
  initalize();
 }

 //初始化View
 private void initalize() {
  LayoutInflater inflater = LayoutInflater.from(context);
  View view = inflater.inflate(R.layout.confirm_dialog, null);
  setContentView(view);
  initWindow();

  titleTv = (TextView) findViewById(R.id.title_name);
  contentTv = (TextView) findViewById(R.id.text_view);
  okBtn = findViewById(R.id.btn_ok);
  cancelBtn = findViewById(R.id.btn_cancel);
  okBtn.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    dismiss();
    if(dialogClickListener != null){
     dialogClickListener.onOKClick();
    }
   }
  });
  cancelBtn.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    dismiss();
    if(dialogClickListener != null){
     dialogClickListener.onCancelClick();
    }
   }
  });
 }

 /**
 *添加黑色半透明背景
 */
 private void initWindow() {
  Window dialogWindow = getWindow();
  dialogWindow.setBackgroundDrawable(new ColorDrawable(0));//设置window背景
  dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);//设置输入法显示模式
  WindowManager.LayoutParams lp = dialogWindow.getAttributes();
  DisplayMetrics d = context.getResources().getDisplayMetrics();//获取屏幕尺寸
  lp.width = (int) (d.widthPixels * 0.8); //宽度为屏幕80%
  lp.gravity = Gravity.CENTER;  //中央居中
  dialogWindow.setAttributes(lp);
 }

 public void setOnDialogClickListener(OnDialogClickListener clickListener){
  dialogClickListener = clickListener;
 }

 /**
 *添加按钮点击事件
 */
 public interface OnDialogClickListener{
  void onOKClick();
  void onCancelClick();
 }
}

2.2 PopupWindow 

  PopupWindow是阻塞式对话框,只有在退出操作时候程序才会继续运行。另外PopupWindow可以根据自由确定自身位置。按照位置有无偏移分,可以分为偏移和无偏移两种;按照参照物的不同,可以分为相对于某个控件(Anchor锚)和相对于父控件。具体如下

showAsDropDown(View anchor):相对某个控件的位置(正左下方),无偏移
showAsDropDown(View anchor, int xoff, int yoff):相对某个控件的位置(正下方),有偏移
showAtLocation(View parent, int gravity, int x, int y):相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移

这里只是达到同样的显示效果,仅示范下showAtBottom的使用:

public class ConfirmPopWindow extends PopupWindow{
 private Context context;
 private TextView titleTv,contentTv;
 private View okBtn,cancelBtn;
 private OnDialogClickListener dialogClickListener;

 public ConfirmPopWindow(Context context) {
  super(context);
  this.context = context;
  initalize();
 }

 private void initalize() {
  LayoutInflater inflater = LayoutInflater.from(context);
  View view = inflater.inflate(R.layout.confirm_dialog, null);
  setContentView(view);
  initWindow();

  titleTv = (TextView) view.findViewById(R.id.title_name);
  contentTv = (TextView) view.findViewById(R.id.text_view);
  okBtn = view.findViewById(R.id.btn_ok);
  cancelBtn = view.findViewById(R.id.btn_cancel);
  okBtn.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    dismiss();
    if(dialogClickListener != null){
     dialogClickListener.onOKClick();
    }
   }
  });
  cancelBtn.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    dismiss();
    if(dialogClickListener != null){
     dialogClickListener.onCancelClick();
    }
   }
  });
 }

 private void initWindow() {
  this.setBackgroundDrawable(new ColorDrawable(0));
  DisplayMetrics d = context.getResources().getDisplayMetrics();
  this.setWidth((int) (d.widthPixels * 0.8));
  this.setHeight(LayoutParams.WRAP_CONTENT);
  this.setFocusable(true);
  this.setOutsideTouchable(true);
  this.update();
 }

 public void showAtBottom(View view){
  showAsDropDown(view, Math.abs((view.getWidth() - getWidth())/2), 20);
 }

 public void setOnDialogClickListener(OnDialogClickListener clickListener){
  dialogClickListener = clickListener;
 }

 public interface OnDialogClickListener{
  void onOKClick();
  void onCancelClick();
 }
}

2.3 自定义Layout 

  前边两种是系统封装好的View ,同样的,我们也可以自定义layout布局来实现的弹出式对话框效果。既然是自定义,有必要细致讲述一下,
  ConfirmLayout继承自FrameLayout,通过获取窗口管理器WindowManager 将我们的自定义view添加到窗口最前端并显示出来,达到预期效果。     

1.初始化View

  先初始化半透明黑色背景和对应的confirm_layout,然后给窗体添加按键返回事件

 protected void initialize() {
  initBackground();//初始化黑色背景
  initContentView();//初始化confirm_layout 对应的View

  windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
  setOnKeyListener(new OnKeyListener() { //添加按键返回事件
   @Override
   public boolean onKey(View v, int keyCode, KeyEvent event) {
    if (KeyEvent.KEYCODE_BACK == keyCode && KeyEvent.ACTION_DOWN == event.getAction()) {
     hide();//隐藏当前view
     return true;
    }
    return false;
   }

  setFocusable(true); //可获得焦点
  setFocusableInTouchMode(true); //可触碰获得焦点
 }

2.显示自定义VIew : show()

  调用显示的时候保证在主线程中,如果当前View没有被添加至窗口中,则添加;然后使用动画渐变效果显示背景,最后动画完成时显示当前对话框View.

public void show() {
  ((Activity) getContext()).runOnUiThread(new Runnable() {
   @Override
   public void run() {
    if (getParent() == null) { //没有添加则添加至窗体
     //获取窗体的布局属性,设置左上角对齐,填充父容器
     WindowManager.LayoutParams wlp = new WindowManager.LayoutParams();
     wlp.type = WindowManager.LayoutParams.TYPE_APPLICATION;
     wlp.format = PixelFormat.TRANSPARENT;
     wlp.gravity = Gravity.LEFT | Gravity.TOP;
     wlp.width = LayoutParams.MATCH_PARENT;
     wlp.height = LayoutParams.MATCH_PARENT;
     windowManager.addView(ConfirmLayout.this, wlp);
    }
    showBackGround();//显示背景动画和自定义View
   }
  });
 }

 /**
 *显示背景动画
 */
 protected void showBackGround() {
  if (isShowing)
   return;
  isShowing = true;
  background.clearAnimation();
  background.setVisibility(View.VISIBLE);
  AlphaAnimation an = new AlphaAnimation(0, 1);
  an.setDuration(durationMillis);
  background.startAnimation(an);
 }

3.隐藏自定义VIew : hide()

  隐藏对话框的方法跟show()恰恰相反,首先调用隐藏动画,动画结束从窗体中移除View

 public void hide() {
  ((Activity) getContext()).runOnUiThread(new Runnable() {
   @Override
   public void run() {
    hideBackGround();//隐藏背景
    if (getParent() != null)
     windowManager.removeView(ConfirmLayout.this);//移除view
   }
  });
 }

 /**
 *隐藏背景背景动画
 */
 protected void hideBackGround() {
  if (!isShowing)
   return;
  isShowing = false;
  background.clearAnimation();
  AlphaAnimation an = new AlphaAnimation(1, 0);
  an.setDuration(durationMillis);
  an.setAnimationListener(new AnimationListener() {
   @Override
   public void onAnimationStart(Animation animation) {
   }

   @Override
   public void onAnimationRepeat(Animation animation) {
   }

   @Override
   public void onAnimationEnd(Animation animation) {
    background.setVisibility(View.GONE);
   }
  });
  background.startAnimation(an);
 }

其他部分同上,不再一一贴出,详细可查看示例源码。   

2.4 Activity的Dialog样式 

  通过使用主题Theme来实现Activity作为一个dialog来显示的效果。我们首先在 AndroidManifest.xml 中配置该activity,使得

android:theme=”@android:style/Theme.Dialog”
同样我们可以自定义继承于Theme.Dialog的style样式增加自定义属性,比如:

<resources>
 <style name="DialogStyle" parent="@android:style/Theme.Dialog">
  <item name="android:windowBackground">@android:color/transparent</item>
  <item name="android:windowFrame">@null</item>
  <item name="android:windowNoTitle">true</item>
  <item name="android:windowIsFloating">true</item>
  <item name="android:windowIsTranslucent">true</item>
  <item name="android:windowFullscreen">true</item>
  <item name="android:backgroundDimEnabled">true</item>
 </style>
</resources>

然后使用 > android:theme=”@style/DialogStyle” 达到上述效果。具体实现跟dialog类似:

public class ConfirmActivity extends Activity{
 private TextView titleTv,contentTv;
 private View okBtn,cancelBtn;
 private OnDialogClickListener dialogClickListener;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.confirm_dialog);
  initViews();
  initListeners();
 }

 private void initViews() {
  initWindow();
  titleTv = (TextView) findViewById(R.id.title_name);
  contentTv = (TextView) findViewById(R.id.text_view);
  okBtn = findViewById(R.id.btn_ok);
  cancelBtn = findViewById(R.id.btn_cancel);
  okBtn.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    finish();
    if(dialogClickListener != null){
     dialogClickListener.onOKClick();
    }
   }
  });
 }

 private void initWindow() {
  getWindow().setBackgroundDrawable(new ColorDrawable(0));
  getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN |
    WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
 }

 private void initListeners() {
  cancelBtn.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    finish();
    if(dialogClickListener != null){
     dialogClickListener.onCancelClick();
    }
   }
  });
 }

 public void setOnDialogClickListener(OnDialogClickListener clickListener){
  dialogClickListener = clickListener;
 }

 public interface OnDialogClickListener{
  void onOKClick();
  void onCancelClick();
 }
}

2.5 DialogFragment 

  DialogFragment在android 3.0时被引入并被加以推广。
  我们在使用DialogFragment时,至少需要实现onCreateView或者onCreateDIalog方法。这里在onCreateDIalog中直接返回前面写好的ConfirmDialog来实现这个Fragment。

public class ConfirmFragment extends DialogFragment{

 @Override
 @NonNull
 public Dialog onCreateDialog(Bundle savedInstanceState) {
  return new ConfirmDialog(getActivity());
 }

public void setOnDialogClickListener(OnDialogClickListener clickListener){
  ((ConfirmDialog)getDialog()).setOnDialogClickListener(clickListener);
 }
}

  当然并不推荐偷懒直接返回前面定义好的ConfirmDialog。我们实际上使用fragment的onCreateView也更合理和简介,他可以产生同样的Dialog效果,同时可以作为内嵌fragment引用。从使用总结来看,FragmentDialog 相较于Dialog有两点好处:

在手机配置变化,导致Activity需要重新创建时,例如旋屏,DialogFragment对话框将会由FragmentManager自动重建,然而Dialog实现的对话框则不会重新生成;

DialogFragment还拥有fragment的优点,即可以在一个Activity内部实现回退(因为FragmentManager会管理一个回退栈 ,另外,他可以直接作为一个普通Fragment嵌套在其他布局里边;

3.小结

  从实现效果来看我们确实有很多选择,当然我们用的最多的必然要数Dialog(FragmentDialog)和PopupWindow了。但在一般情况下,选择Dialog和PopupWindow是由我们的具体使用场景来定。比如有些提示消息,比较适合Dialog,而弹出一些具体选项,需要等待选择结果等情况,更倾向于使用PopupWindow了。

FragmentDialog的几种使用场景

PopupWindow的几种使用场景

4.补充

  其实还有种长按弹出菜单,这种除了可以通过上述方法弹出菜单选项外,还可以通过系统提供的 View.setOnCreateContextMenu()方法来实现。比如:

itemView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
     menu.add("删除").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
      @Override
      public boolean onMenuItemClick(MenuItem item) {
       //执行删除操作
       return true;
      }
     });
    }
   });

长按弹出,基本效果为:

有兴趣的不妨试一下。

  后边的话我们先从源码角度来看一下这里讲的几种实现方案的具体原理,最后通过一些简易封装来做一个类似IOS上的ActionSheet控件的效果。
  演示效果大概为:   

  

  详情请继续关注接下来的博文。

最后附上本篇所讲内容的源码:示例源码demo(已重新更新)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android中自定义PopupWindow实现弹出框并带有动画效果

    使用PopupWindow来实现弹出框,并且带有动画效果 首先自定义PopupWindow public class LostPopupWindow extends PopupWindow { public Lost lost; public void onLost(Lost lost){ this.lost = lost; } private View conentView; public View getConentView() { return conentView; } public L

  • Android仿IOS底部弹出对话框

    在Android开发过程中,常常会因为感觉Android自带的Dialog的样式很丑,项目开发过程中会影响整体效果,会使得开发过程很是忧伤....(话唠时间结束!) 本文我将介绍一款开源的Dialog仿IOS底部弹窗效果IOS_Dialog_Library的使用.我将通过几个简单的示例介绍IOS_Dialog_Library.zip的使用方法. 1.IOS_Dialog_Library是开源的Dialog框架,所以首先你得下载IOS_Dialog_Library.zip包,并作为Library引

  • Android输入法弹出时覆盖输入框问题的解决方法

    当一个activity中含有输入框时,我们点击输入框,会弹出输入法界面,整个界面的变化效果与manifest中对应设置的android:windowSoftInputMode属性有关,一般可以设置的值如下, <activity android:windowSoftInputMode=[ "stateUnspecified", "stateUnchanged", "stateHidden", "stateAlwaysHidden&q

  • Android使用Dialog风格弹出框的Activity

    在Android中经常会遇到需要使用Dialog风格弹出框的activity,首先我们可能会首先想到的是在XML布局文件中设置android:layout_height="wrap_content"属性,让activity的高度自适应,显然这还不行,我们还需要为其DialogActivity设置自定义一个样式 <style name="dialogstyle"> <!--设置dialog的背景--> <item name="a

  • Android实现可输入数据的弹出框

    之前一篇文章,介绍了如何定义从屏幕底部弹出PopupWindow即<Android Animation实战之屏幕底部弹出PopupWindow>,写完之后,突然想起之前写过自定义内容显示的弹出框,就随手写了两个实例,分享出来: 第一种实现方式:继承Dialog  1.1 线定义弹出框要显示的内容:create_user_dialog.xml <?xml version="1.0" encoding="utf-8"?> <LinearLa

  • android 弹出提示框的使用(图文实例)

    复制代码 代码如下: //删除全部 else if(id==R.id.btnDelet){ new AlertDialog.Builder(this).setTitle("删除提示框").setMessage("确认删除该数据?").setPositiveButton("确定", new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, i

  • 高仿IOS的Android弹出框

    先看一下效果图,不过这是网上的图片. 效果不错,就借此拿来与大伙分享分享. github源码地址:https://github.com/saiwu-bigkoo/Android-AlertView. 1.怎么用:添加依赖. compile 'com.bigkoo:alertview:1.0.3' 2.实例demo(大家可以根据需要来选择自己需要的框框). package com.example.my.androidalertview; import android.app.Activity; i

  • android 对话框弹出位置和透明度的设置具体实现方法

    例如,屏幕的上方或下方.要实现这种效果.就需要获得对话框的Window对象,获得这个Window对象有多种方法.最容易的就是直接通过AlertDialog类的getWindow方法来获得Window对象. 复制代码 代码如下: AlertDialog dialog = new AlertDialog.Builder(this).setTitle("title")                       .setMessage("message").create(

  • android底部弹出iOS7风格对话选项框(QQ对话框)--第三方开源之IOS_Dialog_Library

    先给大家展示下效果图,喜欢的朋友可以下载源码哦. 完成这个效果的是使用了 IOS_Dialog_Library 下载地址:http://xiazai.jb51.net/201509/yuanma/IOS_Dialog_Library(jb51.net) 下载后导入到Eclipse中,然后作为Library引入到自己的工程中,直接作为第三方控件使用. 测试代码: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/a

  • Android实现弹出登陆框的方案

    下面是我的实现经历: 1.首先,我是直接使用AlertDialog来实现,确定是,形状有点难看,而且获得Dialog里面的控件略显麻烦(因为我要做的登陆框有一定的布局),然后就给我就放弃了,可能因为我太水了,不能很好的使用它 2.然后我就使用PopupWindow来实现,界面是达到了我的要求,控件的获得通过Inflater就可以获得了相对较简单,但是有一个缺点就是,当点击输入的时候,对话框不会根据软键盘的位置而改变位置,网上搜了搜,还不能直接监听软键盘的出现和消失的事件消息,挣扎了一个下午,果断

随机推荐