基於Android的小巫新聞客戶端開發--主介面業務邏輯實現
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
基於Android的小巫新聞客戶端開發--主介面業務邏輯實現
上一篇介紹了主介面的UI設計,現在直接進入主題,業務邏輯的實現,由於專案的開發總是在不斷的完善的,最初實現的效果,總會隨專案的進度而做出相應的改變,小巫也不可能從新開發整個客戶端,然後再一步一步記錄,那沒有必要,學習東西,只需要知道關鍵點在哪裡就行了,關於細節方面,遇到再去解決。就是這麼簡單。
主介面的最終實現效果如下;
下面是MainActivity.java的程式碼
package com.xiaowu.news;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import org.json.JSONArray;import org.json.JSONObject;import android.app.Activity;import android.content.Intent;import android.graphics.Color;import android.graphics.drawable.ColorDrawable;import android.os.AsyncTask;import android.os.Bundle;import android.view.Gravity;import android.view.KeyEvent;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.Button;import android.widget.GridView;import android.widget.HorizontalScrollView;import android.widget.LinearLayout;import android.widget.LinearLayout.LayoutParams;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.SimpleAdapter;import android.widget.TextView;import android.widget.Toast;import com.xiaowu.news.custom.ConstomSimpleAdapter;import com.xiaowu.news.model.Category;import com.xiaowu.news.service.SyncHttp;import com.xiaowu.news.update.UpdateManager;import com.xiaowu.news.util.DensityUtil;import com.xiaowu.news.util.StringUtil;/** * * @author wwj * */public class MainActivity extends Activity { private final int COLUMNWIDTH_PX = 56; // GridView每個單元格的寬度(畫素) private final int FLINGVELOCITY_PX = 800; // ViewFilper滑動的距離(畫素) private final int NEWSCOUNT = 5; // 顯示新聞的條數 private final int SUCCESS = 0; // 載入新聞成功 private final int NONEWS = 1; // 沒有新聞 private final int NOMORENEWS = 2; // 沒有更多新聞 private final int LOADERROR = 3; // 載入失敗 private long exitTime; //按返回鍵退出的時間 private int mColumnWidth_dip; private int mFlingVelocity_dip; private int mCid; // 新聞編號 private String mCategoryTitle; // 新聞分類標題 private ListView mNewslist; // 新聞列表 private SimpleAdapter mNewslistAdapter; // 為新聞內容提供需要顯示的列表 private ArrayList<HashMap<String, Object>> mNewsData; // 儲存新聞資訊的資料集合 private LayoutInflater mInflater; // 用來動態載入沒有loadmore_layout介面 private Button category_Button = null; // 新聞分類標題欄的向右檢視的按鈕 private HorizontalScrollView categoryScrollView = null;// 水平滾動圖 private Button mTitleBarRefresh; // 標題欄的重新整理按鈕 private ProgressBar mTitleBarProgress; // 進度條 private Button mLoadmoreButton; // 載入更多按鈕 private LoadNewsAsyncTack mLoadNewsAsyncTack; // 宣告LoadNewsAsyncTack引用 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.news_home_layout); //通過id來獲取按鈕的引用 mTitleBarRefresh = (Button) findViewById(R.id.titlebar_refresh); mTitleBarProgress = (ProgressBar) findViewById(R.id.titlebar_progress); mTitleBarRefresh.setOnClickListener(loadmoreListener); // 將px轉換為dip mColumnWidth_dip = DensityUtil.px2dip(this, COLUMNWIDTH_PX); mFlingVelocity_dip = DensityUtil.px2dip(this, FLINGVELOCITY_PX); //初始化新聞分類的編號 mCid = 1; mCategoryTitle = "焦點"; mInflater = getLayoutInflater(); //儲存新聞資訊的資料集合 mNewsData = new ArrayList<HashMap<String, Object>>(); // 獲取陣列資源 String[] categoryArray = getResources().getStringArray( R.array.categories); // 定義一個List陣列,用來存放HashMap物件 final List<HashMap<String, Category>> categories = new ArrayList<HashMap<String, Category>>(); // 分割新聞字串 for (int i = 0; i < categoryArray.length; i++) { String temp[] = categoryArray[i].split("[|]"); if (temp.length == 2) { int cid = StringUtil.string2Int(temp[0]); String title = temp[1]; Category type = new Category(cid, title); // 定義一個HashMap物件,用來存放鍵值對 HashMap<String, Category> hashMap = new HashMap<String, Category>(); hashMap.put("category_title", type); categories.add(hashMap); } } ConstomSimpleAdapter categoryAdapter = new ConstomSimpleAdapter(this, categories, R.layout.category_item_layout, new String[] { "category_title" }, new int[] { R.id.category_title }); // 建立一個網格檢視, 用於實現新聞標題的佈局 GridView category = new GridView(this); // 設定單元格的背景色為透明,這樣選擇分類時就不會顯示黃色背景了 category.setSelector(new ColorDrawable(Color.TRANSPARENT)); // 設定每一個新聞標題的寬度 category.setColumnWidth(mColumnWidth_dip); // 設定網格檢視的列數 category.setNumColumns(GridView.AUTO_FIT); // 設定對齊方式 category.setGravity(Gravity.CENTER); // 根據單元格的寬度和數目計算網格檢視的寬度 int width = mColumnWidth_dip * categories.size(); // 獲取佈局引數 LayoutParams params = new LayoutParams(width, LayoutParams.MATCH_PARENT); // 設定引數 category.setLayoutParams(params); // 設定Adapter category.setAdapter(categoryAdapter); // 通過ID獲取LinearLayout佈局物件 LinearLayout categoryLayout = (LinearLayout) findViewById(R.id.category_layout); // 將網格檢視元件新增到LinearLayout佈局當中 categoryLayout.addView(category); // 新增單元格點選事件 category.setOnItemClickListener(new OnItemClickListener() { TextView categoryTitle; @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub for (int i = 0; i < parent.getCount(); i++) { categoryTitle = (TextView) parent.getChildAt(i); categoryTitle.setTextColor(0XFFADB2AD); categoryTitle.setBackgroundDrawable(null); } categoryTitle = (TextView) view; categoryTitle.setTextColor(0xFFFFFFFF); categoryTitle .setBackgroundResource(R.drawable.image_categorybar_item_selected_background); Toast.makeText(MainActivity.this, categoryTitle.getText(), Toast.LENGTH_SHORT).show(); //獲取新聞分類編號 mCid = categories.get(position).get("category_title").getCid(); mCategoryTitle = categories.get(position).get("category_title").getTitle(); mLoadNewsAsyncTack = new LoadNewsAsyncTack(); mLoadNewsAsyncTack.execute(0, true); } }); //第一次獲取新聞列表 getSpecCatNews(mCid, mNewsData, 0, true); // 箭頭 categoryScrollView = (HorizontalScrollView) findViewById(R.id.categorybar_scrollView); category_Button = (Button) findViewById(R.id.category_arrow_right); category_Button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub categoryScrollView.fling(mFlingVelocity_dip); } }); mNewslistAdapter = new SimpleAdapter(this, mNewsData, R.layout.newslist_item_layout, new String[] { "newslist_item_title", "newslist_item_digest", "newslist_item_source", "newslist_item_ptime" }, new int[] { R.id.newslist_item_title, R.id.newslist_item_digest, R.id.newslist_item_source, R.id.newslist_item_ptime }); mNewslist = (ListView) findViewById(R.id.news_list); View footerView = mInflater.inflate(R.layout.loadmore_layout, null); //在LiseView下面新增“載入更多” mNewslist.addFooterView(footerView); //顯示列表 mNewslist.setAdapter(mNewslistAdapter); mNewslist.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub Intent intent = new Intent(MainActivity.this, NewsDetailActivity.class); intent.putExtra("categoryTitle", mCategoryTitle); intent.putExtra("newsData", mNewsData); intent.putExtra("position", position); startActivity(intent); } }); mLoadmoreButton = (Button) findViewById(R.id.loadmore_btn); mLoadmoreButton.setOnClickListener(loadmoreListener); } /** * 獲取指定型別的新聞列表 * * @param cid * @return */ private int getSpecCatNews(int cid, List<HashMap<String, Object>> newsList, int startnid, boolean firstTime) { // 如果是第一次載入的話 if (firstTime) { newsList.clear(); } //本機:http://10.0.2.2:8080/web/getSpecifyCategoryNews //wifi區域網:192.168.220.1 String url = "http://10.0.2.2:8080/web/getSpecifyCategoryNews"; String params = "startnid=" + startnid + "&count=" + NEWSCOUNT + "&cid=" + cid; SyncHttp syncHttp = new SyncHttp(); try { // 通過Http協議傳送Get請求,返回字串 String retStr = syncHttp.httpGet(url, params); JSONObject jsonObject = new JSONObject(retStr); int retCode = jsonObject.getInt("ret"); if (retCode == 0) { JSONObject dataObj = jsonObject.getJSONObject("data"); // 獲取返回數目 int totalNum = dataObj.getInt("totalnum"); if (totalNum > 0) { // 獲取返回新聞集合 JSONArray newslistArray = dataObj.getJSONArray("newslist"); // 將用JSON格式解析的資料新增到資料集合當中 for (int i = 0; i < newslistArray.length(); i++) { JSONObject newsObject = (JSONObject) newslistArray .opt(i); HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("nid", newsObject.getInt("nid")); hashMap.put("newslist_item_title", newsObject.getString("title")); hashMap.put("newslist_item_digest", newsObject.getString("digest")); hashMap.put("newslist_item_source", newsObject.getString("source")); hashMap.put("newslist_item_ptime", newsObject.getString("ptime")); hashMap.put("newslist_item_comments", newsObject.getInt("commentcount")); newsList.add(hashMap); } return SUCCESS; } else { //第一次載入新聞列表 if (firstTime) { return NONEWS; //沒有新聞 } else { return NOMORENEWS; //沒有更多新聞 } } } else { return LOADERROR; //載入新聞失敗 } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return LOADERROR; //載入新聞失敗 } } /** * 為“載入更多”按鈕定義匿名內部類 */ private OnClickListener loadmoreListener = new OnClickListener() { @Override public void onClick(View v) { mLoadNewsAsyncTack = new LoadNewsAsyncTack(); switch (v.getId()) { //點選載入更多 case R.id.loadmore_btn: mLoadNewsAsyncTack.execute(mNewsData.size(), false); //不是第一次載入新聞裡列表 break; //點選重新整理按鈕 case R.id.titlebar_refresh: mLoadNewsAsyncTack.execute(0, true); break; } } }; /** * 非同步更新UI * @author wwj * */ private class LoadNewsAsyncTack extends AsyncTask<Object, Integer, Integer> { //準備執行 @Override protected void onPreExecute() { mTitleBarRefresh.setVisibility(View.GONE); mTitleBarProgress.setVisibility(View.VISIBLE); mLoadmoreButton.setText(R.string.loadmore_text); } //在後臺執行 @Override protected Integer doInBackground(Object... params) { return getSpecCatNews(mCid, mNewsData, (Integer) params[0], (Boolean) params[1]); } //完成後臺任務 @Override protected void onPostExecute(Integer result) { switch (result) { //該欄目沒有新聞 case NONEWS: Toast.makeText(MainActivity.this, R.string.nonews, Toast.LENGTH_SHORT) .show(); break; //該欄目沒有更多新聞 case NOMORENEWS: Toast.makeText(MainActivity.this, R.string.nomorenews, Toast.LENGTH_SHORT).show(); break; //載入失敗 case LOADERROR: Toast.makeText(MainActivity.this, R.string.loadnewserror, Toast.LENGTH_SHORT) .show(); break; } mTitleBarRefresh.setVisibility(View.VISIBLE); //重新整理按鈕設定為可見 mTitleBarProgress.setVisibility(View.GONE); //進度條設定為不可見 mLoadmoreButton.setText(R.string.loadmore_btn); //按鈕資訊替換為“載入更多” mNewslistAdapter.notifyDataSetChanged(); //通知ListView更新資料 } } /** * 新增選單 */ @Override public boolean onCreateOptionsMenu(Menu menu) { // TODO Auto-generated method stub menu.add(1, 1, 1, "更新"); menu.add(1, 2, 2, "退出"); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()) { case 1: UpdateManager updateManager = new UpdateManager(MainActivity.this); //檢測更新 updateManager.checkUpdate(); break; case 2: finish(); break; } return true; } /** * 按鍵觸發的事件 */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){ if((System.currentTimeMillis() - exitTime > 2000)){ Toast.makeText(getApplicationContext(), R.string.backcancel , Toast.LENGTH_LONG).show(); exitTime = System.currentTimeMillis(); } else{ finish(); System.exit(0); } return true; } return super.onKeyDown(keyCode, event); }}
主介面的業務邏輯實現,要一步就實現是非常困難的,因為專案總是從簡單到複雜,所以小巫只把關鍵點說一下就行了:
這裡主要有三個關鍵點:
1.分類欄的實現?
首先建立一個GridView檢視,通過GridView來填充資料,把每一類新聞分類顯示到GridView檢視中去,最後通過獲取到介面佈局中的LinearLayout物件,把GridView新增到LinearLayout佈局當中去,最終實現效果。
2.獲取新聞分類列表(對JSON格式資料的解析)?
JSON資料的解析並不算太難,主要把JSON資料的資料結構搞清楚,解析起來還是挺方便的。
進行解析雖然方便,但前提是要把資料得到,因為資料是要在伺服器端得到,需要利用Android的Http通訊來實現。
這裡需要利用到httpGet還有httpPost方法,這個程式碼很需要貼一貼滴。自定義的SyncHttp類
package com.xiaowu.news.service;import java.util.ArrayList;import java.util.List;import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.HttpClient;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.message.BasicNameValuePair;import org.apache.http.params.BasicHttpParams;import org.apache.http.params.HttpConnectionParams;import org.apache.http.params.HttpParams;import org.apache.http.protocol.HTTP;import org.apache.http.util.EntityUtils;import com.xiaowu.news.model.Parameter;public class SyncHttp { /** * 通過Get方式傳送請求 * @param url * @param params * @return * @throws Exception */ public String httpGet(String url, String params) throws Exception { String response = null; //返回資訊 //拼接請求URl if(null != params && !params.equals("")) { url += "?" + params; } int timeOutConnection = 3000; int timeOutSocket = 5000; HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, timeOutConnection); HttpConnectionParams.setSoTimeout(httpParams, timeOutSocket); //構造HttpClient例項 HttpClient httpClient = new DefaultHttpClient(); //建立GET方法例項 HttpGet httpGet = new HttpGet(url); try { HttpResponse httpResponse = httpClient.execute(httpGet); int statusCode = httpResponse.getStatusLine().getStatusCode(); if(statusCode == HttpStatus.SC_OK) { //獲得返回結果 response = EntityUtils.toString(httpResponse.getEntity()); } else{ response = "返回碼:" + statusCode; } } catch (Exception e) { // TODO: handle exception throw new Exception(e); } return response; } /** * 通過post方式傳送請求 * @param url * @param params * @return * @throws Exception */ public String httpPost(String url, List<Parameter> params) throws Exception { String response = null; int timeOutConnection = 3000; int timeOutSocket = 5000; HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, timeOutConnection); HttpConnectionParams.setSoTimeout(httpParams, timeOutSocket); //構造HttpClient例項 HttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(url); if(params.size() > 0) { //設定post請求引數 httpPost.setEntity(new UrlEncodedFormEntity(buildNameValuePair(params), HTTP.UTF_8)); } //使用execute方法傳送Http Post 請求,並返回HttpResponse物件 HttpResponse httpResponse = httpClient.execute(httpPost); int statusCode = httpResponse.getStatusLine().getStatusCode(); if(statusCode == HttpStatus.SC_OK) { //獲得返回結果 response = EntityUtils.toString(httpResponse.getEntity()); } else { response = "返回碼:" + statusCode; } return response; } /** * 把Paramster型別集合轉換為NameValuePair型別集合 * @param params * @return */ private List<BasicNameValuePair> buildNameValuePair (List<Parameter> params) { List<BasicNameValuePair> result = new ArrayList<BasicNameValuePair>(); for(Parameter param : params) { BasicNameValuePair pair = new BasicNameValuePair(param.getName(), param.getValue()); result.add(pair); } return result; }}
定義好了SyncHttp類之後,就可以通過呼叫httpGet方法來獲取資料,在Activity的getSpecCatNews方法有詳細實現,看一下就可以知道了。
3.非同步更新UI的實現?
關於非同步更新UI也算是一個比較難理解的東西,在Activity裡定義了一個繼承AsyncTask類的內部類,並實現三個方法,比較靈活。具體實現看程式碼。
以上三點是小巫認為比較核心的地方,具體的需要動手之後才知道。