android 项目练习:自己的词典app——生词本(一)

前言:

自学android差不多两个月了,由于本身对英语不感冒,而且记英语单词总是很快忘记,因此学习的过程也是蛮累的,好多类和方法都不知道啥意思,还要去查词典才知道。

还是延续我读书时的记忆方法——每次遇到生词就写在笔记本上,下次在遇到就算不记得中文意思,也能记得写过这个单词,然后就是找笔记本就可以了。不过那,这种方法也有个问题——自己的字太丑,每次都是找了好久都没找那个词,其实明明在哪里,只是快速扫看不到o(╯□╰)o。

后来,就想找一个背单词的app,可以把我不认识的生词添加到一个生词本,可以快速浏览生词本里的单词,也可以仅仅针对生词本里的词出一些帮助记忆的练习题?又想,既然我在学android,为什么不自己做一个那?于是就有了这个项目练习!

项目实现:

我在网上找到了一个前辈分享的类似的app编写过程,发现其中很多内容都是我会的,于是我就参考着自己动手写起来。

由于这个项目不是完成后才开始写这篇博客,是我边实践边写的,因此整体思路是根据我的写代码进度来的,在写这里的时候刚实现了查单词的界面和完整功能。

查词界面:

先来看下这个界面的功能和实现思路:

(一)肯定是要能查单词

简单的实现思路就是使用现有词典的API接口,我采用的是金山词霸的API接口,地址:http://open.iciba.com/。优点是这个接口会返回发音MP3的http地址。

查词接口:http://dict-co.iciba.com/api/dictionary.php?w=go&key=** 这里的key是你自己申请的金山词霸开放平台的API key。

打开后是这样的:

<dict num="219" id="219" name="219">
<key>go</key>
<ps>g??</ps>
<pron>http://res.iciba.com/resource/amp3/0/0/34/d1/34d1f91fb2e514b8576fab1a75a89a6b.mp3</pron>
<ps>go?</ps>
<pron>http://res.iciba.com/resource/amp3/1/0/34/d1/34d1f91fb2e514b8576fab1a75a89a6b.mp3</pron>
<pos>vi.</pos><acceptation>走;离开;去做;进行;
</acceptation>
<pos>vt.</pos>
<acceptation>变得;发出…声音;成为;处于…状态;</acceptation>
<pos>n.</pos>
<acceptation>轮到的顺序;精力;干劲;尝试;</acceptation>
<sent><orig>
Go is an irregular verb.
</orig><trans>
go是个不规则动词.
</trans></sent><sent><orig>
Kyong - go means a warning or half - point deduction and gam - jeom means a one - point deduction.
</orig><trans>
Kyoug -go是指一次警告或被扣减半分, gam -jeom是指被扣减1分.
</trans></sent><sent><orig>
From the get - go means from the beginning.
</orig><trans>
原来fromtheget-go 就是一开始的时候.
</trans></sent><sent><orig>
With the reduction of SRWC, GO activity decreased mild water stress and increased water stress.
</orig><trans>
随着土壤相对含水量的下降,GO酶括性在土壤水分含量下降时首先降低,以后又逐渐上升.
</trans></sent><sent><orig>
We proved orthocompactness and weakly suborthocompactness are equivalent for all subspaces of product of two GO - space.
</orig><trans>
证明了GO - 空间子空间的正交紧性和弱子正交紧性是等价的.
</trans></sent></dict>

因此这里就要用到Http网络访问和XML解析。为避免重复访问网络,我们可以将解析出来单词的数据保存在本地,这样下次在查到该词是可以直接从本地读取了,同样的我们可以直接把MP3文件也保存本地。

分析完成后开始动手,首先按功能分模块,这方面由于我是新手,就是按照自己看的清晰的方式来,新建一个util包,这里都是放一些工具类。然后新建一个类HttpUtil,通过HttpURLConnection实现网络访问功能:

public class HttpUtil {
    /**
     * 在新线程中发送网络请求
     *
     * @param address  网络地址
     * @param listener HttpCallBackListener接口的实现类;
     *                 onFinish方法为访问成功后的回调方法;
     *                 onError为访问不成功时的回调方法
     */
    public static void sentHttpRequest(final String address, final HttpCallBackListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                try {
                    URL url = new URL(address);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    InputStream inputStream = connection.getInputStream();
                    if (listener != null) {
                        listener.onFinish(inputStream);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    if (listener != null) {
                        listener.onError();
                    }
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

android网络访问不能在UI线程中进行,避免阻塞,因此,这里我直接在新线程实现,根据目的需要,完成网络请求后要对返回的XML文件进行解析,因此方法第二参数传入HttpCallBackListener接口的实现类,分别对应onFinish方法为访问成功后的回调方法,onError为访问不成功时的回调方法。

接下来是XML解析了,我采用的是SAX解析方法。

我们先分析下XML文件,看看有哪些节点:

key:单词本身; ps:第一个是英音音标,第二个是美音音标; pron第一个是英音的MP3地址,第二个是美音的;pos 词性; acception 词义;sent 例句; orig例句英语;trans例句中文翻译。

这个api接口也可以查中文,只需要在待查的词前面加上一个下划线 _ 即可,如 :_你好。

<dict num="219" id="219" name="219">
<key>你好</key>
<fy>Hello</fy>
<sent>
<orig>Hello! Hello! Hello! Hello! Hel - lo!</orig>
<trans>你好! 你好! 你好! 你好! 你好!</trans>
</sent>
<sent>
<orig>Hello! Hello! Hello! Hello ! I‘m glad to meet you.</orig>
<trans>你好! 你好! 你好! 你好! 见到你很高兴.</trans>
</sent>
<sent>
<orig>Hello Marie. Hello Berlioz. Hello Toulouse.</orig>
<trans>你好玛丽, 你好柏里欧, 你好图鲁兹.</trans>
</sent>
<sent>
<orig>
B Hi Gao. How are you doing? It‘s good to meet you.
</orig>
<trans>B你好,高. 你好 吗 ?很高兴认识你.</trans>
</sent>
<sent>
<orig>
Grant: Hi , Tess. Hi , Jenna. Are you doing your homework?
</orig>
<trans>格兰特: 你好! 苔丝. 你好! 詹娜. 你们在做家庭作业 吗 ?</trans>
</sent>
</dict>

可以看到查中文的话会多一个属性:fy 即中文的英文翻译,要一起考虑进去。

因为要把查到单词的内容保存本地,我们就要建一个Words类用来管理xml解析出来的内容,新建一个model包,在其下新建一个Words类:

public class Words {
    //中英文标记
    private boolean isChinese;
    //要翻译的单词,可以是中文;
    private String key;
    //key为中文时的翻译
    private String fy;
    //英音发音
    private String psE;
    //英音发音的mp3地址
    private String pronE;
    //美音发音
    private String psA;
    //美音发音的mp3地址
    private String pronA;
    //单词的词性与词义
    private String posAcceptation;
    //例句
    private String sent;

    public Words() {
        this.key = "";
        this.fy = "";
        this.psE = "";
        this.pronE = "";
        this.psA = "";
        this.pronA = "";
        this.posAcceptation = "";
        this.sent = "";
        this.isChinese = false;
    }

    public Words(boolean isChinese, String key, String fy, String psE,
                 String pronE, String psA, String pronA, String posAcceptation, String sent) {
        this.isChinese = isChinese;
        this.key = key;
        this.fy = fy;
        this.psE = psE;
        this.pronE = pronE;
        this.psA = psA;
        this.pronA = pronA;
        this.posAcceptation = posAcceptation;
        this.sent = sent;
    }

    public boolean getIsChinese() {
        return isChinese;
    }

    public void setIsChinese(boolean isChinese) {
        this.isChinese = isChinese;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getFy() {
        return fy;
    }

    public void setFy(String fy) {
        this.fy = fy;
    }

    public String getPsE() {
        return psE;
    }

    public void setPsE(String psE) {
        this.psE = psE;
    }

    public String getPronE() {
        return pronE;
    }

    public void setPronE(String pronE) {
        this.pronE = pronE;
    }

    public String getPsA() {
        return psA;
    }

    public void setPsA(String psA) {
        this.psA = psA;
    }

    public String getPronA() {
        return pronA;
    }

    public void setPronA(String pronA) {
        this.pronA = pronA;
    }

    public String getPosAcceptation() {
        return posAcceptation;
    }

    public void setPosAcceptation(String posAcceptation) {
        this.posAcceptation = posAcceptation;
    }

    public String getSent() {
        return sent;
    }

    public void setSent(String sent) {
        this.sent = sent;
    }
}

其中只包含一些成员变量,对应我们需要的内容,还有各自的get()和set()方法。

接着在util包下新建一个WordsHandler类继承自DefaultHandler,这个类中解析XML内容成一个words对象:

public class WordsHandler extends DefaultHandler {
    //记录当前节点
    private String nodeName;
    private Words words;
    //单词的词性与词义
    private StringBuilder posAcceptation;
    //例句
    private StringBuilder sent;

    /**
     * 获取解析后的words对象
     */
    public Words getWords() {
        return words;
    }

    //开始解析XML时调用
    @Override
    public void startDocument() throws SAXException {
        //初始化
        words = new Words();
        posAcceptation = new StringBuilder();
        sent = new StringBuilder();
    }

    //结束解析XML时调用
    @Override
    public void endDocument() throws SAXException {
        //将所有解析出来的内容赋予words
        words.setPosAcceptation(posAcceptation.toString().trim());
        words.setSent(sent.toString().trim());
    }

    //开始解析节点时调用
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        nodeName = localName;
    }

    //结束解析节点时调用
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        //在读完整个节点后换行
        if ("acceptation".equals(localName)) {
            posAcceptation.append("\n");
        } else if ("orig".equals(localName)) {
            sent.append("\n");
        } else if ("trans".equals(localName)) {
            sent.append("\n");
            sent.append("\n");
        }
    }

    //获取节点中内容时调用
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String a = new String(ch, start, length);
        //去掉文本中原有的换行
        for (int i = start; i < start + length; i++) {
            if (ch[i] == ‘\n‘)
                return;
        }
        //将节点的内容存入Words对象对应的属性中
        if ("key".equals(nodeName)) {
            words.setKey(a.trim());
        } else if ("ps".equals(nodeName)) {
            if (words.getPsE().length() <= 0) {
                words.setPsE(a.trim());
            } else {
                words.setPsA(a.trim());
            }
        } else if ("pron".equals(nodeName)) {
            if (words.getPronE().length() <= 0) {
                words.setPronE(a.trim());
            } else {
                words.setPronA(a.trim());
            }
        } else if ("pos".equals(nodeName)) {
            posAcceptation.append(a);
        } else if ("acceptation".equals(nodeName)) {
            posAcceptation.append(a);
        } else if ("orig".equals(nodeName)) {
            sent.append(a);
        } else if ("trans".equals(nodeName)) {
            sent.append(a);
        } else if ("fy".equals(nodeName)) {
            words.setFy(a);
            words.setIsChinese(true);
        }
    }
}

在这里,如何对解析出来的文本重新排版换行这个问题卡了我好几个小时,最后终于找到解决方法,我在昨天的一篇博客中有分享:SAX解析中换行问题解决

接着,同样是util包下新建一个ParseXML类,作为解析XML的工具类:

public class ParseXML {
    /**
     * 使用SAX解析XML的方法
     */
    public static void parse(DefaultHandler handler, InputStream inputStream) {
        try {
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader reader = new BufferedReader(inputStreamReader);
            SAXParserFactory factory = SAXParserFactory.newInstance();
            XMLReader xmlReader = factory.newSAXParser().getXMLReader();
            xmlReader.setContentHandler(handler);
            xmlReader.parse(new InputSource(reader));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里应该没什么问题,调用SAXParserFactory.newInstance()获得SAXParserFactory的实例,再调用newSAXParser().getXMLReader()获得XMLPreader实例,setContentHandler()传入自定义的解析类WordsHandler,最后调用parse()方法,传入inputStream包装成的BufferReader开始解析。

解析后我们可以调用WordsHandler的getWords获得所查单词对应的words对象,接下来可以用SQLite保存在本地。新建一个db包,在db包中新建一个WordsSQLiteOpenHelper类继承自SQLiteOpenHelper,在这个类中新建words库:

public class WordsSQLiteOpenHelper extends SQLiteOpenHelper {
    /**建表语句*/
    private String CREATE_WORDS = "create table Words(id Integer primary key autoincrement," +
            "isChinese text,key text,fy text,psE text,pronE text,psA text,pronA text,posAcceptation text,sent text)";

    public WordsSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_WORDS);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

目前没有升级数据的需要,因此仅写了创建数据库的代码。

接下来是一个大类WordsAction,我把它放在util包下,这个类里包含了大部分查词界面所用到的方法,包括:保存words到数据库、获取address地址、向数据库中查找words、保存发音mp3文件、播放发音MP3。

发音MP3我们后面再看,先来看数据库这块:

public class WordsAction {
    /**
     * 本类的实例
     */
    private static WordsAction wordsAction;
    /**
     * Words的表名
     */
    private final String TABLE_WORDS = "Words";
    /**
     * 数据库工具,用于增、删、该、查
     */
    private SQLiteDatabase db;
    private MediaPlayer player = null;

    /**
     * 私有化的构造器
     */
    private WordsAction(Context context) {
        WordsSQLiteOpenHelper helper = new WordsSQLiteOpenHelper(context, TABLE_WORDS, null, 1);
        db = helper.getWritableDatabase();
    }

    /**
     * 单例类WordsAction获取实例方法
     *
     * @param context 上下文
     */
    public static WordsAction getInstance(Context context) {
        //双重效验锁,提高性能
        if (wordsAction == null) {
            synchronized (WordsAction.class) {
                if (wordsAction == null) {
                    wordsAction = new WordsAction(context);
                }
            }
        }
        return wordsAction;
    }

    /**
     * 向数据库中保存新的Words对象
     * 会先对word进行判断,为有效值时才会保存
     *
     * @param words 单词类的实例
     */
    public boolean saveWords(Words words) {
        //判断是否是有效对象,即有数据
        if (words.getSent().length() > 0) {
            ContentValues values = new ContentValues();
            values.put("isChinese", "" + words.getIsChinese());
            values.put("key", words.getKey());
            values.put("fy", words.getFy());
            values.put("psE", words.getPsE());
            values.put("pronE", words.getPronE());
            values.put("psA", words.getPsA());
            values.put("pronA", words.getPronA());
            values.put("posAcceptation", words.getPosAcceptation());
            values.put("sent", words.getSent());
            db.insert(TABLE_WORDS, null, values);
            values.clear();
            return true;
        }
        return false;
    }

    /**
     * 从数据库中查找查询的words
     *
     * @param key 查找的值
     * @return words 若返回words的key为空,则说明数据库中没有该词
     */
    public Words getWordsFromSQLite(String key) {
        Words words = new Words();
        Cursor cursor = db.query(TABLE_WORDS, null, "key=?", new String[]{key}, null, null, null);
        //数据库中有
        if (cursor.getCount() > 0) {
            Log.d("测试", "数据库中有");
            if (cursor.moveToFirst()) {
                do {
                    String isChinese = cursor.getString(cursor.getColumnIndex("isChinese"));
                    if ("true".equals(isChinese)) {
                        words.setIsChinese(true);
                    } else if ("false".equals(isChinese)) {
                        words.setIsChinese(false);
                    }
                    words.setKey(cursor.getString(cursor.getColumnIndex("key")));
                    words.setFy(cursor.getString(cursor.getColumnIndex("fy")));
                    words.setPsE(cursor.getString(cursor.getColumnIndex("psE")));
                    words.setPronE(cursor.getString(cursor.getColumnIndex("pronE")));
                    words.setPsA(cursor.getString(cursor.getColumnIndex("psA")));
                    words.setPronA(cursor.getString(cursor.getColumnIndex("pronA")));
                    words.setPosAcceptation(cursor.getString(cursor.getColumnIndex("posAcceptation")));
                    words.setSent(cursor.getString(cursor.getColumnIndex("sent")));
                } while (cursor.moveToNext());
            }
            cursor.close();
        } else {
            Log.d("测试", "数据库中没有");
            cursor.close();
        }

        return words;
    }

这是一个单例类,我采用了双重锁的方式,提高性能。

方法说明都在代码中有。

这个类中还有一个方法,用于获取Http访问的地址:

/**
     * 获取网络查找单词的对应地址
     *
     * @param key 要查询的单词
     * @return address 所查单词对应的http地址
     */
    public String getAddressForWords(final String key) {
        String address_p1 = "http://dict-co.iciba.com/api/dictionary.php?w=";
        String address_p2 = "";
        String address_p3 = "&key=E568F04171398072F7EC5D8B4A6CBDB4";
        if (isChinese(key)) {
            try {
                //此处非常重要!对中文的key进行重新编码,生成正确的网址
                address_p2 = "_" + URLEncoder.encode(key, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else {
            address_p2 = key;
        }
        return address_p1 + address_p2 + address_p3;

    }

看到代码中“此处非常重要!”的提示那段没,这也是一个卡了我几个小时的问题。

原本我是这样写的:

address_p2 = "_"+key;

问题是在查询中文的时候得不到任何数据,我还打印了访问的网址,Log出来的地址,我复制到ie浏览器返回有数据的,没有问题,又检查了WordsHandler,也没有问题。想了好久才意识到我在浏览器地址栏输入的中文会自动转码,而用HttpURLConnection访问时却不会自动转码。

所以在这里要手动的对中文进行重新编码。if里的isChinese()方法可以通过Unicode编码完美的判断中文汉字和符号

/**
     * 判断是否是中文
     *
     * @param strName String类型的字符串
     */
    public static boolean isChinese(String strName) {
        char[] ch = strName.toCharArray();
        for (int i = 0; i < ch.length; i++) {
            char c = ch[i];
            if (isChinese(c)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据Unicode编码完美的判断中文汉字和符号
     *
     * @param c char类型的字符串
     */
    private static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
            return true;
        }
        return false;
    }

到这里,基本的查词功能就能实现了!

其实我写这些代码的时候,会简单写一个Activity,里面有TextView,然后调用上述方法,测试我写的代码是否正确。

HttpUtil.sentHttpRequest(address, new HttpCallBackListener() {
                @Override
                public void onFinish(InputStream inputStream) {
                    WordsHandler wordsHandler = new WordsHandler();
                    ParseXML.parse(wordsHandler, inputStream);
                    words = wordsHandler.getWords();
                    wordsAction.saveWords(words);
                    wordsAction.saveWordsMP3(words);
                    }
                @Override
                public void onError() {

                }
            });
 }

这就是测试的时候简单调用方法,看看能不能实现功能。

今天就到这里,明天继续后续内容!

时间: 2024-10-30 09:21:03

android 项目练习:自己的词典app——生词本(一)的相关文章

原生Android App项目调用Untiy导出的Android项目

背景:采用Google VR SDK for Unity 开发3D场景功能,然后导出Android项目,合并到一个Android App里面,供其它Activity调用. 用Google VR for Unity SDK开发的Untiy项目导出来的Android项目,主Activity为com.google.unity.GoogleUnityActivity.如果需要在此基础上扩展一些功能,则需要实现自己的Activity(比如命名为GoogleCardboardActivity),并继承com

Android项目实战(十一):启动另一个APP

目前公司项目需求,一个主APP,需要打开某些小APP,这些小APP是整合了Unity的,但是还是android程序(所有小APP的包名是已知的). 以前没做过,查询了一下实现方法,其实还是挺简单的,测试也可以. 代码比较简单,直接贴上: try { PackageManager packageManager = getActivity().getPackageManager(); Intent intent=new Intent(); intent = packageManager.getLau

Android项目实战(二十三):仿QQ设置App全局字体大小

原文:Android项目实战(二十三):仿QQ设置App全局字体大小 一.项目需求: 因为产品对象用于中老年人,所以产品设计添加了APP全局字体调整大小功能. 这里仿做QQ设置字体大小的功能. QQ实现的效果是,滚动下面的seekbar,当只有seekbar到达某一个刻度的时候,这时候上部分的效果展示部分会改变文字大小, 但是在拖动过程中字体不会改变.关闭此界面,就可以看到改变文字后app整体的实际文字大小效果了. ----------------------------------------

最新app源码下载:200款优秀Android项目源码

200款优秀Android项目源码!菜鸟必备!Android开发又将带来新一轮热潮,很多开发者都投入到这个浪潮中去了,创造了许许多多相当优秀的应用.其中也有许许多多的开发者提供了应用开源项目,贡献出他们的智慧和创造力.学习开源代码是掌握技术的一个最佳方式. 下载地址: http://bbs.aiyingli.com/forum.php?mod=viewthread&tid=13120

&#167;1.2 Android项目结构及“Hello World”应用解析

书接上文,我们创建一个新的Android项目,进入Android Studio开发页面,如下图: 左侧为项目工具窗口,通过项目工具窗口可以管理整个项目的相关文件右侧为代码编辑区,我们可以在这里进行Android程序的开发工作底部为调试区,在调试区可以进行程序的断点调试.日志打印,可以监视整个程序的内存消耗,从而有针对性的进行性能优化. 首先,通过查看左侧的项目工具窗口区,为大家介绍一个Android项目的基本结构: 如上图所示,在这里我们只需要关注图中app目录下的内容: build目录:构建目

gradle构建android项目

工具: Android Studio2.0 gradle-2.10 一.gradle文件 当我们新建一个android项目后,Andriod Studio会自动为我们创建多个gradle文件和目录,具体内容如下: 下面我们分别对这几个目录进行分析: 二.app下的build.gradle apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.3" de

Android项目:proguard混淆之常见开源项目混淆配置

1.Gson混淆 ## ---------------------------------- ##   ########## Gson混淆    ########## ## ---------------------------------- -keepattributes Signature  -keep class sun.misc.Unsafe { *; }  -keep class com.google.gson.examples.android.model.** { *; } 2.gr

java枚举在android项目应用

今天修复一个公司很早以前的android应用功能,里面的代码逻辑已经完全错乱,然后发现返回的数据完全不对了.然后修复了整整两天.然后我重新整理了一遍,重构就算不上了.然后就用上了枚举. 什么是枚举?我以前也不懂,当时我看见公司的项目中使用了枚举当做项目一个控制,比如修改已经写好的app然后为一些手机厂商做定制版.可能要去掉广告,还有跳转到商店url都不同,特别是国内基本都没有google play.我们为了避免以后的修改,就会写个枚举来控制它. public enum Market { Defa

Cocos2d-x创建android项目(cocos2d-x系列三)

不例外.最显眼的就是,在文件的根目录中增加了一个名为setup.py的配置文件.运行它可以配置系统的一些环境变量. 2.创建项目 新的版本我们来动手创建一个新项目吧.找到tools文件夹....额 打包的那个名为create-project.py的文件似乎木有了!原来,在新版本中,创建新项目已经可以通过命令行来创建了,相对于先前版本的图形界面,命令行更简洁.我们要在setup.py中设置参数.以mac平台为例,我们进入到对应目录,运行脚本setup.py: Setting up cocos2d-