您的位置:首页 > 移动开发 > 微信开发

仿微信通过拍照、本地图片然后裁剪完美更换头像

2015-11-18 10:26 726 查看
其实更换头像这个功能是个老梗了,写的人也很多,但是我没有看见过特别让我满意的,没办法,只能自己搞了。这里面我只说难点吧,最后的会附上完整的代码。

这里面涉及到的功能有哪些呢?

大概有:拍照 、扫描本地图片、裁剪、可以拖动放大缩小的图片、圆形头像,自认为还是比较不错的,代码风格可能能有改进,大家可以自行修改!~

一、首页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<ImageBean> marrayList=new ArrayList<>();//所有图片的数据

private ArrayList<ImageFolderBean> arrayList=new ArrayList<>();//popuwindow的适配器数据源

private HashMap<String,ArrayList<ImageBean>>  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<String,Integer,String>{

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<ImageBean> 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<String,ArrayList<ImageBean>>  mgroupMap){

if(mgroupMap==null||mgroupMap.size()==0){

return;
}

Iterator<Map.Entry<String,ArrayList<ImageBean>>> iterator=mgroupMap.entrySet().iterator();

while (iterator.hasNext()){

Map.Entry<String,ArrayList<ImageBean>> 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哈哈~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: