如今的APP许多都是要联网获取数据的,比如新闻客户端,如果想要尝试下这个流程的话,那么就需要有服务器和数据库了。在服务器上搭建各种环境是十分繁琐的,所以我们可以选择搭建一个本地服务器,然后在手机端获取其数据库内的数据。
这里选用XAMPP
XAMPP 是一个整合型的Apache套件,包含Apache、MySQL、PHP、PERL,可以一键式搭建起开发环境,推荐初学者使用。官方网址——https://www.apachefriends.org/zh_cn/index.html
软件开启后的界面是:
这里点击start开启Apache和MySQL就好,点击admin可以进入控制界面。
首先进入phpmyadmin界面,新建一个数据库 news,再建立一张表 new,为其建立六个属性,分别是:
id 主键
title 文章标题
description 摘要
date 文章发表时间
newUrl 文章链接
imageUrl 图片链接
创建new表的SQL语句如下
CREATE TABLE `new` (
`id` int(11) NOT NULL,
`title` text NOT NULL,
`description` text NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`newUrl` text NOT NULL,
`imageUrl` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
为new表插入数据
手机端的工作就是来读取数据并显示出来,不过手机端也无法直接访问数据库内容。所以在数据库端需要利用PHP将数据库内容返回为JSON格式的数据,然后手机端再去解析JSON数组
首先是进行数据库连接
mysql_connect.php
//数据库的用户名为root,密码为空
$con = mysql_connect("localhost", "root", "");
//设置字符集为utf8
mysql_query("SET NAMES 'utf8'");
mysql_query("SET CHARACTER SET utf8");
mysql_query("SET CHARACTER_SET_RESULT=utf8");
if (!$con){
die(mysql_error());
}
//访问数据库news
mysql_select_db("news", $con);
?>
接下来是将读取到的数据库内容返回为JSON数组
getNewsJSON.php
/*
* 获得JSON数据
*/
require 'mysql_connect.php';
$n = 0;
$result = mysql_query("select * from new");
while ($row = mysql_fetch_array($result)){
$arr[$n++] = array("title" => $row['title'],
"description" => $row['description'],
"date" => $row['date'],
"newUrl" => $row['newUrl'],
"imageUrl" => $row['imageUrl']
);
}
//数组转换为JSON字符串
echo json_encode($arr);
?>
将两个php文件放到XAMPP安装目录的htdocs文件夹下,直接存放或者再新建个文件夹存放也可以,例如我的存放路径就是:C:\xampp\htdocs\news
然后打开浏览器,输入http://localhost/news/getNewsJSON.php
如果是呈现如下格式的数据,那就说明数据库数据读取成功了。没有直接呈现中文字符是因为编码问题,无需理会。
服务器端开发完毕了,接下来就要进行移动端的设计了。
新建个工程。要做的是一个新闻客户端,新闻列表就采用RecyclerView来呈现,将默认布局改为:
<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="com.zy.news.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent">android.support.v7.widget.RecyclerView>
RelativeLayout>
列表的子项布局:
new_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#f7f9fa"
android:orientation="horizontal"
android:padding="6dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/new_image"
android:layout_width="130dp"
android:layout_height="130dp"
android:layout_gravity="center" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="150dp"
android:layout_weight="1"
android:orientation="vertical"
android:padding="6dp">
<TextView
android:id="@+id/new_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/black"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/new_introduction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="4"
android:textColor="@android:color/black"
android:textSize="16sp" />
<TextView
android:id="@+id/new_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="6dp"
android:textColor="@android:color/black"
android:textSize="13sp" />
LinearLayout>
LinearLayout>
SimpleDraweeView是Fresco推出的一款图片加载控件,导入SimpleDraweeView的方法是在build.gradle文件下加入语句compile 'com.facebook.fresco:fresco:0.10.0'
,然后点击同步即可
前边在建表时时设立了六个属性,除去主键id外,一篇新闻还有五个属性。为新闻建立一个实体NewIntroduction.java,省略相应的get和set属性。
public class NewIntroduction {
//文章标题
private String title;
//文章摘要
private String description;
//文章发表时间
private String date;
//文章链接
private String newUrl;
//图片链接
private String imageUrl;
}
新建一个NewsItemListAdapter.java文件,继承自RecyclerView.Adapter,作为RecyclerView的适配器
/**
* Created by ZY on 2016/6/10.
*/
public class NewsItemListAdapter extends RecyclerView.Adapter {
private List introductionList;
private LayoutInflater inflater;
public NewsItemListAdapter(Context context, List introductionList) {
this.introductiOnList= introductionList;
this.inflater = LayoutInflater.from(context);
}
@Override
public NewsHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.new_item, parent, false);
return new NewsHolder(view);
}
@Override
public void onBindViewHolder(NewsHolder holder, int position) {
holder.new_image.setImageURI(Uri.parse(introductionList.get(position).getImageUrl()));
holder.new_title.setText(introductionList.get(position).getTitle());
holder.new_introduction.setText(introductionList.get(position).getDescription());
holder.new_date.setText(introductionList.get(position).getDate());
}
@Override
public int getItemCount() {
if (introductiOnList== null || introductionList.size() == 0) {
return 0;
}
return introductionList.size();
}
}
NewsHolder也是一个自定义类,继承自RecyclerView.ViewHolder
/**
* Created by ZY on 2016/6/10.
*/
public class NewsHolder extends RecyclerView.ViewHolder {
public SimpleDraweeView new_image;
public TextView new_title;
public TextView new_introduction;
public TextView new_date;
public NewsHolder(View itemView) {
super(itemView);
new_image = (SimpleDraweeView) itemView.findViewById(R.id.new_image);
new_title = (TextView) itemView.findViewById(R.id.new_title);
new_introduction = (TextView) itemView.findViewById(R.id.new_introduction);
new_date = (TextView) itemView.findViewById(R.id.new_date);
}
}
修改MainActivity
public class MainActivity extends AppCompatActivity {
private List newIntroductionList;
private NewsItemListAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fresco.initialize(this);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycleView);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false));
newIntroductiOnList= new ArrayList<>();
adapter = new NewsItemListAdapter(MainActivity.this, newIntroductionList);
recyclerView.setAdapter(adapter);
}
}
此时newIntroductionList 内还没有填充数据,程序打开后还是一片空白,所以接下来就要来获取本地服务器内的数据
新建一个Util类
getNewsJSon(String strUrl)方法用来访问getNewsJSON.php页面,获取页面数据
public static String getNewsJSon(String strUrl) throws IOException {
URL url;
URLConnection urlConnection;
HttpURLConnection httpURLConnection;
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader reader = null;
StringBuffer resultBuffer = null;
try {
url = new URL(strUrl);
urlCOnnection= url.openConnection();
httpURLCOnnection= (HttpURLConnection) urlConnection;
httpURLConnection.setRequestProperty("Accept-Charset", "utf-8");
inputStream = httpURLConnection.getInputStream();
inputStreamReader = new InputStreamReader(inputStream);
reader = new BufferedReader(inputStreamReader);
resultBuffer = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
resultBuffer.append(line);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
reader.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (inputStream != null) {
inputStream.close();
}
}
if (resultBuffer == null) {
return null;
}
return resultBuffer.toString();
}
getNewIntroductionList(String strUrl)方法用来解析JSON数组
public static List<NewIntroduction> getNewIntroductionList(String strUrl) throws IOException {
String html = getNewsJSon(strUrl);
if (html == null) {
return null;
}
List<NewIntroduction> newIntroductionList = new ArrayList<>();
try {
JSONArray jsonArray = new JSONArray(html);
for (int i = 0; i < jsonArray.length(); i++) {
NewIntroduction newIntroduction = new NewIntroduction();
JSONObject jsonObject = jsonArray.optJSONObject(i);
String title = jsonObject.getString("title");
String description = jsonObject.getString("description");
String date = jsonObject.getString("date");
String newUrl = jsonObject.getString("newUrl");
String imageUrl = jsonObject.getString("imageUrl");
newIntroduction.setTitle(title);
newIntroduction.setDescription(description);
newIntroduction.setDate(date);
newIntroduction.setNewUrl(newUrl);
newIntroduction.setImageUrl(imageUrl);
newIntroductionList.add(newIntroduction);
}
} catch (JSONException e) {
e.printStackTrace();
}
return newIntroductionList;
}
然后在MainActivity文件中建立一个内部类MyAsyncTask继承自
AsyncTask<String, Void, List<NewIntroduction>>
用来进行联网操作。将读取到的JSON数组解析为NewIntroduction对象,传入适配器中
class MyAsyncTask extends AsyncTask<String, Void, List<NewIntroduction>> {
@Override
protected List doInBackground(String... params) {
List newIntroductiOnList= null;
try {
newIntroductiOnList= Util.getNewIntroductionList(params[0]);
} catch (IOException e) {
e.printStackTrace();
}
return newIntroductionList;
}
@Override
protected void onPostExecute(List introductionList) {
if (introductiOnList== null || introductionList.size() == 0) {
return;
}
MainActivity.this.newIntroductionList.addAll(introductionList);
MainActivity.this.adapter.notifyDataSetChanged();
}
}
然后在onCreate(Bundle savedInstanceState)方法的最后一行new一个MyAsyncTask对象即可。模拟器访问本地服务器的IP地址为10.0.2.2,所以完整的访问地址应为http://10.0.2.2/news/getNewsJSON.php
new MyAsyncTask().execute("http://10.0.2.2/news/getNewsJSON.php");
最后还要加上联网访问权限
<uses-permission android:name="android.permission.INTERNET" />
程序运行结果如下:
代码下载地址:一个简单的联网新闻客户端的开发思路