最近天气真他娘的热,炸鸡啤酒,我觉得如果不演那么什么我从来都不看的韩剧,绝对没有人喜欢这种吃法。好了,废话不多说,天这么热,我只能晚上腾出时间来写这个东东,顺便引用吉日嘎拉的博客上面的一句话"每天进步一点点"。
OK,我们这次是要把这个界面翻成Android版,大家看过我的博客都知道我一直都是拿这几个界面再弄,
什么java实战篇也是用这个界面,唉,没办法,我只有这个界面。
先上一张Android版的图,吊胃口,下面的这张是模拟器上的图,怎么样,还像个app的样子吧。
首先我们来看一下.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实体
我们的Photo是byte[]类型,因为Ksoap是无法传递byte[]的,所以在android客户端,我们要先将byte[]编码成string,然后在.net wenservice端再反编码。好了,这里就是webservice端。
接着就到了我们的android客户端了,他才是我们这篇文章的重头戏。先上一张真机上的图,开启笔记本wifi,手机连接wifi,更换代码中的IP,OK,运行
就是这张图,大家可能会问,那右边的那个东西是什么,是鸟的翅膀吗,不是,是一个人
这边的功能是如果用户勾选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资源,用来加载到下拉列表。
在代码中,我们会构造一个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: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),看一下效果
在他的日期设置事件(onDateSet)中,我们拿到选择的日期,先赋给出生日期文本框,然后再用当前的年份减去选择的年份,算出来就是年龄,把年龄赋给年龄文本框。如果你想设置初始化的日期的话,需要注意他DatePickerDialog的构造函数最后三个参数,来自API的解释
OK,日期看完之后,就是右边的图片了,首先我们要知道图片从哪里来,当然是从手机里来,是个人都知道。我们看一下点击浏览按钮做的事情。
this.btnBrowser.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, 1);
}
});
看到了吧,启动手机的图片照片搜索界面,如下
选择一张照片,图片就会显示到图片框中,如下
那么图片是怎么显示到图片框中的,第一步,我们要重写当前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,点击取消,不关闭界面
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。
走起,见证奇迹的时刻
yeah,成功了,图片是否成功我们需要借助C#版的程序看一下,成功了。
最后,哥们这博客可真是货真价实,中兴U880S测试机。
评价一下你不会吃亏,评价一下你不会上当,你评的越多,我写的越多。他大舅他二舅都是他舅,高桌子低板凳都是木头,进来的都是干这一行的,给个评价吧。