声明: 本文使用Bmob作为云后台,实现一个简简单单的头像的选取、截取、上传、下载功能的实现。
编码环境:Android Studio2.1.1
运行环境:Miui8.6.8.18(安卓版本号6.0.1MMB29M)
手机型号:小米3
如有错误,欢迎指正!
准备工作:
添加必要的权限,Bmob的配置这里不再进行介绍。
<!--相机的权限-->
<uses-permission android:name="android.permission.CAMERA"/>
<!--sd卡读取权限-->
<!--获取sd卡写的权限,用于文件上传和下载-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!--允许联网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!--获取GSM(2g)、WCDMA(联通3g)等网络状态的信息 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--获取wifi网络状态的信息 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--保持CPU 运转,屏幕和键盘灯有可能是关闭的,用于文件上传和下载 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!--允许读取手机状态 用于创建BmobInstallation-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
另外Bmob上传文件和下载文件需要okhttp库,所以需要导入okhttp和ok.io依赖,如果导入的是bmob.3.5这样的依赖的话,内含了ok.io依赖,所以只需要导入okhttp就好。下边是两个build.gradle文件
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
//**兼容Android6.0系统所需,如果这句话报错,可在dependencies标签下使用compile 'cn.bmob.android:http-legacy:1.0'**
useLibrary 'org.apache.http.legacy'
defaultConfig {
applicationId "com.example.no_clay.demolist"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
//以下SDK开发者请根据需要自行选择
//bmob-sdk:Bmob的android sdk包,包含了Bmob的数据存储、文件等服务,以下是最新的bmob-sdk:
compile 'cn.bmob.android:bmob-sdk:3.5.0'
//如果你想应用能够兼容Android6.0,请添加此依赖(org.apache.http.legacy.jar)
compile 'cn.bmob.android:http-legacy:1.0'
// compile 'com.squareup.okhttp:okhttp:2.4.0'
// compile 'com.squareup.okio:okio:1.5.0'
compile files('libs/okhttp-2.4.0.jar')
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
maven { url "https://raw.github.com/bmob/bmob-android-sdk/master" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Step1:选取图片:
选取图片通常有两种思路,一是利用相机进行拍照选取,而是利用SD卡中现成的图片设置为头像,首先贴一波代码,关于更加详细的“ android.intent.action.GET_CONTENT ”的使用,请查看博客 Android--利用相机或相册截取用户头像(解决了miui无法截取,以及部分机型拍照无返回Uri)
chooseUserImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
menuWindow = new SelectPicPopupWindow(context, new
View.OnClickListener() {
@Override
public void onClick(View v) {
menuWindow.dismiss();
switch (v.getId()) {
case R.id.takePhotoBtn: {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
Intent getImageByCamera = new
Intent("android.media.action.IMAGE_CAPTURE");
startActivityForResult(getImageByCamera,
REQUEST_CODE_CAPTURE_CAMEIA);
} else {
Toast.makeText(getApplicationContext(),
"请确认已经插入SD卡", Toast.LENGTH_LONG).show();
}
break;
}
case R.id.pickPhotoBtn:
Intent intent = new Intent(Intent.ACTION_PICK);//从相册中选取图片
// Intent intent = new Intent("android.intent.action.GET_CONTENT");//从相册/文件管理中选取图片
intent.setType("image/*");//相片类型
startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE);
break;
case R.id.cancelBtn: {
break;
}
}
}
});
menuWindow.showAtLocation(findViewById(R.id.mainLayout),
Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0);
}
});
在返回事件中处理返回的图片(详细请查看博客 Android--利用相机或相册截取用户头像(解决了miui无法截取,以及部分机型拍照无返回Uri)):
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Uri imageUri;
if (resultCode == RESULT_CANCELED) {
Toast.makeText(context, "获取失败", Toast.LENGTH_SHORT).show();
chooseUserImage.setImageDrawable(getResources().getDrawable(R.drawable.img_1));//预设的图片
} else if (resultCode == RESULT_OK) {//选取成功后进行裁剪
switch (requestCode) {
case REQUEST_CODE_PICK_IMAGE: {
//从图库中选择图片作为头像
imageUri = data.getData();
reSizeImage(imageUri);
break;
}
case REQUEST_CODE_CAPTURE_CAMEIA: {
//使用相机获取头像
imageUri = data.getData();
Log.d(TAG, "onActivityResult: " + imageUri);
if (imageUri == null) {
//use bundle to get data
Bundle bundle = data.getExtras();
if (bundle != null) {
Bitmap bitMap = (Bitmap) bundle.get("data"); //get bitmap
imageUri = Uri.parse(MediaStore.Images.Media.
insertImage(getContentResolver(), bitMap, null, null));
Log.d(TAG, "onActivityResult: bndle != null" + imageUri);
reSizeImage(imageUri);
} else {
Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_SHORT).show();
}
}
break;
}
case RESIZE_REQUEST_CODE: {
//剪切图片返回
Log.d(TAG, "onActivityResult: UserImage" + userImageUri);
if (userImageUri == null) {
Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show();
} else {
showImage(userImageUri);
}
break;
}
}
}
}
Step2:截取图片
利用系统库 com.android.camera.action.CROP 进行截取图片的操作,代码如下:
private void reSizeImage(Uri uri) {//重新剪裁图片的大小
File outputImage = new File(Environment.getExternalStorageDirectory(), "crop.jpg");
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (Exception e) {
e.printStackTrace();
}//删除输出路径的文件,保证输出的Uri是一个具有路径的空文件,可以解决某些机型截取后返回为null
userImageUri = Uri.fromFile(outputImage);
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setType("image");
intent.setDataAndType(uri, "image/*");
// 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
intent.putExtra("crop", true);//允许剪切
intent.putExtra("scale", true);//支持缩放
intent.putExtra("scaleUpIfNeeded", true);// 去黑边
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);//输出是X方向的比例
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高,切忌不要再改动下列数字,会卡死
intent.putExtra("outputX", 500);//输出X方向的像素
intent.putExtra("outputY", 500);//
intent.putExtra("noFaceDetection", true);
intent.putExtra("return-data", false);//设置为不返回数据
/**
* 此方法返回的图片只能是小图片(测试为高宽160px的图片)
* 故将图片保存在Uri中,调用时将Uri转换为Bitmap,此方法还可解决miui系统不能return data的问题
*/
// intent.putExtra("return-data", true);
// intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp")));//保存路径
intent.putExtra(MediaStore.EXTRA_OUTPUT, userImageUri);//输出的Uri
Log.d(TAG, "reSizeImage() called with: " + "uri = [" + userImageUri + "]");
startActivityForResult(intent, RESIZE_REQUEST_CODE);
}
Step3:上传头像
利用Bmob的云后台上传文件的方法是,首先将BmobFile上传到云端,然后通过保存表的方法,让文件与表数据之间产生联系,下面是表结构(帐号和头像):
package com.example.no_clay.demolist.ChooseImage;
import cn.bmob.v3.BmobObject;
import cn.bmob.v3.datatype.BmobFile;
/**
* Created by 寒 on 2016/6/5.
*/
public class SignUserImage extends BmobObject {
private String phoneNumber = null;
private BmobFile image = null;
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public BmobFile getImage() {
return image;
}
public void setImage(BmobFile image) {
this.image = image;
}
}
上传功能的实现:
uploadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (userImageUri == null) {
Toast.makeText(context, "还没有选择照片", Toast.LENGTH_SHORT);
} else {
try {
Log.d(TAG, "onClick() called with: " + "uri = [" + userImageUri + "]");
final BmobFile bmobFile = new BmobFile(new File(new URI(userImageUri.toString())));
bmobFile.upload(new UploadFileListener() {
@Override
public void done(BmobException e) {
if (e == null) {
signUserImage.setPhoneNumber("测试");
signUserImage.setImage(bmobFile);
signUserImage.save(new SaveListener<String>() {
@Override
public void done(String s, BmobException e) {
if (e == null) {
Toast.makeText(context, "上传成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "上传失败", Toast.LENGTH_SHORT).show();
}
}
});
Log.d(TAG, "done() called with: " + "true");
} else {
Log.d(TAG, "done() called with: " + "false");
}
}
});
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
});
Step4:下载功能的实现
这里设置一个下载按钮,和一个圆形的头像显示ImageView,下载的话,首先查表找到当前的用户,我这里为了方便,直接使用了ObjectId,可以采用其他查询表的方法进行查询,然后查看与之关联的头像是否为null,不为空则利用BmobFile的下载方法进行下载,这里分别利用BmobFile的两种下载方法实现,我个人推荐后一种,使用简便。代码如下:
downloadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
BmobQuery<SignUserImage> query = new BmobQuery<SignUserImage>();
query.getObject("5d5bb7a60c", new QueryListener<SignUserImage>() {
@Override
public void done(SignUserImage signUserImage, BmobException e) {
BmobFile bmobFile = signUserImage.getImage();
Log.d(TAG, "done() called with: " + "signUserImage = [" + signUserImage.getObjectId() + "]");
if (bmobFile != null) {//如果有头像
Log.d(TAG, "done() called with: " + "下载中...");
//方法一
// bmobFile.download(new DownloadFileListener() {
// @Override
// public void done(String s, BmobException e) {
// Log.d(TAG, "done() called with: " + "s = [" + s + "]");
// if(e == null){
// Message message = new Message();
// message.what = DOWNLOAD_IMAGE_MSG;
// message.obj = s;
// handler.sendMessage(message);
// }
// }
//
// @Override
// public void onProgress(Integer integer, long l) {
// Log.d(TAG, "onProgress() called with: " + "integer = [" + integer + "], l = [" + l + "]");
// }
// });
//方法二
bmobFile.download(new File(Environment.
getExternalStorageDirectory() + "/userImage.jpg"),//设置保存的路径
new DownloadFileListener() {
@Override
public void done(String s, BmobException e) {
Log.d(TAG, "done() called with: " + "s = [" + s + "]");
if (e == null) {
Message message = new Message();
message.what = DOWNLOAD_IMAGE_MSG;
message.obj = s;
handler.sendMessage(message);
}
}
@Override
public void onProgress(Integer integer, long l) {
Log.d(TAG, "onProgress() called with: " + "integer = [" + integer + "], l = [" + l + "]");
}
});
}
}
});
}
});
}
Step5:将下载的图片显示出来
首先贴上一个自定义的圆形ImageView,我们上边设置正方形的剪切范围,就是为了显示圆形的头像,这里现贴出圆形的自定义ImageView:
<span style="font-size:18px;">package com.example.no_clay.demolist.ChooseImage;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Created by 82661 on 2016/8/31.
*/
public class MyCircleImageView extends ImageView {
private Paint paint;
public MyCircleImageView(Context context) {
this(context, null);
}
public MyCircleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyCircleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint();
}
/**
* 绘制圆形图片
*
* @author caizhiming
*/
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
Bitmap b = getCircleBitmap(bitmap, 14);
final Rect rectSrc = new Rect(0, 0, b.getWidth(), b.getHeight());
final Rect rectDest = new Rect(0, 0, getWidth(), getHeight());
paint.reset();
canvas.drawBitmap(b, rectSrc, rectDest, paint);
} else {
super.onDraw(canvas);
}
}
/**
* 获取圆形图片方法
*
* @param bitmap
* @param pixels
* @return Bitmap
* @author caizhiming
*/
private Bitmap getCircleBitmap(Bitmap bitmap, int pixels) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
int x = bitmap.getWidth();
canvas.drawCircle(x / 2, x / 2, x / 2, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
}
</span>
处理下载的头像,只需要在Handler里边将路径设置转换为Uri设置给ImageView就可以:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case DOWNLOAD_IMAGE_MSG: {
String path = (String) msg.obj;
showUserImage.setImageURI(Uri.parse(path));
Toast.makeText(context, "下载头像成功", Toast.LENGTH_SHORT).show();
break;
}
}
}
};
这里是最终的效果图
具体的代码可以到这里下载
几个小Demo的合集的下载