按下时更改单个ClickableSpan的文本颜色,而不影响同一TextView中的其他ClickableSpans

 小太郎在路上_439 发布于 2023-02-07 13:03

我有一个带有多个ClickableSpans的TextView.当按下ClickableSpan时,我希望它改变其文本的颜色.

我已经尝试将颜色状态列表设置为TextView的textColorLink属性.这不会产生所需的结果,因为当用户单击TextView上的任何位置时,这会导致所有跨度改变颜色.

有趣的是,使用textColorHighlight更改背景颜色按预期工作:单击跨度仅更改该跨度的背景颜色,并单击TextView中的任何其他位置不执行任何操作.

我也尝试使用与ClickableSpans相同的边界设置ForegroundColorSpans,我在其中传递与上面相同的颜色状态列表作为颜色资源.这也不起作用.跨度始终保持颜色状态列表中默认状态的颜色,并且永远不会进入按下状态.

有谁知道如何做到这一点?

这是我使用的颜色状态列表:


  
  

Steven Melio.. 62

我终于找到了一个解决方案来完成我想要的一切.它基于这个答案.

这是我修改过的LinkMovementMethod,它标记了在触摸事件开始时按下的跨度(MotionEvent.ACTION_DOWN),并在触摸结束或触摸位置移出跨度时取消标记.

public class LinkTouchMovementMethod extends LinkMovementMethod {
    private TouchableSpan mPressedSpan;

    @Override
    public boolean onTouchEvent(TextView textView, Spannable spannable, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            mPressedSpan = getPressedSpan(textView, spannable, event);
            if (mPressedSpan != null) {
                mPressedSpan.setPressed(true);
                Selection.setSelection(spannable, spannable.getSpanStart(mPressedSpan),
                        spannable.getSpanEnd(mPressedSpan));
            }
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            TouchableSpan touchedSpan = getPressedSpan(textView, spannable, event);
            if (mPressedSpan != null && touchedSpan != mPressedSpan) {
                mPressedSpan.setPressed(false);
                mPressedSpan = null;
                Selection.removeSelection(spannable);
            }
        } else {
            if (mPressedSpan != null) {
                mPressedSpan.setPressed(false);
                super.onTouchEvent(textView, spannable, event);
            }
            mPressedSpan = null;
            Selection.removeSelection(spannable);
        }
        return true;
    }

    private TouchableSpan getPressedSpan(
            TextView textView,
            Spannable spannable,
            MotionEvent event) {

            int x = (int) event.getX() - textView.getTotalPaddingLeft() + textView.getScrollX();
            int y = (int) event.getY() - textView.getTotalPaddingTop() + textView.getScrollY();

            Layout layout = textView.getLayout();
            int position = layout.getOffsetForHorizontal(layout.getLineForVertical(y), x);

            TouchableSpan[] link = spannable.getSpans(position, position, TouchableSpan.class);
            TouchableSpan touchedSpan = null;
            if (link.length > 0 && positionWithinTag(position, spannable, link[0])) {
                touchedSpan = link[0];
            }

            return touchedSpan;
        }

        private boolean positionWithinTag(int position, Spannable spannable, Object tag) {
            return position >= spannable.getSpanStart(tag) && position <= spannable.getSpanEnd(tag);
        }
    }

这需要应用于TextView,如下所示:

    yourTextView.setMovementMethod(new LinkTouchMovementMethod());

这是修改后的ClickableSpan,它根据LinkTouchMovementMethod设置的按下状态编辑绘制状态:(它还从链接中删除下划线)

public abstract class TouchableSpan extends ClickableSpan {
    private boolean mIsPressed;
    private int mPressedBackgroundColor;
    private int mNormalTextColor;
    private int mPressedTextColor;

    public TouchableSpan(int normalTextColor, int pressedTextColor, int pressedBackgroundColor) {
        mNormalTextColor = normalTextColor;
        mPressedTextColor = pressedTextColor;
        mPressedBackgroundColor = pressedBackgroundColor;
    }

    public void setPressed(boolean isSelected) {
        mIsPressed = isSelected;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        super.updateDrawState(ds);
        ds.setColor(mIsPressed ? mPressedTextColor : mNormalTextColor);
        ds.bgColor = mIsPressed ? mPressedBackgroundColor : 0xffeeeeee;
        ds.setUnderlineText(false);
    }
}

好方案!为了避免创建无数的`LinkTouchMovementMethod`对象,我会覆盖静态的`LinkMovementMethod.getInstance()`方法,以便随时返回单个实例. (5认同)


zundi.. 22

更简单的解决方案,IMO:

final int colorForThisClickableSpan = Color.RED; //Set your own conditional logic here.

final ClickableSpan link = new ClickableSpan() {
    @Override
    public void onClick(final View view) {
        //Do something here!
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        super.updateDrawState(ds);
        ds.setColor(colorForThisClickableSpan);
    }
};

这不仅设置了按下状态的颜色,还设置了所有 (3认同)


Maksim Dmitr.. 8

legr3c的回答对我帮助很大.我想补充几点意见.

备注#1.

TextView myTextView = (TextView) findViewById(R.id.my_textview);
myTextView.setMovementMethod(new LinkTouchMovementMethod());
myTextView.setHighlightColor(getResources().getColor(android.R.color.transparent));
SpannableString mySpannable = new SpannableString(text);
mySpannable.setSpan(new TouchableSpan(), 0, 7, 0);
mySpannable.setSpan(new TouchableSpan(), 15, 18, 0);
myTextView.setText(mySpannable, BufferType.SPANNABLE);

我申请LinkTouchMovementMethodTextView两个跨度.单击时,跨度以蓝色突出显示. myTextView.setHighlightColor(getResources().getColor(android.R.color.transparent)); 修复了这个bug.

备注#2.

不要忘了路过的时候,从资源获取的颜色normalTextColor,pressedTextColor以及pressedBackgroundColor.

应该在这里传递已解析的颜色而不是资源ID

4 个回答
  • 所有这些解决方案都需要太多工作。

    只需android:textColorLink在您的TextView选择器中设置即可。然后创建一个无需覆盖updateDrawState(...)的clickableSpan。全部做完。

    这里有个简单的例子:

    在你strings.xml有一个这样的声明的字符串:

    <string name="mystring">This is my message%1$s these words are highlighted%2$s and awesome. </string>
    

    然后在您的活动中:

    private void createMySpan(){
        final String token = "#";
        String myString = getString(R.string.mystring,token,token);
    
        int start = myString.toString().indexOf(token);
        //we do -1 since we are about to remove the tokens afterwards so it shifts
        int finish = myString.toString().indexOf(token, start+1)-1;
    
        myString = myString.replaceAll(token, "");
    
        //create your spannable
        final SpannableString spannable = new SpannableString(myString);
        final ClickableSpan clickableSpan = new ClickableSpan() {
                @Override
                public void onClick(final View view) {
                    doSomethingOnClick();
                }
            };
    
        spannable.setSpan(clickableSpan, start, finish, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    
        mTextView.setMovementMethod(LinkMovementMethod.getInstance());
        mTextView.setText(spannable);
    }
    

    这里是重要的部分..声明一个选择器,如下所示myselector.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:state_pressed="true" android:color="@color/gold"/>
        <item android:color="@color/pink"/>
    
    </selector>
    

    最后在您TextView的xml中执行以下操作:

     <TextView
         android:id="@+id/mytextview"
         android:background="@android:color/transparent"
         android:text="@string/mystring"
         android:textColorLink="@drawable/myselector" />
    

    现在,您可以在clickableSpan上具有按下状态。

    2023-02-07 13:05 回答
  • 我终于找到了一个解决方案来完成我想要的一切.它基于这个答案.

    这是我修改过的LinkMovementMethod,它标记了在触摸事件开始时按下的跨度(MotionEvent.ACTION_DOWN),并在触摸结束或触摸位置移出跨度时取消标记.

    public class LinkTouchMovementMethod extends LinkMovementMethod {
        private TouchableSpan mPressedSpan;
    
        @Override
        public boolean onTouchEvent(TextView textView, Spannable spannable, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                mPressedSpan = getPressedSpan(textView, spannable, event);
                if (mPressedSpan != null) {
                    mPressedSpan.setPressed(true);
                    Selection.setSelection(spannable, spannable.getSpanStart(mPressedSpan),
                            spannable.getSpanEnd(mPressedSpan));
                }
            } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                TouchableSpan touchedSpan = getPressedSpan(textView, spannable, event);
                if (mPressedSpan != null && touchedSpan != mPressedSpan) {
                    mPressedSpan.setPressed(false);
                    mPressedSpan = null;
                    Selection.removeSelection(spannable);
                }
            } else {
                if (mPressedSpan != null) {
                    mPressedSpan.setPressed(false);
                    super.onTouchEvent(textView, spannable, event);
                }
                mPressedSpan = null;
                Selection.removeSelection(spannable);
            }
            return true;
        }
    
        private TouchableSpan getPressedSpan(
                TextView textView,
                Spannable spannable,
                MotionEvent event) {
    
                int x = (int) event.getX() - textView.getTotalPaddingLeft() + textView.getScrollX();
                int y = (int) event.getY() - textView.getTotalPaddingTop() + textView.getScrollY();
    
                Layout layout = textView.getLayout();
                int position = layout.getOffsetForHorizontal(layout.getLineForVertical(y), x);
    
                TouchableSpan[] link = spannable.getSpans(position, position, TouchableSpan.class);
                TouchableSpan touchedSpan = null;
                if (link.length > 0 && positionWithinTag(position, spannable, link[0])) {
                    touchedSpan = link[0];
                }
    
                return touchedSpan;
            }
    
            private boolean positionWithinTag(int position, Spannable spannable, Object tag) {
                return position >= spannable.getSpanStart(tag) && position <= spannable.getSpanEnd(tag);
            }
        }
    

    这需要应用于TextView,如下所示:

        yourTextView.setMovementMethod(new LinkTouchMovementMethod());
    

    这是修改后的ClickableSpan,它根据LinkTouchMovementMethod设置的按下状态编辑绘制状态:(它还从链接中删除下划线)

    public abstract class TouchableSpan extends ClickableSpan {
        private boolean mIsPressed;
        private int mPressedBackgroundColor;
        private int mNormalTextColor;
        private int mPressedTextColor;
    
        public TouchableSpan(int normalTextColor, int pressedTextColor, int pressedBackgroundColor) {
            mNormalTextColor = normalTextColor;
            mPressedTextColor = pressedTextColor;
            mPressedBackgroundColor = pressedBackgroundColor;
        }
    
        public void setPressed(boolean isSelected) {
            mIsPressed = isSelected;
        }
    
        @Override
        public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            ds.setColor(mIsPressed ? mPressedTextColor : mNormalTextColor);
            ds.bgColor = mIsPressed ? mPressedBackgroundColor : 0xffeeeeee;
            ds.setUnderlineText(false);
        }
    }
    

    2023-02-07 13:05 回答
  • legr3c的回答对我帮助很大.我想补充几点意见.

    备注#1.

    TextView myTextView = (TextView) findViewById(R.id.my_textview);
    myTextView.setMovementMethod(new LinkTouchMovementMethod());
    myTextView.setHighlightColor(getResources().getColor(android.R.color.transparent));
    SpannableString mySpannable = new SpannableString(text);
    mySpannable.setSpan(new TouchableSpan(), 0, 7, 0);
    mySpannable.setSpan(new TouchableSpan(), 15, 18, 0);
    myTextView.setText(mySpannable, BufferType.SPANNABLE);
    

    我申请LinkTouchMovementMethodTextView两个跨度.单击时,跨度以蓝色突出显示. myTextView.setHighlightColor(getResources().getColor(android.R.color.transparent)); 修复了这个bug.

    备注#2.

    不要忘了路过的时候,从资源获取的颜色normalTextColor,pressedTextColor以及pressedBackgroundColor.

    应该在这里传递已解析的颜色而不是资源ID

    2023-02-07 13:05 回答
  • 更简单的解决方案,IMO:

    final int colorForThisClickableSpan = Color.RED; //Set your own conditional logic here.
    
    final ClickableSpan link = new ClickableSpan() {
        @Override
        public void onClick(final View view) {
            //Do something here!
        }
    
        @Override
        public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            ds.setColor(colorForThisClickableSpan);
        }
    };
    

    2023-02-07 13:05 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有