1. 程式人生 > >RecyclerView新增點選事件和textView的超連結點選事件衝突

RecyclerView新增點選事件和textView的超連結點選事件衝突

最近做了一個小的demo,使用RecyclerView,item要求顯示一段文字,文字包含點選事件,結果發現,超連結的點選事件和RecyclerView的點選事件發生衝突,於是上網搜尋相關問題,走了一些彎路,最後在國外的網站發現解決方法,

以下是解決該問題的步驟:

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的點選事件衝突問題,希望對大家有所幫助!