谢谢大家的厚爱。今天是第二天了,第二个小项目,比第一个要复杂一点点。今天的项目呢是要做一个英语学习小软件,就是英语每日一句。由于是小项目,就不用那么复杂,没有自己去做服务器(自己做也可以,这样最好,可以扩展出很多项目。比如,糗事百科,知乎,或再大点的,美团,淘宝等。可是大项目是要团队做的)。在21世纪,作为一名程序员,不应该推崇单兵做战,之前倒是有很多大神,比如求伯君和WPS,但现在的系统都比较庞大,并且用户需求多元化,并且要求开发周期足够短,所以必须要团队合作。闲话少说,既然没有服务器,那资源从哪里来呢,本文选择金山词霸的每日一句。Let’s go.
一、需求分析
功能性需求:每日显示,一句英语,一张图片,还有翻译,和小编的废话
非功能性需求:要求无网络时程序不会死,网络请求时间要10s之内完成
废话:要想做好需求分析要作为用户去和用户沟通。此,仁者见仁,智者见智。
二、概要设计
内容:金山词霸每日一句http://news.iciba.com/dailysentence/detail-1504.html
数据接口:html
显示内容:图片,英语,汉语,小编
三、详细设计
1、html解析
本文采用JSoup(http://jsoup.org/)进行解析
使用JSoup很简单,首先是导入lib然后build path最后使用如下代码(或参考官方api)进行解析
<span style="font-family:SimSun;">Document doc = Jsoup.connect("http://en.wikipedia.org/").get();
Elements newsHeadlines = doc.select("#mp-itn b a");</span>2、异步任务
对于异步任务就是实现一个类继承AsyncTask类。
AsyncTask定义了三个泛型类型:Params Progress Result。
· Params 启动任务执行的输入参数
· Progress 后台任务执行的百分比
· Result 后台执行任务最终返回的结果
AsyncTask 一个异步加载数据最少要重写以下这两个方法:
· doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
· onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回
有必要的话还得重写以下这三个方法,但不是必须的:
· onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
· onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
· onCancelled() 用户调用取消时,要做的操作
使用AsyncTask类,以下是几条必须遵守的准则:
· Task的实例必须在UI thread中创建;
· execute方法必须在UI thread中调用;
· 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
· 该task只能被执行一次,否则多次调用时将会出现异常;
具体实现请看四中的代码
3、网络图片下载
这个比较固定,基本如下函数,先建立http网络连接,获取到InputStream,然后通过Bitmap生成Bitmap,此时可以显示到ImageView上或者保存,这取决于你。
/**
* 网络请求图片生成Bitmap
*
* @param path
* @return
*/
private Bitmap getBitmapByUrl(String path) {
Bitmap bm = null;
URL url;
try {
url = new URL(path);
HttpURLConnection con;
con = (HttpURLConnection) url.openConnection();
con.setDoInput(true);
con.connect();
InputStream is = con.getInputStream();
bm = BitmapFactory.decodeStream(is);
is.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bm;
}4、时间间隔计算
所有的时间都是从1979年开始的。Java提供了方法,可以把日期转换成秒数(相对于1979),然后我们可以把两个日期都转换成秒,然后相减,再然后就是把秒转换成天数,一天24个小时,这不用我说了吧。就知道两个日期相差几天了。
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = df.parse("2015-08-19");
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long old = date.getTime();
long now = System.currentTimeMillis();
long day = (now - old) / 1000 / 60 / 60 / 12;四、编码测试
AndroidMenifest.xml 对程序进行配置
<?xml version="1.0" encoding="utf-8"?>
<!-- package的包名是App运行的凭证 -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.leo.everyday"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="13"
android:targetSdkVersion="21" />
<!-- 同样的网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- theme设置程序主题风格 holo风格 title栏将随着activity最上面颜色的改变而改变 -->
<application
android:allowBackup="true"
android:icon="@drawable/every_day"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.NoActionBar.TranslucentDecor" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>activity_main.xml 布局文件,内容的显示界面
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/main_bg"
android:orientation="vertical"
android:paddingBottom="20dip"
android:paddingLeft="20dip"
android:paddingRight="20dip"
android:paddingTop="55dip" >
<ImageView
android:id="@+id/iv_image_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@drawable/test" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_text_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="EN"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@android:color/black"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_translate_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CN"
android:textColor="@android:color/black"
android:layout_margin="6dip" />
<TextView
android:id="@+id/tv_editor_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="EDITOR"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@android:color/black"
android:textStyle="italic" />
</LinearLayout>
</ScrollView>
</LinearLayout>MainActivity.java
package com.leo.everyday;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
/**
* @time 2015-09-12
* @author Leo
* @describe 程序主Activity 负责网络请求数据得到html然后进行解析 最过显示到相应的控件上
*
*/
public class MainActivity extends Activity {
private Context mContext = null;// 自己
private static final String TAG = MainActivity.class.getSimpleName();// Log调试时作为标签
private static final String MAIN_URL = "http://news.iciba.com/dailysentence/detail-";// 网络URL
private static final String IMAGE_CLASS = "r_pic";// html中css定义的属性名 此为图片
private static final String TEXT_CLASS = "en";// 英文文本
private static final String TRANSLATE_CLASS = "cn";// 翻译文本
private static final String EDITOR_CLASS = "editor";// 小编
private ImageView imageShow = null;// 显示图片
private TextView textShow = null, translateShow = null, editorShow = null;// 显示文本内容
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);// 设置布局文件
// 游戏中会通过继承SurfaceView来自定义游戏布局
// 会经常使用此方法
findViewsByIds();// 根据ids初始化控件
task.execute(calculateUrlByTime());// 执行异步任务 从4.0开始google限制不能在主线程中执行网络任务
}
/**
* 取得控件
*/
private void findViewsByIds() {
imageShow = (ImageView) findViewById(R.id.iv_image_main);
textShow = (TextView) findViewById(R.id.tv_text_main);
translateShow = (TextView) findViewById(R.id.tv_translate_main);
editorShow = (TextView) findViewById(R.id.tv_editor_main);
}
/**
* html解析的异步任务
*/
private AsyncTask<String, Integer, Sentence> task = new AsyncTask<String, Integer, Sentence>() {
@Override
protected Sentence doInBackground(String... arg0) {
// TODO Auto-generated method stub
Sentence mSentence = null;// bean接收数据
try {
Document dom = Jsoup.connect(arg0[0]).get();// Jsoup通过网络连接取得html文档
Element imageElement = dom.getElementsByClass(IMAGE_CLASS)
.first();
Element textElement = dom.getElementsByClass(TEXT_CLASS)
.first();
Element translateElement = dom.getElementsByClass(
TRANSLATE_CLASS).first();
Element editorElement = dom.getElementsByClass(EDITOR_CLASS)
.first();
String image = imageElement.getElementsByTag("img").first()
.attr("src");
String text = textElement.getElementsByTag("a").first().text();
String translate = translateElement.getElementsByTag("a")
.first().text();
String editor = editorElement.text();
mSentence = new Sentence(image, text, translate, editor);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
System.out.println("网络异常");
}
return mSentence;
}
@Override
protected void onPostExecute(Sentence result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if (result != null) {
showInformation(result);
} else {
toast("内容空,请重试");
}
}
};
/**
* 弹出提示
*
* @param text
*/
private void toast(String text) {
Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
}
/**
* 把内容显示到控件上去
*
* @param result
*/
private void showInformation(final Sentence result) {
textShow.setText(result.getText());
translateShow.setText(result.getTranslate());
editorShow.setText(result.getEditor());
/**
* 偷一回懒 同样用简单异步任务加载图片
*/
new AsyncTask<String, Integer, Bitmap>() {
@Override
protected Bitmap doInBackground(String... arg0) {
// TODO Auto-generated method stub
Bitmap bmp = getBitmapByUrl(arg0[0]);
return bmp;
}
@Override
protected void onPostExecute(Bitmap result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
imageShow.setImageBitmap(result);
}
}.execute(result.getImage());
}
/**
* 网络请求图片生成Bitmap
*
* @param path
* @return
*/
private Bitmap getBitmapByUrl(String path) {
Bitmap bm = null;
URL url;
try {
url = new URL(path);
HttpURLConnection con;
con = (HttpURLConnection) url.openConnection();
con.setDoInput(true);
con.connect();
InputStream is = con.getInputStream();
bm = BitmapFactory.decodeStream(is);
is.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bm;
}
/**
* 标准阶段1455对应时期为2015-08-19方便计算出以后日期对应的网页连接 比如:2015-08-20对应为1456和1457
*/
private static final long OLD_NUMBER = 1455;
/**
* 计算开始
*
* @return
*/
private static String calculateUrlByTime() {
String url = MAIN_URL + 1 + ".html";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = df.parse("2015-08-19");
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long old = date.getTime();
long now = System.currentTimeMillis();
long day = (now - old) / 1000 / 60 / 60 / 12;
long number = OLD_NUMBER + day;
url = MAIN_URL + number + ".html";
return url;
}
}
Sentence.java
package com.leo.everyday;
/**
* @time 2015-09-12
* @author Leo
* @describe 内容实体内 java bean
*
*/
public class Sentence {
private String image;
private String text;
private String translate;
private String editor;
public Sentence() {
}
public Sentence(String image, String text, String translate, String editor) {
super();
this.image = image;
this.text = text;
this.translate = translate;
this.editor = editor;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getTranslate() {
return translate;
}
public void setTranslate(String translate) {
this.translate = translate;
}
public String getEditor() {
return editor;
}
public void setEditor(String editor) {
this.editor = editor;
}
}
项目源码:http://download.csdn.net/detail/zhaicaixiansheng/9101147
五、项目总结
做一个项目最重要的不是编码,而是需求分析和测试。需求做不好,做了也白做;测试没做好,做了还是demo。一个项目要发布交互,首先就是稳定性。再一个就是在实际项目中,就算是大神也会遇到自己不会和地方,这就是知识盲点。或者说,等到了大神的阶段,都不屑于写代码记代码了。所以说,对于一个程序员,重要的是编程的思想和学习的能力。编程思想决定你写出的代码的质量,学习能力决定你开发的速度和发展的前景。对于编程思想,没什么好说的,什么《算法大全》《面向对象分析与设计》《数据结构》《设计模式》《软件工程》等等,这些一定要饿补。学到学习能力,每个人有自己的方法。对于IT技术,我的学习方法是,以IOS为例:首先选择一本书,重点看第一章和下面的目录,然后随便翻翻,这是为了对这个新东西有个全面的了解,知道他是什么,要做什么,能做什么,怎么做;然后就是看每张的最开始的容易的地方,这样是为了掌握基础的东西,可以写出helloworld类的程序。然后就是做项目,遇到什么不会的就百度谷歌api文档,进行学习使用;当你这样达到一定水平后,就是总结和看视频了,总结是系统的全面的理解。看视频是为了知道别人怎么做,并查漏补缺。
好了说了那么多废话,希望对您有所帮助。加油!!!
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/zhaicaixiansheng/article/details/48396203