uma breve introdução
1.1 A caixa pop-up refere-se à janela flutuante na página, como a caixa pop-up do teclado, a caixa pop-up do sistema, a caixa pop-up de confirmação, a caixa suspensa de seleção, a caixa flutuante do aplicativo, etc.
1.2 Há também muitos controles de caixa de marcadores, como o comumente usado Spinner, Dialog, Toast, PopWindow, etc., bem como o recém-adicionado SnackBar, DialogFragment, etc.
Seleção suspensa de dois Spinners para usar
2.1 Spinner root Listview, Gridview, etc. são produtos da mesma época, então o uso é semelhante a eles, principalmente usando BaseAdapter para carregar fontes de dados
2.2 Use o Adapter fornecido pelo sistema , que é fácil de usar, mas o estilo é fixo, o modelo de dados é fixo e só pode ser do tipo String.
renderizações
Arquivo de layout, adicione o controle Spinner
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="40dp"
android:background="#eeeeee">
<Spinner
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
O arquivo java define a fonte de dados e a configuração
private void initSystemAdapter(){
//设置数据源
List<String> list = new ArrayList<String>();
list.add("苹果");
list.add("香蕉");
list.add("橘子");
list.add("香蕉");
//设置系统适配器
ArrayAdapter<String> adapter=new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,list);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
//设置弹出偏移位置
spinner.setDropDownVerticalOffset(40);
//点击监听
spinner.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(SpinnerActivity.this, list.get(position), Toast.LENGTH_SHORT).show();
}
});
}
Nota: O estilo do Adaptador do sistema é fixo, existem os seguintes tipos
simple_spinner_dropdown_item (o espaçamento mais alto da lista é melhor)
simple_spinner_item (o espaçamento apertado da lista não é bonito)
simple_list_item_checked (caixa de seleção selecionada com sulco verde)
simple_list_item_single_choice (botão de opção)
2.3 Adaptador personalizado , alta flexibilidade, pode definir qualquer tipo de fonte de dados e estilo
renderizações
Layout de item personalizado item_fruit_list.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">
<TextView
android:id="@+id/tv_name"
android:layout_width="100dp"
android:layout_height="30dp"
android:background="#00ffff"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="条目"
android:textSize="15sp" />
</LinearLayout>
Origem de dados personalizada FruitBean.java
public class FruitBean {
public String name;
public FruitBean(String name) {
this.name = name;
}
}
Adaptador customizado CustomerAdapter.java
public class CustomerAdapter extends BaseAdapter {
private Context mContext;
private List<FruitBean> mList;
public CustomerAdapter(Context mContext, List<FruitBean> mList) {
this.mContext = mContext;
this.mList = mList;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public Object getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_fruit_list, null);
holder.tvName = (TextView) convertView.findViewById(R.id.tv_name);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvName.setText(mList.get(position).name);
return convertView;
}
class ViewHolder {
public TextView tvName;
}
}
adaptador personalizado de configuração giratória
private void initCustomerAdapter(){
//设置数据源
List<FruitBean> list = new ArrayList<>();
list.add(new FruitBean("苹果"));
list.add(new FruitBean("香蕉"));
list.add(new FruitBean("橘子"));
list.add(new FruitBean("香蕉"));
//设置系统适配器
CustomerAdapter customerAdapter=new CustomerAdapter(this,list);
spinner.setAdapter(customerAdapter);
//设置弹出偏移位置
spinner.setDropDownVerticalOffset(40);
}
O uso de três diálogos
3.1 A caixa de diálogo é a caixa de marcador usada com mais frequência além do Toast. Várias caixas de carregamento, caixas de prompt e caixas de seleção são usadas principalmente na caixa de diálogo
3.2 O diálogo suporta qualquer layout e posição pop-up, por isso é muito flexível.
3.2 Exemplos de renderizações
3.3 Criar uma caixa de diálogo personalizada
dialog_confirm.xm
<?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:background="@drawable/shape_bg_fffff_corner10"
android:orientation="vertical"
android:paddingLeft="25dp"
android:paddingTop="20dp"
android:paddingRight="25dp"
android:paddingBottom="@dimen/dimen_20">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="退出提示"
android:textColor="@color/color_333333"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="15dp"
android:gravity="center"
android:text="是否对本地编辑的内容进行保存?"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_14" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:background="@drawable/shape_bg_1677ff_corners7_border"
android:gravity="center"
android:paddingTop="@dimen/dimen_9"
android:paddingBottom="@dimen/dimen_9"
android:text="取消"
android:textColor="@color/color_1677ff"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_sure"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:background="@drawable/shape_bg_1677ff_corners7"
android:gravity="center"
android:paddingTop="@dimen/dimen_9"
android:paddingBottom="@dimen/dimen_9"
android:text="立即充值"
android:textColor="@color/white"
android:textSize="@dimen/sp_14" />
</LinearLayout>
</LinearLayout>
Defina o estilo da caixa de diálogo custom_dialog.style, translucidez, título, plano de fundo, animação pop-up, etc.
<style name="custom_dialog" parent="@android:style/Theme.Dialog">
<item name="android:windowFrame">@null</item>
<!-- Dialog的windowFrame框为无 -->
<item name="android:windowIsFloating">true</item>
<!-- 是否漂现在activity上 -->
<item name="android:windowIsTranslucent">true</item>
<!-- 是否半透明 -->
<item name="android:windowNoTitle">true</item>
<item name="android:background">@null</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<!-- 去除黑色边框的关键设置项 -->
<item name="android:backgroundDimEnabled">true</item>
<!-- 屏幕背景是否变暗 -->
<item name="android:backgroundDimAmount">0.7</item>
</style>
Precisa herdar a classe pai Dialog do sistema
public class ConfirmDialog extends Dialog {
private Context context;
private TextView tvTitle;
private TextView tvContent;
private TextView tvCancel;
private TextView tvSure;
private String title, content, cancelString, sureString;
public interface OnViewClickLiatener {
void sureClick();
void cancelClick();
}
public OnViewClickLiatener onViewClickLiatener;
public void setOnViewClickLiatener(OnViewClickLiatener onViewClickLiatener) {
this.onViewClickLiatener = onViewClickLiatener;
}
public ConfirmDialog(Context context) {
this(context, R.style.custom_dialog);
}
public ConfirmDialog(Context context, int themeResId) {
super(context, themeResId);
this.context = context;
}
public ConfirmDialog(Context context, int themeResId, String title, String content, String cancelString, String sureString) {
super(context, themeResId);
this.context = context;
this.title = title;
this.content = content;
this.cancelString = cancelString;
this.sureString = sureString;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_confirm);
setCanceledOnTouchOutside(true);
WindowManager.LayoutParams params = getWindow().getAttributes();
params.width = (int) (ScreenUtils.getScreenWidth((Activity) context) * 0.8f);
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
getWindow().setGravity(Gravity.CENTER);
getWindow().setAttributes(params);
getWindow().setBackgroundDrawableResource(R.color.trans);
initView();
setData();
}
public void initView() {
tvTitle = (TextView) findViewById(R.id.tv_title);
tvContent = (TextView) findViewById(R.id.tv_content);
tvCancel = (TextView) findViewById(R.id.tv_cancel);
tvSure = (TextView) findViewById(R.id.tv_sure);
}
public void setData() {
if (!TextUtils.isEmpty(title)) {
tvTitle.setText(title);
}
if (!TextUtils.isEmpty(content)) {
tvContent.setText(content);
}
if (!TextUtils.isEmpty(cancelString)) {
tvCancel.setText(cancelString);
}
if (!TextUtils.isEmpty(sureString)) {
tvSure.setText(sureString);
}
tvCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss();
if (onViewClickLiatener != null) {
onViewClickLiatener.cancelClick();
}
}
});
tvSure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss();
if (onViewClickLiatener != null) {
onViewClickLiatener.sureClick();
}
}
});
}
@Override
public void dismiss() {
super.dismiss();
}
}
usar
/**
* 确认取消提示
*/
ConfirmDialog showCancelDialog;
public void showCancelDialog(String reportOrderNo, String storeCode) {
if (showCancelDialog == null) {
showCancelDialog = new ConfirmDialog(activity, "取消提醒", "取消后将不再对已添加的内容进行保存", "暂不取消", "确认取消");
showCancelDialog.setOnViewClickLiatener(new ConfirmDialog.OnViewClickLiatener() {
@Override
public void sureClick() {
if (TextUtils.isEmpty(reportOrderNo)) {
activity.finish();
} else {
httpReportOrderCancel(reportOrderNo, storeCode);
}
}
@Override
public void cancelClick() {
}
});
}
if (!showCancelDialog.isShowing()) {
showCancelDialog.show();
}
}
3.4 Defina a caixa de diálogo que considera o local, a renderização a seguir está no canto superior direito da tela e há uma distância de deslocamento
A raiz é a mesma do Dialog normal, mas uma view precisa ser passada no construtor, ou seja, para que essa view apareça, a posição do offset do Dialog é definida obtendo-se a posição da view na tela. O seguinte exemplo
public class OtherUserMainMoreDialog extends Dialog {
private DialogViewListener listener;
private Activity mContext;
private View locationView;
private LinearLayout llRemarks;
private LinearLayout llPullBlack;
private LinearLayout llDeleteFriend;
public interface DialogViewListener {
void remarksClick();
void pullBlackClick();
void deleteFriend();
}
public OtherUserMainMoreDialog(Activity context) {
super(context);
mContext = context;
}
public void setDialogViewListener(DialogViewListener listener) {
this.listener = listener;
}
public OtherUserMainMoreDialog(Activity context, int themeResId, View locationView) {
super(context, themeResId);
mContext = context;
this.locationView = locationView;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = LayoutInflater.from(mContext).inflate(R.layout.dialog_other_more, null);
setContentView(view);
//设置Dialog点击外部消失
setCanceledOnTouchOutside(true);
setCancelable(true);
//获取控件 textview 的绝对坐标,( y 轴坐标是控件上部到屏幕最顶部(不包括控件本身))
//location [0] 为x绝对坐标;location [1] 为y绝对坐标
int[] location = new int[2];
locationView.getLocationInWindow(location); //获取在当前窗体内的绝对坐标
//获取当前Activity所在的窗体
Window window = getWindow();
WindowManager.LayoutParams wlp = window.getAttributes();
//获取通知栏高度 重要的在这,获取到通知栏高度
int notificationBar = Resources.getSystem().getDimensionPixelSize(
Resources.getSystem().getIdentifier("status_bar_height", "dimen", "android"));
wlp.x = location[0];//对 dialog 设置 x 轴坐标
wlp.y = location[1] + locationView.getHeight() - notificationBar - 15; //对dialog设置y轴坐标
wlp.gravity = Gravity.TOP;
wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(wlp);
llRemarks = (LinearLayout) findViewById(R.id.ll_remarks);
llPullBlack = (LinearLayout) findViewById(R.id.ll_pull_black);
llDeleteFriend = (LinearLayout) findViewById(R.id.ll_delete_friend);
llPullBlack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
listener.pullBlackClick();
}
cancel();
}
});
llRemarks.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
listener.remarksClick();
}
cancel();
}
});
llDeleteFriend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
listener.deleteFriend();
}
cancel();
}
});
}
}
3.5 Notas sobre o uso do DIalog:
Como o DIalog deve depender de uma Activity, se a Activity for acidentalmente destruída, o Dialog aparecerá ou a mensagem não encontrará a Activity, resultando em um travamento. A solução é a seguinte:
- Defina a atividade como uma referência fraca para que, quando a atividade for destruída, o objeto que contém a atividade possa ser reciclado a tempo.
- Determine se a atividade está ativa antes que o diálogo exiba a mensagem, para garantir o pop-up ou mensagem segura
Trate-o da seguinte forma:
@Override
public void dismiss() {
if (context == null || ((Activity) context).isDestroyed() || ((Activity) context).isFinishing()) {
return;
}
super.dismiss();
}
@Override
public void show() {
if (context == null || ((Activity) context).isDestroyed() || ((Activity) context).isFinishing()) {
return;
}
super.show();
}
Em quarto lugar, o uso de Toast
4.1 O mais fácil de usar, chame a API do sistema
Toast.makeText(context, "提示消息", Toast.LENGTH_SHORT).show();
4.2 Defina a posição do Toast, topo, meio, fundo, etc.
Toast toast = Toast.makeText(context, "提示消息", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.TOP, 0, 0);
toast.show();
4.3 Controle a frequência pop-up em um curto espaço de tempo
private static String oldMsg;
protected static Toast toast = null;
private static long oneTime = 0;
private static long twoTime = 0;
public static void showToast(Context context, String s, int gravity, int offX, int offY) {
if (toast == null) {
toast = Toast.makeText(context, s, Toast.LENGTH_SHORT);
toast.setGravity(gravity, offX, offY);
toast.show();
oneTime = System.currentTimeMillis();
} else {
twoTime = System.currentTimeMillis();
if (s.equals(oldMsg)) {
if (twoTime - oneTime > Toast.LENGTH_SHORT) {
toast.show();
}
} else {
oldMsg = s;
toast.setText(s);
toast.show();
}
}
oneTime = twoTime;
}
4.4 Personalizar o estilo de layout do brinde
private static TextView textView;
/**
* 自定义样式的吐司
* <p/>
* 静态toast 只创建一个toast实例 可以实时显示弹出的内容
*
* @param context
* @param text
*/
public static void showPicToast(Context context, String text) {
if (toast == null) { // 1. 创建前 2.消失后toast为null
// 获取打气筒
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//创建视图
View view = inflater.inflate(R.layout.toast_bg, null);
textView = (TextView) view.findViewById(R.id.tv_toast_text);
//创建土司
toast = new Toast(context);
//设置居中方式 默认在底部
//toast.setGravity(Gravity.CENTER, 0, 0);//如果不设置剧中方式,使用系统默认的吐司位置
//设置土司的持续时长
toast.setDuration(Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
//设置土司的背景View
toast.setView(view);
}
//设置土司的显示额内容
textView.setText(text);
toast.show();
}
4.5 Notas de uso:
Após o Android 30, o parâmetro de posição do Toast será inválido e a posição definida seguirá a posição do sistema, portanto, o Google recomenda substituir o Toast pelo SnackBar.
Se você insistir em usar o Toast e recomendar uma biblioteca do Toast, poderá definir o local do Toast e adicionar a biblioteca dependente da seguinte maneira
implementation 'com.hjq:toast:8.8'
Inicializar no aplicativo
ToastUtils.init(this);
Abra o brinde em outros lugares
ToastUtils.show("提示消息");
Five SnackBar
5.1
5.2 Fácil de usar
public void snackbar1(View view) {
Snackbar.make(this,llRootLayout,"snack bar",Snackbar.LENGTH_SHORT).show();
}
O primeiro parâmetro é Context,
o segundo parâmetro é a exibição a ser exibida,
o terceiro parâmetro é a string a ser exibida
e o quarto parâmetro é a duração da exibição. Existem três tipos de duração:
Snackbar.LENGTH_SHORT e Toast.LENGHT_SHORT ( cerca de 1 segundo a mais ) desaparecerá automaticamente após um curto período de exibição.
Snackbar.LENGTH_LONG é o mesmo que Toast.LENGHT_LONG (cerca de 3 segundos) e
desaparecerá automaticamente após um período de tempo relativamente longo.
5.3 Uso interativo
public void snackbar2(View view) {
Snackbar snack_bar = Snackbar.make(this,view, "确定退出吗?", Snackbar.LENGTH_INDEFINITE);
snack_bar.setAction("确认", new View.OnClickListener() {
@Override
public void onClick(View v) {
//退出
}
});
snack_bar.show();
}
5.4 Layout personalizado SnackBar
layout snackbar_view.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#000000">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginLeft="12dp"
android:textColor="@color/black"
android:textSize="11sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/textView" />
<TextView
android:id="@+id/textViewSub"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:gravity="center"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:text=""
android:textColor="#ffffff"
android:textSize="11sp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Classe de ferramenta SnackBarUtil.java
public class SnackBarUtil {
//自定义 SnackBar 布局
public static void show(Activity activity, View view, String msg, String action, SnackBarOnClick listener) {
//获取示例 findViewById(android.R.id.content) //LENGTH_LONG/LENGTH_SHORT: 会自动消失 LENGTH_INDEFINITE: 需要手动点击消失
Snackbar snackbar = Snackbar.make(view, "", Snackbar.LENGTH_SHORT);
//设置 Snackbar 的深度,避免被其他控件遮挡
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
snackbar.getView().setElevation(0);
}
//设置背景透明,避免自带黑色背景影响
snackbar.getView().setBackgroundColor(Color.TRANSPARENT);
//设置padding 取消自定义时黑色边框
snackbar.getView().setPadding(0, 0, 0, 0);
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) snackbar.getView();
//设置SnackBar的显示位置
//ViewGroup.LayoutParams layoutParams = snackbarLayout.getLayoutParams();
FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(dip2px(activity,260),dip2px(activity,32)); // 将原来Snackbar的宽高传入新的LayoutParams
flp.gravity = Gravity.CENTER | Gravity.BOTTOM; // 设置显示位置
flp.bottomMargin = dip2px(activity,8);
((View) snackbarLayout).setLayoutParams(flp);
//获取自定义布局
View inflate = LayoutInflater.from(activity).inflate(R.layout.snackbar_view, null);
//获取布局内控件
TextView textView = inflate.findViewById(R.id.textView);
//TextView 前边添加图片
//Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher_round);//图片自己选择
//drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
//textView.setCompoundDrawables(drawable, null, null, null);
//增加文字和图标的距离
//textView.setCompoundDrawablePadding(20);
//设置文本
textView.setText(msg);
if (action != null && listener != null) {
TextView textViewSub = inflate.findViewById(R.id.textViewSub);
textViewSub.setVisibility(View.VISIBLE);
textViewSub.setText(action);
textViewSub.setOnClickListener(v -> {
if (listener != null) {
listener.clickEvent(snackbar);
}
});
}
//添加图片 获取布局内控件
//ImageView imageView = inflate.findViewById(R.id.imageView2);
//获取图片资源
//Drawable drawable = activity.getResources().getDrawable(closeIcon);
//设置图片
//imageView.setImageDrawable(drawable);
//将自定义布局添加到 Snackbar 中
snackbarLayout.addView(inflate);
//显示
snackbar.show();
}
public interface SnackBarOnClick {
void clickEvent(Snackbar snackbar);
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Activity activity, float dpValue) {
final float scale = activity.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
Chamar classe de ferramenta SnackBarUtil
public void snackbar3(View view) {
SnackBarUtil.show(this, view, "确定退出吗?", "确认", new SnackBarUtil.SnackBarOnClick() {
@Override
public void clickEvent(Snackbar snackbar) {
//退出
}
});
}
Uso da caixa suspensa Seis PopWindow
6.1 O PopWindow também é uma caixa pop-up. Comparado com o Dialog, é mais usado no pop-up com base em uma determinada posição de controle e também oferece suporte a layout e estilo arbitrários.
6.2 Exemplo, adicionar arquivo de layout, popup_size_listview.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/dp_80"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rcl_view"
android:layout_width="@dimen/dp_80"
android:layout_height="wrap_content" />
</RelativeLayout>
6.3 Personalizar PopupWindow
public class PopupviewSizeModel extends PopupWindow {
private IPopuWindowListener mOnClickListener;
private Activity mContext;
private RecyclerView recyclerView;
private PopupModelListAdapter popupModelListAdapter;
private List<InfoBean.BindSizeListDTO> listString=new ArrayList<>();
/**
* 暴露给外面的设置单击事件
*/
public void setPopuWindowListener(IPopuWindowListener mOnClickListener) {
this.mOnClickListener = mOnClickListener;
}
public PopupviewSizeModel(Activity context) {
super(context);
this.mContext = context;
//获取布局文件
View mContentView = LayoutInflater.from(mContext).inflate(R.layout.popup_size_listview, null);
initView(mContentView);
//设置布局
setContentView(mContentView);
int width = context.getWindowManager().getDefaultDisplay().getWidth();
setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
//设置可以点击外部消息
//开始
setOutsideTouchable(true);
setFocusable(true);
// setBackgroundDrawable(new BitmapDrawable());
setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent));
setAnimationStyle(R.style.pop_animation);
}
private void initView(View contentView) {
recyclerView = (RecyclerView) contentView.findViewById(R.id.rcl_view);
LinearLayoutManager manager = new LinearLayoutManager(mContext);
recyclerView.setLayoutManager(manager);
recyclerView.setNestedScrollingEnabled(false);
popupModelListAdapter = new PopupModelListAdapter(mContext, R.layout.item_popup_size_list, listString);
recyclerView.setAdapter(popupModelListAdapter);
popupModelListAdapter.setOnItemClickListener(new MultiItemTypeAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) {
dismiss();
if(mOnClickListener!=null){
mOnClickListener.onItemClick(position);
}
}
@Override
public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position) {
return false;
}
});
}
public void setListData(List<InfoBean.BindSizeListDTO> list) {
listString.clear();
listString.addAll(list);
popupModelListAdapter.setData(listString);
popupModelListAdapter.notifyDataSetChanged();
}
// /***
// * 在android7.0上,如果不主动约束PopuWindow的大小,比如,设置布局大小为 MATCH_PARENT,那么PopuWindow会变得尽可能大,以至于 view下方无空间完全显示PopuWindow,而且view又无法向上滚动,此时PopuWindow会主动上移位置,直到可以显示完全。
// * 解决办法:主动约束PopuWindow的内容大小,重写showAsDropDown方法:
// * @param anchor
// */
// @Override
// public void showAsDropDown(View anchor,int xoff,int yoff,int gravity) {
// if (Build.VERSION.SDK_INT >= 24) {
// Rect visibleFrame = new Rect();
// anchor.getGlobalVisibleRect(visibleFrame);
// int height = anchor.getResources().getDisplayMetrics().heightPixels - visibleFrame.bottom;
// setHeight(height);
// showAsDropDown(anchor, xoff, yoff,gravity);
// } else {
// showAsDropDown(anchor, xoff, yoff,gravity);
// }
// super.showAsDropDown(anchor);
// }
public interface IPopuWindowListener {
void onItemClick(int position);
}
}
6.4 Configurando o modelo de dados
publicclass BindSizeListDTO{
public String id;
public String name;
public BindSizeListDTO(String name) {
this.name = name;
}
}
6.5 Abra a PopupWindow na posição de exibição especificada
private PopupviewSizeModel popupviewModelSize;
List<InfoBean.BindSizeListDTO> listStringSize = new ArrayList<>();
private void showSizePopup() {
listStringSize.add(new InfoBean.BindSizeListDTO("230x150"));
listStringSize.add(new InfoBean.BindSizeListDTO("270x200"));
listStringSize.add(new InfoBean.BindSizeListDTO("300x200"));
listStringSize.add(new InfoBean.BindSizeListDTO("330x225"));
listStringSize.add(new InfoBean.BindSizeListDTO("340x239"));
if (popupviewModelSize == null) {
popupviewModelSize = new PopupviewSizeModel(mContext);
popupviewModelSize.setPopuWindowListener(new PopupviewSizeModel.IPopuWindowListener() {
@Override
public void onItemClick(int position) {
Toast.makeText(SnackBarActivity.this, "点击了条目", Toast.LENGTH_SHORT).show();
}
});
}
popupviewModelSize.setListData(listStringSize);
popupviewModelSize.showAsDropDown(tvSize);
}
Seven DialogFragment
7.1 Dialog depende do ciclo de vida da Activity A diferença entre DialogFragment e Dialog é que ele é essencialmente um Fragment, que também tem o ciclo de vida pertencente ao Fragment.
7.1 A forma de criar DialogFragment, use onCreateDialog para criar o Dialog fornecido pelo sistema
public class SystemDialogFragment extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setTitle("系统弹窗")
.setMessage("信息")
//.setIcon(R.drawable.assign_set_question_ic_v2)
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getContext(), "确认", Toast.LENGTH_SHORT).show();
}
}).create();
return dialog;
}
}
Chame o DialogFragment na Activity
public class DialogFragmentActivity extends ComponentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activty_dialog_fragment);
}
public void alertdialog(View view) {
SystemDialogFragment systemDialogFragment=new SystemDialogFragment();
systemDialogFragment.show(getFragmentManager(),"ss");
}
}
Efeito:
7.2 Layout personalizado DialogFragment
Crie um arquivo de layout dialog_confirm.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:background="#ffffff"
android:orientation="vertical"
android:paddingLeft="25dp"
android:paddingTop="20dp"
android:paddingRight="25dp"
android:paddingBottom="20dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="退出提示"
android:textColor="#333333"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="15dp"
android:gravity="center"
android:text="是否对本地编辑的内容进行保存?"
android:textColor="#333333"
android:textSize="14sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:gravity="center"
android:paddingTop="9dp"
android:paddingBottom="9dp"
android:text="取消"
android:textColor="#666666"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_sure"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:gravity="center"
android:paddingTop="9dp"
android:paddingBottom="9dp"
android:text="立即充值"
android:textColor="#000000"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
Criar DialogFragment, CustomerDialogFragment.java
public class CustomerDialogFragment extends DialogFragment {
public View mRootView;
private TextView tvTitle;
private TextView tvContent;
private TextView tvCancel;
private TextView tvSure;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null){
//获取布局
mRootView = inflater.inflate(R.layout.dialog_confirm,container,false);
}
tvTitle = (TextView) mRootView.findViewById(R.id.tv_title);
tvContent = (TextView) mRootView.findViewById(R.id.tv_content);
tvCancel = (TextView) mRootView.findViewById(R.id.tv_cancel);
tvSure = (TextView) mRootView.findViewById(R.id.tv_sure);
tvCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
tvSure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "点击确定", Toast.LENGTH_SHORT).show();
dismiss();
}
});
return mRootView;
}
}
A atividade chama o DialogFragment
public void customerdialog(View view) {
CustomerDialogFragment systemDialogFragment=new CustomerDialogFragment();
systemDialogFragment.show(getFragmentManager(),"Customer");
}
Demonstração de efeito
Estrutura de suspensão de oito sistemas
8.1 O quadro flutuante do sistema segue o ciclo de vida do aplicativo, não pode estar acima da Atividade, não depende da Atividade e é adequado para o quadro pop-up do ciclo de vida global
8.2 Use, crie um novo arquivo de layout, float_layout_window.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="match_parent">
<LinearLayout
android:id="@+id/linear"
android:layout_width="200dp"
android:orientation="vertical"
android:gravity="center"
android:background="@color/purple_200"
android:layout_height="200dp">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:text="移动"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:text="移动"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
8.2 Adicionar permissão de quadro flutuante
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
8.3 Crie um novo FloatWindowsActivity.java e adicione um layout de quadro flutuante
public class FloatWindowsActivity extends ComponentActivity {
public WindowManager mWindowManager;
public View mWindowView;
public LinearLayout mText;
public WindowManager.LayoutParams wmParams;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activty_float_windows);
}
public void folatwindows(View view) {
checkFloatPermission();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==0){
checkFloatPermission();
}
}
/**
* 检查是否开启悬浮框权限
*/
public void checkFloatPermission(){
if(!Settings.canDrawOverlays(FloatWindowsActivity.this)) {
Toast.makeText( FloatWindowsActivity.this, "当前无权限,请授权", Toast.LENGTH_SHORT);
startActivityForResult( new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse( "package:"+ getPackageName())), 0);
}else {
initWindowParams();
}
}
/**
* 初始化Window对象的参数
*/
private void initWindowParams() {
//1,获取系统级别的WindowManager
mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
wmParams = new WindowManager.LayoutParams();
//2,添加系统参数,确保悬浮框能显示到手机上
//电话窗口。它用于电话交互(特别是呼入)。它置于所有应用程序之上,状态栏之下。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
// flag 设置 Window 属性
wmParams.flags
|= WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
//期望的位图格式。默认为不透明
wmParams.format = PixelFormat.TRANSLUCENT;
//不许获得焦点
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//窗口停靠位置
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
addWindowViewZWindow();
initClick();
}
/**
* 添加View到桌面Window界面上
*/
private void addWindowViewZWindow() {
if(mWindowView==null){
mWindowView = LayoutInflater.from(getApplication()).inflate(R.layout.float_layout_window, null);
mText = (LinearLayout) mWindowView.findViewById(R.id.linear);
}
mWindowManager.addView(mWindowView, wmParams);
}
/**
* 点击事件和拖拽事件
*/
int mStartX, mStartY;
int mEndX, mEndY;
private void initClick() {
mText.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
//按下鼠标的时候记录下屏幕的位置
case MotionEvent.ACTION_DOWN:
mStartX = (int) event.getRawX();
mStartY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
mEndX = (int) event.getRawX();
mEndY = (int) event.getRawY();
if (needIntercept()) {
//getRawX是触摸位置相对于整个屏幕的位置,getX是控触摸点相对于控件最左边的位置
wmParams.x = (int) event.getRawX() - mWindowView.getMeasuredWidth() / 2;
wmParams.y = (int) event.getRawY() - mWindowView.getMeasuredHeight() / 2;
mWindowManager.updateViewLayout(mWindowView, wmParams);
return true;
}
break;
case MotionEvent.ACTION_UP:
if (needIntercept()) {
return true;
}
break;
}
return false;
}
});
mText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(FloatWindowsActivity.this, "点击悬浮框", Toast.LENGTH_SHORT).show();
}
});
}
/**
* 判断是否拦截,根据滑动的距离
*
* @return
*/
private boolean needIntercept() {
if (Math.abs(mStartX - mEndX) > 30 || Math.abs(mStartY - mEndY) > 30) {
return true;
}
return false;
}
}
8.4 Renderização
Pode ser exibido em qualquer Activity
Você também pode retornar à área de trabalho e ainda exibir