# ChromeLikeTabSwitcher **Repository Path**: chinasoft3_ohos/ChromeLikeTabSwitcher ## Basic Information - **Project Name**: ChromeLikeTabSwitcher - **Description**: ChromeLikeTabSwitcher是一个仿Chrome浏览器中Tab切换的库 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2021-05-20 - **Last Updated**: 2022-09-06 ## Categories & Tags **Categories**: harmonyos-menu **Tags**: None ## README ## ChromeLikeTabSwitcher #### 项目介绍 - 项目名称:ChromeLikeTabSwitcher - 所属系列:openharmony的第三方组件适配移植 - 功能:ChromeLikeTabSwitcher是一个仿Chrome浏览器中Tab切换的库,它提供的标签切换器类似于在Chrome浏览器中使用的标签切换器。 - 项目移植状态:主功能完成 - 调用差异:无 - 开发版本:sdk6,DevEco Studio2.2 beta1 - 基线版本:tag 0.4.6 #### 效果演示 - 主页上下滑动 ![](images/main.gif) - UNDO功能 ![](images/undo.gif) -添加新Tab页 ![](images/add_new_tab.gif) ![](images/add_new_tab2.gif) #### 安装教程 1.在项目根目录下的build.gradle文件中, ``` allprojects { repositories { maven { url 'https://s01.oss.sonatype.org/content/repositories/releases/' } } } ``` 2.在entry模块的build.gradle文件中, ``` dependencies { implementation('com.gitee.chinasoft_ohos:ChromeLikeTabSwitcher:1.0.1') implementation('com.gitee.chinasoft_ohos:ChromeLikeTabSwitcher_ohos_util:1.0.0') implementation('com.gitee.chinasoft_ohos:ChromeLikeTabSwitcher_java_util:1.0.0') implementation('com.gitee.chinasoft_ohos:ChromeLikeTabSwitcher_materialviewlibrary:1.0.0') ...... } ``` 在sdk6,DevEco Studio2.2 beta1下项目可直接运行 如无法运行,删除项目.gradle,.idea,build,gradle,build.gradle文件, 并依据自己的版本创建新项目,将新项目的对应文件复制到根目录下 #### 使用说明 库的选项卡切换器实现为自定义视图TabSwitcher。可以通过编程方式声明或通过XML资源将其添加到活动或片段中。以下XML代码显示了如何将视图添加到XML布局资源。通常应通过将属性设置为layout_width和layout_height将标签切换器全屏显示match_parent。此外,该视图提供了各种自定义属性以自定义其外观,这在给定的示例中也可以看到。 ```xml ``` 以TabSwitcher编程方式实例化时,可以使用以下Java代码。对于上面示例中显示的所有XML属性,都可以使用相应的setter方法。 ```java TabSwitcher tabSwitcher = new TabSwitcher(context); tabSwitcher.showAddTabButton(createAddTabButtonListener()); tabSwitcher.setToolbarNavigationIcon(ResourceTable.Media_ic_plus_white_24dp, createAddTabListener()); ``` 由a包含的选项卡TabSwitcher由class的实例表示Tab。以下Java代码演示了如何创建新标签并将其添加到标签切换器。通过设置自定义图标,背景颜色,标题颜色等,TabSwitcher可以覆盖特定标签的默认设置(应用于)。所述setParameters-method允许以一个标签相关联Bundle,其可包含关于标签的附加信息。通过实现接口Tab.Callback并Tab使用其addCallback-method在a处注册实例,可以在选项卡的属性已更改时观察到它。 ```java Tab tab = new Tab("Title"); PacMap parameters = new PacMap(); tab.setIcon(ResourceTable.Media_ic_file_outline_18dp); ... tab.setParameters(parameters); ``` 为了指定选项卡的`TabSwitcher` 外观,`TabSwitcherDecorator`必须重写抽象类,并且必须使用`setDecorator`方法将实现类的实例应用于选项卡切换器。这是非常常用的适配器,用于填充一个发展中的范式 `ListView`,`RecyclerView`等等之类的每一个自定义实现 `TabSwitcherDecorator`必须重写 `onInflateView`和`onShowTab`方法。第一个用于使视图膨胀,应由选项卡使用,后者允许根据当前状态自定义膨胀视图的外观。在 `onShowTab`的范围内,装饰器的`findComponentById`方法可用于引用视图。它使用内置的视图保持器以获得更好的性能。 如果对不同的选项卡使用不同的视图,则`getViewTypeCount`和`getViewType`方法也必须被覆盖。第一个返回的是`onInflateView` 方法放大的不同视图的总数,后一个必须返回一个不同的整数值,该值指定特定选项卡 的视图类型。以下代码说明了如何实现该类`TabSwitcherDecorator`。 ```java class Decorator extends TabSwitcherDecorator { @NonNull @Override public View onInflateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, int viewType) { Component view; if (viewType == 0) { LogUtil.loge("=== onInflateView 0"); view = inflater.parse(ResourceTable.Layout_tab_text_view, parent, false); } else if (viewType == 1) { LogUtil.loge("=== onInflateView 1"); view = inflater.parse(ResourceTable.Layout_tab_edit_text, parent, false); } else { LogUtil.loge("=== onInflateView 2"); view = inflater.parse(ResourceTable.Layout_tab_list_view, parent, false); } Toolbar toolbar = (Toolbar) view.findComponentById(ResourceTable.Id_toolbar); toolbar.inflateMenu(ResourceTable.Layout_tab); toolbar.setOnMenuItemClickListener(tabSwitcher, createToolbarMenuListener()); TabSwitcher.setupWithToolbar(tabSwitcher, toolbar, createTabSwitcherButtonListener()); return view; } @Override public void onShowTab(@NonNull Context context, @NonNull TabSwitcher tabSwitcher, @NonNull View view, @NonNull Tab tab, int index, int viewType, @Nullable Bundle savedInstanceState) { Text textView = findComponentById(ResourceTable.Id_title); textView.setText((String) tab.getTitle()); Toolbar toolbar = findComponentById(ResourceTable.Id_toolbar); toolbar.addMenuClickListener(); toolbar.setVisibility(tabSwitcher.isSwitcherShown() ? Component.HIDE : Component.VISIBLE); if (toolbar.getMenu() != null) { String theme_name = getThemeValue("theme_name"); if ("dark".equals(theme_name)) { toolbar.getMenu().setImageSrc(de.mrapp.tabswitcher.ResourceTable.Media_more_menu_white); toolbar.setButtonColor(1, Color.WHITE); } else { toolbar.getMenu().setImageSrc(de.mrapp.tabswitcher.ResourceTable.Media_more_menu); toolbar.setButtonColor(0.5f, Color.DKGRAY); } } ColorUiUtil.changeTheme(view, getContext().getTheme()); if (viewType == 1) { LogUtil.loge("=== onShowTab viewType -> 1"); TextField editText = findComponentById(ResourceTable.Id_edit); if (savedInstanceState == null) { editText.setText(""); } editText.requestFocus(); } else if (viewType == 2 && state != null) { LogUtil.loge("=== onShowTab viewType -> 2"); ListContainer listView = findComponentById(ResourceTable.Id_list); state.loadItems(listView); } } @Override public int getViewTypeCount() { return 3; } @Override public int getViewType(@NonNull Tab tab, int index) { Bundle parameters = tab.getParameters(); return parameters != null ? parameters.getInt("view_type") : 0; } @Override public void onSaveInstanceState(@NonNull View view, @NonNull Tab tab, int index, int viewType, @NonNull Bundle outState) { // Store the tab's current state in the Bundle outState if necessary } } ``` 为了将装饰器应用于`TabSwitcher`其 `setDecorator` 方法,必须如下所示使用。如果未设置装饰器,`IllegalStateException` 则在视图应可见时将立即引发。 ```java tabSwitcher.setDecorator(new Decorator()); ``` 为了观察`TabSwitcher`状态,可以实现`TabSwitcher`接口`TabSwitcherListener` 侦听器。该界面提供了以下方法:在将选项卡添加到选项卡切换器或从选项卡切换器中删除时, 或者如果选项卡切换器已隐藏或显示(仅在使用智能手机布局时),则将调用这些方法。`TabSwitcherListener` 可以使用`TabSwitcher`的`addListener`方法添加类型的实例。 为了观察,当单击选项卡的关闭按钮时,`TabCloseListener` 可以相应地使用`addTabCloseListener`方法来实现和添加接口。 ##### 使用动画 该类 `TabSwitcher` 提供了添加或删除一个或几个选项卡的各种方法。如果当前显示选项卡切换器,则以动画方式添加或删除选项卡。为了使用自定义动画,`Animation` 可以将类的实例传递给方法。 ##### 滑动动画 使用智能手机布局时,`SwipeAnimation` 默认情况下使用a来添加或删除选项卡。这会导致标签页水平滑动(或在横向模式下垂直滑动)。通过指定enum的值`SwipeAnimation.SwipeDirection`, 可以指定选项卡是否应该向左或向右移动(在横向模式下分别从顶部或底部移动)。以下代码示例说明了如何 `SwipeAnimation` 使用构建器来创建类的实例。 ```java Animation animation = new SwipeAnimation.Builder().setDuration(2000) .setInterpolator(new LinearInterpolator()).setDirection(SwipeAnimation.SwipeDirection.LEFT) .create(); ``` ![](images/swipe.gif) ##### 工具栏和菜单 `TabSwitcher`库提供的视图允许显示工具栏。默认情况下,工具栏始终处于隐藏状态。为了显示它, `showToolbars` 必须使用-method。使用智能手机布局时,当前显示选项卡切换器或选项卡切换器不 包含任何选项卡时,将显示工具栏。使用数位板布局时,始终显示两个工具栏-一个在选项卡的左侧 ,另一个在选项卡的右侧。可以使用 `getToolbars` 方法来引用工具栏。它返回一个数组, 其中包含布局的工具栏。 使用智能手机布局时,`Toolbar`数组仅包含一个;使用平板电脑布局时,左一个包含在索引0中, 右一个包含在索引1中。 该类 `TabSwitcher` 提供了一些方法,可用于设置工具栏的标题,导航图标和菜单。 本 `setToolbarTitle` 方法允许设置一个标题。使用数位板布局时,标题将应用到左侧工具栏。 所述 `setToolbarNavigationIcon` 方法允许指定工具栏的导航图标以及点击图标时被调用的监听器。 使用数位板布局时,导航图标将应用于左侧工具栏。为了将菜单添加到 `TabSwitcher` 的工具栏, `inflateToolbarMenu` 可以使用-method。除了应该应用的菜单资源的资源ID外,它还允许指定一个侦听器, 当单击菜单项时会通知该侦听器。 为了提供类似于Chrome浏览器中使用的按钮的按钮,该按钮显示了a包含的选项卡总数, `TabSwitcher` 并允许切换选项卡切换器的可见性,该类 `TabSwitcherButton` 由库公开。 它实现了一个自定义的 `ImageButton` ,它实现了接口 `TabSwitcherListener` ,以使显示的选项 卡数目保持最新。 该按钮的外观由类指定 `TabSwitcherDrawable` 。如果 `TabSwitcherButton` 应将用作工具栏菜单的一部分, 则必须将其包含在菜单资源中。具体使用如下所示 TabSwitcherDrawable使用方法: ``` private void initImage() { image = new Image(getContext()); Resource resource = convertRes(getContext(), ResourceTable.Media_tab_switcher_drawable_background); //The drawable, which is used by the image button. drawable = new TabSwitcherDrawable(getContext(), resource); image.setImageElement(drawable); StackLayout.LayoutConfig imageParams = new LayoutConfig(StackLayout.LayoutConfig.MATCH_PARENT, StackLayout.LayoutConfig.MATCH_PARENT); imageParams.alignment = TextAlignment.CENTER; addComponent(image, imageParams); } ``` TabSwitcherButton使用方法: ``` TabSwitcherButton actionView = new TabSwitcherButton(getContext()); DirectionalLayout.LayoutConfig actionConfig = new LayoutConfig(); int tabHeight = (int) PixelUtil.fp2px(DimensionUtil.parseDimension(getContext().getString(ResourceTable.String_tablet_tab_height))); actionConfig.height = tabHeight; actionConfig.width = tabHeight; addComponent(actionView, actionConfig); ``` 为了将菜单注册 `TabSwitcherButton` 为的侦听器,可以使用`TabSwitcher`静态 `setupWithMenu`方法。它会自动为所有的items 注入一个`Menu`菜单,将使用的所有项注册 `TabSwitcherButton` 为 特定的侦听器 `TabSwitcher` 。在 `OnClickListener` 当被点击这些按钮中的一个,其可任选被指定, 被调用。以下代码显示了如何将该方法与任意菜单一起使用。 ```java TabSwitcher.setupWithMenu(tabSwitcher, menu, new OnClickListener() { /* ... */ }); ``` If the menu, which is part of the tab switcher itself, should be set up, the following method call can be used. ```java TabSwitcher.setupWithMenu(tabSwitcher, new OnClickListener() { /* ... */ }); ``` ##### 使用主题 默认情况下,图书馆的会使用浅色主题TabSwitcher。但是,该库`TabSwitcher`除主题外还带有预定义的深色主题。 下面的屏幕快照显示了深色主题的外观 ![](images/theme.gif) ##### 当TabSwitcher为空时显示一个占位符 该类 `TabSwitcher` 提供`setEmptyView` 方法,可用于指定选项卡切换器为空时显示的自定义视图。 通过使用淡入或淡出动画来显示或隐藏指定的视图。可以选择指定这些动画的持续时间。使用 `setEmptyView` 方法 的第一种可能性是指定该类的实例`ohos.agp.components.Component`: ```java Component component = // ... inflate component tabSwitcher.setEmptyView(component); ``` 或者,`setEmpyView`可以通过指定视图的布局资源ID来使用: ```java tabSwitcher.setEmptyView(ResourceTable.Id_empty_view); ``` 当标签切换器为空时,显示的占位符视图的示例可以在下面的屏幕快照中看到。 ![](images/empty.png) ##### 填充 该视图将 `TabSwitcher` 覆盖 `setPadding` 该类的方法 `View` ,以便将填充应用于所有选项卡及其父视图。此行为的主要目的是在使用半透明状态和/或导航栏时应用窗口插图, 如在库的示例应用程序中可以看到的那样。以下代码示例演示如何通过使用活动将窗口插入到选项卡切换器`OnApplyWindowInsetsListener` 。它应在活动的 `onCreate` 方法中使用。 ```java tabSwitcher.getComponentTreeObserver().addWindowBoundListener( new ComponentTreeObserver.WindowBoundListener() { @Override public void onWindowBound() { int left = main_root.getLeft(); int top = main_root.getTop(); int right = main_root.getRight(); int bottom = main_root.getBottom(); float touchableAreaTop = top; if (tabSwitcher.getLayout() == Layout.TABLET) { // touchableAreaTop += getResources().getDimensionPixelSize(R.dimen.tablet_tab_container_height); } LogUtil.loge("===" + left + ":" + top + ":" + right + ":" + bottom); int actionBarSize = (int) PixelUtil.fp2px(DimensionUtil.parseDimension(tabSwitcher.getContext().getString(ResourceTable.String_actionBarSize))); LogUtil.loge("===" + actionBarSize); RectFloat touchableArea = new RectFloat(left, touchableAreaTop, right, touchableAreaTop + actionBarSize); tabSwitcher.addDragGesture( new SwipeGesture.Builder().setTouchableArea(touchableArea).create()); tabSwitcher.addDragGesture( new PullDownGesture.Builder().setTouchableArea(touchableArea).create()); } @Override public void onWindowUnbound() { } } ); ``` #### 测试信息 CodeCheck代码测试无异常 CloudTest代码测试无异常 病毒安全检测通过 当前版本demo功能与原组件基本无差异 #### 版本迭代 - 1.0.2 - 1.0.1 - 1.0.0 - 0.0.1-SNAPSHOT