2013年2月27日,继续小巫新闻客户端的开发。
上一篇忘记谈及一个比较重要的内容,有些网友留言给小巫问:Json数据的明细是怎样的?,在这里小巫先声明一点,小巫对Json数据的格式也是刚接触,这是稍微知道其的结构组成,关于是否尤其内容并不是很清楚。但小巫对与怎么进行JSON格式的解析已经比较清晰了。下面就接这篇博客来介绍一下,新闻详细内容的JSON数据是怎样的,通过浏览器得到的数据到底是怎样的。
如果得到小巫所共享的资源的话,服务端的项目是一个叫web的JavaEE项目,如果有看过里面的具体实现的话,读者可能就会明白,服务端是如何与数据库进行交互的了。那好,部署好项目到Tomcat中,在浏览器就可以得到相应的JSON数据源。
获取新闻详细内容的Servlet代码如下:注:(这是服务端的代码),关于解析JSON数据的解析,在介绍客户端业务逻辑实现的时候会说明。
package com.szy.web.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.JSONException; import org.json.JSONObject; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import com.szy.web.dao.CommentDAO; import com.szy.web.dao.NewsDAO; import com.szy.web.model.News; import com.szy.web.util.TextUtility; /** *@author coolszy *@date Feb 19, 2012 *@blog http://blog.92coding.com *http://localhost:8080/web/getNews?nid=1 */ public class GetNewsServlet extends HttpServlet { private static final long serialVersionUID = -7715894432269979527L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); String nidStr= request.getParameter("nid"); int nid = 0; nid = TextUtility.String2Int(nidStr); JSONObject jObject = new JSONObject(); try { CommentDAO commentDAO = new CommentDAO(); long commentCount = commentDAO.getSpecifyNewsCommentsCount(nid); NewsDAO newsDAO = new NewsDAO(); News news = newsDAO.getNews(nid); JSONObject jObject2 = new JSONObject(); if (!TextUtility.isNull(news.getTitle())) { HashMap<String, Object> hashMap = new HashMap<String, Object>(); /***************后期增加代码,主要用于测试TextView显示图片功能********************/ String newsbody = news.getBody(); ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String,Object>>(); HashMap<String, Object> hashMap1 = new HashMap<String, Object>(); hashMap1.put("index", 0); hashMap1.put("type", "image"); hashMap1.put("value", "http://www.eportfolio.wtuc.edu.tw/blog/attach/35/16035/95/bf_22696_7751198_66497_4.jpg"); HashMap<String, Object> hashMap2 = new HashMap<String, Object>(); hashMap2.put("index", 1); hashMap2.put("type", "text"); hashMap2.put("value", newsbody); list.add(hashMap1); list.add(hashMap2); /********************************************************/ hashMap.put("nid", news.getNid()); hashMap.put("title", news.getTitle()); //hashMap.put("body", news.getBody()); hashMap.put("body", list); hashMap.put("source", news.getSource()); hashMap.put("replycount", commentCount); hashMap.put("ptime", news.getPtime()); hashMap.put("imgsrc", news.getImgSrc()); jObject2.put("news", hashMap); } jObject.put("ret", 0); jObject.put("msg", "ok"); jObject.put("data", jObject2); } catch (Exception e) { e.printStackTrace(); try { jObject.put("ret", 1); jObject.put("msg", e.getMessage()); jObject.put("data", ""); } catch (JSONException ex) { ex.printStackTrace(); } } PrintWriter out = response.getWriter(); out.println(jObject); out.flush(); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { throw new NotImplementedException(); } }
这就是服务端从数据库获取新闻详细内容的Servlet,当然这只是Servlet的代码,查询数据库的代码需要到web项目查看。
在浏览器敲入如下URL:http://localhost:8080/web/getNews?nid=2
就会得到数据nid为2的新闻内容如下:
{"ret":0,"data":{"news":{"body":[{"index":0,"value":"http://www.eportfolio.wtuc.edu.tw/blog/attach/35/16035/95/bf_22696_7751198_66497_4.jpg","type":"image"},{"index":1,"value":"
新华网十八大快讯:党的十八届一中全会选举习近平、李克强、张德江、俞正声、刘云山、王岐山、张高丽为中央政治局常委。<\/p>","type":"text"}],"title":"党的十八届一中全会选举习近平、李克强、张德江、俞正声、刘云山、王岐山、张高丽为中央政治局常委 ","source":"来源: 新华网","nid":2,"replycount":1,"ptime":"2012年11月15日 11:45:36"}},"msg":"ok"}
这样看是比较乱的,需要将这些数据进行一下格式化,看起来比较舒服。
{ "ret": 0, "data": { "news": { "body": [ { "index": 0, "value": "http://www.eportfolio.wtuc.edu.tw/blog/attach/35/16035/95/bf_22696_7751198_66497_4.jpg", "type": "image" }, { "index": 1, "value": " <p>新华网十八大快讯:党的十八届一中全会选举习近平、李克强、张德江、俞正声、刘云山、王岐山、张高丽为中央政治局常委。</p>", "type": "text" } ], "title": "党的十八届一中全会选举习近平、李克强、张德江、俞正声、刘云山、王岐山、张高丽为中央政治局常委 ", "source": "来源: 新华网", "nid": 2, "replycount": 1, "ptime": "2012年11月15日 11:45:36" } }, "msg": "ok" }
那好,关于JSON数据的明细,就说到这里。从格式化的JSON数据是可以很清楚得到新闻的组成结构的,接下来就是要在客户端解析这些数据,并把它们显示到手机界面上,这才是我们花那么大功夫去解析JSON数据的原因。
贴上代码之前,当然需要看一下最终需要实现的效果图:
关于这个界面我就不做过多的说明了,最重要的是具体实现。
package com.xiaowu.news; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import org.json.JSONArray; import org.json.JSONObject; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.ViewFlipper; import com.xiaowu.news.service.SyncHttp; import com.xiaowu.news.thread.PostCommentsThread; public class NewsDetailActivity extends Activity { private final int FINISH = 0; //代表线程的状态的结束 private LayoutInflater mNewsbodyLayoutInflater; private ViewFlipper mNewsBodyFlipper; //屏幕切换控件 private ArrayList<HashMap<String, Object>> mNewsData; private float mStartX; //手指按下的开始位置 private int mPosition = 0; //点击新闻位置 private int mCursor = 0; //用来标记新闻点击的位置 private int mNid; //新闻编号 private Button mNewsDetailTitleBarComm; //显示评论条数的按钮 private ConstomTextView mNewsBodyDetail; //新闻详细内容 private LinearLayout mNewsReplyEditLayout; //新闻回复的布局 private LinearLayout mNewsReplyImgLayout; //新闻图片回复的布局 private EditText mNewsReplyEditText; //新闻回复的文本框 private ImageButton mShareNewsButton; //分享新闻的按钮 private ImageButton mFavoritesButton; //收藏新闻的按钮 private boolean keyboardShow; //键盘是否显示 private Handler mHandler = new Handler() { @SuppressWarnings("unchecked") @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub switch (msg.arg1) { case FINISH: //把获取到的新闻显示到界面上 ArrayList<HashMap<String, Object>> bodyList = (ArrayList<HashMap<String, Object>>) msg.obj; mNewsBodyDetail.setText(bodyList); break; } } }; @SuppressWarnings("unchecked") @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.newsdetails_layout); mNewsReplyEditLayout = (LinearLayout) findViewById(R.id.news_reply_edit_layout); mNewsReplyImgLayout = (LinearLayout) findViewById(R.id.news_reply_img_layout); Button newsDetailPrev = (Button) findViewById(R.id.newsdetail_titlebar_previous); Button newsDetailNext = (Button) findViewById(R.id.newsdetail_titlebar_next); mNewsDetailTitleBarComm = (Button) findViewById(R.id.newsdetail_titlebar_comments); mNewsReplyEditText = (EditText) findViewById(R.id.news_reply_edittext); mShareNewsButton = (ImageButton) findViewById(R.id.news_share_btn); mFavoritesButton = (ImageButton) findViewById(R.id.news_favorites_btn); NewsDetailOnClickListener newsDetailOnClickListener = new NewsDetailOnClickListener(); newsDetailPrev.setOnClickListener(newsDetailOnClickListener); newsDetailNext.setOnClickListener(newsDetailOnClickListener); mNewsDetailTitleBarComm.setOnClickListener(newsDetailOnClickListener); mShareNewsButton.setOnClickListener(newsDetailOnClickListener); mFavoritesButton.setOnClickListener(newsDetailOnClickListener); Button newsReplyPost = (Button) findViewById(R.id.news_reply_post); newsReplyPost.setOnClickListener(newsDetailOnClickListener); ImageButton newsReplyImgBtn = (ImageButton) findViewById(R.id.news_reply_img_btn); newsReplyImgBtn.setOnClickListener(newsDetailOnClickListener); //获取传送的数据 Intent intent = getIntent(); Bundle bundle = intent.getExtras(); String categoryName = bundle.getString("categoryTitle"); TextView titleBarTitle = (TextView) findViewById(R.id.newsdetail_titlebar_title); //设置标题栏的标题 titleBarTitle.setText(categoryName); //获取新闻集合 Serializable serializable = bundle.getSerializable("newsData"); mNewsData = (ArrayList<HashMap<String, Object>>) serializable; //获取点击位置 mCursor = mPosition = bundle.getInt("position"); mNewsBodyFlipper = (ViewFlipper) findViewById(R.id.news_body_flipper); // 获取LayoutInflater对象 mNewsbodyLayoutInflater = getLayoutInflater(); inflateView(0); //启动线程 new UpdateNewsThread().start(); } /** * 显示上一条新闻 */ private void showPrevious() { if(mPosition > 0) { mPosition--; //记录当前新闻编号 HashMap<String, Object> hashMap = mNewsData.get(mPosition); mNid = (Integer) hashMap.get("nid"); if(mCursor > mPosition){ mCursor = mPosition; inflateView(0); mNewsBodyFlipper.showNext(); } mNewsBodyFlipper.setInAnimation(this, R.anim.push_right_in); //设置下一页进来时的动画 mNewsBodyFlipper.setOutAnimation(this, R.anim.push_right_out); //设置当前页出去的动画 mNewsBodyFlipper.showPrevious(); } else { Toast.makeText(NewsDetailActivity.this, "没有上一篇新闻", Toast.LENGTH_SHORT).show(); } } /** * 显示下一条新闻 */ private void showNext() { if(mPosition < mNewsData.size() - 1){ // 设置下一屏动画 mNewsBodyFlipper.setInAnimation(this, R.anim.push_left_in); mNewsBodyFlipper.setOutAnimation(this, R.anim.push_left_out); mPosition++; //记录当前新闻编号 HashMap<String, Object> hashMap = mNewsData.get(mPosition); mNid = (Integer) hashMap.get("nid"); if(mPosition >= mNewsBodyFlipper.getChildCount()){ inflateView(mNewsBodyFlipper.getChildCount()); } mNewsBodyFlipper.showNext(); } else { Toast.makeText(NewsDetailActivity.this, "没有下篇新闻", Toast.LENGTH_SHORT).show(); } } private void inflateView(int index) { //获取点击新闻信息 HashMap<String, Object> hashMap = mNewsData.get(mPosition); mNid = (Integer) hashMap.get("nid"); View mNewsBodyView = mNewsbodyLayoutInflater.inflate( R.layout.newsbody_layout, null); mNewsDetailTitleBarComm.setText(hashMap.get("newslist_item_comments").toString() + "跟帖"); //新闻标题 TextView newsTitle = (TextView) mNewsBodyView .findViewById(R.id.news_body_title); newsTitle.setText(hashMap.get("newslist_item_title").toString()); //新闻的出处和发布时间 TextView newsPtimeAndSource = (TextView) mNewsBodyView .findViewById(R.id.news_body_ptime_source); newsPtimeAndSource.setText(hashMap.get("newslist_item_source").toString() + " " + hashMap.get("newslist_item_ptime").toString()); mNewsBodyDetail = (ConstomTextView) mNewsBodyView .findViewById(R.id.news_body_details); mNewsBodyDetail.setText(getNewsBody()); mNewsBodyFlipper.addView(mNewsBodyView, index); mNewsBodyDetail.setOnTouchListener(new NewsBodyOntouchListener()); } // 定义内部类--用于处理标题栏的按钮的触发事件 private class NewsDetailOnClickListener implements OnClickListener { @Override public void onClick(View v) { InputMethodManager m = (InputMethodManager) mNewsReplyEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); // TODO Auto-generated method stub switch (v.getId()) { //上一篇 case R.id.newsdetail_titlebar_previous: showPrevious(); break; //下一篇 case R.id.newsdetail_titlebar_next: showNext(); break; //跟帖 case R.id.newsdetail_titlebar_comments: Intent intent = new Intent(NewsDetailActivity.this, CommentsActivity.class); intent.putExtra("nid", mNid); startActivity(intent); break; //“写跟帖”图片 case R.id.news_reply_img_btn: mNewsReplyEditLayout.setVisibility(View.VISIBLE); mNewsReplyImgLayout.setVisibility(View.GONE); mNewsReplyEditText.requestFocus(); //显示输入法 m.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT); keyboardShow = true; break; //分享按钮 case R.id.news_share_btn: Intent shareIntent = new Intent(Intent.ACTION_SEND); //纯文本 shareIntent.setType("text/plain"); shareIntent.putExtra(Intent.EXTRA_SUBJECT, "分享"); shareIntent.putExtra(Intent.EXTRA_TEXT, "我想将这个分享给你...."+ getTitle()); startActivity(Intent.createChooser(shareIntent, getTitle())); break; //收藏按钮 case R.id.news_favorites_btn: Toast.makeText(NewsDetailActivity.this, "收藏成功", Toast.LENGTH_SHORT).show(); break; //发表按钮 case R.id.news_reply_post: //隐藏输入法 m.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); String str = mNewsReplyEditText.getText().toString(); if(str.equals("")){ Toast.makeText(NewsDetailActivity.this, "不能为空", Toast.LENGTH_SHORT).show(); } else { mNewsReplyEditLayout.post(new PostCommentsThread(mNid, "广州市", str + "", new NewsDetailActivity())); mNewsReplyEditLayout.setVisibility(View.GONE); mNewsReplyImgLayout.setVisibility(View.VISIBLE); } break; } } } private class NewsBodyOntouchListener implements OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { //手指按下 case MotionEvent.ACTION_DOWN: if(keyboardShow){ mNewsReplyEditLayout.setVisibility(View.GONE); mNewsReplyImgLayout.setVisibility(View.VISIBLE); //隐藏输入法 InputMethodManager m = (InputMethodManager) mNewsReplyEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); m.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); keyboardShow = false; } //得到按下的横坐标的位置 mStartX = event.getX(); break; case MotionEvent.ACTION_UP: // 往左滑动 if (event.getX() < mStartX) { showNext(); } // 往右滑动 else if (event.getX() > mStartX) { showPrevious(); } break; } return true; } } /** * 定义一个线程类,用来更新获取到新闻的信息 * @author Administrator * */ private class UpdateNewsThread extends Thread { @Override public void run() { // TODO Auto-generated method stub ArrayList<HashMap<String, Object>> newsStr = getNewsBody(); Message msg = mHandler.obtainMessage(); //获取msg msg.arg1 = FINISH; msg.obj = newsStr; mHandler.sendMessage(msg); //给Handler发送信息 } } /** * 获取新闻详细信息 * @return */ private ArrayList<HashMap<String, Object>> getNewsBody(){ //String retStr = "网络连接失败,请稍后再试"; ArrayList<HashMap<String, Object>> bodylist = new ArrayList<HashMap<String,Object>>(); SyncHttp syncHttp = new SyncHttp(); //模拟器:url = "http://10.0.2.2:8080/web/getNews"; //本机:http://127.0.0.1:8080 //wifi局域网:http://192.168.220.1:8080 String url = "http://10.0.2.2:8080/web/getNews"; String params = "nid=" + mNid; try { String retString = syncHttp.httpGet(url, params); JSONObject jsonObject = new JSONObject(retString); //获取返回码,0表示成功 int retCode = jsonObject.getInt("ret"); if(retCode == 0) { JSONObject dataObject = jsonObject.getJSONObject("data"); JSONObject newsObject = dataObject.getJSONObject("news"); //retStr = newsObject.getString("body"); JSONArray bodyArray = newsObject.getJSONArray("body"); for(int i = 0; i < bodyArray.length(); i++) { JSONObject object = (JSONObject) bodyArray.opt(i); HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("index", object.get("index")); hashMap.put("type", object.get("type")); hashMap.put("value", object.get("value")); bodylist.add(hashMap); } } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return bodylist; } @Override public boolean onCreatePanelMenu(int featureId, Menu menu) { // TODO Auto-generated method stub menu.add(0, 0, 0, "分享"); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // TODO Auto-generated method stub switch(item.getItemId()) { case 0: Intent shareIntent = new Intent(Intent.ACTION_SEND); //纯文本 shareIntent.setType("text/plain"); shareIntent.putExtra(Intent.EXTRA_SUBJECT, "分享"); shareIntent.putExtra(Intent.EXTRA_TEXT, "我想把这个分享给你:" + getTitle()); shareIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(Intent.createChooser(shareIntent, getTitle())); System.out.println(getTitle()); break; } return super.onOptionsItemSelected(item); } }
这段代码设计到以下几个关键点:
1.异步更新新闻详细内容。 这里是用Handler来实现线程异步。
2.实现上下篇新闻切换的功能。
3.自定义TextView的实现类ConstomTextView。
关于上面的代码已经实现得比较完善了,不知道是否还可以进行优化。
那么关于新闻详细内容显示的业务逻辑实现就写到这里,然后关于小巫新闻客户端开发的系列博客就暂时记录到这里,感谢网友们的支持。因为新闻回复的内容具体实现没什么可说的,只要把前面的业务逻辑实现弄懂了,新闻回复的业务逻辑也就没什么难的。
如果网友们对小巫新闻客户端那部分有疑问,可以给我留言,小巫会把自己知道的东西都写出来。