android 仿微信demo————微信启动界面实现
android 仿微信demo————注册功能实现(移动端)
android 仿微信demo————注册功能实现(服务端)
android 仿微信demo————登录功能实现(移动端)
android 仿微信demo————登录功能实现(服务端)
android 仿微信demo————微信主界面实现
android 仿微信demo————微信消息界面实现(移动端)
android 仿微信demo————微信消息界面实现(服务端)
android 仿微信demo————微信通讯录界面功能实现(移动端,服务端)
android 仿微信demo————微信发现界面实现
android 仿微信demo————微信顶部操作栏界面实现
android 仿微信demo————微信顶部操作栏搜索按钮实现(查询通讯录好友功能)
android 仿微信demo————微信顶部操作栏加号按钮实现(弹出子菜单)
android 仿微信demo————注册功能完善添加头像功能(移动端)
源码获取点我,记得给个start哦
在前面文章只是简单实现前端的表单,以及表单处理,并没有实现头像功能,由于第四个界面个人信息有个头像数据,所以这一篇在移动端实现添加头像功能,只需在前面的代码做相应添加即可
移动端
修改注册布局
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"><ImageViewandroid:layout_width="17dp"android:layout_height="17dp"android:layout_marginLeft="20dp"android:layout_marginTop="45dp"android:onClick="rigister_activity_back"android:src="@drawable/backpay" /><TextViewandroid: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" /><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="64dp" ><ImageViewandroid:id="@+id/iv_photo"android:layout_width="64dp"android:layout_height="64dp"android:layout_alignParentRight="true"android:layout_marginRight="10dp"android:src="@drawable/icon_register" /><EditTextandroid:id="@+id/reg_name"android:layout_width="match_parent"android:layout_height="48dp"android:layout_alignParentBottom="true"android:layout_marginRight="10dp"android:layout_toLeftOf="@id/iv_photo"android:background="@null"android:gravity="center_vertical"android:hint="例如:陈晨"android:paddingLeft="120dp"android:singleLine="true"android:textColorHint="#DDDDDD"android:textSize="16sp" /><TextViewandroid:layout_width="90dp"android:layout_height="48dp"android:layout_alignLeft="@id/reg_name"android:layout_alignParentBottom="true"android:layout_marginLeft="30dp"android:gravity="center_vertical"android:text="昵称"android:textColor="#353535"android:textSize="16sp" /></RelativeLayout><ImageViewandroid:id="@+id/reg_diver1"android:layout_width="320dp"android:layout_height="1dp"android:layout_gravity="center_horizontal"android:background="@color/input_dvier" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:text="手机号"android:textColor="@color/loginText"android:textSize="16sp" /><EditTextandroid:id="@+id/reg_phone"android:layout_width="200dp"android:layout_height="wrap_content"android:layout_marginLeft="39dp"android:background="@null"android:hint="请填写手机号"android:singleLine="true"android:textColorHint="@color/textColorHint"android:textCursorDrawable="@drawable/edit_cursor_color"android:textSize="16sp" /></LinearLayout><ImageViewandroid: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" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:text="密码"android:textColor="@color/loginText"android:textSize="16sp" /><EditTextandroid:id="@+id/reg_passwd"android:layout_width="200dp"android:layout_height="wrap_content"android:layout_marginLeft="55dp"android:background="@null"android:hint="请填写密码"android:inputType="textPassword"android:singleLine="true"android:textColorHint="@color/textColorHint"android:textCursorDrawable="@drawable/edit_cursor_color"android:textSize="16sp" /></LinearLayout><ImageViewandroid: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" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="40dp"android:gravity="center_horizontal"><Buttonandroid: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>
上面代码改动的地方就是添加了一个ImageView组件,可以点击添加头像并显示,点击事件在注册activity中处理
修改注册activity
package com.example.wxchatdemo;import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
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.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
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.text.SimpleDateFormat;
import java.util.Date;
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 ImageView iv_photo;private static final int PHOTO_REQUEST_GALLERY = 2;// 从相册中选择private static final int PHOTO_REQUEST_CUT = 3;// 结果private String imageName;//随机微信号private String randomNumber;//自定义一个UI修改机制private MyHander myhander = new MyHander();private boolean flag = false; //表单提交成后改成true,开始传输图片@Overrideprotected 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() {@Overridepublic 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()) {flag = true;// 开一个线程完成网络请求操作new Thread(new Runnable() {@Overridepublic void run() {httpUrlConnPost(Reigister.this.username.getText() + "",phone.getText() + "", password.getText() + "");}}).start();} else {Toast.makeText(getApplicationContext(), "手机格式错误", Toast.LENGTH_LONG).show();}if (flag) {flag = false;// 开一个线程完成网络请求操作new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);httpUrlConnPostImage();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}});//头像点击事件iv_photo.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {getNowTime();imageName = getNowTime() + ".png";Intent intent = new Intent(Intent.ACTION_PICK, null);intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");startActivityForResult(intent, PHOTO_REQUEST_GALLERY);}});}@SuppressLint("SdCardPath")@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {if (resultCode == RESULT_OK) {switch (requestCode) {case PHOTO_REQUEST_GALLERY:if (data != null)startPhotoZoom(data.getData(), 480);break;case PHOTO_REQUEST_CUT:Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/zhuojiajin/"+ imageName);iv_photo.setImageBitmap(bitmap);break;}super.onActivityResult(requestCode, resultCode, data);}}@SuppressLint("SdCardPath")private void startPhotoZoom(Uri uri1, int size) {Intent intent = new Intent("com.android.camera.action.CROP");intent.setDataAndType(uri1, "image/*");// crop为true是设置在开启的intent中设置显示的view可以剪裁intent.putExtra("crop", "true");// aspectX aspectY 是宽高的比例intent.putExtra("aspectX", 1);intent.putExtra("aspectY", 1);// outputX,outputY 是剪裁图片的宽高intent.putExtra("outputX", size);intent.putExtra("outputY", size);intent.putExtra("return-data", false);intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(new File("/sdcard/zhuojiajin/", imageName)));intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());intent.putExtra("noFaceDetection", true); // no face detectionstartActivityForResult(intent, PHOTO_REQUEST_CUT);}@SuppressLint("SimpleDateFormat")private String getNowTime() {Date date = new Date(System.currentTimeMillis());SimpleDateFormat dateFormat = new SimpleDateFormat("MMddHHmmssSS");return dateFormat.format(date);}/*在这里面获取到每个需要用到的控件的实例*/@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);iv_photo = (ImageView) findViewById(R.id.iv_photo);}/*监听EditView变色*/public void inputFocus() {username.setOnFocusChangeListener(new View.OnFocusChangeListener() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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/AndroidServer1_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连接,释放资源}}/*发送请求的主要方法*/public void httpUrlConnPostImage() {HttpURLConnection urlConnection = null;URL url;try {// 请求的URL地地址url = new URL("http://100.2.178.10:8080/AndroidServer1_war_exploded/Image");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连接// ------------字节流写入数据------------FileInputStream fis = new FileInputStream("/sdcard/zhuojiajin/" + imageName);OutputStream out = urlConnection.getOutputStream();// 输出流,用来发送请求,http请求实际上直到这个函数里面才正式发送出去BufferedOutputStream bos = new BufferedOutputStream(out);byte[] buf = new byte[1024];int len = 0;//往输出流里面投放数据//先将流文件变成byte[], 然后利用套接字的输出流发送给客户端while ((len = fis.read(buf)) != -1) {bos.write(buf, 0, len);bos.flush();// 刷新缓冲区,把数据发送出去,这步很重要}bos.close();// 使用完关闭//以下判斷是否訪問成功,如果返回的状态码是200则说明访问成功Log.i("aa", urlConnection.getResponseCode() + "");} catch (Exception e) {e.printStackTrace();Log.i("aa", e.toString());} finally {urlConnection.disconnect();// 使用完关闭TCP连接,释放资源}}// 在Android中不可以在线程中直接修改UI,只能借助Handler机制来完成对UI的操作class MyHander extends Handler {@Overridepublic 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(); //结束当前activitybreak;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}
}
上面代码添加了ImageView监听器,点击可打开本地图库挑选一张图片进行剪切,点击保存后会把图片保存在手机sd卡中进行存储以显示在ImageView上,注册按钮的点击事件也进行了相应添加,在表单请求服务器代码添加了请求服务器传输图片的线程(以字节流传输),但是必须在表单请求成功后才能向服务器传输图片(防止手机号重复也传输图片)
在ActivityMainfest.xml中添加访问sd卡的权限
ActivityMainfest.xml
<!-- 在SDCard中创建与删除文件权限 --><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"tools:ignore="ProtectedPermissions" /><!-- 往SDCard写入数据权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.CAMERA"/>
在sd卡目录下创建存储图片的文件夹