前言:
调用系统自带的分享功能将文本,当前界面截图、CSV和PDF文件分享出去;
效果图【测试机型:小米10】:
添加依赖:
implementation 'com.itextpdf:itextg:5.5.10'
1、代码部分
①:工具类:FileShareUtils
public class FileShareUtils {
private static final String TAG = "FileShareUtils";
//文件输出流
private static OutputStream outputStream;
/**
* 维度。A4尺寸为210×297毫米或8.27×11.69英寸。
* 在PostScript中,其尺寸四舍五入为595×842点。
*/
private static final int A4_WIDTH = 2520 / 2; // 210 * 6
private static final int A4_HEIGHT = 3564 / 2; // 297 * 6
/**
* 创建csv文件
*
* @param context 上下文
* @param datas Lists of csv data
*/
public static void shareCsvFile(Context context, List<String> datas) {
//应用路径:/storage/emulated/0/Android/data/你的应用包名/files/test
File csvFile = context.getExternalFilesDir("HelloWord");
if (!csvFile.exists()) {
// 如果你想在已经存在的文件夹(zainar)下建立新的文件夹(database),就可以用此方法。
// 此方法不能在不存在的文件夹下建立新的文件夹。假如想建立名字是"database"文件夹,那么它的父文件夹必须存在。
csvFile.mkdir();
}
String fileName = getFileName();
//时间戳.csv
File file = new File(csvFile, fileName + ".csv");
try {
//创建一个文件夹
file.createNewFile();
} catch (IOException e) {
Log.e(TAG, "createCsvFile: " + e.getMessage());
}
//分享CSV
startIntent(context, getUriForFile(context, writeDataToFile(file, datas)), fileName, 0);
}
/**
* 写数据至文件夹中,创建file
*
* @param file 创建CSV文件对象
* @param dataList 分享的数组
* @return
*/
private static File writeDataToFile(final File file, List<String> dataList) {
//目录是否存在该文件
if (file.exists()) {
try {
outputStream = new FileOutputStream(file);
} catch (FileNotFoundException e) {
Log.e(TAG, "writeDataToFile: " + e.getMessage());
}
//文件输出流
final OutputStream putStream = outputStream;
try { //写入Utf-8文件头
//在utf-8编码文件中BOM在文件头部,占用三个字节,用来标示该文件属于utf-8编码,
//现在已经有很多软件识别bom头,但是还有些不能识别bom头,比如PHP就不能识别bom头,
//这也是用记事本编辑utf-8编码后执行就会出错的原因了
putStream.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
} catch (IOException e) {
e.printStackTrace();
}
//减去1的标题栏
String[] headerArray = new String[dataList.size() - 1];
headerArray = dataList.toArray(headerArray);
//1、普通形式
try {
for (int i = 0; i < headerArray.length; i++) {
putStream.write(headerArray[i].getBytes());
}
putStream.close();//关闭流
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "writeDataToFile: " + e.getMessage());
}
} else {
//无法创建CSV文件
Log.e(TAG, "创建CSV文件失败");
}
return file;
}
/**
* 创建Pdf
*/
public static void sharePdfFile(Context mContext) {
//1、关联布局
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_pdf_view, null);
//设置宽高为A4纸大小
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(A4_WIDTH, A4_HEIGHT);
view.setLayoutParams(params);
//2、布局View转化为Bitmap
view.measure(View.MeasureSpec.makeMeasureSpec(A4_WIDTH, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(A4_HEIGHT, View.MeasureSpec.EXACTLY));
Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.layout(0, 0, view.getLayoutParams().width, view.getLayoutParams().height);
view.draw(canvas);
//3、创建BitmapPDF
String fileName = getFileName();
File file = createBitmapPdf(mContext, fileName, bitmap);
//4、Intent意图-分享PDF
startIntent(mContext, getUriForFile(mContext, file), fileName, 1);
}
/**
* 创建PDF文件
* https://github.com/itext/itextpdf/tree/itextg
* iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。
* 通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。
* iText 官网:http://itextpdf.com/
* iText 开发文档: http://developers.itextpdf.com/developers-home
*/
public static File createBitmapPdf(Context context, String fileName, Bitmap bitmap) {
if (null == bitmap) {
return null;
}
File dir = getFileUrl(context);
//是否存在该文件,不存在则创建
if (!dir.exists()) {
dir.mkdir();
}
File file = new File(dir, fileName + ".pdf");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
//Page大小,左右上下边距【注意依赖库的包】
Document document = new Document(PageSize.A4, 0, 0, 0, 0);
try {
PdfWriter.getInstance(document, new FileOutputStream(file));
document.open();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
//png、质量100
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
//Image注意导com.itextpdf.text的包
Image image = Image.getInstance(byteArray);
//将图像缩放到一定的百分比。
image.scalePercent(50);
document.add(image);
document.close();
} catch (DocumentException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
/**
* 创建Bitmap图片
*/
public static void sharePicFile(Activity context) {
String fileName = getFileName();
File path = getFileUrl(context);
saveBitmap(context, path, viewToBitmap(context), fileName);
File file = new File(path + "/" + fileName + ".png");
startIntent(context, getUriForFile(context, file), fileName, 2);
Log.e(TAG, "sharePicFile: " + file.toString());
}
/**
* 当前界面转化为Bitmap
* 需要截取状态栏则将stateHeight设置为0
*/
private static Bitmap viewToBitmap(Activity activity) {
Bitmap bitmap;
View view = activity.getWindow().getDecorView();
//设置是否可以进行绘图缓存
view.setDrawingCacheEnabled(true);
//如果绘图缓存无法,强制构建绘图缓存
view.buildDrawingCache();
//返回这个缓存视图
bitmap = view.getDrawingCache();
//获取状态栏高度(90)
Rect frame = new Rect();
//测量屏幕宽和高
view.getWindowVisibleDisplayFrame(frame);
int stateHeight = frame.top;
Display display = activity.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e(TAG, "stateHeight size:" + stateHeight);
Log.e(TAG, "width size:" + width);
Log.e(TAG, "height size:" + height);
// 根据坐标点和需要的宽和高创建bitmap
bitmap = Bitmap.createBitmap(bitmap, 0, stateHeight, width, height - stateHeight);
return bitmap;
}
/**
* 保存图片
*
* @param context
* @param dir
* @param bitmap
* @param fileName
*/
@SuppressLint("SdCardPath")
public static void saveBitmap(Context context, File dir, Bitmap bitmap, String fileName) {
if (!dir.exists()) {
dir.mkdir();
}
File file = new File(dir, fileName + ".png");
FileOutputStream out;
try {
out = new FileOutputStream(file);
if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)) {
out.flush();//空方法体,此输出流并强制写出所有缓冲的输出字节
out.close();//关闭流
bitmap.recycle();
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "saveBitmap: " + e.getMessage());
}
//发送广播更新,扫描某个文件(文件绝对路径,必须是以 Environment.getExternalStorageDirectory() 方法的返回值开头)
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + file.getAbsolutePath())));
}
/**
* 分享文本
*/
public static void shareText(Context context) {
Intent intent = new Intent(Intent.ACTION_SEND);
//指定包名:注意判断是否安装微信、QQ等;否则报错ActivityNotFoundException: No Activity found to handle Intent
//微信:com.tencent.mm QQ:com.tencent.mobileqq
//intent.setPackage("com.tencent.mobileqq");//不指定包名则会显示所有可分享的应用
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "分享的内容");
context.startActivity(intent);
}
/**
* 当前时间戳作为分享的文件名
*/
private static String getFileName() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
}
/**
* 返回uri
*/
private static Uri getUriForFile(Context context, File file) {
//应用包名.fileProvider
String authority = context.getPackageName().concat(".fileProvider");
Uri fileUri = FileProvider.getUriForFile(context, authority, file);
Log.e(TAG, "onSuccess: 文件路径:" + authority);
Log.e(TAG, "onSuccess: 文件路径:" + fileUri.toString());
Log.e(TAG, "onSuccess: 文件路径:" + file.toString());
return fileUri;
}
/**
* 返回文件夹
*/
private static File getFileUrl(Context context) {
File root = context.getFilesDir();
File dir = new File(root, "hello/");
if (!dir.exists()) {
//创建失败
if (!dir.mkdir()) {
Log.e(TAG, "createBitmapPdf: 创建失败");
}
}
return dir;
}
/**
* 分享CSV文件
* true:csv false:pdf
*/
@SuppressLint("WrongConstant")
private static void startIntent(Context context, Uri fileUri, String fileName, int isType) {
Log.e(TAG, "startIntent: " + fileUri.toString());
Log.e(TAG, "startIntent: " + fileName);
Intent share = new Intent(Intent.ACTION_SEND);
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
share.putExtra(Intent.EXTRA_STREAM, fileUri);
share.putExtra(Intent.EXTRA_SUBJECT, fileName);
String title = "分享标题";
if (isType == 0) {
share.setType("application/vnd.ms-excel");
context.startActivity(Intent.createChooser(share, title));
} else if (isType == 1) {
share.setType("application/pdf");
//管理应用程序包
PackageManager packageManager = context.getPackageManager();
//该组件或应用程序处于默认开启状态(其在清单指定)。
List<ResolveInfo> list = packageManager.queryIntentActivities(share, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
if (null == list || list.size() == 0) {
Toast.makeText(context, "没有找到可阅读PDF程序", Toast.LENGTH_SHORT).show();
} else {
context.startActivity(Intent.createChooser(share, title));
}
} else if (isType == 2) {
share.setType("image/*");
//安卓版本是否大于7.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.startActivity(Intent.createChooser(share, title));
} else {
share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(getFileUrl(context), title)));
context.startActivity(share);
}
}
}
}
②:使用方法:ShareActivity
public class ShareActivity extends AppCompatActivity {
private static final String TAG = "ShareActivity";
//CSV标题
private List<String> listTitles = new ArrayList<>();
//CSV每行list
private List<String> listStrings = new ArrayList<>();
//CSV内容
private List<String> listContents = new ArrayList<>();
//CSV合并后的list
private List<String> mergeList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share);
}
/**
* 分享CSV文件
*/
public void onCsvClick(View view) {
for (int i = 0; i < 5; i++) {
listTitles.add("标题" + i + ",");
}
listTitles.add("标题4\n");
for (int i = 0; i < 5; i++) {
listStrings.add("内容" + i);
}
listStrings.add("内容4\n");
//去除空格
String str = listStrings.toString().replace(" ", "");
//去除收尾[]符号
String result = str.substring(1, str.length() - 1);
for (int i = 0; i < 5; i++) {
listContents.add(result);
}
//合并List
for (String value : listTitles) {
mergeList.add(value);
}
for (String value : listContents) {
mergeList.add(value);
}
//分享CSV文件
FileShareUtils.shareCsvFile(this, mergeList);
}
/**
* 分享PDF文件
*/
public void onPdfClick(View view) {
String authority = getPackageName().concat(".fileProvider");
Log.e(TAG, "onPdfClick: " + authority);
FileShareUtils.sharePdfFile(this);
}
/**
* 分享图片文件
*/
public void onPicClick(View view) {
FileShareUtils.sharePicFile(this);
}
/**
* 分享文字
*/
public void onTextClick(View view) {
FileShareUtils.shareText(this);
}
}
对应布局:activity_share
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ShareActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onCsvClick"
android:text="CSV分享" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onPdfClick"
android:text="PDF分享" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onPicClick"
android:text="Picture分享" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onTextClick"
android:text="Text分享" />
</LinearLayout>
对应PDF分享布局:layout_pdf_view
<?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="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@mipmap/ic_launcher" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="按钮" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="1.2"
android:text="Android是由Google公司和开放手机联盟领导并开发的一种基于Linux的自由且开放源代码的操作系统,主要使用于移动设备。其最初由Andy Rubin开发,后被Google于2005年8月收购。之后Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟,共同研发改良Android系统,完成开发后,Google以Apache开源许可证的授权方式,发布了Android的源代码。"
android:textSize="16sp" />
</LinearLayout>
③:app/res目录下新建provider_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="Hello_Word/" />
<external-path
name="external_storage_root"
path="." />
<root-path name="root_path" path="."/>
</paths>
④:清单文件AndroidManifest.xml添加
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>