Android调用系统相机和自定义相机实例

本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人SurfaceView出现黑屏的原因。

Android应用拍照的两种方式,下面为两种形式的Demo展示出来的效果。

知识点:

一、调用系统自带的相机应用

二、自定义我们自己的拍照界面

 

三、关于计算机解析图片原理(如何正确加载图片到Android应用中)

所需权限:

  1.  

一、调用系统照相机程序拍照(方式一)

1.定义所需要的权限
2.我们需要定义调用系统相机App的Intent,当然是通过设定IntentFilter中的Action来打开我们想要的activity了。

MediaStore.ACTION_IMAGE_CAPTURE - 这个Action将打开拍照的系统相机。返回一个Image

MediaStore.ACTION_VIDEO_CAPTURE - 这个Action将打开录像的系统相机。返回一个Video

在MediaStore.ACTION_IMAGE_CAPTURE中,我们可以看到这段话:

【The caller may pass an extra EXTRA_OUTPUT to control where this image will be

written. If the EXTRA_OUTPUT is not present, then a small sized image is returned

as a Bitmap object in the extra field. This is useful for applications that only

need a small image. If the EXTRA_OUTPUT is present, then the full-sized image will

be written to the Uri value of EXTRA_OUTPUT.】

3.API规定我们传入拍照得到图片的存储位置的Uri。否则Bimmap将以一个压缩后的形式返回到我们当前Activity.

intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name

则会把拍照的图片存储到我们传入的Uri对应的File里面。

4.我们调用startActivityForResult(intent)来启动这样一个系统相机app之后,然后在当前应用Activity的onActivityResult()中接受到返回拍照成功或者失败的消息,做相应处理。

5.“压缩处理”(Android应用中加载大图片),并显示到ImageView中。

二、自定义照相机

1.检查相机是否存在,并获取相机Camera。

2.创建一个相机图像预览类:extends SurfaceView 并 implements SurfaceHolder (我定义:MySurfaceView)
3.把这个预览类放入一个自定义布局layout里面,并且可以在layout里添加自己的其他按钮
4.设置对应的拍照按钮然后听事件
5.捕获照片和保存图片

6.释放掉我们使用的相机Camera,不然之后其他应用将无法使用它。

三、计算机解析图片的方式和Android中大图片Bitmap的压缩显示处理

这个问题有点老生长谈了,平时我们经常遇到一些图片资源,我们把它加载到内存发现抛出内存不够用的异常,即OOM,当然加载图片时出现的OOM情况有很多种,比如单张图片没有做压缩,导致图片占用内存过大而发生内存溢出,也有多张图片一次性加载进来,导致的内存溢出。

通常单张大图,我们加载进来往往会经过一个图片的压缩处理的过程,而如果多张图片加载,我们可能就需要一些缓存机制,再加上一些算法来保证程序不出现OOM。

我们这里想要讲的知识点跟单张大图比较有关系

首先,我们知道一个图片,它是由很多像素点来表示的,而像素点的个数只跟图片的分辨率有关,而跟图片所占的内存空间大小无关。比如我们的桌面壁纸:1280 * 768 的分辨率,那么它就有 1280 * 768 = 983040个像素点,这意味着什么呢?我们知道我们要表示一个像素点的颜色,最经常我们需要RGB三种颜色来表示,而R:0~255,相当于两个FF的位置,就是8位,这样的话RGB合起来,一个像素点的表示就需要24位(这就是我们平衡听到的24位图),而加上透明度的8位,就是平时说的32位图。那么一张图片,它加载到内存中的话,它会占用多大的空间呢?

计算方法:(像素点 * 一个像素所占用的byte数) / 1024 / 1024 (MB)

以1280 * 768 的分辨率,32位图为例:所占内存大小: ((1280 * 768 * (32 / 8)) / 1024)/1024=3.75(MB)

说了这么多,那么我们再来说下Android系统的规定吧,Android系统严格规定了每个应用所能分配的最大的内存为多少,我们知道有一个VM值(在我们创建模拟器的时候),这个VM值里面便是我们所说的堆空间(Heap Size),当你的应用占用的空间已经超出我们定义的堆空间大小,那么不好意思,OOM

这样的话,我们明白了图片的大小占据原理,还有尽量不要超出这个堆空间,那么OK,现在问题变得简单了。如果我们有一种方式可以在图片加载进来之前,知道图片的大小,然后改变它的长、宽,这样的话,分辨率便变小了,这样出来的乘积也就变小了。比如:我们的屏幕只有320 * 240, 这时候你加载大分辨的图片进来最多也只能显示成这样,所以我们常采用的是对图片进行压缩处理。这里有个概念叫压缩比:

长:1024 / 320 = 3.2 约等于 3

宽:768 / 240 = 3.2

那这样我们如果把图片压缩成这样大小的,最后的图片加载进来的大小便是

((320 * 240 * (32 / 8)) / 1024)/1024=0.29(MB)

希望我这样讲完,大家都能听懂了,我这里先把照相机实例中出现的关于如果处理这块图片的代码先粘出来

  1. //-----------------------Android大图的处理方式---------------------------
  2. private void setPicToImageView(ImageView imageView, File imageFile){
  3. int imageViewWidth = imageView.getWidth();
  4. int imageViewHeight = imageView.getHeight();
  5. BitmapFactory.Options opts = new Options();
  6.  
  7. //设置这个,只得到Bitmap的属性信息放入opts,而不把Bitmap加载到内存中
  8. opts.inJustDecodeBounds = true;
  9. BitmapFactory.decodeFile(imageFile.getPath(), opts);
  10.  
  11. int bitmapWidth = opts.outWidth;
  12. int bitmapHeight = opts.outHeight;
  13. //取最大的比例,保证整个图片的长或者宽必定在该屏幕中可以显示得下
  14. int scale = Math.max(imageViewWidth / bitmapWidth, imageViewHeight / bitmapHeight);
  15.  
  16. //缩放的比例
  17. opts.inSampleSize = scale;
  18. //内存不足时可被回收
  19. opts.inPurgeable = true;
  20. //设置为false,表示不仅Bitmap的属性,也要加载bitmap
  21. opts.inJustDecodeBounds = false;
  22.  
  23. Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getPath(), opts);
  24. imageView.setImageBitmap(bitmap);
  25. }

关于堆空间:

堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。

废话少说下面就看代码咯~~为了大家看起来方便点,代码的结构可能不是很规范!

源码下载地址:http://download.csdn.net/detail/u011133213/7844683

代码部分:

一、用系统的相机

按钮点击之后开启系统相机Activity

  1. findViewById(R.id.system_camera_btn).setOnClickListener(new OnClickListener() {
  2.  
  3. @Override
  4. public void onClick(View v) {
  5. Intent intent = new Intent();
  6. intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
  7. imageFileUri = getOutFileUri(TYPE_FILE_IMAGE);//得到一个File Uri
  8. intent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
  9. startActivityForResult(intent, SYSTEM_CAMERA_REQUESTCODE);
  10. }
  11. });

生成File文件,并得到Uri

  1. //-----------------------生成Uri---------------------------------------
  2. //得到输出文件的URI
  3. private Uri getOutFileUri(int fileType) {
  4. return Uri.fromFile(getOutFile(fileType));
  5. }
  6.  
  7. //生成输出文件
  8. private File getOutFile(int fileType) {
  9.  
  10. String storageState = Environment.getExternalStorageState();
  11. if (Environment.MEDIA_REMOVED.equals(storageState)){
  12. Toast.makeText(getApplicationContext(), "oh,no, SD卡不存在", Toast.LENGTH_SHORT).show();
  13. return null;
  14. }
  15.  
  16. File mediaStorageDir = new File (Environment
  17. .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
  18. ,"MyPictures");
  19. if (!mediaStorageDir.exists()){
  20. if (!mediaStorageDir.mkdirs()){
  21. Log.i("MyPictures", "创建图片存储路径目录失败");
  22. Log.i("MyPictures", "mediaStorageDir : " + mediaStorageDir.getPath());
  23. return null;
  24. }
  25. }
  26.  
  27. File file = new File(getFilePath(mediaStorageDir,fileType));
  28.  
  29. return file;
  30. }
  31. //生成输出文件路径
  32. private String getFilePath(File mediaStorageDir, int fileType){
  33. String timeStamp =new SimpleDateFormat("yyyyMMdd_HHmmss")
  34. .format(new Date());
  35. String filePath = mediaStorageDir.getPath() + File.separator;
  36. if (fileType == TYPE_FILE_IMAGE){
  37. filePath += ("IMG_" + timeStamp + ".jpg");
  38. }else if (fileType == TYPE_FILE_VEDIO){
  39. filePath += ("VIDEO_" + timeStamp + ".mp4");
  40. }else{
  41. return null;
  42. }
  43. return filePath;
  44. }

二、用自定义的相机

检测相机设备是否存在:

  1. /*检测相机是否存在*/
  2. private boolean checkCameraHardWare(Context context){
  3. PackageManager packageManager = context.getPackageManager();
  4. if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)){
  5. return true;
  6. }
  7. return false;
  8. }

按钮按下之后的判断:

  1. findViewById(R.id.myapp_camera_btn).setOnClickListener(new OnClickListener() {
  2.  
  3. @Override
  4. public void onClick(View v) {
  5. if (checkCameraHardWare(getApplicationContext())){
  6. Intent intent = new Intent(getApplicationContext(), MyCameraActivity.class);
  7. startActivity(intent);
  8. }else{
  9. Toast.makeText(getApplicationContext(), "没有相机存在", Toast.LENGTH_SHORT).show();
  10. }
  11. }
  12. });

自定义的SurfaceView类:

  1. package cn.panghu.camera;
  2.  
  3. import android.content.Context;
  4. import android.graphics.Canvas;
  5. import android.graphics.Rect;
  6. import android.hardware.Camera;
  7. import android.util.AttributeSet;
  8. import android.view.Surface;
  9. import android.view.SurfaceHolder;
  10. import android.view.SurfaceView;
  11.  
  12. public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{
  13.  
  14. private Camera camera = null;
  15. private SurfaceHolder surfaceHolder = null;
  16.  
  17. public MySurfaceView(Context context, Camera camera) {
  18. super(context);
  19. this.camera = camera;
  20. surfaceHolder = getHolder();
  21. surfaceHolder.addCallback(this);
  22. surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  23. }
  24. public MySurfaceView(Context context) {
  25. super(context);
  26. // TODO Auto-generated constructor stub
  27. }
  28.  
  29. @Override
  30. public void surfaceCreated(SurfaceHolder holder) {
  31. try{
  32. camera.setPreviewDisplay(surfaceHolder);
  33. camera.startPreview();
  34. }catch(Exception e){
  35. e.printStackTrace();
  36. }
  37. }
  38.  
  39. @Override
  40. public void surfaceChanged(SurfaceHolder holder, int format, int width,
  41. int height) {
  42. //根本没有可处理的SurfaceView
  43. if (surfaceHolder.getSurface() == null){
  44. return ;
  45. }
  46.  
  47. //先停止Camera的预览
  48. try{
  49. camera.stopPreview();
  50. }catch(Exception e){
  51. e.printStackTrace();
  52. }
  53.  
  54. //这里可以做一些我们要做的变换。
  55.  
  56. //重新开启Camera的预览功能
  57. try{
  58. camera.setPreviewDisplay(surfaceHolder);
  59. camera.startPreview();
  60. }catch(Exception e){
  61. e.printStackTrace();
  62. }
  63. }
  64.  
  65. @Override
  66. public void surfaceDestroyed(SurfaceHolder holder) {
  67.  
  68. }
  69.  
  70. }

自定义相机Activity类:(为了避免当用户按下Home键,之后再回到我们App中,SurfaceView变黑屏,我们需要将SurfaceView加载到FrameLayout中的代码写在onResume中)

  1. package cn.panghu.camera;
  2.  
  3. import java.io.File;
  4. import java.io.FileNotFoundException;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.text.SimpleDateFormat;
  8. import java.util.Date;
  9.  
  10. import android.app.Activity;
  11. import android.hardware.Camera;
  12. import android.hardware.Camera.PictureCallback;
  13. import android.net.Uri;
  14. import android.os.Bundle;
  15. import android.os.Environment;
  16. import android.util.Log;
  17. import android.view.View;
  18. import android.view.View.OnClickListener;
  19. import android.widget.Button;
  20. import android.widget.FrameLayout;
  21. import android.widget.Toast;
  22.  
  23. import com.example.camerademoapp.R;
  24.  
  25. public class MyCameraActivity extends Activity {
  26. private Button btn_camera_capture = null;
  27. private Button btn_camera_cancel = null;
  28. private Button btn_camera_ok = null;
  29.  
  30. private Camera camera = null;
  31. private MySurfaceView mySurfaceView = null;
  32.  
  33. private byte[] buffer = null;
  34.  
  35. private final int TYPE_FILE_IMAGE = 1;
  36. private final int TYPE_FILE_VEDIO = 2;
  37.  
  38. private PictureCallback pictureCallback = new PictureCallback() {
  39.  
  40. @Override
  41. public void onPictureTaken(byte[] data, Camera camera) {
  42. if (data == null){
  43. Log.i("MyPicture", "picture taken data: null");
  44. }else{
  45. Log.i("MyPicture", "picture taken data: " + data.length);
  46. }
  47.  
  48. buffer = new byte[data.length];
  49. buffer = data.clone();
  50. }
  51. };
  52. @Override
  53. protected void onCreate(Bundle savedInstanceState) {
  54. // TODO Auto-generated method stub
  55. super.onCreate(savedInstanceState);
  56. setContentView(R.layout.mycamera_layout);
  57.  
  58. btn_camera_capture = (Button) findViewById(R.id.camera_capture);
  59. btn_camera_ok = (Button) findViewById(R.id.camera_ok);
  60. btn_camera_cancel = (Button) findViewById(R.id.camera_cancel);
  61.  
  62. btn_camera_capture.setOnClickListener(new OnClickListener() {
  63. @Override
  64. public void onClick(View v) {
  65.  
  66. camera.takePicture(null, null, pictureCallback);
  67.  
  68. btn_camera_capture.setVisibility(View.INVISIBLE);
  69. btn_camera_ok.setVisibility(View.VISIBLE);
  70. btn_camera_cancel.setVisibility(View.VISIBLE);
  71. }
  72. });
  73. btn_camera_ok.setOnClickListener(new OnClickListener() {
  74.  
  75. @Override
  76. public void onClick(View v) {
  77. //保存图片
  78. saveImageToFile();
  79.  
  80. camera.startPreview();
  81. btn_camera_capture.setVisibility(View.VISIBLE);
  82. btn_camera_ok.setVisibility(View.INVISIBLE);
  83. btn_camera_cancel.setVisibility(View.INVISIBLE);
  84. }
  85. });
  86. btn_camera_cancel.setOnClickListener(new OnClickListener() {
  87.  
  88. @Override
  89. public void onClick(View v) {
  90.  
  91. camera.startPreview();
  92. btn_camera_capture.setVisibility(View.VISIBLE);
  93. btn_camera_ok.setVisibility(View.INVISIBLE);
  94. btn_camera_cancel.setVisibility(View.INVISIBLE);
  95. }
  96. });
  97. }
  98. @Override
  99. protected void onPause() {
  100. // TODO Auto-generated method stub
  101. super.onPause();
  102.  
  103. camera.release();
  104. camera = null;
  105. }
  106.  
  107. @Override
  108. protected void onResume() {
  109. // TODO Auto-generated method stub
  110. super.onResume();
  111. if (camera == null){
  112. camera = getCameraInstance();
  113. }
  114. //必须放在onResume中,不然会出现Home键之后,再回到该APP,黑屏
  115. mySurfaceView = new MySurfaceView(getApplicationContext(), camera);
  116. FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
  117. preview.addView(mySurfaceView);
  118. }
  119.  
  120. /*得到一相机对象*/
  121. private Camera getCameraInstance(){
  122. Camera camera = null;
  123. try{
  124. camera = camera.open();
  125. }catch(Exception e){
  126. e.printStackTrace();
  127. }
  128. return camera;
  129. }
  130.  
  131. //-----------------------保存图片---------------------------------------
  132. private void saveImageToFile(){
  133. File file = getOutFile(TYPE_FILE_IMAGE);
  134. if (file == null){
  135. Toast.makeText(getApplicationContext(), "文件创建失败,请检查SD卡读写权限", Toast.LENGTH_SHORT).show();
  136. return ;
  137. }
  138. Log.i("MyPicture", "自定义相机图片路径:" + file.getPath());
  139. Toast.makeText(getApplicationContext(), "图片保存路径:" + file.getPath(), Toast.LENGTH_SHORT).show();
  140. if (buffer == null){
  141. Log.i("MyPicture", "自定义相机Buffer: null");
  142. }else{
  143. try{
  144. FileOutputStream fos = new FileOutputStream(file);
  145. fos.write(buffer);
  146. fos.close();
  147. }catch(IOException e){
  148. e.printStackTrace();
  149. }
  150. }
  151. }
  152.  
  153. //-----------------------生成Uri---------------------------------------
  154. //得到输出文件的URI
  155. private Uri getOutFileUri(int fileType) {
  156. return Uri.fromFile(getOutFile(fileType));
  157. }
  158.  
  159. //生成输出文件
  160. private File getOutFile(int fileType) {
  161.  
  162. String storageState = Environment.getExternalStorageState();
  163. if (Environment.MEDIA_REMOVED.equals(storageState)){
  164. Toast.makeText(getApplicationContext(), "oh,no, SD卡不存在", Toast.LENGTH_SHORT).show();
  165. return null;
  166. }
  167.  
  168. File mediaStorageDir = new File (Environment
  169. .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
  170. ,"MyPictures");
  171. if (!mediaStorageDir.exists()){
  172. if (!mediaStorageDir.mkdirs()){
  173. Log.i("MyPictures", "创建图片存储路径目录失败");
  174. Log.i("MyPictures", "mediaStorageDir : " + mediaStorageDir.getPath());
  175. return null;
  176. }
  177. }
  178.  
  179. File file = new File(getFilePath(mediaStorageDir,fileType));
  180.  
  181. return file;
  182. }
  183. //生成输出文件路径
  184. private String getFilePath(File mediaStorageDir, int fileType){
  185. String timeStamp =new SimpleDateFormat("yyyyMMdd_HHmmss")
  186. .format(new Date());
  187. String filePath = mediaStorageDir.getPath() + File.separator;
  188. if (fileType == TYPE_FILE_IMAGE){
  189. filePath += ("IMG_" + timeStamp + ".jpg");
  190. }else if (fileType == TYPE_FILE_VEDIO){
  191. filePath += ("VIDEO_" + timeStamp + ".mp4");
  192. }else{
  193. return null;
  194. }
  195. return filePath;
  196. }
  197.  
  198. }

Android调用系统相机、自定义相机、处理大图片的更多相关文章

  1. android调用系统的自定义裁剪后得到的图片不清晰,使用MediaStore.EXTRA_OUTPUT获取缓存下的清晰图片

    在使用系统自带的剪切图片工具时,通常只能设置一些比较小的值,像 intent.putExtra("outputX", 320); intent.putExtra("out ...

  2. Android调用系统相机、自己定义相机、处理大图片

    Android调用系统相机和自己定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,而且因为涉及到要把拍到的照片显示出来,该样例也会涉及到Android载入大图片时候的处 ...

  3. Android 用 camera2 API 自定义相机

    前言 笔者因为项目需要自定义相机,所以了解了一下 Android 关于 camera 这块的 API.Android SDK 21(LOLLIPOP) 开始已经弃用了之前的 Camera 类,提供了 ...

  4. Swift—调用系统相册和相机

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px Menlo; color: #000000 } p.p2 { margin: 0.0px 0. ...

  5. ios调用系统相册、相机 显示中文标题、本地化多语言支持

    因为调用系统相册.相机需要显示中文,所以搞了半天才知道是在Project->info->Custom ios Target Properties 添加 Localizations 并加入C ...

  6. iOS调用系统相册、相机 显示中文标题

    解决手机语言已经设置显示中文 在调用系统相册.相机界面 时显示英文问题, 在 info.plist里面添加Localized resources can be mixed          YES 表 ...

  7. Android调用系统相册和拍照的Demo

    最近我在群里看到有好几个人在交流说现在网上的一些Android调用系统相册和拍照的demo都有bug,有问题,没有一个完整的.确实是,我记得一个月前,我一同学也遇到了这样的问题,在低版本的系统中没问题 ...

  8. Android 调用系统分享文字、图片、文件,可直达微信、朋友圈、QQ、QQ空间、微博

    原文:Android 调用系统分享文字.图片.文件,可直达微信.朋友圈.QQ.QQ空间.微博 兼容SDK 18以上的系统,直接调用系统分享功能,分享文本.图片.文件到第三方APP,如:微信.QQ.微博 ...

  9. Android开发中如何解决加载大图片时内存溢出的问题

    Android开发中如何解决加载大图片时内存溢出的问题    在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...

随机推荐

  1. Codeforces 86D - Powerful array(莫队算法)

    题目链接:http://codeforces.com/problemset/problem/86/D 题目大意:给定一个数组,每次询问一个区间[l,r],设cnt[i]为数字i在该区间内的出现次数,求 ...

  2. First Missing Positive——数学类

    转:http://blog.csdn.net/nanjunxiao/article/details/12973173 Given an unsorted integer array, find the ...

  3. python开发学习-day02(元组、字符串、列表、字典深入)

    s12-20160109-day02 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...

  4. 使用STL sort对字符串按字典序排序

    使用string数组 #include<iostream> #include<string> #include<algorithm> using namespace ...

  5. LoadRunner去除事物中的程序的执行时间

    大家在性能测试过程中,经常会用到程序处理或组织数据,以达到一定的测试目的,但是程序本身执行会消耗一些时间,这部分消耗的时间是包含在响应时间里面,此时,响应时间=正常响应时间+程序执行消耗时间.那么如何 ...

  6. 14:Spark Streaming源码解读之State管理之updateStateByKey和mapWithState解密

    首先简单解释一下)) //要使用updateStateByKey方法,必须设置Checkpoint. ssc.checkpoint("/checkpoint/") val sock ...

  7. 用memcached实现的php锁机制

    <?php /** * 使用Memcache实现给进程加锁的类 * * Copyright (C) 2013 JeffJing * * 一些时候需要让系统的某些操作串行化,这个时候就要对这些操作 ...

  8. Scrollify – jQuery全屏滚动插件

    和 fullPage.js 一样,Scrollify 也是一款基于 jQuery 的全屏滚动插件.跟 fullPage.js 相比,Scrollify 更加小巧,压缩后不足 4KB.但功能上不如 fu ...

  9. vue使用路由判断是否登录

    router.beforeEach((to, from, next) => { // console.log('to:' + to.path) if (to.path.startsWith('/ ...

  10. 最短路——spfa

    适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了. 我们约定有向加权图G不存在负权回路,即最短路径一 ...