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

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

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

 上一篇介绍了主界面的UI设计,现在直接进入主题,业务逻辑的实现,由于项目的开发总是在不断的完善的,最初实现的效果,总会随项目的进度而做出相应的改变,小巫也不可能从新开发整个客户端,然后再一步一步记录,那没有必要,学习东西,只需要知道关键点在哪里就行了,关于细节方面,遇到再去解决。就是这么简单。

 

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

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

 

下面是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类的内部类,并实现三个方法,比较灵活。具体实现看代码。

 

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

 

 

 

 

5楼liushicheng1昨天 16:07
写的不错。支持一下
Re: wwj_7482小时前
回复liushicheng1n谢谢
4楼ken0927昨天 15:42
感謝分享,謝謝
3楼zhijianhrc昨天 13:26
不错不错,完美!
Re: wwj_748昨天 13:46
回复zhijianhrcn写得还是比较粗的
2楼fromscnu昨天 13:14
楼主分享的资源貌似只有客户端代码,能否把服务器端代码分享一下呢?
Re: wwj_748昨天 13:14
回复fromscnun不可能的啊,服务端代码是一个名为web的项目文件,需要导入到MyEclipse当中的
1楼ly348018前天 09:13
能分享一下源代码看看吗,我想学习一下好看的布局,布局最头疼了,谢谢。linyu4928@126.com
Re: wwj_748昨天 10:23
回复ly348018n可以去我的资源里下载哦