实训第七周(2)

李晨晨:

这次主要实现了聊天记录列表的adapter,即上次的MsgRecyclerView的adapter,主要是各类消息的左右显示和各条消息的头像设置。

1.通过消息的MsgDirectionEnum和MsgTypeEnum属性(网易云信提供),确定消息的显示位置和方式。

[java] view plain copy
  1. private int getMsgViewType(MsgDirectionEnum direct, MsgTypeEnum type) {  
  2.   
  3.         // 收到的消息,头像显示在 left  
  4.         if (direct == MsgDirectionEnum.In) {  
  5.             if (type == MsgTypeEnum.text) {  
  6.                 return MSG_TEXT_L;  
  7.             } else if (type == MsgTypeEnum.image) {  
  8.                 return MSG_IMG_L;  
  9.             } else if (type == MsgTypeEnum.audio) {  
  10.                 return MSG_AUDIO_L;  
  11.             } else if (type == MsgTypeEnum.location) {  
  12.                 return MSG_LOC_L;  
  13.             } else {  
  14.                 return 0;  
  15.             }  
  16.         } else { // 发出的消息,头像显示在右边  
  17.             if (type == MsgTypeEnum.text) {  
  18.                 return MSG_TEXT_R;  
  19.             } else if (type == MsgTypeEnum.image) {  
  20.                 return MSG_IMG_R;  
  21.             } else if (type == MsgTypeEnum.audio) {  
  22.                 return MSG_AUDIO_R;  
  23.             } else if (type == MsgTypeEnum.location) {  
  24.                 return MSG_LOC_R;  
  25.             } else {  
  26.                 return 0;  
  27.             }  
  28.         }  
  29.     }  
[java] view plain copy
  1. private static final int MSG_TEXT_L = 0x20000;  
  2.    private static final int MSG_IMG_L = 0x20001;  
  3.    private static final int MSG_AUDIO_L = 0x20002;  
  4.    private static final int MSG_VIDEO_L = 0x20003;  
  5.    private static final int MSG_LOC_L = 0x20004;  
  6.   
  7.    private static final int MSG_TEXT_R = 0x30000;  
  8.    private static final int MSG_IMG_R = 0x30001;  
  9.    private static final int MSG_AUDIO_R = 0x30002;  
  10.    private static final int MSG_VIDEO_R = 0x30003;  
  11.    private static final int MSG_LOC_R = 0x30004;  

2.RecycleView 的adapter的onCreateViewHolder、onBindViewHolder以及getItemCount

[java] view plain copy
  1. @Override  
  2.    public RViewHolder onCreateViewHolder(ViewGroup parent, int layoutId) {  
  3.        View view = mInflater.inflate(layoutId, parent, false);  
  4.        return new RViewHolder(mContext, view);  
  5.    }  
  6.   
  7.    @Override  
  8.    public void onBindViewHolder(RViewHolder holder, int position) {  
  9.        if (mMessageList.get(position - 1).getUuid() == null) {  
  10.            String time = mDateFormat.format(new Date(mMessageList.get(position - 1).getTime()));  
  11.            holder.setText(R.id.tv_msg_time, time);  
  12.        } else {  
  13.            bindMsgView(holder, mMessageList.get(position - 1));  
  14.        }  
  15.    }  
  16.   
  17.    @Override  
  18.    public int getItemCount() {  
  19.        return mMessageList.size();  
  20.    }  

其中用到的bindMsgView方法:包括头像设置、process bar的显示、根据类型绑定数据

[java] view plain copy
  1. private void bindMsgView(final RViewHolder holder, final IMMessage message) {  
  2.   
  3.        ImageView headView = holder.getImageView(R.id.iv_head_picture);  
  4.         // 设置头像  
  5.         if (message.getDirect() == MsgDirectionEnum.In) {  
  6.   
  7.             ImageUtils.setImageByUrl(mContext, headView, mChatSession.getChatInfo().getAvatar(),  
  8.                     R.mipmap.app_logo_main);  
  9.   
  10.             // 设置好友头像点击事件--打开好友信息界面  
  11.             headView.setOnClickListener(new View.OnClickListener() {  
  12.                 @Override  
  13.                 public void onClick(View v) {  
  14.                     Intent intent = new Intent(mContext, FriendInfoActivity.class);  
  15.                     intent.putExtra("NimUserInfo", mChatSession.getChatInfo());  
  16.                     intent.putExtra("FLAG",FriendInfoActivity.FLAG_SHOW_FRIEND);  
  17.                     mContext.startActivity(intent);  
  18.                 }  
  19.             });  
  20.   
  21.         } else {  
  22.             ImageUtils.setImageByUrl(mContext, headView, mChatSession.getMyInfo().getAvatar(),  
  23.                     R.mipmap.app_logo_main);  
  24.         }  
  25.   
  26.         // 根据消息状态和附件传输状态决定是否显示progress bar  
  27.         if (mChatUtils.isTransferring(message)) {  
  28.             holder.setVisible(R.id.progress_status, true);  
  29.         } else {  
  30.             holder.setVisible(R.id.progress_status, false);  
  31.         }  
  32.   
  33.         // 根据类型绑定数据  
  34.         int viewType = getMsgViewType(message.getDirect(), message.getMsgType());  
  35.         switch (viewType) {  
  36.   
  37.             // 文本  
  38.             case MSG_TEXT_L:  
  39.             case MSG_TEXT_R:  
  40.                 TextView textView = holder.getTextView(R.id.tv_chat_msg);  
  41.                 textView.setText(EmojiUtils.text2Emoji(mContext,message.getContent(),  
  42.                         textView.getTextSize()));  
  43.                 textView.setOnClickListener(new View.OnClickListener() {  
  44.                     @Override  
  45.                     public void onClick(View v) {  
  46.                         if (mItemClickListener != null){  
  47.                             mItemClickListener.onItemClick(holder,message);  
  48.                         }  
  49.                     }  
  50.                 });  
  51.                 break;  
  52.   
  53.             // 图像  
  54.             case MSG_IMG_L:  
  55.             case MSG_IMG_R:  
  56.                 ImageAttachment imageAttachment = (ImageAttachment) message.getAttachment();  
  57.                 final SelectableRoundedImageView imageView = (SelectableRoundedImageView)  
  58.                         holder.getImageView(R.id.iv_msg_img);  
  59.                 Bitmap bitmap = mChatUtils.getBitmap(imageAttachment);  
  60.                 if (bitmap != null){  
  61.                     imageView.setImageBitmap(bitmap);  
  62.                 }else {  
  63.                     imageView.setImageResource(R.mipmap.bg_img_defalut);  
  64.                 }  
  65.                 imageView.setOnClickListener(new View.OnClickListener() {  
  66.                     @Override  
  67.                     public void onClick(View v) {  
  68.                         if (mItemClickListener != null){  
  69.                             mItemClickListener.onItemClick(holder,message);  
  70.                         }  
  71.                     }  
  72.                 });  
  73.                 break;  
  74.   
  75.             // 音频  
  76.             case MSG_AUDIO_L:  
  77.             case MSG_AUDIO_R:  
  78.                 AudioAttachment audioAttachment = (AudioAttachment) message.getAttachment();  
  79.                 holder.setText(R.id.tv_audio_time, mChatUtils.getAudioTime(audioAttachment.getDuration()));  
  80.                 RelativeLayout layout = holder.getReltiveLayout(R.id.layout_audio_msg);  
  81.                 mChatUtils.setAudioLayoutWidth(layout, audioAttachment.getDuration());  
  82.   
  83.                 holder.getReltiveLayout(R.id.layout_audio_msg)  
  84.                         .setOnClickListener(new View.OnClickListener() {  
  85.                     @Override  
  86.                     public void onClick(View v) {  
  87.                         if (mItemClickListener != null){  
  88.                             mItemClickListener.onItemClick(holder,message);  
  89.                         }  
  90.                     }  
  91.                 });  
  92.                 break;  
  93.   
  94.   
  95.             // 位置  
  96.             case MSG_LOC_L:  
  97.             case MSG_LOC_R:  
  98.                 LocationAttachment locationAttachment = (LocationAttachment) message.getAttachment();  
  99.                 holder.setText(R.id.tv_loc_address,locationAttachment.getAddress());  
  100.                 holder.getTextView(R.id.tv_show_loc).setOnClickListener(new View.OnClickListener() {  
  101.                     @Override  
  102.                     public void onClick(View v) {  
  103.                         if (mItemClickListener != null){  
  104.                             mItemClickListener.onItemClick(holder,message);  
  105.                         }  
  106.                     }  
  107.                 });  
  108.                 break;  
  109.   
  110.         }  
  111.     }  

3.重写的RecyclerView.Adapter的getItemViewType方法:

[java] view plain copy
  1. @Override  
  2.     public int getItemViewType(int position) {  
  3.         if (mMessageList.get(position - 1).getUuid() == null) {  
  4.             return R.layout.item_msg_list_time;//如果没有Uuid,则为时间消息  
  5.         } else {  
  6.             return getViewLayoutId(getMsgViewType(mMessageList.get(position - 1).getDirect(),  
  7.                     mMessageList.get(position - 1).getMsgType()));  
  8.         }  
  9.     }  
[java] view plain copy
  1. private int getViewLayoutId(int viewType) {  
  2.         switch (viewType) {  
  3.             // 收到的消息  
  4.             case MSG_TEXT_L:  
  5.                 return R.layout.item_msg_text_left;  
  6.             case MSG_IMG_L:  
  7.                 return R.layout.item_msg_img_left;  
  8.             case MSG_AUDIO_L:  
  9.                 return R.layout.item_msg_audio_left;  
  10.             case MSG_LOC_L:  
  11.                 return R.layout.item_msg_loc_left;  
  12.   
  13.             // 发出的消息  
  14.             case MSG_TEXT_R:  
  15.                 return R.layout.item_msg_text_right;  
  16.             case MSG_IMG_R:  
  17.                 return R.layout.item_msg_img_right;  
  18.             case MSG_AUDIO_R:  
  19.                 return R.layout.item_msg_audio_right;  
  20.             case MSG_LOC_R:  
  21.                 return R.layout.item_msg_loc_right;  
  22.   
  23.             // 其他消息  
  24.             default:  
  25.                 return R.layout.item_msg_list_time;  
  26.   
  27.         }  
  28.     }  
4.
[java] view plain copy
  1. public MessageListAdapter(Context context, List<IMMessage> messages, ChatSession session) {  
  2.        mContext = context;  
  3.        mInflater = LayoutInflater.from(context);  
  4.        mChatUtils = new ChatUtils(context);  
  5.        mMessageList = messages;  
  6.        mChatSession = session;  
  7.        mDateFormat = new SimpleDateFormat("MM-dd HH:mm");//月-日 时(0~23):分  
  8.    }  




仝心:

因为登陆界面有注册功能要能转到账号的注册界面,所以先完成了注册界面的布局,内容包括输入账号、输入密码、确定密码以及用已有账号登陆等等。

注册界面的布局文件如下:

[java] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:fitsSystemWindows="true"  
  7.     android:background="@drawable/splash"  
  8.     android:orientation="vertical">  
  9.   
  10.     <include layout="@layout/title_layout"/>  
  11.   
  12.     <LinearLayout  
  13.         android:layout_width="match_parent"  
  14.         android:layout_height="wrap_content"  
  15.         android:layout_marginTop="50dp"  
  16.         android:layout_marginLeft="40dp"  
  17.         android:gravity="center_vertical"  
  18.         android:orientation="horizontal">  
  19.   
  20.         <ImageView  
  21.             android:layout_width="24dp"  
  22.             android:layout_height="24dp"  
  23.             android:src="@mipmap/icon_user_account" />  
  24.   
  25.         <EditText  
  26.             android:id="@+id/et_account"  
  27.             android:layout_width="match_parent"  
  28.             android:layout_height="wrap_content"  
  29.             android:layout_marginLeft="16dp"  
  30.             android:background="@null"  
  31.             android:hint="请输入账号"  
  32.             android:digits="@string/digits"  
  33.             android:inputType="textEmailAddress"  
  34.             android:textColor="@android:color/white"  
  35.             android:textColorHint="@color/white_color"  
  36.             android:textSize="16sp" />  
  37.     </LinearLayout>  
  38.   
  39.     <View  
  40.         android:layout_width="match_parent"  
  41.         android:layout_height="0.8dp"  
  42.         android:layout_marginLeft="38dp"  
  43.         android:layout_marginRight="38dp"  
  44.         android:layout_marginTop="7dp"  
  45.         android:background="@color/hint_color" />  
  46.   
  47.     <LinearLayout  
  48.         android:layout_width="match_parent"  
  49.         android:layout_height="wrap_content"  
  50.         android:layout_marginTop="38dp"  
  51.         android:layout_marginLeft="40dp"  
  52.         android:gravity="center_vertical"  
  53.         android:orientation="horizontal">  
  54.   
  55.         <ImageView  
  56.             android:layout_width="24dp"  
  57.             android:layout_height="24dp"  
  58.             android:src="@mipmap/icon_user_name" />  
  59.   
  60.         <EditText  
  61.             android:id="@+id/et_user_name"  
  62.             android:layout_width="match_parent"  
  63.             android:layout_height="wrap_content"  
  64.             android:layout_marginLeft="16dp"  
  65.             android:background="@null"  
  66.             android:hint="请输入昵称"  
  67.             android:inputType="text"  
  68.             android:textColor="@android:color/white"  
  69.             android:textColorHint="@color/white_color"  
  70.             android:textSize="16sp" />  
  71.     </LinearLayout>  
  72.   
  73.     <View  
  74.         android:layout_width="match_parent"  
  75.         android:layout_height="0.8dp"  
  76.         android:layout_marginLeft="38dp"  
  77.         android:layout_marginRight="38dp"  
  78.         android:layout_marginTop="7dp"  
  79.         android:background="@color/hint_color" />  
  80.     <LinearLayout  
  81.         android:layout_width="match_parent"  
  82.         android:layout_height="wrap_content"  
  83.         android:layout_marginTop="38dp"  
  84.         android:layout_marginLeft="40dp"  
  85.         android:gravity="center_vertical"  
  86.         android:orientation="horizontal">  
  87.   
  88.         <ImageView  
  89.             android:layout_width="24dp"  
  90.             android:layout_height="24dp"  
  91.             android:src="@mipmap/icon_pass_word" />  
  92.   
  93.         <EditText  
  94.             android:id="@+id/et_pass_word"  
  95.             android:layout_width="match_parent"  
  96.             android:layout_height="wrap_content"  
  97.             android:layout_marginLeft="16dp"  
  98.             android:background="@null"  
  99.             android:hint="请输入密码"  
  100.             android:digits="@string/digits"  
  101.             android:inputType="textPassword"  
  102.             android:textColor="@android:color/white"  
  103.             android:textColorHint="@color/white_color"  
  104.             android:textSize="16sp" />  
  105.     </LinearLayout>  
  106.   
  107.     <View  
  108.         android:layout_width="match_parent"  
  109.         android:layout_height="0.8dp"  
  110.         android:layout_marginLeft="38dp"  
  111.         android:layout_marginRight="38dp"  
  112.         android:layout_marginTop="7dp"  
  113.         android:background="@color/hint_color" />  
  114.     <LinearLayout  
  115.         android:layout_width="match_parent"  
  116.         android:layout_height="wrap_content"  
  117.         android:layout_marginTop="38dp"  
  118.         android:layout_marginLeft="40dp"  
  119.         android:gravity="center_vertical"  
  120.         android:orientation="horizontal">  
  121.   
  122.         <ImageView  
  123.             android:layout_width="24dp"  
  124.             android:layout_height="24dp"  
  125.             android:src="@mipmap/icon_confirm_pass" />  
  126.   
  127.         <EditText  
  128.             android:id="@+id/et_confirm_pass"  
  129.             android:layout_width="match_parent"  
  130.             android:layout_height="wrap_content"  
  131.             android:layout_marginLeft="16dp"  
  132.             android:background="@null"  
  133.             android:hint="请确认密码"  
  134.             android:digits="@string/digits"  
  135.             android:inputType="textPassword"  
  136.             android:textColor="@android:color/white"  
  137.             android:textColorHint="@color/white_color"  
  138.             android:textSize="16sp" />  
  139.     </LinearLayout>  
  140.   
  141.     <View  
  142.         android:layout_width="match_parent"  
  143.         android:layout_height="0.8dp"  
  144.         android:layout_marginLeft="38dp"  
  145.         android:layout_marginRight="38dp"  
  146.         android:layout_marginTop="7dp"  
  147.         android:background="@color/hint_color" />  
  148.   
  149.     <TextView  
  150.         android:id="@+id/btn_register"  
  151.         android:layout_width="match_parent"  
  152.         android:layout_height="42dp"  
  153.         android:layout_gravity="center_horizontal"  
  154.         android:layout_marginLeft="38dp"  
  155.         android:layout_marginRight="38dp"  
  156.         android:layout_marginTop="46dp"  
  157.         android:background="@drawable/login_nol_bg"  
  158.         android:gravity="center"  
  159.         android:text="@string/register"  
  160.         android:textColor="#1A8ECB"  
  161.         android:textSize="16sp"/>  
  162.   
  163.     <TextView  
  164.         android:id="@+id/btn_to_login"  
  165.         android:layout_width="wrap_content"  
  166.         android:layout_height="wrap_content"  
  167.         android:layout_gravity="center_horizontal"  
  168.         android:paddingTop="20dp"  
  169.         android:layout_marginTop="4dp"  
  170.         android:gravity="center"  
  171.         android:text="@string/have_account_login"  
  172.         android:textColor="@color/white_color"  
  173.         android:textSize="14sp" />  
  174.     <View  
  175.         android:layout_width="120dp"  
  176.         android:layout_height="0.8dp"  
  177.         android:layout_gravity="center_horizontal"  
  178.         android:background="@color/white_color" />  
  179.   
  180.     <RelativeLayout  
  181.         android:layout_width="match_parent"  
  182.         android:layout_height="match_parent"  
  183.         android:layout_marginTop="6dp">  
  184.   
  185.         <TextView  
  186.             android:layout_width="wrap_content"  
  187.             android:layout_height="wrap_content"  
  188.             android:layout_alignParentBottom="true"  
  189.             android:layout_centerHorizontal="true"  
  190.             android:layout_marginBottom="6dp"  
  191.             android:textColor="@color/app_blue_color"  
  192.             android:text="@string/app_version"/>  
  193.     </RelativeLayout>  
  194.   
  195. </LinearLayout>  

为注册按键绑定监听器,内容包括检测信息是否填充完整,如果有空缺则提示,信息合法后创建账号

[java] view plain copy
  1. @OnClick(R.id.btn_register)  
  2.     public void register(){  
  3.         String account = mEtAccount.getText().toString().trim();  
  4.         String name = mEtName.getText().toString().trim();  
  5.         String pass = mEtPass.getText().toString().trim();  
  6.         String confirmPass = mEtConfirmPass.getText().toString().trim();  
  7.         if (TextUtils.isEmpty(account) || TextUtils.isEmpty(name) || TextUtils.isEmpty(pass)){  
  8.             Toast.makeText(this,"请将信息填写完整",Toast.LENGTH_SHORT).show();  
  9.             return;  
  10.         }  
  11.         if (TextUtils.isEmpty(confirmPass) || !confirmPass.equals(pass)){  
  12.             ToastUtils.showMessage(this,"确认密码为空或与密码不符");  
  13.             return;  
  14.         }  
  15.   
  16.         NimClientHandle.getInstance().register(account,pass, name, new OnRegisterListener() {  
  17.             @Override  
  18.             public void onSuccess() {  
  19.                 ToastUtils.showMessage(RegisterActivity.this,"注册成功");  
  20.                 finish();  
  21.             }  
  22.   
  23.             @Override  
  24.             public void onFailed(String message) {  
  25.                 ToastUtils.showMessage(RegisterActivity.this,"注册失败:" + message);  
  26.             }  
  27.         });  
  28.     }  

然后在上次自定义的LoginActivity中实现按注册键后,跳转到注册界面的功能

[java] view plain copy
  1. @OnClick(R.id.tv_btn_register)  
  2.     public void startRegister(){  
  3.         startActivity(new Intent(this,  
  4.                 RegisterActivity.class));  
  5.     }  

利用上次自定义的SharesPerferecesUtil类来实现保存登陆信息的方法,然后会在下面登陆功能中国调用

[java] view plain copy
  1. private void saveLoginInfo(LoginInfo info){  
  2.         SharedPreferencesUtil.setStringSharedPreferences(this, Constant.LOCAL_LOGIN_TABLE,  
  3.                 Constant.LOCAL_USER_ACCOUNT,info.getAccount());  
  4.         SharedPreferencesUtil.setStringSharedPreferences(this, Constant.LOCAL_LOGIN_TABLE,  
  5.                 Constant.LOCAL_USER_TOKEN,info.getToken());  
  6.         NimUserHandler.getInstance().setMyAccount(info.getAccount());//**内部仔细看  
  7.     }  

为登陆按键实现跳转到个人账号的功能,如果账号或密码不合法,则弹出提示且登陆无效

[java] view plain copy
  1. @OnClick(R.id.tv_btn_login)  
  2.     public void login(){  
  3.         String account = mEtUserAccount.getText().toString().trim();  
  4.         String pass = mEtPassWord.getText().toString().trim();  
  5.         if (TextUtils.isEmpty(account) || TextUtils.isEmpty(pass)){  
  6.             ToastUtils.showMessage(this,"账号或密码为空~");  
  7.             return;  
  8.         }  
  9.         RequestCallback<LoginInfo> callBack = new RequestCallback<LoginInfo>() {  
  10.             @Override  
  11.             public void onSuccess(LoginInfo loginInfo) {  
  12.                 isLogin = false;  
  13.                 // 保存登录信息  
  14.                 saveLoginInfo(loginInfo);  
  15.                 // 转入主页面  
  16.                 startActivity(new Intent(LoginActivity.this,MainActivity.class));  
  17.             }  
  18.   
  19.             @Override  
  20.             public void onFailed(int code) {  
  21.                 isLogin = false;  
  22.                 ToastUtils.showMessage(LoginActivity.this,  
  23.                         "登录失败:"+ ConvertUtils.code2String(code));  
  24.             }  
  25.   
  26.             @Override  
  27.             public void onException(Throwable exception) {  
  28.                 isLogin = false;  
  29.                 ToastUtils.showMessage(LoginActivity.this,  
  30.                         "登录出错:"+exception.getMessage());  
  31.             }  
  32.         };  
  33.         LoginInfo loginInfo = new LoginInfo(account,pass);  
  34.         mLoginFuture = NIMClient.getService(AuthService.class).login(loginInfo);  
  35.         isLogin = true;  
  36.         mLoginFuture.setCallback(callBack);  
  37.     }  

实现退出程序的功能,计算点击次数,点击两次才可成功退出,避免用户错按

[java] view plain copy
  1. private void backDoubleExit(){  
  2.         mKeyBackCount++;  
  3.         if (mKeyBackCount == 1){  
  4.             ToastUtils.showMessage(LoginActivity.this,"再点一次退出程序~~");  
  5.         }else if (mKeyBackCount == 2){  
  6.             MyApplication.getInstance().AppExit();  
  7.         }  
  8.     }  



张静:

接下来完成AccountInfoActivity(账号信息详情页)

一. TimePickerView——用于设置生日

1. 初始化控件

(1)实例化layout_time_picker.xml

[java] view plain copy
  1. private void initView(Context context) {  
  2.     LayoutInflater.from(getContext()).inflate(R.layout.layout_time_picker, this);  
  3.     mWvYear = (WheelView) findViewById(R.id.year);  
  4.     mWvMonth = (WheelView) findViewById(R.id.month);  
  5.     mWvDay = (WheelView) findViewById(R.id.day);  
  6.     mTvConfirm = (TextView) findViewById(R.id.tv_btn_confirm);  
  7. }  

(2)加载分别设置年、月、日的滚轮控件(WheelView)

(3)加载“确认”TextView

[java] view plain copy
  1. private void initView(Context context) {  
  2.     LayoutInflater.from(getContext()).inflate(R.layout.layout_time_picker, this);  
  3.     mWvYear = (WheelView) findViewById(R.id.year);  
  4.     mWvMonth = (WheelView) findViewById(R.id.month);  
  5.     mWvDay = (WheelView) findViewById(R.id.day);  
  6.     mTvConfirm = (TextView) findViewById(R.id.tv_btn_confirm);  
  7. }  


2. 初始化数据

设置格式为“年-月-日”

获取当前时间,当用户想要设置生日时,默认显示为当前年月日

[java] view plain copy
  1. private void initDate(){  
  2.     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());  
  3.     String[] split = sdf.format(new Date()).split("-");  
  4.     int currentYear = Integer.parseInt(split[0]);  
  5.     int currentMonth = Integer.parseInt(split[1]);  
  6.     int currentDay = Integer.parseInt(split[2]);  
  7.   
  8.     mWvYear.setData(getYearData(currentYear));  
  9.     mWvYear.setDefault(1);  
  10.     mWvMonth.setData(getMonthData());  
  11.     mWvMonth.setDefault(currentMonth - 1);  
  12.     mWvDay.setData(getDayData(getMaxDay(currentYear, currentMonth)));  
  13.     mWvDay.setDefault(currentDay - 1);  
  14. }  


3. 初始化监听器

[java] view plain copy
  1. private void initListener(){  
  2.     mTvConfirm.setOnClickListener(this);  
  3.     mWvYear.setOnSelectListener(new WheelView.OnSelectListener() {  
  4.         @Override  
  5.         public void endSelect(int id, String text) {  
  6.             changeDayData();  
  7.         }  
  8.   
  9.         @Override  
  10.         public void selecting(int id, String text) {  
  11.   
  12.         }  
  13.     });  
  14.   
  15.     mWvMonth.setOnSelectListener(new WheelView.OnSelectListener() {  
  16.         @Override  
  17.         public void endSelect(int id, String text) {  
  18.             changeDayData();  
  19.         }  
  20.   
  21.         @Override  
  22.         public void selecting(int id, String text) {  
  23.   
  24.         }  
  25.     });  
  26. }  

4. 列表加载可供用户选择的年月日

年份选择为(1900-2019)

月份选择为(1-12)

计算为闰年或平年,据此以及大月小月设置每年每月可选择的日期(28,29,30)

[java] view plain copy
  1. private ArrayList<String> getYearData(int currentYear) {  
  2.     ArrayList<String> list = new ArrayList<>();  
  3.     for (int i = currentYear + 1; i >= 1900; i--) {  
  4.         list.add(String.valueOf(i));  
  5.     }  
  6.     return list;  
  7. }  
  8.   
  9. private ArrayList<String> getMonthData() {  
  10.     ArrayList<String> list = new ArrayList<>();  
  11.     for (int i = 1; i <= 12; i++) {  
  12.         list.add(String.valueOf(i));  
  13.     }  
  14.     return list;  
  15. }  
  16.   
  17. private ArrayList<String> getDayData(int maxDay){  
  18.     ArrayList<String> list = new ArrayList<>();  
  19.     for (int i=1;i <= maxDay;i++){  
  20.         list.add(String.valueOf(i));  
  21.     }  
  22.     return list;  
  23. }  
  24.   
  25. private int getMaxDay(int year,int month){  
  26.     if (month == 2){  
  27.         if (isLeapYear(year)){  
  28.             return 29;  
  29.         }else {  
  30.             return 28;  
  31.         }  
  32.     }else if (month == 1 || month == 3 || month == 5 || month == 7  
  33.             || month == 8 || month ==10 || month == 12){  
  34.         return 31;  
  35.     }else {  
  36.         return 30;  
  37.     }  
  38. }  
  39.   
  40. private boolean isLeapYear(int year){  
  41.     return (year % 100 == 0 && year % 400 == 0)  
  42.             || (year % 100 != 0 && year % 4 == 0);  
  43. }  


5. 点击“确认”后,设为当前选择年月日

[java] view plain copy
  1. @Override  
  2. public void onClick(View v) {  
  3.     if (v.getId() == R.id.tv_btn_confirm){  
  4.         if (mSelectedListener != null){  
  5.             mSelectedListener.selectedDate(getYear(), getMonth(),getDay());  
  6.         }  
  7.     }  
  8. }  

附上完整TimePicker.java

[java] view plain copy
  1. package com.ezreal.ezchat.timeselectview;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.widget.LinearLayout;  
  8. import android.widget.TextView;  
  9.   
  10. import com.ezreal.ezchat.R;  
  11.   
  12. import java.text.SimpleDateFormat;  
  13. import java.util.ArrayList;  
  14. import java.util.Date;  
  15. import java.util.Locale;  
  16.   
  17. /** 
  18.  * Created by 张静 
  19.  */  
  20.   
  21. public class TimePickerView extends LinearLayout implements View.OnClickListener{  
  22.     private WheelView mWvYear;  
  23.     private WheelView mWvMonth;  
  24.     private WheelView mWvDay;  
  25.     private OnDateSelectedListener mSelectedListener;  
  26.     private TextView mTvConfirm;  
  27.   
  28.     public TimePickerView(Context context) {  
  29.         this(context, null);  
  30.     }  
  31.   
  32.     public TimePickerView(Context context, AttributeSet attrs) {  
  33.         super(context, attrs);  
  34.         initView(context);  
  35.         initDate();  
  36.         initListener();  
  37.     }  
  38.   
  39.     private void initView(Context context) {  
  40.         LayoutInflater.from(getContext()).inflate(R.layout.layout_time_picker, this);  
  41.         mWvYear = (WheelView) findViewById(R.id.year);  
  42.         mWvMonth = (WheelView) findViewById(R.id.month);  
  43.         mWvDay = (WheelView) findViewById(R.id.day);  
  44.         mTvConfirm = (TextView) findViewById(R.id.tv_btn_confirm);  
  45.     }  
  46.   
  47.     private void initDate(){  
  48.         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());  
  49.         String[] split = sdf.format(new Date()).split("-");  
  50.         int currentYear = Integer.parseInt(split[0]);  
  51.         int currentMonth = Integer.parseInt(split[1]);  
  52.         int currentDay = Integer.parseInt(split[2]);  
  53.   
  54.         mWvYear.setData(getYearData(currentYear));  
  55.         mWvYear.setDefault(1);  
  56.         mWvMonth.setData(getMonthData());  
  57.         mWvMonth.setDefault(currentMonth - 1);  
  58.         mWvDay.setData(getDayData(getMaxDay(currentYear, currentMonth)));  
  59.         mWvDay.setDefault(currentDay - 1);  
  60.     }  
  61.   
  62.     private void initListener(){  
  63.         mTvConfirm.setOnClickListener(this);  
  64.         mWvYear.setOnSelectListener(new WheelView.OnSelectListener() {  
  65.             @Override  
  66.             public void endSelect(int id, String text) {  
  67.                 changeDayData();  
  68.             }  
  69.   
  70.             @Override  
  71.             public void selecting(int id, String text) {  
  72.   
  73.             }  
  74.         });  
  75.   
  76.         mWvMonth.setOnSelectListener(new WheelView.OnSelectListener() {  
  77.             @Override  
  78.             public void endSelect(int id, String text) {  
  79.                 changeDayData();  
  80.             }  
  81.   
  82.             @Override  
  83.             public void selecting(int id, String text) {  
  84.   
  85.             }  
  86.         });  
  87.     }  
  88.   
  89.     private ArrayList<String> getYearData(int currentYear) {  
  90.         ArrayList<String> list = new ArrayList<>();  
  91.         for (int i = currentYear + 1; i >= 1900; i--) {  
  92.             list.add(String.valueOf(i));  
  93.         }  
  94.         return list;  
  95.     }  
  96.   
  97.     private ArrayList<String> getMonthData() {  
  98.         ArrayList<String> list = new ArrayList<>();  
  99.         for (int i = 1; i <= 12; i++) {  
  100.             list.add(String.valueOf(i));  
  101.         }  
  102.         return list;  
  103.     }  
  104.   
  105.     private ArrayList<String> getDayData(int maxDay){  
  106.         ArrayList<String> list = new ArrayList<>();  
  107.         for (int i=1;i <= maxDay;i++){  
  108.             list.add(String.valueOf(i));  
  109.         }  
  110.         return list;  
  111.     }  
  112.   
  113.     private int getMaxDay(int year,int month){  
  114.         if (month == 2){  
  115.             if (isLeapYear(year)){  
  116.                 return 29;  
  117.             }else {  
  118.                 return 28;  
  119.             }  
  120.         }else if (month == 1 || month == 3 || month == 5 || month == 7  
  121.                 || month == 8 || month ==10 || month == 12){  
  122.             return 31;  
  123.         }else {  
  124.             return 30;  
  125.         }  
  126.     }  
  127.   
  128.     private boolean isLeapYear(int year){  
  129.         return (year % 100 == 0 && year % 400 == 0)  
  130.                 || (year % 100 != 0 && year % 4 == 0);  
  131.     }  
  132.   
  133.     private void changeDayData(){  
  134.         int selectDay = getDay();  
  135.         int currentYear = getYear();  
  136.         int currentMonth = getMonth();  
  137.         int maxDay = getMaxDay(currentYear,currentMonth);  
  138.   
  139.         mWvDay.setData(getDayData(maxDay));  
  140.   
  141.         if (selectDay > maxDay){  
  142.             mWvDay.setDefault(maxDay - 1);  
  143.         }else {  
  144.             mWvDay.setDefault(selectDay - 1);  
  145.         }  
  146.   
  147.     }  
  148.   
  149.   
  150.     public int getYear(){  
  151.         return Integer.parseInt(mWvYear.getSelectedText());  
  152.     }  
  153.   
  154.     public int getMonth(){  
  155.         return Integer.parseInt(mWvMonth.getSelectedText());  
  156.     }  
  157.   
  158.     public int getDay(){  
  159.         return Integer.parseInt(mWvDay.getSelectedText());  
  160.     }  
  161.   
  162.     public void setSelectedListener(OnDateSelectedListener listener){  
  163.         this.mSelectedListener = listener;  
  164.     }  
  165.   
  166.     @Override  
  167.     public void onClick(View v) {  
  168.         if (v.getId() == R.id.tv_btn_confirm){  
  169.             if (mSelectedListener != null){  
  170.                 mSelectedListener.selectedDate(getYear(), getMonth(),getDay());  
  171.             }  
  172.         }  
  173.     }  
  174.   
  175.     public interface OnDateSelectedListener{  
  176.         void selectedDate(int year, int month, int day);  
  177.     }  
  178. }  


写完TimePicker可以开始写AccountInfoActivity中设置生日

(1)实例化dialog_select_birthday.xml,利用AlertDialog创建

(2)在timePickerView上添加选择监听器

        根据一定格式显示(若年份或月份是1-9之间的,显示时前面加0)

        若与设置前发生变化,个人账户信息设置为现在新设置的,并设置为已发生改变

[java] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical">  
  7.   
  8.     <com.ezreal.ezchat.timeselectview.TimePickerView  
  9.         android:id="@+id/date_picker"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"/>  
  12.   
  13. </LinearLayout>  

(2) 

[java] view plain copy
  1. private void setBirthday() {  
  2.     View view = LayoutInflater.from(this).inflate(R.layout.dialog_select_birthday, null);  
  3.     final AlertDialog dialog = new AlertDialog.Builder(this).setView(view).create();  
  4.     TimePickerView timePickerView = (TimePickerView) view.findViewById(R.id.date_picker);  
  5.     timePickerView.setSelectedListener(new TimePickerView.OnDateSelectedListener() {  
  6.         @Override  
  7.         public void selectedDate(int year, int month, int day) {  
  8.             String yearString = String.valueOf(year);  
  9.             String monthString = String.valueOf(month);  
  10.             String dayString = String.valueOf(day);  
  11.             if (monthString.length() == 1){  
  12.                 monthString = "0" + monthString;  
  13.             }  
  14.             if (dayString.length() == 1){  
  15.                 dayString = "0" + dayString;  
  16.             }  
  17.             String birthday = String.format("%s-%s-%s", yearString, monthString, dayString);  
  18.             if (!birthday.equals(mTvBirthDay.getText().toString())) {  
  19.                 mAccountBean.setBirthDay(birthday);  
  20.                 mTvBirthDay.setText(birthday);  
  21.                 haveAccountChange = true;  
  22.             }  
  23.             dialog.dismiss();  
  24.   
  25.         }  
  26.     });  
  27.     dialog.show();  
  28. }  


二. CityPickerView——用于设置地区

1. layout_city_picker.xml

[java] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="wrap_content"  
  7.     android:gravity="center_horizontal"  
  8.     android:orientation="vertical">  
  9.     <LinearLayout  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"  
  12.         android:gravity="center_horizontal"  
  13.         android:orientation="horizontal"  
  14.         android:padding="10dp">  
  15.   
  16.     <com.ezreal.ezchat.timeselectview.WheelView  
  17.         android:id="@+id/province_wv"  
  18.         android:layout_width="0dp"  
  19.         android:layout_height="wrap_content"  
  20.         android:layout_weight="1"  
  21.         app:itemNumber="3"  
  22.         app:lineColor="@color/default_line_color"  
  23.         app:lineHeight="2dp"  
  24.         app:maskHeight="32dp"  
  25.         app:noEmpty="true"  
  26.         app:normalTextColor="@color/default_unSelect_text_color"  
  27.         app:normalTextSize="14sp"  
  28.         app:selectedTextColor="@color/default_selected_text_color"  
  29.         app:selectedTextSize="18sp"  
  30.         app:unitHeight="50dp"/>  
  31.   
  32.     <View  
  33.         android:layout_width="2dp"  
  34.         android:layout_height="40dp"/>  
  35.   
  36.     <com.ezreal.ezchat.timeselectview.WheelView  
  37.         android:id="@+id/city_wv"  
  38.         android:layout_width="0dp"  
  39.         android:layout_height="wrap_content"  
  40.         android:layout_weight="1"  
  41.         app:itemNumber="3"  
  42.         app:lineColor="@color/default_line_color"  
  43.         app:lineHeight="2dp"  
  44.         app:maskHeight="32dp"  
  45.         app:noEmpty="true"  
  46.         app:normalTextColor="@color/default_unSelect_text_color"  
  47.         app:normalTextSize="14sp"  
  48.         app:selectedTextColor="@color/default_selected_text_color"  
  49.         app:selectedTextSize="18sp"  
  50.         app:unitHeight="50dp"/>  
  51.     </LinearLayout>  
  52.   
  53.     <TextView  
  54.         android:id="@+id/tv_btn_confirm"  
  55.         android:layout_marginTop="10dp"  
  56.         android:gravity="center"  
  57.         android:layout_width="match_parent"  
  58.         android:layout_height="30dp"  
  59.         android:textSize="18sp"  
  60.         android:textColor="@color/white_color"  
  61.         android:background="@color/default_selected_text_color"  
  62.         android:text="@string/confirm"/>  
  63.   
  64. </LinearLayout>  


2. 当View中所有子控件均被映射成xml后触发

(1)为“确定”TextView添加OnClickListener,初始化滚轮控件省份选择和城市选择并为其设置初始显示(即用户进行地区设置时所看到的,北京 西城区)

(2)为mProvincePick添加选择监听器

        根据指定id从mAreaDataUtil(全国省份城市操作类)中取出所选择的省

        此时,城市还没选择,默认显示为a. 若该省份城市数大于1,默认显示城市为第二个

                                                     b. 若该省份城市数为1,默认显示城市为第一个

(3)为mCityPicker添加选择监听器

        根据指定id设置

[java] view plain copy
  1. protected void onFinishInflate() {  
  2.     super.onFinishInflate();  
  3.     LayoutInflater.from(getContext()).inflate(R.layout.layout_city_picker, this);  
  4.     mTvConfirm = (TextView) findViewById(R.id.tv_btn_confirm);  
  5.     mTvConfirm.setOnClickListener(this);  
  6.     mProvincePicker = (WheelView) findViewById(R.id.province_wv);  
  7.     mCityPicker = (WheelView) findViewById(R.id.city_wv);  
  8.   
  9.     mProvincePicker.setData(mProvinceList);  
  10.     mProvincePicker.setDefault(0);  
  11.   
  12.     String defaultProvince = mProvinceList.get(0);  
  13.     mCityPicker.setData(mAreaDataUtil.getCityByProvince(defaultProvince));  
  14.     mCityPicker.setDefault(1);  
  15.   
  16.     mProvincePicker.setOnSelectListener(new WheelView.OnSelectListener() {  
  17.         @Override  
  18.         public void endSelect(int id, String text) {  
  19.             if (text.equals("") || text == null)  
  20.                 return;  
  21.             if (mCurrProvinceIndex != id) {  
  22.                 mCurrProvinceIndex = id;  
  23.                 String selectProvince = mProvincePicker.getSelectedText();  
  24.                 if (selectProvince == null || selectProvince.equals(""))  
  25.                     return;  
  26.   
  27.                 // get city names by province  
  28.                 ArrayList<String> city = mAreaDataUtil.getCityByProvince(mProvinceList.get(id));  
  29.                 if (city.size() == 0) {  
  30.                     return;  
  31.                 }  
  32.   
  33.                 mCityPicker.setData(city);  
  34.   
  35.                 if (city.size() > 1) {  
  36.                     //if city is more than one,show start index == 1  
  37.                     mCityPicker.setDefault(1);  
  38.                 } else {  
  39.                     mCityPicker.setDefault(0);  
  40.                 }  
  41.             }  
  42.   
  43.         }  
  44.   
  45.         @Override  
  46.         public void selecting(int id, String text) {  
  47.         }  
  48.     });  
  49.   
  50.     mCityPicker.setOnSelectListener(new WheelView.OnSelectListener() {  
  51.   
  52.         @Override  
  53.         public void endSelect(int id, String text) {  
  54.             if (text.equals("") || text == null)  
  55.                 return;  
  56.             if (mCurrCityIndex != id) {  
  57.                 mCurrCityIndex = id;  
  58.                 String selectCity = mCityPicker.getSelectedText();  
  59.                 if (selectCity == null || selectCity.equals(""))  
  60.                     return;  
  61.                 int lastIndex = Integer.valueOf(mCityPicker.getListSize());  
  62.                 if (id > lastIndex) {  
  63.                     mCityPicker.setDefault(lastIndex - 1);  
  64.                 }  
  65.             }  
  66.         }  
  67.   
  68.         @Override  
  69.         public void selecting(int id, String text) {  
  70.   
  71.         }  
  72.     });  
  73. }  

附上完整CityPickerView.java

[java] view plain copy
  1. package com.ezreal.ezchat.timeselectview;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.widget.LinearLayout;  
  8. import android.widget.TextView;  
  9.   
  10. import com.ezreal.ezchat.R;  
  11.   
  12. import java.util.ArrayList;  
  13.   
  14. /** 
  15.  * Created by 张静 
  16.  */  
  17.   
  18. public class CityPickerView extends LinearLayout implements View.OnClickListener{  
  19.   
  20.     private WheelView mProvincePicker;  
  21.     private WheelView mCityPicker;  
  22.   
  23.     private int mCurrProvinceIndex = -1;  
  24.     private int mCurrCityIndex = -1;  
  25.   
  26.     private AreaDataUtil mAreaDataUtil;  
  27.     private ArrayList<String> mProvinceList = new ArrayList<>();  
  28.     private TextView mTvConfirm;  
  29.     private OnCitySelectedListener mSelectedListener;  
  30.     public CityPickerView(Context context, AttributeSet attrs) {  
  31.         super(context, attrs);  
  32.         getAreaInfo();  
  33.     }  
  34.   
  35.     public CityPickerView(Context context) {  
  36.         this(context, null);  
  37.     }  
  38.   
  39.     private void getAreaInfo() {  
  40.         mAreaDataUtil = new AreaDataUtil(getContext());  
  41.         mProvinceList = mAreaDataUtil.getProvinces();  
  42.     }  
  43.   
  44.     @Override  
  45.     public void onClick(View v) {  
  46.         if (v.getId() == R.id.tv_btn_confirm){  
  47.             if (mSelectedListener != null){  
  48.                 mSelectedListener.citySelected(getProvince(),getCity());  
  49.             }  
  50.         }  
  51.     }  
  52.   
  53.     @Override  
  54.     protected void onFinishInflate() {  
  55.         super.onFinishInflate();  
  56.         LayoutInflater.from(getContext()).inflate(R.layout.layout_city_picker, this);  
  57.         mTvConfirm = (TextView) findViewById(R.id.tv_btn_confirm);  
  58.         mTvConfirm.setOnClickListener(this);  
  59.         mProvincePicker = (WheelView) findViewById(R.id.province_wv);  
  60.         mCityPicker = (WheelView) findViewById(R.id.city_wv);  
  61.   
  62.         mProvincePicker.setData(mProvinceList);  
  63.         mProvincePicker.setDefault(0);  
  64.   
  65.         String defaultProvince = mProvinceList.get(0);  
  66.         mCityPicker.setData(mAreaDataUtil.getCityByProvince(defaultProvince));  
  67.         mCityPicker.setDefault(1);  
  68.   
  69.         mProvincePicker.setOnSelectListener(new WheelView.OnSelectListener() {  
  70.             @Override  
  71.             public void endSelect(int id, String text) {  
  72.                 if (text.equals("") || text == null)  
  73.                     return;  
  74.                 if (mCurrProvinceIndex != id) {  
  75.                     mCurrProvinceIndex = id;  
  76.                     String selectProvince = mProvincePicker.getSelectedText();  
  77.                     if (selectProvince == null || selectProvince.equals(""))  
  78.                         return;  
  79.   
  80.                     // get city names by province  
  81.                     ArrayList<String> city = mAreaDataUtil.getCityByProvince(mProvinceList.get(id));  
  82.                     if (city.size() == 0) {  
  83.                         return;  
  84.                     }  
  85.   
  86.                     mCityPicker.setData(city);  
  87.   
  88.                     if (city.size() > 1) {  
  89.                         //if city is more than one,show start index == 1  
  90.                         mCityPicker.setDefault(1);  
  91.                     } else {  
  92.                         mCityPicker.setDefault(0);  
  93.                     }  
  94.                 }  
  95.   
  96.             }  
  97.   
  98.             @Override  
  99.             public void selecting(int id, String text) {  
  100.             }  
  101.         });  
  102.   
  103.         mCityPicker.setOnSelectListener(new WheelView.OnSelectListener() {  
  104.   
  105.             @Override  
  106.             public void endSelect(int id, String text) {  
  107.                 if (text.equals("") || text == null)  
  108.                     return;  
  109.                 if (mCurrCityIndex != id) {  
  110.                     mCurrCityIndex = id;  
  111.                     String selectCity = mCityPicker.getSelectedText();  
  112.                     if (selectCity == null || selectCity.equals(""))  
  113.                         return;  
  114.                     int lastIndex = Integer.valueOf(mCityPicker.getListSize());  
  115.                     if (id > lastIndex) {  
  116.                         mCityPicker.setDefault(lastIndex - 1);  
  117.                     }  
  118.                 }  
  119.             }  
  120.   
  121.             @Override  
  122.             public void selecting(int id, String text) {  
  123.   
  124.             }  
  125.         });  
  126.     }  
  127.   
  128.     public String getProvince() {  
  129.         if (mProvincePicker == null) {  
  130.             return null;  
  131.         }  
  132.         return mProvincePicker.getSelectedText();  
  133.     }  
  134.   
  135.     public String getCity() {  
  136.         if (mCityPicker == null) {  
  137.             return null;  
  138.         }  
  139.         return mCityPicker.getSelectedText();  
  140.     }  
  141.   
  142.     public void setCitySelectedListener(OnCitySelectedListener listener){  
  143.         this.mSelectedListener = listener;  
  144.     }  
  145.   
  146.     public interface OnCitySelectedListener{  
  147.         void citySelected(String province, String city);  
  148.     }  
  149.   
  150.   
  151. }  

其中,AreaDataUtil——全国省份城市操作类

[java] view plain copy
  1. package com.ezreal.ezchat.timeselectview;  
  2.   
  3. import android.content.Context;  
  4.   
  5. import com.ezreal.ezchat.R;  
  6.   
  7. import java.util.ArrayList;  
  8. import java.util.Arrays;  
  9. import java.util.HashMap;  
  10. import java.util.List;  
  11.   
  12. /** 
  13.  * 全国省份城市操作类 
  14.  * 
  15.  * @author 张静 
  16.  */  
  17. public class AreaDataUtil {  
  18.   
  19.     /** 
  20.      * 所有的省市String 
  21.      */  
  22.     public String AREAS_DATA;  
  23.     /** 
  24.      * 一个省份对应多个城市 
  25.      */  
  26.     private String[] single_province_city;  
  27.     /** 
  28.      * 全国省市Map key:省份 |Value:城市集合 
  29.      */  
  30.     private HashMap<String, List<String>> mCityMap = new HashMap<>();  
  31.   
  32.     public AreaDataUtil(Context context) {  
  33.         AREAS_DATA = context.getResources().getString(R.string.province_and_city);  
  34.         splitProvince();  
  35.         getAllCityMap();  
  36.     }  
  37.   
  38.     /** 
  39.      * 将省份和对应城市分割出来 
  40.      * <p/> 
  41.      * 得到:宁夏!!银川!石嘴山!吴忠!固原 
  42.      */  
  43.     private void splitProvince() {  
  44.         single_province_city = AREAS_DATA.split("!!!");  
  45.     }  
  46.   
  47.     /** 
  48.      * 获得全国省份的列表 
  49.      * 
  50.      * @return 
  51.      */  
  52.     public ArrayList<String> getProvinces() {  
  53.         ArrayList<String> provinceList = new ArrayList<>();  
  54.         for (String str : single_province_city) {  
  55.             String province = str.split("!!")[0];  
  56.             provinceList.add(province);  
  57.         }  
  58.         return provinceList;  
  59.     }  
  60.   
  61.     /** 
  62.      * 根据省份获取城市列表 
  63.      * 
  64.      * @return 
  65.      */  
  66.     private void getAllCityMap() {  
  67.         for (String str : single_province_city) {  
  68.             // 得到省份  
  69.             String province = str.split("!!")[0];  
  70.             // 得到当前省份对应的城市  
  71.             String city = str.split("!!")[1];  
  72.             // 分离城市放入集合  
  73.             List<String> cityList = Arrays.asList(city.split("!"));  
  74.             // 省份和城市放入Map中  
  75.             mCityMap.put(province, cityList);  
  76.         }  
  77.     }  
  78.   
  79.     /** 
  80.      * 根据省份查找对应的城市列表 
  81.      * 
  82.      * @return 城市集合 
  83.      */  
  84.     public ArrayList<String> getCityByProvince(String provinceStr) {  
  85.   
  86.         List<String> list = mCityMap.get(provinceStr);  
  87.         ArrayList<String> arrList = new ArrayList<>();  
  88.         for (String city : list) {  
  89.             arrList.add(city);  
  90.         }  
  91.         return arrList;  
  92.     }  
  93.   
  94. }  


写完CityPickerView可以开始写AccountInfoActivity中的设置地区方法

(1)实例化dialog_select_location.xml,并利用AlertDialog创建

[java] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical">  
  7.   
  8.     <com.ezreal.ezchat.timeselectview.CityPickerView  
  9.         android:id="@+id/city_picker"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"/>  
  12. </LinearLayout>  

(2)将设置完后的地区信息与原信息比对,若发生变化,则在个人账户信息中保存新设置,并设置已发生变化

[java] view plain copy
  1. private void setLocation(){  
  2.     View view = LayoutInflater.from(this).inflate(R.layout.dialog_select_location, null);  
  3.     final AlertDialog dialog = new AlertDialog.Builder(this).setView(view).create();  
  4.     CityPickerView cityPickerView = (CityPickerView) view.findViewById(R.id.city_picker);  
  5.     cityPickerView.setCitySelectedListener(new CityPickerView.OnCitySelectedListener() {  
  6.         @Override  
  7.         public void citySelected(String province, String city) {  
  8.             String location = province + "/" + city;  
  9.             if (!location.equals(mTvLocation.getText().toString())) {  
  10.                 mAccountBean.setLocation(location);  
  11.                 mTvLocation.setText(location);  
  12.                 haveAccountChange = true;  
  13.             }  
  14.             dialog.dismiss();  
  15.         }  
  16.     });  
  17.     dialog.show();  
  18. }  



三. 完整AccountInfoActivity.java

[java] view plain copy
  1. package com.ezreal.ezchat.activity;  
  2.   
  3. import android.content.Context;  
  4. import android.content.DialogInterface;  
  5. import android.content.Intent;  
  6. import android.graphics.Bitmap;  
  7. import android.net.Uri;  
  8. import android.os.Bundle;  
  9. import android.provider.MediaStore;  
  10. import android.support.annotation.Nullable;  
  11. import android.support.v7.app.AlertDialog;  
  12. import android.util.Log;  
  13. import android.view.LayoutInflater;  
  14. import android.view.MotionEvent;  
  15. import android.view.View;  
  16. import android.view.inputmethod.InputMethodManager;  
  17. import android.widget.EditText;  
  18. import android.widget.RelativeLayout;  
  19. import android.widget.TextView;  
  20.   
  21. import com.ezreal.ezchat.R;  
  22. import com.ezreal.ezchat.bean.LocalAccountBean;  
  23. import com.ezreal.ezchat.handler.NimUserHandler;  
  24. import com.ezreal.ezchat.utils.Constant;  
  25. import com.ezreal.ezchat.timeselectview.CityPickerView;  
  26. import com.ezreal.ezchat.timeselectview.TimePickerView;  
  27. import com.joooonho.SelectableRoundedImageView;  
  28. import com.netease.nimlib.sdk.AbortableFuture;  
  29. import com.netease.nimlib.sdk.NIMClient;  
  30. import com.netease.nimlib.sdk.RequestCallback;  
  31. import com.netease.nimlib.sdk.nos.NosService;  
  32. import com.netease.nimlib.sdk.uinfo.constant.GenderEnum;  
  33. import com.ezreal.ezchat.commonlibrary.utils.ImageUtils;  
  34. import com.ezreal.ezchat.commonlibrary.utils.TextUtils;  
  35. import com.ezreal.ezchat.commonlibrary.utils.ToastUtils;  
  36.   
  37. import java.io.File;  
  38.   
  39. import butterknife.BindView;  
  40. import butterknife.ButterKnife;  
  41. import io.reactivex.Flowable;  
  42. import io.reactivex.android.schedulers.AndroidSchedulers;  
  43. import io.reactivex.functions.Consumer;  
  44. import io.reactivex.functions.Function;  
  45. import io.reactivex.schedulers.Schedulers;  
  46.   
  47. /** 
  48.  * 账号信息详情页 
  49.  * Created by 张静. 
  50.  */  
  51.   
  52. public class AccountInfoActivity extends BaseActivity implements View.OnClickListener, View.OnTouchListener {  
  53.   
  54.     private static final String TAG = AccountInfoActivity.class.getSimpleName();  
  55.   
  56.     @BindView(R.id.layout_head)  
  57.     RelativeLayout mLayoutHead;  
  58.     @BindView(R.id.iv_head_picture)  
  59.     SelectableRoundedImageView mIvHead;  
  60.     @BindView(R.id.tv_account)  
  61.     TextView mTvAccount;  
  62.     @BindView(R.id.et_account_nick)  
  63.     EditText mEtNick;  
  64.     @BindView(R.id.tv_account_sex)  
  65.     TextView mTvSex;  
  66.     @BindView(R.id.tv_account_birth)  
  67.     TextView mTvBirthDay;  
  68.     @BindView(R.id.tv_account_location)  
  69.     TextView mTvLocation;  
  70.     @BindView(R.id.et_account_signature)  
  71.     EditText mEtSignature;  
  72.     // 个人信息  
  73.     private LocalAccountBean mAccountBean;  
  74.     // 头像本地路径  
  75.     private String mHeadImgPath = "";  
  76.     // 获取图像请求码  
  77.     private static final int SELECT_PHOTO = 30000;  
  78.     private static final int TAKE_PHOTO = 30001;  
  79.     // 信息是否有被更新  
  80.     private boolean haveAccountChange = false;  
  81.     // 是否处于编辑状态  
  82.     private boolean isEditor;  
  83.     // 输入服务,用于显示键盘  
  84.     private InputMethodManager mInputMethodManager;  
  85.   
  86.     @Override  
  87.     protected void onCreate(@Nullable Bundle savedInstanceState) {  
  88.         super.onCreate(savedInstanceState);  
  89.         setStatusBarColor(R.color.app_blue_color);  
  90.         setContentView(R.layout.activity_account_info);  
  91.         setTitleBar("个人信息"truetrue);  
  92.         ButterKnife.bind(this);  
  93.         showData();  
  94.         init();  
  95.     }  
  96.   
  97.     // 显示数据  
  98.     private void showData() {  
  99.         mAccountBean = NimUserHandler.getInstance().getLocalAccount();  
  100.         if (mAccountBean != null) {  
  101.             ImageUtils.setImageByFile(this, mIvHead,  
  102.                     mAccountBean.getHeadImgUrl(), R.mipmap.bg_img_defalut);  
  103.             mTvAccount.setText(mAccountBean.getAccount());  
  104.             mEtNick.setText(mAccountBean.getNick());  
  105.             if (mAccountBean.getGenderEnum() == GenderEnum.FEMALE) {  
  106.                 mTvSex.setText("女");  
  107.             } else if (mAccountBean.getGenderEnum() == GenderEnum.MALE) {  
  108.                 mTvSex.setText("男");  
  109.             } else {  
  110.                 mTvSex.setText("保密");  
  111.             }  
  112.             mEtSignature.setText(mAccountBean.getSignature());  
  113.             String birthday = mAccountBean.getBirthDay();  
  114.             if (TextUtils.isEmpty(birthday)) {  
  115.                 mTvBirthDay.setText("未设置");  
  116.             } else {  
  117.                 mTvBirthDay.setText(birthday);  
  118.             }  
  119.             String location = mAccountBean.getLocation();  
  120.             if (TextUtils.isEmpty(location)) {  
  121.                 mTvLocation.setText("未设置");  
  122.             } else {  
  123.                 mTvLocation.setText(location);  
  124.             }  
  125.         }  
  126.     }  
  127.   
  128.     private void init() {  
  129.         mInputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);  
  130.         // 文字  
  131.         mLayoutHead.setOnClickListener(this);  
  132.         mTvSex.setOnClickListener(this);  
  133.         mTvBirthDay.setOnClickListener(this);  
  134.         mTvLocation.setOnClickListener(this);  
  135.   
  136.         // 标题栏  
  137.         mIvBack.setOnClickListener(this);  
  138.         mIvMenu.setOnClickListener(this);  
  139.   
  140.         // 输入框  
  141.         mEtNick.setOnTouchListener(this);  
  142.         mEtSignature.setOnTouchListener(this);  
  143.   
  144.         // 结束编辑,相当于初始化为非编辑状态  
  145.         finishEdit();  
  146.     }  
  147.   
  148.     @Override  
  149.     public void onClick(View v) {  
  150.         switch (v.getId()) {  
  151.             case R.id.layout_head:  
  152.                 setHeadImg();  
  153.                 break;  
  154.             case R.id.tv_account_sex:  
  155.                 setSex();  
  156.                 break;  
  157.             case R.id.tv_account_location:  
  158.                 setLocation();  
  159.                 break;  
  160.             case R.id.tv_account_birth:  
  161.                 setBirthday();  
  162.                 break;  
  163.             case R.id.iv_back_btn:  
  164.                 this.finish();  
  165.                 break;  
  166.             case R.id.iv_menu_btn:  
  167.                 if (isEditor) {  
  168.                     finishEdit();  
  169.                 } else {  
  170.                     startEdit();  
  171.                 }  
  172.                 break;  
  173.         }  
  174.     }  
  175.   
  176.     // EditText 获取焦点并将光标移动到末尾  
  177.     @Override  
  178.     public boolean onTouch(View v, MotionEvent event) {  
  179.         if (isEditor) {  
  180.             if (v.getId() == R.id.et_account_nick) {  
  181.                 mEtNick.requestFocus();  
  182.                 mEtNick.setSelection(mEtNick.getText().length());  
  183.                 mInputMethodManager.showSoftInput(mEtNick, 0);  
  184.             } else if (v.getId() == R.id.et_account_signature) {  
  185.                 mEtSignature.requestFocus();  
  186.                 mEtSignature.setSelection(mEtSignature.getText().length());  
  187.                 mInputMethodManager.showSoftInput(mEtSignature, 0);  
  188.             }  
  189.             return true;  
  190.         }  
  191.         return false;  
  192.     }  
  193.   
  194.     /** 
  195.      * 启动编辑 
  196.      */  
  197.     private void startEdit() {  
  198.         mIvMenu.setImageResource(R.mipmap.done);  
  199.         // 可点击  
  200.         mLayoutHead.setClickable(true);  
  201.         mTvSex.setClickable(true);  
  202.         mTvLocation.setClickable(true);  
  203.         mTvBirthDay.setClickable(true);  
  204.         // 可编辑  
  205.         mEtNick.setFocusable(true);  
  206.         mEtNick.setFocusableInTouchMode(true);  
  207.         mEtSignature.setFocusable(true);  
  208.         mEtSignature.setFocusableInTouchMode(true);  
  209.   
  210.         isEditor = true;  
  211.     }  
  212.   
  213.     /** 
  214.      * 结束编辑,判断是否有修改,决定是否同步缓存数据 
  215.      */  
  216.     private void finishEdit() {  
  217.         if (!mEtNick.getText().toString()  
  218.                 .equals(mAccountBean.getNick())) {  
  219.             mAccountBean.setNick(mEtNick.getText().toString());  
  220.             haveAccountChange = true;  
  221.         }  
  222.   
  223.         if (!mEtSignature.getText().toString()  
  224.                 .equals(mAccountBean.getSignature())) {  
  225.             mAccountBean.setSignature(mEtSignature.getText().toString());  
  226.             haveAccountChange = true;  
  227.         }  
  228.   
  229.         if (haveAccountChange) {  
  230.   
  231.             // 将数据更新到缓存  
  232.             NimUserHandler.getInstance().setLocalAccount(mAccountBean);  
  233.             // 通知handler将数据更新到服务器  
  234.             NimUserHandler.getInstance().syncChange2Service();  
  235.   
  236.             haveAccountChange = false;  
  237.         }  
  238.   
  239.         mIvMenu.setImageResource(R.mipmap.editor);  
  240.         // 不可点击  
  241.         mLayoutHead.setClickable(false);  
  242.         mTvSex.setClickable(false);  
  243.         mTvLocation.setClickable(false);  
  244.         mTvBirthDay.setClickable(false);  
  245.         // 不可编辑  
  246.         mEtNick.setFocusable(false);  
  247.         mEtNick.setFocusableInTouchMode(false);  
  248.         mEtSignature.setFocusable(false);  
  249.         mEtSignature.setFocusableInTouchMode(false);  
  250.   
  251.         isEditor = false;  
  252.     }  
  253.   
  254.     /** 
  255.      * 设置性别 
  256.      */  
  257.     private void setSex(){  
  258.         final int[] selected = new int[1];  
  259.         if (mAccountBean.getGenderEnum() == GenderEnum.MALE) {  
  260.             selected[0] = 0;  
  261.         } else if (mAccountBean.getGenderEnum() == GenderEnum.FEMALE) {  
  262.             selected[0] = 1;  
  263.         } else {  
  264.             selected[0] = 2;  
  265.         }  
  266.         final String[] items = new String[]{"男""女""保密"};  
  267.         new AlertDialog.Builder(this)  
  268.                 .setTitle("性别")  
  269.                 .setSingleChoiceItems(items, selected[0], new DialogInterface.OnClickListener() {  
  270.                     @Override  
  271.                     public void onClick(DialogInterface dialog, int which) {  
  272.                         if (which != selected[0]) {  
  273.                             if (which == 0) {  
  274.                                 mAccountBean.setGenderEnum(GenderEnum.MALE);  
  275.                                 mTvSex.setText("男");  
  276.                             } else if (which == 1) {  
  277.                                 mAccountBean.setGenderEnum(GenderEnum.FEMALE);  
  278.                                 mTvSex.setText("女");  
  279.                             } else {  
  280.                                 mAccountBean.setGenderEnum(GenderEnum.UNKNOWN);  
  281.                                 mTvSex.setText("保密");  
  282.                             }  
  283.                             haveAccountChange = true;  
  284.                         }  
  285.                         dialog.dismiss();  
  286.                     }  
  287.                 }).create().show();  
  288.     }  
  289.   
  290.     /** 
  291.      * 设置生日 
  292.      */  
  293.     private void setBirthday() {  
  294.         View view = LayoutInflater.from(this).inflate(R.layout.dialog_select_birthday, null);  
  295.         final AlertDialog dialog = new AlertDialog.Builder(this).setView(view).create();  
  296.         TimePickerView timePickerView = (TimePickerView) view.findViewById(R.id.date_picker);  
  297.         timePickerView.setSelectedListener(new TimePickerView.OnDateSelectedListener() {  
  298.             @Override  
  299.             public void selectedDate(int year, int month, int day) {  
  300.                 String yearString = String.valueOf(year);  
  301.                 String monthString = String.valueOf(month);  
  302.                 String dayString = String.valueOf(day);  
  303.                 if (monthString.length() == 1){  
  304.                     monthString = "0" + monthString;  
  305.                 }  
  306.                 if (dayString.length() == 1){  
  307.                     dayString = "0" + dayString;  
  308.                 }  
  309.                 String birthday = String.format("%s-%s-%s", yearString, monthString, dayString);  
  310.                 if (!birthday.equals(mTvBirthDay.getText().toString())) {  
  311.                     mAccountBean.setBirthDay(birthday);  
  312.                     mTvBirthDay.setText(birthday);  
  313.                     haveAccountChange = true;  
  314.                 }  
  315.                 dialog.dismiss();  
  316.   
  317.             }  
  318.         });  
  319.         dialog.show();  
  320.     }  
  321.   
  322.     /** 
  323.      * 设置地区 
  324.      */  
  325.     private void setLocation(){  
  326.         View view = LayoutInflater.from(this).inflate(R.layout.dialog_select_location, null);  
  327.         final AlertDialog dialog = new AlertDialog.Builder(this).setView(view).create();  
  328.         CityPickerView cityPickerView = (CityPickerView) view.findViewById(R.id.city_picker);  
  329.         cityPickerView.setCitySelectedListener(new CityPickerView.OnCitySelectedListener() {  
  330.             @Override  
  331.             public void citySelected(String province, String city) {  
  332.                 String location = province + "/" + city;  
  333.                 if (!location.equals(mTvLocation.getText().toString())) {  
  334.                     mAccountBean.setLocation(location);  
  335.                     mTvLocation.setText(location);  
  336.                     haveAccountChange = true;  
  337.                 }  
  338.                 dialog.dismiss();  
  339.             }  
  340.         });  
  341.         dialog.show();  
  342.     }  
  343.   
  344.     /** 
  345.      * 设置头像,拍照或选择照片 
  346.      */  
  347.     private void setHeadImg() {  
  348.         View view = LayoutInflater.from(this).inflate(R.layout.dialog_set_head_img, null);  
  349.         final AlertDialog alertDialog = new AlertDialog.Builder(this).setView(view).create();  
  350.         TextView take = (TextView) view.findViewById(R.id.tv_take_photo);  
  351.         TextView select = (TextView) view.findViewById(R.id.tv_select_img);  
  352.         take.setOnClickListener(new View.OnClickListener() {  
  353.             @Override  
  354.             public void onClick(View v) {  
  355.                 alertDialog.dismiss();  
  356.                 try {  
  357.                     Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
  358.                     mHeadImgPath = Constant.APP_CACHE_PATH + File.separator + "image"  
  359.                             + File.separator + mAccountBean.getAccount() + ".jpg";  
  360.                     Uri uri = Uri.fromFile(new File(mHeadImgPath));  
  361.                     intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);//设置图像文件名  
  362.                     startActivityForResult(intent, TAKE_PHOTO);  
  363.                 } catch (Exception e) {  
  364.                     ToastUtils.showMessage(AccountInfoActivity.this"启动相机出错!请重试");  
  365.                     e.printStackTrace();  
  366.                 }  
  367.   
  368.             }  
  369.         });  
  370.         select.setOnClickListener(new View.OnClickListener() {  
  371.             @Override  
  372.             public void onClick(View v) {  
  373.                 alertDialog.dismiss();  
  374.                 Intent intent = new Intent(Intent.ACTION_PICK,  
  375.                         MediaStore.Images.Media.EXTERNAL_CONTENT_URI);  
  376.                 intent.setType("image/*");  
  377.                 startActivityForResult(Intent.createChooser(intent, "选择头像图片"), SELECT_PHOTO);  
  378.             }  
  379.         });  
  380.         alertDialog.show();  
  381.     }  
  382.   
  383.   
  384.     @Override  
  385.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  386.         super.onActivityResult(requestCode, resultCode, data);  
  387.         if (resultCode == RESULT_OK) {  
  388.             if (requestCode == TAKE_PHOTO) {  
  389.                 dealTakePhotoResult();  
  390.             } else if (requestCode == SELECT_PHOTO) {  
  391.                 mHeadImgPath = ImageUtils.getFilePathFromUri(AccountInfoActivity.this, data.getData());  
  392.                 dealTakePhotoResult();  
  393.             }  
  394.         }  
  395.     }  
  396.   
  397.     /**  
  398.      * 处理拍照回传数据  
  399.      */  
  400.     private void dealTakePhotoResult() {  
  401.         Flowable.just(mHeadImgPath)  
  402.                 .map(new Function<String, Bitmap>() {  
  403.                     @Override  
  404.                     public Bitmap apply(String path) throws Exception {  
  405.                         // 调整旋转角度,压缩  
  406.                         int bitmapDegree = ImageUtils.getBitmapDegree(mHeadImgPath);  
  407.                         Bitmap bitmap = ImageUtils.getBitmapFromFile(mHeadImgPath, 600400);  
  408.                         bitmap = ImageUtils.rotateBitmapByDegree(bitmap, bitmapDegree);  
  409.                         ImageUtils.saveBitmap2Jpg(bitmap, path);  
  410.                         return bitmap;  
  411.                     }  
  412.                 })  
  413.                 .subscribeOn(Schedulers.io())  
  414.                 .observeOn(AndroidSchedulers.mainThread())  
  415.                 .subscribe(new Consumer<Bitmap>() {  
  416.                     @Override  
  417.                     public void accept(Bitmap bitmap) throws Exception {  
  418.                         // 显示,记录更新,同步至网易云服务器  
  419.                         if (bitmap != null) {  
  420.                             // 上传至服务器  
  421.                             uploadHeadImg(bitmap);  
  422.                         }  
  423.                     }  
  424.                 });  
  425.     }  
  426.   
  427.     /** 
  428.      * 将头像数据上传至网易云服务器存储,获取服务器返回URL 
  429.      */  
  430.     private void uploadHeadImg(final Bitmap bitmap) {  
  431.         AbortableFuture<String> upload = NIMClient.getService(NosService.class)  
  432.                 .upload(new File(mHeadImgPath), "image/ipeg");  
  433.         upload.setCallback(new RequestCallback() {  
  434.             @Override  
  435.             public void onSuccess(Object param) {  
  436.                 Log.e(TAG,"uploadHeadImg onSuccess url = " + param.toString());  
  437.                 mIvHead.setImageBitmap(bitmap);  
  438.                 // 保存图片本地路径和服务器路径  
  439.                 mAccountBean.setHeadImgUrl(param.toString());  
  440.                 haveAccountChange = true;  
  441.             }  
  442.   
  443.             @Override  
  444.             public void onFailed(int code) {  
  445.                 Log.e(TAG,"uploadHeadImg onFailed code " + code);  
  446.                 ToastUtils.showMessage(AccountInfoActivity.this,  
  447.                         "修改失败,头像上传失败,code:" + code);  
  448.             }  
  449.   
  450.             @Override  
  451.             public void onException(Throwable exception) {  
  452.                 Log.e(TAG,"uploadHeadImg onException message " + exception.getMessage());  
  453.                 ToastUtils.showMessage(AccountInfoActivity.this,  
  454.                         "修改失败,图像上传出错:" + exception.getMessage());  
  455.             }  
  456.         });  
  457.     }  
  458.   
  459. }  

阅读更多
上一篇实训第七周(1)
下一篇实训第八周(1)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页