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

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

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

一、首页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;


        }


    }
}

首页效果大致就是这个样子:

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

二、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;
    }
}

效果如图所示:
仿微信通过拍照、本地图片然后裁剪完美更换头像_第2张图片

仿微信通过拍照、本地图片然后裁剪完美更换头像_第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哈哈~

你可能感兴趣的:(android,高手进阶)