題目是這樣的,給一大段英文文字,要求實(shí)現(xiàn)的效果是點(diǎn)擊其中任意一個(gè)英文單詞會(huì)出來(lái)英文的釋義以及其他信息,這題目有兩個(gè)難點(diǎn),一個(gè)是,如何將一大段長(zhǎng)text分解成每一個(gè)獨(dú)立的并且添加點(diǎn)擊事件,第二個(gè)難點(diǎn),是如何自定義Snackbar實(shí)現(xiàn)內(nèi)容的提示,效果如下:
111.gif
自定義TextView
public class GetWordTextView extends TextView {
private CharSequence mText;
private BufferType mBufferType;
private OnWordClickListener mOnWordClickListener;
private SpannableString mSpannableString;
private BackgroundColorSpan mSelectedBackSpan;
private ForegroundColorSpan mSelectedForeSpan;
private int highlightColor;
private String highlightText;
private int selectedColor;
private int language;//0:english,1:chinese
public GetWordTextView(Context context) {
this(context, null);
}
public GetWordTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GetWordTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, me.solidev.getwordtextview.R.styleable.GetWordTextView);
highlightColor = ta.getColor(me.solidev.getwordtextview.R.styleable.GetWordTextView_highlightColor, Color.RED);
highlightText = ta.getString(me.solidev.getwordtextview.R.styleable.GetWordTextView_highlightText);
selectedColor = ta.getColor(me.solidev.getwordtextview.R.styleable.GetWordTextView_selectedColor, Color.BLUE);
language = ta.getInt(me.solidev.getwordtextview.R.styleable.GetWordTextView_language, 0);
ta.recycle();
}
@Override
public void setText(CharSequence text, BufferType type) {
this.mText = text;
mBufferType = type;
setHighlightColor(Color.TRANSPARENT);
setMovementMethod(LinkMovementMethod.getInstance());//沒(méi)有這句話沒(méi)有點(diǎn)擊效果
setText();
}
private void setText() {
mSpannableString = new SpannableString(mText);//肯定是利用SpannableString設(shè)置符合文本 下面是關(guān)于SpannableString的一些屬性設(shè)置
setHighLightSpan(mSpannableString);
//下面處理文字的點(diǎn)擊事件
if (language == 0) {
dealEnglish();
} else {
dealChinese();
}
super.setText(mSpannableString, mBufferType);
}
private void dealChinese() {
for (int i = 0; i < mText.length(); i++) {
char ch = mText.charAt(i);
if (Utils.isChinese(ch)) {
mSpannableString.setSpan(getClickableSpan(), i, i + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private void dealEnglish() {
List<WordInfo> wordInfoList = Utils.getEnglishWordIndices(mText.toString());//將輸入的英文文本解析出一個(gè)包含WordInfo的list
for (WordInfo wordInfo : wordInfoList) {
mSpannableString.setSpan(getClickableSpan(), wordInfo.getStart(), wordInfo.getEnd(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
private void setHighLightSpan(SpannableString spannableString) {
if (TextUtils.isEmpty(highlightText)) {
return;
}
int hIndex = mText.toString().indexOf(highlightText);
while (hIndex != -1) {
spannableString.setSpan(new ForegroundColorSpan(highlightColor), hIndex, hIndex + highlightText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
hIndex += highlightText.length();
hIndex = mText.toString().indexOf(highlightText, hIndex);
}
}
private void setSelectedSpan(TextView tv) {
if (mSelectedBackSpan == null || mSelectedForeSpan == null) {
mSelectedBackSpan = new BackgroundColorSpan(selectedColor);
mSelectedForeSpan = new ForegroundColorSpan(Color.WHITE);
} else {
mSpannableString.removeSpan(mSelectedBackSpan);
mSpannableString.removeSpan(mSelectedForeSpan);
}
mSpannableString.setSpan(mSelectedBackSpan, tv.getSelectionStart(), tv.getSelectionEnd(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mSpannableString.setSpan(mSelectedForeSpan, tv.getSelectionStart(), tv.getSelectionEnd(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
GetWordTextView.super.setText(mSpannableString, mBufferType);
}
public void dismissSelected() {
mSpannableString.removeSpan(mSelectedBackSpan);
mSpannableString.removeSpan(mSelectedForeSpan);
GetWordTextView.super.setText(mSpannableString, mBufferType);
}
/**
* 重寫(xiě)點(diǎn)擊事件
* @return
*/
private ClickableSpan getClickableSpan() {
return new ClickableSpan() {
@Override
public void onClick(View widget) {
TextView tv = (TextView) widget;
String word = tv.getText().toString().trim().subSequence(tv.getSelectionStart(), tv.getSelectionEnd()).toString();
setSelectedSpan(tv);
if (mOnWordClickListener != null) {
mOnWordClickListener.onClick(word);
}
}
@Override
public void updateDrawState(TextPaint ds) {
}
};
}
public void setOnWordClickListener(OnWordClickListener listener) {
this.mOnWordClickListener = listener;
}
public void setHighLightText(String text) {
highlightText = text;
}
public void setHighLightColor(int color) {
highlightColor = color;
}
public interface OnWordClickListener {
void onClick(String word);
}
}
里面有一個(gè)很重要的工具類(lèi),將文本分割
class Utils {
private static List<Character> sPunctuations;
static {
Character[] arr = new Character[]{',', '.', ';', '!', '"', '魏颓,', '檀训。', '峻村!', ';', '、', ':', '“', '”','?','瞳脓?'};
sPunctuations = Arrays.asList(arr);
}
static boolean isChinese(char ch) {
return !sPunctuations.contains(ch);
}
@NonNull
static List<WordInfo> getEnglishWordIndices(String content) {
List<Integer> separatorIndices = getSeparatorIndices(content, ' ');//去除開(kāi)頭空格,然后將剩下的英文內(nèi)容轉(zhuǎn)化為集合
for (Character punctuation : sPunctuations) {
separatorIndices.addAll(getSeparatorIndices(content, punctuation));
}
Collections.sort(separatorIndices);
List<WordInfo> wordInfoList = new ArrayList<>();
int start = 0;
int end;
for (int i = 0; i < separatorIndices.size(); i++) {//判定買(mǎi)一個(gè)單詞是從哪一個(gè)開(kāi)始哪一個(gè)結(jié)束
end = separatorIndices.get(i);
if (start == end) {
start++;
} else {
WordInfo wordInfo = new WordInfo();
wordInfo.setStart(start);
wordInfo.setEnd(end);
wordInfoList.add(wordInfo);
start = end + 1;
}
}
return wordInfoList;
}
/**
* 獲取每一個(gè)字,轉(zhuǎn)化成集合
*
* @param word the content
* @param ch separate char
* @return index array
*/
private static List<Integer> getSeparatorIndices(String word, char ch) {
int pos = word.indexOf(ch);
List<Integer> indices = new ArrayList<>();
while (pos != -1) {
indices.add(pos);
pos = word.indexOf(ch, pos + 1);
}
return indices;
}
}
bean類(lèi)WordInfo:
public class WordInfo {
private int start;
private int end;
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
}
在attrs中的
<declare-styleable name="GetWordTextView">
<attr name="highlightColor" format="reference|color" />
<attr name="highlightText" format="reference|string" />
<attr name="selectedColor" format="reference|color" />
<attr name="language">
<enum name="English" value="0" />
<enum name="Chinese" value="1" />
</attr>
</declare-styleable>
這個(gè)自定義TextView寫(xiě)好之后就方便多了,用法如下:
<getword.orchid.com.myapplication.view.GetWordTextView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/english_get_word_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
app:highlightColor="@color/colorAccent"
app:language="English"
app:selectedColor="@color/colorPrimary"/>
其實(shí)和TextView的用法一樣,就是多了幾個(gè)屬性,用于點(diǎn)擊時(shí)和內(nèi)容的判定.
在MainActivity中這樣使用:
mEnglishGetWordTextView.setText(text_info);
mEnglishGetWordTextView.setOnWordClickListener(new GetWordTextView.OnWordClickListener() {
@Override
public void onClick(final String word) {
//點(diǎn)擊之后會(huì)獲取到對(duì)應(yīng)的單詞
}
});
關(guān)于Snackbar本來(lái)也想自定義的,后來(lái)發(fā)現(xiàn)
public final class Snackbar extends BaseTransientBottomBar<Snackbar>
重寫(xiě)不了,那只能用笨方法自定義了
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "", Snackbar.LENGTH_INDEFINITE);
View snackbarview = snackbar.getView();
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) snackbarview;
View add_view = LayoutInflater.from(snackbarview.getContext()).inflate(R.layout.snackbar_addview, null);//加載布局文件新建View
TextView tv_word = (TextView) add_view.findViewById(R.id.tv_word);
TextView tv_phonogram = (TextView) add_view.findViewById(R.id.tv_phonogram);
TextView tv_info_usa = (TextView) add_view.findViewById(R.id.tv_info_usa);
TextView tv_info_uk = (TextView) add_view.findViewById(R.id.tv_info_uk);
tv_word.setText(word);
tv_phonogram.setText(result.getData().getCn_definition().getDefn());
tv_info_usa.setText("美:[" + result.getData().getPronunciations().getUs() + "]");
tv_info_uk.setText("英:[" + result.getData().getPronunciations().getUk() + "]");
.
.
.
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);//設(shè)置新建布局參數(shù)
p.gravity = Gravity.CENTER_VERTICAL;//設(shè)置新建布局在Snackbar內(nèi)垂直居中顯示
snackbarLayout.addView(add_view, 0, p);
snackbar.show();
在找關(guān)于Snackbar 的資料的時(shí)候發(fā)現(xiàn)了一個(gè)很有用的Snackbar 的工具類(lèi)
public class SnackbarUtil {
public static final int Info = 1;
public static final int Confirm = 2;
public static final int Warning = 3;
public static final int Alert = 4;
public static int red = 0xfff44336;
public static int green = 0xff4caf50;
public static int blue = 0xff2195f3;
public static int orange = 0xffffc107;
/**
* 短顯示Snackbar缺亮,自定義顏色
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
public static Snackbar ShortSnackbar(View view, String message, int messageColor, int backgroundColor){
Snackbar snackbar = Snackbar.make(view,message, Snackbar.LENGTH_SHORT);
setSnackbarColor(snackbar,messageColor,backgroundColor);
return snackbar;
}
/**
* 長(zhǎng)顯示Snackbar,自定義顏色
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
public static Snackbar LongSnackbar(View view, String message, int messageColor, int backgroundColor){
Snackbar snackbar = Snackbar.make(view,message, Snackbar.LENGTH_LONG);
setSnackbarColor(snackbar,messageColor,backgroundColor);
return snackbar;
}
/**
* 自定義時(shí)常顯示Snackbar晚吞,自定義顏色
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
public static Snackbar IndefiniteSnackbar(View view, String message, int duration, int messageColor, int backgroundColor){
Snackbar snackbar = Snackbar.make(view,message, Snackbar.LENGTH_INDEFINITE).setDuration(duration);
setSnackbarColor(snackbar,messageColor,backgroundColor);
return snackbar;
}
/**
* 短顯示Snackbar延旧,可選預(yù)設(shè)類(lèi)型
* @param view
* @param message
* @param type
* @return
*/
public static Snackbar ShortSnackbar(View view, String message, int type){
Snackbar snackbar = Snackbar.make(view,message, Snackbar.LENGTH_SHORT);
switchType(snackbar,type);
return snackbar;
}
/**
* 長(zhǎng)顯示Snackbar,可選預(yù)設(shè)類(lèi)型
* @param view
* @param message
* @param type
* @return
*/
public static Snackbar LongSnackbar(View view, String message, int type){
Snackbar snackbar = Snackbar.make(view,message, Snackbar.LENGTH_LONG);
switchType(snackbar,type);
return snackbar;
}
/**
* 自定義時(shí)常顯示Snackbar槽地,可選預(yù)設(shè)類(lèi)型
* @param view
* @param message
* @param type
* @return
*/
public static Snackbar IndefiniteSnackbar(View view, String message, int duration, int type){
Snackbar snackbar = Snackbar.make(view,message, Snackbar.LENGTH_INDEFINITE).setDuration(duration);
switchType(snackbar,type);
return snackbar;
}
//選擇預(yù)設(shè)類(lèi)型
private static void switchType(Snackbar snackbar,int type){
switch (type){
case Info:
setSnackbarColor(snackbar,blue);
break;
case Confirm:
setSnackbarColor(snackbar,green);
break;
case Warning:
setSnackbarColor(snackbar,orange);
break;
case Alert:
setSnackbarColor(snackbar, Color.YELLOW,red);
break;
}
}
/**
* 設(shè)置Snackbar背景顏色
* @param snackbar
* @param backgroundColor
*/
public static void setSnackbarColor(Snackbar snackbar, int backgroundColor) {
View view = snackbar.getView();
if(view!=null){
view.setBackgroundColor(backgroundColor);
}
}
/**
* 設(shè)置Snackbar文字和背景顏色
* @param snackbar
* @param messageColor
* @param backgroundColor
*/
public static void setSnackbarColor(Snackbar snackbar, int messageColor, int backgroundColor) {
View view = snackbar.getView();
if(view!=null){
view.setBackgroundColor(backgroundColor);
((TextView) view.findViewById(R.id.snackbar_text)).setTextColor(messageColor);
}
}
/**
* 向Snackbar中添加view
* @param snackbar
* @param layoutId
* @param index 新加布局在Snackbar中的位置
*/
public static void SnackbarAddView( Snackbar snackbar,int layoutId,int index) {
View snackbarview = snackbar.getView();
Snackbar.SnackbarLayout snackbarLayout=(Snackbar.SnackbarLayout)snackbarview;
View add_view = LayoutInflater.from(snackbarview.getContext()).inflate(layoutId,null);
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
p.gravity= Gravity.CENTER_VERTICAL;
snackbarLayout.addView(add_view,index,p);
}
}
關(guān)于播放單詞的讀音,發(fā)音是來(lái)自于網(wǎng)絡(luò)的url,用了最簡(jiǎn)單的方法,如下:
MediaPlayer mediaPlayer = new MediaPlayer();
stopMedia(mediaPlayer);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);// 設(shè)置媒體流類(lèi)型
try {
mediaPlayer.setDataSource(result.getData().getUs_audio());
mediaPlayer.prepareAsync();//異步的準(zhǔn)備
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
github地址: https://github.com/orchidTJJ/getword