1. 程式人生 > >【Android實戰】圖片選取、拍攝、裁剪、上傳

【Android實戰】圖片選取、拍攝、裁剪、上傳

圖片上傳

效果圖展示

Drawing Drawing

圖片上傳前的準備

帶圓角的方形圖片

使用的第三方控制元件,主要關注如下一些檔案

  • RoundedDrawable
  • RoundedImageView
  • RoundedTransformationBuilder
  • res目錄下的anim、color以及values下的attrs

佈局檔案程式碼如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res/com.soulrelay.uploadpic" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:background="@color/f5f5f5">
<com.soulrelay.uploadpic.view.RoundedImageView android:id="@+id/imageCover"
android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="50dp" android:layout_centerVertical="true" android:scaleType="center" android:src="@drawable/default_cover_img" app:border_color="@color/border" app:border_width
="1dip" app:corner_radius="10dp" app:mutate_background="true" app:oval="false" />
<TextView android:id="@+id/add_cover_txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="10dp" android:layout_toRightOf="@id/imageCover" android:text="新增封面" android:textColor="@color/_3e363d" android:textSize="15sp" /> </RelativeLayout>

從下而上彈出的PopupWindow

關注如下檔案:

  • CoverSelelctPopupWindow
  • cover_select_pop_layout.xml
public class CoverSelelctPopupWindow extends PopupWindow {

    private Button albumBtn, photoGraphBtn,cancelBtn;

    private View mMenuView;

    public CoverSelelctPopupWindow(Activity context, OnClickListener itemsOnClick) {
        super(context);
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mMenuView = inflater.inflate(R.layout.cover_select_pop_layout, null);
        albumBtn = (Button) mMenuView.findViewById(R.id.btn_album);
        photoGraphBtn = (Button) mMenuView.findViewById(R.id.btn_photo_graph);
        //startRecordBtn = (Button) mMenuView.findViewById(R.id.btn_upload_record);
        cancelBtn = (Button) mMenuView.findViewById(R.id.btn_cancel_join);
        // 設定按鈕監聽
        albumBtn.setOnClickListener(itemsOnClick);
        photoGraphBtn.setOnClickListener(itemsOnClick);

        // 設定SelectPicPopupWindow的View
        this.setContentView(mMenuView);
        // 設定SelectPicPopupWindow彈出窗體的寬
        this.setWidth(LayoutParams.FILL_PARENT);
        // 設定SelectPicPopupWindow彈出窗體的高
        this.setHeight(LayoutParams.WRAP_CONTENT);
        // 設定SelectPicPopupWindow彈出窗體可點選
        this.setFocusable(true);
        // 設定SelectPicPopupWindow彈出窗體動畫效果
        this.setAnimationStyle(R.style.AnimBottom);
        // 例項化一個ColorDrawable顏色為半透明
        ColorDrawable dw = new ColorDrawable(0x00FFFFFF);
        // 設定SelectPicPopupWindow彈出窗體的背景
        this.setBackgroundDrawable(dw);
        // mMenuView新增OnTouchListener監聽判斷獲取觸屏位置如果在選擇框外面則銷燬彈出框
        mMenuView.setOnTouchListener(new OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {

                int height = mMenuView.findViewById(R.id.pop_layout).getTop();
                int y = (int) event.getY();
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    if (y < height) {
                        dismiss();
                    }
                }
                return true;
            }
        });

        cancelBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
    }

}

佈局檔案:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/pop_layout"
        android:layout_width="fill_parent"
        android:layout_height="175dp"
        android:background="@color/f33e363d"
        android:gravity="center_horizontal"
        android:orientation="vertical" >

        <Button
            android:id="@+id/btn_album"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dip"
            android:layout_marginRight="20dip"
            android:layout_marginTop="16dip"
            android:textSize="14sp"
            android:background="@drawable/btn_report_selector"
            android:text="@string/user_info_photo_album"
            android:textColor="@color/_3e363d" />

        <Button
            android:id="@+id/btn_photo_graph"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dip"
            android:layout_marginRight="20dip"
            android:layout_marginTop="8dip"
            android:textSize="14sp"
            android:background="@drawable/btn_report_selector"
            android:text="@string/user_info_photo_camera"
            android:textColor="@color/_3e363d" />

        <Button
            android:id="@+id/btn_cancel_join"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dip"
            android:layout_marginLeft="20dip"
            android:layout_marginRight="20dip"
            android:textSize="14sp"
            android:background="@drawable/btn_report_selector"
            android:text="@string/join_cancel_txt"
            android:textColor="@color/_6f6a6f" />
    </LinearLayout>

</RelativeLayout>

通過拍照以及相簿獲取圖片

public class MainActivity extends Activity implements android.view.View.OnClickListener {

    private RoundedImageView imgCover;

    private TextView addCover;

    private CoverSelelctPopupWindow coverSelelctPopupWindow;

    public static final int ACTIVITY_ALBUM_REQUESTCODE = 2000;

    public static final int ACTIVITY_CAMERA_REQUESTCODE = 2001;

    public static final int ACTIVITY_MODIFY_PHOTO_REQUESTCODE = 2002;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }

    private void initViews() {
        imgCover = (RoundedImageView) this.findViewById(R.id.imageCover);
        addCover = (TextView) this.findViewById(R.id.add_cover_txt);
        imgCover.setOnClickListener(this);
        addCover.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.imageCover:
            case R.id.add_cover_txt:
                coverSelelctPopupWindow = new CoverSelelctPopupWindow(this, itemsOnClick);
                coverSelelctPopupWindow.showAtLocation(findViewById(R.id.add_cover_txt), Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); //設定layout在PopupWindow中顯示的位置
                break;
            default:
                break;
        }

    }

    // 為彈出視窗實現監聽類
    private OnClickListener itemsOnClick = new OnClickListener() {
        public void onClick(View v) {
            coverSelelctPopupWindow.dismiss();
            if (CommonUtils.isFastDoubleClick()) {
                return;
            }
            switch (v.getId()) {
                case R.id.btn_album:
                    Intent i = new Intent(Intent.ACTION_PICK, null);// 呼叫android的相簿
                    i.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
                    startActivityForResult(i, ACTIVITY_ALBUM_REQUESTCODE);
                    break;

                case R.id.btn_photo_graph:
                    if (CommonUtils.isExistCamera(MainActivity.this)) {
                        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// 呼叫android自帶的照相機
                        Uri imageUri = Uri.fromFile(FileUtil.getHeadPhotoFileRaw());
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                        intent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
                        startActivityForResult(intent, ACTIVITY_CAMERA_REQUESTCODE);
                    } else {
                        Toast.makeText(MainActivity.this,
                                getResources().getString(R.string.user_no_camera),
                                Toast.LENGTH_SHORT).show();
                    }
                    break;
            }
        }
    };

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case ACTIVITY_ALBUM_REQUESTCODE:
                if (resultCode == Activity.RESULT_OK) {
                    if(data.getData() == null){
                        ToastUtils.toast(this, getString(R.string.pic_not_valid));
                        return;
                    }
                    CommonUtils.cutPhoto(this, data.getData(), true);
                }
                break;
            case ACTIVITY_CAMERA_REQUESTCODE:
                if (resultCode == Activity.RESULT_OK) {
                    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
                    bitmapOptions.inSampleSize = 2;
                    int degree = FileUtil.readPictureDegree(FileUtil.getHeadPhotoDir() + FileUtil.HEADPHOTO_NAME_RAW);
                    Bitmap cameraBitmap = BitmapFactory.decodeFile(FileUtil.getHeadPhotoDir() + FileUtil.HEADPHOTO_NAME_RAW, bitmapOptions);
                    cameraBitmap = FileUtil.rotaingImageView(degree, cameraBitmap);
                    FileUtil.saveCutBitmapForCache(this,cameraBitmap);
                    CommonUtils.cutPhoto(this, Uri.fromFile(FileUtil.getHeadPhotoFileRaw()), true);
                }
                break;
            case ACTIVITY_MODIFY_PHOTO_REQUESTCODE:
//                Bundle bundle = data.getExtras();
//                if (bundle != null) {
//                    Bitmap bitmap = bundle.getParcelable("data");
//                    if (bitmap == null) {
//                        return;
//                    }
//                    headImg.setImageBitmap(bitmap);
//                }
                String coverPath = FileUtil.getHeadPhotoDir()  + FileUtil.HEADPHOTO_NAME_TEMP;
                Bitmap bitmap = BitmapFactory.decodeFile(coverPath);
                imgCover.setImageBitmap(bitmap);
                //接下來是完成上傳功能
               /* HttpUtil.uploadCover(this, UrlContainer.UP_LIVE_COVER + "?uid="
                        + LoginUtils.getInstance(this), coverPath, this);*/
                //成功之後刪除臨時圖片
                FileUtil.deleteTempAndRaw();

                break;

        }
    }
}

拍照獲取圖片失敗的問題

過程中發現,如果拍照獲取圖片的儲存路徑與裁切後儲存的路徑一致的話會出現問題,所以分別設定了兩個路徑,請參考

  • FileUtil
    相關程式碼
public class FileUtil {
    private static final String TAG = "FileUtil";
    // 使用者頭像儲存位置
    private final static String HEADPHOTO_PATH = "/Android/data/com.soulrelay.uploadpic/";

    // 剪下頭像時臨時儲存頭像名字,完成或取消時刪除
    public final static String HEADPHOTO_NAME_TEMP = "user_photo_temp.jpg";
    //拍照臨時存取圖片
    public final static String HEADPHOTO_NAME_RAW = "user_photo_raw.jpg";

    // 剪下桌布圖片
    private final static String WALLPAPER = "wallpaper.jpg";


    public static String getCropPath(String path) {
        String storageState = Environment.getExternalStorageState();
        if (Environment.MEDIA_REMOVED.equals(storageState)) {
            return null;
        }

        String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath() + HEADPHOTO_PATH + "cache" + File.separator;

        String s = MD5.Md5Encode(path)+".jpg";
        return dirPath + s;
    }

    /**
     * 使用者頭像儲存路徑
     */
    public static String getHeadPhotoDir() {
        String storageState = Environment.getExternalStorageState();
        if (Environment.MEDIA_REMOVED.equals(storageState)) {
            return null;
        }
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + HEADPHOTO_PATH;
        SDCardUtil.mkdirs(path);
        return path;
    }

    /**
     * 剪下頭像時臨時儲存頭像名字,完成或取消時刪除
     */
    public static File getHeadPhotoFileTemp() {
        File file = new File(getHeadPhotoDir() + HEADPHOTO_NAME_TEMP);
        return file;
    }

    /**
     * 剪下頭像時臨時儲存頭像名字,完成或取消時刪除(用於拍照時儲存原始圖片)
     */
    public static File getHeadPhotoFileRaw() {
        File file = new File(getHeadPhotoDir() + HEADPHOTO_NAME_RAW);
        return file;
    }

    /**
     * 獲取剪下桌布圖片
     */
    public static File getWallPaperFile() {
        File file = new File(getHeadPhotoDir() + WALLPAPER);
        return file;
    }

    public static void saveCutBitmapForCache(Context context, Bitmap bitmap) {
        File file = new File(getHeadPhotoDir() + /*File.separator +*/ HEADPHOTO_NAME_RAW);
        try {
            FileOutputStream out = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 讀取圖片屬性:旋轉的角度
     * @param path 圖片絕對路徑
     * @return degree旋轉的角度
     */
    public static int readPictureDegree(String path) {
        int degree  = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }

    /**
     * 旋轉圖片
     * @param angle
     * @param bitmap
     * @return Bitmap
     */
    public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {
        //旋轉圖片 動作
        Matrix matrix = new Matrix();;
        matrix.postRotate(angle);
        System.out.println("angle2=" + angle);
        // 建立新的圖片
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
                bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return resizedBitmap;
    }

    /**
     * Delete the file/dir from the local disk
     * 
     */
    public static boolean deleteFile(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            return false;
        }

        File file = new File(filePath);
        if (!file.exists()) {
            Log.w(TAG, "the file is not exist while delete the file");
            return false;
        }

        return deleteDir(file);
    }

    /**
     * Delete the file from the local disk
     * 
     * @param dir
     */
    private static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            if (children != null) {
                // 遞迴刪除目錄中的子目錄下
                for (int i = 0; i < children.length; i++) {
                    boolean success = deleteDir(new File(dir, children[i]));
                    if (!success) {
                        return false;
                    }
                }
            }

        }
        if (!dir.canRead() || !dir.canWrite()) {
            Log.w(TAG, "has no permission to can or write while delete the file");
            return false;
        }
        // 目錄此時為空,可以刪除
        return dir.delete();
    }

    /**
     * 刪除臨時檔案(拍照的原始圖片以及臨時檔案)
     * @param path
     */
    public static void deleteTempAndRaw() {
       deleteFile(FileUtil.getHeadPhotoDir()  + FileUtil.HEADPHOTO_NAME_TEMP);
       deleteFile(FileUtil.getHeadPhotoDir()  + FileUtil.HEADPHOTO_NAME_RAW);
    }

}

拍照獲取圖片角度不正確的問題

   case ACTIVITY_CAMERA_REQUESTCODE:
                if (resultCode == Activity.RESULT_OK) {
                    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
                    bitmapOptions.inSampleSize = 2;
                    int degree = FileUtil.readPictureDegree(FileUtil.getHeadPhotoDir() + FileUtil.HEADPHOTO_NAME_RAW);
                    Bitmap cameraBitmap = BitmapFactory.decodeFile(FileUtil.getHeadPhotoDir() + FileUtil.HEADPHOTO_NAME_RAW, bitmapOptions);
                    cameraBitmap = FileUtil.rotaingImageView(degree, cameraBitmap);
                    FileUtil.saveCutBitmapForCache(this,cameraBitmap);
                    CommonUtils.cutPhoto(this, Uri.fromFile(FileUtil.getHeadPhotoFileRaw()), true);
                }
                break;

如果不存在圖片角度的問題可以直接呼叫(我測試的時候發現:型號相同,系統版本相同的手機上也有可能存在角度不統一的問題,如果有更好的處理方法,歡迎指教!)

CommonUtils.cutPhoto(this, Uri.fromFile(FileUtil.getHeadPhotoFileRaw()), true);

上傳成功之後刪除臨時檔案

可以根據伺服器介面形式,來進行圖片上傳,我這邊上傳的格式是
傳入兩個引數

  • uid 使用者id
  • image 圖片二進位制資料
 case ACTIVITY_MODIFY_PHOTO_REQUESTCODE:
                String coverPath = FileUtil.getHeadPhotoDir()  + FileUtil.HEADPHOTO_NAME_TEMP;
                Bitmap bitmap = BitmapFactory.decodeFile(coverPath);
                imgCover.setImageBitmap(bitmap);
                //接下來是完成上傳功能
               /* HttpUtil.uploadCover(this, UrlContainer.UP_LIVE_COVER + "?uid="
                        + LoginUtils.getInstance(this), coverPath, this);*/
                //成功之後刪除臨時圖片
                FileUtil.deleteTempAndRaw();

圖片上傳以及上傳之後的處理

大家可以在自己的網路請求中按照如下方式處理,然後根據上傳成功和失敗的回撥做相應的事情

ublic static void uploadCover(final Context context, final String url, final String path,
                                   final UploadLiveCoverListener listener) {
        ThreadPoolUtils.execute(new Runnable() {
            @Override
            public void run() {
                // 新增金鑰
                try {
                    HttpClient client = new DefaultHttpClient();
                    MultipartEntity multipartContent = getEntity(context,
                            new HashMap<String, String>());
                    FileBody fileBody = new FileBody(new File(path), "image/jpeg", "UTF-8");
                    multipartContent.addPart("image", fileBody);
                    String ret = doPost(context, url, multipartContent, client);
                    JSONObject json = new JSONObject(ret);
                    int status = json.getInt("status");
                    if (status == 0) {
                        JSONObject resultO = json.getJSONObject("result");
                        String imgUrl = resultO.getString("img_url");
                        listener.onUploadSuccess(imgUrl);
                    }else {
                        listener.onUploadFail(status);
                    }
                    deleteTempAndRaw(path);
                } catch (Exception e) {
                    listener.onUploadFail(-1);
                }
            }
        });

    }

程式碼下載