"天天背单词"--的设计与实现(四)

金山API介绍、xml解析数据、查词布局

惯例先下载地址:https://yunpan.cn/cqt5xp3QAJsSM (提取码:ffaa)

总结一下(三)中的小问题,

  1. Activity继承的是FragmentActivity,当然是因为要往里面方Fragment…..他是3.0以后的东西,为了在低版本中使用Fragment就要用到v4包,而FragmentActivity就是这个兼容包里面的,它提供了操作Fragment的一些方法,其功能跟3.0及以后的版本的Activity的功能一样。
    下面是API中的原话:
    FragmentActivity is a special activity provided in the Support Library to handle fragments on system versions older than API level 11. If the lowest system version you support is API level 11 or higher, then you can use a regular Activity.
    说到这就出现了下面
  2. 导包要一致,若用V4的Fragment那么一定全要用V4,否则全不用V4,混合搭配就会出现找不到方法等等的错误,其获得Manager的方式也有不同
    3.0以下:getSupportFragmentManager()
    3.0以上:getFragmentManager()

好,继续
这一次先介绍一下金山词霸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':'' #合成图片,建议分享微博用的
    }

那么总结起来就是两步

  1. 申请key
  2. 解析XML

这里写图片描述
写上邮箱,剩下两个没有多大意义,然后就会有一个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

<?xml version="1.0" encoding="utf-8"?>
<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,这样可以让布局文件变得更清晰.

有关流式查询记录的布局(下一篇会介绍)

<?xml version="1.0" encoding="utf-8"?>
<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

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="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>

发音块:

<?xml version="1.0" encoding="utf-8"?>
<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>

发音块:

<?xml version="1.0" encoding="utf-8"?>
<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>

例句块:

<?xml version="1.0" encoding="utf-8"?>
<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和历史记录的流式自定义布局

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qi T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值