码迷,mamicode.com
首页 > 其他好文 > 详细

第二天 每日一句

时间:2015-09-12 20:24:41      阅读:194      评论:0      收藏:0      [点我收藏+]

标签:html解析   jsoup   网络图片   android   

    谢谢大家的厚爱。今天是第二天了,第二个小项目,比第一个要复杂一点点。今天的项目呢是要做一个英语学习小软件,就是英语每日一句。由于是小项目,就不用那么复杂,没有自己去做服务器(自己做也可以,这样最好,可以扩展出很多项目。比如,糗事百科,知乎,或再大点的,美团,淘宝等。可是大项目是要团队做的)。在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文档,进行学习使用;当你这样达到一定水平后,就是总结和看视频了,总结是系统的全面的理解。看视频是为了知道别人怎么做,并查漏补缺。

   好了说了那么多废话,希望对您有所帮助。加油!!!

 

 

 

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

第二天 每日一句

标签:html解析   jsoup   网络图片   android   

原文地址:http://blog.csdn.net/zhaicaixiansheng/article/details/48396203

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!