上一篇文章我们分析了网易评论列表的界面效果,有了一个大概的了解后我们来为我们的评论列表做些数据,为了方便起见,就不通过服务器来做了,我们直接用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)};}
万事俱备,当应用启动时我们先不显示任何东西,而先创建数据:
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化控件// initWidget(); //初始化数据 sqLiteDataBO = new SQLiteDataBO(this); sqLiteDataBO.initServerData();}
数据只需创建一次即可,永久使用(注:如果你clean了项目记得要重新生成数据,因为头像资源ID在clean后有可能改变与插入数据库的ID值不同),数据生成好后我们可以在
SQLiteManager中查看下生成的数据:
好了~~测试数据的创建就讲到这里,劳资都烦了越写越不晓得写了什么……囧!下一节我们将进入核心部分~~来实现这么一个评论列表~~先来两张高清无码大图养养眼: