目录导航

本节开始,进行代码的实战练习。我的这个App是管理保险客户信息的,数据采用Sqlite存储在本地手机上,第一次使用需要先登记自己的个人信息,这个功能非常简单,也无关紧要,我是拿这个练手,方便做后面复杂的功能。

  • 效果图

废话不多说,先看看个人信息的效果。

主页右上角一个[设置]按钮,点击按钮会弹出对话框,目前只有一个[我的信息]用于查看个人信息。

点击弹出框上的[我的信息],这时会进行个人信息详细列表,点击[返回]按钮会回到主页,而点击[修改]按钮会跳转到个人信息修改页面。

下图是从[我的信息]页面点击[编辑]按钮,跳转到的个人信息修改页面。

如果是第一次使用该程序,会被默认跳转到这个页面,并且没有返回按钮。

点击保存按钮会进行输入有效性验证,如果表单数据有错会在页面提示错误信息。

  • 数据库相关

如果不了解SQLite用法,建议先看看[学习系列(5)-SQLite数据库]。

个人信息建表代码如下:

      StringBuilder user_info = new StringBuilder();user_info.append("CREATE TABLE IF NOT EXISTS \"user_info\" (");user_info.append("\"id\" INTEGER NOT NULL,");user_info.append("\"name\"  varchar(99),");user_info.append("\"sex\"  INTEGER,");user_info.append("\"birthday\"  varchar(32),");user_info.append("\"mobile_phone\"  varchar(32),");user_info.append("\"email\"  varchar(99),");user_info.append("\"delete_flag\"  INTEGER,");user_info.append("\"user_type\"  INTEGER,");user_info.append("\"update_time\"  varchart(99),");user_info.append("\"id_number\"  varchart(99),");user_info.append("\"qq_number\"  varchart(99),");user_info.append("\"address\"  varchart(999),");user_info.append("PRIMARY KEY (\"id\")");user_info.append(");");db.execSQL(user_info.toString());

对应的JavaBean如下,为了方便,我的属性名全部与数据库字段一致,某些带下划线的也就没使用驼峰命名模式。

public class UserInfo extends BasePO{private static final long serialVersionUID = -8834697836148097731L;private Long id;private String name;private int sex;private String birthday;private String mobile_phone;private String id_number;private String qq_number;private String email;private String address;private int delete_flag;private int user_type;private String update_time;......}

而Manager类似于DAO层,嫌麻烦,没有分开,增删改查全部在BaseManagerImpl封装好的,有特殊SQL才在UserManager中实现。

public class UserManager extends BaseManagerImpl<UserInfo>{public UserManager(Context context) {super(context);super.TABLE = "user_info";super.clazz = UserInfo.class;}public UserInfoBO getCurrentUser(){String sql = "SELECT * FROM "+TABLE+" ORDER BY update_time DESC";List<UserInfo> userList = super.findBySql(sql, null, 1, 1);if(!ObjectUtils.isEmpty(userList)){return new UserInfoBO(userList.get(0));}return null;}
}
public class BaseManagerImpl<T> implements BaseManager<T> {protected String LOG_TAG = "BaseManagerImpl";protected String TABLE = null;protected DBHelper dbHelper = null;protected Class clazz = null;protected Context context = null;public BaseManagerImpl(Context context) {super();dbHelper = BeanFactory.getDBHelper(context);}@Overridepublic long save(T t) {SQLiteDatabase db = dbHelper.getWritableDatabase();return db.insert(TABLE, null, ClassUtils.getContentValues(t));}@Overridepublic int delete(Serializable id) {SQLiteDatabase db = dbHelper.getWritableDatabase();return db.delete(TABLE, "id=?", new String[]{id.toString()});}@Overridepublic int update(T t) {SQLiteDatabase db = dbHelper.getWritableDatabase();try {return db.update(TABLE, ClassUtils.getContentValues(t), "id=?", new String[]{ClassUtils.getFieldValue(t, "id").toString()});} catch (Exception e) {Log.e(LOG_TAG, "反射无法找到对象的ID值", e);return 0;}}@Overridepublic T get(Serializable id) {String getSQL = "SELECT * FROM "+TABLE+" WHERE id=?";List<T> list = this.findBySql(getSQL, new String[]{id.toString()}, 0, 0);if(!ObjectUtils.isEmpty(list)){return list.get(0);}return null;}@Overridepublic List<T> findBySql(String sql, String[] params,int pageSize,int pageNo) {if(StringUtils.isBlank(sql)){throw new RuntimeException("findBySql的sql不能为空!");}if(pageSize != 0 && pageNo != 0){//分页操作int begin = (pageSize - 1)*pageNo;sql = sql + " limit "+begin + ","+pageNo;}SQLiteDatabase db = dbHelper.getReadableDatabase();Cursor cursor = db.rawQuery(sql, params);List<T> tList = null;try {tList = ClassUtils.getObject(cursor, clazz);} catch (Exception e) {Log.e(LOG_TAG, "反射创建对象失败,clazz:"+clazz.getName(), e);}finally{cursor.close();}return tList;}@Overridepublic List<Map<String,String>> find(String sql,String[] params,int pageSize,int pageNo){if(StringUtils.isBlank(sql)){throw new RuntimeException("findBySql的sql不能为空!");}if(pageSize != 0 && pageNo != 0){//分页操作int begin = (pageSize - 1)*pageNo;sql = sql + " limit "+begin + ","+pageNo;}SQLiteDatabase db = dbHelper.getReadableDatabase();Cursor cursor = db.rawQuery(sql, params);List<Map<String,String>> list = new ArrayList<Map<String,String>>();try {String[] columnNames = cursor.getColumnNames();Map<String,String> map = null;while (cursor.moveToNext()) {map = new HashMap<String, String>();for (String column : columnNames) {map.put(column, cursor.getString(cursor.getColumnIndex(column)));list.add(map);}}} catch (Exception e) {Log.e(LOG_TAG, "反射创建对象失败,clazz:"+clazz.getName(), e);}finally{cursor.close();}return list;}@Overridepublic void updateBySql(String sql, Object[] params) {SQLiteDatabase db = dbHelper.getWritableDatabase();db.execSQL(sql, params);}@Overridepublic List<T> findAll() {String findAllSql = "SELECT * FROM "+TABLE;return this.findBySql(findAllSql, null, 0, 0);}}
  • 开发过程

数据库存储准备好之后,就是开发实体功能。因为采用的是Web开发,所以肯定会涉及HTML和javascript文件,我将这些文件全部放在assets目录下,如下图所示。

------>入口界面

应用程序的入口界面是home.html,前面提到过JqueryMobile的转场方式采用的是AJAX的形式转场,也就是说所有的跳转都是在home.html中操作,所以home.html一定要包含所有的css和js文件,而其它html则完全不需要再单独引用js等(引用了也无效)。

<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="../scripts/jquery.mobile-1.3.2/jquery.mobile-1.3.2.min.css" type="text/css"><script src="../scripts/jquery.mobile-1.3.2/jquery.js" type="text/javascript" ></script><script src="../scripts/jquery.mobile-1.3.2/jquery.mobile-1.3.2.min.js" type="text/javascript" ></script><script src="../scripts/bless.ui.core.js" type="text/javascript" ></script><script src="../scripts/home/bless.home.js" type="text/javascript" ></script><script src="../scripts/insurer/bless.insurer.js" type="text/javascript" ></script><script src="../scripts/insure_remind/bless.insurer_remind.js" type="text/javascript" ></script><script src="../scripts/self/bless.self.js" type="text/javascript" ></script><script src="../scripts/insurance_company/bless.company.js" type="text/javascript" ></script><title></title></head>
<body>
<div data-role="page" id="home_index_page">
......
</div>
</body>

每次用户进入home.html页面都会先检查用户是否注册个人信息,如果没有则会直接跳转到个人信息新增页面(self_edit.html)。

 $(document).on("pageinit", "#home_index_page", function() {initData_home();});//全部变量 用于存储当前用户var CURRENT_USER;function initData_home() {var currentUserJson = javascriptUser.getCurrentUser();if (!$.isNull(currentUserJson)) {CURRENT_USER = $.parseJSON(currentUserJson);$("#home_index_page").find("#header_h1").html(CURRENT_USER.name);} else {$.changePageInitData("self/self_edit.html","self_edit_page",function(page, data, params){javascriptUser.toUserNew();initData_self_edit(page);});}}

注意一点:javascriptUser不是我在javascript中定义的,而是Android的WebView控件自己提供的后台与前台数据交互的接口,这个是一个Java类,你可以在里面编写各种后台代码,然后在前台通过[java类.方法]的形式调用后台数据,非常方便。但是注意这个Java类的参数和返回值最好都用最简单的数据类型(我基本都是用String)。

public class JavascriptUser {protected Activity activity;protected AppContext app;protected UserManager userManager;protected InsurerInfoManager insurerInfoManager;protected InsurancesManager insurancesManager;protected ImageInfoManager imageInfoManager;protected InsuranceCompanyManager insuranceCompanyManager;public JavascriptUser(Activity activity) {super();this.activity = activity;app = (AppContext) activity.getApplication();userManager = (UserManager) BeanFactory.getDBManager(UserManager.class, activity);insurerInfoManager = (InsurerInfoManager) BeanFactory.getDBManager(InsurerInfoManager.class, activity);insurancesManager = (InsurancesManager) BeanFactory.getDBManager(InsurancesManager.class, activity);imageInfoManager = (ImageInfoManager) BeanFactory.getDBManager(ImageInfoManager.class, activity);insuranceCompanyManager = (InsuranceCompanyManager) BeanFactory.getDBManager(InsuranceCompanyManager.class, activity);}......}

在MainActivity中注册与前端关联。

/** javascript与Java对象映射,页面可使用javascript:ajax.xx()来调用AjaxManager的方法 */
webView.addJavascriptInterface(new JavascriptUser(MainActivity.this), "javascriptUser");

有了JavascriptUser后台接口就非常方便了,你就可以用在里面写代码逻辑来查询数据库数据,判断用户是否存在。

------>新增/修改页面

新增和修改因为文本元素一样,所以共用一个页面。根据我的开发习惯,代码逻辑先不写,而是先把界面UI做好,下面是self_edit.html页面代码。

<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"><title></title></head><body><div data-role="page" data-control-title="修改我的信息" id="self_edit_page"><div data-theme="b" data-role="header" data-position="fixed"><a id="a_back" data-role="button" data-rel="back" href="#" class="ui-btn-left" data-icon="back">返回</a><h3>修改我的信息</h3><a id="a_save" data-role="button" data-theme="b" href="#" data-icon="check" data-iconpos="left" class="ui-btn-right">保存</a></div><div data-role="content"><h5 style="color:red" id="error_msg"></h5><form method="post" action="#"><input type="hidden" name="id" id="id" ><input type="hidden" name="delete_flag" id="delete_flag" ><input type="hidden" name="user_type" id="user_type" ><div role="main" class="ui-content"><div class="ui-field-contain" data-controltype="textinput"><label for="name">姓名</label><input name="name" id="name" placeholder="您的姓名..." value="" type="text"></div><div id="sex" class="ui-field-contain" data-controltype="radiobuttons"><fieldset data-role="controlgroup" data-type="horizontal"><legend>性别</legend><input id="radio1" name="sex" value="1" type="radio" checked="checked"><label for="radio1">男</label><input id="radio2" name="sex" value="2" type="radio"><label for="radio2">女</label></fieldset></div><div class="ui-field-contain" data-controltype="dateinput"><label for="birthday">生日</label><input name="birthday" id="birthday" placeholder="您的出生日期..." value=""type="date"></div><div class="ui-field-contain" data-controltype="textinput"><label for="id_number">身份证号</label><input name="id_number" id="id_number" placeholder="您的身份证号码..." value=""type="text"></div><div class="ui-field-contain" data-controltype="textinput"><label for="mobile_phone">移动电话</label><input name="mobile_phone" id="mobile_phone" placeholder="您的手机号码..." value=""type="tel"></div><div class="ui-field-contain" data-controltype="textinput"><label for="email">电子邮箱</label><input name="email" id="email" placeholder="您的E-Mail地址..." value="" type="email"></div><div class="ui-field-contain" data-controltype="textinput"><label for="qq_number">QQ</label><input name="qq_number" id="qq_number" placeholder="您的QQ号码..." value=""type="text"></div><div class="ui-field-contain" data-controltype="textarea"><label for="address">联系地址</label><textarea name="address" id="address" placeholder="你的直接联系地址..."></textarea></div></div></form></div></div></body>
</html>

整个页面都是采用JqueryMobile样式,对于新手不知道怎么做页面布局的,我建议到JqueryMobile中文站了解,该站点首页有一个“jquery mobile UI builder”页面元素在线设计器,你可以通过拖动控件的形式设计页面元素,然后选择“inspect code”查看代码。

新增修改页面唯一的交互操作就是[保存]按钮,当用户点击[保存]按钮时,首先用js获取页面元素的值,然后以参数的形式调用与后台交互的JavascriptUser接口中的saveUserInfo方法执行输入有效性和保存操作。

 $(document).on("pageinit", "#self_edit_page", function() {$("#self_edit_page").find("#a_save").unbind("click").bind("click",function(){//保存var pageDom = $("#self_edit_page");var id = pageDom.find("#id").val();var name = pageDom.find("#name").val();//radio button获取值得方式var sex = pageDom.find('input[name="sex"]:checked').val();var birthday = pageDom.find("#birthday").val();var mobilePhone = pageDom.find("#mobile_phone").val();var email = pageDom.find("#email").val();var delete_flag = pageDom.find("#delete_flag").val();var user_type = pageDom.find("#user_type").val();var id_number = pageDom.find("#id_number").val();var qq_number = pageDom.find("#qq_number").val();var address = pageDom.find("#address").html();//调用JavascriptUser的saveUserInfo保存信息var result = javascriptUser.saveUserInfo(id, name, sex, birthday, mobilePhone, email, delete_flag, user_type, id_number, qq_number, address);if ($.startWith(result,"true,")) {//返回true则保存成功if($.isNull(id)){//第一次注册后返回到主页$.mobile.changePage ('../home.html');}else{//编辑操作返回 个人详细信息页面$.changePageInitData("self_detail.html","self_detail_page",function(page, data, params){initData_self_Detail(page, result.substring("true,".length) );});}} else {//返回其它信息则将错误信息显示在页面$("#self_edit_page").find("#error_msg").html(result);}});});
public String saveUserInfo(String id_,String name,String sex,String birthday,String mobile_phone,String email,String deleteFlag_,String userType_,String id_number,String qq_number,String address){try {/**先做输入有效性验证*/List<Validator> list = new ArrayList<Validator>();list.add( new Validator(ValidateUtils.ValidateEnum.EMPTY, "姓名", new Object[]{name}) );list.add( new Validator(ValidateUtils.ValidateEnum.CHARACTERS, "姓名", new Object[]{name.trim(),"<>'\""}) );if(StringUtils.isNotBlank(birthday)){list.add( new Validator(ValidateUtils.ValidateEnum.DATE, "生日", new Object[]{birthday,"yyyy-MM-dd"}) );}if(StringUtils.isNotBlank(mobile_phone)){list.add( new Validator(ValidateUtils.ValidateEnum.NUMBER, "移动电话", new Object[]{mobile_phone}) );}if(StringUtils.isNotBlank(email)){list.add( new Validator(ValidateUtils.ValidateEnum.EMAIL, "E-Mail", new Object[]{email}) );}if(StringUtils.isNotBlank(qq_number)){list.add( new Validator(ValidateUtils.ValidateEnum.NUMBER, "QQ", new Object[]{qq_number}) );}String check = ValidateUtils.validateList(list);if(StringUtils.isNotBlank(check)){return check;}/**再做入库操作*/boolean edit = StringUtils.isNotBlank(id_);Long id = StringUtils.isNotBlank(id_) ? Long.valueOf(id_) : UserInfo.uuid();Integer delete_flag = Enums.DeleteEnum.AVAILABLE.getKey();Integer user_type = StringUtils.isNotBlank(userType_) ? Integer.valueOf(userType_) : Enums.UserTypeEnum.USER.getKey();String update_time = DateUtil.date2string(new Date(), DateUtil.yyyy_MM_dd_HH_mm_ss);UserInfo user = new UserInfo(id, name, Integer.valueOf(sex), birthday, mobile_phone, id_number, qq_number, email, address, delete_flag, user_type, update_time);if(edit){userManager.update(user);Log.i(Enums.LogTagEnum.COMMON.getKey(), "修改个人信息:"+user.toString());}else{userManager.save(user);Log.i(Enums.LogTagEnum.COMMON.getKey(), "新增个人信息:"+user.toString());}// 初始化当前用户app.setUser(new UserInfoBO(user));return "true,"+user.getId();} catch (Exception e) {Log.e(Enums.LogTagEnum.COMMON.getKey(), "输入异常:"+e.getMessage(), e);return "输入异常:"+e.getMessage();}}

我写了一个非常非常简单的Validator验证组件,因为为了方便写了很多小组件,具体逻辑在后面章节[组件化]统一说明。

------>个人信息详细页面

在首页选择"我的信息"可以跳转到个人信息页面,首先先看self_detail.html页面代码。

<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"><title></title></head><body><!-- data-dom-cache="true" --><div data-role="page" data-control-title="我的信息" id="self_detail_page" ><div data-theme="b" data-role="header" data-position="fixed"><a data-role="button" data-rel="back" data-transition="slide" href="#" class="ui-btn-left" data-icon="back">返回</a><h3>我的信息</h3><a id="a_edit" data-role="button" data-theme="b" href="#" data-icon="edit" data-iconpos="left" class="ui-btn-right">修改</a></div><div data-role="content"><ul data-role='listview' id="ul_self_detail"></ul></div></div>
</body>
</html>

下面这段是转场到self_detail.html的逻辑代码,在转场到self_detail.html时会调用JavascriptUser接口获取个人信息数据。

 $(document).on("pageinit", "#home_setting_page", function() {$("#home_setting_page").find("#a_editSelf").unbind("click").bind("click", function() {$.changePageInitData("self/self_detail.html","self_detail_page",function(page, data, params){initData_self_Detail(page,CURRENT_USER.id);});});});function initData_self_Detail(pageDOM,id) {if (id != undefined) { //修改操作var userJson = javascriptUser.getUser(id);var currentUser = $.parseJSON(userJson);var DOM = pageDOM.find("#ul_self_detail");var html = "";html = html + "<li><p>姓名:"+currentUser.name+"</p></li>";html = html + "<li><p>性别:"+ (currentUser.sex==1 ? "男" : "女") +"</p></li>";html = html + "<li><p>生日:"+currentUser.birthday+"</p></li>";html = html + "<li><p>移动电话:"+currentUser.mobile_phone+"</p></li>";html = html + "<li><p>电子邮箱:"+currentUser.email+"</p></li>";html = html + "<li><p>身份证号:"+currentUser.id_number+"</p></li>";html = html + "<li><p>QQ号:"+currentUser.qq_number+"</p></li>";html = html + "<li><p>联系地址:"+currentUser.address+"</p></li>";DOM.html(html);//只有执行刷新操作才能出ListView样式DOM.listview("refresh");}else{pageDOM.find("#a_back").hide();}
}

------>详情页面到修改页面

最后一个功能是self_detail.html页面点击[修改]按钮进入self_edit.html页面并且将数据回填到页面元素中。

 $(document).on("pageinit", "#self_detail_page", function() {$("#self_detail_page").find("#a_edit").unbind("click").bind("click",function(){$.changePageInitData("self_edit.html","self_edit_page",function(page, data, params){initData_self_edit(page,CURRENT_USER.id);});});});function initData_self_edit(pageDOM,id) {if (id != undefined) { //修改操作var userJson = javascriptUser.getUser(id);var currentUser = $.parseJSON(userJson);pageDOM.find("#id").val(currentUser.id);pageDOM.find("#delete_flag").val(currentUser.delete_flag);pageDOM.find("#user_type").val(currentUser.user_type);pageDOM.find("#name").val(currentUser.name);$.radioChecked(pageDOM.find("input[name='sex']"),currentUser.sex);pageDOM.find("#birthday").val(currentUser.birthday);pageDOM.find("#mobile_phone").val(currentUser.mobilePhone);pageDOM.find("#email").val(currentUser.email);pageDOM.find("#id_number").val(currentUser.id_number);pageDOM.find("#qq_number").val(currentUser.qq_number);pageDOM.find("#address").html(currentUser.address);}else{pageDOM.find("#a_back").hide();}
}
  • 注意事项

1、关于WebView与javascript交互:不是只有javascript调用Java,也可以java调用javascript方法。具体可参考博客[Android中webview跟JAVASCRIPT中的交互]

2、关于JqueryMobile页面转场及传参与常规的Java Web有很大不同,可以参考我的博客[学习系列(4)-页面转场及参数传递]或者这个博客[Jquery Mobile中文章跳转传值如何实现]

3、javascript调用Java代码传递的参数和返回值最好都是基本数据类型,我基本用的String,以前试过用JavaBean或者Map之类的不行。

4、如果javascript想从后台获得一个对象或者对象集合,建议在后台转换成JSON字符串,然后在前端通过$.parseJSON转换成对象。一定要注意的是JavaBean中的属性值一定不能包含"\n"(换行符)之类的特殊字符,如果有的话,传给$.parseJSON就会报错,而且LogCat不会直接说这行错了,而是会显示一些莫名其妙的错误提示,很难跟踪。

关于"\n"换行符的解决办法非常简单,就是将字符串"\n"替换成"\\n":

String value ......
value.replaceAll("\r\n", "\\\\r\\\\n").replaceAll("\n", "\\\\n")

5、动态添加listview元素之后一定要刷新listview,否则JqueryMobile不会渲染成自己的效果。

$("#listview").append(......);
$("#listview").listview("refresh");

6、radio button动态设置值的话也要刷新checkboxradio,否则页面看起来没效果:

$.radioChecked(pageDOM.find("input[name='sex']"),currentUser.sex);$.radioChecked = function(fn, value) {for (var i = 0; i < fn.length; i++) {if ($(fn[i]).val() == value) {$(fn[i]).attr("checked",true).checkboxradio("refresh");} else {$(fn[i]).attr("checked",false).checkboxradio("refresh");}}
};

Android+Jquery Mobile学习系列(6)-个人信息设置相关推荐

  1. Android+Jquery Mobile学习系列(8)-保单/生日提醒功能

    其实这个App基本功能早已做完,并且交给老婆试用去了.但由于最近项目要保证稳定,所以持续加班,没有时间写最后一点内容,本节也就简单截图做个说明,不详细叙述实现方式.我会把代码上传到最后一章中,有兴趣的 ...

  2. Android+Jquery Mobile学习系列(3)-创建Android项目

    前两章分别对开发环境和Jquery Mobile基础知识进行了介绍,本章介绍创建一个Android项目,并使用WebView控件显示HTML数据. 首先创建一个Android Application项 ...

  3. Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  4. Android音视频学习系列(七) — 从0~1开发一款Android端播放器(支持多协议网络拉流本地文件)

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  5. Android音视频学习系列(八) — 基于Nginx搭建(rtmp、http)直播服务器

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  6. Android音视频学习系列(六) — 掌握视频基础知识并使用OpenGL ES 2.0渲染YUV数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  7. Android音视频学习系列(十) — 基于FFmpeg + OpenSL ES实现音频万能播放器

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  8. Android音视频学习系列(九) — Android端实现rtmp推流

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  9. JQuery Mobile 学习

    手机端JQuery: http://www.w3school.com.cn/jquerymobile/ 1.JQuery mobile库引用: <link rel="styleshee ...

最新文章

  1. linux 下 将 shell script 与 一个桌面图标联系在一起 (2)
  2. java web Junit
  3. 【大牛疯狂教学】cdhkafka打开的文件描述符临界阈值
  4. Nhibernate中的连接超时时事务回滚引发异常的处理方法
  5. 算法练习day16——190404(KMP算法)
  6. 【数据结构与算法】之深入解析“柱状图中最大的矩形”的求解思路与算法示例
  7. iOS定位服务与地图开发(6)---使用程序外地图之调用谷歌Web地图
  8. HDU2566 统计硬币【暴力】
  9. 解决复杂问题的思路 —— 从简单到复杂
  10. i9507 android8.1,三星I9507V官方原版固件rom系统刷机包下载
  11. IAR MCS-51 v7.51A 软件注册机下载
  12. 常用模板 UPD12/4
  13. 那些悄悄变厉害的人,都在偷偷对自己下狠手
  14. 主数据项目交付最佳实践
  15. SAP中复合角色的应用实例
  16. 语音转文字软件哪个好,这三款值得收藏
  17. 表单验证设计的用户体验基本原则
  18. Code Snippets Library
  19. 微信公众号:开发者工具下载、安装、使用
  20. 如何系统掌握产品功能改进方案?

热门文章

  1. 电脑显示器突然变了颜色
  2. Python-pandas:数据合并merge函数用法详解
  3. 杜拉拉升职记-选段-如何来定位一家公司以及所谓的中产阶级生活
  4. 弹性伸缩(Auto Scaling)
  5. PySide2安装教程
  6. YOLOv3中的非极大值抑制
  7. IDEA 自动下载依赖jar包
  8. windows 内存映射文件
  9. 远程服务器套娃,无限套娃!RemoteView云上浏览器:运行在浏览器里的远程浏览器...
  10. 【置顶】图灵近期出版和即将出版的新书