fix: 修复长按贴中文本无法弹出菜单

This commit is contained in:
HuanChengFly 2020-08-20 16:20:16 +08:00
parent 9e50d48999
commit 3da25a4c49
6 changed files with 285 additions and 42 deletions

View File

@ -7,7 +7,6 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.method.LinkMovementMethod;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
@ -26,6 +25,7 @@ import com.huanchengfly.tieba.post.api.TiebaApi;
import com.huanchengfly.tieba.post.api.models.CommonResponse;
import com.huanchengfly.tieba.post.api.models.SubFloorListBean;
import com.huanchengfly.tieba.post.api.models.ThreadContentBean;
import com.huanchengfly.tieba.post.components.LinkMovementClickMethod;
import com.huanchengfly.tieba.post.components.spans.MyURLSpan;
import com.huanchengfly.tieba.post.components.spans.MyUserSpan;
import com.huanchengfly.tieba.post.fragments.ConfirmDialogFragment;
@ -236,7 +236,7 @@ public class RecyclerFloorAdapter extends CommonBaseAdapter<SubFloorListBean.Pos
private TextView createTextView(int type) {
TintTextView textView = new TintTextView(mContext);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setMovementMethod(LinkMovementClickMethod.getInstance());
textView.setClickable(false);
textView.setFocusable(false);
textView.setFocusableInTouchMode(false);

View File

@ -7,7 +7,6 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
@ -21,8 +20,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import com.allen.library.SuperTextView;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestOptions;
import com.huanchengfly.tieba.post.BaseApplication;
import com.huanchengfly.tieba.post.R;
import com.huanchengfly.tieba.post.activities.BaseActivity;
@ -30,6 +27,8 @@ import com.huanchengfly.tieba.post.activities.ReplyActivity;
import com.huanchengfly.tieba.post.api.TiebaApi;
import com.huanchengfly.tieba.post.api.models.CommonResponse;
import com.huanchengfly.tieba.post.api.models.ThreadContentBean;
import com.huanchengfly.tieba.post.components.LinkMovementClickMethod;
import com.huanchengfly.tieba.post.components.LinkTouchMovementMethod;
import com.huanchengfly.tieba.post.components.spans.MyImageSpan;
import com.huanchengfly.tieba.post.components.spans.MyURLSpan;
import com.huanchengfly.tieba.post.components.spans.MyUserSpan;
@ -53,6 +52,7 @@ import com.huanchengfly.tieba.post.widgets.MyImageView;
import com.huanchengfly.tieba.post.widgets.MyLinearLayout;
import com.huanchengfly.tieba.post.widgets.VideoPlayerStandard;
import com.huanchengfly.tieba.post.widgets.VoicePlayerView;
import com.huanchengfly.tieba.post.widgets.theme.TintMySpannableTextView;
import com.huanchengfly.tieba.post.widgets.theme.TintTextView;
import com.othershe.baseadapter.ViewHolder;
import com.othershe.baseadapter.base.MultiBaseAdapter;
@ -80,10 +80,8 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
private static final int TEXT_VIEW_TYPE_FLOOR = 1;
private Map<String, ThreadContentBean.UserInfoBean> userInfoBeanMap;
private NavigationHelper navigationHelper;
private RequestOptions avatarRequestOptions;
private LinearLayout.LayoutParams defaultLayoutParams;
private RequestOptions defaultRequestOptions;
private DrawableTransitionOptions transitionOptions;
private LinearLayout.LayoutParams defaultLayoutParamsWithNoMargins;
private ThreadContentBean.ThreadBean threadBean;
private ThreadContentBean dataBean;
private TreeMap<Integer, List<PhotoViewBean>> photoViewBeansMap;
@ -112,19 +110,12 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
});
showForum = true;
userInfoBeanMap = new HashMap<>();
avatarRequestOptions = new RequestOptions()
.placeholder(R.drawable.bg_placeholder_circle)
.circleCrop()
.skipMemoryCache(true);
navigationHelper = NavigationHelper.newInstance(mContext);
immersive = false;
this.screenWidthPx = BaseApplication.ScreenInfo.EXACT_SCREEN_WIDTH;
defaultLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
defaultLayoutParams.setMargins(0, 8, 0, 8);
defaultRequestOptions = new RequestOptions()
.placeholder(R.drawable.bg_placeholder)
.skipMemoryCache(true);
transitionOptions = DrawableTransitionOptions.withCrossFade();
defaultLayoutParamsWithNoMargins = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
photoViewBeansMap = new TreeMap<>();
blockCacheMap = new HashMap<>();
}
@ -217,7 +208,7 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
}
private void setPic(List<ThreadContentBean.PostListItemBean> postListItemBeans) {
photoViewBeansMap = new TreeMap<Integer, List<PhotoViewBean>>();
photoViewBeansMap = new TreeMap<>();
addPic(postListItemBeans);
}
@ -260,24 +251,22 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
builder.append(userInfoBean.getNameShow(), new MyUserSpan(mContext, userInfoBean.getId()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (threadBean.getAuthor() != null && userInfoBean.getId() != null && userInfoBean.getId().equals(threadBean.getAuthor().getId())) {
builder.append(" ");
int start = builder.length();
builder.append("楼主", new RoundBackgroundColorSpan(mContext,
alphaColor(ThemeUtils.getColorByAttr(mContext, R.attr.colorAccent), 30),
ThemeUtils.getColorByAttr(mContext, R.attr.colorAccent),
DisplayUtil.dp2px(mContext, 10)), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//builder.setSpan(new TextAppearanceSpan("serif", Typeface.BOLD, DisplayUtil.dp2px(mContext, 10), ColorStateList.valueOf(Color.WHITE), ColorStateList.valueOf(Color.WHITE)), start, start + "楼主".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.append(" ");
}
builder.append(":");
}
if (subPostListItemBean.getContent().size() >= 1 && "10".equals(subPostListItemBean.getContent().get(0).getType())) {
if (subPostListItemBean.getContent().size() > 0 && "10".equals(subPostListItemBean.getContent().get(0).getType())) {
String voiceUrl = "http://c.tieba.baidu.com/c/p/voice?voice_md5=" + subPostListItemBean.getContent().get(0).getVoiceMD5() + "&play_from=pb_voice_play";
RelativeLayout container = new RelativeLayout(mContext);
container.setLayoutParams(defaultLayoutParams);
container.setLayoutParams(defaultLayoutParamsWithNoMargins);
container.setPadding(DisplayUtil.dp2px(mContext, 8),
1,
8,
DisplayUtil.dp2px(mContext, 8),
1);
8);
container.setBackground(Util.getDrawableByAttr(mContext, R.attr.selectableItemBackground));
container.setOnClickListener(view -> mContext.startActivity(new Intent(mContext, ReplyActivity.class)
.putExtra("data", new ReplyInfoBean(dataBean.getThread().getId(),
@ -303,7 +292,7 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
return container;
}
TextView textView = createTextView(TEXT_VIEW_TYPE_FLOOR);
textView.setLayoutParams(defaultLayoutParams);
textView.setLayoutParams(defaultLayoutParamsWithNoMargins);
for (ThreadContentBean.ContentBean contentBean : subPostListItemBean.getContent()) {
switch (contentBean.getType()) {
case "0":
@ -329,9 +318,9 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
}
textView.setText(StringUtil.getEmotionContent(EmotionUtil.EMOTION_ALL_TYPE, textView, builder));
textView.setPadding(DisplayUtil.dp2px(mContext, 8),
1,
8,
DisplayUtil.dp2px(mContext, 8),
1);
8);
textView.setBackground(Util.getDrawableByAttr(mContext, R.attr.selectableItemBackground));
textView.setOnClickListener(view -> mContext.startActivity(new Intent(mContext, ReplyActivity.class)
.putExtra("data", new ReplyInfoBean(dataBean.getThread().getId(),
@ -393,10 +382,6 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
}
}
private boolean canLoadGlide() {
return Util.canLoadGlide(mContext);
}
private int getMaxWidth(String floor) {
int maxWidth = screenWidthPx - DisplayUtil.dp2px(mContext, 28 + 38);
if (isImmersive() || "1".equals(floor)) {
@ -575,7 +560,7 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
holder.setText(R.id.thread_list_item_user_time, mContext.getString(R.string.tip_thread_item, data.getFloor(), String.valueOf(DateUtils.getRelativeTimeSpanString(Long.valueOf(data.getTime()) * 1000L))));
holder.setText(R.id.thread_list_item_content_title, data.getTitle());
holder.setOnClickListener(R.id.thread_list_item_reply, view -> showMenu(data, position));
if (data.getFloor().equals("1"))
if ("1".equals(data.getFloor()))
holder.setVisibility(R.id.thread_list_item_reply, View.GONE);
else
holder.setVisibility(R.id.thread_list_item_reply, View.VISIBLE);
@ -591,11 +576,11 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
});
ImageUtil.load(holder.getView(R.id.thread_list_item_user_avatar), ImageUtil.LOAD_TYPE_AVATAR, userInfoBean.getPortrait());
}
if (!data.getFloor().equals("1"))
if (!"1".equals(data.getFloor()))
holder.setVisibility(R.id.thread_list_item_content_title, View.GONE);
else
holder.setVisibility(R.id.thread_list_item_content_title, View.VISIBLE);
initContentView(holder, data);
initContentView(holder, data, position);
initFloorView(holder, data);
if (isImmersive()) {
holder.getView(R.id.thread_list_item_content).setPadding(DisplayUtil.dp2px(mContext, 4), 0, DisplayUtil.dp2px(mContext, 4), 0);
@ -694,15 +679,24 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
}
private TextView createTextView(int type) {
TintTextView textView = new TintTextView(mContext);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setClickable(false);
TextView textView;
if (type == TEXT_VIEW_TYPE_CONTENT) {
TintMySpannableTextView mySpannableTextView = new TintMySpannableTextView(mContext);
mySpannableTextView.setTintResId(R.color.default_color_text);
mySpannableTextView.setLinkTouchMovementMethod(LinkTouchMovementMethod.getInstance());
textView = mySpannableTextView;
} else {
TintTextView tintTextView = new TintTextView(mContext);
tintTextView.setTintResId(R.color.default_color_text);
tintTextView.setMovementMethod(LinkMovementClickMethod.getInstance());
textView = tintTextView;
}
textView.setFocusable(false);
textView.setFocusableInTouchMode(false);
textView.setClickable(false);
textView.setLongClickable(false);
textView.setTextIsSelectable(false);
textView.setOnClickListener(null);
textView.setOnLongClickListener(null);
textView.setTintResId(R.color.default_color_text);
textView.setLetterSpacing(0.02F);
if (type == TEXT_VIEW_TYPE_CONTENT) {
textView.setTextSize(16);
@ -720,7 +714,7 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
}
private LinearLayout.LayoutParams getLayoutParams(ThreadContentBean.ContentBean contentBean, String floor) {
if (!contentBean.getType().equals("3") && !contentBean.getType().equals("20") && !contentBean.getType().equals("5")) {
if (!"3".equals(contentBean.getType()) && !"20".equals(contentBean.getType()) && !"5".equals(contentBean.getType())) {
return defaultLayoutParams;
}
float widthFloat, heightFloat;
@ -778,7 +772,7 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
return false;
}
private List<View> getContentViews(ThreadContentBean.PostListItemBean postListItemBean) {
private List<View> getContentViews(ThreadContentBean.PostListItemBean postListItemBean, int position) {
List<View> views = new ArrayList<>();
for (ThreadContentBean.ContentBean contentBean : postListItemBean.getContent()) {
switch (contentBean.getType()) {
@ -895,10 +889,10 @@ public class RecyclerThreadAdapter extends MultiBaseAdapter<ThreadContentBean.Po
return views;
}
private void initContentView(ViewHolder viewHolder, ThreadContentBean.PostListItemBean postListItemBean) {
private void initContentView(ViewHolder viewHolder, ThreadContentBean.PostListItemBean postListItemBean, int position) {
MyLinearLayout myLinearLayout = viewHolder.getView(R.id.thread_list_item_content_content);
myLinearLayout.removeAllViews();
myLinearLayout.addViews(getContentViews(postListItemBean));
myLinearLayout.addViews(getContentViews(postListItemBean, position));
}
@Override

View File

@ -0,0 +1,52 @@
package com.huanchengfly.tieba.post.components
import android.text.Selection
import android.text.Spannable
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.view.MotionEvent
import android.widget.TextView
object LinkMovementClickMethod : LinkMovementMethod() {
private const val CLICK_DELAY = 500L
private var lastClickTime: Long = 0
override fun canSelectArbitrarily(): Boolean {
return false
}
override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean {
val action = event.actionMasked
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
var x = event.x.toInt()
var y = event.y.toInt()
x -= widget.totalPaddingLeft
y -= widget.totalPaddingTop
x += widget.scrollX
y += widget.scrollY
val layout = widget.layout
val line = layout.getLineForVertical(y)
val off = layout.getOffsetForHorizontal(line, x.toFloat())
val link = buffer.getSpans(off, off, ClickableSpan::class.java)
if (link.isNotEmpty()) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget)
} else {
Selection.setSelection(buffer, buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]))
}
return true
} else {
Selection.removeSelection(buffer)
}
}
return false
}
override fun initialize(widget: TextView?, text: Spannable?) {
Selection.removeSelection(text)
}
}

View File

@ -0,0 +1,81 @@
package com.huanchengfly.tieba.post.components;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.MotionEvent;
import android.widget.TextView;
public class LinkTouchMovementMethod extends LinkMovementMethod {
private static LinkTouchMovementMethod sInstance;
private ClickableSpan 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) {
Selection.setSelection(spannable, spannable.getSpanStart(mPressedSpan),
spannable.getSpanEnd(mPressedSpan));
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
ClickableSpan touchedSpan = getPressedSpan(textView, spannable, event);
if (mPressedSpan != null && touchedSpan != mPressedSpan) {
mPressedSpan = null;
Selection.removeSelection(spannable);
}
} else {
if (mPressedSpan != null) {
super.onTouchEvent(textView, spannable, event);
}
mPressedSpan = null;
Selection.removeSelection(spannable);
}
return mPressedSpan != null;
}
/**
* Copy from:
* http://stackoverflow.com/questions/20856105/change-the-text-color-of-a-single-clickablespan-when-pressed-without-affecting-o
* By:
* Steven Meliopoulos
*/
private ClickableSpan getPressedSpan(TextView textView, Spannable spannable, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= textView.getTotalPaddingLeft();
y -= textView.getTotalPaddingTop();
x += textView.getScrollX();
y += textView.getScrollY();
Layout layout = textView.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = spannable.getSpans(off, off, ClickableSpan.class);
ClickableSpan touchedSpan = null;
if (link.length > 0) {
touchedSpan = link[0];
}
return touchedSpan;
}
public boolean isPressedSpan() {
return mPressedSpan != null;
}
public static LinkTouchMovementMethod getInstance() {
if (sInstance == null) {
sInstance = new LinkTouchMovementMethod();
}
return sInstance;
}
}

View File

@ -0,0 +1,38 @@
package com.huanchengfly.tieba.post.widgets;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
import com.huanchengfly.tieba.post.components.LinkTouchMovementMethod;
public class MySpannableTextView extends AppCompatTextView {
private LinkTouchMovementMethod mLinkTouchMovementMethod;
public MySpannableTextView(Context context) {
super(context);
}
public MySpannableTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MySpannableTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
return mLinkTouchMovementMethod != null ? mLinkTouchMovementMethod.isPressedSpan() : result;
}
public void setLinkTouchMovementMethod(LinkTouchMovementMethod linkTouchMovementMethod) {
mLinkTouchMovementMethod = linkTouchMovementMethod;
setMovementMethod(linkTouchMovementMethod);
}
}

View File

@ -0,0 +1,78 @@
package com.huanchengfly.tieba.post.widgets.theme;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import com.huanchengfly.tieba.post.R;
import com.huanchengfly.tieba.post.ui.theme.interfaces.Tintable;
import com.huanchengfly.tieba.post.ui.theme.utils.ColorStateListUtils;
import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils;
import com.huanchengfly.tieba.post.widgets.MySpannableTextView;
@SuppressLint("CustomViewStyleable")
public class TintMySpannableTextView extends MySpannableTextView implements Tintable {
private int mBackgroundTintResId;
private int mTintResId;
private int mTintListResId;
public TintMySpannableTextView(Context context) {
this(context, null);
}
public TintMySpannableTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TintMySpannableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (isInEditMode()) {
return;
}
if (attrs == null) {
mBackgroundTintResId = 0;
mTintResId = 0;
mTintListResId = 0;
applyTintColor();
return;
}
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.TintView, defStyleAttr, 0);
mBackgroundTintResId = array.getResourceId(R.styleable.TintView_backgroundTint, 0);
mTintResId = array.getResourceId(R.styleable.TintView_tint, 0);
mTintListResId = array.getResourceId(R.styleable.TintView_tintList, 0);
array.recycle();
applyTintColor();
}
public void setBackgroundTintResId(int mBackgroundTintResId) {
this.mBackgroundTintResId = mBackgroundTintResId;
applyTintColor();
}
public void setTintResId(int mTintResId) {
this.mTintResId = mTintResId;
applyTintColor();
}
private void applyTintColor() {
if (mBackgroundTintResId != 0) {
if (getBackground() == null) {
setBackgroundColor(ThemeUtils.getColorById(getContext(), mBackgroundTintResId));
} else {
setBackgroundTintList(ColorStateListUtils.createColorStateList(getContext(), mBackgroundTintResId));
}
}
if (mTintResId != 0 && mTintListResId == 0) {
setTextColor(ColorStateList.valueOf(ThemeUtils.getColorById(getContext(), mTintResId)));
} else if (mTintListResId != 0) {
setTextColor(ColorStateListUtils.createColorStateList(getContext(), mTintListResId));
}
}
@Override
public void tint() {
applyTintColor();
}
}