最近在做一個單詞本的項目實戰,其中涉及到一個功能,英漢互譯。在衆多翻譯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