【助手APP】登陆

延续大学时做课程设计的思路,首先就是登陆


当然,在登陆之前还是有其他界面的,比如第一次登陆时会有欢迎导航界面,,启动应用时会显示logo界面。下一篇再介绍。


logo界面结束之后会进入登陆界面,当然,前提是没有自动登陆的用户(有自动登陆的用户,是跳过登陆界面,直接进入主界面的)。



登陆界面由两个输入框,两个可选按钮,两个按钮组成。界面虽然简单,但是费了很大的劲处理,简单罗列如下:

在输入框的左边显示一个头像,右边是一个一键清空的小叉叉;

输入学号时会自动匹配已登录过的用户,如果登录时勾选了记住密码时,选择了用户后会自动填充密码;

当用户名或密码为空时点击登录按钮,为空的输入框会抖动,并会有红色字体提示为空;

勾选自动登陆后,下次打开应用就会直接以该用户的账户自动登陆,而不会显示登陆界面。


首先是,带有头像和叉叉的输入框

代码如下:

public class ClearEditText extends EditText implements  
        OnFocusChangeListener, TextWatcher { 
	/**
	 * 删除按钮的引用
	 */
    private Drawable mClearDrawable; 
    /**
     * 控件是否有焦点
     */
    private boolean hasFoucs;
 
    public ClearEditText(Context context) { 
    	this(context, null); 
    } 
 
    public ClearEditText(Context context, AttributeSet attrs) { 
    	//这里构造方法也很重要,不加这个很多属性不能再XML里面定义
    	this(context, attrs, android.R.attr.editTextStyle); 
    } 
    
    public ClearEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }
    
    
    private void init() { 
    	//获取EditText的DrawableRight,假如没有设置我们就使用默认的图片
    	mClearDrawable = getCompoundDrawables()[2]; 
        if (mClearDrawable == null) { 
//        	throw new NullPointerException("You can add drawableRight attribute in XML");
        	mClearDrawable = getResources().getDrawable(R.drawable.delete_selector); 
        } 
        
        mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight()); 
        //默认设置隐藏图标
        setClearIconVisible(false); 
        //设置焦点改变的监听
        setOnFocusChangeListener(this); 
        //设置输入框里面内容发生改变的监听
        addTextChangedListener(this); 
    } 
 
 
    /**
     * 因为我们不能直接给EditText设置点击事件,所以我们用记住我们按下的位置来模拟点击事件
     * 当我们按下的位置 在  EditText的宽度 - 图标到控件右边的间距 - 图标的宽度  和
     * EditText的宽度 - 图标到控件右边的间距之间我们就算点击了图标,竖直方向就没有考虑
     */
    @Override 
	public boolean onTouchEvent(MotionEvent event) {
		if (event.getAction() == MotionEvent.ACTION_UP) {
			if (getCompoundDrawables()[2] != null) {

				boolean touchable = event.getX() > (getWidth() - getTotalPaddingRight())
						&& (event.getX() < ((getWidth() - getPaddingRight())));
				
				if (touchable) {
					this.setText("");
				}
			}
		}

		return super.onTouchEvent(event);
	}
 
    /**
     * 当ClearEditText焦点发生变化的时候,判断里面字符串长度设置清除图标的显示与隐藏
     */
    @Override 
    public void onFocusChange(View v, boolean hasFocus) { 
    	this.hasFoucs = hasFocus;
        if (hasFocus) { 
            setClearIconVisible(getText().length() > 0); 
        } else { 
            setClearIconVisible(false); 
        } 
    } 
 
 
    /**
     * 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去
     * @param visible
     */
    protected void setClearIconVisible(boolean visible) { 
        Drawable right = visible ? mClearDrawable : null; 
        setCompoundDrawables(getCompoundDrawables()[0], 
                getCompoundDrawables()[1], right, getCompoundDrawables()[3]); 
    } 
     
    
    /**
     * 当输入框里面内容发生变化的时候回调的方法
     */
    @Override 
    public void onTextChanged(CharSequence s, int start, int count, 
            int after) { 
            	if(hasFoucs){
            		setClearIconVisible(s.length() > 0);
            	}
    } 
 
    @Override 
    public void beforeTextChanged(CharSequence s, int start, int count, 
            int after) { 
         
    } 
 
    @Override 
    public void afterTextChanged(Editable s) { 
         
    } 
    
   
    /**
     * 设置晃动动画
     */
    public void setShakeAnimation(){
    	this.setAnimation(shakeAnimation(5));
    }
    
    
    /**
     * 晃动动画
     * @param counts 1秒钟晃动多少下
     * @return
     */
    public static Animation shakeAnimation(int counts){
    	Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0);
    	translateAnimation.setInterpolator(new CycleInterpolator(counts));
    	translateAnimation.setDuration(1000);
    	return translateAnimation;
    }
 
 
}

在定义输入框时:

        <cn.edu.wit.withelper.util.ClearEditText
            android:id="@+id/userid"
            android:layout_weight="1"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:background="@drawable/et_userid_border"
            android:drawableLeft="@drawable/users_selector"
            android:drawableRight="@drawable/delete_selector"
            android:hint="请输入您的学号"
            android:inputType="number"
            android:focusableInTouchMode="true"
            android:padding="3dp"
            android:singleLine="true"
            android:textSize="25sp" />

抖动的动画也在上面定义了,

下面就介绍下拉列表,

		
		ib_spinner = (ImageButton) findViewById(R.id.ib_spinner);//一个图片,覆盖到输入框的右端

		ib_spinner.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {

				// 弹出下拉列表
				ListView listView = new ListView(getApplicationContext());
				listView.setCacheColorHint(0x00000000);// 滑动时 不变色
				listView.setVerticalScrollBarEnabled(false);
				listView.setBackgroundColor(getResources().getColor(R.color.white));
				//设置透明度
				listView.getBackground().setAlpha(230);
				listView.setAdapter(new MyAdapter());

				pop = new PopupWindow(listView, etUserId.getWidth()+ib_spinner.getWidth(),
						LayoutParams.WRAP_CONTENT, true);

				// pop隐藏
				pop.setBackgroundDrawable(new ColorDrawable(0x00000000));
				pop.setOutsideTouchable(true);
				pop.setFocusable(true);
//				pop.setAnimationStyle(R.style.PopupAnimation);
				pop.showAsDropDown(etUserId, 0, -8);
				pop.update();
			}
		});



接下来就是,记住密码与自动登陆了

				if (1 == isAuto) {	//自动登录则存入数据库,且写到配置文件
					SharedPreferencesUtil.saveLoginUser(LoginActivity.this, user);
					userInfoServices.insertUserInfo(user);
				}else if (1 == isRemember) {	//记住密码就写入数据库
					userInfoServices.insertUserInfo(user);
				}

登陆成功后,就根据用户的选择,对数据进行处理


然后就是,如何实现登陆

	public static UserInfo login(UserInfo loginUser) {

		String URL = "http://www.******.com/API/Android/Login";
		final Map<String, String> params = new HashMap<String, String>();

		params.put("userID", loginUser.getUserId());
		params.put("password", loginUser.getPassword());

		JSONObject jsonResult = InterfaceUtil.getJSONObject(URL, params);

		if (jsonResult == null) {
			Log.i(TAG, "result = null");
			return loginUser;
		} else {
			Log.i(TAG, "result = " + jsonResult.toString());
			return getUserByJson(jsonResult, loginUser);
		}
	}
	public static JSONObject getJSONObject(
			String url, // 请求的URL
			Map<String, String> params // 请求的参数序列
	) {

		long timestamp = new Date().getTime();
		//时间戳
		params.put("timestamp", "" + timestamp);
		//sign
		params.put("sign", MD5Util.getMD5String(SALT + timestamp));
		
		String result = postRequestToServer(url, params);
		
		if (null == result) {
			Log.i(TAG, "result = null");
			return null;
			
		}else {
			Log.i(TAG, "result = " + result);
			JSONTokener jsonTokener = new JSONTokener(result);
			JSONObject json = null;
			try {
				
				json = (JSONObject) jsonTokener.nextValue();
				
			} catch (JSONException e) {
				e.printStackTrace();
			} finally {
				return json;
			}
		}//end of else

	}

这里就涉及到对网络的访问,对json的解析,以及对数据的封装

	public static String postRequestToServer(String url, // 请求的URL
			Map<String, String> params // 请求的参数序列
	) {
Log.i(TAG, "访问网络");
		HttpEntityEnclosingRequestBase httpRequest = new HttpPost(url);
		List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(params.size());

		for (Map.Entry<String, String> entry : params.entrySet()) {// 构建表单字段内容
			nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
		}
		String strResult = "";
		try {

			httpRequest.setEntity(new UrlEncodedFormEntity(nameValuePairs,"UTF-8"));

			HttpClient client = new DefaultHttpClient();
			
			HttpParams httpparams = client.getParams();

			HttpConnectionParams.setConnectionTimeout(httpparams,7000);
			HttpConnectionParams.setSoTimeout(httpparams,7000);
			
			// 执行请求
			HttpResponse httpResponse = client.execute(httpRequest);

			// 判断返回结果,200则说明正确返回
			if (httpResponse.getStatusLine().getStatusCode() == 200) {
				// 从返回的结果中获取内容
				strResult = EntityUtils.toString(httpResponse.getEntity(),"UTF-8");
			} else {
				strResult = null;
			}

		} catch (Exception e) {
			
			e.printStackTrace();
			
			Message msg = new Message();
			msg.what = Task.ERROR_NETEXCEPTION ;
			msg.obj = e;
			MainService.handler.sendMessage(msg);
			
			strResult = null;
		} finally {
			return strResult;
		}
	}

本应用中,所有的网络访问都是通过这个函数实现的


还有一个获取网络图片的函数,在本应用中没有使用,是准备用来获取用户头像的

	/**
	 * 通过url获取图片
	 * @param url
	 * @return
	 */
	public static Drawable getNetImage(URL url) {

		if (null == url)
			return null;

		try {
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();

			return Drawable.createFromStream(connection.getInputStream(),"image");
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;

	}

还有一个就是对SharedPreferences的操作;

	/**
	 * 保存登录用户信息
	 * @param context
	 * @param user
	 */
	public static void saveLoginUser(Context context, UserInfo user) {

		SharedPreferences sp = context.getSharedPreferences(LOGIN_USER, Context.MODE_PRIVATE);

		Editor editor = sp.edit();

		editor.putString(UserInfo.USERID, user.getUserId());
		editor.putString(UserInfo.SESSIONID, user.getSessionId());
		editor.putString(UserInfo.USERNAME, user.getUserName());

		editor.commit();

	}

	/**
	 * 获取登录用户信息
	 * @param context
	 * @return
	 */
	public static UserInfo getLoginUser(Context context) {

		SharedPreferences sp = context.getSharedPreferences(LOGIN_USER, Context.MODE_PRIVATE);
		String userId = sp.getString(UserInfo.USERID, "");
		String session = sp.getString(UserInfo.SESSIONID, "");
		String userName = sp.getString(UserInfo.USERNAME, "");

		if ("".equals(userId))
			return null;

		UserInfo user = new UserInfo();
		user.setUserId(userId);
		user.setSessionId(session);
		user.setUserName(userName);
		return user;
	}



登陆差不多就这么些东西,现在看来有很多东西都太low了,但还是原汁原味地展示出来。明天把项目的源码上传到github上,有时间就修改一下,希望有兴趣,或者有心赐教的大神多多指点。

ps:自动获取标签太烂了

本文出自 “年少的风” 博客,谢绝转载!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。