天天看点

RecyclerView添加点击事件和textView的超链接点击事件冲突

最近做了一个小的demo,使用RecyclerView,item要求显示一段文字,文字包含点击事件,结果发现,超链接的点击事件和RecyclerView的点击事件发生冲突,于是上网搜索相关问题,走了一些弯路,最后在国外的网站发现解决方法,

以下就是发现答案的原网站:http://stackoverflow.com/questions/8558732/listview-textview-with-linkmovementmethod-makes-list-item-unclickable。

以下是解决该问题的步骤:

1.      自定义TextView,定义一个linkHit去控制当前TextView的onTouchEvent的返回值,用于判断是否由TextView来处理当前的点击事件。

2.      自定义linkMovementMethod,并重写它onTouchEvent方法,根据不同情况考虑返回值。

如果点击的是超链接的部分,则selection继续被选中,并且把自定义textView的linkHit设置为true,textView的onTouchEvent返回true,即处理当前点击事件。

如果是点击的是非超链接的部分,即把当前selection给移除掉,textView的onTouchEvent返回false,即不处理当前点击事件,那么这时候就会调用我们定义的RecyclerView他的item的点击事件。

public class LinkTextView extends TextView {

    boolean dontConsumeNonUrlClicks = true;
    boolean linkHit;

    public LinkTextView(Context context) {
        super(context);
    }

    public LinkTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public LinkTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public LinkTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        linkHit = false;
        boolean res = super.onTouchEvent(event);

        if (dontConsumeNonUrlClicks)
            return linkHit;
        return res;

    }

    public void setTextViewHTML(String html) {
        CharSequence sequence = Html.fromHtml(html);
        SpannableStringBuilder strBuilder =
                new SpannableStringBuilder(sequence);
        setText(strBuilder);
    }

    public static class LocalLinkMovementMethod extends LinkMovementMethod {
        static LocalLinkMovementMethod sInstance;


        public static LocalLinkMovementMethod getInstance() {
            if (sInstance == null)
                sInstance = new LocalLinkMovementMethod();

            return sInstance;
        }

        @Override
        public boolean onTouchEvent(TextView widget,
                                    Spannable buffer, MotionEvent event) {
            int action = event.getAction();

            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) {
                        link[0].onClick(widget);
                    } else if (action == MotionEvent.ACTION_DOWN) {
                        Selection.setSelection(buffer,
                                buffer.getSpanStart(link[0]),
                                buffer.getSpanEnd(link[0]));
                    }

                    if (widget instanceof LinkTextView) {
                        ((LinkTextView) widget).linkHit = true;
                    }
                    return true;
                } else {
                    Selection.removeSelection(buffer);
                    Touch.onTouchEvent(widget, buffer, event);
                    return false;
                }
            }
            return Touch.onTouchEvent(widget, buffer, event);
        }
    }

}
           

3.      布局使用自定义的textView,并且将该textView的focusable和clickable都设置为true。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <test.myapplication.LinkTextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:focusable="true"
        android:padding="20dp"
        android:text="TextView" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginTop="2dp"
        android:background="#000" />

</LinearLayout>
           

4.      用法同普通textView添加超链接一致,在设置setMovementMethod方法时,使用我们自定义的linkMovementMethod。我把整个Adapter代码贴出来。

public class ListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private LayoutInflater inflater;
    private static Context mContext;
    List<String> data = null;
    
    public ListAdapter(Context context, List<String> data) {
        this.mContext = context;
        inflater = LayoutInflater.from(mContext);
        this.data = data;
    }
    
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = inflater.inflate(R.layout.item_list, parent, false);
        return new ItemViewHolder(v);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        SpannableString sp = new SpannableString(data.get(position));
        sp.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(mContext, "超链接点击事件", Toast.LENGTH_SHORT).show();
            }
        }, 2, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        ((ItemViewHolder) holder).txt.setText(sp);
        ((ItemViewHolder) holder).txt.setMovementMethod(LinkTextView.LocalLinkMovementMethod.getInstance());
    }

    @Override

    public int getItemCount() {
        return data == null ? 0 : data.size();
    }

    public static class ItemViewHolder extends RecyclerView.ViewHolder {

        LinkTextView txt;

        public ItemViewHolder(View convertView) {
            super(convertView);
            convertView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //每个item的点击事件
                    Intent intentLogin = new Intent(mContext, RatingBarActivity.class);
                    mContext.startActivity(intentLogin);
                }
            });
            txt = (LinkTextView) convertView.findViewById(R.id.textView);
        }
    }
}
           

以上就是解决textView超链接点击事件和RecyclerView的每个item的点击事件冲突问题,希望对大家有所帮助!