基于Android的小巫新闻客户端开发--主界面业务逻辑实现

来源:互联网 发布:linux运行程序命令 编辑:程序博客网 时间:2024/05/15 06:50

基于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引用@Overridepublic 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转换为dipmColumnWidth_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);// 设置Adaptercategory.setAdapter(categoryAdapter);// 通过ID获取LinearLayout布局对象LinearLayout categoryLayout = (LinearLayout) findViewById(R.id.category_layout);// 将网格视图组件添加到LinearLayout布局当中categoryLayout.addView(category);// 添加单元格点击事件category.setOnItemClickListener(new OnItemClickListener() {TextView categoryTitle;@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stubfor (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() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubcategoryScrollView.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() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stubIntent 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.1String 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 blocke.printStackTrace();return LOADERROR;//加载新闻失败}}/** * 为“加载更多”按钮定义匿名内部类 */private OnClickListener loadmoreListener = new OnClickListener() {@Overridepublic 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> {//准备运行@Overrideprotected void onPreExecute() {mTitleBarRefresh.setVisibility(View.GONE);mTitleBarProgress.setVisibility(View.VISIBLE);mLoadmoreButton.setText(R.string.loadmore_text);}//在后台运行@Overrideprotected Integer doInBackground(Object... params) {return getSpecCatNews(mCid, mNewsData, (Integer) params[0],(Boolean) params[1]);}//完成后台任务@Overrideprotected 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更新数据}}/** * 添加菜单 */@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// TODO Auto-generated method stubmenu.add(1, 1, 1, "更新");menu.add(1, 2, 2, "退出");return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch(item.getItemId()) {case 1:UpdateManager updateManager = new UpdateManager(MainActivity.this);//检测更新updateManager.checkUpdate();break;case 2:finish();break;}return true;}/** * 按键触发的事件 */@Overridepublic 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;//返回信息//拼接请求URlif(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 exceptionthrow 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类的内部类,并实现三个方法,比较灵活。具体实现看代码。

 

以上三点是小巫认为比较核心的地方,具体的需要动手之后才知道。