匹配表情、关键字、Url的Textview

本文介绍了一个Android应用案例,实现输入文字的同时插入表情符号,并通过正则表达式处理URL链接及关键词高亮显示等功能。

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

源码Demo下载

http://download.csdn.net/detail/u014620028/9609661

注:在Edittext和Textview中,不要加下面2个属性中的任何一种。否则,当行数大于1行以后会发生表情、图片对不齐的情况

android:lineSpacingExtra=""
android:lineSpacingMultiplier=""

效果图:
输入:
这里写图片描述
展示效果:
这里写图片描述

注:为了举例,把“我”当成关键字,“你”当成要凸显出来的字。比如搜索“你”,在结果中,把“你”字变红


把下面的代码,都复制到自己项目下,改改包名什么的,就可以直接用了
代码:
1、布局文件
activity_main_12:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#ffffff"
                android:fitsSystemWindows="true"
                android:orientation="vertical">


    <EditText
        android:id="@+id/et_word_message"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_margin="8dp"
        android:layout_marginTop="10dp"
        android:background="#dedede"
        android:gravity="top|left"
        android:hint="输入内容"
        android:maxLength="300"
        android:textSize="20sp"
        />

    <TextView
        android:id="@+id/show_result_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/et_word_message"
        android:layout_marginTop="5dp"
        android:background="#00ff00"
        android:gravity="center"
        android:padding="5dp"
        android:text="显示"
        android:textSize="20sp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_gravity="bottom"
        android:orientation="vertical"
        android:paddingBottom="5dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#dedede">

            <ImageView
                android:id="@+id/iv_expression"
                android:layout_width="25dp"
                android:layout_height="25dp"
                android:layout_alignParentLeft="true"
                android:layout_marginLeft="10dp"/>

            <TextView
                android:id="@+id/tv_show_residue_num"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:layout_marginRight="10dp"
                android:text=""
                android:textColor="#999999"
                android:textSize="17sp"
                />
        </RelativeLayout>

        <android.support.v4.view.ViewPager
            android:id="@+id/show_emoji_viewPager"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            android:visibility="gone"/>
    </LinearLayout>

</RelativeLayout>

emoji_gridview:

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridView_Expression"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:gravity="center"
    android:background="#ffffff"
    android:numColumns="7">

</GridView>

restul_layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp"
    >

    <com.chen.customviewdemo.view.MyTextView
        android:id="@+id/result_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:text=""
        android:textSize="20sp"/>
</RelativeLayout>

工具等:
Expressions:

package com.chen.customviewdemo.view;


import com.chen.customviewdemo.R;

import java.util.ArrayList;
import java.util.List;

/**
 * 处理表情
 */
public class Expressions {

    public static List<int[]> emojiIdList = new ArrayList<int[]>();
    public static List<String[]> emojiNameList = new ArrayList<String[]>();

    public static final int[] emojID = new int[]{R.mipmap.emoji_0, R.mipmap.emoji_1, R.mipmap.emoji_2,
            R.mipmap.emoji_3, R.mipmap.emoji_4, R.mipmap.emoji_5, R.mipmap.emoji_6, R.mipmap.emoji_7,
            R.mipmap.emoji_8, R.mipmap.emoji_9, R.mipmap.emoji_10, R.mipmap.emoji_11, R.mipmap.emoji_12,
            R.mipmap.emoji_13, R.mipmap.emoji_14, R.mipmap.emoji_15, R.mipmap.emoji_16, R.mipmap.emoji_17,
            R.mipmap.emoji_18, R.mipmap.emoji_19, R.mipmap.emoji_20, R.mipmap.emoji_21, R.mipmap.emoji_22,
            R.mipmap.emoji_23, R.mipmap.emoji_24, R.mipmap.emoji_25, R.mipmap.emoji_26, R.mipmap.emoji_27,
            R.mipmap.emoji_28, R.mipmap.emoji_29, R.mipmap.emoji_30, R.mipmap.emoji_31, R.mipmap.emoji_32,
            R.mipmap.emoji_33, R.mipmap.emoji_34, R.mipmap.emoji_35, R.mipmap.emoji_36, R.mipmap.emoji_37,
            R.mipmap.emoji_38, R.mipmap.emoji_39, R.mipmap.emoji_40, R.mipmap.emoji_41, R.mipmap.emoji_42,
            R.mipmap.emoji_43, R.mipmap.emoji_44, R.mipmap.emoji_45, R.mipmap.emoji_46, R.mipmap.emoji_47,
            R.mipmap.emoji_48, R.mipmap.emoji_49, R.mipmap.emoji_50, R.mipmap.emoji_51, R.mipmap.emoji_52,
            R.mipmap.emoji_53, R.mipmap.emoji_54, R.mipmap.emoji_55, R.mipmap.emoji_56, R.mipmap.emoji_57,
            R.mipmap.emoji_58, R.mipmap.emoji_59, R.mipmap.emoji_60, R.mipmap.emoji_61, R.mipmap.emoji_62,
            R.mipmap.emoji_63, R.mipmap.emoji_64, R.mipmap.emoji_65, R.mipmap.emoji_66,
            R.mipmap.emoji_67, R.mipmap.emoji_68, R.mipmap.emoji_69, R.mipmap.emoji_70, R.mipmap.emoji_71,
            R.mipmap.emoji_72, R.mipmap.emoji_73, R.mipmap.emoji_74, R.mipmap.emoji_75, R.mipmap.emoji_76,
            R.mipmap.emoji_77, R.mipmap.emoji_78, R.mipmap.emoji_79, R.mipmap.emoji_80, R.mipmap.emoji_81,
            R.mipmap.emoji_82, R.mipmap.emoji_83, R.mipmap.emoji_84, R.mipmap.emoji_85, R.mipmap.emoji_86,
            R.mipmap.emoji_87, R.mipmap.emoji_88, R.mipmap.emoji_89, R.mipmap.emoji_90, R.mipmap.emoji_91,
            R.mipmap.emoji_92, R.mipmap.emoji_93, R.mipmap.emoji_94, R.mipmap.emoji_95, R.mipmap.emoji_96,
            R.mipmap.emoji_97, R.mipmap.emoji_98, R.mipmap.emoji_99, R.mipmap.emoji_100, R.mipmap.emoji_101,
            R.mipmap.emoji_102, R.mipmap.emoji_103, R.mipmap.emoji_104};

    public static final String[] emojName = new String[]{"[/微笑]", "[/撇嘴]", "[/色]", "[/发呆]",
            "[/得意]", "[/流泪]", "[/害羞]", "[/闭嘴]", "[/睡]", "[/大哭]", "[/尴尬]", "[/发怒]",
            "[/调皮]", "[/呲牙]", "[/惊讶]", "[/难过]", "[/酷]", "[/冷汗]", "[/抓狂]", "[/吐]",
            "[/偷笑]", "[/可爱]", "[/白眼]", "[/傲慢]", "[/饥饿]", "[/困]", "[/惊恐]", "[/流汗]",
            "[/憨笑]", "[/大兵]", "[/奋斗]", "[/咒骂]", "[/疑问]", "[/嘘...]", "[/晕]", "[/折磨]",
            "[/衰]", "[/骷髅]", "[/敲打]", "[/再见]", "[/擦汗]", "[/抠鼻]", "[/鼓掌]", "[/糗大了]",
            "[/坏笑]", "[/左哼哼]", "[/右哼哼]", "[/哈欠]", "[/鄙视]", "[/委屈]", "[/快哭了]", "[/阴险]",
            "[/亲亲]", "[/吓]", "[/可怜]", "[/菜刀]", "[/西瓜]", "[/啤酒]", "[/篮球]", "[/乒乓]", "[/咖啡]",
            "[/饭]", "[/猪头]", "[/玫瑰]", "[/凋谢]", "[/示爱]", "[/爱心]", "[/心碎]", "[/蛋糕]", "[/闪电]",
            "[/炸弹]", "[/刀]", "[/足球]", "[/瓢虫]", "[/便便]", "[/月亮]", "[/太阳]", "[/礼物]", "[/拥抱]",
            "[/强]", "[/弱]", "[/握手]", "[/胜利]", "[/抱拳]", "[/勾引]", "[/拳头]", "[/差劲]", "[/爱你]",
            "[/NO]", "[/OK]", "[/爱情]", "[/飞吻]", "[/跳跳]", "[/发抖]", "[/怄火]", "[/转圈]", "[/磕头]",
            "[/回头]", "[/跳绳]", "[/挥手]", "[/激动]", "[/街舞]", "[/献吻]", "[/左太极]", "[/右太极]"};

    static {
        emojiIdList.add(emojID);
        emojiNameList.add(emojName);
    }

    /**
     * 由表情名称获得表情图片id
     *
     * @param name
     * @return
     */
    public static int getIdAsName(String name) throws NullPointerException {
        for (int i = 0; i < emojName.length; i++) {
            if (emojName[i].equals(name)) {
                return emojID[i];
            }
        }
        return 0;
    }

}

EmotionBean

package com.chen.customviewdemo.bean;

/**
 * 表情bean
 */
public class EmotionBean {

    private int Id; // 表情对应的Id
    private String Name; // 表情对应的名字

    public int getId() {
        return Id;
    }

    public void setId(int id) {
        Id = id;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

}

KeyBean

package com.chen.customviewdemo.bean;

/**
 * 关键字bean。
 * 包含显式内容和隐式内容。
 * 如:在界面上显示“百度”,点击了它,就去一个界面打开百度的网页。要打开网页,肯定需要Url。这就需要,在点击的时候,把Url传递过去。
 * 这个时候,这个Url,就是隐式内容了。不必让用户看到,只是在触发后,隐含的做一定操作就行
 */
public class KeyBean {

    public KeyBean(String content, String hide_content) {
        this.content = content;
        this.hide_content = hide_content;
    }

    /**
     * 内容
     */
    public String content;

    /**
     * 隐藏的内容,点击内容后,要传递的值或者要做的事
     */
    public String hide_content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getHide_content() {
        return hide_content;
    }

    public void setHide_content(String hide_content) {
        this.hide_content = hide_content;
    }


}

ExpressionUtil

package com.chen.customviewdemo.utils;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;

import com.chen.customviewdemo.view.Expressions;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExpressionUtil {

    public static SpannableString getSpannableString(String str, Context context, int size) {
        SpannableString spannableString = new SpannableString(str);
        String string = "\\[(.+?)\\]";
        Pattern patten = Pattern.compile(string, Pattern.CASE_INSENSITIVE);
        try {
            dealSpannableString(context, spannableString, patten, 0, size);
        } catch (Exception e) {
        }
        return spannableString;
    }

    private static void dealSpannableString(Context context, SpannableString spannableString, Pattern patten, int start, int emojiSize) {
        Matcher matcher = patten.matcher(spannableString);
        while (matcher.find()) {
            String key = matcher.group();
            if (matcher.start() < start) {
                continue;
            }
            int id = Expressions.getIdAsName(key);
            if (id != 0) {
                Drawable drawable = context.getResources().getDrawable(id);
                drawable.setBounds(0, 0, Utils.sp2px(context, emojiSize), Utils.sp2px(context, emojiSize));
                ImageSpan imgSpan = new ImageSpan(drawable);
                int end = matcher.start() + key.length();
                spannableString.setSpan(imgSpan, matcher.start(), end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                if (end < spannableString.length()) {                        //如果整个字符串还未验证完,则继续。。
                    dealSpannableString(context, spannableString, patten, end, emojiSize);
                }
                break;
            }
        }
    }
}

Utils

package com.chen.customviewdemo.utils;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import android.util.DisplayMetrics;

/**
 * 工具类
 */
public class Utils {

    /**
     * 检查字符串是否是空
     *
     * @param str
     * @return true:字符串为空
     */
    public static boolean checkStringIsEmpty(String str) {

        if (TextUtils.isEmpty(str) || "null".equals(str)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 将sp值转换为px值
     */
    public static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

}

CHEN

package com.chen.customviewdemo.utils;

/**
 * 用于保存全局变量值
 */
public class CHEN {

    /**
     * 要被传递的文字
     */
    public static String Str = "";

    /**
     * 要变红的字。一般用于搜索,如:把搜索的字变为红色用于突出显示
     */
    public static String toRedWord = "";

    /**
     * 网址要被替换成的文字
     */
    public static String REPLACEMENT_STRING = "*点击链接";

    /**
     * 匹配网址的正则表达式。以http://为例
     */
    public static String urlRegex = "(http://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)";

    /**
     * 匹配网址的正则表达式。有http://、https://、ftp://这3中开头的
     */
    //    public static String urlRegex = "((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)";


}

EmotionGridViewAdapter

package com.chen.customviewdemo.adapter;

import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;

import com.chen.customviewdemo.R;
import com.chen.customviewdemo.bean.EmotionBean;
import com.chen.customviewdemo.view.Expressions;

import java.util.ArrayList;
import java.util.List;

/**
 * 每一个表情页的adapter
 */
public class EmotionGridViewAdapter extends BaseAdapter {

    private List<EmotionBean> list;
    private Context context;
    private String tag;

    public EmotionGridViewAdapter(Context context, int page) {
        this.context = context;
        this.list = new ArrayList<EmotionBean>();
        setData1(page);
    }

    private void setData1(int page) {
        switch (page) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
                int len = 20;  // 每页显示20个表情,一个删除键
                for (int i = 0 + (len * page); i < len + (len * page); i++) {
                    EmotionBean bean = new EmotionBean();
                    bean.setId(Expressions.emojID[i]);
                    bean.setName(Expressions.emojName[i]);
                    list.add(bean);
                }
                EmotionBean bean = new EmotionBean();
                bean.setId(R.mipmap.emoji_del);
                list.add(bean);
                break;
            case 5:
                // 最后一页,显示5个表情和一个删除键
                for (int i = 100; i < 105; i++) {
                    EmotionBean bean1 = new EmotionBean();
                    bean1.setId(Expressions.emojID[i]);
                    bean1.setName(Expressions.emojName[i]);
                    list.add(bean1);
                }
                EmotionBean bean1 = new EmotionBean();
                bean1.setId(R.mipmap.emoji_del);
                list.add(bean1);
                break;
        }
    }


    public void setData(List<EmotionBean> list) {
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final EmotionBean bean = list.get(position);
        int itemWidth = getWindowPX();
        int padding = itemWidth / 6;
        ImageView iv = new ImageView(context);
        LayoutParams params = new LayoutParams(itemWidth, itemWidth);
        iv.setLayoutParams(params);
        iv.setScaleType(ScaleType.FIT_CENTER);
        iv.setPadding(padding, 0, padding, 0);
        iv.setImageResource(bean.getId());
        return iv;
    }


    private int getWindowPX() {
        Activity activity;
        if (context instanceof Activity) {
            activity = (Activity) context;
            DisplayMetrics dm = new DisplayMetrics();
            activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
            int width = dm.widthPixels;
            return width / 8;
        } else {
            return 200;
        }
    }


}

ShowEmojiViewPagerAdapter

package com.chen.customviewdemo.adapter;

import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;

import java.util.List;

/**
 * 展示表情的viewpager的adapter
 */
public class ShowEmojiViewPagerAdapter extends PagerAdapter {

    private List<GridView> mGridlist;

    public ShowEmojiViewPagerAdapter(List<GridView> mList) {
        super();
        this.mGridlist = mList;
    }

    @Override
    public int getCount() {
        return mGridlist.size();
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0==arg1;
    }

    @Override
    public void destroyItem(View container, int position, Object object) {
        ((ViewGroup)container).removeView(mGridlist.get(position));
    }


    @Override
    public Object instantiateItem(View container, int position) {

        ((ViewGroup)container).addView(mGridlist.get(position));

        return mGridlist.get(position);
    }


}

MyTextView

package com.chen.customviewdemo.view;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.chen.customviewdemo.R;
import com.chen.customviewdemo.bean.KeyBean;
import com.chen.customviewdemo.utils.CHEN;
import com.chen.customviewdemo.utils.ExpressionUtil;
import com.chen.customviewdemo.utils.Utils;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 自定义textview,用于处理url、表情、文字等
 */
public class MyTextView extends TextView {
    private Context context = null;

    public MyTextView(Context context, AttributeSet attr) {
        super(context, attr);
        this.context = context;
    }


    public MyTextView(Context context) {
        super(context);
        this.context = context;
    }

    /**
     * TextView显示有颜色的字,并添加点击事件,适用于一段文字中有多个内容要处理
     */
    public void handleText(String str, final ArrayList<KeyBean> keyBeanList, int size) {
        String content = str;

        //处理匹配的url
        Pattern p = Pattern.compile(CHEN.urlRegex);
        Matcher m = p.matcher(content);
        ArrayList<String> urlList = new ArrayList<String>();
        while (m.find()) {
            String urlStr = m.group();
            if (urlStr.contains("http://")) {
                //如果末尾有英文逗号或者中文逗号等,就去掉
                while (urlStr.endsWith(",") || urlStr.endsWith(",") || urlStr.endsWith(".") || urlStr.endsWith("。") || urlStr.endsWith(";") || urlStr.endsWith(";") || urlStr.endsWith("!") || urlStr.endsWith("!") || urlStr.endsWith("?") || urlStr.endsWith("?")) {
                    urlStr = urlStr.substring(0, urlStr.length() - 1);
                }
                urlList.add(urlStr);
                content = content.replace(urlStr, CHEN.REPLACEMENT_STRING);
            }
        }

        final SpannableString spannableString = ExpressionUtil.getSpannableString(content, context, size);
        content = spannableString.toString();

        //处理链接
        if (urlList.size() > 0) {

            int urlStartNew = 0;
            int urlStartOld = 0;

            String urlTemp = content;

            for (int i = 0; i < urlList.size(); i++) {

                final String regexUrl = urlList.get(i);

                spannableString.setSpan(new ClickableSpan() {

                                            @Override
                                            public void updateDrawState(TextPaint ds) {
                                                // TODO Auto-generated method stub
                                                super.updateDrawState(ds);
                                                ds.setColor(context.getResources().getColor(R.color.linkcolor));
                                                ds.setUnderlineText(false);
                                            }

                                            @Override
                                            public void onClick(View widget) {
                                                Toast.makeText(context, regexUrl, Toast.LENGTH_SHORT).show();
                                            }

                                        }, urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + CHEN.REPLACEMENT_STRING.length(),
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                try {
                    //“点击链接”前面的回形针图片。大小可自己调整
                    Drawable drawable = context.getResources().getDrawable(R.mipmap.web_link);
                    drawable.setBounds(0, 0, Utils.sp2px(context, size), Utils.sp2px(context, size));
                    ImageSpan imgSpan = new ImageSpan(drawable);
                    spannableString.setSpan(imgSpan, urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

                } catch (Exception e) {
                    //异常以后,就不加小图片了
                }

                setText(spannableString);
                setMovementMethod(LinkMovementMethod.getInstance());

                urlStartNew = urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + CHEN.REPLACEMENT_STRING.length();
                urlStartOld += urlStartNew;
                urlTemp = urlTemp.substring(urlStartNew);
            }
        }

        //处理文本中的关键字
        if (keyBeanList != null && keyBeanList.size() > 0) {
            for (int i = 0; i < keyBeanList.size(); i++) {
                final String data = keyBeanList.get(i).getContent();
                final String hide_content = keyBeanList.get(i).getHide_content();
                String temp = content;
                int startNew = 0;
                int startOld = 0;
                if (temp.contains(data)) {
                    while (temp.contains(data)) {
                        spannableString.setSpan(new ClickableSpan() {
                                                    @Override
                                                    public void updateDrawState(TextPaint ds) {
                                                        // TODO Auto-generated method stub
                                                        super.updateDrawState(ds);
                                                        ds.setColor(context.getResources().getColor(R.color.linkcolor));
                                                        ds.setUnderlineText(false);
                                                    }

                                                    @Override
                                                    public void onClick(View widget) {
                                                        Toast.makeText(context, hide_content, Toast.LENGTH_SHORT).show();
                                                    }

                                                }, startOld + temp.indexOf(data), startOld + temp.indexOf(data) + data.length(),
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                        setText(spannableString);
                        setMovementMethod(LinkMovementMethod.getInstance());
                        startNew = temp.indexOf(data) + data.length();
                        startOld += startNew;
                        temp = temp.substring(startNew);
                    }
                } else {
                    setText(spannableString);
                }
            }

        } else {
            setText(spannableString);
        }

        //处理要变红的字
        if (!TextUtils.isEmpty(CHEN.toRedWord)) {
            //只要把关键字变红就行了,不需要加点击事件。如果需要,可以自己加
            String temp = content;
            int startNew = 0;
            int startOld = 0;
            while (temp.contains(CHEN.toRedWord)) {
                spannableString.setSpan(new ClickableSpan() {

                                            @Override
                                            public void updateDrawState(TextPaint ds) {
                                                // TODO Auto-generated method stub
                                                super.updateDrawState(ds);
                                                ds.setColor(0xffff0000);
                                                ds.setUnderlineText(false);
                                            }

                                            @Override
                                            public void onClick(View widget) {
                                            }

                                        }, startOld + temp.indexOf(CHEN.toRedWord), startOld + temp.indexOf(CHEN.toRedWord) + CHEN.toRedWord.length(),
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                setText(spannableString);
                setMovementMethod(LinkMovementMethod.getInstance());
                startNew = temp.indexOf(CHEN.toRedWord) + CHEN.toRedWord.length();
                startOld += startNew;
                temp = temp.substring(startNew);
            }
        } else {
            setText(spannableString);
        }
    }
}

Activity中使用
BaseActivity

package com.chen.customviewdemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;

/**
 * 基础Activity
 */
public abstract class BaseActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //不显示title
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        initview();
    }

    abstract void initview();

}

MainActivity_12

package com.chen.customviewdemo;

import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.support.v4.view.ViewPager;
import android.text.Editable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;

import com.chen.customviewdemo.adapter.EmotionGridViewAdapter;
import com.chen.customviewdemo.adapter.ShowEmojiViewPagerAdapter;
import com.chen.customviewdemo.utils.CHEN;
import com.chen.customviewdemo.utils.ExpressionUtil;
import com.chen.customviewdemo.view.Expressions;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 输入文字和表情的界面
 */
public class MainActivity_12 extends BaseActivity implements View.OnClickListener {

    private Context mContext;
    // 表情
    private ImageView iv_expression;
    // 显示表情的ViewPager
    private ViewPager show_emoji_viewPager;
    // 装载表情的viewPager的Adapter
    private ShowEmojiViewPagerAdapter showEmojiViewPagerAdapter;

    /**
     * 装载表情的viewPager的Adapter的每一页,都是一个gridview,这些gridview,用集合管理起来
     */
    private List<GridView> mGridList;

    /**
     * 文本输入框
     */
    private EditText et_word_message;
    /**
     * 当前是否显示表情键盘
     */
    private boolean isShowEmotion = false;
    private final String reg = "^[\u4e00-\u9fa5\u0020-\u007E\uFE30-\uFFA0。、……“”‘’《》——¥~]*$";
    private Pattern pattern = Pattern.compile(reg);
    /**
     * 显示动态的剩余字数
     */
    private TextView tv_show_residue_num;
    /**
     * 动态内容剩余字数。以300为例
     */
    private int dynamicContentResidueNum = 300;

    /**
     * 展示输入结果的按钮
     */
    private TextView show_result_tv;

    /**
     * 动态输入框的监听
     */
    private TextWatcher watcher = new TextWatcher() {

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

            String dynicContent = s.toString();
            int contentCount = dynicContent.length();

            if (TextUtils.isEmpty(dynicContent) || contentCount < 250) {
                tv_show_residue_num.setText("");
                dynamicContentResidueNum = 300;
            } else if (contentCount >= 250 && contentCount <= dynamicContentResidueNum) {
                int tempCount = dynamicContentResidueNum - contentCount;
                tv_show_residue_num.setText("还能输入" + tempCount + "字");
            } else if (contentCount > dynamicContentResidueNum) {
                tv_show_residue_num.setText(dynamicContentResidueNum - contentCount + "");
            }
        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    };


    @Override
    void initview() {
        setContentView(R.layout.activity_main_12);

        mContext = this;

        //默认。以此为例
        CHEN.toRedWord = "你";

        show_result_tv = (TextView) findViewById(R.id.show_result_tv);

        mGridList = new ArrayList<GridView>();

        mGridList.clear();

        tv_show_residue_num = (TextView) findViewById(R.id.tv_show_residue_num);

        iv_expression = (ImageView) findViewById(R.id.iv_expression);
        iv_expression.setBackgroundResource(R.mipmap.biao_qing);
        show_emoji_viewPager = (ViewPager) findViewById(R.id.show_emoji_viewPager);

        initGridViews();

        // 填充 表情的ViewPager的adapter
        showEmojiViewPagerAdapter = new ShowEmojiViewPagerAdapter(mGridList);
        show_emoji_viewPager.setAdapter(showEmojiViewPagerAdapter);

        et_word_message = (EditText) findViewById(R.id.et_word_message);

        et_word_message.addTextChangedListener(watcher);

        iv_expression.setOnClickListener(this);
        show_result_tv.setOnClickListener(this);
        et_word_message.setOnClickListener(this);

    }

    /**
     * 初始化表情键盘的Grideview
     */
    private void initGridViews() {
        mGridList = new ArrayList<GridView>();
        LayoutInflater inflater = LayoutInflater.from(mContext);
        mGridList.clear();
        for (int i = 0; i < 6; i++) {
            final int j = i;
            GridView gridView = (GridView) inflater.inflate(
                    R.layout.emoji_gridview, null);
            gridView.setAdapter(new EmotionGridViewAdapter(mContext, i));
            mGridList.add(gridView);
            gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                                        int position, long id) {
                    if (position == 20 || position == 40 || position == 60
                            || position == 80 || position == 100
                            || (j == 5 && position == 5)) {//处理删除键
                        int selectionStart = et_word_message.getSelectionStart();
                        String text = et_word_message.getText().toString();

                        if (!TextUtils.isEmpty(text)) {
                            String body = et_word_message.getText().toString();
                            String tempStr = body.substring(0, selectionStart);
                            int i = tempStr.lastIndexOf("]");// 获取最后一个表情的位置
                            if (i == tempStr.length() - 1) {// 说明光标刚好停在这个表情之后
                                int j = tempStr.lastIndexOf("[");// 将这两个之间的字符删掉
                                et_word_message.getEditableText().delete(j,
                                        selectionStart);
                            } else {
                                Matcher matcher = pattern.matcher(text);
                                if (!matcher.matches()) {
                                    new Thread(new Runnable() {

                                        @Override
                                        public void run() {
                                            Instrumentation instrumentation = new Instrumentation();
                                            instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DEL);
                                        }
                                    }) {
                                    }.start();
                                } else {
                                    et_word_message.getEditableText().delete(
                                            tempStr.length() - 1, selectionStart);
                                }
                            }
                        }
                    } else {
                        //表情会被转换成字符串。防止最后输入一个表情,超出字数显示
                        if ((300 - et_word_message.getText().toString().trim().length()) < 5) {
                            return;
                        }
                        String emotionString = Expressions.emojName[position
                                + j * 20];
                        String text = et_word_message.getText() + emotionString;
                        SpannableString spannableString = ExpressionUtil
                                .getSpannableString(text, mContext, 23);
                        et_word_message.setText(spannableString);
                        Editable b = et_word_message.getText();
                        et_word_message.setSelection(b.length());
                    }
                }
            });
        }
        showEmojiViewPagerAdapter = new ShowEmojiViewPagerAdapter(mGridList);
        show_emoji_viewPager.setAdapter(showEmojiViewPagerAdapter);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv_expression:
                // 点击表情的按钮,弹出表情
                if (isShowEmotion) {
                    iv_expression.setBackgroundResource(R.mipmap.biao_qing);
                    show_emoji_viewPager.setVisibility(View.GONE);
                    showKeyBoard();
                } else if (!isShowEmotion) {
                    iv_expression.setBackgroundResource(R.mipmap.keyboard);
                    show_emoji_viewPager.setVisibility(View.VISIBLE);
                    hideKeyBoard(v);
                }
                isShowEmotion = !isShowEmotion;
                break;
            case R.id.et_word_message:
                if (isShowEmotion) {
                    iv_expression.setBackgroundResource(R.mipmap.biao_qing);
                    show_emoji_viewPager.setVisibility(View.GONE);
                    forceShowKeyBoard(et_word_message);
                    isShowEmotion = !isShowEmotion;
                }
                break;
            case R.id.show_result_tv:
                //去展示结果的界面
                CHEN.Str = et_word_message.getText().toString().trim();
                startActivity(new Intent(this, ShowWordAndEmojiDataActivity.class));
                break;

            default:
                break;
        }
    }

    /**
     * 强制显示软键盘
     */
    public void forceShowKeyBoard(View v) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showSoftInput(v, InputMethodManager.SHOW_FORCED);
    }

    /**
     * 隐藏软键盘
     * @param v
     */
    public void hideKeyBoard(View v) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
    }

    /**
     * 显示软键盘
     */
    public void showKeyBoard() {
        InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        inputManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
    }
}

ShowWordAndEmojiDataActivity

package com.chen.customviewdemo;

import com.chen.customviewdemo.bean.KeyBean;
import com.chen.customviewdemo.utils.CHEN;
import com.chen.customviewdemo.view.MyTextView;

import java.util.ArrayList;

/**
 * 展示输入结果的Activity
 */
public class ShowWordAndEmojiDataActivity extends BaseActivity {

    MyTextView result_tv;

    ArrayList<KeyBean> keyBeanList;

    @Override
    void initview() {
        setContentView(R.layout.restul_layout);

        keyBeanList = new ArrayList<KeyBean>();
        //还有关键字的话,可以继续加
        keyBeanList.add(new KeyBean("我", "哈哈"));

        result_tv = (MyTextView) findViewById(R.id.result_tv);

        result_tv.handleText(CHEN.Str, keyBeanList, 23);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        CHEN.toRedWord = "";
    }
}

清单文件:

 <activity
            android:name=".MainActivity_12"
            android:configChanges="orientation|keyboardHidden"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustResize"/>
        <activity
            android:name=".ShowWordAndEmojiDataActivity"
            android:screenOrientation="portrait"/>
/* ========== Information.java ========== */ package com.example.kucun2.entity; import android.annotation.SuppressLint; import androidx.annotation.Keep; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; @Keep // 防止Proguard混淆 public class Information<T> { private Integer status; private String text; private T data; // 使用泛型保证类型安全 private static final Gson gson = new Gson(); // Gson实例复用 // 构造方法 public Information(Integer status, String text, T data) { this.status = status; this.text = text; this.data = data; } // JSON反序列化构造方法 @SuppressLint("NewApi") public Information(Integer status, String text, String jsonData, Type dataType) { this.status = status; this.text = text; try { this.data = gson.fromJson(jsonData, dataType); } catch (JsonSyntaxException e) { this.data = null; // 处理解析失败 } } // 空构造方法 public Information() {} // 序列化为JSON public String toJson() { return gson.toJson(this); } // 静态创建方法 public static <T> Information<T> newSuccess(T data) { return new Information<>(200, "success", data); } public static Information<String> newSuccess(String text) { return new Information<>(200, "success", text); } public static <T> Information<T> newFail(int status, String text, T data) { return new Information<>(status, text, data); } // Getter/Setter public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public String getText() { return text; } public void setText(String text) { this.text = text; } public T getData() { return data; } public void setData(T data) { this.data = data; } } /* ========== Jinhuo.java ========== */ package com.example.kucun2.entity; import java.util.Date; /** * 进货消耗类 */ public class Jinhuo { private Integer id; //属于哪个订单 private Dingdan dingdan; //属于哪个产品 private Chanpin chanpin; //属于哪个组件 private Zujian zujian; //进的什么板材 private Bancai bancai; //进货数量 private Integer shuliang; private Date date; private User user; public Jinhuo(Integer id, Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, Integer shuliang, Date date, User user) { super(); this.id = id; this.dingdan = dingdan; this.chanpin = chanpin; this.zujian = zujian; this.bancai = bancai; this.shuliang = shuliang; this.date = date; this.user = user; } public Jinhuo() { super(); // TODO Auto-generated constructor stub } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { this.zujian = zujian; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } /* ========== Kucun.java ========== */ package com.example.kucun2.entity; /** * 库存 */ public class Kucun { private Integer id; private Integer shuliang; private Bancai bancai; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Kucun() { } public Kucun(Integer id, Integer shuliang, Bancai bancai) { this.id = id; this.shuliang = shuliang; this.bancai = bancai; } } /* ========== Mupi.java ========== */ package com.example.kucun2.entity; import android.annotation.SuppressLint; import java.util.List; public class Mupi { private Integer id; private String name; /** * 是否有油漆 */ private Boolean you; public Boolean getYou() { return you; } public void setYou(Boolean you) { this.you = you; } private List<Bancai> bancais; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Bancai> getBancais() { return bancais; } public void setBancais(List<Bancai> bancais) { this.bancais = bancais; } public Mupi() { } public Mupi(Integer id, String name, List<Bancai> bancais) { this.id = id; this.name = name; this.bancais = bancais; } } /* ========== User.java ========== */ package com.example.kucun2.entity; /** * 用户 */ public class User { private Integer id; private String name; private String andy; private String pass; private Integer role; public int getId() { return id; } public String getName() { return name; } public String getAndy() { return andy; } public String getPass() { return pass; } public int getRole() { return role; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setAndy(String andy) { this.andy = andy; } public void setPass(String pass) { this.pass = pass; } public void setRole(int role) { this.role = role; } public User(int id, String name, String andy, String pass, int role) { this.id = id; this.name = name; this.andy = andy; this.pass = pass; this.role = role; } public User() { } @Override public String toString() { StringBuilder sb = new StringBuilder("{"); // 处理属性名 sb.append("\"id\": "); // 处理不同数据类型 // 其他对象类型 sb.append((id != null) ? id : "null"); sb.append(","); // 处理属性名 sb.append("\"name\": "); // 处理不同数据类型 // 字符串类型处理(含转义) sb.append("\"") .append(name .replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\b", "\\b") .replace("\f", "\\f") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t")) .append("\""); sb.append(","); // 处理属性名 sb.append("\"andy\": "); // 处理不同数据类型 // 字符串类型处理(含转义) sb.append("\"") .append(andy .replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\b", "\\b") .replace("\f", "\\f") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t")) .append("\""); sb.append(","); // 处理属性名 sb.append("\"pass\": "); // 处理不同数据类型 // 字符串类型处理(含转义) sb.append("\"") .append(pass .replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\b", "\\b") .replace("\f", "\\f") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t")) .append("\""); sb.append(","); // 处理属性名 sb.append("\"role\": "); // 处理不同数据类型 // 其他对象类型 sb.append((role != null) ? role : "null"); sb.append("}"); return sb.toString(); } } /* ========== Zujian.java ========== */ package com.example.kucun2.entity; /** * 组件 */ public class Zujian { private Integer id; private String name; public Zujian() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } /* ========== Bancai.java ========== */ package com.example.kucun2.entity; import android.annotation.SuppressLint; //板材 public class Bancai { private Integer id; private Caizhi caizhi; private Mupi mupi1; private Mupi mupi2; private Double houdu; public Bancai() { } public Bancai(Integer id, Caizhi caizhi, Mupi mupi1, Mupi mupi2, Double houdu) { this.id = id; this.caizhi = caizhi; this.mupi1 = mupi1; this.mupi2 = mupi2; this.houdu = houdu; } public Integer getId() { return id; } public Caizhi getCaizhi() { return caizhi; } public Mupi getMupi1() { return mupi1; } public Mupi getMupi2() { return mupi2; } public Double getHoudu() { return houdu; } public void setId(Integer id) { this.id = id; } public void setCaizhi(Caizhi caizhi) { this.caizhi = caizhi; } public void setMupi1(Mupi mupi1) { this.mupi1 = mupi1; } public void setMupi2(Mupi mupi2) { this.mupi2 = mupi2; } public void setHoudu(Double houdu) { this.houdu = houdu; } /** * 表格中显示的文字 * @return */ @SuppressLint("DefaultLocale") public String TableText(){ String boardInfo=""; boardInfo +=String.format("%.1f", this.getHoudu())+this.getCaizhi().getName()+"("; if (mupi1 != null) { boardInfo += (this.getMupi1().getYou()?this.getMupi1().getName()+"油":this.getMupi1().getName()); } if (mupi2 != null&&mupi1 != null) { boardInfo+=","; } if (mupi2 != null) { boardInfo += (this.getMupi2().getYou()?this.getMupi2().getName()+"油":this.getMupi2().getName()); } boardInfo+=")"; return boardInfo; } } /* ========== Caizhi.java ========== */ package com.example.kucun2.entity; import java.util.List; //材质 public class Caizhi { private Integer id; private String name; private List<Bancai> bancais; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Bancai> getBancais() { return bancais; } public void setBancais(List<Bancai> bancais) { this.bancais = bancais; } public Caizhi() { } public Caizhi(Integer id, String name, List<Bancai> bancais) { this.id = id; this.name = name; this.bancais = bancais; } } /* ========== Chanpin.java ========== */ package com.example.kucun2.entity; import java.util.List; //产品 public class Chanpin { private Integer id; private String name; private List<Chanpin_Zujian> zujians; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Chanpin_Zujian> getZujians() { return zujians; } public void setZujians(List<Chanpin_Zujian> zujians) { this.zujians = zujians; } public Chanpin() { } } /* ========== Chanpin_Zujian.java ========== */ package com.example.kucun2.entity; //产品组件关联 public class Chanpin_Zujian { private Integer id; private Chanpin chanpin; private Zujian zujian ; private Double one_several; private Bancai bancai; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { this.zujian = zujian; } public Double getOne_several() { return one_several; } public void setOne_several(Double one_several) { this.one_several = one_several; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Chanpin_Zujian() { } } /* ========== Dingdan.java ========== */ package com.example.kucun2.entity; import java.util.Date; import java.util.List; /** * 订单 */ public class Dingdan { private Integer id; private String number; private List<Dingdan_Chanpin> chanpins; private Date xiadan; private Date jiaohuo; public Date getXiadan() { return xiadan; } public void setXiadan(Date xiadan) { this.xiadan = xiadan; } public Date getJiaohuo() { return jiaohuo; } public void setJiaohuo(Date jiaohuo) { this.jiaohuo = jiaohuo; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public List<Dingdan_Chanpin> getChanpins() { return chanpins; } public void setChanpins(List<Dingdan_Chanpin> chanpins) { this.chanpins = chanpins; } public Dingdan() { } } /* ========== Dingdan_Bancai.java ========== */ package com.example.kucun2.entity; /**、 * 订单板材关联 */ public class Dingdan_Bancai { private Integer id; private Dingdan dingdian; private Chanpin_Zujian zujian; private Bancai bancai; private Integer shuliang; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdian() { return dingdian; } public void setDingdian(Dingdan dingdian) { this.dingdian = dingdian; } public Chanpin_Zujian getZujian() { return zujian; } public void setZujian(Chanpin_Zujian zujian) { this.zujian = zujian; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Dingdan_Bancai() { } } /* ========== Dingdan_Chanpin.java ========== */ package com.example.kucun2.entity; /** * 订单产品关联 */ public class Dingdan_Chanpin { private Integer id; private Dingdan dingdan; private Chanpin chanpin; private Integer shuliang; public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Dingdan_Chanpin() { } } /app/all返回json:{"bancais":[{"houdu":15.0,"mupi1":{"id":1},"mupi2":{"id":1},"caizhi":{"id":1},"kucun":{"id":1},"id":1},{"houdu":15.0,"mupi1":{"id":2},"mupi2":{"id":2},"caizhi":{"id":1},"kucun":{"id":2},"id":2}],"dingdans":[{"dingdanChanpinZujianIds":[],"number":"直接入库","id":1,"dingdanChanpinIds":[1]}],"mupis":[{"name":"千和板","id":1,"you":null},{"name":"桃花芯","id":2,"you":null},{"name":"杉木","id":3,"you":null}],"chanpins":[{"chanpinZujianIds":[1],"bianhao":"直接入库","id":1,"dingdanChanpinIds":[1]}],"kucuns":[{"bancaiId":1,"shuliang":12,"id":1},{"bancaiId":2,"shuliang":32,"id":2}],"chanpin_zujians":[{"chanpinId":1,"bancaiId":1,"id":1,"zujianId":1,"one_howmany":15.0}],"zujians":[{"chanpinZujianIds":[1],"name":"前板","id":1}],"caizhis":[{"name":"千和板","bancai":[{"id":1},{"id":2}],"id":1},{"name":"杉木","bancai":[],"id":3},{"name":"桃花芯","bancai":[],"id":2}],"users":[{"role":0,"name":"超管","id":1,"andy":"123456"}]} <string name="url">https://tian.sef/Kucun2</string> <string name="url_all">/app/all</string> <string name="url_bancis">/app/bancai/all</string> <string name="url_caizhis">/app/caizhi/all</string> <string name="url_mupis">/app/mupi/all</string> <string name="url_dingdans">/app/dingdan/all</string> <string name="url_chanpins">/app/chanpin/all</string> <string name="url_zujians">/app/zujian/all</string> <string name="url_chanpin_zujians">/app/chanpin_zujian/all</string> <string name="url_dingdan_zujians">/app/dingdan_zujian/all</string> <string name="url_dingdan_chanpins">/app/dingdan_chanpin/all</string> <string name="url_jinhuos">/app/jinhuo/all</string> <string name="url_add_bancai">/app/bancai/add</string> <string name="url_add_dingdan">/app/dingdan/add</string> <string name="url_add_chanpin">/app/chanpin/add</string> <string name="url_add_zujian">/app/zujian/add</string> <string name="url_add_caizhi">/app/caizhi/add</string> <string name="url_add_mupi">/app/mupi/add</string> <string name="url_add_dingdan_chanpin">/app/dingdan_chanpi/add</string> <string name="url_add_dingdan_zujian">/app/dingdan_zujian/add</string> <string name="url_add_chanpin_zujian">/app/chanpin_zujian/add</string> <string name="url_add_jinhuo">/app/jinhuo/add</string> <string name="url_login">/user/login</string> public class Data { // 数据集合声明 public final static List<Bancai> bancais = new ArrayList<>(); public final static List<Caizhi> caizhis = new ArrayList<>(); public final static List<Mupi> mupis = new ArrayList<>(); public final static List<Chanpin> chanpins = new ArrayList<>(); public final static List<Chanpin_Zujian> chanpinZujians = new ArrayList<>(); public final static List<Dingdan> dingdans = new ArrayList<>(); public final static List<Dingdan_Chanpin> dingdanChanpins = new ArrayList<>(); public final static List<Dingdan_Bancai> dingdanBancais = new ArrayList<>(); public final static List<Kucun> kucuns = new ArrayList<>(); public final static List<Zujian> zujians = new ArrayList<>(); public final static List<User> users = new ArrayList<>(); public final static AbstractCollection<Jinhuo> jinhuoList=new ArrayList<>();虚拟数据类改造成从api获得数据
最新发布
06-09
<think>我们之前讨论了如何将虚拟数据类替换为从API获取数据。这涉及到三个主要步骤:网络请求、JSON解析和数据绑定。根据用户提供的引用,我们可以参考以下几点:引用[2]提到了数据应用环境与存储环境分离,我们可以通过API(网络请求)来获取数据,这符合解耦的思想。引用[5]提供了JSON数据格式的定义,我们解析API返回的JSON数据时需要遵循这些规则。具体步骤:1.网络请求:使用Java中的HttpURLConnection或第三方库(如OkHttp)发起HTTP请求获取数据。2.JSON解析:将获取的JSON字符串解析为Java对象,可以使用原生的JSONObject和JSONArray,或者使用Gson、Jackson等库。3.数据绑定:将解析后的数据绑定到应用程序的UI组件上。以下是一个详细的实现方案:步骤1:添加网络权限在AndroidManifest.xml中添加:```xml<uses-permissionandroid:name="android.permission.INTERNET"/>```步骤2:使用OkHttp进行网络请求(需添加依赖)在build.gradle(Module:app)的dependencies中添加:```groovyimplementation'com.squareup.okhttp3:okhttp:4.9.1'```步骤3:创建数据模型类(POJO)以匹配JSON结构例如,如果API返回的JSON如下:```json{"id":1,"name":"JohnDoe","email":"john@example.com"}```则对应的Java类:```javapublicclassUser{privateintid;privateStringname;privateStringemail;//GettersandsetterspublicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetEmail(){returnemail;}publicvoidsetEmail(Stringemail){this.email=email;}}```步骤4:发起网络请求并解析JSON在后台线程中执行网络请求,然后使用Gson解析JSON。添加Gson依赖:```groovyimplementation'com.google.code.gson:gson:2.8.7'```示例代码:```javaOkHttpClientclient=newOkHttpClient();Requestrequest=newRequest.Builder().url("https://api.example.com/user").build();//异步请求client.newCall(request).enqueue(newCallback(){@OverridepublicvoidonFailure(@NonNullCallcall,@NonNullIOExceptione){e.printStackTrace();}@OverridepublicvoidonResponse(@NonNullCallcall,@NonNullResponseresponse)throwsIOException{if(response.isSuccessful()){StringjsonData=response.body().string();Gsongson=newGson();Useruser=gson.fromJson(jsonData,User.class);//注意:不能在后台线程更新UI,需要切换到主线程runOnUiThread(newRunnable(){@Overridepublicvoidrun(){//更新UI,例如设置TextViewtextView.setText(user.getName());}});}}});```步骤5:处理线程问题网络请求不能在主线程中进行,所以使用异步请求(如上面enqueue方法)。解析完成后,通过runOnUiThread切换到主线程更新UI。步骤6:错误处理考虑网络请求失败的情况,例如在onFailure中进行错误提示。注意事项:1.使用第三方库可以简化网络请求和解析,但要注意添加依赖。2.使用Gson解析时,要求数据模型的字段名与JSON中的键名完全一致,或者使用@SerializedName注解来映射不同的名称。3.如果API返回的是数组,则使用数组类型:User[]users=gson.fromJson(jsonData,User[].class);或者List<User>users=gson.fromJson(jsonData,newTypeToken<List<User>>(){}.getType());步骤7:替换虚拟数据将原先使用虚拟数据的地方,替换为从网络获取的数据。例如,原先在Activity中直接创建User对象,现在改为网络请求获取。引用[3]提到多线程安全问题,但在这个场景中,由于是异步请求,我们通过回调函数处理结果,不会造成线程冲突(因为UI更新在主线程,数据解析在后台线程)。但是要注意,如果多个线程同时操作同一个UI组件,可能会造成并发问题,所以我们使用runOnUiThread确保安全。总结:通过以上步骤,我们就可以将应用程序的数据来源从虚拟数据类改为API接口,实现动态数据加载。生成相关问题:1.如何设计API返回的数据结构以便在Android应用中解析?2.网络请求时如何避免内存泄漏?3.使用Gson解析JSON时如何处理字段名称不一致的问题?</think> 我将详细指导你如何将虚拟数据类改造为从API获取数据,涵盖网络请求、JSON解析和数据绑定三部分。 ### 一、技术实现流程 ```mermaid graph TD A[发起网络请求] --> B[API服务器] B --> C[JSON响应] C --> D[解析JSON数据] D --> E[数据绑定到UI] ``` ### 二、具体实现步骤 #### 1. 网络请求实现 **使用Retrofit库(推荐)** ```java // 添加依赖 implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // 定义API接口 public interface ApiService { @GET("users/{id}") Call<User> getUser(@Path("id") String userId); } // 创建Retrofit实例 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://your-api-url.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); ApiService service = retrofit.create(ApiService.class); ``` #### 2. JSON解析处理 **数据模型类(POJO)** ```java public class User { @SerializedName("user_name") private String name; @SerializedName("email") private String email; @SerializedName("role") private String role; // Getters and setters } ``` **处理特殊字符方案** JSON解析时会自动处理特殊字符,但要确保: - 服务器响应包含正确Content-Type: `application/json` - 使用`@SerializedName`注解处理字段名差异 - 遇到特殊数据结构时使用自定义反序列化器 #### 3. 数据绑定到UI **执行请求并更新UI** ```java service.getUser("123").enqueue(new Callback<User>() { @Override public void onResponse(Call<User> call, Response<User> response) { if (response.isSuccessful()) { User user = response.body(); // 数据绑定到UI组件 textUserName.setText(user.getName()); textUserEmail.setText(user.getEmail()); } } @Override public void onFailure(Call<User> call, Throwable t) { // 处理网络错误 } }); ``` #### 4. 线程安全与内存管理 ```java // 在Activity/Fragment中声明 private Call<User> userCall; // 取消请求避免内存泄漏 @Override protected void onDestroy() { super.onDestroy(); if (userCall != null) { userCall.cancel(); } } ``` ### 三、替换虚拟数据的完整方案 | 原虚拟代码 | 替换为API代码 | |------------|---------------| | `User user = new User("Test", "test@mail.com")` | `service.getUser(userId).enqueue(...)` | | 硬编码数据 | 动态API响应解析 | | 立即可用 | 需处理加载状态 | **完整改造步骤:** 1. 移除所有虚拟数据生成代码 2. 添加网络权限:`<uses-permission android:name="android.permission.INTERNET" />` 3. 添加Retrofit和Gson依赖 4. 创建API接口和数据模型类 5. 在数据加载处添加网络请求 6. 添加加载状态UI(ProgressBar) 7. 实现错误处理机制 ### 四、最佳实践建议 1. **使用Repository模式**:创建数据仓库类隔离网络请求逻辑 2. **实现缓存机制**:`Cache-Control: max-age=60`头部减少请求 3. **依赖注入**:使用Dagger/Hilt管理Retrofit实例 4. **状态管理**: ```java enum LoadState { LOADING, SUCCESS, ERROR } ``` 5. **数据转换**:通过映射函数将API实体转换为领域模型[^2] ### 五、故障处理方案 | 问题现象 | 解决方案 | |---------|----------| | `JSONSyntaxException` | 验证JSON结构是否匹配POJO,使用在线JSON验证器 | | 空指针异常 | 检查@Nullable注解,使用空对象模式 | | 数据不同步 | 添加`Synchronized`关键字保护共享资源 | | `NetworkOnMainThreadException` | 确保网络请求在后台线程执行 | > 参考:引用[3]中的线程安全问题同样适用于网络请求回调场景,建议通过`runOnUiThread()`确保UI更新在主线程执行[^3]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值