Android低代码开发 - MenuPanel的源码剖析和基本使用

看了我上篇文章Android低代码开发 - 像启蒙和乐高玩具一样的MenuPanel 之后,本篇开始讲解代码。

源代码剖析

首先从MenuPanelItemRoot讲起。

kt 复制代码
package dora.widget.panel

interface MenuPanelItemRoot {

    /**
     * 菜单的标题。
     *
     * @return
     */
    var title: String?

    fun hasTitle(): Boolean

    /**
     * 获取标题四周的间距。
     *
     * @return
     */
    fun getTitleSpan(): Span

    fun setTitleSpan(titleSpan: Span)

    /**
     * 菜单的上边距。
     *
     * @return
     */
    var marginTop: Int

    class Span {
        var left = 0
        var top = 0
        var right = 0
        var bottom = 0

        constructor()

        /**
         * 根据水平间距和垂直间距设置四周的间距,常用。
         *
         * @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 = left
            this.top = top
            this.right = right
            this.bottom = bottom
        }
    }
}

无论是菜单还是菜单组,都要实现这个接口,这是什么模式啊?对,这是组合模式的应用。树枝节点可以添加若干树叶节点,且它们不会直接产生依赖,而是同时依赖其抽象。这个类里面看到,有title、title span和margin top,它们分别代表什么呢?

title就是红圈圈出来的地方。title span就是标题的间隙,你直接当成margins比较容易理解。

红框标出来的为margin top。如果有title的情况下,即title不为空以及空字符串,hasTitle()方法会检测出有标题。marginTop是指标题上面的区域。

接下来来看MenuPanel。

kt 复制代码
package dora.widget.panel

import 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_COLOR
    protected var menuPanelItems: MutableList<MenuPanelItem> = ArrayList()
    protected var viewsCache: MutableList<View> = ArrayList()
    private var onPanelMenuClickListener: OnPanelMenuClickListener? = null
    private var onPanelScrollListener: OnPanelScrollListener? = null
    private val groupInfoList: MutableList<GroupInfo> = ArrayList()
    private val listenerInfo = LinkedList<ListenerDelegate>()

    lateinit var panelRoot: FrameLayout

    /**
     * 存放Menu和Custom View。
     */
    lateinit var container: LinearLayout

    constructor(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 = true
        addContainer(context)
    }

    fun setOnPanelMenuClickListener(l: OnPanelMenuClickListener) {
        onPanelMenuClickListener = l
    }

    fun setOnPanelScrollListener(l: OnPanelScrollListener?) {
        onPanelScrollListener = l
    }

    @JvmOverloads
    fun parseItemView(item: MenuPanelItem?, isLoadData: Boolean = false): View {
        val menuView = item!!.inflateView(context)
        if (isLoadData) {
            item.initData(menuView)
        }
        return menuView
    }

    val items: List<MenuPanelItem>
        get() = menuPanelItems

    fun getItem(position: Int): MenuPanelItem? {
        if (position < 0 || position > menuPanelItems.size - 1) {
            return null
        }
        return menuPanelItems[position]
    }

    val itemViewsCache: List<View>
        get() = viewsCache

    fun getGroupInfo(item: MenuPanelItem): GroupInfo? {
        for (groupInfo in groupInfoList) {
            if (groupInfo.hasItem(item)) {
                return groupInfo
            }
        }
        return null
    }

    /**
     * 根据item的position移除一个item,此方法被多处引用,修改前需要理清布局层级结构。
     *
     * @param position
     * @return
     */
    fun removeItem(position: Int): MenuPanel {
        val item = menuPanelItems[position]
        val groupInfo = getGroupInfo(item)
        val belongToGroup = groupInfo != null
        val view = getCacheViewFromPosition(position)
        if (!belongToGroup) {
            container.removeView(view)
        } else {
            // 属于一个组
            val menuGroupCard = groupInfo!!.groupMenuCard
            menuGroupCard.removeView(view)
            groupInfo.removeItem(item)
            // 一个组内的item全部被移除后,也移除掉这个组
            if (groupInfo.isEmpty) {
                // 连同title一起移除
                container.removeView(menuGroupCard)
                groupInfoList.remove(groupInfo)
            }
        }
        menuPanelItems.removeAt(position)
        viewsCache.removeAt(position)
        listenerInfo.removeAt(position)
        return this
    }

    /**
     * 清空所有item和相关view。
     */
    fun clearAll(): MenuPanel {
        if (menuPanelItems.size > 0) {
            menuPanelItems.clear()
        }
        container.removeAllViews()
        viewsCache.clear()
        groupInfoList.clear()
        listenerInfo.clear()
        return this
    }

    /**
     * 移除连续的item。
     *
     * @param start 第一个item的下标,包括
     * @param end   最后一个item的下标,包括
     * @return
     */
    fun removeItemRange(start: Int, end: Int): MenuPanel {
        for (i in start until end + 1) {
            removeItem(start)
        }
        return this
    }

    /**
     * 从某个位置移除到最后一个item。
     *
     * @param start 第一个item的下标,包括
     * @return
     */
    fun removeItemFrom(start: Int): MenuPanel {
        val end = menuPanelItems.size - 1
        if (start <= end) {
            // 有就移除
            removeItemRange(start, end)
        }
        return this
    }

    /**
     * 从第一个item移除到某个位置。
     *
     * @param end 最后一个item的下标,包括
     * @return
     */
    fun removeItemTo(end: Int): MenuPanel {
        val start = 0
        removeItemRange(start, end)
        return this
    }

    val itemCount: Int
        get() = menuPanelItems.size

    fun addMenuGroup(itemGroup: MenuPanelItemGroup): MenuPanel {
        val hasTitle = itemGroup.hasTitle()
        val items = itemGroup.items
        val titleView = TextView(context)
        titleView.setPadding(
            itemGroup.getTitleSpan().left, itemGroup.getTitleSpan().top,
            itemGroup.getTitleSpan().right, itemGroup.getTitleSpan().bottom
        )
        titleView.text = itemGroup.title
        titleView.textSize = 15f
        titleView.setTextColor(DEFAULT_TITLE_COLOR)
        val menuGroupCard = LinearLayout(context)
        menuGroupCard.orientation = LinearLayout.VERTICAL
        val lp = LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        )
        lp.topMargin = itemGroup.marginTop
        menuGroupCard.layoutParams = lp
        if (hasTitle) {
            menuGroupCard.addView(titleView)
        }
        for (item in items) {
            // 清除组内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.VERTICAL
        container.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.title
            titleView.textSize = 15f
            titleView.setTextColor(DEFAULT_PANEL_BG_COLOR)
            val menuCard = LinearLayout(context)
            menuCard.orientation = LinearLayout.VERTICAL
            menuCard.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.marginTop
        menuView.layoutParams = lp
        container.addView(menuView)
    }

    fun seekForItemPosition(item: MenuPanelItem): Int {
        for (i in menuPanelItems.indices) {
            val mpi = menuPanelItems[i]
            val menu = mpi.menuName
            if (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的数据。
     *
     * @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的数据。
     *
     * @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.tag
            if (dtag == tag) {
                val position = delegate.position
                return getCacheViewFromPosition(position)
            }
        }
        return null
    }

    /**
     * 绑定item的点击事件。
     *
     * @param item
     * @return 绑定成功后返回item的view
     */
    private fun bindItemListener(item: MenuPanelItem): View {
        menuPanelItems.add(item)
        //解析Item所对应的布局,并调用item的initData
        val menuView = parseItemView(item, true)
        viewsCache.add(menuView)
        val tag = UUID.randomUUID().toString().substring(0, 16)
        menuView.tag = tag
        val delegate = getListenerInfo(tag)
        menuView.setOnClickListener(delegate)
        listenerInfo.add(delegate)
        return menuView
    }

    private fun applyDefault(item: MenuPanelItem) {
        // item的上边距修改为1dp
        item.marginTop =
            TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, 1f,
                resources.displayMetrics
            ).toInt()
        // item去掉标题
        item.title = ""
        // item去掉标题边距
        item.setTitleSpan(MenuPanelItemRoot.Span())
    }

    /**
     * 不是菜单,所以不会影响菜单的点击事件位置,但需要自己处理控件内部的点击事件。
     *
     * @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
    }

    /**
     * 样式等参数改变才需要更新,只有类似于addItem、removeItem这样的,不需要调用此方法。
     */
    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: Int
            get() = items.size

        fun addItem(item: MenuPanelItem) {
            items.add(item)
        }

        fun removeItem(item: MenuPanelItem?) {
            items.remove(item)
        }

        val isEmpty: Boolean
            get() = items.size == 0

        fun getItems(): MutableList<MenuPanelItem> {
            return items
        }
    }

    override fun onClick(v: View) {
        val tag = v.tag as String
        for (delegate in listenerInfo) {
            if (delegate.tag == tag) {
                val clickPos = delegate.position
                menuPanelItems[clickPos].menuName?.let {
                    onPanelMenuClickListener?.onMenuClick(clickPos, v, it)
                }
                break
            }
        }
    }

    fun setPanelBgColor(color: Int): MenuPanel {
        panelBgColor = color
        container.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 = -0xa0a07
        private const val DEFAULT_TITLE_COLOR = -0x666667
        private const val SEEK_FOR_ITEM_ERROR_NOT_FOUND = -1
        private const val SEEK_FOR_ITEM_ERROR_MISS_MENU_NAME = -2
    }
}

由于它仿RecyclerView的布局,它可以实现少量固定数量的item的高效创建,但不适应于大量item的场景。本来这个控件设计之初就是用在菜单上面的,而业务功能不可能无限多,所以这个问题可以忽略。根据代码我们可以得知,它是一个ScrollView,通常我们宽高都设置成match_parent,上面放一个titlebar,这样就填满了整个内容视图。这里面有addMenu()、addMenuGroup()和addCustomView()三种添加子控件的方法,只有前两种会受框架的约束。也就是说,如果你调用addCustomView()添加非菜单的视图,那么不会有OnPanelMenuClickListener面板菜单点击事件的回调,需要自己处理自身的事件。通过getCacheChildView()getCacheViewFromPosition()这两个方法都是用来更新菜单数据的,它们的区别在于前者是拿item的具体某一个子控件,后者是拿item本身。删除菜单和回调菜单的点击事件会使用到menuName这个属性,所以你在addMenu()的时候,务必保证menuName不重复。无论是添加还是移除菜单,最后都需要调用updatePanel()进行刷新。

kt 复制代码
package dora.widget.panel

import 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 = -0x666667
    private var tipsView: TextView? = null

    constructor(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 = tips
        return this
    }

    fun setTipsColor(color: Int): TipsMenuPanel {
        tipsColor = color
        return 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_HORIZONTAL
            tipsView!!.setTextColor(tipsColor)
            tipsView!!.layoutParams = lp
            tipsView!!.text = tips
            // 增加了底部的tips
            container.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的底部提示信息的布局也需要调用updatePanel()方法。

开始使用

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

kt 复制代码
/*
 * 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.layout

fun 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>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <dora.widget.DoraTitleBar
            android: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.MenuPanel
            android:id="@+id/menuPanel"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
</layout>
"""

以上为生成xml布局。

kt 复制代码
/*
 * 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.src

fun menuPanelActivityKt(
        applicationPackage: String,
  packageName: String,
        activityClass: String,
  bindingName: String,
  layoutName: String
) = """
package ${packageName}

import android.os.Bundle

import dora.BaseActivity

import ${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}> {

   @Override
    protected int getLayoutId() {
        return R.layout.${layoutName};
    }

   @Override
    public 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依赖配置
groovy 复制代码
// 添加以下代码到项目根目录下的build.gradle
allprojects {
    repositories {
        maven { url "https://jitpack.io" }
    }
}
// 添加以下代码到app模块的build.gradle
dependencies {
    implementation 'com.github.dora4:dview-menu-panel:1.0'
}
添加菜单和菜单组
添加菜单
kt 复制代码
binding.menuPanel.addMenu(NormalMenuPanelItem("menuName", "text", true, "arrowText"))
添加菜单组
kt 复制代码
binding.menuPanel.addMenuGroup(
             MenuPanelItemGroup(
                 DensityUtils.dp2px(10f),
                 NormalMenuPanelItem("menuName", "text", true, "arrowText")
             )
         )

不要无脑copy,参数请自行更换。

修改菜单数据

例如:更新颜色选择菜单的标签颜色。

kt 复制代码
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
                }
            }
        }
    }
}
设置菜单点击事件
kt 复制代码
binding.menuPanel.setOnPanelMenuClickListener(object : MenuPanel.OnPanelMenuClickListener {
    override fun onMenuClick(position: Int, view: View, menuName: String) {
        when (menuName) {
            "newMsgNotice" -> {
                // 新消息通知
                spmSelectContent("点击新消息通知")
                val intent = Intent(this@SettingsActivity, NewMsgNoticeActivity::class.java)
                startActivity(intent)
            }
            "switchLanguage" -> {
                // 切换语言
                spmSelectContent("点击切换语言")
                val intent = Intent(this@SettingsActivity, SetLanguageActivity::class.java)
                startActivity(intent)
            }
            "chatFont" -> {
                IntentUtils.startActivityWithString(
                    this@SettingsActivity,
                    ChatFontActivity::class.java,
                    KEY_USER_ID,
                    userId
                )
            }
            "chatBg" -> {
                spmSelectContent("点击聊天背景")
                IntentUtils.startActivityWithString(
                    this@SettingsActivity,
                    ChatBackgroundActivity::class.java,
                    KEY_USER_ID,
                    userId
                )
            }
            "cacheClear" -> {
                spmSelectContent("点击缓存清理")
                IntentUtils.startActivityWithString(
                    this@SettingsActivity,
                    CacheCleanActivity::class.java,
                    KEY_USER_ID,
                    userId
                )
            }
            "aboutUs" -> {
                spmSelectContent("点击关于我们")
                IntentUtils.startActivityWithString(
                    this@SettingsActivity,
                    AboutActivity::class.java,
                    KEY_USER_ID,
                    userId
                )
            }
            "superUser" -> {
                spmSelectContent("点击超级管理员")
                IntentUtils.startActivityWithString(
                    this@SettingsActivity,
                    SuperUserActivity::class.java,
                    KEY_USER_ID,
                    userId
                )
            }
            "logout" -> {
                spmSelectContent("点击注销登录")
                // 注销登录
                dialog!!.show(
                    "logout",
                    getString(R.string.are_you_sure_logout)
                )
            }
        }
    }
})

这里注意一点,尽量使用menuName去判断具体是哪一个菜单,而不建议使用position。因为在有删除菜单的情况下,position会错位。spm埋点统计的代码你无需关心。

总结

本篇详细讲解了MenuPanel的核心代码实现及其使用方式,下篇我们演示IDE插件的操作流程。最后不忘点个star支持一下,https://github.com/dora4/dview-menu-panel

相关推荐
zhangphil3 小时前
Android绘图Path基于LinearGradient线性动画渐变,Kotlin(2)
android·kotlin
watl04 小时前
【Android】unzip aar删除冲突classes再zip
android·linux·运维
键盘上的蚂蚁-4 小时前
PHP爬虫类的并发与多线程处理技巧
android
喜欢猪猪5 小时前
Java技术专家视角解读:SQL优化与批处理在大数据处理中的应用及原理
android·python·adb
JasonYin~6 小时前
HarmonyOS NEXT 实战之元服务:静态案例效果---手机查看电量
android·华为·harmonyos
zhangphil7 小时前
Android adb查看某个进程的总线程数
android·adb
抛空7 小时前
Android14 - SystemServer进程的启动与工作流程分析
android
Gerry_Liang9 小时前
记一次 Android 高内存排查
android·性能优化·内存泄露·mat
天天打码10 小时前
ThinkPHP项目如何关闭runtime下Log日志文件记录
android·java·javascript
爱数学的程序猿13 小时前
Python入门:6.深入解析Python中的序列
android·服务器·python