惯例先下载地址:https://yunpan.cn/cqt5xp3QAJsSM (提取码:ffaa)
总结一下(三)中的小问题,
好,继续
这一次先介绍一下金山词霸api http://open.iciba.com/?c=api
点击文档
点击查词接口 总结起来就是 将http://dict-co.iciba.com/api/dictionary.php?w=go&key=**这样一个地址的XML文件解析,就会有
'word_name':'' #单词
'exchange': '' #单词的各种时态
'symbols':'' #单词各种信息 下面字段都是这个字段下面的
'ph_en': '' #英式音标
'ph_am': '' #美式音标
'ph_en_mp3':'' #英式发音
'ph_am_mp3': '' #美式发音
'ph_tts_mp3': '' #TTS发音
'parts':'' #词的各种意思
这样的数据出来(地址中go就是要查的词,*就是一会要申请的KEY).
再点击每日一句,下载http://open.iciba.com/dsapi/?地址的json文件(这里为了在练习一下json解析就与查词区分开,实际也可用xml)就会有
JSON 字段解释
{
'sid':'' #每日一句ID
'tts': '' #音频地址
'content':'' #英文内容
'note': '' #中文内容
'love': '' #每日一句喜欢个数
'translation':'' #词霸小编
'picture': '' #图片地址
'picture2': '' #大图片地址
'caption':'' #标题
'dateline':'' #时间
's_pv':'' #浏览数
'sp_pv':'' #语音评测浏览数
'tags':'' #相关标签
'fenxiang_img':'' #合成图片,建议分享微博用的
}
那么总结起来就是两步
写上邮箱,剩下两个没有多大意义,然后就会有一个key发到你的邮箱里,之后代替之前的**就可以了.
XML解析常用dom,sax,pull…根据熟练度我选择了sax,而且这个文件非常简单,也不用去做循环之类的操作.
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.entity.WordValue;
public class JinShanContentHandler extends DefaultHandler {
public WordValue wordValue = null;
private String tagName = null;
private String interpret = ""; // 防止空指针异常
private String orig = "";
private boolean isChinese = false;
public JinShanContentHandler() {
wordValue = new WordValue();
isChinese = false;
}
public WordValue getWordValue() {
return wordValue;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
if (length <= 0)
return;
for (int i = start; i < start + length; i++) {
if (ch[i] == '\n')
return;
}
// 去除莫名其妙的换行!
String str = new String(ch, start, length);
if (tagName == "key") {
wordValue.setWord(str);
} else if (tagName == "ps") {
if (wordValue.getPsE().length() <= 0) {
wordValue.setPsE(str);
} else {
wordValue.setPsA(str);
}
} else if (tagName == "pron") {
if (wordValue.getPronE().length() <= 0) {
wordValue.setPronE(str);
} else {
wordValue.setPronA(str);
}
} else if (tagName == "pos") {
isChinese = false;
interpret = interpret + str + " ";
} else if (tagName == "acceptation") {
interpret = interpret + str + "\n";
interpret = wordValue.getInterpret() + interpret;
wordValue.setInterpret(interpret);
interpret = ""; // 初始化操作,预防有多个释义
} else if (tagName == "orig") {
orig = wordValue.getSentOrig();
wordValue.setSentOrig(orig + str + "\n");
} else if (tagName == "trans") {
String temp = wordValue.getSentTrans() + str + "\n";
wordValue.setSentTrans(temp);
} else if (tagName == "fy") {
isChinese = true;
wordValue.setInterpret(str);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
tagName = null;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
tagName = localName;
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
if (isChinese)
return;
String interpret = wordValue.getInterpret();
if (interpret != null && interpret.length() > 0) {
char[] strArray = interpret.toCharArray();
wordValue.setInterpret(new String(strArray, 0, interpret.length() - 1));
// 去掉解释的最后一个换行符
}
}
}
XMLParser类
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
public class XMLParser {
public SAXParserFactory factory = null;
public XMLReader reader = null;
public XMLParser() {
try {
factory = SAXParserFactory.newInstance();
reader = factory.newSAXParser().getXMLReader();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void parseJinShanXml(DefaultHandler content, InputSource inSource) {
if (inSource == null)
return;
try {
reader.setContentHandler(content);
reader.parse(inSource);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
接下就是NetOperate,关于网络的操作类
public class NetOperator {
public final static String iCiBaURL1 = "http://dict-co.iciba.com/api/dictionary.php?w=";
public final static String iCiBaURL2 = "&key=1A107615BE5622161FF987B90758D04B"; // key
public static boolean IsHaveInternet(Context context) {
try {
ConnectivityManager manger = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manger.getActiveNetworkInfo();
return (info != null && info.isConnected());
} catch (Exception e) {
return false;
}
}
public static InputStream getInputStreamByUrl(String urlStr) {
InputStream tempInput = null;
URL url = null;
HttpURLConnection connection = null;
// 设置超时时间
try {
url = new URL(urlStr);
connection = (HttpURLConnection) url.openConnection(); // 别忘了强制类型转换
connection.setConnectTimeout(8000);
connection.setReadTimeout(10000);
tempInput = connection.getInputStream();
} catch (Exception e) {
e.printStackTrace();
}
return tempInput;
}
@SuppressWarnings("deprecation")
public static WordValue getWordFromInternet(String searchedWord) {
WordValue wordValue = null;
String tempWord = searchedWord;
if (tempWord.equals(""))
return null;
char[] array = tempWord.toCharArray();
if (array[0] > 256) // 是中文,或其他语言的的简略判断
tempWord = "_" + URLEncoder.encode(tempWord);
InputStream in = null;
try {
String tempUrl = NetOperator.iCiBaURL1 + tempWord + NetOperator.iCiBaURL2;
in = NetOperator.getInputStreamByUrl(tempUrl); // 从网络获得输入流
if (in != null) {
// new FileUtils().saveInputStreamToFile(in, "", "gfdgf.txt");
XMLParser xmlParser = new XMLParser();
InputStreamReader reader = new InputStreamReader(in, "utf-8"); // 最终目的获得一个InputSource对象用于传入形参
JinShanContentHandler contentHandler = new JinShanContentHandler();
xmlParser.parseJinShanXml(contentHandler, new InputSource(reader));
wordValue = contentHandler.getWordValue();
wordValue.setWord(searchedWord);
}
} catch (Exception e) {
e.printStackTrace();
}
return wordValue;
}
}
再来一个单词MP3的播放类
package com.search;
import java.io.InputStream;
import com.entity.WordValue;
import com.internet.NetOperator;
import com.util.Dict;
import com.util.FileUtils;
import android.content.Context;
import android.media.MediaPlayer;
import android.net.Uri;
public class Mp3Player {
public final static String MUSIC_ENG_RELATIVE_PATH = "CET4/sounds/sounds_EN/";
public final static String MUSIC_USA_RELATIVE_PATH = "CET4/sounds/sounds_US/";
public final static int ENGLISH_ACCENT = 0;
public final static int USA_ACCENT = 1;
public Context context = null;
public String tableName = null;
public MediaPlayer mediaPlayer = null;
FileUtils fileU = null;
Dict dict = null;
public boolean isMusicPermitted = true; // 用于对是否播放音乐进行保护性设置,当该变量为false时,可以阻止一次音乐播放
public Mp3Player(Context context, String tableName) {
this.context = context;
this.tableName = tableName;
fileU = new FileUtils();
dict = new Dict(context, tableName);
isMusicPermitted = true;
}
/**
* 首先先看一下SD卡上有没有,若有则播放,没有执行下一步
* 看一下dict表中有没有单词的记录,若有,看一下发音字段是不是有美式发音或英式发音,若无则退出 若没有字段记录,访问网络下载Mp3然后播放
* 一个Activity中一般只能有一个Voice成员变量,对应的也就只有一个MediaPlayer对象,这样才能对播放 状态进行有效控制
* 该方法原则上只能在线程中调用
*
* @param word
* @param accent
*/
public void playMusicByWord(String word, int accent, boolean isAllowedToUseInternet, boolean isPlayRightNow) {
if (word == null || word.length() <= 0)
return;
char[] wordArray = word.toCharArray();
char initialCharacter = wordArray[0];
String path = null;
String pronUrl = null;
WordValue w = null;
if (accent == ENGLISH_ACCENT) {
path = MUSIC_ENG_RELATIVE_PATH;
} else {
path = MUSIC_USA_RELATIVE_PATH;
}
if (fileU.isFileExist(path + initialCharacter + "/", "-$-" + word + ".mp3") == false) {
if (isAllowedToUseInternet == false)
return;
// 为了避免多次多个线程同时访问网络下载同一个文件,这里加了这么一个控制变量
if (dict.isWordExist(word) == false) { // 数据库中没有单词记录,从网络上进行同步
if ((w = dict.getWordFromInternet(word)) == null) {
return;
}
dict.insertWordToDict(w, true);
} // 能走到这一步说明从网上同步成功,数据库中一定存在单词记录
if (accent == ENGLISH_ACCENT) {
pronUrl = dict.getPronEngUrl(word);
} else {
pronUrl = dict.getPronUSAUrl(word);
}
if (pronUrl == null || pronUrl == "null" || pronUrl.length() <= 0)
return; // 这说明网络上也没有对应发音,故退出
// 得到了Mp3地址后下载到文件夹中然后进行播放
InputStream in = null;
in = NetOperator.getInputStreamByUrl(pronUrl);
if (in == null)
return;
if (fileU.saveInputStreamToFile(in, path + initialCharacter + "/", "-$-" + word + ".mp3") == false)
return;
}
// 走到这里说明文件夹里一定有响应的音乐文件,故在这里播放
if (isPlayRightNow == false)
return;
/**
* 这个方法存在缺点,可能因为同时new 了多个MediaPlayer对象,导致start方法失效,
* 因此解决的方法是,使用同一个MediaPlayer对象,若一次播放时发现对象非空,那么先
* 调用release()方法释放资源,再重新create
*/
if (isMusicPermitted == false) {
return;
}
try {
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying())
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null; // 为了防止mediaPlayer多次调用stop release,这里置空还是有必要
}
mediaPlayer = MediaPlayer.create(context,
Uri.parse("file://" + fileU.getSDRootPath() + path + initialCharacter + "/-$-" + word + ".mp3"));
mediaPlayer.start();
} catch (Exception e) {
mediaPlayer.release();
e.printStackTrace();
}
}
}
有了以上类就可以得到数据了,之后将数据存入数据库中..上面每一个类都有判断在数据库的表中是否存在,数据库在上一篇中已经完成,至此下载数据就已经完成,接下来就是显示.
首先布局文件
activity_search.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/adLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
LinearLayout>
<ScrollView
android:id="@+id/sv_intercept"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<include layout="@layout/search_record" />
<include layout="@layout/search_search" />
<View
android:id="@+id/view_scroll_flag"
android:layout_width="match_parent"
android:layout_height="1dp" />
<include layout="@layout/search_pronounce" />
<include layout="@layout/search_meaning" />
<include layout="@layout/search_eg" />
LinearLayout>
ScrollView>
LinearLayout>
我在其中用了很多include,这样可以让布局文件变得更清晰.
有关流式查询记录的布局(下一篇会介绍)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.search.FlowLayout
android:id="@+id/id_flowlayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/record_bg"
android:padding="20dp" >
com.search.FlowLayout>
RelativeLayout>
查询块:
search_search.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="42dp"
android:layout_marginLeft="40dp"
android:layout_marginRight="30dp"
android:layout_marginTop="40dp"
android:focusable="true"
android:focusableInTouchMode="true" >
<ImageButton
android:id="@+id/image_btn_search"
android:layout_width="45dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="@android:color/transparent"
android:src="@android:drawable/ic_menu_search" />
<EditText
android:id="@+id/edit_text_search"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/image_btn_search"
android:layout_weight="1"
android:gravity="center_vertical"
android:hint="请在此输入单词"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:paddingLeft="10dp"
/>
RelativeLayout>
发音块:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_word"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginBottom="3dp"
android:layout_marginLeft="22dp"
android:layout_marginRight="22dp"
android:layout_marginTop="36dp"
android:layout_weight="1"
android:gravity="bottom"
android:textColor="#3B3C3D"
android:textSize="24sp"
android:textStyle="bold" />
<Button
android:id="@+id/image_btn_add_to_wordlist"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="30dp"
android:layout_marginTop="10dp"
android:background="@android:color/transparent"
android:scaleType="fitXY"
android:text="☆" />
LinearLayout>
<RelativeLayout
android:id="@+id/phonetic_bar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_below="@id/tv_word"
android:layout_marginLeft="22dp" >
<ImageButton
android:id="@+id/image_btn_pronounce_eng"
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_marginBottom="7dp"
android:layout_marginTop="7dp"
android:background="@android:color/transparent"
android:scaleType="fitCenter"
android:src="@drawable/pronounce" />
<TextView
android:id="@+id/tv_phosymbol_eng"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toRightOf="@id/image_btn_pronounce_eng"
android:gravity="center_vertical"
android:text="英[]"
android:textColor="#6C6C6C"
android:textSize="14sp" />
<TextView
android:id="@+id/text_dict_phosymbol_divider"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/tv_phosymbol_eng"
android:gravity="center"
android:text="/"
android:textColor="#6C6C6C"
android:textSize="14sp" />
<ImageButton
android:id="@+id/image_btn_pronounce_usa"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_marginBottom="7dp"
android:layout_marginTop="7dp"
android:layout_toRightOf="@id/text_dict_phosymbol_divider"
android:background="@android:color/transparent"
android:scaleType="fitCenter"
android:src="@drawable/pronounce" />
<TextView
android:id="@+id/tv_phosymbol_usa"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toRightOf="@id/image_btn_pronounce_usa"
android:gravity="center_vertical"
android:text="美[]"
android:textColor="#6C6C6C"
android:textSize="14sp" />
RelativeLayout>
LinearLayout>
发音块:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/dict_interpret_divider"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginLeft="10dp"
android:orientation="horizontal" >
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="基本释义"
android:textColor="#00A2DC"
android:textSize="14sp" />
LinearLayout>
<TextView
android:id="@+id/tv_meaning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/dict_interpret_divider"
android:layout_margin="10dp"
android:background="@drawable/layer_list_dict_item_back"
android:lineSpacingMultiplier="1.2"
android:padding="12dp"
android:text=""
android:textColor="#3C3C3C"
android:textSize="14sp" />
LinearLayout>
例句块:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/dict_sentence_divider"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginLeft="10dp"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="2" >
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="例句"
android:textColor="#00A2DC"
android:textSize="14sp" />
LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" >
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="bottom"
android:text="supported by iCIBA"
android:textColor="#6C6C6C"
android:textSize="10sp" />
RelativeLayout>
LinearLayout>
<com.search.EgListView
android:id="@+id/listview_eg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:divider="#8C8C8C"
android:dividerHeight="0px"
android:padding="12dp" >
com.search.EgListView>
LinearLayout>
布局文件中并没有特殊之处,整体就是一个scrollview上面放了乱七八糟一堆控件和一个自定义Listview.
下一章:自定义listview和历史记录的流式自定义布局