接口地址:
登录接口:
http://120.27.23.105/user/login
请求参数:
mobile 手机号 必填
password 密码 必填
注册接口:
![](/qrcode.jpg)
请求参数:
mobile 手机号 必填
password 密码 必填
个人中心接口:
http://120.27.23.105/user/getUserInfo
请求参数:
uid 用户id 必填
案例:实现原生登录注册和个人中心模块。(100分)
需求分析:
1. 新建一个工程,命名为UserCenter
2. 实现如图登录注册模块和个人中心模块,登录成功后跳转到个人中心页面;
3. 第二次进入自动登录;
效果图描述:
1. 使用RxJava结合Retrofit+OkHTTP封装工具类请求数据,使用MVP分层,进行原生登录,注册,个人信息页;
2. 使用Fresco加载图片,设置用户头像(图片上传或拍照)。
3. 实现如图登录注册模块和个人中心模块,登录成功后跳转到个人中心页面;
第二次使用 SharedPreferences保存信息 进入自动登录;
首先在项目Model的build.gradle里面导入依赖
//butterknife在Studio3.0版本上需使用以下8.8.1版本(下面2行代码都要加) compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' compile 'com.facebook.fresco:fresco:1.5.0' //由于Retrofit是基于OkHttp,所以还需要添加OkHttp库依赖 compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-scalars:2.3.0' compile 'com.squareup.okhttp3:okhttp:3.9.0' //如果采用了 Gson 解析,需要在 Gradle加入retrofit2类库中的gson依赖 compile 'com.squareup.retrofit2:converter-gson:2.3.0' //较新版本 compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.7' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
import android.app.Application; import com.facebook.drawee.backends.pipeline.Fresco; /** * 用于全局配置初始化异步加载类 */ public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); // 用于全局配置初始化Fresco 图片加载 Fresco.initialize(this); } }
- <application
-
-
<uses-permission android:name="android.permission.INTERNET" />
请求网络获得的数据生成实体类(自己根据接口生成,这里不再重写)
1. 登录信息 LoginBean
用户登录信息 http://120.27.23.105/user/login?mobile=18631090582&password=888888
2. 注册信息 RegBean
用户注册数据 http://120.27.23.105/user/reg?mobile=18631090582&password=888888
3.个人信息 PersonInfoBean
个人信息数据 http://120.27.23.105/user/getUserInfo?uid=100
新建网络请求接口类GetDataInterface
import io.reactivex.Observable; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.Query; /** * 网络接口数据的请求类 */ public interface GetDataInterface { //注册的接口 //https://www.zhaoapi.cn/user/reg?mobile=18631090582&password=888888 @FormUrlEncoded @POST("/user/reg") Observable<RegBean> reg(@FieldMap Map<String,String> map); //登录的接口 //https://www.zhaoapi.cn/user/login?mobile=18631090582&password=888888 @FormUrlEncoded @POST("/user/login") Observable<LoginBean> login(@FieldMap Map<String,String> map); //个人中心接口: //https://www.zhaoapi.cn/user/getUserInfo?uid=100 @GET("/user/getUserInfo") Observable<PersonInfoBean> person(@Query("uid") int uid); }
import okhttp3.OkHttpClient; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; /** * Retrofit+Rxjava+OkHttp 单例模式+双重锁模式 封装类 */ public class RetrofitUnitl { private Retrofit mRetrofit; private String baseUrl; OkHttpClient client; private static RetrofitUnitl mRetrofitManager; private RetrofitUnitl(String baseUrl,OkHttpClient client){ this.baseUrl=baseUrl; this.client=client; initRetrofit(); } //单例模式+双重锁模式 封装方法 public static synchronized RetrofitUnitl getInstance(String baseUrl,OkHttpClient client){ if (mRetrofitManager == null){ mRetrofitManager = new RetrofitUnitl(baseUrl,client); } return mRetrofitManager; } //实例化Retrofit请求 private void initRetrofit() { mRetrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(client) .build(); } //封装泛型方法 public <T> T setCreate(Class<T> reqServer) { return mRetrofit.create(reqServer); } }
自定义添加公共请求参数的拦截器
import java.io.IOException; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; //自定义添加公共请求参数的拦截器 public class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); //开始响应的时间 long t1 = System.nanoTime(); //执行请求,返回响应的数据 Response response = chain.proceed(request); //请求结束,响应返回 long t2 = System.nanoTime(); //响应的耗费时间 System.out.println(" this request url " + response.request().url() + " " + (t2 - t1)/1000000); return response; } }
自定义存取数据的SharedUtils
import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; public class SharedUtils { static SharedPreferences preferences; static String File = "data"; //保存数据的方法 public static void savaBooleanData(Context context, String key,boolean value){ if (preferences == null) { preferences = context.getSharedPreferences(File, Context.MODE_PRIVATE); } //获取编辑器对象 Editor editor = preferences.edit(); editor.putBoolean(key, value).commit(); } //获取数据的方法 public static boolean getBooleanData(Context context, String key,boolean defValue){ if (preferences == null) { preferences = context.getSharedPreferences(File, Context.MODE_PRIVATE); } return preferences.getBoolean(key, defValue); } }
自定义图片的上传及转换类
import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * 图片的上传及转换类 */ public class ImageUtils { /** * Save image to the SD card * * @param photoBitmap * @param photoName * @param path */ public static String savePhoto(Bitmap photoBitmap, String path, String photoName) { String localPath = null; if (android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)) { File dir = new File(path); if (!dir.exists()) { dir.mkdirs(); } File photoFile = new File(path, photoName + ".png"); FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(photoFile); if (photoBitmap != null) { if (photoBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)) { // 转换完成 localPath = photoFile.getPath(); fileOutputStream.flush(); } } } catch (FileNotFoundException e) { photoFile.delete(); localPath = null; e.printStackTrace(); } catch (IOException e) { photoFile.delete(); localPath = null; e.printStackTrace(); } finally { try { if (fileOutputStream != null) { fileOutputStream.close(); fileOutputStream = null; } } catch (IOException e) { e.printStackTrace(); } } } return localPath; } /** * 转换图片成圆形 * * @param bitmap 传入Bitmap对象 * @return */ public static Bitmap toRoundBitmap(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); float roundPx; float left,top,right,bottom,dst_left,dst_top,dst_right,dst_bottom; if (width <= height) { roundPx = width / 2; top = 0; bottom = width; left = 0; right = width; height = width; dst_left = 0; dst_top = 0; dst_right = width; dst_bottom = width; } else { roundPx = height / 2; float clip = (width - height) / 2; left = clip; right = width - clip; top = 0; bottom = height; width = height; dst_left = 0; dst_top = 0; dst_right = height; dst_bottom = height; } Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect src = new Rect((int)left, (int)top, (int)right, (int)bottom); final Rect dst = new Rect((int)dst_left, (int)dst_top, (int)dst_right, (int)dst_bottom); final RectF rectF = new RectF(dst); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, src, dst, paint); return output; } }
使用MVP三层编写数据
1. 登录model层
import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import okhttp3.OkHttpClient; /** * 登录model层 */ public class LoginModel { //https://www.zhaoapi.cn/user/login?mobile=18631090582&password=888888 public void getLoginData(final String tel, String pwd, final ModelCallBack.LoginCallBack callBack){ //使用okhttp请求,添加拦截器时把下面代码解释 OkHttpClient ok = new OkHttpClient.Builder() .connectTimeout(20000, TimeUnit.SECONDS) .writeTimeout(20000,TimeUnit.SECONDS) .readTimeout(20000,TimeUnit.SECONDS) .addInterceptor(new LoggingInterceptor()) .build(); //使用Retrofit结合RxJava,okhttp封装类的单例模式 Map<String,String> map = new HashMap<>(); map.put("mobile",tel); map.put("password",pwd); RetrofitUnitl.getInstance("https://www.zhaoapi.cn",ok) .setCreate(GetDataInterface.class) .login(map) .subscribeOn(Schedulers.io()) //请求完成后在io线程中执行 .observeOn(AndroidSchedulers.mainThread()) //最后在主线程中执行 //进行事件的订阅,使用Consumer实现 .subscribe(new Consumer<LoginBean>() { @Override public void accept(LoginBean loginBean) throws Exception { //请求成功时返回数据 callBack.success(loginBean); System.out.println("m登录信息:" + loginBean.toString()); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { callBack.failed(throwable); } }); } }
注册model层
import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import okhttp3.OkHttpClient; /** * 注册model层 */ public class RegModel { //https://www.zhaoapi.cn/user/reg?mobile=18631090582&password=888888 public void getRegData(final String tel, String pwd, final ModelCallBack.RegCallBack callBack){ //使用okhttp请求,添加拦截器时把下面代码解释 OkHttpClient ok = new OkHttpClient.Builder() .connectTimeout(20000, TimeUnit.SECONDS) .writeTimeout(20000,TimeUnit.SECONDS) .readTimeout(20000,TimeUnit.SECONDS) .addInterceptor(new LoggingInterceptor()) .build(); //使用Retrofit结合RxJava,okhttp封装类的单例模式 Map<String,String> map = new HashMap<>(); map.put("mobile",tel); map.put("password",pwd); RetrofitUnitl.getInstance("https://www.zhaoapi.cn",ok) .setCreate(GetDataInterface.class) .reg(map) .subscribeOn(Schedulers.io()) //请求完成后在io线程中执行 .observeOn(AndroidSchedulers.mainThread()) //最后在主线程中执行 //进行事件的订阅,使用Consumer实现 .subscribe(new Consumer<RegBean>() { @Override public void accept(RegBean regBean) throws Exception { //请求成功时返回数据 callBack.success(regBean); System.out.println("m注册信息:" + regBean.toString()); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { callBack.failed(throwable); } }); } }
个人信息model层
import java.util.concurrent.TimeUnit; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import okhttp3.OkHttpClient; /** * 个人信息model层 */ public class PersonModel { //https://www.zhaoapi.cn/user/getUserInfo?uid=100 public void getPersonData(int uid, final ModelCallBack.PersonCallBack callBack){ //使用okhttp请求,添加拦截器时把下面代码解释 OkHttpClient ok = new OkHttpClient.Builder() .connectTimeout(20000, TimeUnit.SECONDS) .writeTimeout(20000,TimeUnit.SECONDS) .readTimeout(20000,TimeUnit.SECONDS) .addInterceptor(new LoggingInterceptor()) .build(); //使用Retrofit结合RxJava,okhttp封装类的单例模式 RetrofitUnitl.getInstance("https://www.zhaoapi.cn",ok) .setCreate(GetDataInterface.class) .person(uid) .subscribeOn(Schedulers.io()) //请求完成后在io线程中执行 .observeOn(AndroidSchedulers.mainThread()) //最后在主线程中执行 //进行事件的订阅,使用Consumer实现 .subscribe(new Consumer<PersonInfoBean>() { @Override public void accept(PersonInfoBean personInfoBean) throws Exception { //请求成功时返回数据 callBack.success(personInfoBean); System.out.println("m个人信息:" + personInfoBean.toString()); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { callBack.failed(throwable); } }); } }
model层接口
/** * MVP的三层编写步骤: * 1. 先写model层接口类,进行数据的存取 */ public interface ModelCallBack { public interface LoginCallBack{ //登录时,数据获取成功的方法,返回一个值表示登陆成功 public void success(LoginBean loginBean); //登录时,数据获取失败的方法,返回一个int值响应码表示登陆失败 public void failed(Throwable code); } public interface RegCallBack { //注册时,数据获取成功的方法,返回一个值表示登陆成功 public void success(RegBean regBean); //注册时,数据获取失败的方法,返回一个int值响应码表示登陆失败 public void failed(Throwable code); } //个人中心 public interface PersonCallBack{ void success(PersonInfoBean personBean); void failed(Throwable code); } }2.view层
/** * MVP的三层编写步骤: * 2. View 层 数据展示类 * View – 用户界面:View通常是指Activity、Fragment或者某个View控件,它含有一个Presenter成员变量。 通常View需要实现一个逻辑接口,将View上的操作转交给Presenter进行实现,最后,Presenter 调用View逻辑接口将结果返回给View元素。 */ public interface MyView { //登录页面由presenter层交互数据到view层MainActivity public interface LoginView{ //登录时,数据获取成功的方法,返回一个值表示登陆成功 void success(LoginBean loginBean); //登录时,数据获取失败的方法,返回一个int值响应码表示登陆失败 void failed(int code); } //注册页面由presenter层到view层RegActivity public interface RegView{ void failed(int code); void sucess(RegBean regBean); } //个人页面由presenter层到view层RegActivity public interface PersonView{ void failed(int code); void sucess(PersonInfoBean personInfoBean); } }
3.presenter层
// Presenter层,进行view层与model数据的交互
/** * 登录p层 */ public class LoginPresenter { LoginModel loginModel = new LoginModel(); MyView.LoginView loginView; public LoginPresenter(MyView.LoginView loginView) { this.loginView = loginView; } public void getData(String tel, String pwd) { loginModel.getLoginData(tel,pwd, new ModelCallBack.LoginCallBack() { @Override public void success(LoginBean dengluBean) { loginView.success(dengluBean); System.out.println("登录p数据:"+dengluBean.toString()); } @Override public void failed(Throwable code) { System.out.println("登录p错误:"+code); } }); } }
/** * 注册p层 */ public class RegPresenter { RegModel regModel = new RegModel(); MyView.RegView regView; public RegPresenter(MyView.RegView regView) { this.regView = regView; } public void getData(String tel, String pwd) { regModel.getRegData(tel,pwd, new ModelCallBack.RegCallBack() { @Override public void success(RegBean regBean) { regView.sucess(regBean); System.out.println("注册p数据:"+regBean.toString()); } @Override public void failed(Throwable code) { System.out.println("注册p错误:"+code); } }); } }
/** * 个人p层 */ public class PersonPresenter { PersonModel personModel = new PersonModel(); MyView.PersonView personView; public PersonPresenter(MyView.PersonView personView) { this.personView = personView; } public void getData(int uid) { personModel.getPersonData(uid, new ModelCallBack.PersonCallBack() { @Override public void success(PersonInfoBean personInfoBean) { personView.sucess(personInfoBean); System.out.println("个人p数据:"+personInfoBean.toString()); } @Override public void failed(Throwable code) { System.out.println("个人p错误:"+code); } }); } }
进入页面 MainActivity
import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; //页面进入页 public class MainActivity extends AppCompatActivity { private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0: Intent intent = new Intent(MainActivity.this, PersonInfoActivity.class); startActivity(intent); finish(); break; case 1: Intent intent2 = new Intent(MainActivity.this,LoginActivity.class); startActivity(intent2); finish(); break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //调用工具类判断保存的布尔值 boolean b = SharedUtils.getBooleanData(MainActivity.this, "flag", false); if (b) { //已经进入过,现在是第二次 handler.sendEmptyMessageDelayed(0, 0); } else { //现在是第一次 SharedUtils.savaBooleanData(MainActivity.this, "flag", true); handler.sendEmptyMessageDelayed(1, 0); } } }
import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; //用户登录 public class LoginActivity extends AppCompatActivity implements MyView.LoginView { @BindView(R.id.tel) EditText tel; @BindView(R.id.pwd) EditText pwd; @BindView(R.id.btn) Button btn; @BindView(R.id.reg) Button reg; private LoginPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); //调用p层 presenter = new LoginPresenter(this); } @Override public void success(LoginBean loginBean) { Toast.makeText(this, "登录结果: " + loginBean.getMsg(), Toast.LENGTH_SHORT).show(); if(loginBean.getMsg().equals("登录成功")){ try { Thread.sleep(1000); Intent intent = new Intent(LoginActivity.this, PersonInfoActivity.class); startActivity(intent); finish(); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void failed(int code) { Toast.makeText(this, "登录失败!请检查登录信息" + code, Toast.LENGTH_SHORT).show(); } @OnClick({R.id.btn, R.id.reg}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.btn: if (!TextUtils.isEmpty(tel.getText().toString()) && !TextUtils.isEmpty(pwd.getText().toString())) { //如果都不为空,请求接口 presenter.getData(tel.getText().toString(), pwd.getText().toString()); } else { Toast.makeText(this, "账号或密码不能为空", Toast.LENGTH_SHORT).show(); } break; case R.id.reg: //注册页 Intent intent = new Intent(LoginActivity.this, RegActivity.class); startActivity(intent); break; } } }
注册 RegActivity
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; //注册用户信息 public class RegActivity extends AppCompatActivity implements MyView.RegView { @BindView(R.id.back) TextView back; @BindView(R.id.text) TextView text; @BindView(R.id.regTel) EditText regTel; @BindView(R.id.regPwd) EditText regPwd; @BindView(R.id.regNow) Button regNow; private RegPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reg); ButterKnife.bind(this); //调用p层 presenter = new RegPresenter(this); } @Override public void failed(int code) { Toast.makeText(this, "注册失败!请检查网络"+ code, Toast.LENGTH_SHORT).show(); } @Override public void sucess(RegBean regBean) { Toast.makeText(this, "注册结果: "+regBean.getMsg(), Toast.LENGTH_SHORT).show(); if(regBean.getMsg().equals("注册成功")){ try { Thread.sleep(2000); Toast.makeText(this, "即将跳转到登录页面", Toast.LENGTH_SHORT).show(); finish(); } catch (InterruptedException e) { e.printStackTrace(); } } } @OnClick({R.id.back, R.id.regNow}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.back: //点击返回按钮,返回到登录页面 finish(); break; case R.id.regNow: //注册按钮 if (!TextUtils.isEmpty(regTel.getText().toString()) && !TextUtils.isEmpty(regPwd.getText().toString())) { if (regTel.getText().toString().length() < 6){ Toast.makeText(this, "密码长度不能小于 6 位", Toast.LENGTH_SHORT).show(); }else { // mvp请求注册的接口 presenter.getData(regTel.getText().toString(),regPwd.getText().toString()); } } else { Toast.makeText(this, "账号或密码不能为空", Toast.LENGTH_SHORT).show(); } break; } } }
个人 PersonInfoActivity
import android.Manifest; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.facebook.drawee.view.SimpleDraweeView; import java.io.File; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; //用户个人信息页面 public class PersonInfoActivity extends AppCompatActivity implements MyView.PersonView { @BindView(R.id.headPhoto) SimpleDraweeView headPhoto; @BindView(R.id.userName) TextView userName; @BindView(R.id.nickName) TextView nickName; @BindView(R.id.outLogin) Button outLogin; protected static final int CHOOSE_PICTURE = 0; protected static final int TAKE_PICTURE = 1; private static final int CROP_SMALL_PICTURE = 2; protected static Uri tempUri; private PersonPresenter dataPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_person_info); ButterKnife.bind(this); //从presenter调用个人中心的接口 dataPresenter = new PersonPresenter(this); dataPresenter.getData(100); } @Override public void failed(int code) { Toast.makeText(PersonInfoActivity.this,"数据错误:"+code,Toast.LENGTH_SHORT).show(); } @Override public void sucess(PersonInfoBean personInfoBean) { Toast.makeText(PersonInfoActivity.this,"个人数据:"+personInfoBean.getMsg()+" "+personInfoBean.toString(),Toast.LENGTH_SHORT).show(); //设置个人信息 if(personInfoBean.getMsg().equals("获取用户信息成功")) { headPhoto.setImageURI(personInfoBean.getData().getIcon()); nickName.setText(personInfoBean.getData().getNickname()); userName.setText(personInfoBean.getData().getUsername()); } } @OnClick({R.id.headPhoto, R.id.userName, R.id.nickName, R.id.outLogin}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.headPhoto: //上传头像 showChoosePicDialog(view); break; case R.id.outLogin: //点击退出登录按钮,返回到登录页面 Intent intent = new Intent(PersonInfoActivity.this, LoginActivity.class); startActivity(intent); finish(); break; } } /** * 显示修改头像的对话框 */ public void showChoosePicDialog(View v) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("设置头像"); String[] items = { "选择本地照片", "拍照" }; builder.setNegativeButton("取消", null); builder.setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case CHOOSE_PICTURE: // 选择本地照片 Intent openAlbumIntent = new Intent( Intent.ACTION_GET_CONTENT); openAlbumIntent.setType("image/*"); startActivityForResult(openAlbumIntent, CHOOSE_PICTURE); break; case TAKE_PICTURE: // 拍照 takePicture(); break; } } }); builder.create().show(); } private void takePicture() { String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; if (Build.VERSION.SDK_INT >= 23) { // 需要申请动态权限 int check = ContextCompat.checkSelfPermission(this, permissions[0]); // 权限是否已经 授权 GRANTED---授权 DINIED---拒绝 if (check != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); } } Intent openCameraIntent = new Intent( MediaStore.ACTION_IMAGE_CAPTURE); File file = new File(Environment .getExternalStorageDirectory(), "image.jpg"); //判断是否是AndroidN以及更高的版本 if (Build.VERSION.SDK_INT >= 24) { openCameraIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); tempUri = FileProvider.getUriForFile(PersonInfoActivity.this, "com.lt.uploadpicdemo.fileProvider", file); } else { tempUri = Uri.fromFile(new File(Environment .getExternalStorageDirectory(), "image.jpg")); } // 指定照片保存路径(SD卡),image.jpg为一个临时文件,每次拍照后这个图片都会被替换 openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, tempUri); startActivityForResult(openCameraIntent, TAKE_PICTURE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { // 如果返回码是可以用的 switch (requestCode) { case TAKE_PICTURE: startPhotoZoom(tempUri); // 开始对图片进行裁剪处理 break; case CHOOSE_PICTURE: startPhotoZoom(data.getData()); // 开始对图片进行裁剪处理 break; case CROP_SMALL_PICTURE: if (data != null) { setImageToView(data); // 让刚才选择裁剪得到的图片显示在界面上 } break; } } } /** * 裁剪图片方法实现 * * @param uri */ protected void startPhotoZoom(Uri uri) { if (uri == null) { Log.i("tag", "The uri is not exist."); } tempUri = uri; Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); // 设置裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 150); intent.putExtra("outputY", 150); intent.putExtra("return-data", true); startActivityForResult(intent, CROP_SMALL_PICTURE); } /** * 保存裁剪之后的图片数据 * * @param */ protected void setImageToView(Intent data) { Bundle extras = data.getExtras(); if (extras != null) { Bitmap photo = extras.getParcelable("data"); photo = ImageUtils.toRoundBitmap(photo); // 这个时候的图片已经被处理成圆形的了 headPhoto.setImageBitmap(photo); uploadPic(photo); } } private void uploadPic(Bitmap bitmap) { // 上传至服务器 // ... 可以在这里把Bitmap转换成file,然后得到file的url,做文件上传操作 // 注意这里得到的图片已经是圆形图片了 // bitmap是没有做个圆形处理的,但已经被裁剪了 String imagePath = ImageUtils.savePhoto(bitmap, Environment .getExternalStorageDirectory().getAbsolutePath(), String .valueOf(System.currentTimeMillis())); Log.e("imagePath", imagePath+""); if(imagePath != null){ // 拿着imagePath上传了 Log.d("TAG","imagePath:"+imagePath); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { } else { // 没有获取 到权限,从新请求,或者关闭app Toast.makeText(this, "需要存储权限", Toast.LENGTH_SHORT).show(); } } }
登录布局 activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:text="登录" android:padding="20dp" android:textSize="26sp" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <View android:background="#000" android:visibility="visible" android:layout_height="1dp" android:layout_width="match_parent"></View> <EditText android:id="@+id/tel" android:hint="请输入手机号" android:layout_marginTop="28dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/pwd" android:hint="请输入密码" android:layout_marginTop="28dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:text="登录" android:id="@+id/btn" android:background="#1E90FF" android:layout_marginLeft="78dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="78dp" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginTop="54dp" /> <Button android:text="注册" android:id="@+id/reg" android:background="#1E90FF" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginRight="83dp" android:layout_marginEnd="83dp" android:layout_alignTop="@+id/btn" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> </RelativeLayout> </LinearLayout>
注册布局 activity_reg.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:background="@drawable/back_arrow_black" android:padding="20dp" android:id="@+id/back" android:textSize="40sp" android:layout_width="28dp" android:layout_height="28dp" android:layout_alignParentLeft="true" android:layout_centerVertical="true"/> <TextView android:text="注册" android:padding="20dp" android:textSize="26sp" android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> <View android:background="#000" android:visibility="visible" android:layout_height="1dp" android:layout_below="@+id/text" android:layout_width="match_parent"></View> </RelativeLayout> <EditText android:id="@+id/regTel" android:hint="请输入手机号" android:layout_marginTop="28dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/regPwd" android:hint="请输入密码" android:layout_marginTop="28dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:text="立即注册" android:id="@+id/regNow" android:background="#1E90FF" android:layout_marginTop="28dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"/> </LinearLayout>
个人布局 activity_person_info.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:fresco="http://schemas.android.com/apk/res-auto" > <RelativeLayout android:id="@+id/reative_01" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:padding="20dp" android:text="个人信息" android:textSize="26sp" android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> <View android:background="#000" android:visibility="visible" android:layout_height="1dp" android:layout_below="@+id/text" android:layout_width="match_parent"></View> </RelativeLayout> <RelativeLayout android:id="@+id/reative_02" android:layout_below="@+id/reative_01" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:text="头像" android:padding="20dp" android:textSize="26sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true"/> <com.facebook.drawee.view.SimpleDraweeView android:padding="20dp" fresco:roundAsCircle="true" android:id="@+id/headPhoto" android:layout_width="108dp" android:layout_height="108dp" android:layout_centerVertical="true" android:layout_alignParentRight="true" fresco:actualImageScaleType="focusCrop" fresco:placeholderImageScaleType="fitCenter" fresco:placeholderImage="@drawable/app_default" fresco:failureImageScaleType="fitCenter" fresco:failureImage="@drawable/load_error_image" /> </RelativeLayout> <View android:background="#000" android:visibility="visible" android:layout_height="1dp" android:layout_below="@+id/reative_02" android:layout_width="match_parent"></View> <RelativeLayout android:id="@+id/reative_03" android:layout_below="@+id/reative_02" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:text="用户名" android:padding="20dp" android:textSize="26sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true"/> <TextView android:text="用户名" android:padding="20dp" android:id="@+id/userName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_alignParentRight="true"/> </RelativeLayout> <View android:background="#000" android:visibility="visible" android:layout_height="1dp" android:layout_below="@+id/reative_03" android:layout_width="match_parent"></View> <RelativeLayout android:id="@+id/reative_04" android:layout_below="@+id/reative_03" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:text="昵称" android:padding="20dp" android:textSize="26sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true"/> <TextView android:text="问天" android:padding="20dp" android:id="@+id/nickName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_alignParentRight="true"/> </RelativeLayout> <View android:id="@+id/view" android:background="#000" android:visibility="visible" android:layout_height="1dp" android:layout_below="@+id/reative_04" android:layout_width="match_parent"></View> <Button android:text="退出登录" android:id="@+id/outLogin" android:background="#1E90FF" android:layout_marginTop="88dp" android:layout_below="@+id/view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" /> </RelativeLayout>
Fresco加载图片的素材:
1. app_deafult.jpg
2. load_error_image.jpg
3. back_arrowblack.png