其实更换头像这个功能是个老梗了,写的人也很多,但是我没有看见过特别让我满意的,没办法,只能自己搞了。这里面我只说难点吧,最后的会附上完整的代码。
这里面涉及到的功能有哪些呢?
大概有:拍照 、扫描本地图片、裁剪、可以拖动放大缩小的图片、圆形头像,自认为还是比较不错的,代码风格可能能有改进,大家可以自行修改!~
一、首页main.activity
public class MainActivity extends AppCompatActivity {
private CircleImageView headerPic;
private TextView textView;
private static final String IMAGE_FILE_LOCATION = ConstantSet.LOCALFILE;//拍照之后照片的输出文件路径
File file ;
Uri imageUri ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
headerPic=(CircleImageView)findViewById(R.id.headerImage);
textView=(TextView)findViewById(R.id.updateImages);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
View uv = getLayoutInflater().from(MainActivity.this).inflate(R.layout.upload_user_pic, null);
TextView photograph = (TextView) uv.findViewById(R.id.Photograph);
TextView selectPic = (TextView) uv.findViewById(R.id.selectImage_from_local);
TextView dimissDialoag = (TextView) uv.findViewById(R.id.dimiss_dialoag);
builder.setView(uv);
//builder不能直接dimiss dialoag 需要取得show之后返回的alertdialoag
final AlertDialog alertDialog = builder.show();
MyDialoagListener myDialoagListener = new MyDialoagListener(alertDialog);
photograph.setOnClickListener(myDialoagListener);
selectPic.setOnClickListener(myDialoagListener);
dimissDialoag.setOnClickListener(myDialoagListener);
}
});
//判断文件目录是否存在
file = new File(IMAGE_FILE_LOCATION);
if(!file.exists()){
//不存在的话就新建文件目录,注意在清单文件内添加权限
SDCardUtils.makeRootDirectory(IMAGE_FILE_LOCATION);
}
file=new File(IMAGE_FILE_LOCATION+ConstantSet.USERTEMPPIC);
imageUri = Uri.fromFile(file);
}
/***
* 头像更改监听
*/
private class MyDialoagListener implements View.OnClickListener {
private AlertDialog alertDialog;
public MyDialoagListener(AlertDialog alertDialog) {
this.alertDialog = alertDialog;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.Photograph:
alertDialog.dismiss();
//拍照
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, ConstantSet.TAKEPICTURE);
break;
case R.id.selectImage_from_local:
//从本地选择图片
Intent sintent = new Intent(MainActivity.this, SelectImagesFromLocalActivity.class);
startActivityForResult(sintent, ConstantSet.SELECTPICTURE);
alertDialog.dismiss();
break;
case R.id.dimiss_dialoag:
alertDialog.dismiss();
break;
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case ConstantSet.TAKEPICTURE:
//拍照完成之后跳到裁剪页面
Intent tcutIntent = new Intent(MainActivity.this, ClippingPageActivity.class);
tcutIntent.putExtra("type", "takePicture");
startActivityForResult(tcutIntent, ConstantSet.CROPPICTURE);
break;
case ConstantSet.SELECTPICTURE:
//本地图片选择完成之后跳转到裁剪页面
Intent scutIntent = new Intent(MainActivity.this, ClippingPageActivity.class);
scutIntent.putExtra("type", "selectPicture");
scutIntent.putExtra("path",data.getStringExtra("path"));
startActivityForResult(scutIntent, ConstantSet.CROPPICTURE);
break;
case ConstantSet.CROPPICTURE:
//裁剪之后的结果 这里面采用的是传输字节码,bitmap虽然序列化了,但是据说智能传输不超过40KB的,反正我这里试验了一下
//用不了
byte[] bis = data.getByteArrayExtra("result");
Bitmap bitmap = BitmapFactory.decodeByteArray(bis, 0, bis.length);
headerPic.setImageBitmap(bitmap);
break;
default:
break;
}
}
}
首页效果大致就是这个样子:
二、SelectImagesFromLocalActivity 本地图片
public class SelectImagesFromLocalActivity extends AppCompatActivity implements
View.OnClickListener,ImagesFolderPopupWindow.FinishOnItemClickListener,AdapterView.OnItemClickListener {
private DisplayImageOptions options = null;
private GridView mgridView;
private SpinnerProgressDialoag msp;
private LinearLayout mSelectImages;
private TextView mFolderName;
private ImageGridViewAdapter madapter;
private ImagesFolderPopupWindow pop;
private ArrayList marrayList=new ArrayList<>();//所有图片的数据
private ArrayList arrayList=new ArrayList<>();//popuwindow的适配器数据源
private HashMap> mgroupMap=new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_from_local);
msp=new SpinnerProgressDialoag(this);
initTitle();
mgridView=(GridView)findViewById(R.id.imagesGridView);
mgridView.setOnItemClickListener(this);
mSelectImages=(LinearLayout)findViewById(R.id.selectImagesFromFolder);
mSelectImages.setOnClickListener(this);
mFolderName=(TextView)findViewById(R.id.imagesFolderName);
initImageLoader();
madapter=new ImageGridViewAdapter(this,options);
mgridView.setAdapter(madapter);
pop=new ImagesFolderPopupWindow(this,options);
pop.setFinishOnItemClickListener(this);
new RequestLocalImages().execute();
}
private void initTitle() {
ImageView rightImage = (ImageView) findViewById(R.id.id_img_right);
TextView title = (TextView) findViewById(R.id.id_title);
ImageView back = (ImageView) findViewById(R.id.id_back);
rightImage.setVisibility(View.GONE);
title.setText(getResources().getString(R.string.images));
back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SelectImagesFromLocalActivity.this.finish();
}
});
}
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
ImageBean imageBean=(ImageBean)parent.getItemAtPosition(position);
String path=imageBean.getImagePath();
File file=new File(path);
if(file.exists()) {
Intent itToClip = new Intent();
itToClip.putExtra("path", path);
setResult(RESULT_OK, itToClip);
this.finish();
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.selectImagesFromFolder:
pop.showPopupWindow(v);
break;
}
}
@Override
public void OnFinishedClick(String name) {
madapter.setArrayList(mgroupMap.get(name));
mFolderName.setText(name);
}
/***
* 请求本地图片数据,扫描本地图片,存储到
*/
private class RequestLocalImages extends AsyncTask{
public RequestLocalImages() {
super();
}
@Override
protected String doInBackground(String... params) {
Cursor imageCursor = getContentResolver().
query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID},
null, null, MediaStore.Images.Media._ID);
//把存储所有图片的数据集合放到map中来
mgroupMap.put(getResources().getString(R.string.all_images),marrayList);
if (imageCursor != null && imageCursor.getCount() > 0) {
while (imageCursor.moveToNext()) {
ImageBean item = new ImageBean(imageCursor.
getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA)));
mgroupMap.get(getResources().getString(R.string.all_images)).add(item);//每个图片都加入进去
String parentPath=new File(item.getImagePath()).getParentFile().getName();
//比较是否是同一个文件夹,如果不是的话,重建以文件夹为key
if(!mgroupMap.containsKey(parentPath)){
ArrayList arrayList=new ArrayList<>();
arrayList.add(item);
mgroupMap.put(parentPath,arrayList);
}else{
mgroupMap.get(parentPath).add(item);
}
}
subGroupOfImage(mgroupMap);
}
return null;
}
@Override
protected void onPostExecute(String s) {
madapter.setArrayList(mgroupMap.get(getResources().getString(R.string.all_images)));
pop.setArrayList(arrayList);
super.onPostExecute(s);
}
}
/**
* map集合中的数据添加到popwindow适配器数据源 集合 arraylist中
* @param mgroupMap
*/
private void subGroupOfImage(HashMap> mgroupMap){
if(mgroupMap==null||mgroupMap.size()==0){
return;
}
Iterator>> iterator=mgroupMap.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry> entry=iterator.next();
ImageFolderBean ifb=new ImageFolderBean();
ifb.setFirstImage(entry.getValue().get(0).getImagePath());
ifb.setFolderName(entry.getKey());
ifb.setImages(entry.getValue().size());
if(!entry.getKey().equals(getResources().getString(R.string.all_images))){
ifb.setIsSelected(false);
arrayList.add(ifb);
}else{
ifb.setIsSelected(true);
arrayList.add(0,ifb);
}
}
}
private void initImageLoader() {
if (options == null) {
DisplayImageOptions.Builder displayBuilder = new DisplayImageOptions.Builder();
displayBuilder.cacheInMemory(true);
displayBuilder.cacheOnDisk(true);
displayBuilder.showImageOnLoading(R.mipmap.default_photo);
displayBuilder.showImageForEmptyUri(R.mipmap.default_photo);
displayBuilder.considerExifParams(true);
displayBuilder.bitmapConfig(Bitmap.Config.RGB_565);
displayBuilder.imageScaleType(ImageScaleType.EXACTLY);
displayBuilder.displayer(new FadeInBitmapDisplayer(300));
options = displayBuilder.build();
}
if (!ImageLoader.getInstance().isInited()) {
ImageLoaderConfiguration.Builder loaderBuilder = new ImageLoaderConfiguration.Builder(getApplication());
loaderBuilder.memoryCacheSize(getMemoryCacheSize());
try {
File cacheDir = new File(getExternalCacheDir() + File.separator + ConstantSet.IMAGE_CACHE_DIRECTORY);
loaderBuilder.diskCache(new LruDiskCache(cacheDir, DefaultConfigurationFactory.createFileNameGenerator(), 500 * 1024 * 1024));
} catch (IOException e) {
e.printStackTrace();
}
ImageLoader.getInstance().init(loaderBuilder.build());
}
}
private int getMemoryCacheSize() {
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
// 4 bytes per pixel
return screenWidth * screenHeight * 4 * 3;
}
}
三、裁剪中最重要的可以拖动、缩放的imageView,当然很多代码网上都有,只不过我稍加改动
PerfectControlImageView
//完美的图片缩放,拖动,边界判断
public class PerfectControlImageView extends ImageView {
float x_down = 0;
float y_down = 0;
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
float oldRotation = 0;
Matrix matrix = new Matrix();
Matrix matrix1 = new Matrix();
Matrix savedMatrix = new Matrix();
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
int mode = NONE;
boolean matrixCheck = false;
int widthScreen;
int heightScreen;
private Bitmap gintama;
private int titleHeight;
public PerfectControlImageView(Context context) {
this(context, null);
}
public PerfectControlImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray=context.obtainStyledAttributes
(attrs, R.styleable.PerfectControlImageView,defStyleAttr,0);
//获取到标题的高度
titleHeight=typedArray.getDimensionPixelSize(R.styleable.PerfectControlImageView_titleHeight,
(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,59,getResources().getDisplayMetrics()));
widthScreen= ScreenUtils.getScreenWidth(context);//当前view的宽度
//计算当前view的高度
heightScreen=ScreenUtils.getScreenHeight(context)-
ScreenUtils.getStatusHeight(context)- titleHeight;
matrix = new Matrix();
}
public PerfectControlImageView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
@Override
public void setImageBitmap(Bitmap bm) {
this.gintama=bm;
//初始化图片的matrix属性,让图片居中显示
matrix.postTranslate((widthScreen-gintama.getWidth())/2,(heightScreen-gintama.getHeight())/2);
}
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.drawBitmap(gintama, matrix, null);
canvas.restore();
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mode = DRAG;
x_down = event.getX();
y_down = event.getY();
savedMatrix.set(matrix);
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = ZOOM;
oldDist = spacing(event);
// oldRotation = rotation(event);
savedMatrix.set(matrix);
midPoint(mid, event);
break;
case MotionEvent.ACTION_MOVE:
if (mode == ZOOM) {
matrix1.set(savedMatrix);
// float rotation = rotation(event) - oldRotation;
float newDist = spacing(event);
float scale = newDist / oldDist;
matrix1.postScale(scale, scale, mid.x, mid.y);// 縮放
// matrix1.postRotate(rotation, mid.x, mid.y);// 旋轉 //暂时去掉
matrixCheck = matrixCheck();
if (matrixCheck == false) {
matrix.set(matrix1);
invalidate();
}
} else if (mode == DRAG) {
matrix1.set(savedMatrix);
matrix1.postTranslate(event.getX() - x_down, event.getY()
- y_down);// 平移
matrixCheck = matrixCheck();
matrixCheck = matrixCheck();
if (matrixCheck == false) {
matrix.set(matrix1);
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
return true;
}
private boolean matrixCheck() {
float[] f = new float[9];
matrix1.getValues(f);
// 图片4个顶点的坐标
float x1 = f[0] * 0 + f[1] * 0 + f[2];
float y1 = f[3] * 0 + f[4] * 0 + f[5];
float x2 = f[0] * gintama.getWidth() + f[1] * 0 + f[2];
float y2 = f[3] * gintama.getWidth() + f[4] * 0 + f[5];
float x3 = f[0] * 0 + f[1] * gintama.getHeight() + f[2];
float y3 = f[3] * 0 + f[4] * gintama.getHeight() + f[5];
float x4 = f[0] * gintama.getWidth() + f[1] * gintama.getHeight() + f[2];
float y4 = f[3] * gintama.getWidth() + f[4] * gintama.getHeight() + f[5];
// 图片现宽度
double width = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
// 缩放比率判断
if (width < widthScreen / 3 || width > widthScreen * 3) {
return true;
}
// 出界判断
if ((x1 < widthScreen / 3 && x2 < widthScreen / 3
&& x3 < widthScreen / 3 && x4 < widthScreen / 3)
|| (x1 > widthScreen * 2 / 3 && x2 > widthScreen * 2 / 3
&& x3 > widthScreen * 2 / 3 && x4 > widthScreen * 2 / 3)
|| (y1 < heightScreen / 3 && y2 < heightScreen / 3
&& y3 < heightScreen / 3 && y4 < heightScreen / 3)
|| (y1 > heightScreen * 2 / 3 && y2 > heightScreen * 2 / 3
&& y3 > heightScreen * 2 / 3 && y4 > heightScreen * 2 / 3)) {
return true;
}
return false;
}
// 触碰两点间距离
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float)Math.sqrt(x * x + y * y);
}
// 取手势中心点
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
// 取旋转角度
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
// 将移动,缩放以及旋转后的图层保存为新图片
// 本例中沒有用到該方法,需要保存圖片的可以參考
public Bitmap CreatNewPhoto() {
Bitmap bitmap = Bitmap.createBitmap(widthScreen, heightScreen,
Bitmap.Config.ARGB_8888); // 背景图片
Canvas canvas = new Canvas(bitmap); // 新建画布
canvas.drawBitmap(gintama, matrix, null); // 画图片
canvas.save(Canvas.ALL_SAVE_FLAG); // 保存画布
canvas.restore();
return bitmap;
}
}
四、ClippingPageActivity
public class ClippingPageActivity extends AppCompatActivity {
private PerfectControlImageView imageView;
private CuttingFrameView cuttingFrameView;
private Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clipping_page);
initTitle();
cuttingFrameView = (CuttingFrameView) findViewById(R.id.cutingFrame);
imageView = (PerfectControlImageView) findViewById(R.id.targetImage);
if (getIntent().getStringExtra("type").equals("takePicture")) {
bitmap = BitmapUtils.DecodLocalFileImage(ConstantSet.LOCALFILE + ConstantSet.USERTEMPPIC, this);
} else {
String path=getIntent().getStringExtra("path");
bitmap = BitmapUtils.DecodLocalFileImage(path, this);
}
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
/**
* 初始化title
*/
private void initTitle() {
ImageView rightImage = (ImageView) findViewById(R.id.id_img_right);
TextView title = (TextView) findViewById(R.id.id_title);
TextView righttext=(TextView)findViewById(R.id.id_text_right);
ImageView back = (ImageView) findViewById(R.id.id_back);
rightImage.setVisibility(View.GONE);
righttext.setVisibility(View.VISIBLE);
title.setText(getResources().getString(R.string.picture_cutting));
back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ClippingPageActivity.this.finish();
}
});
righttext.setText(getResources().getString(R.string.sure));
righttext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SpinnerProgressDialoag sp=new SpinnerProgressDialoag(ClippingPageActivity.this);
sp.show();
//把裁剪过的图片保存到本地
Bitmap bitmap=cuttingFrameView.takeScreenShot(ClippingPageActivity.this);
SDCardUtils.saveMyBitmap(ConstantSet.LOCALFILE, ConstantSet.USERPIC, bitmap);
//把裁剪过的图片转换成字节模式传递 这两个方法 选其一即可
Intent it=new Intent();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte [] bitmapByte =baos.toByteArray();
it.putExtra("result", bitmapByte);
setResult(RESULT_OK, it);
sp.dismiss();
ClippingPageActivity.this.finish();
}
});
}
}
主要的内容都在这里的,其它的代码,大家可以直接下载源码:
仿微信通过拍照或者本地图片裁剪完美更换头像
只要一份,最近积分不多啦。。O(∩_∩)O哈哈~