Android可以纵向滚动的textView

在Android开发中,遇到一个需求:textView显示两行文字,超过部分可滚动,点击单词跳转不同页面,同时在recyclerView内部滚动时不干扰。通过设置maxLines为2并使用LinkMovementMethod,实现了在保持固定高度的同时,使textView具备纵向滚动功能,并确保点击事件正常工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在项目上遇到这么一个需求

1、一段文字,最多显示两行,点击某个单词进入A页面,点击另一个单词进入B页面,

2、如果句子太长的话,让这个textView可以上下滚动,但textView的高度始终是原来两行的高度,不能增高,并且单词的点击事件不能消失,

3、由于这个textView是套在recyclerVIew中的,在滚动textView的时候还不能让recyclerView滚动。而且为了性能问题,不能采用scrollView内嵌textView的方式,所以采用了下面的方法。

思路比较简单,实现textView中具体某个单词的点击事件只需要用textView.append(一个可点击的继承spannable的类就好)


首先这个textView的布局

<TextView
                    android:id="@+id/ctv_card_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentTop="true"
                    android:layout_weight="1"
                    android:maxLines="2"
                    android:textSize="15sp"
                    android:text="John Doe uploaded 4 photos"
                    android:textColor="#505256"
                    />

注意这里的maxLines=2也就是说最多显示两行,这里这么设置主要为了后面拿到这个textView高度设置的


下面就是初始化这个TextView,在代码里设置可以点击的单词,并且执行textView.setMovementMethod(),让TextView可以纵向滚动

public static void initCardHeader(final Activity activity, final TextView textView, String userName, String middleText, String restaurantName, final long userId , final long restaurantId , TextView ctv_reviewPhoto_count, long reviewCount , int photoCount){
        if(textView==null||activity==null)return;
        textView.setText("");//清空textView
        SpannableString userNameSpan = new SpannableString(userName);
        userNameSpan.setSpan(new ClickableSpan() {
            @Override
            public void updateDrawState(TextPaint ds) {
                super.updateDrawState(ds);
                ds.setColor(0xFF505256);
                ds.setAntiAlias(true);//抗锯齿
                ds.setUnderlineText(false);
                ds.setFakeBoldText(true);//设置粗体
            }
            @Override
            public void onClick(View widget) {//在这里复写点击事件
                JLogUtils.i("Alex","点击了用户名,用户id是"+userId);
                if(QravedApplication.getAppConfiguration().getUserId()!=userId)return;
                JLogUtils.i("Alex","准备跳转到myQraved");
                if(activity instanceof HomeActivity)((HomeActivity)activity).switchFragment(HomeActivity.FRAGMENT_TYPE_HOME_HOME, HomeActivity.FRAGMENT_TYPE_HOME_MYPROFILE_MYLIST);
            }
        }, 0, userNameSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        textView.append(userNameSpan);
        if(activity instanceof HomeActivity || activity instanceof JournalCommentsActivity) {
            textView.append(middleText);//添加中间部分

            SpannableString restaurantSpan = new SpannableString(restaurantName);
            restaurantSpan.setSpan(new ClickableSpan() {
                @Override
                public void updateDrawState(TextPaint ds) {
                    super.updateDrawState(ds);
                    ds.setColor(0xFF505256);
                    ds.setAntiAlias(true);//抗锯齿
                    ds.setUnderlineText(false);
                    ds.setFakeBoldText(true);//设置粗体
                }

                @Override
                public void onClick(View widget) {//在这里复写点击事件
                    JLogUtils.i("Alex", "点击了餐馆名字,餐馆id是" + restaurantId);
                    Intent intent = new Intent();
                    intent.putExtra("restaurantId", String.valueOf(restaurantId));
                    intent.setClass(activity, RestaurantDetailActivity.class);
                    activity.startActivity(intent);
                    activity.overridePendingTransition(R.anim.enter_righttoleft, R.anim.exit_righttoleft);
                }
            }, 0, restaurantSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            textView.append(restaurantSpan);
        }
        textView.setMovementMethod(AlxLinkMovementMethod.getInstance());//让textView可以纵向滚动,里面这个类在本文有源码
        ctv_reviewPhoto_count.setText(reviewCount + (reviewCount > 1 ? " reviews" : " review") + " - " + photoCount + (photoCount > 1 ? " photos" :" photo"));
        //让两行以上可以滚动的方法
        ViewTreeObserver observer = textView.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            
            @Override
            public void onGlobalLayout() {
                // TODO Auto-generated method stub
               
                Layout layout2 = textView.getLayout();
                if(textView!=null&&layout2!=null){
                    int lines = layout2.getLineCount();
                    
                    if(lines!=2)return;//如果不到两行,就不管了
                    int textViewHeight = textView.getHeight();//得到两行时候的textView的高度
                    textView.setMaxHeight(textViewHeight);//设置textView的最大高度为现在这样
                    textView.setMaxLines(Integer.MAX_VALUE);//不限制textView的行数,这样才能够滚动起来
                }

            }
        });

    }

下面是最重要的LinkMovementMethod的写法了,用了这个类就可以实现textView的纵向滚动

并且实现了在recyclerView内滚动事件不冲突,并且在滚动之后仍然保持点击事件,并且防止了点击事件的误触发

package com.imaginato.qravedconsumer.widget;

import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.ClickableSpan;
import android.view.MotionEvent;
import android.widget.TextView;

import com.imaginato.qravedconsumer.utils.JLogUtils;

/**
 * Created by Administrator on 2016/3/21.
 */
public class AlxLinkMovementMethod extends LinkMovementMethod{
    @Override
    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
        if (widget.getLayout().getLineCount() > 1) {
            JLogUtils.i("Alex", "大于一行");
            boolean ret = mOnTouchEvent(widget, buffer, event);
            widget.getParent().requestDisallowInterceptTouchEvent(true);//阻止父层的View截获touch事件
            if(event.getAction()==MotionEvent.ACTION_UP) widget.getParent().requestDisallowInterceptTouchEvent(false);
            JLogUtils.i("Alex","ret是"+ret);
            return true;
        } else {//小于一行
            boolean ret =  super.onTouchEvent(widget, buffer, event);
            JLogUtils.i("Alex", "小于一行"+ret);
            return ret;
        }
    }
    private boolean couldClick = true;//现在是否响应点击事件
    private float lastY;

    private boolean mOnTouchEvent(TextView widget, Spannable buffer, MotionEvent event){
        int action = event.getAction();
        switch (event.getAction()){
            case MotionEvent.ACTION_MOVE:
                if(Math.abs(event.getY()-lastY)>10)couldClick = false;//只有在发生了滚动的时候才禁止点击事件
                break;
        }
        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 = buffer.getSpans(off, off, ClickableSpan.class);

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    JLogUtils.i("Alex","现在up了"+couldClick);
                    if(couldClick)link[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    lastY = event.getY();
                    Selection.setSelection(buffer,
                            buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0]));
                    couldClick = true;
                }

                return true;
            } else {
                Selection.removeSelection(buffer);
            }
        }

        return super.onTouchEvent(widget, buffer, event);
    }

    private static AlxLinkMovementMethod hahaInstance;
    public static MovementMethod getInstance() {
        if (hahaInstance == null)
            hahaInstance = new AlxLinkMovementMethod();

        return hahaInstance;
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值