从服务器中获取新闻,然后以列表的形式显示到界面上,每一个条目有图片/标题/正文/评论数
服务器把新闻保存在数据库里,把新闻给客户端就是把数据从数据库里取出来,封装到xml文件里,然后把xml给客户端,客户端再解析xml
也就是说客户端请求新闻信息的时候是请求所有新闻的文本信息以及新闻所属图片的网址,拿到网址,再对网址单独请求一次图片
加一个ListView就可以了
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
>ListView>
RelativeLayout>
每个条目的布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.loopj.android.image.SmartImageView ////开源项目自定义控件
android:id="@+id/iv"
//宽高要写死防止不匹配
android:layout_width="100dp"
android:layout_height="80dp"
android:src="@drawable/ic_launcher"
//图片竖直居中
android:layout_centerVertical="true"
/>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="这是大标题"
//不要换行,多余的部分会用...显示
android:singleLine="true"
android:layout_toRightOf="@id/iv"
android:textSize="22sp"
/>
<TextView
android:id="@+id/tv_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="这里是正文kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk"
android:layout_toRightOf="@id/iv"
android:layout_below="@id/tv_title"
android:textSize="14sp"
android:textColor="@android:color/darker_gray"
//正文只能是两行
android:lines="2"
/>
<TextView
android:id="@+id/tv_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2333条评论"
android:textColor="#ff0000"
//位于条目右下方
android:layout_alignParentRight="true"
android:layout_below="@id/tv_detail"
/>
RelativeLayout>
点击启动客户端时新闻就已经下载了
//获取新闻信息
private void getNewsInfo() {
//开一个子线程去下载新闻信息
Thread t = new Thread(){
@Override
public void run() {
//先确定新闻的网址
String path = "http://10.66.98.197/news.xml";
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setReadTimeout(5000);
//发送http GET请求获取响应码
if(conn.getResponseCode() == 200){
InputStream is = conn.getInputStream();
//使用pull解析器,解析这个流
parseNewsXml(is);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
对news.xml文件进行pull解析
private void parseNewsXml(InputStream is) {
XmlPullParser xp = Xml.newPullParser();
try {
xp.setInput(is, "utf-8");
//对结点的事件类型进行判断,就可以知道当前结点是什么节点
int type = xp.getEventType();
News news = null;
while(type != XmlPullParser.END_DOCUMENT){
switch(type){
case XmlPullParser.START_TAG:
if("newslist".equals(xp.getName())){
newsList = new ArrayList();
}
else if("news".equals(xp.getName())){
news = new News();
}
else if("title".equals(xp.getName())){
String title = xp.nextText();
news.setTitle(title);
}
else if("detail".equals(xp.getName())){
String detail = xp.nextText();
news.setDetail(detail);
}
else if("comment".equals(xp.getName())){
String comment = xp.nextText();
news.setComment(comment);
}
else if("image".equals(xp.getName())){
String image = xp.nextText();
news.setImageUrl(image);
}
break;
case XmlPullParser.END_TAG:
if("news".equals(xp.getName())){
newsList.add(news);
}
break;
}
//解析完当前节点后,把指针移动到下一个节点,并返回它的事件类型
type = xp.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
主线程中进行调用
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getNewsInfo();
// ListView lv = (ListView) findViewById(R.id.lv);
// //要保证在设置适配器时,新闻xml文件已经解析完毕,又来Handler
// lv.setAdapter(new MyAdapter());
}
class MyAdapter extends BaseAdapter{
//得到模型层元素的数量,用来确定listview需要有多少个条目
@Override
public int getCount() {
return newsList.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
//需要返回一个View对象,作为Listview条目显示至界面
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = View.inflate(MainActivity.this, R.layout.item_listview, null);
return v;
}
}
因为pull解析是在子线程中完成,如果没有完成就执行ListViewAdapter就会报空指针异常,因此要在子线程pull解析完后用Handler
//发消息,让主线程设置ListView的适配器,消息队列中不带任何数据
//如果消息不需要携带数据,可以发送空消息
handler.sendEmptyMessage(1);
Handler中重写HandleMessage方法
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
ListView lv = (ListView) findViewById(R.id.lv);
//要保证在设置适配器时,新闻xml文件已经解析完毕,又来Handler
lv.setAdapter(new MyAdapter());
}
};
将各条目里的数据分别写入各条目中
//需要返回一个View对象,作为Listview条目显示至界面
@Override
public View getView(int position, View convertView, ViewGroup parent) {
News news = newsList.get(position);
View v = View.inflate(MainActivity.this, R.layout.item_listview, null);
//给三个文本框设置内容
TextView tv_title = (TextView) v.findViewById(R.id.tv_title);
tv_title.setText(news.getTitle());
TextView tv_detail = (TextView) v.findViewById(R.id.tv_detail);
tv_detail.setText(news.getDetail());
TextView tv_comment = (TextView) v.findViewById(R.id.tv_comment);
tv_comment.setText(news.getComment() + "条评论");
//给新闻图片imageview设置内容
//使用开源项目smartImageView
SmartImageView siv = (SmartImageView) v.findViewById(R.id.iv);
siv.setImageUrl(news.getImageUrl());
return v;
}
1,ListView缓存条目
News news = newsList.get(position);
View v = null;
if(convertView == null){
//无缓存
v = View.inflate(MainActivity.this, R.layout.item_listview, null);
}
else{
//有缓存
v = convertView;
}
进一步优化
findViewById经常执行也是浪费时间的,因此可创建内部类,然后缓存,省去findViewById的时间
//创建内部类,省去findViewById的时间
class ViewHolder{
//条目的布局文件中有什么组件,这里就定义什么属性
TextView tv_title;
TextView tv_detail;
TextView tv_comment;
SmartImageView siv;
}
News news = newsList.get(position);
View v = null;
ViewHolder mHolder;
if(convertView == null){
//无缓存
v = View.inflate(MainActivity.this, R.layout.item_listview, null);
//把布局文件中所有组件都封装到mHolder对象中
mHolder = new ViewHolder();
mHolder.tv_title = (TextView) v.findViewById(R.id.tv_title);
mHolder.tv_detail = (TextView) v.findViewById(R.id.tv_detail);
mHolder.tv_comment = (TextView) v.findViewById(R.id.tv_comment);
mHolder.siv = (SmartImageView) v.findViewById(R.id.iv);
//再把mHolder封装到view对象中
//意义:
v.setTag(mHolder);
}
else{
//有缓存
v = convertView;
//重新拿到mHolder
mHolder = (ViewHolder) v.getTag();
}
//给三个文本框设置内容
mHolder.tv_title.setText(news.getTitle());
mHolder.tv_detail.setText(news.getDetail());
mHolder.tv_comment.setText(news.getComment() + "条评论");
//给新闻图片imageview设置内容
//使用开源项目smartImageView
mHolder.siv.setImageUrl(news.getImageUrl());
return v;
}
public class MainActivity extends Activity {
List newsList;
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
ListView lv = (ListView) findViewById(R.id.lv);
//要保证在设置适配器时,新闻xml文件已经解析完毕,又来Handler
lv.setAdapter(new MyAdapter());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getNewsInfo();
// ListView lv = (ListView) findViewById(R.id.lv);
// //要保证在设置适配器时,新闻xml文件已经解析完毕,又来Handler
// lv.setAdapter(new MyAdapter());
}
class MyAdapter extends BaseAdapter{
//得到模型层元素的数量,用来确定listview需要有多少个条目
@Override
public int getCount() {
return newsList.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
//需要返回一个View对象,作为Listview条目显示至界面
@Override
public View getView(int position, View convertView, ViewGroup parent) {
News news = newsList.get(position);
View v = null;
ViewHolder mHolder;
if(convertView == null){
//无缓存
v = View.inflate(MainActivity.this, R.layout.item_listview, null);
//把布局文件中所有组件都封装到mHolder对象中
mHolder = new ViewHolder();
mHolder.tv_title = (TextView) v.findViewById(R.id.tv_title);
mHolder.tv_detail = (TextView) v.findViewById(R.id.tv_detail);
mHolder.tv_comment = (TextView) v.findViewById(R.id.tv_comment);
mHolder.siv = (SmartImageView) v.findViewById(R.id.iv);
//再把mHolder封装到view对象中
//意义:
v.setTag(mHolder);
}
else{
//有缓存
v = convertView;
//重新拿到mHolder
mHolder = (ViewHolder) v.getTag();
}
//给三个文本框设置内容
mHolder.tv_title.setText(news.getTitle());
mHolder.tv_detail.setText(news.getDetail());
mHolder.tv_comment.setText(news.getComment() + "条评论");
//给新闻图片imageview设置内容
//使用开源项目smartImageView
mHolder.siv.setImageUrl(news.getImageUrl());
return v;
}
//创建内部类,省去findViewById的时间
class ViewHolder{
//条目的布局文件中有什么组件,这里就定义什么属性
TextView tv_title;
TextView tv_detail;
TextView tv_comment;
SmartImageView siv;
}
}
//获取新闻信息
private void getNewsInfo() {
//开一个子线程去下载新闻信息
Thread t = new Thread(){
@Override
public void run() {
//先确定新闻的网址
String path = "http://10.66.98.197/news.xml";
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setReadTimeout(5000);
//发送http GET请求获取响应码
if(conn.getResponseCode() == 200){
InputStream is = conn.getInputStream();
//使用pull解析器,解析这个流
parseNewsXml(is);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
private void parseNewsXml(InputStream is) {
XmlPullParser xp = Xml.newPullParser();
try {
xp.setInput(is, "utf-8");
//对结点的事件类型进行判断,就可以知道当前结点是什么节点
int type = xp.getEventType();
News news = null;
while(type != XmlPullParser.END_DOCUMENT){
switch(type){
case XmlPullParser.START_TAG:
if("newslist".equals(xp.getName())){
newsList = new ArrayList();
}
else if("news".equals(xp.getName())){
news = new News();
}
else if("title".equals(xp.getName())){
String title = xp.nextText();
news.setTitle(title);
}
else if("detail".equals(xp.getName())){
String detail = xp.nextText();
news.setDetail(detail);
}
else if("comment".equals(xp.getName())){
String comment = xp.nextText();
news.setComment(comment);
}
else if("image".equals(xp.getName())){
String image = xp.nextText();
news.setImageUrl(image);
}
break;
case XmlPullParser.END_TAG:
if("news".equals(xp.getName())){
newsList.add(news);
}
break;
}
//解析完当前节点后,把指针移动到下一个节点,并返回它的事件类型
type = xp.next();
}
//发消息,让主线程设置ListView的适配器,消息队列中不带任何数据
//如果消息不需要携带数据,可以发送空消息
handler.sendEmptyMessage(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}