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


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

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

Android切近实战(五)_android·下拉列表

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

Android切近实战(五)_android byte数组_02

首先我们来看一下.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实体

Android切近实战(五)_android byte数组_03

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


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

Android切近实战(五)_android 相册_04

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

Android切近实战(五)_android·下拉列表_05

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


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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent" android:layout_height="fill_parent">
	<HorizontalScrollView android:layout_height="wrap_content"
		android:layout_width="fill_parent"
		android:scrollbarAlwaysDrawHorizontalTrack="false">
		<TableLayout android:layout_width="wrap_content"
			android:layout_height="wrap_content" android:background="@color/blue1"
			android:stretchColumns="0">
			<TableRow>
				<TableLayout android:layout_width="wrap_content"
					android:layout_height="wrap_content" android:stretchColumns="1"
					android:padding="3dp" android:layout_column="0"
					android:layout_margin="1dp" android:background="@color/teal">
					<TableRow>
						<TextView android:text="@string/userName"
							android:gravity="right" android:textSize="8pt"></TextView>
						<EditText android:id="@+id/txtUserName"
							android:drawableLeft="@drawable/userhint" android:hint="@string/hintInputUserName"
							android:textColorHint="@color/hintColor" android:width="200dp"
							android:singleLine="true" android:maxLength="25"></EditText>
					</TableRow>
					<TableRow>
						<TextView android:text="@string/userSex"
							android:layout_gravity="center_vertical" android:textSize="8pt">
						</TextView>
						<Spinner android:id="@+id/cmbUserSex"
							android:layout_width="fill_parent" android:layout_height="wrap_content"></Spinner>
					</TableRow>
					<TableRow>
						<TextView android:text="@string/userAge" android:gravity="right"
							android:textSize="8pt"></TextView>
						<EditText android:id="@+id/txtUserAge" android:editable="false"></EditText>
					</TableRow>
					<TableRow>
						<TextView android:text="@string/userBirthDay"
							android:gravity="right" android:textSize="8pt"
							android:layout_gravity="center_vertical"></TextView>
						<LinearLayout android:layout_width="wrap_content"
							android:layout_height="wrap_content" android:orientation="horizontal">
							<EditText android:id="@+id/txtUserBirthDay"
								android:editable="false"
								android:layout_width="wrap_content" android:layout_height="wrap_content"
								android:width="140dp" android:hint="@string/hintChooseBirth"
								android:textColorHint="@color/hintColor" android:drawableLeft="@drawable/calander"
								android:singleLine="true"></EditText>
							<Button android:id="@+id/btnChoose" android:layout_width="80dp"
								android:layout_height="45dp" android:drawableLeft="@drawable/pencil"
								android:layout_gravity="center_vertical" android:text="@string/btnChoose"
								android:textStyle="bold"></Button>
						</LinearLayout>
					</TableRow>
					<TableRow>
						<TextView android:text="@string/userTemper"
							android:gravity="right" android:textSize="8pt"
							android:layout_gravity="center_vertical"></TextView>
						<RadioGroup android:id="@+id/radioGroup"
							android:contentDescription="脾气" android:layout_width="wrap_content"
							android:orientation="horizontal" android:layout_height="wrap_content">
							<RadioButton android:layout_width="wrap_content"
								android:textColor="@color/red1" android:layout_height="wrap_content"
								android:id="@+id/radioTemper1" android:text="@string/userTemper1"
								android:checked="true"></RadioButton>
							<RadioButton android:layout_width="wrap_content"
								android:textColor="@color/red1" android:layout_height="wrap_content"
								android:id="@+id/radioTemper2" android:text="@string/userTemper2"></RadioButton>
						</RadioGroup>
					</TableRow>
					<TableRow>
						<LinearLayout android:orientation="horizontal"
						android:layout_height="wrap_content"
						android:layout_width="wrap_content"
						android:layout_span="2">
							<Button
								android:id="@+id/btnSave" 
								android:text="@string/btnSave" 
								android:textColor="@color/blue"
								android:layout_width="wrap_content"
								android:layout_height="wrap_content"
								android:layout_weight="1"
								android:textStyle="bold"></Button>
							<Button android:text="@string/btnCancelText"
								android:layout_width="wrap_content"
								android:layout_height="wrap_content"
								android:layout_weight="1"
								android:id="@+id/btnCancel"
								android:textColor="@color/blue" android:textStyle="bold"
								></Button>
						</LinearLayout>
					</TableRow>
				</TableLayout>
				<TableLayout android:layout_width="wrap_content"
					android:background="@color/teal" android:layout_margin="1dp"
					android:layout_height="wrap_content" android:layout_column="1">
					<TableRow>
						<ImageView android:id="@+id/imgUserPhoto"
							android:background="@color/imgBg" android:layout_marginLeft="10dp"
							android:layout_marginTop="5dp" android:layout_marginRight="5dp"
							android:layout_width="160dp" android:layout_height="240dp"
							android:src="@drawable/deaultphoto" android:scaleType="fitCenter"
							android:layout_span="2" />
					</TableRow>
					<TableRow>
						<CheckBox android:id="@+id/chkChoosePhoto"
							android:layout_marginLeft="10dp" android:layout_column="0"></CheckBox>
						<Button android:id="@+id/btnBrowser" android:text="@string/btnChoosePhoto"
							android:textStyle="bold" android:layout_width="150dp"
							android:layout_height="45dp" android:layout_marginTop="1dp"
							android:textColor="@color/blue" android:layout_column="1"></Button>
					</TableRow>
				</TableLayout>
			</TableRow>
		</TableLayout>
	</HorizontalScrollView>
</LinearLayout>


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


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

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


Android切近实战(五)_androd webservice_06


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

private void InitData() {
		// ArrayAdapter<String> adapter;
		// adapter = new ArrayAdapter<String>(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设置适配器。我注释的上面部分是当不从资源文件加载数据的时候的代码,下面部分是下拉事件响应的代码。我们看一下下拉效果

Android切近实战(五)_android·下拉列表_07


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

Android切近实战(五)_android byte数组_08

好了,那我们接下来看这个出生日期,为什么要先看出生日期呢,因为年龄是根据出生日期算出来的,难道您刚才没注意那个年龄的文本框是设置为不能编辑的吗(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),看一下效果

Android切近实战(五)_android byte数组_09

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

Android切近实战(五)_androd webservice_10

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);
			}
		});

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

Android切近实战(五)_android 相册_11

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

Android切近实战(五)_android byte数组_12

那么图片是怎么显示到图片框中的,第一步,我们要重写当前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,点击取消,不关闭界面

Android切近实战(五)_android·下拉列表_13

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。

Android切近实战(五)_androd webservice_14

走起,见证奇迹的时刻

Android切近实战(五)_androd webservice_15

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

Android切近实战(五)_android byte数组_16

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

Android切近实战(五)_android·下拉列表_17

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