仿微信通过拍照、本地图片然后裁剪完美更换头像
2015-11-18 10:26
726 查看
其实更换头像这个功能是个老梗了,写的人也很多,但是我没有看见过特别让我满意的,没办法,只能自己搞了。这里面我只说难点吧,最后的会附上完整的代码。
这里面涉及到的功能有哪些呢?
大概有:拍照 、扫描本地图片、裁剪、可以拖动放大缩小的图片、圆形头像,自认为还是比较不错的,代码风格可能能有改进,大家可以自行修改!~
一、首页main.activity
首页效果大致就是这个样子:
二、SelectImagesFromLocalActivity 本地图片
效果如图所示:
三、裁剪中最重要的可以拖动、缩放的imageView,当然很多代码网上都有,只不过我稍加改动
PerfectControlImageView
四、ClippingPageActivity
主要的内容都在这里的,其它的代码,大家可以直接下载源码:
仿微信通过拍照或者本地图片裁剪完美更换头像
只要一份,最近积分不多啦。。O(∩_∩)O哈哈~
这里面涉及到的功能有哪些呢?
大概有:拍照 、扫描本地图片、裁剪、可以拖动放大缩小的图片、圆形头像,自认为还是比较不错的,代码风格可能能有改进,大家可以自行修改!~
一、首页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哈哈~
相关文章推荐
- 微信JSSDK录音的一些bug
- 启明星 微信版 会议室预定系统
- 欢迎关注微信公众号——风色年代
- 微信服务器发送三次重复的排重问题
- 微信移动应用接入开发, Android 授权微信登录获取openid,unionid等,score参数错误或者没有scope权限
- 微信服务器之三次数据传递,排重的问题
- android 微信分享
- android 微信分享
- 仿的微信总结
- ****微信开发问题总结(原创)
- 微信默认表情符号的代码对照表
- 社会化分享(附源码)
- 微信登录开发-java
- 微信支付宝签约流程总结
- [Android] 通过GridView仿微信动态添加本地图片
- 微信公众号与HTML 5混合模式揭秘2——分享手机相册中照片
- 微信公众号与HTML 5混合模式揭秘1——如何部署JSSDK
- 微信公众号与HTML 5混合模式揭秘2——分享手机相册中照片
- SAE服务下用java实现微信公众账号图灵机器人
- 微信公众号与HTML 5混合模式揭秘1——如何部署JSSDK