本项目是即时通讯的示例项目,使用了MVP模式,集成了环信SDK和Bmob后端云,展示了即时通讯基本的功能的实现,包括注册登录,退出登录,联系人列表,添加好友,删除好友,收发消息,消息提醒等功能。
- 环信SDK的集成与使用
- MVP模式的运用
- ORM数据库的集成与使用
- 模块化思想的运用
允许两人或多人使用网路即时的传递文字讯息、档案、语音与视频交流。
- 鼻祖 ICQ
- 国内主流 QQ 微信 陌陌 YY等
- 国外主流 Facebook Messenger WhatsApp Skype Instagram Line
-
放在jniLibs
-
也可以放在libs目录下,不过需要在模块下的配置文件中配置
android { sourceSets { main { jniLibs.srcDirs = ['libs'] } } }
运行出错:Didn't find class "com.hyphenate.chat.adapter.EMACallSession",原因是hyphenatechat_3.2.0.jar包内没有该类。
解决办法:导入Demo源码中EaseUI库里面的hyphenatechat_3.2.0.jar替换。
MVC应用于Ruby on Rails, Spring Framework, iOS开发和 ASP.NET等。
- Model: 获取数据的业务逻辑,网络操作,数据库操作
- View: UI
- Controller: 操作Model层获取数据传递给UI
Android中并没有清晰的MVC框架,如果把Activity当做Controller,根据我们实际开发经验,里面会有大量的UI操作,所以V和C就傻傻分不清了。
- Model:Java Bean, NetworkManager, DataBaseHelper
- View: xml res
- Controller: Activity Fragment
- ArrayList-ListView-Adapter(MVC)
MVP主要应用于ASP.NET等。MVP与MVC主要区别是View和Model不再耦合。
MVVM主要应用于WPF, Silverlight, Caliburn, nRoute等。
- Model: 获取数据的业务逻辑,网络操作,数据库操作
- View: UI
- ViewModel: 将View和Model绑定
分层分模块
Understanding MVC, MVP and MVVM Design Patterns
- adapter 存放适配器
- app 存放常量类,Application类以及一些app层级的全局类
- database 数据库相关类
- event EventBus使用的事件类
- factory 工厂类
- model 数据模型
- presenter MVP模型中的Presenter类
- ui 存放activity和fragment
- utils 工具类
- view MVP模型中的View类
- widget 自定义控件
- BaseActivity
- BaseFragment
- 如果没有登录,延时2s, 跳转到登录界面
- 如果已经登录,则跳转到主界面
- SplashView
- SplashPresenter
- 有两种情况都可以发起登录操作,一是点击登录按钮,而是点击虚拟键盘上的Action键。
- 点击新用户,跳转到注册界面。
注意配置EditText的imeOptions属性时,需要配合inputType才能起作用。
android:imeOptions="actionNext"//下一个
android:imeOptions="actionGo"//启动
android:imeOptions="actionDone"//完成
android:imeOptions="actionPrevious"//上一个
android:imeOptions="actionSearch"//搜索
android:imeOptions="actionSend"//发送
- LoginView
- LoginPresenter
public class EMCallBackAdapter implements EMCallBack{
@Override
public void onSuccess() {
}
@Override
public void onError(int i, String s) {
}
@Override
public void onProgress(int i, String s) {
}
}
举个栗子:高德地图 百度地图等
/**
* 是否有写磁盘权限
*/
private boolean hasWriteExternalStoragePermission() {
int result = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
return result == PermissionChecker.PERMISSION_GRANTED;
}
/**
* 申请权限
*/
private void applyPermission() {
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
ActivityCompat.requestPermissions(this, permissions, REQUEST_WRITE_EXTERNAL_STORAGE);
}
/**
* 申请权限回调
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_WRITE_EXTERNAL_STORAGE:
if (grantResults[0] == PermissionChecker.PERMISSION_GRANTED) {
login();
} else {
toast(getString(R.string.not_get_permission));
}
break;
}
}
- 用户名的长度必须是3-20位,首字母必须为英文字符,其他字符则除了英文外还可以是数字或者下划线。
- 密码必须是3-20位的数字。
- 密码和确认密码一致
private static final String USER_NAME_REGEX = "^[a-zA-Z]\\w{2,19}$";
private static final String PASSWORD_REGEX = "^[0-9]{3,20}$";
- \w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
- RegisterView
- RegisterPresenter
- 实际项目中,注册会将用户名和密码注册到APP的服务器,然后APP的服务器再通过REST API方式注册到环信服务器。
- 由于本项目没有APP服务器,会将用户数据注册到第三方云数据库Bmob,注册成功后,在客户端发送请求注册到环信服务器。
- 注册创建应用
- 下载SDK
- 导入SDK
- 初始化SDk
protected void hideSoftKeyboard() {
if (mInputMethodManager == null) {
mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
}
mInputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
private TextView.OnEditorActionListener mOnEditorActionListener = new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_GO) {
reigister();//注册
return true;
}
return false;
}
};
RadioGroup, TabHost, FragmentTabHost, 自定义
BottomBar AHBottomNavigation BottomNavigation
- ContactView
- ContactPresenter
private boolean itemInSameGroup(int i, ContactItem item) {
return i > 0 && (item.getFirstLetter() == mContactItems.get(i - 1).getFirstLetter());
}
mSwipeRefreshLayout.setColorSchemeResources(R.color.qq_blue, R.color.qq_red);
mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
private static final String[] 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"};
http://www.cnblogs.com/tianzhijiexian/p/4297664.html
private SlideBar.OnSlideBarChangeListener mOnSlideBarChangeListener = new SlideBar.OnSlideBarChangeListener() {
@Override
public void onSectionChange(int index, String section) {
mSection.setVisibility(View.VISIBLE);
mSection.setText(section);
scrollToSection(section);
}
@Override
public void onSlidingFinish() {
mSection.setVisibility(View.GONE);
}
};
/**
* RecyclerView滚动直到界面出现对应section的联系人
*
* @param section 首字符
*/
private void scrollToSection(String section) {
int sectionPosition = getSectionPosition(section);
if (sectionPosition != POSITION_NOT_FOUND) {
mRecyclerView.smoothScrollToPosition(sectionPosition);
}
}
/**
*
* @param section 首字符
* @return 在联系人列表中首字符是section的第一个联系人在联系人列表中的位置
*/
private int getSectionPosition(String section) {
List<ContactItem> contactItems = mContactListAdapter.getContactItems();
for (int i = 0; i < contactItems.size(); i++) {
if (section.equals(contactItems.get(i).getFirstLetterString())) {
return i;
}
}
return POSITION_NOT_FOUND;
}
- 保存联系人
- 查询联系人
- 删除联系人
anim文件夹:补间动画
animator文件夹:属性动画
drawable文件夹:帧动画
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/loading1" android:duration="100"/>
<item android:drawable="@mipmap/loading2" android:duration="100"/>
<item android:drawable="@mipmap/loading3" android:duration="100"/>
<item android:drawable="@mipmap/loading4" android:duration="100"/>
<item android:drawable="@mipmap/loading5" android:duration="100"/>
<item android:drawable="@mipmap/loading6" android:duration="100"/>
<item android:drawable="@mipmap/loading7" android:duration="100"/>
<item android:drawable="@mipmap/loading8" android:duration="100"/>
</animation-list>