环信UI开源Demo情景分析四、登陆界面

本文深入解析登录界面配置及登录逻辑,包括竖屏显示、动画效果、用户名密码输入监听、对话框设置昵称功能、网络连接判断、登录流程与SDK调用细节,以及首次登录后的本地群聊和联系人加载。

接下来咱们来分析下登录界面,根据清单文件的配置不难发现接下来的Activity基本都是竖屏显示,以及与启动界面一样的动画:

        <!-- 登陆 -->
        <activity
            android:name=".activity.LoginActivity"
            android:screenOrientation="portrait"
            android:theme="@style/horizontal_slide" >
        </activity>

在下面的代码中有一处很神奇的逻辑,如果光从这个登录逻辑来看,不讨论以后的需要的话应该是不可能执行的:

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// 如果用户名密码都有,直接进入主页面
		if (DemoHXSDKHelper.getInstance().isLogined()) {
			autoLogin = true;
			startActivity(new Intent(LoginActivity.this, MainActivity.class));
			return;
		}
		setContentView(R.layout.activity_login);
		usernameEditText = (EditText) findViewById(R.id.username);
		passwordEditText = (EditText) findViewById(R.id.password);
		// 如果用户名改变,清空密码
		usernameEditText.addTextChangedListener(new TextWatcher() {
			@Override
			public void onTextChanged(CharSequence s, int start, int before, int count) {
				passwordEditText.setText(null);
			}
			@Override
			public void beforeTextChanged(CharSequence s, int start, int count, int after) {
			}
			@Override
			public void afterTextChanged(Editable s) {
			}
		});
		if (DemoApplication.getInstance().getUserName() != null) {
			usernameEditText.setText(DemoApplication.getInstance().getUserName());
		}
	}

没错,就是第五行,是不是有点熟悉,对,他就是在启动界面的逻辑,正因为没有登录才跳转到这个界面,结果在这个界面又判断一次,好吧,可能是以后有大用,先不管这个,咱们继续往下看,接下来是一个对用户名改变的监听,改变之后让密码清空。

最后面是对用户名的判断,咱们点进去看看到底是怎么存储用户名的:

	/**
	 * 获取当前登陆用户名
	 * @return
	 */
	public String getUserName() {
		return hxSDKHelper.getHXId();
	}
	/**
	 * 设置用户名
	 * @param user
	 */
	public void setUserName(String username) {
		hxSDKHelper.setHXId(username);
	}

这个是在APP类中的set,get方法通过Helper实例来保存的,至于到底保存在哪里了,后面碰到了再说。

	/**
	 * 登录
	 * 
	 * @param view
	 */
	public void login(View view) {
		if (!CommonUtils.isNetWorkConnected(this)) {
			Toast.makeText(this, R.string.network_isnot_available, Toast.LENGTH_SHORT).show();
			return;
		}
		currentUsername = usernameEditText.getText().toString().trim();
		currentPassword = passwordEditText.getText().toString().trim();
		
		if(TextUtils.isEmpty(currentUsername)){
			Toast.makeText(this, R.string.User_name_cannot_be_empty, Toast.LENGTH_SHORT).show();
			return;
		}
		if(TextUtils.isEmpty(currentPassword)){
			Toast.makeText(this, R.string.Password_cannot_be_empty, Toast.LENGTH_SHORT).show();
			return;
		}
		Intent intent = new Intent(LoginActivity.this, com.easemob.chatuidemo.activity.AlertDialog.class);
		intent.putExtra("editTextShow", true);
		intent.putExtra("titleIsCancel", true);
		intent.putExtra("msg", getResources().getString(R.string.please_set_the_current));
		intent.putExtra("edit_text", currentUsername);
		startActivityForResult(intent, REQUEST_CODE_SETNICK);
	}

在填完账号密码后就是可以登录了,首先是对网络状态的判断,


	/**
	 * 检测网络是否可用
	 * @param context
	 * @return
	 */
	public static boolean isNetWorkConnected(Context context) {
		if (context != null) {
			ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
			NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
			if (mNetworkInfo != null) {
				return mNetworkInfo.isAvailable();
			}
		}
		return false;
	}

没有网的情况下会直接提示返回。假设当前咱们条件俱全,下面是对输入的判断,接下来启动一个有返回的AlertDialog并附带一些内容。

很显然,这是一个自定义的对话框,功能是设置昵称。

        <!-- 自定义的alertdialog -->
        <activity
            android:name=".activity.AlertDialog"
            android:screenOrientation="portrait"
            android:theme="@style/MyDialogStyle" >
        </activity>

<style name="MyDialogStyle">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
        <item name="android:backgroundDimEnabled">true</item>
    </style>

设置了一下风格:

windowBackground:设置dialog的背景 默认的 windowBackground 定义为screen_background_dark,可以在frameworks\base\core\res\res\values\colors.xml 中找到,定义为:<drawable name="screen_background_dark">#ff000000</drawable>  现在设置为transparent   顺便说一下,如果自定义Theme中将android:windowBackground置为null,能小幅提升界面的绘制效率。

windowFrame:Dialog的windowFrame框为无

windowNoTitle:是否显示title

windowIsFloating:是否浮现在activity之上

windowIsTranslucent:是否半透明

windowContentOverlay:窗口内容的前景之上的可绘制资源。默认情况下,就是状态栏下的阴影
windowAnimationStyle:窗口动画风格 使用的是默认的

backgroundDimEnabled:背景是否模糊显示 显示

这个自定义的对画框说完了,接下来继续看这个对话框是怎能运作的。

		mTextView = (TextView) findViewById(R.id.title);
		mButton = (Button) findViewById(R.id.btn_cancel);
		imageView = (ImageView) findViewById(R.id.image);
		editText = (EditText) findViewById(R.id.edit);

其中有四个控件,分别显示标题、按钮、图案和一个编辑框。跟上面截图一样。

传过去的参数有:

intent.putExtra("editTextShow", true);
		intent.putExtra("titleIsCancel", true);
		intent.putExtra("msg", getResources().getString(R.string.please_set_the_current));
		intent.putExtra("edit_text", currentUsername);

这边获取到响应的内容:

//提示内容
		String msg = getIntent().getStringExtra("msg");
		//提示标题
		String title = getIntent().getStringExtra("title");
		position = getIntent().getIntExtra("position", -1);
		//是否显示取消标题
		boolean isCanceTitle=getIntent().getBooleanExtra("titleIsCancel", false);
		//是否显示取消按钮
		boolean isCanceShow = getIntent().getBooleanExtra("cancel", false);
		//是否显示文本编辑框
		isEditextShow = getIntent().getBooleanExtra("editTextShow",false);
		//转发复制的图片的path
		String path = getIntent().getStringExtra("forwardImage");
		//
		String edit_text = getIntent().getStringExtra("edit_text");
if(msg != null)
            ((TextView)findViewById(R.id.alert_message)).setText(msg);
        if(title != null)
            mTextView.setText(title);
        if(isCanceTitle){
            mTextView.setVisibility(View.GONE);
        }
        if(isCanceShow)
            mButton.setVisibility(View.VISIBLE);
        if(path != null){
             //优先拿大图,没有去取缩略图
            if(!new File(path).exists())
                path = DownloadImageTask.getThumbnailImagePath(path);
            imageView.setVisibility(View.VISIBLE);
            ((TextView)findViewById(R.id.alert_message)).setVisibility(View.GONE);
            if(ImageCache.getInstance().get(path) != null){
                imageView.setImageBitmap(ImageCache.getInstance().get(path));
            }else{
                Bitmap bm = ImageUtils.decodeScaleImage(path, 150, 150);
                imageView.setImageBitmap(bm);
                ImageCache.getInstance().put(path, bm);
            }
            
        }
        if(isEditextShow){
            editText.setVisibility(View.VISIBLE);
            editText.setText(edit_text);
        }
然后根据所需要的,显示出来。就是我们所看到的。

	public void ok(View view){
		setResult(RESULT_OK,new Intent().putExtra("position", position).
				putExtra("edittext", editText.getText().toString())
				/*.putExtra("voicePath", voicePath)*/);
		if(position != -1)
			ChatActivity.resendPos = position;
		finish();
	}
	public void cancel(View view){
		finish();
	}
	@Override
	public boolean onTouchEvent(MotionEvent event){
		finish();
		return true;
	}
其中还有两个按钮,以及非模态。并且设置返回内容为编辑框中的内容。

@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if (resultCode == RESULT_OK) {
			if (requestCode == REQUEST_CODE_SETNICK) {
				DemoApplication.currentUserNick = data.getStringExtra("edittext");
				progressShow = true;
				final ProgressDialog pd = new ProgressDialog(LoginActivity.this);
				pd.setCanceledOnTouchOutside(false);
				pd.setOnCancelListener(new OnCancelListener() {
					@Override
					public void onCancel(DialogInterface dialog) {
						progressShow = false;
					}
				});
				pd.setMessage(getString(R.string.Is_landing));
				pd.show();
				final long start = System.currentTimeMillis();
				// 调用sdk登陆方法登陆聊天服务器
				EMChatManager.getInstance().login(currentUsername, currentPassword, new EMCallBack() {
					@Override
					public void onSuccess() {
						if (!progressShow) {
							return;
						}
						// 登陆成功,保存用户名密码
						DemoApplication.getInstance().setUserName(currentUsername);
						DemoApplication.getInstance().setPassword(currentPassword);

						runOnUiThread(new Runnable() {
							public void run() {
								pd.setMessage(getString(R.string.list_is_for));
							}
						});
						try {
							// ** 第一次登录或者之前logout后再登录,加载所有本地群和回话
							// ** manually load all local groups and
							// conversations in case we are auto login
							EMGroupManager.getInstance().loadAllGroups();
							EMChatManager.getInstance().loadAllConversations();
							// 处理好友和群组
							processContactsAndGroups();
						} catch (Exception e) {
							e.printStackTrace();
							// 取好友或者群聊失败,不让进入主页面
							runOnUiThread(new Runnable() {
								public void run() {
									pd.dismiss();
									DemoApplication.getInstance().logout(null);
									Toast.makeText(getApplicationContext(), R.string.login_failure_failed, 1).show();
								}
							});
							return;
						}
						// 更新当前用户的nickname 此方法的作用是在ios离线推送时能够显示用户nick
						boolean updatenick = EMChatManager.getInstance().updateCurrentUserNick(DemoApplication.currentUserNick.trim());
						if (!updatenick) {
							Log.e("LoginActivity", "update current user nick fail");
						}
						if (!LoginActivity.this.isFinishing())
							pd.dismiss();
						// 进入主页面
						startActivity(new Intent(LoginActivity.this, MainActivity.class));
						finish();
					}

					@Override
					public void onProgress(int progress, String status) {
					}

					@Override
					public void onError(final int code, final String message) {
						if (!progressShow) {
							return;
						}
						runOnUiThread(new Runnable() {
							public void run() {
								pd.dismiss();
								Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message, Toast.LENGTH_SHORT).show();
							}
						});
					}
				});

			}
		}
	}
这里面就是登录的内容了,

先设置APP里的昵称,

	/**
	 * 当前用户nickname,为了苹果推送不是userid而是昵称
	 */
	public static String currentUserNick = "";
然后设置一个圆形进度条,点击外部不退出,点击进度条取消。后面是调用SDK进行登录,并且保存用户名和密码,获取相关本地组和联系人信息。如果获取信息异常,则退出登录。成功后更新昵称。并且取消进度条,进入主界面。

private void processContactsAndGroups() throws EaseMobException {
		// demo中简单的处理成每次登陆都去获取好友username,开发者自己根据情况而定
		List<String> usernames = EMContactManager.getInstance().getContactUserNames();
		EMLog.d("roster", "contacts size: " + usernames.size());
		Map<String, User> userlist = new HashMap<String, User>();
		for (String username : usernames) {
			User user = new User();
			user.setUsername(username);
			setUserHearder(username, user);
			userlist.put(username, user);
		}
		// 添加user"申请与通知"
		User newFriends = new User();
		newFriends.setUsername(Constant.NEW_FRIENDS_USERNAME);
		String strChat = getResources().getString(R.string.Application_and_notify);
		newFriends.setNick(strChat);

		userlist.put(Constant.NEW_FRIENDS_USERNAME, newFriends);
		// 添加"群聊"
		User groupUser = new User();
		String strGroup = getResources().getString(R.string.group_chat);
		groupUser.setUsername(Constant.GROUP_USERNAME);
		groupUser.setNick(strGroup);
		groupUser.setHeader("");
		userlist.put(Constant.GROUP_USERNAME, groupUser);

		// 存入内存
		DemoApplication.getInstance().setContactList(userlist);
		System.out.println("----------------" + userlist.values().toString());
		// 存入db
		UserDao dao = new UserDao(LoginActivity.this);
		List<User> users = new ArrayList<User>(userlist.values());
		dao.saveContactList(users);

		// 获取黑名单列表
		List<String> blackList = EMContactManager.getInstance().getBlackListUsernamesFromServer();
		// 保存黑名单
		EMContactManager.getInstance().saveBlackList(blackList);

		// 获取群聊列表(群聊里只有groupid和groupname等简单信息,不包含members),sdk会把群组存入到内存和db中
		EMGroupManager.getInstance().getGroupsFromServer();
	}
后面是获取联系人信息,并且对每个联系人这是header,方便查找。并保存到本地数据库当中。

	/**
	 * 设置hearder属性,方便通讯中对联系人按header分类显示,以及通过右侧ABCD...字母栏快速定位联系人
	 * @param username
	 * @param user
	 */
	protected void setUserHearder(String username, User user) {
		String headerName = null;
		if (!TextUtils.isEmpty(user.getNick())) {
			headerName = user.getNick();
		} else {
			headerName = user.getUsername();
		}
		if (username.equals(Constant.NEW_FRIENDS_USERNAME)) {
			user.setHeader("");
		} else if (Character.isDigit(headerName.charAt(0))) {
			user.setHeader("#");
		} else {
			user.setHeader(HanziToPinyin.getInstance().get(headerName.substring(0, 1)).get(0).target.substring(0, 1).toUpperCase());
			char header = user.getHeader().toLowerCase().charAt(0);
			if (header < 'a' || header > 'z') {
				user.setHeader("#");
			}
		}
	}
没有昵称用账号名,并且设置字幕前缀。
	/**
	 * 注册
	 * @param view
	 */
	public void register(View view) {
		startActivityForResult(new Intent(this, RegisterActivity.class), 0);
	}
	@Override
	protected void onResume() {
		super.onResume();
		if (autoLogin) {
			return;
		}
	}
对于11行,我很无解,不过不要在意这些细节。

登录界面就到这里了。按照正常流程,先注册一个账号,再继续往下。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值