概述
移动端注册功能实现
微信的注册界面每一个文本段都有下划线且默认颜色都是灰色,当其中一个文本段获取焦点会将下划线的颜色变为绿色,而且文本输入框的光标也是绿色的,还有在文本输入框没有全部输入的情况下,按钮是不能点击的,只有当文本输入框全部输入的情况下才能点击且此时按钮会变成绿色。除了这些UI功能外,当点击注册按钮是还会把表单数据发送给服务器
创建activity Reigister.java
activity Reigister.java
package com.example.wxchatdemo; import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.annotation.Nullable; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.Toast; import com.example.wxchatdemo.tools.IEditTextChangeListener; import com.example.wxchatdemo.tools.RandomUserName; import com.example.wxchatdemo.tools.WorksSizeCheckUtil; import org.json.JSONObject; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Reigister extends AppCompatActivity { //声明组件 private EditText username; private EditText phone; private EditText password; private Button button; //随机微信号 private String randomNumber; //自定义一个UI修改机制 private MyHander myhander = new MyHander(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.register); //设置布局 /* 隐藏自带标题*/ ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.hide(); } if (Build.VERSION.SDK_INT >= 21) { View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN //全屏显示 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; //因为背景为浅色所以将状态栏字体设置为黑色 decorView.setSystemUiVisibility(option); getWindow().setStatusBarColor(Color.TRANSPARENT); } initViews(); // 初始化布局元素 // 设置注册按钮是否可点击 if (username.getText() + "" == "" || phone.getText() + "" == "" || password.getText() + "" == "") { button.setEnabled(false); } else { button.setEnabled(true); } inputFocus(); //监听EditView变色 buttonChangeColor(); //监听登录按钮变色 //button的点击事件事件 button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { /*判断输入的手机号格式对不对,对的话开一个线程完成网络请求操作*/ Pattern pattern = Pattern .compile("^(13[0-9]|15[0-9]|153|15[6-9]|180|18[23]|18[5-9])\d{8}$"); Matcher matcher = pattern.matcher(phone.getText()); if (matcher.matches()) { // 开一个线程完成网络请求操作 new Thread(new Runnable() { @Override public void run() { httpUrlConnPost(Reigister.this.username.getText() + "", phone.getText() + "", password.getText() + ""); } }).start(); } else { Toast.makeText(getApplicationContext(), "手机格式错误", Toast.LENGTH_LONG).show(); } } }); } /*在这里面获取到每个需要用到的控件的实例*/ @SuppressLint("NewApi") public void initViews() { // 得到所有的组件 username = (EditText) this.findViewById(R.id.reg_name); phone = (EditText) this.findViewById(R.id.reg_phone); password = (EditText) this.findViewById(R.id.reg_passwd); button = (Button) this.findViewById(R.id.reg_button); } /*监听EditView变色*/ public void inputFocus() { username.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { // 此处为得到焦点时的处理内容 ImageView imageView = (ImageView) findViewById(R.id.reg_diver1); imageView.setBackgroundResource(R.color.input_dvier_focus); } else { // 此处为失去焦点时的处理内容 ImageView imageView = (ImageView) findViewById(R.id.reg_diver1); imageView.setBackgroundResource(R.color.input_dvier); } } }); phone.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { // 此处为得到焦点时的处理内容 ImageView imageView = (ImageView) findViewById(R.id.reg_diver2); imageView.setBackgroundResource(R.color.input_dvier_focus); } else { // 此处为失去焦点时的处理内容 ImageView imageView = (ImageView) findViewById(R.id.reg_diver2); imageView.setBackgroundResource(R.color.input_dvier); } } }); password.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { // 此处为得到焦点时的处理内容 ImageView imageView = (ImageView) findViewById(R.id.reg_diver3); imageView.setBackgroundResource(R.color.input_dvier_focus); } else { // 此处为失去焦点时的处理内容 ImageView imageView = (ImageView) findViewById(R.id.reg_diver3); imageView.setBackgroundResource(R.color.input_dvier); } } }); } /*监听登录按钮变色*/ public void buttonChangeColor() { //创建工具类对象 把要改变颜色的Button先传过去 WorksSizeCheckUtil.textChangeListener textChangeListener = new WorksSizeCheckUtil.textChangeListener(button); textChangeListener.addAllEditText(username, phone, password);//把所有要监听的EditText都添加进去 //接口回调 在这里拿到boolean变量 根据isHasContent的值决定 Button应该设置什么颜色 WorksSizeCheckUtil.setChangeListener(new IEditTextChangeListener() { @Override public void textChange(boolean isHasContent) { if (isHasContent) { button.setBackgroundResource(R.drawable.login_button_focus); button.setTextColor(getResources().getColor(R.color.loginButtonTextFouse)); } else { button.setBackgroundResource(R.drawable.login_button_shape); button.setTextColor(getResources().getColor(R.color.loginButtonText)); } } }); } /*发送请求的主要方法*/ public void httpUrlConnPost(String name, String phone, String password) { /*使用工具类生成随机的微信号*/ RandomUserName ran = new RandomUserName(); randomNumber = ran.generate(); HttpURLConnection urlConnection = null; URL url; try { // 请求的URL地地址 url = new URL( "http://100.2.178.10:8080/AndroidServer_war_exploded/Reigister"); urlConnection = (HttpURLConnection) url.openConnection();// 打开http连接 urlConnection.setConnectTimeout(3000);// 连接的超时时间 urlConnection.setUseCaches(false);// 不使用缓存 // urlConnection.setFollowRedirects(false);是static函数,作用于所有的URLConnection对象。 urlConnection.setInstanceFollowRedirects(true);// 是成员函数,仅作用于当前函数,设置这个连接是否可以被重定向 urlConnection.setReadTimeout(3000);// 响应的超时时间 urlConnection.setDoInput(true);// 设置这个连接是否可以写入数据 urlConnection.setDoOutput(true);// 设置这个连接是否可以输出数据 urlConnection.setRequestMethod("POST");// 设置请求的方式 urlConnection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");// 设置消息的类型 urlConnection.connect();// 连接,从上述至此的配置必须要在connect之前完成,实际上它只是建立了一个与服务器的TCP连接 JSONObject json = new JSONObject();// 创建json对象 json.put("number", URLEncoder.encode(randomNumber, "UTF-8"));// 使用URLEncoder.encode对特殊和不可见字符进行编码 json.put("name", URLEncoder.encode(name, "UTF-8")); json.put("phone", URLEncoder.encode(phone, "UTF-8")); json.put("password", URLEncoder.encode(password, "UTF-8"));// 把数据put进json对象中 String jsonstr = json.toString();// 把JSON对象按JSON的编码格式转换为字符串 // ------------字符流写入数据------------ OutputStream out = urlConnection.getOutputStream();// 输出流,用来发送请求,http请求实际上直到这个函数里面才正式发送出去 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));// 创建字符流对象并用高效缓冲流包装它,便获得最高的效率,发送的是字符串推荐用字符流,其它数据就用字节流 bw.write(jsonstr);// 把json字符串写入缓冲区中 bw.flush();// 刷新缓冲区,把数据发送出去,这步很重要 out.close(); bw.close();// 使用完关闭 Log.i("aa", urlConnection.getResponseCode() + ""); //以下判斷是否訪問成功,如果返回的状态码是200则说明访问成功 if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {// 得到服务端的返回码是否连接成功 // ------------字符流读取服务端返回的数据------------ InputStream in = urlConnection.getInputStream(); BufferedReader br = new BufferedReader( new InputStreamReader(in)); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = br.readLine()) != null) {// BufferedReader特有功能,一次读取一行数据 buffer.append(str); } in.close(); br.close(); JSONObject rjson = new JSONObject(buffer.toString()); Log.i("aa", "rjson=" + rjson);// rjson={"json":true} boolean result = rjson.getBoolean("json");// 从rjson对象中得到key值为"json"的数据,这里服务端返回的是一个boolean类型的数据 System.out.println("json:===" + result); //如果服务器端返回的是true,则说明注册成功,否则注册失败 if (result) {// 判断结果是否正确 //在Android中http请求,必须放到线程中去作请求,但是在线程中不可以直接修改UI,只能通过hander机制来完成对UI的操作 myhander.sendEmptyMessage(1); Log.i("用户:", "注册成功"); } else { myhander.sendEmptyMessage(2); Log.i("用户:", "手机号已被注册"); } } else { myhander.sendEmptyMessage(2); } } catch (Exception e) { e.printStackTrace(); Log.i("aa", e.toString()); myhander.sendEmptyMessage(2); } finally { urlConnection.disconnect();// 使用完关闭TCP连接,释放资源 } } // 在Android中不可以在线程中直接修改UI,只能借助Handler机制来完成对UI的操作 class MyHander extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //判断hander的内容是什么,如果是1则说明注册成功,如果是2说明注册失败 switch (msg.what) { case 1: Log.i("aa", msg.what + ""); Toast.makeText(getApplicationContext(), "注册成功", Toast.LENGTH_SHORT).show(); /*跳转到登录页面并把微信号也传过去*/ Intent intent = new Intent(); intent.putExtra("weixin_number", randomNumber); intent.setClass(com.example.wxchatdemo.Reigister.this, LoginUser.class); startActivity(intent); com.example.wxchatdemo.Reigister.this.finish(); //结束当前activity break; case 2: Log.i("aa", msg.what + ""); //這是一個提示消息 Toast.makeText(getApplicationContext(), "手机号已被注册", Toast.LENGTH_LONG).show(); } } } //返回按钮处理事件 public void rigister_activity_back(View v) { /*跳转到微信启动页*/ Intent intent = new Intent(); intent.setClass(com.example.wxchatdemo.Reigister.this, Welcome.class); startActivity(intent); com.example.wxchatdemo.Reigister.this.finish(); //结束当前activity } }
上面用到的两个工具类和一个接口,其中一个工具类是监听按钮变色的,是上面接口的实现类(用到面向接口编程思想,把接口作为自己的成员变量,实现接口回调)另一个工具类是随机生成微信号,代码就不全阐述了,注释都有说明,主要讲一下生成微信号的工具类
生成随机微信号工具类:
注册微信时系统会给我们随机生成一个微信号且不能重复的,所以上面用一个工具类RandomUserName(),通过调用generate()方法可以返回一个随机的微信号(封装的细节就不说了,等下后面附上工具类代码都有注释。这个功能应该是在服务端实现的,我懒,所以简单在移动端搞了),然后和注册表单一起发给服务器
在创建工具类接口之前,可以先创建一个存放工具类和接口的包,因为后面会继续完善微信功能时会创建很多工具类,方便管理
创建存放工具类和接口包
创建监听按钮变色工具类WorksSizeCheckUtil.java
WorksSizeCheckUtil.java
package com.example.wxchatdemo.tools; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.widget.Button; import android.widget.EditText; public class WorksSizeCheckUtil { static IEditTextChangeListener mChangeListener; public static void setChangeListener(IEditTextChangeListener changeListener) { mChangeListener = changeListener; } //检测输入框是否都输入了内容 从而改变按钮的是否可点击 public static class textChangeListener { private Button button; private EditText[] editTexts; public textChangeListener(Button button) { this.button = button; } public textChangeListener addAllEditText(EditText... editTexts) { this.editTexts = editTexts; initEditListener(); return this; } private void initEditListener() { //调用了遍历editext的方法 for (EditText editText : editTexts) { editText.addTextChangedListener(new textChange()); } } // edit输入的变化来改变按钮的是否点击 private class textChange implements TextWatcher { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { if (checkAllEdit()) { //所有EditText有值了 mChangeListener.textChange(true); button.setEnabled(true); } else { //所有EditText值为空 button.setEnabled(false); mChangeListener.textChange(false); } } @Override public void afterTextChanged(Editable editable) { } } //检查所有的edit是否输入了数据 private boolean checkAllEdit() { for (EditText editText : editTexts) { if (!TextUtils.isEmpty(editText.getText() + "")) { continue; } else { return false; } } return true; } } }
创建对应的接口IEditTextChangeListener.java
IEditTextChangeListener.java
package com.example.wxchatdemo.tools; public interface IEditTextChangeListener { void textChange(boolean isHasContent); }
创建随机生成微信号的工具类RandomUserName.java
RandomUserName.java
package com.example.wxchatdemo.tools; import java.util.Random; public class RandomUserName { private static final char[] eng_char = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}; private static final String[] first_name = new String[]{"zhao","qian","sun","li","zhou","wang","wu","zheng","feng","chen","chu","wei","jiang","shen","yang" ,"zhu","qin","you","xu","he","shi","zhan","kong","cao","xie","jin","shu","fang","yuan"}; private static final String[] tel_head = new String[]{"13","18","15"}; private static final String[] email_suffix = new String[]{"@gmail.com","@yahoo.com","@msn.com","@hotmail.com","@aol.com","@ask.com" ,"@live.com","@qq.com","@0355.net","@163.com","@163.net","@263.net" ,"@3721.net","@yeah.net","@googlemail.com","@126.com","@sina.com","@sohu.com","@yahoo.com.cn"}; private Random random = new Random(); public String generate(){ StringBuilder uName = new StringBuilder(); int randomType = random.nextInt(Integer.MAX_VALUE)%3; switch (randomType) { case 0: // firstName + randomSecName + birthday uName.append(first_name[random.nextInt(Integer.MAX_VALUE)%first_name.length]) .append(eng_char[random.nextInt(Integer.MAX_VALUE)%eng_char.length]); if(random.nextInt(Integer.MAX_VALUE)%2 == 0){ uName.append(eng_char[random.nextInt(Integer.MAX_VALUE)%eng_char.length]); } // birthday if(random.nextInt(Integer.MAX_VALUE)%2 == 0){ uName.append(String.valueOf(2014 - (random.nextInt(Integer.MAX_VALUE)%(50-15) + 15))); // 大于15小于50岁 } if(random.nextInt(Integer.MAX_VALUE)%2 == 0){ int month = random.nextInt(Integer.MAX_VALUE)%11 + 1; int day = random.nextInt(Integer.MAX_VALUE)%29 + 1; if(month < 10) uName.append("0"); uName.append(month); if(day < 10) uName.append("0"); uName.append(day); } if(random.nextInt(Integer.MAX_VALUE%4) == 0){// add email suffix , 1/4 rate uName.append(email_suffix[random.nextInt(Integer.MAX_VALUE)%email_suffix.length]); } break; case 1: // tel uName.append(tel_head[random.nextInt(Integer.MAX_VALUE)%tel_head.length]) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10); break; case 2: // qq uName.append(random.nextInt(Integer.MAX_VALUE)%9+1) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10) .append(random.nextInt(Integer.MAX_VALUE)%10); int lenth = 0; while(random.nextInt(Integer.MAX_VALUE)%2 == 0){ if(lenth > 6) break; uName.append(random.nextInt(Integer.MAX_VALUE)%10); lenth ++; } break; default: break; } return uName.toString(); } }
创建两个shapre文件,自定义按钮形状,实现按钮在所有文本框获取输入时显示的背景和至少有一个没有输入情况下显示的背景
按钮在所以文本框获取输入时显示的shapre文件login_button_focus.xml
login_button_focus.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/loginButtonBackgroundFouse" /><!-- 填充的颜色 --> <!-- 设置按钮的四个角为弧形 --> <!-- android:radius 弧形的半径 --> <corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp" android:topLeftRadius="6dp" android:topRightRadius="6dp" /> <!-- 边框粗细及颜色 --> </shape>
按钮在所以文本框至少有一个没有获取输入时显示的shapre文件login_button_shape.xml
login_button_shape.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/loginButtonBackgroundNotFouse" /><!-- 填充的颜色 --> <!-- 设置按钮的四个角为弧形 --> <!-- android:radius 弧形的半径 --> <corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp" android:topLeftRadius="6dp" android:topRightRadius="6dp" /> <!-- 边框粗细及颜色 --> </shape>
创建activity Reigister.java对应的布局文件reigister.xml
reigister.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/title" android:orientation="vertical"> <ImageView android:layout_width="17dp" android:layout_height="17dp" android:layout_marginLeft="20dp" android:layout_marginTop="45dp" android:onClick="rigister_activity_back" android:src="@drawable/backpay" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:layout_marginTop="25dp" android:text="手机号注册" android:textColor="@color/loginText" android:textSize="25sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="40dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:text="昵称" android:textColor="@color/loginText" android:textSize="16sp" /> <EditText android:id="@+id/reg_name" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginLeft="55dp" android:background="@null" android:hint="例如:陈晨" android:singleLine="true" android:textColorHint="@color/textColorHint" android:textCursorDrawable="@drawable/edit_cursor_color" android:textSize="16sp" /> </LinearLayout> <ImageView android:id="@+id/reg_diver1" android:layout_width="320dp" android:layout_height="1dp" android:layout_gravity="center_horizontal" android:layout_marginTop="17dp" android:background="@color/input_dvier" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:text="手机号" android:textColor="@color/loginText" android:textSize="16sp" /> <EditText android:id="@+id/reg_phone" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginLeft="36dp" android:background="@null" android:hint="请填写手机号" android:singleLine="true" android:textColorHint="@color/textColorHint" android:textCursorDrawable="@drawable/edit_cursor_color" android:textSize="16sp" /> </LinearLayout> <ImageView android:id="@+id/reg_diver2" android:layout_width="320dp" android:layout_height="1dp" android:layout_gravity="center_horizontal" android:layout_marginTop="17dp" android:background="@color/input_dvier" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:text="密码" android:textColor="@color/loginText" android:textSize="16sp" /> <EditText android:id="@+id/reg_passwd" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginLeft="55dp" android:background="@null" android:hint="请填写密码" android:password="false" android:singleLine="true" android:textColorHint="@color/textColorHint" android:textCursorDrawable="@drawable/edit_cursor_color" android:textSize="16sp" /> </LinearLayout> <ImageView android:id="@+id/reg_diver3" android:layout_width="320dp" android:layout_height="1dp" android:layout_gravity="center_horizontal" android:layout_marginTop="17dp" android:background="@color/input_dvier" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="40dp" android:gravity="center_horizontal"> <Button android:id="@+id/reg_button" android:layout_width="321dp" android:layout_height="48dp" android:background="@drawable/login_button_shape" android:text="注册" android:textColor="@color/loginButtonText" android:textSize="16sp" /> </LinearLayout> </LinearLayout>
创建shape文件edit_cursor_color.xml,自定义光标颜色
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <size android:width="1dp" /> <size android:height="10dp"/> <solid android:color="@color/loginButtonBackgroundFouse" /> </shape>
在colors.xml文件中声明所用到颜色
colors.xml
<color name="input_dvier">#D8D8D8</color> <color name="input_dvier_focus">#1BB879</color> <color name="loginButtonText">#B5B2B2</color> <color name="loginButtonTextFouse">#FFFFFF</color> <color name="loginButtonBackgroundFouse">#07C160</color> <color name="loginButtonBackgroundNotFouse">#D4D8D5</color> <color name="title">#EDEDED</color> <color name="loginText">#5A5959</color> <color name="textColorHint">#DDDDDD</color>
在AndroidMainfest.xml中声明注册activity
测试
虽然服务器代码还没实现,但是还是可以测试相应功能,先把注册成功后跳转到登录页面的那段代码注释点
然后再上篇微信启动页实现文章中的activity Welcome.java注册按钮点击后跳转的activity代码注释取消掉
启动项目测试
虽然输入正确的手机号格式,但是服务器功能还没实现,所以不论怎样输入手机号都会出现手机号已被注册,因为把请求服务器出现错误时执行的代码段写了手机号已被注册的提示,这样做的原因是服务器出现错误就是手机号重复了。
总结
这篇关于微信demo的文章就到这里了,希望大家可以多多关注靠谱客的更多精彩内容!
最后
以上就是悲凉金鱼为你收集整理的android 仿微信demo——注册功能实现(移动端)的全部内容,希望文章能够帮你解决android 仿微信demo——注册功能实现(移动端)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复