Span使用之文字高亮及点击事件

本文介绍了一种在Android应用中实现电话号码高亮显示及点击拨打电话的方法,利用SpannableStringBuilder和自定义ClickableSpan实现核心功能。

由于工作上的需求,类似于淘宝京东在获取物流信息时电话号码高亮显示,并触摸和点击电话号码时跳转到拨打电话.刚开始一时没想到什么好的方法,最后通过查找资料,看到了Span完全能够实现需求,并且使用效果好的不行.话不多说先上效果图如下:

这里写图片描述

效果图如上,现在分析实现的核心代码如下:

 //初始化SpannableStringBuilder
        mSpannableStringBuilder = new SpannableStringBuilder(mContent);
        //初始化需要高亮显示的文字和对其设置点击事件
        SpannableStringBuilder spannableStringBuilder = initClickText(mSpannableStringBuilder, mContent);
         //把mSpannableStringBuilder设置给对应的文本控件
        mTv.setText(spannableStringBuilder);
 //对文本控件设置触摸事件,当触摸区域是电话号码时拨打电话
        mTv.setOnTouchListener((v, event) -> {
            boolean ret = false;
            CharSequence text = ((TextView) v).getText();
            Spannable stext = Spannable.Factory.getInstance().newSpannable(text);
            TextView widget = (TextView) v;
            int action = event.getAction();
            //根据点击判断是否在spannable对象上;
            if (action == MotionEvent.ACTION_UP
                    || action == MotionEvent.ACTION_DOWN) {
                int x = (int) event.getX();
                int y = (int) event.getY();
                x -= widget.getTotalPaddingLeft();
                y -= widget.getTotalPaddingTop();
                x += widget.getScrollX();
                y += widget.getScrollY();
                Layout layout = widget.getLayout();
                int line = layout.getLineForVertical(y);
                int off = layout.getOffsetForHorizontal(line, x);
                ClickableSpan[] link = stext.getSpans(off, off, ClickableSpan.class);
                if (link.length != 0) {
                    if (action == MotionEvent.ACTION_UP) {
                        link[0].onClick(widget);
                    }
                    ret = true;
                }
            }
            return ret;
        });
 /**
     *  获取文字中的电话号码 并高亮显示和设置点击事件
     * @param spannableStringBuilder
     * @param text 文字内容
     * @return
     */
    private SpannableStringBuilder initClickText(SpannableStringBuilder spannableStringBuilder, String text) {
        Pattern patternPhone = Pattern.compile("\\d{3}-\\d{8}|\\d{4}-\\d{7}|\\d{11}");
        Matcher matcherPhone = patternPhone.matcher(text);
        int start = 0;
        //遍历取出字符串中所有的符合条件的;
        while (matcherPhone.find(start)) {
            start = matcherPhone.end();
            spannableStringBuilder.setSpan(new ForegroundColorSpan(CommonUtils.getColor(this, R.color.colorPrimary)), matcherPhone.start(), matcherPhone.end(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
            spannableStringBuilder.setSpan(new MyUrlSpan(matcherPhone.group()), matcherPhone.start(),
                    matcherPhone.end(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
            if (start >= text.length()) {
                break;
            }
        }
        return spannableStringBuilder;

    }
/**
     * Span点击的实现类
     */
    private class MyUrlSpan extends ClickableSpan {
        private String mUrl;

        public MyUrlSpan(String url) {
            mUrl = url;
        }

        //点击链接下划线,弹出dialog,提示提电话,或者发短信;通用的dialog我上篇文章有介绍.
        @Override
        public void onClick(View widget) {
            CommonBaseDialog.showDialog(SpanActivity.this, R.layout.common_dialog)
                    .setText(R.id.title, getPhoneText(mUrl))
                    .setViewVisibility(R.id.content, View.GONE)
                    .setDialogLocation(Gravity.CENTER, 60, 0, 60, 0)
                    .setViewListener((dialog, viewId) -> {
                        switch (viewId) {
                            case R.id.confirm:
                                Intent intent = new Intent(Intent.ACTION_DIAL);
                                Uri data = Uri.parse("tel:" + mUrl);
                                intent.setData(data);
                                SpanActivity.this.startActivity(intent);
                            case R.id.cancel:
                                dialog.dismiss();
                                break;
                        }
                    }, R.id.cancel, R.id.confirm);


        }
    }

这样就完美的实现了上述的需求希望这篇文章对你有所帮助谢谢!!!!!!!!!!

<think> 我们正在讨论如何在侧边栏中使用span标签的文字实现超链接跳转。 根据引用[1]中的内容,我们知道在Vue2/Nuxt2项目中,可以通过解析富文本中的标题标签(h1~h6)来生成侧边栏目录(TOC),并且实现点击目录跳转到对应锚点,以及滚动时自动高亮。 但是,用户现在的问题是在侧边栏中使用span标签的文字实现超链接跳转。这里的关键点在于:侧边栏中的目录项通常是由span(或其他标签)包裹的文字,我们需要让这些文字具有超链接的功能,即点击后跳转到页面内的指定锚点。 实现方法: 1. 首先,我们需要生成锚点。在文章内容中,为每个标题设置一个唯一的id(引用[1]中提到“对富文本内的h1~h6标题进行读取分析”并生成锚点目录)。例如: <h2 id="section1">第一部分</h2> 2. 在侧边栏中,我们使用带有超链接的a标签包裹span标签(或者直接使用a标签,但用户要求用span标签的文字),但实际上,要实现跳转,我们需要使用a标签的href属性指向对应的id。例如: <a href="#section1"><span>第一部分</span></a> 3. 但是,用户特别指出使用span标签的文字。我们可以这样理解:侧边栏的每个目录项可能是用span来显示文字,但整个目录项应该是一个超链接(即a标签包裹span)。这样,点击span所在区域(实际上是a标签)就会跳转。 4. 另外,引用[3]中提供了一个Bootstrap的demo,其中使用了jQuery来为侧边栏菜单添加点击事件,并且通过添加/移除active类来实现高亮。但注意,引用[3]中一开始注释掉了一句e.preventDefault(),如果加上则a标签的默认跳转会失效。因此,我们不应该阻止默认行为,这样a标签的跳转才会生效。 5. 如果我们不使用a标签,而只用span,那么我们需要通过JavaScript来实现跳转。例如,为span绑定点击事件,在事件处理函数中使用window.location.hash或scrollIntoView方法来实现跳转。但这样不如直接使用a标签简洁,且不利于SEO和可访问性。 6. 因此,推荐的方法是在侧边栏中使用a标签,并在其中包含span标签来显示文字。这样既满足了用户想要使用span标签的需求,又实现了超链接的功能。 示例代码(HTML结构): ```html <div id="sidebar"> <a href="#section1"><span>第一部分</span></a> <a href="#section2"><span>第二部分</span></a> </div> <div id="content"> <h2 id="section1">第一部分标题</h2> <p>内容...</p> <h2 id="section2">第二部分标题</h2> <p>内容...</p> </div> ``` 7. 如果侧边栏的目录是通过JavaScript动态生成的(如引用[1]中解析富文本生成目录),那么我们在生成每个目录项时,创建一个a元素,并设置其href为对应标题的id(前面加#),然后在a元素内部添加span元素来显示标题文字。 8. 此外,为了实现滚动时自动高亮当前阅读位置的目录项(如引用[1]所述),我们需要监听滚动事件,计算各个标题的位置,然后给对应的目录项(即a标签或它的父项)添加高亮样式类(如active)。这部分逻辑通常需要JavaScript实现。 9. 在Vue项目中,我们可以使用vue-scrollactive这样的插件,或者自己实现一个滚动监听和高亮逻辑。引用[1]中提到的“用户浏览富文本滚动经过标题锚点时右侧目录会自动高亮选中效果”就是如此。 总结: 在侧边栏中,使用a标签作为超链接容器,内部放置span标签显示文字。a标签的href属性设置为对应标题的id(带#号)。这样点击span(实际上是a标签)就会跳转到对应标题位置。 注意:如果侧边栏是用ul/li列表,那么通常是在li里面放a标签,a标签里面再放span。 示例(ul/li结构): ```html <ul id="toc"> <li><a href="#section1"><span>第一部分</span></a></li> <li><a href="#section2"><span>第二部分</span></a></li> </ul> ``` 样式上,我们可以通过CSS设置span的样式,而a标签则负责跳转功能。 相关问题: 1. 如何实现滚动页面时自动高亮侧边栏当前目录项? 2. 在Vue中如何动态生成侧边栏目录(TOC)? 3. 如何优化锚点跳转的平滑滚动效果? </think> 在侧边栏中使用 `<span>` 标签实现超链接跳转,需要通过 JavaScript 增强其功能,因为 `<span>` 本身不是可点击元素。以下是结合引用内容的实现方法: ### 解决方案(Vue2/Nuxt2 环境) ```html <!-- 侧边栏结构 --> <aside> <div v-for="(item, index) in tocList" :key="index"> <!-- 用 @click 绑定跳转事件 --> <span class="nav-link" @click="scrollToAnchor(item.id)"> {{ item.title }} </span> </div> </aside> ``` ```javascript // Vue 方法实现(参考引用[1][3]) methods: { scrollToAnchor(anchorId) { // 1. 移除旧的高亮 document.querySelectorAll('.nav-link').forEach(el => { el.classList.remove('active'); // 引用[3]的高亮逻辑 }); // 2. 添加新激活状态 event.target.classList.add('active'); // 3. 平滑滚动到锚点 const target = document.getElementById(anchorId); if (target) { target.scrollIntoView({ behavior: 'smooth' }); // 滚动动画 // 更新URL哈希(可选) history.replaceState(null, null, `#${anchorId}`); } }, // 自动生成目录(引用[1]) generateTOC() { this.tocList = []; const headers = document.querySelectorAll('h1, h2, h3, h4'); headers.forEach((header, index) => { if (!header.id) header.id = `heading-${index}`; this.tocList.push({ id: header.id, title: header.innerText, level: header.tagName.substring(1) }); }); } } ``` ```css /* CSS样式(参考引用[3]的高亮效果) */ .nav-link { cursor: pointer; /* 显示手型指针 */ padding: 8px 12px; display: block; transition: background 0.3s; } .nav-link:hover { background: #f0f0f0; } .nav-link.active { background: #e0f0ff; border-left: 3px solid #1890ff; /* 激活态标识 */ } ``` ### 关键实现原理 1. **`<span>` 转超链接** 通过 `@click` 绑定点击事件(Vue)或 `onclick`(原生JS),替代 `<a>` 的默认跳转行为 2. **锚点定位** 使用 `scrollIntoView()` 实现平滑滚动效果(替代传统的 `href="#id"`),支持动画过渡 3. **动态目录生成** - 扫描文章中的 `h1-h6` 标签(引用[1]) - 自动生成 ID 锚点(未预设 id 时) - 构建 tocList 目录数据源 4. **滚动高亮联动** 监听滚动事件,根据当前视口位置自动添加 `.active` 类: ```javascript mounted() { window.addEventListener('scroll', () => { const scrollPos = window.scrollY + 50; this.tocList.forEach(item => { const el = document.getElementById(item.id); if (el.offsetTop <= scrollPos) { document.querySelector('.active')?.classList.remove('active'); // 通过数据索引找到对应span添加active } }); }); } ``` ### 应用场景 适用于需要自定义样式的侧边栏导航: 1. 博客/文档系统的目录导航(引用[1]) 2. Bootstrap 风格的三级菜单(引用[3]) 3. 富文本生成的层级目录(支持 h1-h6 嵌套结构) > **提示**:若需 SEO 友好,建议在 `<span>` 外包裹 `<a href="#id">` 并阻止默认事件,同时保持 JavaScript 滚动控制[^1][^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值