最近天气真他娘的热,炸鸡啤酒,我觉得如果不演那么什么我从来都不看的韩剧,绝对没有人喜欢这种吃法。好了,废话不多说,天这么热,我只能晚上腾出时间来写这个东东,顺便引用吉日嘎拉的博客上面的一句话"每天进步一点点"。

OK,我们这次是要把这个界面翻成Android版,大家看过我的博客都知道我一直都是拿这几个界面再弄,

什么java实战篇也是用这个界面,唉,没办法,我只有这个界面。

wKiom1OGAtSztHyBAAFTqAjWxa0631.jpg

先上一张Android版的图,吊胃口,下面的这张是模拟器上的图,怎么样,还像个app的样子吧。

wKiom1OGAprjMjI-AAKcvMfEp6E760.jpg

首先我们来看一下.net webService端,在webService中我新增了一个方法

 [WebMethod]        public CommonResponse UserInfoModify(UserInfoEntity userInfoEntity)        {            return UserInfoBiz.GetInstance().ModifyUserInfo(userInfoEntity);        }

接下里看一下Biz层

public CommonResponse ModifyUserInfo(UserInfoEntity userInfoEntity)        {            try            {                int suc = UserInfoMngDAL.GetInstance().ModifyUserInfo(userInfoEntity);                if (suc > 0)                {                    return new CommonResponse() { IsSuccess = true };                }                return new CommonResponse() { IsSuccess = false, ErrorMessage = SaveFailed };            }            catch (Exception ex)            {                return new CommonResponse() { IsSuccess = false, ErrorMessage = ex.Message };            }        }

最后看一下DAL层

 public int ModifyUserInfo(UserInfoEntity userInfoEntity)        {            using (BonusEntities bonusEntities = new BonusEntities())            {                if (bonusEntities.UerInfo.Any(u => u.UseNo == userInfoEntity.UserNo)) //has existed                {                    UerInfo uerInfoModify = bonusEntities.UerInfo.SingleOrDefault(u => u.UseNo == userInfoEntity.UserNo);                    uerInfoModify.Name = userInfoEntity.UserName;                    uerInfoModify.Sex = userInfoEntity.UserSex == "男" ? "1" : "2";                    uerInfoModify.Age = userInfoEntity.UserAge;                    uerInfoModify.Temper = userInfoEntity.Temper;                    uerInfoModify.BirthDay = DateTime.Parse(string.Concat(userInfoEntity.BirthDay, " 00:00:01"));                    if (!string.IsNullOrWhiteSpace(userInfoEntity.UserPhoto))                    {                        uerInfoModify.Photo = Convert.FromBase64String(userInfoEntity.UserPhoto);                    }                }                else                {                    UerInfo uerInfo = new UerInfo();                    uerInfo.UseNo = userInfoEntity.UserNo;                    uerInfo.Name = userInfoEntity.UserName;                    uerInfo.Sex = userInfoEntity.UserSex == "男" ? "1" : "2";                    uerInfo.Age = userInfoEntity.UserAge;                    uerInfo.Temper = userInfoEntity.Temper;                    uerInfo.BirthDay = DateTime.Parse(string.Concat(userInfoEntity.BirthDay, " 00:00:01"));                    if (!string.IsNullOrWhiteSpace(userInfoEntity.UserPhoto))                    {                        uerInfo.Photo = Convert.FromBase64String(userInfoEntity.UserPhoto);                    }                    bonusEntities.UerInfo.Add(uerInfo);                }                return bonusEntities.SaveChanges();            }        }

非常的简单,如果存在就是修改,否则是新建。在这里需要注意的是下面这句

uerInfo.Photo = Convert.FromBase64String()

这个解释的话先看一下我们的EF实体

wKiom1OGBQryGo16AAGqOo0Y6s0141.jpg

我们的Photo是byte[]类型,因为Ksoap是无法传递byte[]的,所以在android客户端,我们要先将byte[]编码成string,然后在.net wenservice端再反编码。好了,这里就是webservice端。

接着就到了我们的android客户端了,他才是我们这篇文章的重头戏。先上一张真机上的图,开启笔记本wifi,手机连接wifi,更换代码中的IP,OK,运行

wKiom1OGCJfgeY8XAAGOseUdfQI943.jpg

就是这张图,大家可能会问,那右边的那个东西是什么,是鸟的翅膀吗,不是,是一个人

wKioL1OGCLbi6bUFAAGA2L_pTj0778.jpg

这边的功能是如果用户勾选checkBox,则保存的时候会将图片一并提交webservice去做保存。

图也看了,那么先看一下页面布局

布局的话主要有以下几点,第一,这个布局总体采用TableLayout,因为我们的界面的宽度的缘故,所以我在table的外层加了个HorizontalScrollView,用来左右滚动。第二,这个界面的布局是采用左右各占一列, 在列中又嵌套了Table。做过Silverlight的人都知道,Grid布局是很好用的,这个和Silverlight的Grid布局有点像。布局的话其实都很简单,也没啥看点。

在界面中,大家都看到了有个下拉列表样子的东西,那是什么,是ComboBox?我靠,你以为这是在做Silverlight呢。这个是Android中的Spinner控件。我们来看一下他是如何加载下拉数据和响应事件的。

首先我们在string.xml文件中新增了一个string-array资源,用来加载到下拉列表。

wKioL1OGCzfS-RN5AAKfu2-DjPI708.jpg

在代码中,我们会构造一个Spinner加载数据的一个适配器,如下

private void InitData() {		// ArrayAdapter
 adapter; // adapter = new ArrayAdapter
(this, // android.R.layout.simple_spinner_item, sexArray); final ArrayAdapter
 adapter = ArrayAdapter.createFromResource(this, R.array.sexArray, android.R.layout.simple_spinner_item); adapter .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spnUserSex.setAdapter(adapter); // spnUserSex.setOnItemSelectedListener(new OnItemSelectedListener() { // public void onItemSelected(AdapterView
 arg0, View arg1, // int arg2, long arg3) { // String selectedItem = adapter.getItem(arg2).toString(); // } // // public void onNothingSelected(AdapterView
 arg0) { // } // }); }

看到了吧,那句R.Array.SexArray就是从资源文件取出性别集合的。

setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

这句是表示我们的下拉列表展示简单的项(只有文子字),如果你想让你的下拉列表更生动,你可以去加载模版,比如在男选项前面放一个男人头像,女选项前面放一个女头像。这个其实和Silverlight的ComboBox的模版类似。OK,最后给Spinner设置适配器。我注释的上面部分是当不从资源文件加载数据的时候的代码,下面部分是下拉事件响应的代码。我们看一下下拉效果

wKioL1OGDUGQvS8iAAEfU4r7-Bk634.jpg

此时,就可以在界面选择你想要的结果。

wKioL1OGDe7w5fvnAADlOSDNTcY537.jpg

好了,那我们接下来看这个出生日期,为什么要先看出生日期呢,因为年龄是根据出生日期算出来的,难道您刚才没注意那个年龄的文本框是设置为不能编辑的吗(android:editable="false")。

出生日期后面的那个选择按钮完成的功能是弹出日期选择界面。看代码

this.btnChoose.setOnClickListener(new OnClickListener() {			public void onClick(View view) {				Calendar calendar = Calendar.getInstance();				DatePickerDialog dialog = new DatePickerDialog(owner,						new DatePickerDialog.OnDateSetListener() {							public void onDateSet(DatePicker dp, int year,									int month, int dayOfMonth) {								txtBirthDay.setText(year + "-" + month + "-"										+ dayOfMonth);								SimpleDateFormat df = new SimpleDateFormat();								df.applyPattern("yyyy-MM-dd hh:mm:ss");								try {									Date dt = df.parse(year + "-" + month + "-"											+ dayOfMonth + " 00:00:01");									int age = new Date().getYear()											- dt.getYear();									txtAge.setText(String.valueOf(age));								} catch (ParseException e1) {									// TODO Auto-generated catch block									e1.printStackTrace();								}							}						}, calendar.get(Calendar.YEAR), calendar								.get(Calendar.MONTH), calendar								.get(Calendar.DAY_OF_MONTH));				dialog.show();			}		});

看到了吧,我们直接弹出android内置的Dialog(DatePickerDialog),看一下效果

wKiom1OGD02RVxVeAAGuB6Ma8Wc217.jpg

在他的日期设置事件(onDateSet)中,我们拿到选择的日期,先赋给出生日期文本框,然后再用当前的年份减去选择的年份,算出来就是年龄,把年龄赋给年龄文本框。如果你想设置初始化的日期的话,需要注意他DatePickerDialog的构造函数最后三个参数,来自API的解释

wKiom1OGEITSRiNsAAFLCFuTgto506.jpg

OK,日期看完之后,就是右边的图片了,首先我们要知道图片从哪里来,当然是从手机里来,是个人都知道。我们看一下点击浏览按钮做的事情。

this.btnBrowser.setOnClickListener(new OnClickListener() {			public void onClick(View view) {				Intent intent = new Intent();				intent.setType("p_w_picpath/*");				intent.setAction(Intent.ACTION_GET_CONTENT);				startActivityForResult(intent, 1);			}		});

看到了吧,启动手机的图片照片搜索界面,如下

wKioL1OGEW7jxmW2AAIEdJbz5LE669.jpg

选择一张照片,图片就会显示到图片框中,如下

wKiom1OGEg2AWEy5AAGa5yozDEk805.jpg

那么图片是怎么显示到图片框中的,第一步,我们要重写当前Activity的onActivityResult方法。

protected void onActivityResult(int requestCode, int resultCode,			android.content.Intent data) {		if (resultCode == RESULT_OK) {			Uri uri = data.getData();			ContentResolver contentResolver = this.getContentResolver();			try {				Bitmap bitmap = BitmapFactory.decodeStream(contentResolver						.openInputStream(uri));				imgUserPhoto.setImageBitmap(bitmap);			} catch (FileNotFoundException e) {			}		}		super.onActivityResult(requestCode, resultCode, data);	}

我们拿到图片的资源地址后,转化成Bitmap,赋给图片框。在这里图片框有多种显示拉伸方式,我就不多说了,自己查吧。OK,图片也显示完了,我们看最后的保存。

在看保存之前,我们先看一下取消

this.btnCancel.setOnClickListener(new OnClickListener() {			public void onClick(View view) {				final AlertDialog.Builder builder = new AlertDialog.Builder(						owner);				builder.setIcon(R.drawable.info);				builder.setTitle(R.string.titleSystemCodeModifyName);				builder.setMessage("您确定要退出修改吗?");				builder.setPositiveButton(R.string.btnSure, null);				builder.setNegativeButton(R.string.btnCancelText, null);				final AlertDialog dialog = builder.create();				dialog.show();				dialog.getButton(AlertDialog.BUTTON_POSITIVE)						.setOnClickListener(new View.OnClickListener() {							@Override							public void onClick(View v) {								userinfomanage.this.setResult(RESULT_OK);								userinfomanage.this.finish();							}						});			}		});

取消这个很简单,就是构造一个弹出框,点击确定关闭当前Activity,点击取消,不关闭界面

wKiom1OGE_3iMVe0AAGIJrbUHyk357.jpg

OK,最后我们看一下我们的save。

this.btnSave.setOnClickListener((new OnClickListener() {			public void onClick(View view) {				if (!CheckUserInput())					return;				UserInfoEntity userInfoEntity = GetUserInfoEntity();				SoapObject soapObject = ModifyUserInfoEntty(userInfoEntity);				Boolean isSuccess = Boolean.valueOf(soapObject.getProperty(						"IsSuccess").toString());				if (isSuccess) {					ShowMessage(R.string.SaveSuccess);				} else {					String errorMsg = soapObject.getProperty("ErrorMessage")							.toString();					ShowMessage(errorMsg);				}			}		}));

首先是check,如下,很简单

private Boolean CheckUserInput() {		String userName = this.txtUserName.getText().toString().trim();		if (userName.length() == 0) {			this.ShowMessage("姓名不能为空!");			this.txtUserName.requestFocus();			return false;		}		String birthDay = this.txtBirthDay.getText().toString().trim();		if (birthDay.length() == 0) {			this.ShowMessage("出生日期不能为空!");			this.btnBrowser.requestFocus();			return false;		}		return true;	}

接着是拿到要保存的实体GetUserInfoEntity

private UserInfoEntity GetUserInfoEntity() {		UserInfoEntity userInfoEntity = new UserInfoEntity();		userInfoEntity.setProperty(1, txtUserName.getText().toString());		userInfoEntity.setProperty(0, userNo);		userInfoEntity.setProperty(2, spnUserSex.getSelectedItem());		userInfoEntity.setProperty(3, txtAge.getText());		userInfoEntity.setProperty(4, txtBirthDay.getText());		userInfoEntity.setProperty(5, radiobtnTemper1.isChecked() ? "1" : "2");		if (chkPhoto.isChecked()) {			String strByte = Base64.encode(GetImageByteArray());			userInfoEntity.setProperty(6, strByte);		}		return userInfoEntity;	}

需要注意的是这里Base64.encode(GetImageByteArray()),这个就是刚才说的KSoap不支持直接传byte[],而是要转码。GetImageByteArray这个方法是将图片框中的图片转化成byte[]。

private byte[] GetImageByteArray() {		byte[] compressData = null;		imgUserPhoto.setDrawingCacheEnabled(true);		Bitmap bmp = Bitmap.createBitmap(imgUserPhoto.getDrawingCache());		imgUserPhoto.setDrawingCacheEnabled(false);		if (bmp != null) {			compressData = GetByteArrayByBitmap(bmp);		}		return compressData;	}	private byte[] GetByteArrayByBitmap(Bitmap bmp) {		byte[] compressData = null;		ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();		bmp.compress(Bitmap.CompressFormat.JPEG, 100, byteOutputStream);		compressData = byteOutputStream.toByteArray();		try {			byteOutputStream.close();		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}		return compressData;	}

这都是固定写法,不多做解释。OK,我们看一下实体的定义,免得看得人摸不着头脑

public class UserInfoEntity implements KvmSerializable {	private String UserNo;	private String UserName;	private String UserSex;	private int UserAge;	private String BirthDay;	private String Temper;	private String UserPhoto;	@Override	public Object getProperty(int arg0) {		// TODO Auto-generated method stub		Object property = null;		switch (arg0) {		case 0:			property = this.UserNo;			break;		case 1:			property = this.UserName;			break;		case 2:			property = this.UserSex;			break;		case 3:			property = this.UserAge;			break;		case 4:			property = this.BirthDay;			break;		case 5:			property = this.Temper;			break;		case 6:			property = this.UserPhoto;			break;		default:			break;		}		return property;	}	@Override	public int getPropertyCount() {		// TODO Auto-generated method stub		return 7;	}	@Override	public void getPropertyInfo(int arg0, Hashtable arg1, PropertyInfo arg2) {		// TODO Auto-generated method stub		switch (arg0) {		case 0:			arg2.type = PropertyInfo.STRING_CLASS;			arg2.name = "UserNo";			break;		case 1:			arg2.type = PropertyInfo.STRING_CLASS;			arg2.name = "UserName";			break;		case 2:			arg2.type = PropertyInfo.STRING_CLASS;			arg2.name = "UserSex";			break;		case 3:			arg2.type = PropertyInfo.INTEGER_CLASS;			arg2.name = "UserAge";			break;		case 4:			arg2.type = PropertyInfo.STRING_CLASS;			arg2.name = "BirthDay";			break;		case 5:			arg2.type = PropertyInfo.STRING_CLASS;			arg2.name = "Temper";			break;		case 6:			arg2.type = PropertyInfo.STRING_CLASS;			arg2.name = "UserPhoto";			break;		default:			break;		}	}	@Override	public void setProperty(int arg0, Object arg1) {		// TODO Auto-generated method stub		if (arg1 == null)			return;		switch (arg0) {		case 0:			this.UserNo = arg1.toString();			break;		case 1:			this.UserName = arg1.toString();			break;		case 2:			this.UserSex = arg1.toString();			break;		case 3:			this.UserAge = Integer.parseInt(arg1.toString());			break;		case 4:			this.BirthDay = arg1.toString();			break;		case 5:			this.Temper = arg1.toString();			break;		case 6:			this.UserPhoto = arg1.toString();		default:			break;		}	}}

和.net WebServce端是对应的。OK,最后我们看一下保存(ModifyUserInfoEntty)的代码。

private SoapObject ModifyUserInfoEntty(UserInfoEntity userInfoEntity) {		SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);		PropertyInfo pi = new PropertyInfo();		pi.setName("userInfoEntity");		pi.setValue(userInfoEntity);		pi.setType(userInfoEntity.getClass());		request.addProperty(pi);		SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(				SoapEnvelope.VER11);		soapEnvelope.dotNet = true;		HttpTransportSE httpTS = new HttpTransportSE(URL);		soapEnvelope.bodyOut = httpTS;		soapEnvelope.setOutputSoapObject(request);// 设置请求参数		soapEnvelope.addMapping(NAMESPACE, "UserInfoEntity", userInfoEntity				.getClass());		new MarshalBase64().register(soapEnvelope);		try {			httpTS.call(SOAP_ACTION, soapEnvelope);		} catch (IOException e) {			// TODO Auto-generated catch block			this.ShowMessage(e.getMessage());			// e.printStackTrace();		} catch (XmlPullParserException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}		SoapObject result = null;		try {			result = (SoapObject) soapEnvelope.getResponse();		} catch (SoapFault e) {			// TODO Auto-generated catch block			e.printStackTrace();		}		return result;	}

这里需要注意的是new MarshalBase64().register(soapEnvelope);这是要告诉soap 信使message中包含有Base64转过的byte[]。OK,最后,我们鼓起勇气点击save。

wKiom1OGFp6RVPlfAAGPC9yXtxA485.jpg

走起,见证奇迹的时刻

wKioL1OGFs6SjmyMAAFsjF0FDoc433.jpg

yeah,成功了,图片是否成功我们需要借助C#版的程序看一下,成功了。

wKiom1OHPoTDxpdrAAK7Kl5LEnQ210.jpg

最后,哥们这博客可真是货真价实,中兴U880S测试机。

wKiom1OGGPyiPImDAAFG60n04Aw032.jpg

评价一下你不会吃亏,评价一下你不会上当,你评的越多,我写的越多。他大舅他二舅都是他舅,高桌子低板凳都是木头,进来的都是干这一行的,给个评价吧。