【全網首發】鴻蒙開源三方組件--強大的彈窗庫XPopup組件


目錄:

1、介紹

2、效果一覽

3、依賴

4、如何使用

5、下載鏈接

6、《鴻蒙開源三方組件》文章合集

1. 介紹

​ XPopup是一個彈窗庫,可能是Harmony平台最好的彈窗庫。它從設計的時候就本着實用的原則,兼顧了美觀和優雅的交互。用戶都喜歡自然舒適的UI和交互,希望XPopup能帶給你一些幫助或者驚喜!

2. 效果一覽

內置彈窗(支持復用已有布局) 列表Center彈窗
inset1.gif inset2.gif
Bottom列表彈窗 自定義Bottom彈窗
bottom1gai.gif attach2.gif
Attach彈窗(動畫優雅,智能定位,長按支持) 自定義Attach彈窗(任意方向支持,靈活易用)
attach1.gif attach2.gif
Drawer彈窗 全屏彈窗(可作為Ability替代品,搭配十幾個動畫使用更佳)
drawer.gif full.gif
Position自由定位彈窗(放在屏幕任意地方) 自定義底部彈窗
position.gif input.gif
自定義彈窗和自定義動畫 內置優雅美觀的動畫器,可搭配彈窗結合使用
custom.gif animators.gif
ImageViewer大圖瀏覽彈窗 聯想搜索實現,輕而易舉
image.png search.gif

3. 依賴

allprojects{ repositories{ mavenCentral() } } implementation 'io.openharmony.tpc.thirdlib:XPopup:1.0.3' 

4. 如何使用

4.1 內置彈窗的使用

4.1.1 顯示確認和取消對話框

new XPopup.Builder(getContext()).asConfirm("我是標題", "我是內容", new OnConfirmListener() { @Override public void onConfirm() { toast("click confirm"); } }) .show(); 

4.1.2 顯示待輸入框的確認和取消對話框

new XPopup.Builder(getContext()).asInputConfirm("我是標題", "請輸入內容。", new OnInputConfirmListener() { @Override public void onConfirm(String text) { toast("input text: " + text); } }) .show(); 

4.1.3 顯示中間彈出的列表彈窗

new XPopup.Builder(getContext()) //.maxWidth(600) .asCenterList("請選擇一項", new String[]{"條目1", "條目2", "條目3", "條目4"}, new OnSelectListener() { @Override public void onSelect(int position, String text) { toast("click " + text); } }) .show(); 

4.1.4 顯示中間彈出的加載框

new XPopup.Builder(getContext()) .asLoading("正在加載中") .show(); 

4.1.5 顯示從底部彈出的列表彈窗

new XPopup.Builder(getContext()) .asBottomList("請選擇一項", new String[]{"條目1", "條目2", "條目3", "條目4", "條目5"}, new OnSelectListener() { @Override public void onSelect(int position, String text) { toast("click " + text); } }) .show(); 

4.1.6 顯示依附於某個Component或者某個點的彈窗

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的觸摸點,則需要先watch該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(); } }); 

asAttachList方法內部是對AttachPopupView的封裝,如果你的布局不是列表,可以繼承AttachPopupView實現自己想要的布局。AttachPopupView會出現在目標的上方或者下方,如果你想要出現在目標的左邊或者右邊(像微信朋友圈那樣點贊的彈窗),可以繼承HorizontalAttachPopupView,然后編寫你的布局即可。

最簡單的示例如下:

public class CustomAttachPopup extends HorizontalAttachPopupView { public CustomAttachPopup(Context context) { super(context, null); } @Override protected int getImplLayoutId() { return ResourceTable.Layout_custom_attach_popup; } @Override protected void onCreate() { super.onCreate(); findComponentById(ResourceTable.Id_tv_zan).setClickedListener(new ClickedListener() { @Override public void onClick(Component component) { ToastUtil.showToast(getContext(), "贊"); dismiss(); } }); findComponentById(ResourceTable.Id_tv_comment).setClickedListener(new ClickedListener() { @Override public void onClick(Component component) { ToastUtil.showToast(getContext(), "評論"); dismiss(); } }); } // 設置狀態欄的高度,用以修正自定義位置彈窗的高度 @Override protected int setStatusBarHeight() { return 130; }} 

4.1.7 顯示大圖瀏覽彈窗

// 當你點擊圖片的時候執行以下代碼: // 多圖片場景(你有多張圖片需要瀏覽) 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; } } 

如果你用的不是Glide,請參考去實現即可。

4.1.8 關閉彈窗

先拿到彈窗對象,以Loading彈窗為例,其他也是一樣:

BasePopupView popupView = new XPopup.Builder(getContext()) .asLoading("正在加載中") .show(); 

執行消失:

//有四個消失方法可供選擇: popupView.dismiss(); //立即消失 popupView.delayDismiss(300); //延時消失,有時候消失過快體驗可能不好,可以延時一下 popupView.smartDismiss(); //會等待彈窗的開始動畫執行完畢再進行消失,可以防止接口調用過快導致的動畫不完整。 popupView.dismissWith({}); //消失動畫執行完之后執行某些邏輯 

我們在項目中經常會點擊某個按鈕然后關閉彈窗,接着去做一些事。比如:點擊一個按鈕,關閉彈窗,然后開啟一個界面,要知道彈窗的關閉是有一個動畫過程的,上面的寫法會出現彈窗還沒有完全關閉,就立即跳頁面,界面有一種頓挫感;而且在設備資源不足的時候,還可能造成丟幀。所以很多時候不推薦直接使用dismiss()方法,除非你關閉完彈窗后面沒有任何邏輯執行。
為了得到最佳體驗,您可以等dismiss動畫完全結束去執行一些東西,而不是立即就執行。可以這樣做:

popupView.dismissWith(new Runnable() { @Override public void run() { // 跳轉到新頁面 }}); 

每個彈窗本身也有onShow()和onDismiss()的生命周期回調,可以根據需要使用。
還有這樣一種場景:彈窗show()完之后,你的邏輯執行完畢,然后調用dismiss()。但是你的邏輯執行過快,可能導致彈窗的show動畫還沒有執行完就直接dismiss了,界面上的感覺並不好。這個時候推薦使用smartDismiss()方法,這個方法能保證彈窗的show動畫執行完再關閉彈窗。

4.1.9 復用項目已有布局

如果你項目中已經有寫好的彈窗布局,而想用XPopup提供的動畫和交互能力,也是完全沒有問題的。目前支持設置自定義布局的彈窗有:

  • Confirm彈窗,就是確認和取消彈窗
  • 帶輸入框的Confirm彈窗
  • Loading彈窗
  • 帶列表的Attach彈窗,Center彈窗和Bottom彈窗

假設,你想使用XPopup的Confirm彈窗,但布局想用自己的,只需要這樣設置一下,其他不用動即可:

new XPopup.Builder(getContext()) .asConfirm(null, "您可以復用項目已有布局,來使用XPopup強大的交互能力和邏輯封裝,彈窗的布局完全由你自己控制。\n" + "需要注意的是:你自己的布局必須提供一些控件Id,否則XPopup找不到控件。", "關閉", "XPopup牛逼", new OnConfirmListener() { @Override public void onConfirm() { toast("click confirm"); } }, null, false, ResourceTable.Layout_my_confim_popup)//綁定已有布局 .show(); 

這樣布局就是您自己的了,動畫和交互XPopup會幫你完成。但是需要注意的是,你自己提供的布局必須包含一些id,否則XPopup無法找到控件;id必須有,控件你可以隨意組合與擺放。具體如下:

  • Confirm彈窗必須包含的Text以及id有:tv_title,tv_content,tv_cancel,tv_confirm
  • 帶輸入框的Confirm彈窗在Confirm彈窗基礎上需要增加一個id為et_input的TextField
  • Loading彈窗,如果你想顯示一個Loading文字說明,則必須有一個id為tv_title的Text;如果不需要文字說明,則沒要求
  • 帶列表的彈窗會多一個bindItemLayout()方法用來綁定item布局
  • 其他不在多說,看bindLayout方法說明,會說明要求哪些id

每種內置彈窗的bindLayout和bindItemLayout的要求都在方法說明上,無需記憶,用的時候查看下方法的說明即可。

4.2 自定義彈窗

當你自定義彈窗的時候,需要根據需求選擇繼承CenterPopupViewBottomPopupViewAttachPopupView/HorizontalAttachPopupViewDrawerPopupViewPartShadowPopupViewFullScreenPopupViewPositionPopupView其中之一。

每種彈窗的功能和使用場景如下:

  • CenterPopupView:中間彈窗的彈窗,比如:確認取消對話框,Loading彈窗等,如果不滿意默認的動畫效果,可以設置不同的動畫器
  • BottomPopupView:從底部彈出的彈窗,比如:從底部彈出的分享彈窗,知乎的從底部彈出的評論彈窗,抖音從底部彈出的評論彈窗。這種彈窗帶有智能的嵌套滾動和手勢拖動
  • AttachPopupView/HorizontalAttachPopupView:Attach彈窗是需要依附於某個點或者某個Component來顯示的彈窗;其中AttachPopupView會出現在目標的上方或者下方。如果希望想要微信朋友圈點贊彈窗那樣的效果,出現在目標的左邊或者右邊,則需要繼承 HorizontalAttachPopupView來做
  • DrawerPopupView:從界面的左邊或者右邊彈出的像DrawerLayout那樣的彈窗,Drawer彈窗本身是橫向滑動的,但對PageSlider和ScrollView等橫向滑動控件做了兼容,在彈窗內部可以放心使用它們
  • FullScreenPopupView:全屏彈窗,看起來和Ability 一樣。該彈窗其實是繼承Center彈窗進行的一種實現,可以設置任意的動畫器
  • ImageViewerPopupView:大圖瀏覽彈窗
  • PositionPopupView:自由定位彈窗,如果你想讓彈窗顯示在左上角,或者右上角,或者任意位置,並且不需要依附任何Component,此時你需要它

自定義彈窗只有2個步驟:
一:根據自己的需求編寫一個類繼承對應的彈窗
二:重寫getImplLayoutId()返回彈窗的布局,在onCreate中像Ability那樣編寫你的邏輯即可
注意:自定義彈窗本質是一個自定義控件,但是只需重寫一個參數的構造,其他的不要重寫,所有的自定義彈窗都是這樣。

4.2.1 自定義Center彈窗

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; }} 

注意:Center彈窗的最大寬度默認是屏幕寬度的0.8,如果你自定義布局的寬度是寫死的,有可能超出屏幕寬度的0.8,如果你不想被最大寬度限制,只需要重寫方法:

@Overrideprotected int getMaxWidth() { return 0; //返回0表示不限制最大寬度 } 

使用自定義彈窗:

new XPopup.Builder(getContext()) .asCustom(new CustomPopup(getContext())) .show(); 

4.2.2 自定義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.2.3 自定義DrawerLayout類型彈窗

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(); 

4.2.4 自定義Bottom類型的彈窗

自定義Bottom類型的彈窗會比較常見,默認Bottom彈窗帶有手勢交互和嵌套滾動;如果您不想要手勢交互可以調用enableDrag(false)方法關閉。

請注意:彈窗的寬高是自適應的,大部分情況下都應該將彈窗布局的高設置為match_content;除非你希望得到一個高度撐滿的彈窗。

Demo中有一個模仿知乎評論的實現,代碼如下:

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); }} 

4.2.5 自定義全屏彈窗

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(); // 初始化 }} 

4.2.6 自定義ImageViewer彈窗

目前大圖瀏覽彈窗支持在上面添加任意自定義布局和背景顏色,做法是寫一個類繼承ImageViewerPopupView彈窗,然后重寫布局即可。

代碼如下:

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(); 

4.2.7 自定義Position彈窗

public class QQMsgPopup extends PositionPopupView { public QQMsgPopup(Context context) { super(context, null); } @Override protected int getImplLayoutId() { return ResourceTable.Layout_popup_qq_msg; }} 

自由定位彈窗,默認是顯示在屏幕的左上角,你可以通過offsetX()offsetY()來控制顯示位置,如果你希望水平居中,可以用isCenterHorizontal(true)選項來做到。

new XPopup.Builder(getContext()) .popupAnimation(PopupAnimation.ScaleAlphaFromCenter) .isCenterHorizontal(true) .offsetY(200) .asCustom(new QQMsgPopup(getContext())) .show(); 

4.3 自定義動畫

自定義動畫已經被設計得非常簡單,動畫和彈窗是無關的;這意味着你可以將動畫設置給內置彈窗或者自定義彈窗。繼承PopupAnimator,實現3個方法:

  • 如何初始化動畫

  • 動畫如何開始

  • 動畫如何結束

比如:自定義一個旋轉的動畫:

class RotateAnimator extends PopupAnimator { @Override public void initAnimator() { targetView.setScaleX(0.0f); targetView.setScaleY(0.0f); targetView.setAlpha(0.0f); targetView.setRotation(360.0f); } @Override public void animateShow() { targetView.createAnimatorProperty().rotate(0.0f).scaleX(1.0f).scaleY(1.0f).alpha(1.0f).setDuration(getDuration()).start(); } @Override public void animateDismiss() { targetView.createAnimatorProperty().rotate(720.0f).scaleX(0.0f).scaleY(0.0f).alpha(0.0f).setDuration(getDuration()).start(); }} 

使用自定義動畫:

new XPopup.Builder(getContext()) .customAnimator(new RotateAnimator()) .asConfirm("演示自定義動畫", "當前的動畫是一個自定義的旋轉動畫,無論是自定義彈窗還是自定義動畫,已經被設計得非常簡單;這個動畫代碼只有6行即可完成!", null) .show(); 

不想要動畫:

new XPopup.Builder(getContext()) .customAnimator(new EmptyAnimator(null)) .asConfirm("演示自定義動畫", "當前的動畫是一個自定義的旋轉動畫,無論是自定義彈窗還是自定義動畫,已經被設計得非常簡單;這個動畫代碼只有6行即可完成!", null) .show(); 

4.4 常用設置

4.4.1 全局設置

  1. 設置主色調 默認情況下,XPopup的主色為灰色,主色作用於Button文字,TextField邊框和光標,Check文字的顏色上。 主色調只需要設置一次即可,可以放在啟動頁中。

    XPopup.setPrimaryColor(getColor(ResourceTable.Color_colorPrimary)); 

4.4.2 常用設置

所有的設置如下,根據需要使用:

new XPopup.Builder(getContext()) .isDestroyOnDismiss(true) //是否在消失的時候銷毀資源,默認false。如果你的彈窗對象只使用一次,非常推薦設置這個,可以杜絕內存泄漏。如果會使用多次,千萬不要設置 .dismissOnBackPressed(true) //按返回鍵是否關閉彈窗,默認為true .dismissOnTouchOutside(true) //點擊外部是否關閉彈窗,默認為true .autoOpenSoftInput(true) //是否彈窗顯示的同時打開輸入法,只在包含輸入框的彈窗內才有效,默認為false .popupAnimation(PopupAnimation.ScaleAlphaFromCenter) //設置內置的動畫 .customAnimator(null) //設置自定義的動畫器 .popupPosition(PopupPosition.Right) //手動指定彈窗出現在目標的什么位置,對Attach和Drawer類型彈窗生效 .positionByWindowCenter(false) //默認是false,是否以屏幕中心進行定位,默認是false,為false時根據Material范式進行定位,主要影響Attach系列彈窗 .offsetX(-10) //彈窗在x方向的偏移量 .offsetY(-10) //彈窗在y方向的偏移量 .maxWidth(10) //設置彈窗的最大寬度,如果重寫彈窗的getMaxWidth(),以重寫的為准 .maxHeight(10) //設置彈窗的最大高度,如果重寫彈窗的getMaxHeight(),以重寫的為准 .isCenterHorizontal(true) //是否和目標水平居中,比如:默認情況下Attach彈窗依靠着目標的左邊或者右邊,如果isCenterHorizontal為true,則與目標水平居中對齊 .isRequestFocus(false) //默認為true,默認情況下彈窗會搶占焦點,目的是為了響應返回按鍵按下事件;如果為false,則不搶焦點 .enableDrag(true) //是否啟用拖拽,默認為true,目前對Bottom和Drawer彈窗有用 .isDarkTheme(true) //是否啟用暗色主題 .borderRadius(10) //為彈窗設置圓角,默認是15,對內置彈窗生效 .autoDismiss(false) //操作完畢后是否自動關閉彈窗,默認為true;比如點擊ConfirmPopup的確認按鈕,默認自動關閉;如果為false,則不會關閉 .setPopupCallback(new SimpleCallback() { //設置顯示和隱藏的回調 @Override public void onCreated(BasePopupView basePopupView) { // 彈窗內部onCreate執行完調用 } @Override public void beforeShow(BasePopupView basePopupView) { // 每次show之前都會執行 } @Override public void onShow(BasePopupView basePopupView) { // 完全顯示的時候執行 } @Override public void onDismiss(BasePopupView basePopupView) { // 完全隱藏的時候執行 } // 如果你自己想攔截返回按鍵事件,則重寫這個方法,返回true即可 @Override public boolean onBackPressed(BasePopupView basePopupView) { new ToastDialog(getContext()).setText("我攔截的返回按鍵,按返回鍵XPopup不會關閉了").show(); return true; //默認返回false } //監聽彈窗拖拽,適用於能拖拽的彈窗 @Override public void onDrag(BasePopupView popupView, int value, float percent,boolean upOrLeft) { } }) .asXXX() //所有的設置項都要寫在asXXX()方法調用之前 

5. 下載鏈接

5.1 IDE下載鏈接

https://developer.harmonyos.com/cn/develop/deveco-studio#download

5.2 源碼鏈接

https://gitee.com/openharmony-tpc/XPopup

設置全局的動畫時長 默認情況下,彈窗的動畫時長為360毫秒。你可以通過下面的方法進行修改:

XPopup.setAnimationDuration(200); // 傳入的時長最小為0,動畫的時長會影響除Drawer彈窗外的所有彈窗

作者:朱偉ISRC
想了解更多內容,請訪問51CTO和華為合作共建的鴻蒙社區:https://harmonyos.51cto.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM