上一篇文章我們分析了網易評論列表的界面效果,有了一個大概的瞭解後我們來爲我們的評論列表做些數據,爲了方便起見,就不通過服務器來做了,我們直接用SQLite來模擬一個簡單的本地服務器數據庫~~。經過我們前面的分析得出,我們的數據庫應該有這樣的三個表:
- User—>用來存放用戶的基本信息,比如用戶名、用戶頭像和用戶的地理位置等
- Post—>用來存放帖子的基本信息,比如帖子的創建時間等
- Comment—>用來存放評論的基本信息,比如評論的內容等
在實際項目中我們可以通過服務器後臺的一些配置來實現三者之間的關聯以方便使用,但是因爲我們是用的SQLite且數據庫置於本地,我們就使用最原始的辦法,爲表之間的關聯單獨創建一個關聯表:
Post_Comment—>用來存儲Post和User之間的關聯
User_Praise—>用來存儲用戶和點贊帖子之間的關聯
User_UnPraise—>用來存儲用戶和被踩帖子之間的關聯
User_Collect—>用來存儲用戶和收藏帖子之間的關聯
有了這樣的一個關聯後我們還需要一個唯一的標識值來作爲表之間關聯的標識,在Android中SQLite有主鍵_id,這個主鍵字段我們不能隨意更改否則在進行Cursor查詢的時候會出錯,而且_id是自增長的Integer類型,其在每個表中都是獨立存在的,在我們進行標識的時候就有可能重複,鑑於此我們分別爲User、Post和Comment表添加一個額外的flag字段,該字段的值我們在Android中使用UUID隨機生成。User、Post和Comment的大致關係我在PD中花了兩分鐘粗略畫了下(數據庫不是本節重點)如下圖:
PS:User和Post的多對多關係不會在實例中實現,但是會有體現
關聯表的模型就不給出了,下面我們來看看SQLite中構建表的SQL語句:
User表:
create table user(_id integer primary key autoincrement, flag varchar(16), userName varchar(16), nick integer, location varchar(32))
Post表:create table post(_id integer primary key autoincrement, flag varchar(16), createAt varchar(32))
Comment表:create table comment(_id integer primary key autoincrement, flag varchar(16), userFlag varchar(16), content varchar(1024), createAt varchar(32))
Post_Comment:
create table post_comment(_id integer primary key autoincrement, postFlag varchar(16), commentFlag varchar(16))
User_Praise:
create table user_praise(_id integer primary key autoincrement, userFlag varchar(16), postFlag varchar(16))
User_UnPraise:
create table user_unpraise(_id integer primary key autoincrement, userFlag varchar(16), postFlag varchar(16))
User_Collect:
create table user_collect(_id integer primary key autoincrement, userFlag varchar(16), postFlag varchar(16))
繼承SQLiteOpenHelper類打開數據庫建表:
package com.aigestudio.neteasecommentlistdemo.helper;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* SQLite數據幫助類
*
* @author Aige
* @since 2014/11/14
*/
public class ServerDBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "server.db";//數據庫名
private static final int VERSION = 1;//數據庫版本
/*
創建模型表SQL
*/
private static final String CREATE_TABLE_USER_SQL = "create table user(_id integer primary key autoincrement, flag varchar(16), userName varchar(16), nick integer, location varchar(32))";
private static final String CREATE_TABLE_POST_SQL = "create table post(_id integer primary key autoincrement, flag varchar(16), createAt varchar(32))";
private static final String CREATE_TABLE_COMMENT_SQL = "create table comment(_id integer primary key autoincrement, flag varchar(16), userFlag varchar(16), content varchar(1024), createAt varchar(32))";
/*
創建關聯表SQL
*/
private static final String CREATE_TABLE_POST_AND_COMMENT_SQL = "create table post_comment(_id integer primary key autoincrement, postFlag varchar(16), commentFlag varchar(16))";
private static final String CREATE_TABLE_USER_AND_PRAISE_SQL = "create table user_praise(_id integer primary key autoincrement, userFlag varchar(16), postFlag varchar(16))";
private static final String CREATE_TABLE_USER_AND_UNPRAISE_SQL = "create table user_unpraise(_id integer primary key autoincrement, userFlag varchar(16), postFlag varchar(16))";
private static final String CREATE_TABLE_USER_AND_COLLECT_SQL = "create table user_collect(_id integer primary key autoincrement, userFlag varchar(16), postFlag varchar(16))";
public ServerDBHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_USER_SQL);
db.execSQL(CREATE_TABLE_POST_SQL);
db.execSQL(CREATE_TABLE_COMMENT_SQL);
db.execSQL(CREATE_TABLE_POST_AND_COMMENT_SQL);
db.execSQL(CREATE_TABLE_USER_AND_PRAISE_SQL);
db.execSQL(CREATE_TABLE_USER_AND_UNPRAISE_SQL);
db.execSQL(CREATE_TABLE_USER_AND_COLLECT_SQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//不浪費時間處理了
}
}
所有對服務器數據庫操作的方法都會先封裝在接口IServerDAO.java中:
package com.aigestudio.neteasecommentlistdemo.dao;
import android.content.ContentValues;
import java.util.List;
import java.util.Map;
/**
* 服務器數據層接口
*
* @author Aige
* @since 2014/11/14
*/
public interface IServerDAO extends IDAO {
/**
* 根據參數添加一條數據
*
* @param table 數據要添加到的表名
* @param values 數據對象
* @return 布爾值代表數據添加成功與否
*/
public boolean add(String table, ContentValues values);
/**
* 根據SQL語句添加一條數據
*
* @param sql SQL語句
* @return 布爾值代表數據添加成功與否
*/
public boolean add(String sql);
/**
* 根據條件參數多條數據查詢
*
* @param table 要查詢的數據所在的表名
* @param columns 要查詢的列名
* @param whereClause 查詢的條件子句
* @param selectionArgs 條件子句佔位符的參數
* @param groupBy 分組控制
* @param having 分組過濾
* @param orderBy 排序
* @param limit 分頁
* @return 數據列表
*/
public List<Map<String, String>> queryMulti(String table, String[] columns, String whereClause, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
/**
* 根據SQL語句多條數據查詢
*
* @param sql SQL語句
* @return 數據列表
*/
public List<Map<String, String>> queryMulti(String sql);
/**
* 根據條件參數單條數據查詢
*
* @param table 要查詢的數據所在的表名
* @param columns 要查詢的列名
* @param flag 唯一標識值
* @param groupBy 分組控制
* @param having 分組過濾
* @param orderBy 排序
* @param limit 分頁
* @return 單條
*/
public Map<String, String> querySingle(String table, String[] columns, String flag, String groupBy, String having, String orderBy, String limit);
/**
* 根據SQL語句單條數據查詢
*
* @param sql SQL語句
* @return 單條數據
*/
public Map<String, String> querySingle(String sql);
/**
* 根據條件查詢單個值
*
* @param table 要查詢的數據所在的表名
* @param columns 要查詢的列名
* @param key 查詢的依據的列名
* @param value 查詢依據值
* @return 查詢結果
*/
public String queryValue(String table, String[] columns, String key, String value);
/**
* 根據SQL語句查詢單個值
*
* @param sql SQL語句
* @return 查詢結果
*/
public String queryValue(String sql);
}
具體實現在ServerDAO.java:
package com.aigestudio.neteasecommentlistdemo.dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.aigestudio.neteasecommentlistdemo.helper.ServerDBHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 服務器數據層
*
* @author Aige
* @since 2014/11/14
*/
public class ServerDAO implements IServerDAO {
private ServerDBHelper dbHelper;
public ServerDAO(Context context) {
dbHelper = new ServerDBHelper(context);
}
@Override
public boolean add(String table, ContentValues values) {
boolean flag = false;
SQLiteDatabase db = null;
try {
db = dbHelper.getWritableDatabase();
long id = db.insert(table, null, values);
flag = (id != -1 ? true : false);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != db) {
db.close();
}
}
return flag;
}
@Override
public boolean add(String sql) {
//未實現
return false;
}
@Override
public List<Map<String, String>> queryMulti(String table, String[] columns, String whereClause, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
SQLiteDatabase db = null;
Cursor cursor;
try {
db = dbHelper.getWritableDatabase();
cursor = db.query(table, columns, whereClause, selectionArgs, groupBy, having, orderBy, limit);
while (cursor.moveToNext()) {
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < cursor.getColumnCount(); i++) {
String columnName = cursor.getColumnName(i);
String columnValue = cursor.getString(cursor.getColumnIndex(columnName));
map.put(columnName, columnValue);
}
list.add(map);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != db) {
db.close();
}
}
return list;
}
@Override
public List<Map<String, String>> queryMulti(String sql) {
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
SQLiteDatabase db = null;
Cursor cursor;
try {
db = dbHelper.getWritableDatabase();
cursor = db.rawQuery(sql, null);
while (cursor.moveToNext()) {
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < cursor.getColumnCount(); i++) {
String columnName = cursor.getColumnName(i);
String columnValue = cursor.getString(cursor.getColumnIndex(columnName));
map.put(columnName, columnValue);
}
list.add(map);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != db) {
db.close();
}
}
return list;
}
@Override
public Map<String, String> querySingle(String table, String[] columns, String flag, String groupBy, String having, String orderBy, String limit) {
Map<String, String> map = new HashMap<String, String>();
SQLiteDatabase db = null;
Cursor cursor;
try {
db = dbHelper.getWritableDatabase();
cursor = db.query(table, columns, "flag like ?", new String[]{flag}, groupBy, having, orderBy, limit);
while (cursor.moveToNext()) {
for (int i = 0; i < cursor.getColumnCount(); i++) {
String columnName = cursor.getColumnName(i);
String columnValue = cursor.getString(cursor.getColumnIndex(columnName));
map.put(columnName, columnValue);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != db) {
db.close();
}
}
return map;
}
@Override
public Map<String, String> querySingle(String sql) {
return null;
}
@Override
public String queryValue(String table, String[] columns, String key, String value) {
String result = null;
SQLiteDatabase db = null;
try {
db = dbHelper.getWritableDatabase();
Cursor cursor = db.query(table, columns, key + " like ?", new String[]{value}, null, null, null, null);
cursor.moveToNext();
String columnName = cursor.getColumnName(0);
result = cursor.getString(cursor.getColumnIndex(columnName));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != db) {
db.close();
}
}
return result;
}
@Override
public String queryValue(String sql) {
//未實現
return null;
}
}
就兩種方法:增加數據和查詢數據,其他的未做在本例中也不需要~~。數據層的實現完成後我們來看看如何往數據庫中添加一些測試數據呢。
首先先建立不同的對象實體:
package com.aigestudio.neteasecommentlistdemo.beans;
import com.aigestudio.neteasecommentlistdemo.cons.ClsCons;
import java.util.UUID;
/**
* 用戶實體
*
* @author Aige
* @since 2014/11/14
*/
public class User {
/*
對應數據庫表列名
*/
public static final String COLUMN_FLAG = "flag", COLUMN_USERNAME = "userName", COLUMN_LOCATION = "location", COLUMN_NICK = "nick";
private String flag;//用戶標識系統隨機生成
private String userName;//用戶名
private String location;//用戶當前所在位置
private String nick;//用戶頭像資源ID
/**
* 用戶實體的構造函數,創建用戶插入數據庫時使用
*/
public User() {
this.flag = UUID.randomUUID().toString();
this.userName = ClsCons.USER_NAME[(int) (Math.random() * 10)];
this.location = ClsCons.DEFAULT_LOCATION[(int) (Math.random() * 5)];
this.nick = ClsCons.DEFAULT_NICK_RESID[(int) (Math.random() * 3)];
}
/**
* 用戶實體的構造函數,從數據庫獲取用戶數據並實例化對象時使用
*
* @param flag 用戶標識
* @param userName 用戶名
* @param location 用戶地理位置
* @param nick 用戶頭像資源ID
*/
public User(String flag, String userName, String location, String nick) {
this.flag = flag;
this.userName = userName;
this.location = location;
this.nick = nick;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
}
package com.aigestudio.neteasecommentlistdemo.beans;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
/**
* 帖子實體類
* 注:實體類的結構在實際項目中應在一定程度上參照服務器後臺對應表的數據結構來設計,這裏我就不多BB了
*
* @author Aige
* @since 2014/11/14
*/
public class Post {
private String flag;//評論標識:系統隨機生成
private String createAt;//評論時間:系統生成
private List<Comment> comments;//該帖子下的所有評論,按插入數據庫的_id順序排列保證時間先後的統一
private List<User> userPraises, userUnPraises, userCollects;//該帖子讚的人數、踩的人數和收藏的人數
private Type type;//帖子類型:最新,最熱or普通
/**
* 評論實體的構造函數
* 兩個成員變量均由系統賦值
*/
public Post() {
//生成隨機標識,這個隨機標識準確來說應該是服務端生成,這裏就不麻煩了 = =
flag = UUID.randomUUID().toString();
//生成系統時間,這個數據創建時間也應該是服務端生成
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss", Locale.getDefault());
this.createAt = format.format(date);
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getCreateAt() {
return createAt;
}
public void setCreateAt(String createAt) {
this.createAt = createAt;
}
public List<Comment> getComments() {
return comments;
}
public void setComments(List<Comment> comments) {
this.comments = comments;
}
public List<User> getUserPraises() {
return userPraises;
}
public void setUserPraises(List<User> userPraises) {
this.userPraises = userPraises;
}
public List<User> getUserUnPraises() {
return userUnPraises;
}
public void setUserUnPraises(List<User> userUnPraises) {
this.userUnPraises = userUnPraises;
}
public List<User> getUserCollects() {
return userCollects;
}
public void setUserCollects(List<User> userCollects) {
this.userCollects = userCollects;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
/**
* 帖子類型的枚舉類
*/
public enum Type {
HOTTEST, NEWEST, NORMAL
}
}
package com.aigestudio.neteasecommentlistdemo.beans;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
/**
* 評論實體類
*
* @author Aige
* @since 2014/11/14
*/
public class Comment {
/*
對應數據庫表列名
*/
public static final String COLUMN_FLAG = "flag", COLUMN_USERFLAG = "userFlag", COLUMN_CONTENT = "content", COLUMN_CREATEAT = "createAt";
private String flag;//評論標識:系統隨機生成
private String content;//評論內容
private String createAt;//評論時間:系統生成
private User user;//用戶實體
/**
* 評論實體的構造函數,生成評論插入數據庫時使用
*
* @param content 評論內容
*/
public Comment(String content, User user) {
//生成隨機標識,這個隨機標識準確來說應該是服務端生成,這裏就不麻煩了 = =
flag = UUID.randomUUID().toString();
//生成系統時間,這個數據創建時間也應該是服務端生成
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss", Locale.getDefault());
this.createAt = format.format(date);
this.user = user;
this.content = content;
}
/**
* 評論實體的構造函數,從數據庫獲取評論數據並實例化對象時使用
*
* @param flag 評論標識
* @param user 評論用戶的用戶
* @param content 評論內容
* @param createAt 評論時間
*/
public Comment(String flag, String content, String createAt, User user) {
this.flag = flag;
this.content = content;
this.createAt = createAt;
this.user = user;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getCreateAt() {
return createAt;
}
public void setCreateAt(String createAt) {
this.createAt = createAt;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
然後我們得創建10個不同的用戶來模擬不同用戶的評論效果:
/*
創建用戶數據
*/
for (int i = 0; i < 10; i++) {
User user = new User();
ContentValues values = new ContentValues();
values.put("flag", user.getFlag());
values.put("userName", user.getUserName());
values.put("nick", user.getNick());
values.put("location", user.getLocation());
serverDAO.add("user", values);
//對象標識存儲便於後續操作
userFlagList.add(user.getFlag());
users.add(user);
}
創建用戶後我們會把生成的用戶標識flag存入一個列表用來關聯一些其他的信息,而把整個User實體存入一個列表用來將其隨機綁定到Comment實體中,下面創建100個帖子/*
創建帖子數據
*/
for (int i = 0; i < 100; i++) {
Post comment = new Post();
ContentValues values = new ContentValues();
values.put("flag", comment.getFlag());
values.put("createAt", comment.getCreateAt());
serverDAO.add("post", values);
//對象標識存儲便於後續操作
postFlagList.add(comment.getFlag());
}
同樣地把生成的帖子flag標識存入列表,之後創建100條評論數據:/*
創建評論數據
*/
for (int i = 0; i < 100; i++) {
Comment comment = new Comment(Util.getRandomSimplified((int) (Math.random() * 100)), users.get((int) (Math.random() * 10)));
ContentValues values = new ContentValues();
values.put("flag", comment.getFlag());
values.put("userFlag", comment.getUser().getFlag());
values.put("content", comment.getContent());
values.put("createAt", comment.getCreateAt());
serverDAO.add("comment", values);
//對象標識存儲便於後續操作
commentFlagList.add(comment.getFlag());
}
在生成評論時,將上面生成的User隨機選取注入其構造函數完成User和Comment的綁定,最後就是隨機關聯各種數據了~~~~灰常簡單:/*
關聯帖子和評論的數據
*/
for (String post : postFlagList) {
/*
每條評論最多50條回覆
*/
for (int i = 0; i < (int) (Math.random() * 50); i++) {
ContentValues values = new ContentValues();
values.put("postFlag", post);
values.put("commentFlag", commentFlagList.get((int) (Math.random() * commentFlagList.size())));
serverDAO.add("post_comment", values);
}
}
/*
關聯用戶贊數據
*/
for (String user : userFlagList) {
/*
每個用戶最多贊10個帖子
*/
for (int i = 0; i < (int) (Math.random() * 10); i++) {
ContentValues values = new ContentValues();
values.put("userFlag", user);
//從帖子列表中隨機挑選
values.put("postFlag", postFlagList.get((int) (Math.random() * postFlagList.size())));
serverDAO.add("user_praise", values);
}
}
/*
關聯用戶踩數據
*/
for (String user : userFlagList) {
/*
每個用戶最多踩10個帖子
*/
for (int i = 0; i < (int) (Math.random() * 10); i++) {
ContentValues values = new ContentValues();
values.put("userFlag", user);
//從帖子列表中隨機挑選
values.put("postFlag", postFlagList.get((int) (Math.random() * postFlagList.size())));
serverDAO.add("user_unpraise", values);
}
}
/*
關聯用戶收藏數據
*/
for (String user : userFlagList) {
/*
每個用戶最多踩10個帖子
*/
for (int i = 0; i < (int) (Math.random() * 10); i++) {
ContentValues values = new ContentValues();
values.put("userFlag", user);
//從帖子列表中隨機挑選
values.put("postFlag", postFlagList.get((int) (Math.random() * postFlagList.size())));
serverDAO.add("user_collect", values);
}
}
所有的這些數據創建的業務邏輯都被封裝在SQLiteDataBO.java類中:
package com.aigestudio.neteasecommentlistdemo.bo;
import android.content.ContentValues;
import android.content.Context;
import com.aigestudio.neteasecommentlistdemo.beans.Comment;
import com.aigestudio.neteasecommentlistdemo.beans.Post;
import com.aigestudio.neteasecommentlistdemo.beans.User;
import com.aigestudio.neteasecommentlistdemo.dao.ServerDAO;
import com.aigestudio.neteasecommentlistdemo.utils.Util;
import java.util.ArrayList;
import java.util.List;
/**
* 創建數據的一些業務邏輯
*
* @author Aige
* @since 2014/11/14
*/
public class SQLiteDataBO {
private ServerDAO serverDAO;//服務器數據的訪問對象
private List<String> userFlagList, postFlagList, commentFlagList;//用戶唯一標識、帖子唯一標識、評論唯一標識列表數據
private List<User> users;//用戶列表:用於模擬多個用戶操作
public SQLiteDataBO(Context context) {
serverDAO = new ServerDAO(context);
}
/**
* 初始化服務器數據
*/
public void initServerData() {
//初始化Flag列表
userFlagList = new ArrayList<String>();
postFlagList = new ArrayList<String>();
commentFlagList = new ArrayList<String>();
users = new ArrayList<User>();
/*
創建用戶數據
*/
/*
創建帖子數據
*/
/*
創建評論數據
*/
/*
關聯帖子和評論的數據
*/
/*
關聯用戶贊數據
*/
/*
關聯用戶踩數據
*/
/*
關聯用戶收藏數據
*/
}
}
其中用到的常量類ClsCons.java定義了我們的一些模型數據:
package com.aigestudio.neteasecommentlistdemo.cons;
import com.aigestudio.neteasecommentlistdemo.R;
/**
* 存放一些應用的常量類
*
* @author Aige
* @since 2014/11/14
*/
public class ClsCons {
/*
用戶名數組:用來模擬不同的用戶操作(僅供參考)
*/
public static final String[] USER_NAME = {"斯蒂芬·愛哥", "羅羅亞·草泥馬", "宇智波·逗比", "艾斯比·莊臣", "八歧大蛇", "蒙奇·D·肏飛", "漩渦·溜溜球", "蠟筆·大大新", "虛妄之諾", "風間·八神"};//固定的用戶名}
/*
地理位置數組:用來模擬不同的地理位置(僅供參考)
*/
public static final String[] DEFAULT_LOCATION = {"網易火星網友", "網易天狼星網友", "網易太陽網友", "網易逗比網友", "網易尼麻痹網友"};
/*
用戶頭像的資源ID數組:用來模擬不同的用戶頭像
*/
public static final String[] DEFAULT_NICK_RESID = {String.valueOf(R.drawable.nick1), String.valueOf(R.drawable.nick2), String.valueOf(R.drawable.nick3)};
}
萬事俱備,當應用啓動時我們先不顯示任何東西,而先創建數據:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
// initWidget();
//初始化數據
sqLiteDataBO = new SQLiteDataBO(this);
sqLiteDataBO.initServerData();
}
數據只需創建一次即可,永久使用(注:如果你clean了項目記得要重新生成數據,因爲頭像資源ID在clean後有可能改變與插入數據庫的ID值不同),數據生成好後我們可以在SQLiteManager中查看下生成的數據:
好了~~測試數據的創建就講到這裏,勞資都煩了越寫越不曉得寫了什麼……囧!下一節我們將進入核心部分~~來實現這麼一個評論列表~~先來兩張高清無碼大圖養養眼: