17、Android--悬浮控件

Toas

Android中的Toast是一种简易的消息提示框。

基本吐司

基本Toast的使用如下所示:

Toast.makeText(MainActivity.this, "按钮被单击", Toast.LENGTH_SHORT).show();  

Toast默认的位置在于屏幕的下方居中的位置,如果想改变Toast的位置则可以设置Gravity:

Toast toast = Toast.makeText(this, "当前已是最新版本", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();  

自定义吐司

在Android中还可以自定义吐司,来改变吐司的显示位置和存在的时间等。

private void showMyToast(String address) {
    WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
    View view = View.inflate(this, R.layout.total_address, null);
    TextView tv_address = (TextView) view.findViewById(R.id.tv_address);
    tv_address.setText(address);
    WindowManager.LayoutParams params = new WindowManager.LayoutParams();
    params.height = WindowManager.LayoutParams.WRAP_CONTENT;
    params.width = WindowManager.LayoutParams.WRAP_CONTENT;
    params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
    params.format = PixelFormat.TRANSLUCENT;
    params.type = WindowManager.LayoutParams.TYPE_TOAST;
    wm.addView(view, params);
} 

注意:在自定义Tost使用完后必须调用WindowManager的removeView(view)来释放资源。

Toast工具类

Toast一般要求在主线程执行,所以我们可以编写工具类对Toast所在线程进行判断,然后执行相应的操作。

public class ToastUtils {
    private static final String TAG = "ToastUtil";
    public static void showToast(final Activity context, final String messages) {
        if ("main".equals(Thread.currentThread().getName())) {
            Toast.makeText(context, messages, Toast.LENGTH_LONG).show();
        } else {
            context.runOnUiThread(() -> Toast.makeText(context, messages, Toast.LENGTH_LONG).show());
        }
    }
}

PopupWindow

PopupWindow,顾名思义弹窗。PopupWindow是与AlertDialog在形式上类似的弹窗功能,都是为了在activity最上层显示一个弹窗.但是区别是PopupWindow可以自定义出现的位置,并且可以添加入自己需要的View或者导入自己写好的xml布局。

TextView textView = new TextView(MainActivity.this);
textView.setText("测试文本");
PopupWindow popupWindow = new PopupWindow(textView, 200, 300);//参数为1.View 2.宽度 3.高度
    popupWindow.setOutsideTouchable(true); //设置点击外部区域可以取消popupWindow
mTestButton = (Button)findViewById(R.id.test_btn);
    mTestButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //设置popupWindow显示,并且告诉它显示在那个View下面
        popupWindow.showAsDropDown(mTestButton);
    }
});

使用详解

常用函数

PopupWindow常用的构造函数如下所示:

public PopupWindow (Context context)
public PopupWindow(View contentView, int width, int height)
public PopupWindow(View contentView)
public PopupWindow(View contentView, int width, int height, boolean focusable)

PopupWindow常用的方法如下所示:

setContentView(View contentView):设置PopupWindow显示的View
getContentView():获得PopupWindow显示的View
setTouchable(true):设置PopupWindow可触摸
setOutsideTouchable(boolean touchable):设置外部点击退出
showAsDropDown(View anchor):相对某个控件的位置(正左下方),无偏移
showAsDropDown(View anchor, int xoff, int yoff):相对某个控件的位置,有偏移
showAtLocation(View parent, int gravity, int x, int y): 相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移 PS:parent这个参数只要是activity中的view就可以了!
setWidth/setHeight:设置宽高,也可以在构造方法那里指定好宽高, 除了可以写具体的值,还可以用WRAP_CONTENT或MATCH_PARENT, popupWindow的width和height属性直接和第一层View相对应。
setFocusable(true):设置焦点,设置焦点后,如果返回按下返回键,不会直接退出当前activity而是先退出当前的PopupWindow。
setAnimationStyle(int):设置动画效果

其中设置PopupWindow的显示方法有两种方式,分别是:showAsDropDown()和showAtLocation()

位置显示

showAtLocation()显示View的内部在指定位置(),它有两个方法的重载:

public void showAtLocation(View parent, int gravity, int x, int y)
public void showAtLocation(IBinder token, int gravity, int x, int y)

showAsDropDown()显示在一个参照物View的外部周围,有三个方法重载,这里参照物View的周围,使用这个方法是无法在View的内部找到锚点,它的锚点都是围绕者View的外部四周

public void showAsDropDown(View anchor)
public void showAsDropDown(View anchor, int xoff, int yoff)
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity)

带Gravity参数的方法是API 19新引入的。这里的xoff与yoff是对应view的坐标偏移量,对应的初始坐标位置是view的左下角。

适配问题

解决Android7.0调用showAsDropDown方法失效问题:
原理是重写showAsDropDown,将PopupWindow对话框的高度设置成整个屏幕这么大,然后在减去这View指定的底部坐标值。

@Override
public void showAsDropDown(View anchor) {
    if(Build.VERSION.SDK_INT >= 24) {
        Rect rect = new Rect();
        anchor.getGlobalVisibleRect(rect);
        DisplayMetrics outMetrics = new DisplayMetrics();
        Context context = anchor.getContext();
        ((Activity) context).getWindowManager().getDefaultDisplay().getRealMetrics(outMetrics);
        int h = outMetrics.heightPixels - rect.bottom;
        setHeight(h);
    }
    super.showAsDropDown(anchor);
}

对话框

Android中提供了种类丰富的对话框:

名称 描述
AlertDialog 功能最丰富、实际使用最广的对话框
ProgressDialog 进度对话框,这个对话框支持简单进度条的封装
DatePickDialog 日期选择对话框,该对话框只是对DatePicker的包装
TimePickDialog 时间选择对话框,该对话框只是对TimePick的包装

除上表列出的对话框外,我们还可以进行自定义对话框。

常用对话框

1、确定取消对话框

AlertDialog.Builder builder = new Builder(this);
builder.setTitle("友情提醒");
builder.setMessage("若练此功,必先自宫,是否继续?");
builder.setPositiveButton("好的,想好了", new OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		Toast.makeText(MainActivity.this, "啊。。。", 0).show();
		Toast.makeText(MainActivity.this, "即使自宫,也不一定成功", 0).show();
	}
});
builder.setNegativeButton("想想再说", new OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		Toast.makeText(MainActivity.this, "若不自宫,一定不成功", 0).show();
	}
});
builder.create().show();

2、单选对话框

AlertDialog.Builder builder = new Builder(this);
builder.setTitle("请选择性别:");
final String[] items = { "男", "女", "未知" };
builder.setSingleChoiceItems(items, 2, new OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		dialog.dismiss();
		Toast.makeText(MainActivity.this, "您的性别:" + items[which], 0).show();
	}
});
builder.show();

3、多选对话框

AlertDialog.Builder builder = new Builder(this);
builder.setTitle("请选择您喜欢的水果:");
final String[] items = { "苹果", "香蕉", "葡萄", "橘子" };
final boolean[] result = new boolean[] { true, true, false, false };
builder.setMultiChoiceItems(items, result,
		new OnMultiChoiceClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which, boolean isChecked) {
				result[which] = isChecked;
				Toast.makeText(MainActivity.this,items[which] + isChecked, 0).show();
			}
		});
builder.setPositiveButton("提交", new OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < result.length; i++) {
			if (result[i]) 
				sb.append(items[i] + ",");
		}
		Toast.makeText(MainActivity.this, "您喜欢:" + sb.toString(), 0).show();
	}
});
builder.show();

4、进度对话框

final ProgressDialog pd = new ProgressDialog(this);
pd.setTitle("提醒");
pd.setMessage("正在处理中。。。请稍后");
pd.show();
new Thread() {
	public void run() {
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		pd.dismiss();
	};
}.start(); 

5、进度条对话框

final ProgressDialog pd = new ProgressDialog(this);
// 设置进度的样式
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setTitle("提醒");
pd.setMessage("正在处理中。。。请稍后");
pd.show();
new Thread() {
	public void run() {
		pd.setMax(100);
		try {
			for (int i = 0; i <= 100; i++) {
				pd.setProgress(i);
				Thread.sleep(500);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		pd.dismiss();
	};
}.start();

样式对话框

基本对话框

在Android中可以对对话框进行简单的定制,代码如下:

Builder builder = new Builder(this);
View view = View.inflate(this, R.layout.item_dialog, null);
final AlertDialog dialog = builder.create();
dialog.setView(view, 0, 0, 0, 0);
dialog.show();
Button btn_ok = (Button) view.findViewById(R.id.btn_item_ok);
Button btn_cancel = (Button) view.findViewById(R.id.btn_item_cancel);
btn_ok.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
		Toast.makeText(MainActivity.this, "确认对话框被按下", 0).show();
		dialog.dismiss();
	}
});
btn_cancel.setOnClickListener(new OnClickListener() {   
	@Override
	public void onClick(View v) {
		Toast.makeText(MainActivity.this, "取消对话框被按下", 0).show();
		dialog.dismiss();
	}
});

底部弹出对话框

除了自定义布局文件外,还可以对弹出位置进行修改

View view = getLayoutInflater().inflate(R.layout.photo_choose_dialog, null);
dialog = new Dialog(this, R.style.transparentFrameWindowStyle);
dialog.setContentView(view, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
Window window = dialog.getWindow();
window.setWindowAnimations(R.style.main_menu_animstyle);
WindowManager.LayoutParams wl = window.getAttributes();
wl.x = 0;
wl.y = getWindowManager().getDefaultDisplay().getHeight();
wl.width = ViewGroup.LayoutParams.MATCH_PARENT;
wl.height = ViewGroup.LayoutParams.WRAP_CONTENT;

dialog.onWindowAttributesChanged(wl);
dialog.setCanceledOnTouchOutside(false);
dialog.show();
// 初始化
Button btnCamera = (Button) view.findViewById(R.id.btn_phonedialog_camera);
Button btnPhoto = (Button) view.findViewById(R.id.btn_phonedialog_photo);
Button btnCancel = (Button) view.findViewById(R.id.btn_phonedialog_cancel);

btnCamera.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {           
	}
});

btnPhoto.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
	}
});

btnCancel.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
	}
});

其中使用到的样式

<style name="transparentFrameWindowStyle" parent="android:style/Theme.Dialog">
    <item name="android:windowBackground">@drawable/photo_choose_bg</item>
</style>
<style name="main_menu_animstyle">
    <item name="android:windowEnterAnimation">@anim/photo_dialog_in_anim</item>
    <item name="android:windowExitAnimation">@anim/photo_dialog_out_anim</item>
</style>  

使用到的布局文件如下:

<?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="match_parent"
    android:background="#00000000"
    android:gravity="bottom"
    android:orientation="vertical"
    android:padding="5dip" >
    <Button
        android:id="@+id/btn_phonedialog_camera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/photo_gallery_selector"
        android:paddingBottom="10dip"
        android:paddingTop="10dip"
        android:text="拍照"
        android:textSize="16sp" />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="0.5dip"
        android:background="#DAD9DB" />
    <Button
        android:id="@+id/btn_phonedialog_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/photo_camera_selector"
        android:paddingBottom="10dip"
        android:paddingTop="10dip"
        android:text="从手机相册选择"
        android:textSize="16sp" />
    <Button
        android:id="@+id/btn_phonedialog_cancel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dip"
        android:background="@drawable/photo_cancel_selector"
        android:paddingBottom="10dip"
        android:paddingTop="10dip"
        android:text="取消"
        android:textSize="16sp" />
</LinearLayout>  

全局对话框

写好Alert功能块后,在altet.show()语句前加入:

alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

使用到的权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"></uses-permission>

如果只在Service中写入常在Activity中使用的创建Alert的代码,运行时是会发生错误的,因为Alter的显示需要依附于一个确定的 Activity类。而以上做法就是声明我们要弹出的这个提示框是一个系统的提示框,即全局性质的提示框,所以只要手机处于开机状态,无论它现在处于何种 界面之下,只要调用alter.show(),就会弹出提示框来。

AlertDialog.Builder builder = new AlertDialog.Builder(this); 
builder.setMessage("Are you sure you want to exit?") 
       .setCancelable(false) 
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() { 
           public void onClick(DialogInterface dialog, int id) { 
                MyActivity.this.finish(); 
           } 
       }) 
       .setNegativeButton("No", new DialogInterface.OnClickListener() { 
           public void onClick(DialogInterface dialog, int id) { 
                dialog.cancel(); 
           } 
       }); 
       
AlertDialog alert = builder.create(); 
  alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
  alert.show();

自定义对话框

1、 在theme.xml或style.xml中定义对话框的样式:

<style name="CustomDialog">
    <!-- alertDialog窗口背景色 -->
    <item name="android:windowBackground">@android:color/transparent</item>
    <!--dialog是否有边框-->
    <item name="android:windowFrame">@null</item>
    <!--dialog是否有标题-->
    <item name="android:windowNoTitle">true</item>
    <!--是否浮现在activity之上-->
    <item name="android:windowIsFloating">true</item>
    <!--是否半透明-->
    <item name="android:windowIsTranslucent">true</item>
    <!--是否有遮盖(eg.Activity的闪屏页)-->
    <item name="android:windowContentOverlay">@null</item>
    <!--进出动画-->
    <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
    <!--背景是否模糊显示-->
    <item name="android:backgroundDimEnabled">true</item>
</style>

2、创建自定义的对话框继承自Dialog,并在构造函数中配置相关参数:

public class GeneralDialog extends Dialog {
    private Context mContext;
    @BindView(R.id.title)
    TextView mTvTitle;
    @BindView(R.id.confirm)
    Button mBtnConfirm;
    @BindView(R.id.cancel)
    Button mBtnCancel;
    private OnGeneralClickListener mListener;
    public GeneralDialog(Context context) {
        super(context, R.style.CustomDialog);
        this.mContext = context;
        View view = View.inflate(mContext, R.layout.dialog_genera_layout, null);
        ButterKnife.bind(this, view);
        setContentView(view);
        // Init Dialog
        setCanceledOnTouchOutside(true);
        setCancelable(true);
        WindowManager.LayoutParams params = this.getWindow().getAttributes();
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.gravity = Gravity.CENTER;
        this.getWindow().setAttributes(params);
        // Init Event
        mBtnConfirm.setOnClickListener(v -> {
            if (mListener != null)
                mListener.onConfirmClick(v);
        });
        mBtnCancel.setOnClickListener(v -> {
            if (mListener != null)
                mListener.onCancelClick(v);
        });
    }
    public void setCustomTitle(int titleStr){
        mTvTitle.setText(titleStr);
    }
    public void setOnGeneralClickListener(OnGeneralClickListener listener){
        this.mListener = listener;
    }
    public interface OnGeneralClickListener{
        void onConfirmClick(View view);
        void onCancelClick(View view);
    }
}

3、如果想让对话框的宽度能铺满全屏,可以做如下设置

setCanceledOnTouchOutside(true);
setCancelable(true);
WindowManager.LayoutParams params = this.getWindow().getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
params.y = DensityUtils.dip2px(context, 145);
this.getWindow().setAttributes(params);
this.getWindow().setBackgroundDrawableResource(android.R.color.transparent);

4、使用自定义对话框代码如下:

GeneralDialog generalDialog = new GeneralDialog(SplashActivity.this);
generalDialog.setCustomTitle(R.string.text_splash_network_desc);
generalDialog.setOnGeneralClickListener(new GeneralDialog.OnGeneralClickListener() {
    @Override
    public void onConfirmClick(View view) {
        startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
        generalDialog.dismiss();
    }
    @Override
    public void onCancelClick(View view) {
        requestPermission();
        generalDialog.dismiss();
    }
});
generalDialog.show();
原文地址:https://www.cnblogs.com/pengjingya/p/5509055.html