国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

紹興做網(wǎng)站優(yōu)化大師電腦版

紹興做網(wǎng)站,優(yōu)化大師電腦版,免費(fèi)設(shè)計(jì)app的網(wǎng)站建設(shè),做外貿(mào)網(wǎng)站注冊什么郵箱看了我上篇文章Android低代碼開發(fā) - 像啟蒙和樂高玩具一樣的MenuPanel 之后,本篇開始講解代碼。 源代碼剖析 首先從MenuPanelItemRoot講起。 package dora.widget.panelinterface MenuPanelItemRoot {/*** 菜單的標(biāo)題。** return*/var title: String?fun hasTit…

看了我上篇文章Android低代碼開發(fā) - 像啟蒙和樂高玩具一樣的MenuPanel 之后,本篇開始講解代碼。

源代碼剖析

首先從MenuPanelItemRoot講起。

package dora.widget.panelinterface MenuPanelItemRoot {/*** 菜單的標(biāo)題。** @return*/var title: String?fun hasTitle(): Boolean/*** 獲取標(biāo)題四周的間距。** @return*/fun getTitleSpan(): Spanfun setTitleSpan(titleSpan: Span)/*** 菜單的上邊距。** @return*/var marginTop: Intclass Span {var left = 0var top = 0var right = 0var bottom = 0constructor()/*** 根據(jù)水平間距和垂直間距設(shè)置四周的間距,常用。** @param horizontal* @param vertical*/constructor(horizontal: Int, vertical: Int) : this(horizontal,vertical,horizontal,vertical)constructor(left: Int, top: Int, right: Int, bottom: Int) {this.left = leftthis.top = topthis.right = rightthis.bottom = bottom}}
}

無論是菜單還是菜單組,都要實(shí)現(xiàn)這個接口,這是什么模式啊?對,這是組合模式的應(yīng)用。樹枝節(jié)點(diǎn)可以添加若干樹葉節(jié)點(diǎn),且它們不會直接產(chǎn)生依賴,而是同時依賴其抽象。這個類里面看到,有title、title span和margin top,它們分別代表什么呢?
截屏2024-05-21 17.png
title就是紅圈圈出來的地方。title span就是標(biāo)題的間隙,你直接當(dāng)成margins比較容易理解。

截屏2024-05-21 17.png
紅框標(biāo)出來的為margin top。如果有title的情況下,即title不為空以及空字符串,hasTitle()方法會檢測出有標(biāo)題。marginTop是指標(biāo)題上面的區(qū)域。

接下來來看MenuPanel。

package dora.widget.panelimport android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import java.util.LinkedList
import java.util.UUID/*** 通用功能菜單,類似于RecyclerView。*/
open class MenuPanel : ScrollView, View.OnClickListener {/*** 面板的背景顏色,一般為淺灰色。*/private var panelBgColor = DEFAULT_PANEL_BG_COLORprotected var menuPanelItems: MutableList<MenuPanelItem> = ArrayList()protected var viewsCache: MutableList<View> = ArrayList()private var onPanelMenuClickListener: OnPanelMenuClickListener? = nullprivate var onPanelScrollListener: OnPanelScrollListener? = nullprivate val groupInfoList: MutableList<GroupInfo> = ArrayList()private val listenerInfo = LinkedList<ListenerDelegate>()lateinit var panelRoot: FrameLayout/*** 存放Menu和Custom View。*/lateinit var container: LinearLayoutconstructor(context: Context) : super(context) {init(context)}constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {init(context)}constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr) {init(context)}fun removeItem(item: MenuPanelItem): MenuPanel {val position = seekForItemPosition(item)if (position != SEEK_FOR_ITEM_ERROR_NOT_FOUND &&position != SEEK_FOR_ITEM_ERROR_MISS_MENU_NAME) {removeItem(position)} else {Log.e(TAG, "failed to seekForItemPosition,$position")}return this}private fun init(context: Context) {isFillViewport = trueaddContainer(context)}fun setOnPanelMenuClickListener(l: OnPanelMenuClickListener) {onPanelMenuClickListener = l}fun setOnPanelScrollListener(l: OnPanelScrollListener?) {onPanelScrollListener = l}@JvmOverloadsfun parseItemView(item: MenuPanelItem?, isLoadData: Boolean = false): View {val menuView = item!!.inflateView(context)if (isLoadData) {item.initData(menuView)}return menuView}val items: List<MenuPanelItem>get() = menuPanelItemsfun getItem(position: Int): MenuPanelItem? {if (position < 0 || position > menuPanelItems.size - 1) {return null}return menuPanelItems[position]}val itemViewsCache: List<View>get() = viewsCachefun getGroupInfo(item: MenuPanelItem): GroupInfo? {for (groupInfo in groupInfoList) {if (groupInfo.hasItem(item)) {return groupInfo}}return null}/*** 根據(jù)item的position移除一個item,此方法被多處引用,修改前需要理清布局層級結(jié)構(gòu)。** @param position* @return*/fun removeItem(position: Int): MenuPanel {val item = menuPanelItems[position]val groupInfo = getGroupInfo(item)val belongToGroup = groupInfo != nullval view = getCacheViewFromPosition(position)if (!belongToGroup) {container.removeView(view)} else {// 屬于一個組val menuGroupCard = groupInfo!!.groupMenuCardmenuGroupCard.removeView(view)groupInfo.removeItem(item)// 一個組內(nèi)的item全部被移除后,也移除掉這個組if (groupInfo.isEmpty) {// 連同title一起移除container.removeView(menuGroupCard)groupInfoList.remove(groupInfo)}}menuPanelItems.removeAt(position)viewsCache.removeAt(position)listenerInfo.removeAt(position)return this}/*** 清空所有item和相關(guān)view。*/fun clearAll(): MenuPanel {if (menuPanelItems.size > 0) {menuPanelItems.clear()}container.removeAllViews()viewsCache.clear()groupInfoList.clear()listenerInfo.clear()return this}/*** 移除連續(xù)的item。** @param start 第一個item的下標(biāo),包括* @param end   最后一個item的下標(biāo),包括* @return*/fun removeItemRange(start: Int, end: Int): MenuPanel {for (i in start until end + 1) {removeItem(start)}return this}/*** 從某個位置移除到最后一個item。** @param start 第一個item的下標(biāo),包括* @return*/fun removeItemFrom(start: Int): MenuPanel {val end = menuPanelItems.size - 1if (start <= end) {// 有就移除removeItemRange(start, end)}return this}/*** 從第一個item移除到某個位置。** @param end 最后一個item的下標(biāo),包括* @return*/fun removeItemTo(end: Int): MenuPanel {val start = 0removeItemRange(start, end)return this}val itemCount: Intget() = menuPanelItems.sizefun addMenuGroup(itemGroup: MenuPanelItemGroup): MenuPanel {val hasTitle = itemGroup.hasTitle()val items = itemGroup.itemsval titleView = TextView(context)titleView.setPadding(itemGroup.getTitleSpan().left, itemGroup.getTitleSpan().top,itemGroup.getTitleSpan().right, itemGroup.getTitleSpan().bottom)titleView.text = itemGroup.titletitleView.textSize = 15ftitleView.setTextColor(DEFAULT_TITLE_COLOR)val menuGroupCard = LinearLayout(context)menuGroupCard.orientation = LinearLayout.VERTICALval lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT)lp.topMargin = itemGroup.marginTopmenuGroupCard.layoutParams = lpif (hasTitle) {menuGroupCard.addView(titleView)}for (item in items) {// 清除組內(nèi)item的邊距等applyDefault(item)addMenuToCard(item, menuGroupCard)}container.addView(menuGroupCard)// 保存菜單組信息groupInfoList.add(GroupInfo(items, menuGroupCard))return this}override fun addView(child: View) {if (child !is FrameLayout) {return}if (childCount > 1) {return}super.addView(child)}private fun addContainer(context: Context) {panelRoot = FrameLayout(context)container = LinearLayout(context)container.orientation = LinearLayout.VERTICALcontainer.setBackgroundColor(panelBgColor)panelRoot.addView(container)addView(panelRoot)}fun addMenu(item: MenuPanelItem): MenuPanel {val menuView = bindItemListener(item)if (!item.hasTitle()) {container.addView(menuView)} else {val titleView = TextView(context)titleView.setPadding(item.getTitleSpan().left, item.getTitleSpan().top,item.getTitleSpan().right, item.getTitleSpan().bottom)titleView.text = item.titletitleView.textSize = 15ftitleView.setTextColor(DEFAULT_PANEL_BG_COLOR)val menuCard = LinearLayout(context)menuCard.orientation = LinearLayout.VERTICALmenuCard.addView(titleView)menuCard.addView(menuView)container.addView(menuCard)}return this}private fun addMenuToCard(item: MenuPanelItem, container: LinearLayout) {val menuView = bindItemListener(item)val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT)lp.topMargin = item.marginTopmenuView.layoutParams = lpcontainer.addView(menuView)}fun seekForItemPosition(item: MenuPanelItem): Int {for (i in menuPanelItems.indices) {val mpi = menuPanelItems[i]val menu = mpi.menuNameif (menu == "" || item.menuName == "") {return SEEK_FOR_ITEM_ERROR_MISS_MENU_NAME //失去菜單名稱}if (menu == item.menuName) {return i}}return SEEK_FOR_ITEM_ERROR_NOT_FOUND}/*** 獲取MenuPanel中條目布局中的子控件,推薦使用。** @param position* @param viewId* @return*/fun getCacheChildView(position: Int, viewId: Int): View? {val menuView = getCacheViewFromPosition(position)return menuView?.findViewById(viewId)}/*** 獲取item的view,用于修改item的數(shù)據(jù)。** @param item* @return*/fun getCacheViewFromItem(item: MenuPanelItem): View? {val position = seekForItemPosition(item)return if (position != SEEK_FOR_ITEM_ERROR_NOT_FOUND &&position != SEEK_FOR_ITEM_ERROR_MISS_MENU_NAME) {getCacheViewFromPosition(position)} else null}/*** 獲取item的view,用于修改item的數(shù)據(jù)。** @param position item的位置,從0開始* @return*/fun getCacheViewFromPosition(position: Int): View? {return if (position < viewsCache.size) {viewsCache[position]} else null}protected fun getCacheViewFromTag(tag: String): View? {for (delegate in listenerInfo) {val dtag = delegate.tagif (dtag == tag) {val position = delegate.positionreturn getCacheViewFromPosition(position)}}return null}/*** 綁定item的點(diǎn)擊事件。** @param item* @return 綁定成功后返回item的view*/private fun bindItemListener(item: MenuPanelItem): View {menuPanelItems.add(item)//解析Item所對應(yīng)的布局,并調(diào)用item的initDataval menuView = parseItemView(item, true)viewsCache.add(menuView)val tag = UUID.randomUUID().toString().substring(0, 16)menuView.tag = tagval delegate = getListenerInfo(tag)menuView.setOnClickListener(delegate)listenerInfo.add(delegate)return menuView}private fun applyDefault(item: MenuPanelItem) {// item的上邊距修改為1dpitem.marginTop =TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f,resources.displayMetrics).toInt()// item去掉標(biāo)題item.title = ""// item去掉標(biāo)題邊距item.setTitleSpan(MenuPanelItemRoot.Span())}/*** 不是菜單,所以不會影響菜單的點(diǎn)擊事件位置,但需要自己處理控件內(nèi)部的點(diǎn)擊事件。** @param view* @param <T>*/fun <T : View> addCustomView(view: T): MenuPanel {container.addView(view)return this}fun <T : View> addCustomView(view: T, index: Int): MenuPanel {container.addView(view, index)return this}fun removeCustomViewAt(position: Int): MenuPanel {if (container.childCount > position) {// 有就移除container.removeViewAt(position)}return this}/*** 樣式等參數(shù)改變才需要更新,只有類似于addItem、removeItem這樣的,不需要調(diào)用此方法。*/open fun updatePanel() {requestLayout()}fun getListenerInfo(tag: String): ListenerDelegate {return ListenerDelegate(tag, menuPanelItems.size - 1, this)}class GroupInfo(private var items: MutableList<MenuPanelItem>,var groupMenuCard: LinearLayout) {fun hasItem(item: MenuPanelItem): Boolean {return items.contains(item)}val itemCount: Intget() = items.sizefun addItem(item: MenuPanelItem) {items.add(item)}fun removeItem(item: MenuPanelItem?) {items.remove(item)}val isEmpty: Booleanget() = items.size == 0fun getItems(): MutableList<MenuPanelItem> {return items}}override fun onClick(v: View) {val tag = v.tag as Stringfor (delegate in listenerInfo) {if (delegate.tag == tag) {val clickPos = delegate.positionmenuPanelItems[clickPos].menuName?.let {onPanelMenuClickListener?.onMenuClick(clickPos, v, it)}break}}}fun setPanelBgColor(color: Int): MenuPanel {panelBgColor = colorcontainer.setBackgroundColor(panelBgColor)return this}interface OnPanelMenuClickListener {fun onMenuClick(position: Int, view: View, menuName: String)}override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {super.onScrollChanged(l, t, oldl, oldt)if (scrollY == 0) {onPanelScrollListener?.onScrollToTop()} else if (panelRoot.measuredHeight == scrollY + height) {onPanelScrollListener?.onScrollToBottom()}}interface OnPanelScrollListener {fun onScrollToTop()fun onScrollToBottom()}class ListenerDelegate(val tag: String,val position: Int,private val listener: OnClickListener) : OnClickListener {override fun onClick(v: View) {listener.onClick(v)}}companion object {private const val TAG = "MenuPanel"private const val DEFAULT_PANEL_BG_COLOR = -0xa0a07private const val DEFAULT_TITLE_COLOR = -0x666667private const val SEEK_FOR_ITEM_ERROR_NOT_FOUND = -1private const val SEEK_FOR_ITEM_ERROR_MISS_MENU_NAME = -2}
}

由于它仿RecyclerView的布局,它可以實(shí)現(xiàn)少量固定數(shù)量的item的高效創(chuàng)建,但不適應(yīng)于大量item的場景。本來這個控件設(shè)計(jì)之初就是用在菜單上面的,而業(yè)務(wù)功能不可能無限多,所以這個問題可以忽略。根據(jù)代碼我們可以得知,它是一個ScrollView,通常我們寬高都設(shè)置成match_parent,上面放一個titlebar,這樣就填滿了整個內(nèi)容視圖。這里面有addMenu()、addMenuGroup()和addCustomView()三種添加子控件的方法,只有前兩種會受框架的約束。也就是說,如果你調(diào)用addCustomView()添加非菜單的視圖,那么不會有OnPanelMenuClickListener面板菜單點(diǎn)擊事件的回調(diào),需要自己處理自身的事件。通過getCacheChildView()getCacheViewFromPosition()這兩個方法都是用來更新菜單數(shù)據(jù)的,它們的區(qū)別在于前者是拿item的具體某一個子控件,后者是拿item本身。刪除菜單和回調(diào)菜單的點(diǎn)擊事件會使用到menuName這個屬性,所以你在addMenu()的時候,務(wù)必保證menuName不重復(fù)。無論是添加還是移除菜單,最后都需要調(diào)用updatePanel()進(jìn)行刷新。

package dora.widget.panelimport android.content.Context
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView/*** 自動給最后加一行提示信息,如共有幾條記錄的菜單面板。*/
class TipsMenuPanel : MenuPanel {private var tips: String? = ""private var tipsColor = -0x666667private var tipsView: TextView? = nullconstructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr)fun setEmptyTips(): TipsMenuPanel {setTips("")return this}fun setTips(tips: String?): TipsMenuPanel {this.tips = tipsreturn this}fun setTipsColor(color: Int): TipsMenuPanel {tipsColor = colorreturn this}override fun updatePanel() {if (tipsView != null) {container.removeView(tipsView)}if (tips != null && tips!!.isNotEmpty()) {tipsView = TextView(context)val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT)lp.topMargin = dp2px(context, 5f)lp.bottomMargin = dp2px(context, 5f)tipsView!!.gravity = Gravity.CENTER_HORIZONTALtipsView!!.setTextColor(tipsColor)tipsView!!.layoutParams = lptipsView!!.text = tips// 增加了底部的tipscontainer.addView(tipsView)}super.updatePanel()}private fun dp2px(context: Context, dpVal: Float): Int {return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal, context.resources.displayMetrics).toInt()}
}

另外更新其子類TipsMenuPanel的底部提示信息的布局也需要調(diào)用updatePanel()方法。

開始使用

先給你們看一下dora-studio-plugin中是如何生成代碼的,你就大概知道怎么使用了。

/** Copyright (C) 2022 The Dora Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.dorachat.templates.recipes.app_package.res.layoutfun menuPanelActivityXml(packageName: String,activityClass: String
) = """
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"tools:context="${packageName}.${activityClass}"><data></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><dora.widget.DoraTitleBarandroid:id="@+id/titleBar"android:layout_width="match_parent"android:layout_height="50dp"app:dview_title="@string/app_name"android:background="@color/colorPrimary"/><dora.widget.panel.MenuPanelandroid:id="@+id/menuPanel"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>
</layout>
"""

以上為生成xml布局。

/** Copyright (C) 2022 The Dora Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.dorachat.templates.recipes.app_package.srcfun menuPanelActivityKt(applicationPackage: String,packageName: String,activityClass: String,bindingName: String,layoutName: String
) = """
package ${packageName}import android.os.Bundleimport dora.BaseActivityimport ${applicationPackage}.R
import ${applicationPackage}.databinding.${bindingName}class ${activityClass} : BaseActivity<${bindingName}>() {override fun getLayoutId(): Int {return R.layout.${layoutName}}override fun initData(savedInstanceState: Bundle?, binding: ${bindingName}) {TODO("Not yet implemented")}
}
"""fun menuPanelActivity(applicationPackage: String,packageName: String,activityClass: String,bindingName: String,layoutName: String
) = """
package ${packageName};import android.os.Bundle;
import androidx.annotation.Nullable;import dora.BaseActivity;import ${applicationPackage}.R;
import ${applicationPackage}.databinding.${bindingName};public class ${activityClass} extends BaseActivity<${bindingName}> {@Overrideprotected int getLayoutId() {return R.layout.${layoutName};}@Overridepublic void initData(@Nullable Bundle savedInstanceState, ${bindingName} binding) {// TODO: Not yet implemented// For Example:// binding.menuPanel.addMenuGroup(//     MenuPanelItemGroup(//         DensityUtils.dp2px(10f),//         NormalMenuPanelItem("menuName", "text", true, "arrowText")//     )// )}
}
"""

以上為生成activity。

Gradle依賴配置
// 添加以下代碼到項(xiàng)目根目錄下的build.gradle
allprojects {repositories {maven { url "https://jitpack.io" }}
}
// 添加以下代碼到app模塊的build.gradle
dependencies {implementation 'com.github.dora4:dview-menu-panel:1.0'
}
添加菜單和菜單組
添加菜單
binding.menuPanel.addMenu(NormalMenuPanelItem("menuName", "text", true, "arrowText"))
添加菜單組
binding.menuPanel.addMenuGroup(MenuPanelItemGroup(DensityUtils.dp2px(10f),NormalMenuPanelItem("menuName", "text", true, "arrowText")))

不要無腦copy,參數(shù)請自行更換。

修改菜單數(shù)據(jù)

例如:更新顏色選擇菜單的標(biāo)簽顏色。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (resultCode == Activity.RESULT_OK) {if (requestCode == 0) {data?.let {val tagColor = it.getStringExtra(KEY_PICKED_COLOR)tagColor?.let {groupTitleColor = tagColor}val tvTag = mBinding.menuPanel.getCacheChildView(1, R.id.tv_menu_panel_color_picker_tag)val color = Color.parseColor(tagColor)val drawable = TagDrawable(color, 0, 0,DensityUtils.dp2px(this, 20f),DensityUtils.dp2px(this, 10f),)if (tvTag != null) {tvTag.background = drawable}}}}
}
設(shè)置菜單點(diǎn)擊事件
binding.menuPanel.setOnPanelMenuClickListener(object : MenuPanel.OnPanelMenuClickListener {override fun onMenuClick(position: Int, view: View, menuName: String) {when (menuName) {"newMsgNotice" -> {// 新消息通知spmSelectContent("點(diǎn)擊新消息通知")val intent = Intent(this@SettingsActivity, NewMsgNoticeActivity::class.java)startActivity(intent)}"switchLanguage" -> {// 切換語言spmSelectContent("點(diǎn)擊切換語言")val intent = Intent(this@SettingsActivity, SetLanguageActivity::class.java)startActivity(intent)}"chatFont" -> {IntentUtils.startActivityWithString(this@SettingsActivity,ChatFontActivity::class.java,KEY_USER_ID,userId)}"chatBg" -> {spmSelectContent("點(diǎn)擊聊天背景")IntentUtils.startActivityWithString(this@SettingsActivity,ChatBackgroundActivity::class.java,KEY_USER_ID,userId)}"cacheClear" -> {spmSelectContent("點(diǎn)擊緩存清理")IntentUtils.startActivityWithString(this@SettingsActivity,CacheCleanActivity::class.java,KEY_USER_ID,userId)}"aboutUs" -> {spmSelectContent("點(diǎn)擊關(guān)于我們")IntentUtils.startActivityWithString(this@SettingsActivity,AboutActivity::class.java,KEY_USER_ID,userId)}"superUser" -> {spmSelectContent("點(diǎn)擊超級管理員")IntentUtils.startActivityWithString(this@SettingsActivity,SuperUserActivity::class.java,KEY_USER_ID,userId)}"logout" -> {spmSelectContent("點(diǎn)擊注銷登錄")// 注銷登錄dialog!!.show("logout",getString(R.string.are_you_sure_logout))}}}
})

這里注意一點(diǎn),盡量使用menuName去判斷具體是哪一個菜單,而不建議使用position。因?yàn)樵谟袆h除菜單的情況下,position會錯位。spm埋點(diǎn)統(tǒng)計(jì)的代碼你無需關(guān)心。

總結(jié)

本篇詳細(xì)講解了MenuPanel的核心代碼實(shí)現(xiàn)及其使用方式,下篇我們演示IDE插件的操作流程。最后不忘點(diǎn)個star支持一下,https://github.com/dora4/dview-menu-panel 。

http://aloenet.com.cn/news/45781.html

相關(guān)文章:

  • 免費(fèi)商品列表網(wǎng)頁模板源代碼seo快速排名上首頁
  • 定制制作網(wǎng)站哪家好惠州seo網(wǎng)站推廣
  • 服裝官網(wǎng)網(wǎng)站建設(shè)百度推廣登陸后臺
  • 幫你省網(wǎng)站怎么做怎樣做網(wǎng)絡(luò)銷售平臺
  • 靜態(tài)網(wǎng)頁和動態(tài)網(wǎng)頁的區(qū)別是什么魔貝課凡seo課程好嗎
  • 網(wǎng)站服務(wù)器打不開大數(shù)據(jù)培訓(xùn)班需要多少錢
  • 邢臺市做網(wǎng)站seo工具不包括
  • 昔陽網(wǎng)站建設(shè)注冊域名在哪里注冊
  • php做門戶網(wǎng)站2024年最新時事新聞
  • 電影頻道做的網(wǎng)站廣告谷歌手機(jī)版瀏覽器官網(wǎng)
  • 小企業(yè)網(wǎng)絡(luò)營銷外包南寧seo產(chǎn)品優(yōu)化服務(wù)
  • wordpress建站好嗎武漢seo搜索優(yōu)化
  • 網(wǎng)絡(luò)營銷常用的方法seo 優(yōu)化技術(shù)難度大嗎
  • 專業(yè)做網(wǎng)站公司排名人工智能培訓(xùn)機(jī)構(gòu)哪個好
  • 做壁畫的網(wǎng)站湖南網(wǎng)站優(yōu)化
  • 如何在搜索中找到自己做的網(wǎng)站設(shè)計(jì)模板網(wǎng)站
  • msn網(wǎng)站制作windows優(yōu)化大師怎么用
  • 做商業(yè)廣告有什么網(wǎng)站好推銷的北大青鳥職業(yè)技術(shù)學(xué)院簡介
  • 女人能做網(wǎng)站開發(fā)嗎世界杯比分
  • 做網(wǎng)站可以申請專利嗎優(yōu)化防疫措施
  • 非響應(yīng)式網(wǎng)站改響應(yīng)式百度推廣賬號
  • 淘寶了做網(wǎng)站賣什么好百度怎么優(yōu)化網(wǎng)站關(guān)鍵詞
  • 網(wǎng)站開發(fā)實(shí)施計(jì)劃與安排網(wǎng)絡(luò)推廣培訓(xùn)
  • 定制網(wǎng)站前準(zhǔn)備手機(jī)版怎么用百度快照
  • 廣東企業(yè)網(wǎng)站模板推薦長春網(wǎng)站優(yōu)化團(tuán)隊(duì)
  • 保定專業(yè)做網(wǎng)站seo網(wǎng)絡(luò)貿(mào)易網(wǎng)站推廣
  • 包頭教育平臺網(wǎng)站建設(shè)seo網(wǎng)站推廣免費(fèi)
  • 網(wǎng)站建設(shè)工具有哪些寧波seo整體優(yōu)化公司
  • 公安網(wǎng)站建設(shè)自查報告我要學(xué)電腦哪里有短期培訓(xùn)班
  • 企業(yè)推廣網(wǎng)站百度免費(fèi)推廣平臺