[Android]利用金山词霸API实现英汉互译APP

最近在做一个单词本的项目实战,其中涉及到一个功能,英汉互译。在众多翻译API中查找适合的API,发现金山词霸API很适合完成这样的一个功能。因为它包含发音、基本释义、例句等功能。其他API相对简单,没有发现含有例句的数据。而且金山词霸API的使用也是简单到不行,只需要申请一个Key,就可以进行查询了。

而我在使用这个API的时候遇到了很多困难,不过现在都已经完美解决。网上其他的文章对于该API的介绍和使用都非常模糊,不具体,所以,我决定自己写一篇这样的文章,来帮助更多初学者可以轻松入门。我可以保证,这篇文章讲述的信息是其他任何一篇文章都不能相比的。绝对的详细具体。

一、金山词霸API介绍

1、金山词霸API的网址是:http://open.iciba.com/?c=api ,现在也称为爱词霸。

2、API申请流程:

(1)、首先前往金山词霸API的网站。选择词霸查词。


(2)、然后输入网址名称,网址和你的邮箱地址。其中,网址名称和网址可以随意填写,我们只需要获取到Key就可以了。例如:


(3)点击提交按钮,你所填写的邮箱就会收到一个邮件通知,点开后就可以获取到key了。


目前的使用是不限制次数的,后期可能会有所更改。

3、请求数据

在官网上,点击文档/查词接口。


它的文档说明非常简单,甚至初学者看了之后云里雾里,不知道怎么具体使用。这就要经过一番摸索了。我进行了很多尝试,所以在这里直接把经验分享出来。

针对查词接口:返回数据默认为xml格式,可以选择json格式。但是json格式缺少例句功能,两者都需要使用key。

请求json:http://dict-co.iciba.com/api/dictionary.php?w=good&type=json&key=XXX

请求xml:http://dict-co.iciba.com/api/dictionary.php?w=good&key=XXX

其中XXX就是你刚刚申请的key,复制上去就可以了。

针对每日一词:每日一词功能是金山词霸API提供的一个非常人性化的接口,不需要使用key,直接访问即可。本程序中不会使用到该功能,但是读者可以在优化程序后添加这样的一个人性化功能,下面介绍一个如何请求数据。它默认返回json格式,可以选择xml格式,不需要使用key。

请求json:http://open.iciba.com/dsapi/?date=2018-03-09

请求xml:http://open.iciba.com/dsapi/?file=xml&date=2018-03-10

其中,date后的日期需要格式化为要求的格式,由于它返回的json格式数据非常简单,解析起来相对于xml格式数据要容易的多,一般使用第一个请求就可以了。

4、查看返回数据

返回数据按照刚才的请求网址,就可以进行访问了。英译汉以good为例,汉译英以 好 为例。

(1)、查词接口返回数据。英译汉,查询good。

①、json格式数据:

{
	"word_name": "good",
	"is_CRI": "1",
	"exchange": {
		"word_pl": ["goods"],
		"word_third": "",
		"word_past": "",
		"word_done": "",
		"word_ing": "",
		"word_er": ["better"],
		"word_est": ["best"]
	},
	"symbols": [{
		"ph_en": "g?d",
		"ph_am": "ɡ?d",
		"ph_other": "",
		"ph_en_mp3": "http:\/\/res.iciba.com\/resource\/amp3\/oxford\/0\/28\/a2\/28a24294fed307cf7e65361b8da4f6e5.mp3",
		"ph_am_mp3": "http:\/\/res.iciba.com\/resource\/amp3\/1\/0\/75\/5f\/755f85c2723bb39381c7379a604160d8.mp3",
		"ph_tts_mp3": "http:\/\/res-tts.iciba.com\/7\/5\/5\/755f85c2723bb39381c7379a604160d8.mp3",
		"parts": [{
			"part": "adj.",
			"means": ["好的", "优秀的", "有益的", "漂亮的,健全的"]
		}, {
			"part": "n.",
			"means": ["好处,利益", "善良", "善行", "好人"]
		}, {
			"part": "adv.",
			"means": ["同well"]
		}]
	}]
}

②、xml格式数据:

<?xml version="1.0" encoding="UTF-8"?>
<dict num="219" id="219" name="219">
    <key>good</key>
    <ps>g?d</ps>
    <pron>http://res.iciba.com/resource/amp3/oxford/0/28/a2/28a24294fed307cf7e65361b8da4f6e5.mp3</pron>
    <ps>ɡ?d</ps>
    <pron>http://res.iciba.com/resource/amp3/1/0/75/5f/755f85c2723bb39381c7379a604160d8.mp3</pron>
    <pos>adj.</pos>
    <acceptation>好的;优秀的;有益的;漂亮的,健全的;
</acceptation>
    <pos>n.</pos>
    <acceptation>好处,利益;善良;善行;好人;
</acceptation>
    <pos>adv.</pos>
    <acceptation>同well;
</acceptation>
    <sent>
        <orig>
Best is the superlative form of good and worst is the superlative form of bad.
</orig>
        <trans>
“best”是“good”的最高级形式,“worst” 是“bad”的最高级形式.
</trans>
    </sent>
    <sent>
        <orig>
Good has captured the essence of the runaway, but does not pursue its most disturbing consequences.
</orig>
        <trans>
Good抓住了这场失控的本质, 但没有进一步追踪这个事件最让人担忧的后果.
</trans>
    </sent>
    <sent>
        <orig>
The state of the stream is revealed by the bad, fail, eof, and good operations.
</orig>
        <trans>
流的状态由bad 、 fail 、 eof 和good操作提示.
</trans>
    </sent>
    <sent>
        <orig>
Good Christian, good parent, good child, good wife, good husband.
</orig>
        <trans>
虔诚的基督徒, 慈爱的父母, 孝顺的儿女, 贤良的妻子, 尽职的丈夫.
</trans>
    </sent>
    <sent>
        <orig>
Good habits nurture good characters; good characters mold good fates.
</orig>
        <trans>
好习惯育成好品格, 好品格塑造好命运.
</trans>
    </sent>
</dict>

可以清楚的看到xml格式数据比json数据多了例句数据,所以,在英译汉的功能中我们优先选择xml格式数据。

(2)、查词接口返回数据。汉译英,查询“好”。

①、json格式数据:

{
	"word_id": "2143763",
	"word_name": "好",
	"symbols": [{
		"symbol_id": "2144972",
		"word_id": "2143763",
		"word_symbol": "hǎo",
		"symbol_mp3": "http:\/\/res.iciba.com\/hanyu\/zi\/19a73d32bf61c88bc4ed86c40f26bc9c.mp3",
		"parts": [{
			"part_name": "形",
			"means": [{
				"mean_id": "2465987",
				"part_id": "2148468",
				"word_mean": "good",
				"has_mean": "1",
				"split": 1
			}, {
				"mean_id": "2465988",
				"part_id": "2148468",
				"word_mean": "fine",
				"has_mean": "1",
				"split": 1
			}, {
				"mean_id": "2465989",
				"part_id": "2148468",
				"word_mean": "nice",
				"has_mean": "1",
				"split": 0
			}]
		}],
		"ph_am_mp3": "",
		"ph_en_mp3": "",
		"ph_tts_mp3": "",
		"ph_other": ""
	}, {
		"symbol_id": "2144973",
		"word_id": "2143763",
		"word_symbol": "hào",
		"symbol_mp3": "",
		"parts": [{
			"part_name": "动",
			"means": [{
				"mean_id": "2465990",
				"part_id": "2148469",
				"word_mean": "like",
				"has_mean": "1",
				"split": 1
			}, {
				"mean_id": "2465991",
				"part_id": "2148469",
				"word_mean": "love",
				"has_mean": "1",
				"split": 1
			}, {
				"mean_id": "2465992",
				"part_id": "2148469",
				"word_mean": "be fond of",
				"has_mean": "1",
				"split": 0
			}]
		}, {
			"part_name": "名",
			"means": [{
				"mean_id": "2465993",
				"part_id": "2148470",
				"word_mean": "a surname",
				"has_mean": "0",
				"split": 0
			}]
		}]
	}]
}

数据很长很复杂,不过不影响解析,因为有很多json数据解析的开源库可以选择,这里先卖个关子,下面会具体介绍解析json格式数据的黑科技。

②、xml格式数据:

<?xml version="1.0" encoding="UTF-8"?>
<dict num="219" id="219" name="219">
    <key>好</key>
    <pos></pos>
    <acceptation>;;;
</acceptation>
    <pos></pos>
    <acceptation>;;;
</acceptation>
    <pos></pos>
    <acceptation>;
</acceptation>
    <sent>
        <orig>
That day, good clear blue light well through good good good far better abstruse!
</orig>
        <trans>
那时的天, 好清好亮好透好蓝好高好远好深邃!
</trans>
    </sent>
    <sent>
        <orig>
Good habits nurture good characters; good characters mold good fates.
</orig>
        <trans>
好习惯育成好品格, 好品格塑造好命运.
</trans>
    </sent>
    <sent>
        <orig>
It was a long , long week, and the strain was a heavy one.
</orig>
        <trans>
这个星期好长好长, 那根弦绷得好紧好紧.
</trans>
    </sent>
    <sent>
        <orig>
This country needs good farmers, good businessmen, good plumbers, good carpenters.
</orig>
        <trans>
这个国家需要好的农民, 好的商人, 好的管子工, 好的木匠.
</trans>
    </sent>
    <sent>
        <orig>
Over the past few years I have been saying , When Hong Kong succeeds, China will benefit.
</orig>
        <trans>
我多年来一直讲[香港好,国家好;国家好, 香港更好].
</trans>
    </sent>
</dict>

可以看到xml格式数据的基本释义非常少,甚至可以说没有,但是有更丰富的例句数据,这是json数据所没有的,所以,接下来的程序设计中,我们决定json和xml数据一起解析,即发送两次网络请求,使用json数据的基本释义数据,使用xml数据的例句数据,这样汉译英的数据就完整了。

(3)、每日查词接口返回的数据

①、json格式数据:

{
	"sid": "2899",
	"tts": "http:\/\/news.iciba.com\ / admin\ / tts\ / 2018 - 03 - 09 - day ",
	"content ": "Love is putting someone else 's needs before yours.",
	"note": "爱,就是把那个人的需要,看得比自己还重要。",
	"love": "2112",
	"translation": "词霸小编:无论是亲情还是爱情,不都是把对方的需要放置于自己之上?不管是何种性质的爱,亘久不变的便是无私,打心底的为对方着想,始终将其摆在第一位。",
	"picture": "http:\/\/cdn.iciba.com\/news\/word\/20180308.jpg",
	"picture2": "http:\/\/cdn.iciba.com\/news\/word\/big_20180308b.jpg",
	"caption": "词霸每日一句",
	"dateline": "2018-03-09",
	"s_pv": "0",
	"sp_pv": "0",
	"tags": [{
		"id": null,
		"name": null
	}],
	"fenxiang_img": "http:\/\/cdn.iciba.com\/web\/news\/longweibo\/imag\/2018-03-09.jpg"
}

②、xml格式数据:

<?xml version="1.0" encoding="UTF-8"?>
<Document>
    <sid>2899</sid>
    <tts>http://news.iciba.com/admin/tts/2018-03-09-day</tts>
    <content>Love is putting someone else's needs before yours.</content>
    <note>爱,就是把那个人的需要,看得比自己还重要。</note>
    <love>2420</love>
    <translation>词霸小编:无论是亲情还是爱情,不都是把对方的需要放置于自己之上?不管是何种性质的爱,亘久不变的便是无私,打心底的为对方着想,始终将其摆在第一位。</translation>
    <picture>http://cdn.iciba.com/news/word/20180308.jpg</picture>
    <picture2>http://cdn.iciba.com/news/word/big_20180308b.jpg</picture2>
    <caption>词霸每日一句</caption>
    <dateline>2018-03-09</dateline>
    <s_pv>0</s_pv>
    <sp_pv>0</sp_pv>
    <tagid></tagid>
    <tag></tag>
    <fenxiang_img>http://cdn.iciba.com/web/news/longweibo/imag/2018-03-09.jpg</fenxiang_img>
</Document>
可以看到两者返回的数据都非常简单。本文虽涉及不到,但仍可以作为读者日后优化使用。让你的APP更丰富。

二、开始开发程序

数据准备充分之后就可以开始编写程序了。当然,我们还需要进行一些准备工作。

1、开发工具准备

(1)、网络请求工具类

由于程序中会使用到网络请求功能,为了日后拓展,也就是为了程序的可扩展性增加,我们不能每次使用网络请求都写一遍请求代码。这些东西都是可以封装到工具类中的,使用的时候调用一下即可。这里我推荐一个非常好用的开源库OkHttp。

新建项目:TranslateAPP,所有默认,选择空的活动。

OkHttp的项目主页地址是:https://github.com/square/okhttp

使用OkHttp库,我们需要添加库依赖,使用Android Studio,在project模式下,编辑app/build.gradle文件。在dependencies闭包中添加以下内容:

compile 'com.squareup.okhttp3:okhttp:3.10.0'

然后在项目名下New→package,建立一个名为util的包。在该包下新建一个HttpUtil的类。

编写以下代码:

import okhttp3.OkHttpClient;
import okhttp3.Request;

public class HttpUtil {

    /**
     * 使用OkHttp网络工具发送网络请求
     * */
    public static void sendOkHttpRequest(String address,okhttp3.Callback callback){

        //创建OkHttpClient对象
        OkHttpClient client = new OkHttpClient();

        //创建Request对象,装上地址
        Request request = new Request.Builder().url(address).build();

        //发送请求,返回数据需要自己重写回调方法
        client.newCall(request).enqueue(callback);

    }

}

使用时,调用请求方法,重写回调函数即可,如:

HttpUtil.sendOkHttpRequest(url, new Callback() {
    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
        //返回数据失败时的处理逻辑
    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException {
		//返回数据成功时的处理逻辑
    }
});

在接下来的程序中,你会看到它的具体使用方法。

(2)、建立javaBean、及解析工具介绍

解析json数据的时候需要使用到javaBean(实体类)吗?当然是不一定的,不过我们决定使用更简单的解析方式就需要使用到这样的一个实体类。我们需要将上面介绍到的json数据的每一个属性都设置到javaBean当中,可能面对这么多属性已经心生退意了,不过不用担心,我们使用黑科技来解决。

①、使用Gson解析json数据

Gson解析json数据简直简单到了不能想象的地步,它将json数据字符串格式化为对应的Bean对象,我们需要什么数据,就去对应的实体类对象中get就可以了。是不是超级简单。比起json的一段一段解析简单的多。同样,使用Gson需要添加Gson的库依赖,在app/build.gradle文件中添加以下依赖:

compile 'com.google.code.gson:gson:2.8.2'

使用时只需要两行代码:

Gson gson = new Gson();
Bean bean = gson.fromJson(dataString,Bean.class);

创建Gson对象,将翻译接口返回的数据dataString映射为Bean类对象。是不是超级简单。关于如何创建Bean,也很简单,继续使用黑科技。

②、使用GsonFormat插件快速生成Json实体类

关于GsonFormat的安装,只需要 File->Settings->Plugins—>查找所需插件—>Install即可。


如图所示,我已经安装好了,你可以在搜索框搜索,然后安装。

使用该插件更简单。我们在项目名下New→package,创建一个Gson包,用于存放使用Gson解析Json的Bean实体类。第一步,创建类,在包名上New一个类,类名为JinshanChineseToEnglishPartBean,因为英译汉使用xml数据,汉译英我们只是用到了json数据的一部分,所以叫这个名字,一眼看去就知道是什么含义的名字。

第二步,打开刚创建的类,按下Alt + Insert,选择GsonFormat,在弹出的窗口中,复制进去汉译英的json格式数据。这里一定要保证json数据格式的正确,按照本文所给出的数据格式就一定是正确的,你也可以网上找到Json格式化校验,进行格式确认。网址:http://www.bejson.com/。然后点击ok,格式错误的数据按下OK是没有反应的,或者它会给你错误提示,让你进行修改。格式正确后,点击ok会弹出参数选择界面。


不需要进行修改,全部选择点击ok即可。

这样我们需要的Bean就创建好了,直接使用即可。


(3)、解析工具类

也是为了程序的可扩展性,这点很重要。同样的,我们创建一个关于解析的工具类。因为不能每次使用到解析功能都编写一次解析代码,那样代码冗余会非常严重,后期维护也非常困难。在util包下创建JinshanParseUtil工具类。编写以下代码:

import android.content.SharedPreferences;
import android.util.Log;

import com.google.gson.Gson;
import com.my.wordbar.activity.MyApplication;
import com.my.wordbar.gson.JinshanChineseToEnglishPartBean;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.StringReader;

import static android.content.Context.MODE_PRIVATE;

public class JinshanParseUtil {

    private final static String TAG = "金山解析工具";

    /**
     * 判断一段字符串是否是纯英文
     * */
    public static boolean isEnglish(String content){

        if(content == null){                    //获取内容为空则返回false
            return false;
        }

        content = content.replace(" ","");      //去掉内容中的空格

        return content.matches("^[a-zA-Z]*");   //判断是否是全英文,是则返回true,反之返回false

    }

    /**
     * 英译汉时使用。查词
     * 使用pull方式解析金山词霸返回的XML数据。
     * */
    public static void parseJinshanEnglishToChineseXMLWithPull(String result) {

        try {

            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(result));
            int eventType = xmlPullParser.getEventType();

            String queryText = "";      //查询文本
            String voiceText = "";      //发音信息
            String voiceUrlText = "";   //发音地址信息
            String meanText = "";       //基本释义信息
            String exampleText = "";    //例句信息

            while (eventType != XmlPullParser.END_DOCUMENT) {
                String nodeName = xmlPullParser.getName();
                switch (eventType) {
                    //开始解析
                    case XmlPullParser.START_TAG: {
                        switch (nodeName) {
                            case "key":
                                queryText += xmlPullParser.nextText();
                                break;
                            case "ps":
                                voiceText += xmlPullParser.nextText() + "|";
                                break;
                            case "pron":
                                voiceUrlText += xmlPullParser.nextText() + "|";
                                break;
                            case "pos":
                                meanText += xmlPullParser.nextText() + "  ";
                                break;
                            case "acceptation":
                                meanText += xmlPullParser.nextText();
                                break;
                            case "orig":
                                exampleText += xmlPullParser.nextText();
                                exampleText = exampleText.substring(0,exampleText.length()-1);
                                break;
                            case "trans":
                                exampleText += xmlPullParser.nextText();
                                break;
                            default:
                                break;
                        }
                    }
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }

            String[] voiceArray = voiceText.split("\\|");
            String[] voiceUrlArray = voiceUrlText.split("\\|");

            meanText = meanText.substring(0,meanText.length()-1);
            exampleText = exampleText.substring(1,exampleText.length());

            //创建SharedPreferences.Editor对象,指定文件名为
            SharedPreferences.Editor editor = MyApplication.getContext().getSharedPreferences("JinshanEnglishToChinese",MODE_PRIVATE).edit();

            editor.clear();

            editor.putString("queryText",queryText);
            editor.putString("voiceEnText","["+voiceArray[0]+"]");
            editor.putString("voiceEnUrlText",voiceUrlArray[0]);
            editor.putString("voiceAmText","["+voiceArray[1]+"]");
            editor.putString("voiceAmUrlText",voiceUrlArray[1]);
            editor.putString("meanText",meanText);
            editor.putString("exampleText",exampleText);

            editor.apply();

        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "解析过程中出错!!!");
        }

    }

    /**
     * 汉译英时使用。查词
     * 使用Gson解析金山词霸返回的json数据。
     *
     * ====这里只是解析了查询的文本、拼音、发音地址、基本释义。例句部分由XMl数据解析完成。====
     *
     * */
    public static void parseJinshanChineseToEnglishJSONWithGson(String result) {

        try {
            Gson gson = new Gson();
            JinshanChineseToEnglishPartBean translate = gson.fromJson(result, JinshanChineseToEnglishPartBean.class);

            String queryText;      //查询文本
            String voiceText = "";      //拼音
            String voiceUrlText = "";   //拼音的发音
            String meanText = "";       //词性及含义

            queryText = translate.getWord_name();

            for (JinshanChineseToEnglishPartBean.SymbolsBean voiceMsg : translate.getSymbols()){
                voiceText += voiceMsg.getWord_symbol()+" , ";
                voiceUrlText += voiceMsg.getSymbol_mp3();
                for(JinshanChineseToEnglishPartBean.SymbolsBean.PartsBean meanMsg : voiceMsg.getParts()){
                    meanText += meanMsg.getPart_name()+": ";
                    for(JinshanChineseToEnglishPartBean.SymbolsBean.PartsBean.MeansBean mean :meanMsg.getMeans()){
                        meanText += mean.getWord_mean() + "; ";
                    }
                    meanText = meanText.substring(0,meanText.length()-2);
                    meanText += "\n";
                }
            }

            meanText = meanText.substring(0,meanText.length()-1);

            voiceText = voiceText.substring(0,voiceText.length()-3);

            if(voiceText.equals("")){
                voiceText = "空";
            }

//            if(voiceText.trim().equals(",")){
//                voiceText = "空";
//            }
            if(voiceUrlText.equals("")){
                voiceUrlText = "空";
            }
            if(meanText.charAt(0) == ':'){
                meanText = meanText.substring(2,meanText.length());
            }

            //创建SharedPreferences.Editor对象,指定文件名为
            SharedPreferences.Editor editor = MyApplication.getContext().getSharedPreferences("JinshanChineseToEnglishBaseMean",MODE_PRIVATE).edit();

            editor.clear();

            editor.putString("queryText",queryText);
            editor.putString("voiceText",voiceText);
            editor.putString("voiceUrlText",voiceUrlText);
            editor.putString("meanText",meanText);

            editor.apply();

        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "解析过程中出错!!!");
        }

    }

    /**
     * 汉译英时使用,查词
     * 使用Pull方式解析金山词霸返回的XML数据。
     *
     * ====这里只解析了例句,其他相关释义由json数据解析完成====
     *
     * */
    public static String parseJinshanChineseToEnglishXMLWithPull(String result) {

        String exampleText = "";    //例句信息
        try {

            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(result));
            int eventType = xmlPullParser.getEventType();

            while (eventType != XmlPullParser.END_DOCUMENT) {
                String nodeName = xmlPullParser.getName();
                switch (eventType) {
                    //开始解析
                    case XmlPullParser.START_TAG: {
                        switch (nodeName) {
                            case "orig":
                                exampleText += xmlPullParser.nextText();
                                exampleText = exampleText.substring(0,exampleText.length()-1);
                                break;
                            case "trans":
                                exampleText += xmlPullParser.nextText();
                                break;
                            default:
                                break;
                        }
                    }
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "解析过程中出错!!!");
        }

        exampleText = exampleText.substring(1,exampleText.length());

        return exampleText;

    }

    /**
     * 每日一句,解析json数据方法。
     * */
    public static String parseJinshanEverydayEnglishJSONWithGson(String result){

        return result;
    }

}

在本程序中,将返回的数据通过工具类进行解析,然后存储到对应的SharedPreferences文件中,这一种轻型的存储文件,通过键值对存储,通过对应数据类型的put方法存储,然后可以通过对应数据类型的get方法以键名的方式取出对应值。解析过程比较繁琐,如果你对这方面不是很明白,希望你可以多多理解上面的代码。XML文件我是使用的Pull方式进行解析。在Java7及以上的版本可以使用switch的方式读取对应标签,相对于传统的if/else的方式读取,效率要高出很多。在解析过程中我也加入了很多细节的处理,多数都是利用取子串的方式将多余的符号去除。再就是判断是否为空,为其赋值默认的“空”值。由于解析部分很多,我不能一一说明,但是希望你能看懂,我在解析上也花费了相当长的时间。遇见了太多的问题。如果你真的不懂,在介绍金山词霸API时,有我的邮箱地址,你可以通过它联系我,我会解答你的疑问。当然我相信高手是不会看这篇文章的,仅针对初学者。

工具最后还有一个空方法,是解析每日一句的,这个功能要完成其实非常简单。我没有写,你可以在日后补充。

另外,工具类中使用到了全局获取Context的方法。因为工具类不依赖活动,所以获取上下文的话这是一种很常用的方法。首先在项目下新建一个MyApplication类。并添加以下代码:

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {

    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    public static Context getContext(){
        return context;
    }

}

还需要修改AndroidManifest.xml文件。

<application
    android:name="com.my.translateapp.MyApplication"
      
	...

</application>

一定要是完整的包名,使用时,只需要使用:

MyApplication.getContext()

2、正式编写程序

所有东西都准备就绪就可以正式编写了。

(1)编写布局,修改activity_main文件中的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" >

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="@string/translate_bar_name"
                android:layout_centerInParent="true"
                android:layout_gravity="center"
                android:textColor="@android:color/white"
                android:textSize="20sp"/>

        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout>

    <!-- 内容区 -->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <!--android:layout_margin="@dimen/translate_et_margin"-->

        <EditText
            android:id="@+id/et_translate"
            android:layout_width="match_parent"
            android:layout_height="@dimen/translate_et_height"
            android:background="@drawable/translate_shape_et_bg"
            android:gravity="top"
            android:hint="@string/translate_et_hint"
            android:textColorHint="@color/translate_et_hint"
            android:textColorLink="@android:color/black"
            android:textCursorDrawable="@drawable/translate_shape_et_cursor"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp">

            <!--<TextView-->
                <!--android:id="@+id/translate_tv_chinese"-->
                <!--android:layout_width="wrap_content"-->
                <!--android:layout_height="wrap_content"-->
                <!--android:layout_marginLeft="2dp"-->
                <!--android:text="@string/language_chinese"-->
                <!--android:textColor="@color/text_color_black"-->
                <!--android:textSize="16sp"-->
                <!--app:layout_constraintBaseline_toBaselineOf="@+id/translate_tv_english"-->
                <!--app:layout_constraintLeft_toLeftOf="parent"-->
                <!--android:layout_marginStart="2dp" />-->

            <!--<!– translate_btn_change –>-->
            <!--<ImageButton-->
                <!--android:id="@+id/translate_image_btn_change"-->
                <!--android:layout_width="35dp"-->
                <!--android:layout_height="35dp"-->
                <!--android:layout_marginLeft="8dp"-->
                <!--android:background="@color/white"-->
                <!--android:padding="4dp"-->
                <!--android:scaleType="fitXY"-->
                <!--android:src="@drawable/translate_btn_change"-->
                <!--app:layout_constraintLeft_toRightOf="@+id/translate_tv_chinese"-->
                <!--app:layout_constraintTop_toTopOf="parent"-->
                <!--app:layout_constraintBottom_toBottomOf="parent"-->
                <!--app:layout_constraintVertical_bias="0.0"-->
                <!--android:layout_marginStart="8dp" />-->

            <!--<TextView-->
                <!--android:id="@+id/translate_tv_english"-->
                <!--android:layout_width="wrap_content"-->
                <!--android:layout_height="0dp"-->
                <!--android:text="@string/language_english"-->
                <!--android:textSize="16sp"-->
                <!--android:textColor="@color/text_color_black"-->
                <!--app:layout_constraintTop_toTopOf="parent"-->
                <!--android:layout_marginTop="8dp"-->
                <!--app:layout_constraintLeft_toRightOf="@+id/translate_image_btn_change"-->
                <!--android:layout_marginLeft="8dp"-->
                <!--app:layout_constraintBottom_toBottomOf="parent"-->
                <!--android:layout_marginBottom="8dp"-->
                <!--app:layout_constraintVertical_bias="0.0"-->
                <!--android:layout_marginStart="8dp" />-->

            <Button
                android:id="@+id/btn_translate"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:background="@drawable/button_selector"
                android:minHeight="0dp"
                android:minWidth="0dp"
                android:text="@string/translate_btn_text"
                android:textSize="15sp"
                style="?android:attr/borderlessButtonStyle"
                android:layout_marginRight="0dp"
                app:layout_constraintRight_toRightOf="parent"
                android:layout_marginLeft="0dp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                android:layout_marginTop="0dp"
                app:layout_constraintBottom_toBottomOf="parent"
                android:layout_marginBottom="0dp" />

        </android.support.constraint.ConstraintLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/cut_line"
            android:layout_marginBottom="0dp"
            android:layout_marginTop="5dp"
            android:background="@color/translate_line_color" />

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="2dp"
                android:orientation="vertical">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="3dp"
                        android:text="@string/text_query"
                        android:textColor="@color/text_color_black"
                        android:textSize="15sp" />

                    <TextView
                        android:id="@+id/query"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="3dp"
                        android:text="@string/text_null" />

                </LinearLayout>

                <include
                    layout="@layout/cut_line"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="3dp"
                    android:text="@string/voice_msg"
                    android:textColor="@color/text_color_black"
                    android:textSize="15sp" />

                <TextView
                    android:id="@+id/voice_msg"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:text="@string/text_null" />

                <include
                    layout="@layout/cut_line"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="3dp"
                    android:text="@string/base_mean"
                    android:textColor="@color/text_color_black"
                    android:textSize="15sp" />

                <TextView
                    android:id="@+id/base_mean"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:text="@string/text_null" />

                <include
                    layout="@layout/cut_line"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:text="@string/related_examples"
                    android:textColor="@color/text_color_black"
                    android:textSize="15sp" />

                <TextView
                    android:id="@+id/related_examples"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="3dp"
                    android:text="@string/text_test" />

            </LinearLayout>

        </ScrollView>

    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

这里使用了Material Design的设计风格,使用AppBarLayout嵌套Toolbar,并使用CoordinatorLayout布局。如果你对这方面很陌生,可以参考我的文章:Material Design设计语言的基本使用方法,这里仍然后具体介绍如何使用,首先去掉系统自带的标题栏。打开res/values/styles.xml文件。修改为以下代码:

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    
</style>

然后在MainActivity代码中添加以下代码,即可使用Toolbar。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar toolbar = (Toolbar) this.findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);       //使用Toolbar

    ActionBar actionBar = getSupportActionBar();  //获取ActionBar实例,具体实现由Toolbar完成
    if (actionBar != null) {
        actionBar.setDisplayHomeAsUpEnabled(true);          //显示导航按钮设置为true
    }

}
我们还设置了左面的导航按钮为显示状态

另外,布局中涉及到的文本,都在res/values/strings.xml文件中定义了。

<resources>
    <string name="app_name">翻译</string>

    <!-- 通用 -->
    <string name="text_query">查询:</string>
    <string name="voice_msg">发音信息:</string>
    <string name="base_mean">基本释义:</string>
    <string name="related_examples">相关例句:</string>
    <string name="text_null">Hello World</string>
    <string name="text_test">1 1 1\n2 2 2\n3 3 3\n4 4 4\n5 5 5\n6 6 6\n7 7 7\n8 8 8\n9 9 9\n10 10 10\n11 11 11\n12 12 12\n13 13 13\n14 14 14\n15 15 15\n16 16 16\n17 17 17\n18 18 18\n19 19 19\n20 20 20\n21 21 21\n</string>
    
    <!-- 翻译界面标题栏按钮名称 -->
    <string name="translate_action_add">加入单词本</string>
    <!-- 翻译界面标题栏名称 -->
    <string name="translate_bar_name">翻译</string>
    <!-- 翻译界面编辑框内 hint 显示文本 -->
    <string name="translate_et_hint">请输入翻译内容</string>
    <!-- 翻译界面 翻译按钮 显示的文字 -->
    <string name="translate_btn_text">翻    译</string>

</resources>

布局界面中还涉及到了自定义控件,这部分也不是特别难,但是需要一定的时间消化,如果实在不懂就先复制着用,网上有很多这方面的使用方法。首先是编辑框的定义。在drawable下新建一个translate_shape_et_bg.xml文件,设置编辑框的形状、颜色、圆角、描边等。编辑如下内容:

<?xml version="1.0" encoding="utf-8"?>
<!-- android:shape指定形状类型,默认为rectangle -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <!-- solid指定形状的填充色,只有android:color一个属性 -->
    <!--<solid android:color="#FAFAFA" />-->
    <solid android:color="@color/white" />
    <!-- padding设置内容区域离边界的间距 -->
    <padding
        android:bottom="@dimen/translate_shape_et_bg_padding"
        android:left="@dimen/translate_shape_et_bg_padding"
        android:right="@dimen/translate_shape_et_bg_padding"
        android:top="@dimen/translate_shape_et_bg_padding" />
    <!-- corners设置圆角,只适用于rectangle -->
    <corners android:radius="5dp" />
    <!-- stroke设置描边 -->
    <stroke
        android:width="1dp"
        android:color="@android:color/darker_gray"
        />
</shape>

drawable下新建一个translate_shape_et_cursor.xml文件,设置光标颜色。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <size android:width="1dp" />
    <solid android:color="@color/et_cursor_black"  />
</shape>

drawable下新建一个button_selector.xml文件,设置按钮形状、颜色、填充色、点击效果等。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="false">
        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle">
            <!-- solid指定形状的填充色,只有android:color一个属性 -->
            <!--<solid android:color="#FAFAFA" />-->
            <solid android:color="@color/button_normal_color" />

            <!-- padding设置内容区域离边界的间距 -->
            <padding
                android:bottom="@dimen/shape_btn_padding"
                android:left="@dimen/shape_btn_padding"
                android:right="@dimen/shape_btn_padding"
                android:top="@dimen/shape_btn_padding" />
            <!-- corners设置圆角,只适用于rectangle -->
            <corners android:radius="3dp" />
            <!-- stroke设置描边 -->
            <stroke
                android:width="1dp"
                android:color="@color/green"
                />
        </shape>
    </item>
    <item android:state_pressed="true">
        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle">
            <!-- solid指定形状的填充色,只有android:color一个属性 -->
            <!--<solid android:color="#FAFAFA" />-->
            <solid android:color="@color/button_selected_color" />

            <!-- padding设置内容区域离边界的间距 -->
            <padding
                android:bottom="@dimen/shape_btn_padding"
                android:left="@dimen/shape_btn_padding"
                android:right="@dimen/shape_btn_padding"
                android:top="@dimen/shape_btn_padding" />
            <!-- corners设置圆角,只适用于rectangle -->
            <corners android:radius="3dp" />
            <!-- stroke设置描边 -->
            <stroke
                android:width="1dp"
                android:color="@color/green"
                />
        </shape>
    </item>
</selector>

上面的文件都使用到了res/values/dimens.xml和res/values/colors.xml中的内容。

dimens.xml文件内容:

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>

    <!-- 翻译界面编辑框内,内容与边框的边距 -->
    <dimen name="translate_shape_et_bg_padding">8dp</dimen>
    <!-- 翻译界面编辑框与父布局的距离 -->
    <dimen name="translate_et_margin">8dp</dimen>
    <!-- 翻译界面编辑框高度 -->
    <dimen name="translate_et_height">120dp</dimen>
    <!-- 翻译界面自定义翻译按钮内容与边框的距离 -->
    <dimen name="shape_btn_padding">5dp</dimen>

    <!-- 分割线高度 -->
    <dimen name="cut_line">0.5dp</dimen>

</resources>

colors.xml文件中的内容:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#ff3300</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#ff4081</color>
    <color name="black">#000000</color>
    <color name="white">#FFFFFF</color>
    <color name="green">#009933</color>
    <color name="color_fab">#009933</color>
    <color name="translate_et_hint">#708090</color>
    <color name="translate_et_background">#F0F0F0</color>
    <color name="text_color_black">#000000</color>
    <color name="et_cursor_black">#000000</color>
    <color name="translate_btn_color">#FFFFFF</color>
    <color name="translate_line_color">#d7d7d7</color>
	
    <color name="button_selected_color">#708090</color>
    <color name="button_normal_color">#FFFFFF</color>

</resources>

由于涉及的内容比较多,初学者理解起来又非常困难,我推荐不是很懂的把布局文件中使用到的这些引用都删除,仅仅使用系统提供的控件进行设置。对于上面的内容,想学习的,我可以提供一个学习的网址,分别关于shape的使用(自定义形状)、按钮点击效果的设置。

shape:http://keeganlee.me/post/android/20150830

按钮点击:https://www.oschina.net/question/12_34274

看一下我们设置的界面效果:


基本就是这样,如果是对于自定义控件不理解的,还是上面说到的,推荐使用默认控件进行设置,把所有涉及到的drawbale下的文件都放弃使用。

3、编辑主界面运行逻辑代码

编辑MainActivity的代码如下所示:

public class MainActivity extends BaseActivity implements View.OnClickListener {

    private static final String TAG = "MainActivity";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) this.findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);       //使用Toolbar

        ActionBar actionBar = getSupportActionBar();  //获取ActionBar实例,具体实现由Toolbar完成
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);          //显示导航按钮设置为true
        }

        Button translation = (Button) findViewById(R.id.btn_translate);

        translation.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        try {

            EditText editText = (EditText) findViewById(R.id.et_translate);

            String word = editText.getText().toString();       //查询文本

            //金山每日一词网址,默认json,使用中
            //String url = "http://open.iciba.com/dsapi/?date=2018-03-09";
            //金山每日一词网址,可选xml,file=xml&  未使用
            //String url = "http://open.iciba.com/dsapi/?file=xml&date=2018-03-10";

            //金山查词网址,默认xml,使用中
            final String urlxml = "http://dict-co.iciba.com/api/dictionary.php?w=" + word + "&key=9AA9FA4923AC16CED1583C26CF284C3F";
            //金山查词网址,可选json,&type=json  ,因为缺少例句,未使用
            String url = "http://dict-co.iciba.com/api/dictionary.php?w=" + word + "&type=json&key=9AA9FA4923AC16CED1583C26CF284C3F";

            if (JinshanParseUtil.isEnglish(word)) {
                HttpUtil.sendOkHttpRequest(urlxml, new Callback() {
                    @Override
                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                        Toast.makeText(MainActivity.this, "获取翻译数据失败!", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException {

                        final String result = response.body().string();
                        Log.d(TAG, result);

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {

                                JinshanParseUtil.parseJinshanEnglishToChineseXMLWithPull(result);

                                SharedPreferences pref = getSharedPreferences("JinshanEnglishToChinese", MODE_PRIVATE);

                                String queryText = pref.getString("queryText", "空");
                                String voiceEnText = pref.getString("voiceEnText", "空");
                                String voiceEnUrlText = pref.getString("voiceEnUrlText", "空");
                                String voiceAmText = pref.getString("voiceAmText", "空");
                                String voiceAmUrlText = pref.getString("voiceAmUrlText", "空");
                                String meanText = pref.getString("meanText", "空");
                                String exampleText = pref.getString("exampleText", "空");

                                TextView query = (TextView) findViewById(R.id.query);
                                TextView voiceMsg = (TextView) findViewById(R.id.voice_msg);
                                TextView baseMean = (TextView) findViewById(R.id.base_mean);
                                TextView examples = (TextView) findViewById(R.id.related_examples);

                                query.setText(queryText);
                                voiceMsg.setText("英式发音:"+voiceEnText+"\n"+"美式发音:"+voiceAmText+
                                        "\n英式发音地址:"+voiceEnUrlText+"\n美式发音地址:"+voiceAmUrlText);
                                baseMean.setText(meanText);
                                examples.setText(exampleText);

                            }
                        });
                    }
                });
            } else {
                HttpUtil.sendOkHttpRequest(url, new Callback() {
                    @Override
                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                        Toast.makeText(MainActivity.this, "获取翻译数据失败!", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException {

                        final String result = response.body().string();
                        Log.d(TAG, result);

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {

                                JinshanParseUtil.parseJinshanChineseToEnglishJSONWithGson(result);

                                SharedPreferences pref = getSharedPreferences("JinshanChineseToEnglishBaseMean", MODE_PRIVATE);

                                String queryText = pref.getString("queryText", "空");
                                String voiceText = pref.getString("voiceText", "空");
                                String voiceUrlText = pref.getString("voiceUrlText", "空");
                                String meanText = pref.getString("meanText", "空");

                                TextView query = (TextView) findViewById(R.id.query);
                                TextView voiceMsg = (TextView) findViewById(R.id.voice_msg);
                                TextView baseMean = (TextView) findViewById(R.id.base_mean);

                                query.setText(queryText);
                                voiceMsg.setText("拼音:"+voiceText+"\n拼音发音:"+voiceUrlText);
                                baseMean.setText(meanText);

                            }
                        });
                    }
                });

                HttpUtil.sendOkHttpRequest(urlxml, new Callback() {
                    @Override
                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                        Toast.makeText(MainActivity.this, "获取翻译数据失败!", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                        final String result = response.body().string();

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                String example = parseJinshanChineseToEnglishXMLWithPull(result);

                                TextView examples = (TextView) findViewById(R.id.related_examples);
                                examples.setText(example);
                            }
                        });

                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //为按钮设置点击事件
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {

            case android.R.id.home:
				//退出程序逻辑
                break;

            default:
        }
        return true;
    }

}

代码很长,不过很好理解,就是为翻译按钮设置点击事件,判断编辑框中的是否是英文,是英文则发送英译汉的网络请求,否则发送汉译英的网络请求。通过解析工具解析后已经存储到了文件中,我们在活动中只需要把需要的值从文件中读取出来并设置到控件中就可以了。

也许你会发现我们继承的是BaseActivity,这是什么?其实,为了充分利用系统的状态栏,我们把他的颜色设置为和标题栏一样的颜色。把它定义在BaseActivity中,可以让其他活动不需要再编写相应的代码,继承它就可以了。也是为了程序的可扩展性。

在项目中New一个类,名为BaseActivity,编写以下代码:

import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

import com.my.wordbar.R;

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base);

        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decorView.setSystemUiVisibility(option);
            getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimary));
        }

    }
}

由于该功能(使用系统状态栏)是Android 5.0系统以后才支持的,所以为了兼容原来的老系统,使用了if判断。

最后我们看一下程序的运行效果:


它是汉英自动识别的哦。我们在程序中已经写过了。这样一个简单的程序就写完了。

再做一下补充,优化一下这个APP。

首先,更改布局,添加一个小喇叭图片。(如果你理解了上面的布局代码,添加一个图片很简单,稍稍改变一下上面的布局)

<ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="2dp"
                android:orientation="vertical">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="3dp"
                        android:text="@string/text_query"
                        android:textColor="@color/text_color_black"
                        android:textSize="15sp" />

                    <TextView
                        android:id="@+id/query"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="3dp"
                        android:text="@string/text_null" />

                </LinearLayout>

                <include
                    layout="@layout/cut_line"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="3dp"
                    android:text="@string/voice_msg"
                    android:textColor="@color/text_color_black"
                    android:textSize="15sp" />

                <!--<TextView-->
                    <!--android:id="@+id/voice_msg"-->
                    <!--android:layout_width="wrap_content"-->
                    <!--android:layout_height="wrap_content"-->
                    <!--android:layout_marginTop="5dp"-->
                    <!--android:text="@string/text_null" />-->

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:orientation="horizontal">

                    <TextView
                        android:id="@+id/en_voice"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/translate_voice_en_text" />

                    <ImageView
                        android:id="@+id/iv_en_voice"
                        android:layout_width="18dp"
                        android:layout_height="18dp"
                        android:layout_marginLeft="5dp"
                        android:layout_marginStart="5dp"
                        android:layout_marginRight="8dp"
                        android:layout_marginEnd="8dp"
                        android:paddingTop="2dp"
                        android:paddingBottom="0dp"
                        android:src="@drawable/iv_voice"/>

                    <TextView
                        android:id="@+id/en_voice_text"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/text_null"/>

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:orientation="horizontal">

                    <TextView
                        android:id="@+id/am_voice"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/translate_voice_am_text" />

                    <ImageView
                        android:id="@+id/iv_am_voice"
                        android:layout_width="18dp"
                        android:layout_height="18dp"
                        android:layout_marginLeft="5dp"
                        android:layout_marginStart="5dp"
                        android:layout_marginRight="8dp"
                        android:layout_marginEnd="8dp"
                        android:paddingTop="2dp"
                        android:paddingBottom="0dp"
                        android:src="@drawable/iv_voice"/>

                    <TextView
                        android:id="@+id/am_voice_text"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/text_null"/>

                </LinearLayout>


                <include
                    layout="@layout/cut_line"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="3dp"
                    android:text="@string/base_mean"
                    android:textColor="@color/text_color_black"
                    android:textSize="15sp" />

                <TextView
                    android:id="@+id/base_mean"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:text="@string/text_null" />

                <include
                    layout="@layout/cut_line"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:text="@string/related_examples"
                    android:textColor="@color/text_color_black"
                    android:textSize="15sp" />

                <TextView
                    android:id="@+id/related_examples"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="3dp"
                    android:text="@string/text_test" />

            </LinearLayout>

        </ScrollView>

我把更改之后的都贴出来吧,布局效果就变成了这个样子。


再翻译一下“good”,如下所示:


汉字没有英美发音,不过可以在程序代码中动态修改,效果如下:


怎么获取网上的音频并播放呢?其实也很简单,使用MediaPlayer来加载、播放音频。在对应活动的代码中做这样的修改;

EditText editText = (EditText) findViewById(R.id.et_translate);

            String word = editText.getText().toString();       //查询文本

            //金山每日一词网址,默认json,使用中
            //String url = "http://open.iciba.com/dsapi/?date=2018-03-09";
            //金山每日一词网址,可选xml,file=xml&  未使用
            //String url = "http://open.iciba.com/dsapi/?file=xml&date=2018-03-10";

            //金山查词网址,默认xml,使用中
            final String urlxml = "http://dict-co.iciba.com/api/dictionary.php?w=" + word + "&key=9AA9FA4923AC16CED1583C26CF284C3F";
            //金山查词网址,可选json,&type=json  ,因为缺少例句,未使用
            String url = "http://dict-co.iciba.com/api/dictionary.php?w=" + word + "&type=json&key=9AA9FA4923AC16CED1583C26CF284C3F";

            if (JinshanParseUtil.isEnglish(word)) {
                HttpUtil.sendOkHttpRequest(urlxml, new Callback() {
                    @Override
                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                        Toast.makeText(TranslateActivity.this, "获取翻译数据失败!", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException {

                        final String result = response.body().string();
                        Log.d(TAG, result);

                        runOnUiThread(new Runnable() {
                            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
                            @Override
                            public void run() {

                                JinshanParseUtil.parseJinshanEnglishToChineseXMLWithPull(result);

                                SharedPreferences pref = getSharedPreferences("JinshanEnglishToChinese", MODE_PRIVATE);

                                String queryText = pref.getString("queryText", "空");
                                final String voiceEnText = pref.getString("voiceEnText", "空");
                                final String voiceEnUrlText = pref.getString("voiceEnUrlText", "空");
                                String voiceAmText = pref.getString("voiceAmText", "空");
                                final String voiceAmUrlText = pref.getString("voiceAmUrlText", "空");
                                String meanText = pref.getString("meanText", "空");
                                String exampleText = pref.getString("exampleText", "空");

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

                                TextView enVoiceLab = (TextView) findViewById(R.id.en_voice);
                                ImageView enVoiceImg = (ImageView) findViewById(R.id.iv_en_voice);
                                TextView enVoiceText = (TextView) findViewById(R.id.en_voice_text);

                                TextView amVoiceLab = (TextView) findViewById(R.id.am_voice);
                                ImageView amVoiceImg = (ImageView) findViewById(R.id.iv_am_voice);
                                TextView amVoiceText = (TextView) findViewById(R.id.am_voice_text);

                                TextView baseMean = (TextView) findViewById(R.id.base_mean);
                                TextView examples = (TextView) findViewById(R.id.related_examples);

                                enVoiceLab.setText("英式发音:");
                                amVoiceLab.setVisibility(View.VISIBLE);
                                amVoiceImg.setVisibility(View.VISIBLE);
                                amVoiceText.setVisibility(View.VISIBLE);

                                query.setText(queryText);
                                enVoiceText.setText(voiceEnText);
                                amVoiceText.setText(voiceAmText);

                                baseMean.setText(meanText);
                                examples.setText(exampleText);

                                enVoiceImg.setOnClickListener(new View.OnClickListener() {
                                    @Override
                                    public void onClick(View v) {

                                        try {
                                            MediaPlayer mediaPlayer;
                                            mediaPlayer = MediaPlayer.create(TranslateActivity.this,Uri.parse(voiceEnUrlText));
                                            mediaPlayer.start();
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    }
                                });

                                amVoiceImg.setOnClickListener(new View.OnClickListener() {
                                    @Override
                                    public void onClick(View v) {

                                        try {
                                            MediaPlayer mediaPlayer;
                                            mediaPlayer = MediaPlayer.create(TranslateActivity.this,Uri.parse(voiceAmUrlText));
                                            mediaPlayer.start();
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }

                                    }
                                });

                            }
                        });
                    }
                });
            } else {
                HttpUtil.sendOkHttpRequest(url, new Callback() {
                    @Override
                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                        Toast.makeText(TranslateActivity.this, "获取翻译数据失败!", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException {

                        final String result = response.body().string();
                        Log.d(TAG, result);

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {

                                JinshanParseUtil.parseJinshanChineseToEnglishJSONWithGson(result);

                                SharedPreferences pref = getSharedPreferences("JinshanChineseToEnglish", MODE_PRIVATE);

                                String queryText = pref.getString("queryText", "空");
                                String voiceText = pref.getString("voiceText", "空");
                                final String voiceUrlText = pref.getString("voiceUrlText", "空");
                                String meanText = pref.getString("meanText", "空");

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

                                TextView enVoiceLab = (TextView) findViewById(R.id.en_voice);
                                ImageView enVoiceImg = (ImageView) findViewById(R.id.iv_en_voice);
                                TextView enVoiceText = (TextView) findViewById(R.id.en_voice_text);

                                TextView amVoiceLab = (TextView) findViewById(R.id.am_voice);
                                ImageView amVoiceImg = (ImageView) findViewById(R.id.iv_am_voice);
                                TextView amVoiceText = (TextView) findViewById(R.id.am_voice_text);

                                TextView baseMean = (TextView) findViewById(R.id.base_mean);

                                enVoiceLab.setText("拼音:");
                                amVoiceLab.setVisibility(View.GONE);
                                amVoiceImg.setVisibility(View.GONE);
                                amVoiceText.setVisibility(View.GONE);

                                query.setText(queryText);
                                enVoiceText.setText(voiceText);
                                baseMean.setText(meanText);

                                enVoiceImg.setOnClickListener(new View.OnClickListener() {
                                    @Override
                                    public void onClick(View v) {
                                        try {
                                            MediaPlayer mediaPlayer;
                                            mediaPlayer = MediaPlayer.create(TranslateActivity.this,Uri.parse(voiceUrlText));
                                            mediaPlayer.start();
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    }
                                });

                            }
                        });
                    }
                });

                HttpUtil.sendOkHttpRequest(urlxml, new Callback() {
                    @Override
                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                        Toast.makeText(TranslateActivity.this, "获取翻译数据失败!", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                        final String result = response.body().string();

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                String example = parseJinshanChineseToEnglishXMLWithPull(result);

                                TextView examples = (TextView) findViewById(R.id.related_examples);
                                examples.setText(example);
                            }
                        });

                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
还是对照上面的代码进行修改就好了。然后运行程序,点击小喇叭,稍等一下加载,就可以发声了。

当然,这个发声的bug还是有的,比如加载时间受网络影响,不确定时间。点一次就要加载一次,浪费时间。影响用户体验。而且作为一个耗时操作,还需要新建线程,去处理发声。你需要做的工作就是在解析的时候就把音频文件下载下来。然后主界面的喇叭点击事件直接调用资源就行了。还要加上对MediaPlayer的资源释放,判断其长度,播放完成就释放资源。这些都需要你自己去优化了。由于这是我编写项目中的一部分,还没有进行优化,TranslateActivity也需要你改成MainActivity才能和开始编写的代码更好的融合。好了,就说到这里吧。


写在最后:

程序还有很多值得优化的地方,添加退出程序代码,代码逻辑优化、bug排查等。

虽然代码讲解的比较乱,肯让看了之后你不是很懂,但是你的目标不应该是复制粘贴,而是学习如何编写一个翻译软件的思路。接口信息和返回数据方面我讲解的很详细,你只需要深入了解解析过程,所有界面都可以自己定义了。

另外,文中代码未涉及包名等信息,使用时一定要注意。因为这是我从项目中复制过来的。接下来我会更新这个项目的所有实现。不过由于还在编写优化中,所以时间并不确定,不过相信很快就会完成。。

由于代码涉及的东西很多,对于初学者来说并不简单,我没有做特别多的说明,真的特别抱歉,我不能长篇大论的逐句讲解。我也是自学,希望你也能培养这样的能力。最主要的是学习思路。而不是代码。


原创文章,转载请注明出处,谢谢。http://blog.csdn.net/lone1ycode/article/details/79540647


已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页