Android 新闻客户端开发

主界面的最终实现效果如下;

下面是MainActivity.java的代码

[java]
  1. package com.xiaowu.news;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import org.json.JSONArray;
  6. import org.json.JSONObject;
  7. import android.app.Activity;
  8. import android.content.Intent;
  9. import android.graphics.Color;
  10. import android.graphics.drawable.ColorDrawable;
  11. import android.os.AsyncTask;
  12. import android.os.Bundle;
  13. import android.view.Gravity;
  14. import android.view.KeyEvent;
  15. import android.view.LayoutInflater;
  16. import android.view.Menu;
  17. import android.view.MenuItem;
  18. import android.view.View;
  19. import android.view.View.OnClickListener;
  20. import android.widget.AdapterView;
  21. import android.widget.AdapterView.OnItemClickListener;
  22. import android.widget.Button;
  23. import android.widget.GridView;
  24. import android.widget.HorizontalScrollView;
  25. import android.widget.LinearLayout;
  26. import android.widget.LinearLayout.LayoutParams;
  27. import android.widget.ListView;
  28. import android.widget.ProgressBar;
  29. import android.widget.SimpleAdapter;
  30. import android.widget.TextView;
  31. import android.widget.Toast;
  32. import com.xiaowu.news.custom.ConstomSimpleAdapter;
  33. import com.xiaowu.news.model.Category;
  34. import com.xiaowu.news.service.SyncHttp;
  35. import com.xiaowu.news.update.UpdateManager;
  36. import com.xiaowu.news.util.DensityUtil;
  37. import com.xiaowu.news.util.StringUtil;
  38. /**
  39. *
  40. * @author wwj
  41. *
  42. */
  43. public class MainActivity extends Activity {
  44. private final int COLUMNWIDTH_PX = 56; // GridView每个单元格的宽度(像素)
  45. private final int FLINGVELOCITY_PX = 800; // ViewFilper滑动的距离(像素)
  46. private final int NEWSCOUNT = 5; // 显示新闻的条数
  47. private final int SUCCESS = 0; // 加载新闻成功
  48. private final int NONEWS = 1; // 没有新闻
  49. private final int NOMORENEWS = 2; // 没有更多新闻
  50. private final int LOADERROR = 3; // 加载失败
  51. private long exitTime; //按返回键退出的时间
  52. private int mColumnWidth_dip;
  53. private int mFlingVelocity_dip;
  54. private int mCid; // 新闻编号
  55. private String mCategoryTitle; // 新闻分类标题
  56. private ListView mNewslist; // 新闻列表
  57. private SimpleAdapter mNewslistAdapter; // 为新闻内容提供需要显示的列表
  58. private ArrayList<HashMap<String, Object>> mNewsData; // 存储新闻信息的数据集合
  59. private LayoutInflater mInflater; // 用来动态载入没有loadmore_layout界面
  60. private Button category_Button = null; // 新闻分类标题栏的向右查看的按钮
  61. private HorizontalScrollView categoryScrollView = null;// 水平滚动图
  62. private Button mTitleBarRefresh; // 标题栏的刷新按钮
  63. private ProgressBar mTitleBarProgress; // 进度条
  64. private Button mLoadmoreButton; // 加载更多按钮
  65. private LoadNewsAsyncTack mLoadNewsAsyncTack; // 声明LoadNewsAsyncTack引用
  66. @Override
  67. public void onCreate(Bundle savedInstanceState) {
  68. super.onCreate(savedInstanceState);
  69. setContentView(R.layout.news_home_layout);
  70. //通过id来获取按钮的引用
  71. mTitleBarRefresh = (Button) findViewById(R.id.titlebar_refresh);
  72. mTitleBarProgress = (ProgressBar) findViewById(R.id.titlebar_progress);
  73. mTitleBarRefresh.setOnClickListener(loadmoreListener);
  74. // 将px转换为dip
  75. mColumnWidth_dip = DensityUtil.px2dip(this, COLUMNWIDTH_PX);
  76. mFlingVelocity_dip = DensityUtil.px2dip(this, FLINGVELOCITY_PX);
  77. //初始化新闻分类的编号
  78. mCid = 1;
  79. mCategoryTitle = "焦点";
  80. mInflater = getLayoutInflater();
  81. //存储新闻信息的数据集合
  82. mNewsData = new ArrayList<HashMap<String, Object>>();
  83. // 获取数组资源
  84. String[] categoryArray = getResources().getStringArray(
  85. R.array.categories);
  86. // 定义一个List数组,用来存放HashMap对象
  87. final List<HashMap<String, Category>> categories = new ArrayList<HashMap<String, Category>>();
  88. // 分割新闻字符串
  89. for (int i = 0; i < categoryArray.length; i++) {
  90. String temp[] = categoryArray[i].split("[|]");
  91. if (temp.length == 2) {
  92. int cid = StringUtil.string2Int(temp[0]);
  93. String title = temp[1];
  94. Category type = new Category(cid, title);
  95. // 定义一个HashMap对象,用来存放键值对
  96. HashMap<String, Category> hashMap = new HashMap<String, Category>();
  97. hashMap.put("category_title", type);
  98. categories.add(hashMap);
  99. }
  100. }
  101. ConstomSimpleAdapter categoryAdapter = new ConstomSimpleAdapter(this,
  102. categories, R.layout.category_item_layout,
  103. new String[] { "category_title" },
  104. new int[] { R.id.category_title });
  105. // 创建一个网格视图, 用于实现新闻标题的布局
  106. GridView category = new GridView(this);
  107. // 设置单元格的背景色为透明,这样选择分类时就不会显示黄色背景了
  108. category.setSelector(new ColorDrawable(Color.TRANSPARENT));
  109. // 设置每一个新闻标题的宽度
  110. category.setColumnWidth(mColumnWidth_dip);
  111. // 设置网格视图的列数
  112. category.setNumColumns(GridView.AUTO_FIT);
  113. // 设置对齐方式
  114. category.setGravity(Gravity.CENTER);
  115. // 根据单元格的宽度和数目计算网格视图的宽度
  116. int width = mColumnWidth_dip * categories.size();
  117. // 获取布局参数
  118. LayoutParams params = new LayoutParams(width, LayoutParams.MATCH_PARENT);
  119. // 设置参数
  120. category.setLayoutParams(params);
  121. // 设置Adapter
  122. category.setAdapter(categoryAdapter);
  123. // 通过ID获取LinearLayout布局对象
  124. LinearLayout categoryLayout = (LinearLayout) findViewById(R.id.category_layout);
  125. // 将网格视图组件添加到LinearLayout布局当中
  126. categoryLayout.addView(category);
  127. // 添加单元格点击事件
  128. category.setOnItemClickListener(new OnItemClickListener() {
  129. TextView categoryTitle;
  130. @Override
  131. public void onItemClick(AdapterView<?> parent, View view,
  132. int position, long id) {
  133. // TODO Auto-generated method stub
  134. for (int i = 0; i < parent.getCount(); i++) {
  135. categoryTitle = (TextView) parent.getChildAt(i);
  136. categoryTitle.setTextColor(0XFFADB2AD);
  137. categoryTitle.setBackgroundDrawable(null);
  138. }
  139. categoryTitle = (TextView) view;
  140. categoryTitle.setTextColor(0xFFFFFFFF);
  141. categoryTitle
  142. .setBackgroundResource(R.drawable.image_categorybar_item_selected_background);
  143. Toast.makeText(MainActivity.this, categoryTitle.getText(),
  144. Toast.LENGTH_SHORT).show();
  145. //获取新闻分类编号
  146. mCid = categories.get(position).get("category_title").getCid();
  147. mCategoryTitle = categories.get(position).get("category_title").getTitle();
  148. mLoadNewsAsyncTack = new LoadNewsAsyncTack();
  149. mLoadNewsAsyncTack.execute(0, true);
  150. }
  151. });
  152. //第一次获取新闻列表
  153. getSpecCatNews(mCid, mNewsData, 0, true);
  154. // 箭头
  155. categoryScrollView = (HorizontalScrollView) findViewById(R.id.categorybar_scrollView);
  156. category_Button = (Button) findViewById(R.id.category_arrow_right);
  157. category_Button.setOnClickListener(new OnClickListener() {
  158. @Override
  159. public void onClick(View v) {
  160. // TODO Auto-generated method stub
  161. categoryScrollView.fling(mFlingVelocity_dip);
  162. }
  163. });
  164. mNewslistAdapter = new SimpleAdapter(this, mNewsData,
  165. R.layout.newslist_item_layout, new String[] {
  166. "newslist_item_title", "newslist_item_digest",
  167. "newslist_item_source", "newslist_item_ptime" },
  168. new int[] { R.id.newslist_item_title,
  169. R.id.newslist_item_digest, R.id.newslist_item_source,
  170. R.id.newslist_item_ptime });
  171. mNewslist = (ListView) findViewById(R.id.news_list);
  172. View footerView = mInflater.inflate(R.layout.loadmore_layout, null);
  173. //在LiseView下面添加“加载更多”
  174. mNewslist.addFooterView(footerView);
  175. //显示列表
  176. mNewslist.setAdapter(mNewslistAdapter);
  177. mNewslist.setOnItemClickListener(new OnItemClickListener() {
  178. @Override
  179. public void onItemClick(AdapterView<?> parent, View view,
  180. int position, long id) {
  181. // TODO Auto-generated method stub
  182. Intent intent = new Intent(MainActivity.this,
  183. NewsDetailActivity.class);
  184. intent.putExtra("categoryTitle", mCategoryTitle);
  185. intent.putExtra("newsData", mNewsData);
  186. intent.putExtra("position", position);
  187. startActivity(intent);
  188. }
  189. });
  190. mLoadmoreButton = (Button) findViewById(R.id.loadmore_btn);
  191. mLoadmoreButton.setOnClickListener(loadmoreListener);
  192. }
  193. /**
  194. * 获取指定类型的新闻列表
  195. *
  196. * @param cid
  197. * @return
  198. */
  199. private int getSpecCatNews(int cid, List<HashMap<String, Object>> newsList,
  200. int startnid, boolean firstTime) {
  201. // 如果是第一次加载的话
  202. if (firstTime) {
  203. newsList.clear();
  204. }
  205. //本机:http://10.0.2.2:8080/web/getSpecifyCategoryNews
  206. //wifi局域网:192.168.220.1
  207. String url = "http://10.0.2.2:8080/web/getSpecifyCategoryNews";
  208. String params = "startnid=" + startnid + "&count=" + NEWSCOUNT
  209. + "&cid=" + cid;
  210. SyncHttp syncHttp = new SyncHttp();
  211. try {
  212. // 通过Http协议发送Get请求,返回字符串
  213. String retStr = syncHttp.httpGet(url, params);
  214. JSONObject jsonObject = new JSONObject(retStr);
  215. int retCode = jsonObject.getInt("ret");
  216. if (retCode == 0) {
  217. JSONObject dataObj = jsonObject.getJSONObject("data");
  218. // 获取返回数目
  219. int totalNum = dataObj.getInt("totalnum");
  220. if (totalNum > 0) {
  221. // 获取返回新闻集合
  222. JSONArray newslistArray = dataObj.getJSONArray("newslist");
  223. // 将用JSON格式解析的数据添加到数据集合当中
  224. for (int i = 0; i < newslistArray.length(); i++) {
  225. JSONObject newsObject = (JSONObject) newslistArray
  226. .opt(i);
  227. HashMap<String, Object> hashMap = new HashMap<String, Object>();
  228. hashMap.put("nid", newsObject.getInt("nid"));
  229. hashMap.put("newslist_item_title",
  230. newsObject.getString("title"));
  231. hashMap.put("newslist_item_digest",
  232. newsObject.getString("digest"));
  233. hashMap.put("newslist_item_source",
  234. newsObject.getString("source"));
  235. hashMap.put("newslist_item_ptime",
  236. newsObject.getString("ptime"));
  237. hashMap.put("newslist_item_comments",
  238. newsObject.getInt("commentcount"));
  239. newsList.add(hashMap);
  240. }
  241. return SUCCESS;
  242. } else {
  243. //第一次加载新闻列表
  244. if (firstTime) {
  245. return NONEWS; //没有新闻
  246. } else {
  247. return NOMORENEWS; //没有更多新闻
  248. }
  249. }
  250. } else {
  251. return LOADERROR; //加载新闻失败
  252. }
  253. } catch (Exception e) {
  254. // TODO Auto-generated catch block
  255. e.printStackTrace();
  256. return LOADERROR; //加载新闻失败
  257. }
  258. }
  259. /**
  260. * 为“加载更多”按钮定义匿名内部类
  261. */
  262. private OnClickListener loadmoreListener = new OnClickListener() {
  263. @Override
  264. public void onClick(View v) {
  265. mLoadNewsAsyncTack = new LoadNewsAsyncTack();
  266. switch (v.getId()) {
  267. //点击加载更多
  268. case R.id.loadmore_btn:
  269. mLoadNewsAsyncTack.execute(mNewsData.size(), false); //不是第一次加载新闻里列表
  270. break;
  271. //点击刷新按钮
  272. case R.id.titlebar_refresh:
  273. mLoadNewsAsyncTack.execute(0, true);
  274. break;
  275. }
  276. }
  277. };
  278. /**
  279. * 异步更新UI
  280. * @author wwj
  281. *
  282. */
  283. private class LoadNewsAsyncTack extends AsyncTask<Object, Integer, Integer> {
  284. //准备运行
  285. @Override
  286. protected void onPreExecute() {
  287. mTitleBarRefresh.setVisibility(View.GONE);
  288. mTitleBarProgress.setVisibility(View.VISIBLE);
  289. mLoadmoreButton.setText(R.string.loadmore_text);
  290. }
  291. //在后台运行
  292. @Override
  293. protected Integer doInBackground(Object... params) {
  294. return getSpecCatNews(mCid, mNewsData, (Integer) params[0],
  295. (Boolean) params[1]);
  296. }
  297. //完成后台任务
  298. @Override
  299. protected void onPostExecute(Integer result) {
  300. switch (result) {
  301. //该栏目没有新闻
  302. case NONEWS:
  303. Toast.makeText(MainActivity.this, R.string.nonews, Toast.LENGTH_SHORT)
  304. .show();
  305. break;
  306. //该栏目没有更多新闻
  307. case NOMORENEWS:
  308. Toast.makeText(MainActivity.this, R.string.nomorenews,
  309. Toast.LENGTH_SHORT).show();
  310. break;
  311. //加载失败
  312. case LOADERROR:
  313. Toast.makeText(MainActivity.this, R.string.loadnewserror, Toast.LENGTH_SHORT)
  314. .show();
  315. break;
  316. }
  317. mTitleBarRefresh.setVisibility(View.VISIBLE); //刷新按钮设置为可见
  318. mTitleBarProgress.setVisibility(View.GONE); //进度条设置为不可见
  319. mLoadmoreButton.setText(R.string.loadmore_btn); //按钮信息替换为“加载更多”
  320. mNewslistAdapter.notifyDataSetChanged(); //通知ListView更新数据
  321. }
  322. }
  323. /**
  324. * 添加菜单
  325. */
  326. @Override
  327. public boolean onCreateOptionsMenu(Menu menu) {
  328. // TODO Auto-generated method stub
  329. menu.add(1, 1, 1, "更新");
  330. menu.add(1, 2, 2, "退出");
  331. return true;
  332. }
  333. @Override
  334. public boolean onOptionsItemSelected(MenuItem item) {
  335. switch(item.getItemId()) {
  336. case 1:
  337. UpdateManager updateManager = new UpdateManager(MainActivity.this);
  338. //检测更新
  339. updateManager.checkUpdate();
  340. break;
  341. case 2:
  342. finish();
  343. break;
  344. }
  345. return true;
  346. }
  347. /**
  348. * 按键触发的事件
  349. */
  350. @Override
  351. public boolean onKeyDown(int keyCode, KeyEvent event) {
  352. if(keyCode == KeyEvent.KEYCODE_BACK
  353. && event.getAction() == KeyEvent.ACTION_DOWN){
  354. if((System.currentTimeMillis() - exitTime > 2000)){
  355. Toast.makeText(getApplicationContext(), R.string.backcancel
  356. , Toast.LENGTH_LONG).show();
  357. exitTime = System.currentTimeMillis();
  358. }
  359. else{
  360. finish();
  361. System.exit(0);
  362. }
  363. return true;
  364. }
  365. return super.onKeyDown(keyCode, event);
  366. }
  367. }


主界面的业务逻辑实现,要一步就实现是非常困难的,因为项目总是从简单到复杂,所以小巫只把关键点说一下就行了:

这里主要有三个关键点:

1.分类栏的实现?

首先创建一个GridView视图,通过GridView来填充数据,把每一类新闻分类显示到GridView视图中去,最后通过获取到界面布局中的LinearLayout对象,把GridView添加到LinearLayout布局当中去,最终实现效果。

2.获取新闻分类列表(对JSON格式数据的解析)?

JSON数据的解析并不算太难,主要把JSON数据的数据结构搞清楚,解析起来还是挺方便的。

进行解析虽然方便,但前提是要把数据得到,因为数据是要在服务器端得到,需要利用Android的Http通信来实现。

这里需要利用到httpGet还有httpPost方法,这个代码很需要贴一贴滴。自定义的SyncHttp类

[java]
  1. package com.xiaowu.news.service;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import org.apache.http.HttpResponse;
  5. import org.apache.http.HttpStatus;
  6. import org.apache.http.client.HttpClient;
  7. import org.apache.http.client.entity.UrlEncodedFormEntity;
  8. import org.apache.http.client.methods.HttpGet;
  9. import org.apache.http.client.methods.HttpPost;
  10. import org.apache.http.impl.client.DefaultHttpClient;
  11. import org.apache.http.message.BasicNameValuePair;
  12. import org.apache.http.params.BasicHttpParams;
  13. import org.apache.http.params.HttpConnectionParams;
  14. import org.apache.http.params.HttpParams;
  15. import org.apache.http.protocol.HTTP;
  16. import org.apache.http.util.EntityUtils;
  17. import com.xiaowu.news.model.Parameter;
  18. public class SyncHttp {
  19. /**
  20. * 通过Get方式发送请求
  21. * @param url
  22. * @param params
  23. * @return
  24. * @throws Exception
  25. */
  26. public String httpGet(String url, String params) throws Exception {
  27. String response = null; //返回信息
  28. //拼接请求URl
  29. if(null != params && !params.equals("")) {
  30. url += "?" + params;
  31. }
  32. int timeOutConnection = 3000;
  33. int timeOutSocket = 5000;
  34. HttpParams httpParams = new BasicHttpParams();
  35. HttpConnectionParams.setConnectionTimeout(httpParams, timeOutConnection);
  36. HttpConnectionParams.setSoTimeout(httpParams, timeOutSocket);
  37. //构造HttpClient实例
  38. HttpClient httpClient = new DefaultHttpClient();
  39. //创建GET方法实例
  40. HttpGet httpGet = new HttpGet(url);
  41. try {
  42. HttpResponse httpResponse = httpClient.execute(httpGet);
  43. int statusCode = httpResponse.getStatusLine().getStatusCode();
  44. if(statusCode == HttpStatus.SC_OK) {
  45. //获得返回结果
  46. response = EntityUtils.toString(httpResponse.getEntity());
  47. }
  48. else{
  49. response = "返回码:" + statusCode;
  50. }
  51. } catch (Exception e) {
  52. // TODO: handle exception
  53. throw new Exception(e);
  54. }
  55. return response;
  56. }
  57. /**
  58. * 通过post方式发送请求
  59. * @param url
  60. * @param params
  61. * @return
  62. * @throws Exception
  63. */
  64. public String httpPost(String url, List<Parameter> params) throws Exception {
  65. String response = null;
  66. int timeOutConnection = 3000;
  67. int timeOutSocket = 5000;
  68. HttpParams httpParams = new BasicHttpParams();
  69. HttpConnectionParams.setConnectionTimeout(httpParams, timeOutConnection);
  70. HttpConnectionParams.setSoTimeout(httpParams, timeOutSocket);
  71. //构造HttpClient实例
  72. HttpClient httpClient = new DefaultHttpClient();
  73. HttpPost httpPost = new HttpPost(url);
  74. if(params.size() > 0) {
  75. //设置post请求参数
  76. httpPost.setEntity(new UrlEncodedFormEntity(buildNameValuePair(params), HTTP.UTF_8));
  77. }
  78. //使用execute方法发送Http Post 请求,并返回HttpResponse对象
  79. HttpResponse httpResponse = httpClient.execute(httpPost);
  80. int statusCode = httpResponse.getStatusLine().getStatusCode();
  81. if(statusCode == HttpStatus.SC_OK) {
  82. //获得返回结果
  83. response = EntityUtils.toString(httpResponse.getEntity());
  84. }
  85. else {
  86. response = "返回码:" + statusCode;
  87. }
  88. return response;
  89. }
  90. /**
  91. * 把Paramster类型集合转换为NameValuePair类型集合
  92. * @param params
  93. * @return
  94. */
  95. private List<BasicNameValuePair> buildNameValuePair (List<Parameter> params) {
  96. List<BasicNameValuePair> result = new ArrayList<BasicNameValuePair>();
  97. for(Parameter param : params) {
  98. BasicNameValuePair pair = new BasicNameValuePair(param.getName(), param.getValue());
  99. result.add(pair);
  100. }
  101. return result;
  102. }
  103. }

定义好了SyncHttp类之后,就可以通过调用httpGet方法来获取数据,在Activity的getSpecCatNews方法有详细实现,看一下就可以知道了。

3.异步更新UI的实现?

关于异步更新UI也算是一个比较难理解的东西,在Activity里定义了一个继承AsyncTask类的内部类,并实现三个方法,比较灵活。
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值