其实更换头像这个功能是个老梗了,写的人也很多,但是我没有看见过特别让我满意的,没办法,只能自己搞了。这里面我只说难点吧,最后的会附上完整的代码。
这里面涉及到的功能有哪些呢?
大概有:拍照 、扫描本地图片、裁剪、可以拖动放大缩小的图片、圆形头像,自认为还是比较不错的,代码风格可能能有改进,大家可以自行修改!~
一、首页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 ;@Overrideprotected 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() {@Overridepublic 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之后返回的alertdialoagfinal 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;}@Overridepublic 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;}}}@Overrideprotected 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 implementsView.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<>();@Overrideprotected 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() {@Overridepublic void onClick(View v) {SelectImagesFromLocalActivity.this.finish();}});}@Overridepublic 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();}}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.selectImagesFromFolder:pop.showPopupWindow(v);break;}}@Overridepublic void OnFinishedClick(String name) {madapter.setArrayList(mgroupMap.get(name));mFolderName.setText(name);}/**** 请求本地图片数据,扫描本地图片,存储到*/private class RequestLocalImages extends AsyncTask<String,Integer,String>{public RequestLocalImages() {super();}@Overrideprotected 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();//比较是否是同一个文件夹,如果不是的话,重建以文件夹为keyif(!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;}@Overrideprotected 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 pixelreturn 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);}@Overridepublic 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;@Overrideprotected 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() {@Overridepublic void onClick(View v) {ClippingPageActivity.this.finish();}});righttext.setText(getResources().getString(R.string.sure));righttext.setOnClickListener(new View.OnClickListener() {@Overridepublic 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哈哈~