HarmonyOS弹窗组件,Xpopup 精华
弹窗可以用来操作数据,也可以用来反馈信息。作为产品与用户沟通的核心枢纽,不管是Web前端还是移动端,经常会看见弹窗类的消息。所以,良好的弹窗效果对于用户体验感非常重要。那么,怎样才能设计出好的弹窗?
不少开发者也为之头疼,因为弹窗的样式千变万化,开发起来比较繁琐。为解决这个问题,Xpopup三方弹窗组件应运而生。
一、什么是Xpopup
Xpopup是一款十分强大的弹窗组件,内置了多种常用的弹窗和良好的动画。遵循Material Design,在设计动画的时候考虑了很多细节、过渡、层级的变化,让UI动画更加流畅。同时,用户可以根据需要在Xpopup的内置弹窗的基础上自定义弹窗的样式和业务逻辑。
二、Xpopup分类
综合常见的弹窗场景,可以将其分为以下七类:
1.Center类型:从界面中间弹出,比如确认和取消弹窗、Loading弹窗。
2.Bottom类型:从界面底部弹出,比如知乎的从底部弹出的评论列表。
3.Attach类型:弹窗的位置需要依附于某个Component或者某个触摸点。
4.Drawer类型:从界面的左边或者右边弹出,并支持手势拖拽。
5.ImageViewer类型:大图片浏览弹窗。
6.FullScreen类型:铺满整个界面的弹窗,可以设置任意的动画器,适合用来实现登录、选择性的界面效果。
7.Position类型:自由定位弹窗,可放在屏幕任意地方,结合强大的动画器,可以实现各种效果。
三、Xpopup演示及使用
Xpopup有两种使用方式。一种是内置弹窗,开发者可以直接拿来用。另一种是自定义弹窗,开发者需根据自己的需求来设置相应的样式。
1、Center类型
Center类型,是在界面中间弹出的弹窗。如下图所示,通过点击“显示Confirm弹窗”、“使用内置弹窗绑定项目已有布局”以及“带输入框的Confirm弹窗”,屏幕中央依次弹出了相对应的弹窗效果。
以显示带输入框的Confirm弹窗为例,实现代码如下:
new XPopup.Builder(getContext())
.hasStatusBarShadow(false)
.autoOpenSoftInput(true)
.isDarkTheme(true)
// 用于获取页面根容器,监听页面高度变化,解决输入法盖住弹窗的问题
.setComponent(component)
.setPopupCallback(new DemoXPopupListener())
.asInputConfirm("我是标题", null, null, "我是默认Hint文字",
new OnInputConfirmListener() {
@Override
public void onConfirm(String text) {
toast("input text: " + text);
}
})
.show();
当内置弹窗样式已无法满足开发者的需求,开发者就可以根据自己的需求自定义弹窗。
Center类型的自定义弹窗通过继承CenterPopupView接口,重写getImplLayoutId()返回弹窗的布局,再在onCreate中编写业务需要的逻辑即可。
代码如下:
class CustomPopup extends CenterPopupView {
//注意:自定义弹窗本质是一个自定义控件,但是只需重写一个参数的构造,其他的不要重写,所有的自定义弹窗都是这样
public CustomPopup(Context context) {
super(context, null);
}
// 返回自定义弹窗的布局
@Override
protected int getImplLayoutId() {
return ResourceTable.Layout_custom_popup;
}
// 执行初始化操作,比如:findComponentById,设置点击,或者任何你弹窗内的业务逻辑
@Override
protected void onCreate() {
super.onCreate();
findComponentById(ResourceTable.Id_tv_close).setClickedListener(new ClickedListener() {
@Override
public void onClick(Component component) {
dismiss(); // 关闭弹窗
}
});
}
// 设置最大宽度,看需要而定
@Override
protected int getMaxWidth() {
return super.getMaxWidth();
}
// 设置最大高度,看需要而定
@Override
protected int getMaxHeight() {
return super.getMaxHeight();
}
// 设置自定义动画器,看需要而定
@Override
protected PopupAnimator getPopupAnimator() {
return super.getPopupAnimator();
}
// 弹窗的宽度,用来动态设定当前弹窗的宽度,受getMaxWidth()限制
protected int getPopupWidth() {
return 0;
}
// 弹窗的高度,用来动态设定当前弹窗的高度,受getMaxHeight()限制
protected int getPopupHeight() {
return 0;
}}
2、Bottom类型
Bottom类型,是从页面底部弹出的弹窗。
如下图所示,通过点击“显示Bottom类型的List弹窗”从界面底部弹出了带选中效果的选项列表。
实现代码如下:
// 从底部弹出,带手势拖拽的列表弹窗
case ResourceTable.Id_btnShowBottomList:
new XPopup.Builder(getContext())
.isDarkTheme(true)
.enableDrag(true)
.asBottomList("请选择一项", new String[]{"条目1", "条目2", "条目3", "条目4", "条目5", "条目6", "条目7"},
new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
toast("click " + text);
}
})
.show();
break;
case ResourceTable.Id_btnShowBottomListWithCheck: // 从底部弹出,带选中效果
new XPopup.Builder(getContext())
.isDestroyOnDismiss(true) // 对于只使用一次的弹窗,推荐设置这个
.asBottomList("标题可以没有", new String[]{"条目1", "条目2", "条目3", "条目4", "条目5"},
null, 2,
new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
toast("click " + text);
}
})
.show();
break;
Bottom类型的自定义弹窗通过继承BottomPopupView接口来实现。这里例举一个模仿知乎评论的实现,效果如下:
实现代码如下:
public class ZhihuCommentPopup extends BottomPopupView {
ListContainer listContainer;
public ZhihuCommentPopup(Context context) {
super(context, null);
}
@Override
protected int getImplLayoutId() {
return ResourceTable.Layout_custom_bottom_popup;
}
@Override
protected void onCreate() {
super.onCreate();
listContainer = (ListContainer) findComponentById(ResourceTable.Id_listContainer);
ArrayList<String> strings = new ArrayList<>();
for (int i = 0; i < 30; i++) {
strings.add("");
}
EasyProvider commonAdapter = new EasyProvider<String>(getContext(), strings, ResourceTable.Layout_adapter_zhihu_comment) {
@Override
protected void bind(ViewHolder holder, String itemData, final int position) {}
};
listContainer.setItemClickedListener(new ListContainer.ItemClickedListener() {
@Override
public void onItemClicked(ListContainer listContainer, Component component, int position, long id) {
dismiss();
}
});
listContainer.setItemProvider(commonAdapter);
}
// 最大高度为Window的0.7
@Override
protected int getMaxHeight() {
return (int) (XPopupUtils.getScreenHeight(getContext()) * .7f);
}}
3、Attach类型
Attach类型,弹窗的位置需要依附于某个Component(桌面控件)或者某个触摸点。如下图所示,通过点击或者长按桌面控件,弹窗在点击的位置弹出:
依附于Component的Attach类型弹窗的实现代码如下:
new XPopup.Builder(getContext())
.atView(component) // 依附于所点击的Component,内部会自动判断在上方或者下方显示
.asAttachList(new String[]{"分享", "编辑", "不带icon"},
new int[]{ResourceTable.Media_icon, ResourceTable.Media_icon},
new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
toast("click " + text);
}
})
.show();
如果是想依附于某个Component的触摸点,则需要先监视该Component,然后设置点击或长按触发:
Component component = findComponentById(ResourceTable.Id_btnShowAttachPoint);
// 必须在事件发生前,调用这个方法来监视View的触摸
final XPopup.Builder builder = new XPopup.Builder(getContext()).watchView(component);
component.setLongClickedListener(new LongClickedListener() {
@Override
public void onLongClicked(Component component) {
builder.asAttachList(new String[]{"置顶", "复制", "删除"}, null,
new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
toast("click " + text);
}
})
.show();
}
});
Attach类型的自定义弹窗,开发者需通过继承AttachPopupView接口来实现。如下图所示,展示了自定义Attach弹窗、横向带气泡Attach弹窗,以及垂直带气泡Attach弹窗。
实现代码如下:
public class CustomAttachPopup2 extends AttachPopupView {
public CustomAttachPopup2(Context context) {
super(context, null);
}
@Override
protected int getImplLayoutId() {
return ResourceTable.Layout_custom_attach_popup2;
}
// 设置状态栏的高度,用以修正自定义位置弹窗的高度
@Override
protected int setStatusBarHeight() {
return 130;
}}
4、Drawer类型
Drawer类型,是从界面的左边或者右边弹出,并支持手势拖拽。效果展示如下:
实现代码如下:
public class CustomDrawerPopupView extends DrawerPopupView {
public CustomDrawerPopupView(Context context) {
super(context, null);
}
@Override
protected int getImplLayoutId() {
return ResourceTable.Layout_custom_list_drawer;
}
@Override
protected void onCreate() {
super.onCreate();
findComponentById(ResourceTable.Id_btn).setClickedListener(new ClickedListener() {
@Override
public void onClick(Component component) {
toast("nothing!!!");
}
});
}}
//使用自定义的DrawerLayout弹窗:
new XPopup.Builder(getContext())
.popupPosition(PopupPosition.Right)//右边
.asCustom(new CustomDrawerPopupView(getContext()))
.show();
5、ImageViewer类型
ImageViewer类型,支持大图片浏览弹窗。如下图所示,点击图片浏览按钮后,界面会弹出一个图像列表,任意点击一张图片,一张网络上的图片就加载到了界面上。
实现代码如下:
// 当你点击图片的时候执行以下代码:
// 多图片场景(你有多张图片需要浏览)
new XPopup.Builder(getContext()).asImageViewer(image, position, list,
new OnSrcViewUpdateListener() {
@Override
public void onSrcViewUpdate(ImageViewerPopupView popupView, int position) {
// pager更新当前显示的图片
// 当启用isInfinite时,position会无限增大,需要映射为当前ViewPager中的页
int realPosi = position % list.size();
pager.setCurrentPage(realPosi, false);
}
}, new ImageLoader()).show();
// 单张图片场景
new XPopup.Builder(getContext())
.asImageViewer(image, url, new ImageLoader())
.show();// 图片加载器,XPopup不负责加载图片,需要你实现一个图片加载器传给我,这里以Glide和OkGo为例(可直接复制到项目中):
class ImageLoader implements XPopupImageLoader {
@Override
public void loadImage(int position, String url, Image imageView) {
// 一进入页面就加载图片的话,需要加点延迟
context.getUITaskDispatcher().delayDispatch(new Runnable() {
@Override
public void run() {
Glide.with(context)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(image);
}
}, 50);
}
// 必须实现这个方法,用来下载图片。可参照下面的实现,内部保存图片会用到。如果你不需要保存图片这个功能,可以返回null。
@Override
public File getImageFile(Context context, String url) {
try {
return OkGo.<File>get(url).tag(this).converter(new FileConvert()).adapt().execute().body();
} catch (Exception e) {
LogUtil.error(TAG, e.getMessage());
}
return null;
}
ImageViewer类型的自定义弹窗,开发者需通过继承ImageViewerPopupView接口实现。目前ImageViewer类型弹窗支持在上面添加任意自定义布局和背景颜色。
代码如下:
public class CustomImagePopup extends ImageViewerPopupView {
public CustomImagePopup(Context context) {
super(context, null);
}
@Override
protected int getImplLayoutId() {
return ResourceTable.Layout_custom_image_viewer_popup;
}}
// 自定义的弹窗需要用asCustom来显示,之前的asImageViewer这些方法当然不能用了。
CustomImagePopup viewerPopup = new CustomImagePopup(getContext());
// 自定义的ImageViewer弹窗需要自己手动设置相应的属性,必须设置的有srcView,url和imageLoader。
viewerPopup.setSingleSrcView(image2, url2);
viewerPopup.setXPopupImageLoader(new ImageLoader());
new XPopup.Builder(getContext())
.asCustom(viewerPopup)
.show();
6、FullScreen类型
FullScreen类型,是全屏弹窗。如下图所示,展示了一个铺满整个界面的弹窗。
FullScreen类型的自定义弹窗的实现代码如下:
public class CustomFullScreenPopup extends FullScreenPopupView {
public CustomFullScreenPopup(Context context) {
super(context, null);
}
@Override
protected int getImplLayoutId() {
return ResourceTable.Layout_custom_fullscreen_popup;
}
@Override
protected void onCreate() {
super.onCreate();
// 初始化
}}
7、Position类型
Position类型,是自由定位弹窗。如下图所示,通过依次点击不同位置标签,弹窗从对应的位置弹出:
Position类型的自定义弹窗的实现代码如下:
public class QQMsgPopup extends PositionPopupView {
public QQMsgPopup(Context context) {
super(context, null);
}
@Override
protected int getImplLayoutId() {
return ResourceTable.Layout_popup_qq_msg;
}}
new XPopup.Builder(getContext())
.popupAnimation(PopupAnimation.ScaleAlphaFromCenter)
//通过isCenterHorizontal(true)选项来实现水平居中
.isCenterHorizontal(true)
//默认是显示在屏幕的左上角,可以通过offsetX()和offsetY()来控制显示位置,
.offsetY(200)
.asCustom(new QQMsgPopup(getContext()))
.show();
四、Xpopup常用设置
在使用Xpopup时,开发者可以根据自身需要,通过调用相应的函数对弹窗的最大宽高、是否水平居中、是否启用拖拽等信息进行设置。可用到的函数,如下表所示:
代码如下所示:
new XPopup.Builder(getContext())
//是否在消失的时候销毁资源,默认false。如果你的弹窗对象只使用一次,非常推荐设置这个,可以杜绝内存泄漏。如果会使用多次,千万不要设置
.dismissOnBackPressed(true) //按返回键是否关闭弹窗,默认为true
.customAnimator(null) //设置自定义的动画器
.offsetX(-10) //弹窗在x方向的偏移量
.isDarkTheme(true) //是否启用暗色主题
.borderRadius(10) //为弹窗设置圆角,默认是15,对内置弹窗生效
.autoDismiss(false) //操作完毕后是否自动关闭弹窗,默认为true;比如点击ConfirmPopup的确认按钮,默认自动关闭;如果为false,则不会关闭
.setPopupCallback(new SimpleCallback() { //设置显示和隐藏的回调
以上就是整个Xpopup的使用过程,感兴趣的小伙伴赶快点击下方附件或链接下载吧!
支持,多些组件开发太方便了。