感谢stormzhang博客的思路:

Android中保存图片的方法可能有如下两种:

  • 第一种是自己写方法,如下代码:
public static File saveImage(Bitmap bmp) {
    File appDir = new File(Environment.getExternalStorageDirectory(), "Boohee");
    if (!appDir.exists()) {
        appDir.mkdir();
    }
    String fileName = System.currentTimeMillis() + ".jpg";
    File file = new File(appDir, fileName);
    try {
        FileOutputStream fos = new FileOutputStream(file);
        bmp.compress(CompressFormat.JPEG, 100, fos);
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
以上代码便是将Bitmap保存图片到指定的路径/sdcard/Boohee/下,
文件名以当前系统时间命名,但是这种方法保存的图片没有加入到系统图库中
  • 第二种是调用系统提供的插入图库的方法:
MediaStore.Images.Media.insertImage(getContentResolver(), bitmap, "title", "description");
调用以上系统自带的方法会把bitmap对象保存到系统图库中,
 但是这种方法无法指定保存的路径和名称,
 上述方法的title、description参数只是插入数据库中的字段,
 真实的图片名称系统会自动分配。

看似上述第二种方法就是我们要用到的方法,
但是可惜的调用上述第二种插入图库的方法图片并没有立刻显示在图库中,
而我们需要立刻更新系统图库以便让用户可以立刻查看到这张图片。
  • 更新系统图库的方法
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
上面那条广播是扫描整个sd卡的广播,如果你sd卡里面东西很多会扫描很久,在扫描当中我们是不能访问sd卡,
所以这样子用户体现很不好,所以下面我们还有如下的方法:
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File("/sdcard/Boohee/image.jpg"))););

或者还有如下方法:

final MediaScannerConnection msc = new MediaScannerConnection(mContext, new MediaScannerConnectionClient() {     
    public void onMediaScannerConnected() {     
        msc.scanFile("/sdcard/Boohee/image.jpg", "image/jpeg");     
    }     
    public void onScanCompleted(String path, Uri uri) {     
        Log.v(TAG, "scan completed");     
        msc.disconnect();     
    }     
});
上面代码的图片路径不管是通过自己写方法还是系统插入图库的方法都可以很容易的获取到。
  • 终极完美解决方案
那么到这里可能有人又会问了,如果我想把图片保存到指定的文件夹,同时又需要图片出现在图库里呢?
 答案是可以的,sdk还提供了这样一个方法:
MediaStore.Images.Media.insertImage(getContentResolver(), "image path", "title", "description");
上述方法的第二个参数是image path,这样的话就有思路了,首先自己写方法把图片指定到指定的文件夹,
然后调用上述方法把刚保存的图片路径传入进去,最后通知图库更新。

所以写了一个方法,完整的代码如下:

public static void saveImageToGallery(Context context, Bitmap bmp) {
    // 首先保存图片
    File appDir = new File(Environment.getExternalStorageDirectory(), "Boohee");
    if (!appDir.exists()) {
        appDir.mkdir();
    }
    String fileName = System.currentTimeMillis() + ".jpg";
    File file = new File(appDir, fileName);
    try {
        FileOutputStream fos = new FileOutputStream(file);
        bmp.compress(CompressFormat.JPEG, 100, fos);
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    // 其次把文件插入到系统图库
    try {
        MediaStore.Images.Media.insertImage(context.getContentResolver(),
                file.getAbsolutePath(), fileName, null);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    // 最后通知图库更新
    context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + path)));
}

有同学反映调用广播通知图库更新,但是不起作用,原因是 path 路径在代码中硬编码为 ‘/sdcard/xxxx’,这里需要使用 Eviroment.getExternalStorageDirectory 来获取 sd 卡路径,

这里是详细的分析过程:

http://droidyue.com/blog/2014/07/12/scan-media-files-in-android-chinese-edition/

  • 原作者Demo内容:
    1、ScannerUtils方法
package com.housheng.scanner.util;

import java.io.File;
import java.io.FileOutputStream;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

/**
 *
 */
public class ScannerUtils {

  // 扫描的三种方式
  public static enum ScannerType {
    RECEIVER, MEDIA
  }

  // 首先保存图片
  public static void saveImageToGallery(Context context, Bitmap bitmap, ScannerType type) {
    File appDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "哈哈哈");
    if (!appDir.exists()) {
      // 目录不存在 则创建
      appDir.mkdirs();
    }
    String fileName = System.currentTimeMillis() + ".jpg";
    File file = new File(appDir, fileName);
    try {
      FileOutputStream fos = new FileOutputStream(file);
      bitmap.compress(CompressFormat.JPEG, 100, fos); // 保存bitmap至本地
      fos.flush();
      fos.close();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (type == ScannerType.RECEIVER) {
        ScannerByReceiver(context, file.getAbsolutePath());
      } else if (type == ScannerType.MEDIA) {
        ScannerByMedia(context, file.getAbsolutePath());
      }
      if (!bitmap.isRecycled()) {
        // bitmap.recycle(); 当存储大图片时,为避免出现OOM ,及时回收Bitmap
        System.gc(); // 通知系统回收
      }
      Toast.makeText(context, "图片保存为" + file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
    }
  }

  /** Receiver扫描更新图库图片 **/

  private static void ScannerByReceiver(Context context, String path) {
    context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
            Uri.parse("file://" + path)));
    Log.v("TAG", "receiver scanner completed");
  }

  /** MediaScanner 扫描更新图库图片 **/

  private static void ScannerByMedia(Context context, String path) {
    MediaScannerConnection.scanFile(context, new String[] {path}, null, null);
    Log.v("TAG", "media scanner completed");
  }
}

2、mainActivity中:

package com.housheng.scanner;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;

import com.housheng.scanner.util.ScannerUtils;
import com.housheng.scanner.util.ScannerUtils.ScannerType;

/**
 * @author HouSheng
 */

public class MainActivity extends Activity {

  private Bitmap bitmap = null;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ee);
  }

  public void onReceiver(View view) {
    ScannerUtils.saveImageToGallery(MainActivity.this, bitmap, ScannerType.RECEIVER);
  }

  public void onMedia(View view) {
    ScannerUtils.saveImageToGallery(MainActivity.this, bitmap, ScannerType.MEDIA);
  }

}

感谢stormzhang的博客,

其中如下面写法,支持android7.1系统

MediaStore.Images.Media.insertImage(mContext.getContentResolver(),
                                    file.getAbsolutePath(), response.getName(), null);

                            MediaScannerConnection.scanFile(mContext, new String[]{file.getAbsolutePath()}, null, null);

权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>