一.概述
android的 图片拍照 ,相册选图,以及图片剪切功能可以说非常常用. 尤其是图片上传功能,必然用到此功能. 而公司最近的一个项目中正好用到该功能. 记录下来以便以后再次用到,直接拿来使用.
在此之前,我也参考了网上很多代码示例, 写得都不错, 但是有一个问题可能大家都没发现, 当我参考网上示例写完后,发现 小米手机竟然不能使用该功能, 最后查了很多资料依然不能解决,最后猜测要么是 小米手机bug,要么就是 小米把android底层修改的过火了.
但是不管怎么样,必须要解决这个问题,毕竟用小米手机的人不在少数. 最后经人指点,终于搞定.
二.运行效果图
点击圆形头像弹出自定义Dialog
代码如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv= (CircleImageView) findViewById(R.id.iv);
iv.setBorderWidth(5);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new ActionSheetDialog(MainActivity.this).Builder()
.addSheetItem("拍照", ActionSheetDialog.SheetItemColor.BULE, new ActionSheetDialog.OnSheetItemClickListener() {
@Override
public void onClick(int witch) {
cameraorpic = 1;
openCamera();
}
}).addSheetItem("打开相册",ActionSheetDialog.SheetItemColor.BULE, new ActionSheetDialog.OnSheetItemClickListener() {
@Override
public void onClick(int witch) {
cameraorpic = 0;
openPic();
}
}).show();
}
});
}
/**
* 打开相册
*/
private void openPic() {
Intent pickIntent = new Intent(Intent.ACTION_PICK, null);
pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
startActivityForResult(pickIntent, REQUESTCODE_PICK);
}
/**
* 打开相机
*/
private void openCamera() {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File outDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
if (!outDir.exists()) {
outDir.mkdirs();
}
outFile = new File(outDir, System.currentTimeMillis() + ".jpg");
Log.e("outFile",outFile+"");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outFile));
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
startActivityForResult(intent, PHOTO_REQUEST_TAKEPHOTO);
} else {
Log.e("CAMERA", "请确认已经插入SD卡");
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 进行判断是那个操作跳转回来的,如果是裁剪跳转回来的这块就要把图片现实到View上,其他两种的话都把数据带入裁剪界面
switch (requestCode) {
//相册
case REQUESTCODE_PICK:
if (data == null || data.getData() == null) {
return;
}
startPhotoZoom(data.getData());
break;
//裁剪
case REQUESTCODE_CUTTING:
if (data != null) {
setPicToView(data);
}
break;
//拍照
case PHOTO_REQUEST_TAKEPHOTO:
Log.e("outFile1",outFile+"");
startPhotoZoom(Uri.fromFile(outFile));
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* 把裁剪好的图片设置到View上或者上传到网络
* @param data
*/
private void setPicToView(Intent data) {
Bundle extras = data.getExtras();
if (extras != null) {
/** 可用于图像上传 */
currentBitmap = extras.getParcelable("data");
iv.setImageBitmap(currentBitmap);
}
}
/**
* 调用系统的图片裁剪
* @param data
*/
private void startPhotoZoom(Uri data) {
Log.e("outFile2",outFile+"");
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(data, "image/*");
intent.putExtra("crop", true);
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 300);
intent.putExtra("outputY", 300);
intent.putExtra("scale", true);//黑边
intent.putExtra("scaleUpIfNeeded", true);//黑边
intent.putExtra("return-data", true);
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, REQUESTCODE_CUTTING);
}
上面的CircleImageView 是一个 圆形头像, 可以自定义实现也可以用 第三方库,这里用的是第三方的库
de.hdodenhof.circleimageview.CircleImageView --- github上面有,使用方法非常简单 配置如下所示
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/img_head"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="@dimen/space_xl"
android:layout_marginTop="@dimen/space_xl"
android:src="@mipmap/default_head"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
这里的难点是这个自定义的 Dialog如下
public class ActionSheetDialog {
private Context context;
private Dialog dialog;
private TextView txt_title;
private TextView txt_cancel;
private LinearLayout lLayout_content;
private ScrollView sLayout_content;
private boolean showTitle = false;
private List<SheetItem> sheetItemList;
private Display display;
public ActionSheetDialog(Context context){
this.context = context;
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
display = windowManager.getDefaultDisplay();
}
public ActionSheetDialog Builder(){
// 获取Dialog布局
View view = LayoutInflater.from(context).inflate(R.layout.view_actionsheet,null);
// dialog的最小宽,设置屏幕宽度为
view.setMinimumWidth(display.getWidth());
//获取xml文件中的控件
sLayout_content = (ScrollView) view.findViewById(R.id.sLayout_content);
lLayout_content = (LinearLayout) view.findViewById(R.id.lLayout_content);
txt_title = (TextView) view.findViewById(R.id.txt_title);
txt_cancel = (TextView) view.findViewById(R.id.txt_cancel);
txt_cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
//定义 dialog的布局和参数
dialog = new Dialog(context, R.style.ActionSheetDialogStyle);
dialog.setContentView(view);
Window dialogWindow = dialog.getWindow();
dialogWindow.setGravity(Gravity.LEFT | Gravity.BOTTOM);
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
lp.x = 0;
lp.y = 0;
dialogWindow.setAttributes(lp);
return this;
}
public ActionSheetDialog setTitle(String title) {
showTitle = true;
txt_title.setVisibility(View.VISIBLE);
txt_title.setText(title);
return this;
}
public ActionSheetDialog setCancelable(boolean cancel) {
dialog.setCancelable(cancel);
return this;
}
public ActionSheetDialog setCanceledOnTouchOutside(boolean cancel) {
dialog.setCanceledOnTouchOutside(cancel);
return this;
}
public ActionSheetDialog addSheetItem(String itemName,SheetItemColor itemTextColor,OnSheetItemClickListener listener){
if(null == sheetItemList){
sheetItemList = new ArrayList<SheetItem>();
}
sheetItemList.add(new SheetItem(itemName, itemTextColor, listener));
return this;
}
public void show(){
setSheetItems();
dialog.show();
}
private void setSheetItems() {
if (sheetItemList == null || sheetItemList.size() <= 0){
return;
}
int size = sheetItemList.size();
// 控制高度
if (size > 5){
LayoutParams params = sLayout_content.getLayoutParams();
params.height = display.getHeight()/2;
sLayout_content.setLayoutParams(params);
}
for (int i = 1; i <= size; i++) {
final int index = i;
SheetItem sheetItem = sheetItemList.get(i-1);
String itemName = sheetItem.name;
SheetItemColor itemTextcolor = sheetItem.color;
final OnSheetItemClickListener listener = sheetItem.listener;
TextView textView = new TextView(context);
textView.setText(itemName);
textView.setTextSize(18);
textView.setGravity(Gravity.CENTER);
if (size == 1){
if(showTitle){
textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector);
}else{
textView.setBackgroundResource(R.drawable.actionsheet_top_selector);
}
}else {
if (showTitle){
if (i >= 1 && i < size) {
textView.setBackgroundResource(R.drawable.actionsheet_middle_selector);
} else {
textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector);
}
}else {
if (i == 1) {
textView.setBackgroundResource(R.drawable.actionsheet_top_selector);
} else if (i < size) {
textView.setBackgroundResource(R.drawable.actionsheet_middle_selector);
} else {
textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector);
}
}
}
//字体颜色
if (null != itemTextcolor){
textView.setTextColor(Color.parseColor(itemTextcolor.getName()));
}else{
textView.setTextColor(Color.parseColor(SheetItemColor.BULE.getName()));
}
//高度
float scale = context.getResources().getDisplayMetrics().density;
int height = (int) (45 * scale + 0.5f);
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height));
//点击事件
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onClick(index);
dialog.dismiss();
}
});
lLayout_content.addView(textView);
}
}
public interface OnSheetItemClickListener{
void onClick(int witch);
}
private class SheetItem{
String name;
OnSheetItemClickListener listener;
SheetItemColor color;
public SheetItem(String name,SheetItemColor color,OnSheetItemClickListener listener) {
this.name = name;
this.listener = listener;
this.color = color;
}
}
public enum SheetItemColor{
BULE("#037BFF"),RED("#FD4A2E");
String name ;
private SheetItemColor(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
自定义Dialog对应布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp" >
<TextView
android:id="@+id/txt_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/actionsheet_top_normal"
android:gravity="center"
android:minHeight="45dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:textColor="@color/actionsheet_gray"
android:textSize="13sp"
android:visibility="gone"
/>
<ScrollView
android:id="@+id/sLayout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fadingEdge="none"
>
<LinearLayout
android:id="@+id/lLayout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</ScrollView>
<TextView
android:id="@+id/txt_cancel"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="8dp"
android:background="@drawable/actionsheet_single_selector"
android:gravity="center"
android:text="取消"
android:textColor="@color/actionsheet_blue"
android:textSize="18sp"
/>
</LinearLayout>
自定义Dialog 弹出和收回动画
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromYDelta="100%"
android:toYDelta="0" />
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromYDelta="0"
android:toYDelta="100%" />
自定义Dialog 条目的 点击背景selector就不给出了, 很简单点击一下背景变成浅灰色
以上就是所有代码了, 当然如果觉得自定义dialog麻烦, 完全可以用 popupwindow来代替, 事实上网上很多 类似代码都是用的 popupwindow做的.
需要注意的一点是: 如果要做图片上传操作, 需要加入sd卡 的读写权限.
顺便说2句文件上传吧 . 我在项目中采用的是 按照图片路径方式进行上传的, 因为接口写不出来 以流的方式上传,所以 安卓端 也只能按照路径上传了.但是由于我们最终剪切的图片转成了一个Bitmap对象,所以要想获取这个Bitmap对象所在的路径是不容易获取到的, 因为拍照路径我们存到了outFile中, 而从相册选择的路径位于 content://media/external/images/media "媒体库"中, 他们路径是不同的, 这就为上传带来麻烦了. 为什么相册选择图片位于这里呢, 这就是前面我说到的为了适应小米手机.
我的最终解决办法是: 把这个最终的 Bitmap对象 转成流存入到 sd卡中比如: Environment.getxxxsdpath+"/temp","temp.jpg" 这样无论是 拍照还是 相册选择的 ,最终他们的路径都变成了sd路径下的temp.jpg, 那么拿着这个路径就可以上传图片了.
尽管有点麻烦,但是解决了问题.