先定一个小目标比如写一个QQ.docx
《先定一个小目标比如写一个QQ.docx》由会员分享,可在线阅读,更多相关《先定一个小目标比如写一个QQ.docx(25页珍藏版)》请在冰豆网上搜索。
先定一个小目标比如写一个QQ
先定一个小目标,比如写一个QQ
GitHub地址
QQDemo
项目简介
本项目是即时通讯的示例项目,使用了MVP模式,集成了环信SDK和Bmob后端云,展示了即时通讯基本的功能的实现,包括注册登录,退出登录,联系人列表,添加好友,删除好友,收发消息,消息提醒等功能。
使用的开源项目
BottomBar
EventBus
greenDAO
butterknife
学习目标
环信SDK的集成与使用
MVP模式的运用
ORM数据库的集成与使用
模块化思想的运用
即时通讯IM(InstantMessaging)
允许两人或多人使用网路即时的传递文字讯息、档案、语音与视频交流。
相关产品
鼻祖ICQ
国内主流QQ微信陌陌YY等
国外主流FacebookMessengerWhatsAppSkypeInstagramLine
第三方服务平台
环信
融云
网易云信
极光IM
腾讯云通信IM
爱萌
阿里悟空(2016年10月31号正式下线)
阿里百川云旺
环信
官网
即时通信云3.x文档easemob_business.png
环信集成
注册并创建应用
下载SDK
SDK的导入
SDK的初始化
.so文件夹
放在jniLibs
也可以放在libs目录下,不过需要在模块下的配置文件中配置
android{
sourceSets{
main{
jniLibs.srcDirs=['libs']
}
}
}
巨坑
运行出错:
Didn'tfindclass"com.hyphenate.chat.adapter.EMACallSession",原因是hyphenatechat_3.2.0.jar包内没有该类。
解决办法:
导入Demo源码中EaseUI库里面的hyphenatechat_3.2.0.jar替换。
软件架构
MVC
MVC应用于RubyonRails,SpringFramework,iOS开发和ASP.NET等。
Model:
获取数据的业务逻辑,网络操作,数据库操作
View:
UI
Controller:
操作Model层获取数据传递给UImvc.png
服务器端的MVCmvcpattern.png
Android中MVC
Android中并没有清晰的MVC框架,如果把Activity当做Controller,根据我们实际开发经验,里面会有大量的UI操作,所以V和C就傻傻分不清了。
Model:
JavaBean,NetworkManager,DataBaseHelper
View:
xmlres
Controller:
ActivityFragment
ArrayList-ListView-Adapter(MVC)
MVP
MVP主要应用于ASP.NET等。
MVP与MVC主要区别是View和Model不再耦合。
Model:
获取数据的业务逻辑,网络操作,数据库操作
View:
UI
Presenter:
操作Model层获取数据传递给UImvp.png
MVVM
MVVM主要应用于WPF,Silverlight,Caliburn,nRoute等。
Model:
获取数据的业务逻辑,网络操作,数据库操作
View:
UI
ViewModel:
将View和Model绑定mvvm.png
Android中MVVM
DataBindingLibrary
中文翻译
软件架构的核心思想
分层分模块architecture.png
参考
androidarchitecture
MVC,MVP和MVVM模式如何选择
UnderstandingMVC,MVPandMVVMDesignPatterns
教你认清MVC,MVP和MVVM
AndroidDataBinding
CleanArchitecture
准备好了么?
开车啦!
!
!
包的创建
adapter存放适配器
app存放常量类,Application类以及一些app层级的全局类
database数据库相关类
eventEventBus使用的事件类
factory工厂类
model数据模型
presenterMVP模型中的Presenter类
ui存放activity和fragment
utils工具类
viewMVP模型中的View类
widget自定义控件
基类的创建
BaseActivity
BaseFragment
Git初始化
Splash界面splash.png
功能需求
如果没有登录,延时2s,跳转到登录界面
如果已经登录,则跳转到主界面
MVP实现
SplashView
SplashPresenter
登录界面login.jpg
功能需求
有两种情况都可以发起登录操作,一是点击登录按钮,而是点击虚拟键盘上的Action键。
点击新用户,跳转到注册界面。
IMEOptions
注意配置EditText的imeOptions属性时,需要配合inputType才能起作用。
android:
imeOptions="actionNext"//下一个
android:
imeOptions="actionGo"//启动
android:
imeOptions="actionDone"//完成
android:
imeOptions="actionPrevious"//上一个
android:
imeOptions="actionSearch"//搜索
android:
imeOptions="actionSend"//发送
MVP实现
LoginView
LoginPresenter
EMCallBack的适配器
publicclassEMCallBackAdapterimplementsEMCallBack{
@Override
publicvoidonSuccess(){
}
@Override
publicvoidonError(inti,Strings){
}
@Override
publicvoidonProgress(inti,Strings){
}
}
Android6.0动态权限管理
介绍
举个栗子:
高德地图XX地图等
/**
*是否有写磁盘权限
*/
privatebooleanhasWriteExternalStoragePermission(){
intresult=ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE);
returnresult==PermissionChecker.PERMISSION_GRANTED;
}
/**
*申请权限
*/
privatevoidapplyPermission(){
String[]permissions={Manifest.permission.WRITE_EXTERNAL_STORAGE};
ActivityCompat.requestPermissions(this,permissions,REQUEST_WRITE_EXTERNAL_STORAGE);
}
/**
*申请权限回调
*/
@Override
publicvoidonRequestPermissionsResult(intrequestCode,@NonNullString[]permissions,@NonNullint[]grantResults){
switch(requestCode){
caseREQUEST_WRITE_EXTERNAL_STORAGE:
if(grantResults[0]==PermissionChecker.PERMISSION_GRANTED){
login();
}else{
toast(getString(R.string.not_get_permission));
}
break;
}
}
注册界面register.jpg
功能需求
用户名的长度必须是3-20位,首字母必须为英文字符,其他字符则除了英文外还可以是数字或者下划线。
密码必须是3-20位的数字。
密码和确认密码一致
正则表达式
正则表达式-元字符
privatestaticfinalStringUSER_NAME_REGEX="^[a-zA-Z]\\w{2,19}$";
privatestaticfinalStringPASSWORD_REGEX="^[0-9]{3,20}$";
\w匹配包括下划线的任何单词字符。
等价于'[A-Za-z0-9_]'。
MVP实现
RegisterView
RegisterPresenter
注册流程
实际项目中,注册会将用户名和密码注册到APP的服务器,然后APP的服务器再通过RESTAPI方式注册到环信服务器。
由于本项目没有APP服务器,会将用户数据注册到第三方云数据库Bmob,注册成功后,在客户端发送请求注册到环信服务器。
register_logic.png
云数据库
LeanCloud
Bmob
Parse(2017年1月28日关闭)
Bmob集成
开发文档
注册创建应用
下载SDK
导入SDK
初始化SDk
隐藏软键盘
protectedvoidhideSoftKeyboard(){
if(mInputMethodManager==null){
mInputMethodManager=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
}
mInputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
}
软键盘Action处理
privateTextView.OnEditorActionListenermOnEditorActionListener=newTextView.OnEditorActionListener(){
@Override
publicbooleanonEditorAction(TextViewv,intactionId,KeyEventevent){
if(actionId==EditorInfo.IME_ACTION_GO){
reigister();//注册
returntrue;
}
returnfalse;
}
};
用户名已注册的处理
Bmob错误码
主界面main.jpg
底部导航条
RadioGroup,TabHost,FragmentTabHost,自定义
第三方底部条
BottomBar
AHBottomNavigation
BottomNavigation
Fragment的切换
动态界面logout.jpg
联系人界面contact1.jpgcontact2.jpg
MVP实现
ContactView
ContactPresenter
RecyclerView的使用
CreatingListsandCards
联系人是否在同一个组
privatebooleanitemInSameGroup(inti,ContactItemitem){
returni>0&&(item.getFirstLetter()==mContactItems.get(i-1).getFirstLetter());
}
CardView的使用
SwipeRefreshLayout的使用
mSwipeRefreshLayout.setColorSchemeResources(R.color.qq_blue,R.color.qq_red);
mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
自定义控件SlideBar
分类字符数组(供拷贝)
privatestaticfinalString[]SECTIONS={"A","B","C","D","E","F","G","H","I","J","K","L"
"M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
绘制居中文本
在ContactFragment里面监听SlideBar的事件
privateSlideBar.OnSlideBarChangeListenermOnSlideBarChangeListener=newSlideBar.OnSlideBarChangeListener(){
@Override
publicvoidonSectionChange(intindex,Stringsection){
mSection.setVisibility(View.VISIBLE);
mSection.setText(section);
scrollToSection(section);
}
@Override
publicvoidonSlidingFinish(){
mSection.setVisibility(View.GONE);
}
};
/**
*RecyclerView滚动直到界面出现对应section的联系人
*
*@paramsection首字符
*/
privatevoidscrollToSection(Stringsection){
intsectionPosition=getSectionPosition(section);
if(sectionPosition!
=POSITION_NOT_FOUND){
mRecyclerView.smoothScrollToPosition(sectionPosition);
}
}
/**
*
*@paramsection首字符
*@return在联系人列表中首字符是section的第一个联系人在联系人列表中的位置
*/
privateintgetSectionPosition(Stringsection){
ListcontactItems=mContactListAdapter.getContactItems();
for(inti=0;iif(section.equals(contactItems.get(i).getFirstLetterString())){
returni;
}
}
returnPOSITION_NOT_FOUND;
}
联系人点击事件
privateContactListAdapter.OnItemClickListenermOnItemClickListener=newContactListAdapter.OnItemClickListener(){
/**
*单击跳转到聊天界面
*@paramname点击item的联系人名字
*/
@Override
publicvoidonItemClick(Stringname){
startActivity(ChatActivity.class,Constant.Extra.USER_NAME,name);
}
/**
*长按删除好友
*@paramname点击item的联系人名字
*/
@Override
publicvoidonItemLongClick(finalStringname){
AlertDialog.Builderbuilder=newAlertDialog.Builder(getContext());
Stringmessage=String.format(getString(R.string.delete_friend_message),name);
builder.setTitle(getString(R.string.delete_friend))
.setMessage(message)
.setNegativeButton(getString(R.string.cancel),newDialogInterface.OnClickListener(){
@Override
publicvoidonClick(DialogInterfacedialog,intwhich){
dialog.dismiss();
}
})
.setPositiveButton(getString(R.string.confirm),newDialogInterface.OnClickListener(){
@Override
publicvoidonClick(DialogInterfacedialog,intwhich){
dialog.dismiss();
showProgress(getString(R.string.deleting_friend));
mContactPresenter.deleteFriend(name);
}
});
builder.show();
}
};
添加好友界面add_friend.jpg
搜索用户
查询数据
@Override
publicvoidsearchFriend(finalStringkeyword){
mAddFriendView.onStartSearch();
//注:
模糊查询只对付费用户开放,付费后可直接使用。
BmobQueryquery=newBmobQuery();
query.addWhereContains("username",keyword).addWhereNotEqualTo("username",EMClient.getInstance().getCurrentUser());
query.findObjects(newFindListener(){
@Override
publicvoiddone(Listlist,BmobExceptione){
processResult(list,e);
}
});
}
greenDAO
greenDAO是AndroidSQLite数据库ORM框架的一种。
ORM即对象关系映射,object/relationalmapping,将Java对象映射成数据库的表。
其他ORM框架
DBFlow
Ormlite
Sugar
ActiveAndroid
Sprinkles
Ollie
参考
Github
官网
AppBrain
使用文档
中文使用文档
创建实体类
@Entity
publicclassContact{
@Id
publicLongid;
publicStringuserName;
}
初始化
publicvoidinit(Contextcontext){
DaoMaster.DevOpenHelperdevOpenHelper=newDaoMaster.DevOpenHelper(context,Constant.Database.DATABASE_NAME,null);
SQLiteDatabasewritableDatabase=devOpenHelper.getWritableDatabase();
DaoMasterdaoMaster=newDaoMaster(writableDatabase);
mDaoSession=daoMaster.newSession();
}
保存联系人
publicvoidsaveContact(StringuserName){
Contactcontact=newContact();
contact.setUsername(userName);
mDaoSession.getContactDao().save(contact);
}
查询联系人
publicListqueryAllContacts(){
Listlist=mDaoSession.getContactDao().queryBuilder().list();
ArrayListcontacts=newArrayList();
for(inti=0;iStringcontact=list.get(i).getUsername();
contacts.add(contact);
}
returncontacts;
}
删除联系人
publicvoiddeleteAllContacts(){
ContactDaocontactDao=mDaoSession.getContactDao();
contactDao.deleteAll();
}
发送好友请求
AddFriendItemView里面处理点击事件
@OnClick(R.id.add)
publicvoidonClick(){
StringfriendName=mUserName.getText().toString().trim();
StringaddFriendReason=getContext().getString(R.string.add_friend_reason);
AddFriendEventevent=newAddFriendEvent(friendName,addFriendReason);
EventBus.getDefault().post(event);
}
AddFriendPresenterImpl实现发送好友请求
@Subscribe(threadMode=ThreadMode.BACKGROUND)
publicvoidaddFriend(AddFriendEventevent){
try{
EMClient.get