版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第第2 2章章 异步加载图片异步加载图片理论部分理论部分本章目标为什么要异步加载图片为什么要异步加载图片ExecutorServiceExecutorService线程池线程池SoftReferenceSoftReference的作用的作用异步加载图片的实现方式异步加载图片的实现方式为什么要异步加载图片为什么要异步加载图片为什么要异步加载图片u 提高用户体验提高用户体验u 先加载文字信息,后加载图片等信息先加载文字信息,后加载图片等信息u 提高响应速度提高响应速度实现异步加载Handler+Runnable Handler+Runnable private void loadImage(fin
2、al String url, final int id) handler.post(new Runnable() public void run() Drawable drawable = null; try drawable = Drawable.createFromStream(new URL(url).openStream(), image.png); catch (IOException e) (ImageView) LazyLoadImageActivity.this.findViewById(id).setImageDrawable(drawable); ); 经测试经测试 ,不能
3、实现异步加载,因为,不能实现异步加载,因为Handler和主线程是处于一个线程和主线程是处于一个线程实现异步加载Handler+Thread+Message Handler+Thread+Message u 采用采用handler+Threadhandler+Thread模式实现多线程异步加载模式实现多线程异步加载 u 在主线程中在主线程中newnew一个一个HandlerHandler对象对象private void loadImage2(final String url, final int id) Thread thread = new Thread() Override public
4、void run() Drawable drawable = null; try drawable = Drawable.createFromStream(new URL(url).openStream(), image.png); catch (IOException e) Message message= handler2.obtainMessage() ; message.arg1 = id; message.obj = drawable; handler2.sendMessage(message); ; thread.start(); thread = null; final Hand
5、ler handler2=new Handler() Override public void handleMessage(Message msg) (ImageView) LazyLoadImageActivity.this.findViewById(msg.arg1).setImageDrawable(Drawable)msg.obj); ; 以上可以实现异步加载了,还需要优化,引入线程池,管理线程以上可以实现异步加载了,还需要优化,引入线程池,管理线程ExecutorServiceExecutorServiceExecutorServiceu 线程池的作用线程池的作用 减少了创建和销毁线
6、程的次数,每个工作线程都可以被重复利用,可执行多个任务 可以根据系统的承受能力,调整线程池中工作线线程的数目防止因为因为消耗过多的内存u 创建创建ExecutorServiceExecutorService对象对象u 常用方法常用方法submit(Runnable task)ExecutorService executorService = Executors.newFixedThreadPool(n); 实现异步加载ExecutorServiceExecutorServiceu 引入线程池来管理多线程引入线程池来管理多线程 u 在主线程中加入在主线程中加入 private void load
7、Image3(final String url, final int id) executorService.submit(new Runnable() public void run() try final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), image.png); handler.post(new Runnable() public void run() (ImageView) LazyLoadImageActivity.this.findViewById(id).setImage
8、Drawable(drawable); ); catch (Exception e) throw new RuntimeException(e); ); private ExecutorService executorService = Executors.newFixedThreadPool(5); 考虑到效率问题我们可以引入内存缓存机制考虑到效率问题我们可以引入内存缓存机制 SoftReferenceSoftReferenceSoftReferenceu 当垃圾收集器启动时,会回收如下设置的对象当垃圾收集器启动时,会回收如下设置的对象u 当内存不够用的时候,当内存不够用的时候,GCGC会回
9、收会回收SoftReferenceSoftReference所引用的对象所引用的对象, ,将某些大型数将某些大型数据设置成据设置成SoftReferenceSoftReferenceu 需要获取对象时,可以调用需要获取对象时,可以调用get()get()方法方法Object obj=new Object();obj=null;SoftReference sr=new SoftReference(new Object();Object obj=sr.get();实现异步加载图片使用使用SoftReferenceSoftReference装载图片对象装载图片对象u 为了加快速度,在内存中开启缓存为
10、了加快速度,在内存中开启缓存u 建立建立MapMap集合集合其键为加载图片对象URL其值为SoftReference对象public class AsyncImageLoader public MapString, SoftReference imageCache = new HashMapString, SoftReference(); 实现异步加载图片如果缓存中有数据,就直接从缓冲中取数据如果缓存中有数据,就直接从缓冲中取数据public class AsyncImageLoader public Drawable loadDrawable(final String imageUrl, f
11、inal ImageCallback callback) /如果缓存过就从缓存中取出数据如果缓存过就从缓存中取出数据 if (imageCache.containsKey(imageUrl) SoftReference softReference = imageCache.get(imageUrl); if (softReference.get() != null) return softReference.get(); 实现异步加载图片缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中public class Asyn
12、cImageLoader private ExecutorService executorService =Executors.newFixedThreadPool(5); public Drawable loadDrawable(final String imageUrl, final ImageCallback callback) executorService.submit(new Runnable()Overridepublic void run() /调用网络下载图片的方法调用网络下载图片的方法final Drawable drawable = loadImageFromUrl(im
13、ageUrl); imageCache.put(imageUrl, new SoftReference(drawable);handler.post(new Runnable() public void run() callback.imageLoaded(drawable,);); ); return null; 实现异步加载图片对外开放回调接口对外开放回调接口public class AsyncImageLoader public interface ImageCallback /注意注意 此方法是用来设置目标对象的图像资源此方法是用来设置目标对象的图像资源 public void ima
14、geLoaded(Drawable imageDrawable); 实现异步加载图片在主线程调用代码在主线程调用代码private AsyncImageLoader asyncImageLoader = new AsyncImageLoader();String path= :8080/prj_book/image/+book.getBimagepath();Drawable cacheImage =imageLoader.loadDrawable(path,new ImageCallback()Overridepublic void imageLoaded(Dra
15、wable imageDrawable, String url) if(imageDrawable!=null)imageIcon.setImageDrawable(imageDrawable););Universal_Image_Loader开源组件实现图片异步加载这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影可是有的人并不知道如何去使用这库如何进行配置,网上查到的信息对于刚接触可
16、是有的人并不知道如何去使用这库如何进行配置,网上查到的信息对于刚接触的人来说可能太少了,下面就把在使用过程中所知道的写了下来,希望可以帮助的人来说可能太少了,下面就把在使用过程中所知道的写了下来,希望可以帮助自己和别人更深入了解这个库的使用和配置自己和别人更深入了解这个库的使用和配置GITHUBGITHUB上的下载路径为:上的下载路径为:https:/ ,下载最新的库文件,并且导入到项目的,下载最新的库文件,并且导入到项目的LIBLIB下便可以使用。下便可以使用。介绍介绍u Android-Universal-Image-LoaderAndroid-Universal-Image-Loader
17、是一个开源的是一个开源的UIUI组件程序,该项目的目的是组件程序,该项目的目的是提供一个可重复使用的仪器为异步图像加载,缓存和显示。所以,如果你的程提供一个可重复使用的仪器为异步图像加载,缓存和显示。所以,如果你的程序里需要这个功能的话,那么不妨试试它。因为已经封装好了一些类和方法。序里需要这个功能的话,那么不妨试试它。因为已经封装好了一些类和方法。我们我们 可以直接拿来用了。而不用重复去写了。其实,写一个这方面的程序还可以直接拿来用了。而不用重复去写了。其实,写一个这方面的程序还是比较麻烦的,要考虑多线程,缓存,内存溢出等很多方面。但是,你也可以是比较麻烦的,要考虑多线程,缓存,内存溢出等很
18、多方面。但是,你也可以参考这个例子来自己写出更好的程序。在此为大家介绍一下:参考这个例子来自己写出更好的程序。在此为大家介绍一下:Universal_Image_Loader开源组件特点特点多线程的图像加载的可能性的宽调谐对多线程的图像加载的可能性的宽调谐对ImageLoader的配置(线程池的大小,的配置(线程池的大小,HTTP选项,内存和光盘高速缓存,显示图像,以及其他的图像的可能性中的缓存选项,内存和光盘高速缓存,显示图像,以及其他的图像的可能性中的缓存在在存储器或设备的文件器系统(或存储器或设备的文件器系统(或SD卡)卡)可以可以“监听监听”加载过程中加载过程中可自定义每个显示的图像调
19、用分隔的选项可自定义每个显示的图像调用分隔的选项Widget支持支持Android 1.5以上支持以上支持简单描述一下这个项目的结构:每一个图片的加载和显示任务都运行在独立的线简单描述一下这个项目的结构:每一个图片的加载和显示任务都运行在独立的线程中,除非这个图片缓存在内存中,这种情况下图片会立即显示。如果需要的图程中,除非这个图片缓存在内存中,这种情况下图片会立即显示。如果需要的图片缓存在本地,他们会开启一个独立的线程队列。如果在缓存中没有正确的图片片缓存在本地,他们会开启一个独立的线程队列。如果在缓存中没有正确的图片,任务线程会从线程池中获取,因此,快速显示缓存图片时不会有明显的障碍。,任
20、务线程会从线程池中获取,因此,快速显示缓存图片时不会有明显的障碍。(别人那边借鉴的这段)(别人那边借鉴的这段)Universal_Image_Loader使用方法使用方法这是这是 一个开源的一个开源的Android关于下载显示图片的工具类,在这个下载包里面关于下载显示图片的工具类,在这个下载包里面jar文件文件,用于我们导入项目使用,具体使用方法在包里面也含有。下面是一个例子:,用于我们导入项目使用,具体使用方法在包里面也含有。下面是一个例子:由于是使用过程中会图片获取要通过网络,并且有缓存设置,所以这由于是使用过程中会图片获取要通过网络,并且有缓存设置,所以这2个权限个权限必须要有。必须要有
21、。很多人想知道如何设置缓存的目录,可以通过以下方法很多人想知道如何设置缓存的目录,可以通过以下方法:File cacheDir = StorageUtils.getOwnCacheDirectory(getApplicationContext(), imageloader/Cache); /这个是你希望的缓存文件的目录:这个是你希望的缓存文件的目录:imageloader/Cache之后在之后在ImageLoaderConfiguration的配置文件中通过设置的配置文件中通过设置.discCache(new UnlimitedDiscCache(cacheDir)/自定义缓存自定义缓存路径方
22、法来设置路径方法来设置该应用的图片缓存路径。该应用的图片缓存路径。如何使用这个图片异步加载库如何使用这个图片异步加载库先要配置先要配置ImageLoaderConfiguration这个类实现全局这个类实现全局ImageLoader的实现情况。的实现情况。可以选择在可以选择在Application中初始化设置该类。中初始化设置该类。ImageLoaderConfiguration config = new ImageLoaderConfiguration ImageLoaderConfiguration config = new ImageLoaderConfiguration .Builde
23、r(context) .Builder(context) .memoryCacheExtraOptions(480, 800) / max width, max height .memoryCacheExtraOptions(480, 800) / max width, max height,即保存的每个缓存文件的最大长宽,即保存的每个缓存文件的最大长宽 .discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null) / Can slow ImageLoader, .discCacheExtraOptions(480, 800,
24、CompressFormat.JPEG, 75, null) / Can slow ImageLoader, use it carefully (Better dont use it)/use it carefully (Better dont use it)/设置缓存的详细信息,最好不要设置这个设置缓存的详细信息,最好不要设置这个 .threadPoolSize(3)/.threadPoolSize(3)/线程池内加载的数量线程池内加载的数量 .threadPriority(Thread.NORM_PRIORITY - 2) .threadPriority(Thread.NORM_PRIOR
25、ITY - 2) .denyCacheImageMultipleSizesInMemory() .denyCacheImageMultipleSizesInMemory() .memoryCache(new UsingFreqLimitedMemoryCache(2 .memoryCache(new UsingFreqLimitedMemoryCache(2 * * 1024 1024 * * 1024) / You can pass your own 1024) / You can pass your own memory cache implementation/memory cache
26、implementation/你可以通过自己的内存缓存实现你可以通过自己的内存缓存实现 .memoryCacheSize(2 .memoryCacheSize(2 * * 1024 1024 * * 1024) 1024) .discCacheSize(50 .discCacheSize(50 * * 1024 1024 * * 1024) 1024) .discCacheFileNameGenerator(new Md5FileNameGenerator()/ .discCacheFileNameGenerator(new Md5FileNameGenerator()/将保存的时候的将保存的
27、时候的URIURI名称用名称用MD5 MD5 加密加密 .tasksProcessingOrder(QueueProcessingType.LIFO) .tasksProcessingOrder(QueueProcessingType.LIFO) .discCacheFileCount(100) / .discCacheFileCount(100) /缓存的文件数量缓存的文件数量 .discCache(new UnlimitedDiscCache(cacheDir)/.discCache(new UnlimitedDiscCache(cacheDir)/自定义缓存路径自定义缓存路径 .defa
28、ultDisplayImageOptions(DisplayImageOptions.createSimple() .defaultDisplayImageOptions(DisplayImageOptions.createSimple() .imageDownloader(new BaseImageDownloader(context, 5 .imageDownloader(new BaseImageDownloader(context, 5 * * 1000, 30 1000, 30 * * 1000) / connectTimeout 1000) / connectTimeout (5
29、s), readTimeout (30 s)(5 s), readTimeout (30 s)超时时间超时时间 .writeDebugLogs() / Remove for release app .writeDebugLogs() / Remove for release app .build();/ .build();/开始构建开始构建 / Initialize ImageLoader with configuration./ Initialize ImageLoader with configuration.以上的配置看个人需求进行选择,不是所有都要进行配置。以上的配置看个人需求进行选择
30、,不是所有都要进行配置。如何使用这个图片异步加载库如何使用这个图片异步加载库配置好配置好ImageLoaderConfiguration后,调用以下方法来实现初始化:后,调用以下方法来实现初始化:ImageLoader.getInstance().init(config);/全局初始化此配置全局初始化此配置 注:注:ImageLoaderConfiguration 配置中的配置中的.discCacheFileNameGenerator()方法是方法是将缓存下来的文件以什么方式命名将缓存下来的文件以什么方式命名里面可以调用的方法有里面可以调用的方法有 1.new Md5FileNameGener
31、ator() /使用使用MD5对对UIL进行加密进行加密命名命名2.new HashCodeFileNameGenerator()/使用使用HASHCODE对对UIL进行加密进行加密命名命名使用使用ImageLoader进行图片加载的时候,先要实例化进行图片加载的时候,先要实例化ImageLoader,调用以下方法进,调用以下方法进行实例化,在每个布局里面都要实例化后再使用行实例化,在每个布局里面都要实例化后再使用。protected ImageLoader imageLoader = ImageLoader.getInstance(); 如何使用这个图片异步加载库如何使用这个图片异步加载库之
32、后进行显示的图片的各种格式之后进行显示的图片的各种格式DisplayImageOptions 的设置的设置:DisplayImageOptions options; DisplayImageOptions options; options = new DisplayImageOptions.Builder() options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_launcher) / .showImageOnLoading(R.drawable.ic_launcher) /设置图片在下载期间
33、显示的图片设置图片在下载期间显示的图片 .showImageForEmptyUri(R.drawable.ic_launcher)/.showImageForEmptyUri(R.drawable.ic_launcher)/设置图片设置图片UriUri为空或是错误的时候显示的图片为空或是错误的时候显示的图片 .showImageOnFail(R.drawable.ic_launcher) /.showImageOnFail(R.drawable.ic_launcher) /设置图片加载设置图片加载/ /解码过程中错误时候显示的图片解码过程中错误时候显示的图片.cacheInMemory(tru
34、e)/.cacheInMemory(true)/设置下载的图片是否缓存在内存中设置下载的图片是否缓存在内存中 .cacheOnDisc(true)/.cacheOnDisc(true)/设置下载的图片是否缓存在设置下载的图片是否缓存在SDSD卡中卡中 .considerExifParams(true) /.considerExifParams(true) /是否考虑是否考虑JPEGJPEG图像图像EXIFEXIF参数(旋转,翻转)参数(旋转,翻转).imageScaleType(ImageScaleType.EXACTLY_STRETCHED)/.imageScaleType(ImageSca
35、leType.EXACTLY_STRETCHED)/设置图片以如何的编码方式显示设置图片以如何的编码方式显示 .bitmapConfig(Bitmap.Config.RGB_565)/.bitmapConfig(Bitmap.Config.RGB_565)/设置图片的解码类型设置图片的解码类型/ / .decodingOptions(android.graphics.BitmapFactory.Options decodingOptions)/.decodingOptions(android.graphics.BitmapFactory.Options decodingOptions)/设置图
36、片的解码配置设置图片的解码配置 /.delayBeforeLoading(int delayInMillis)/int delayInMillis/.delayBeforeLoading(int delayInMillis)/int delayInMillis为你设置的下载前的延迟时间为你设置的下载前的延迟时间/设置图片加入缓存前,对设置图片加入缓存前,对bitmapbitmap进行设置进行设置 /.preProcessor(BitmapProcessor preProcessor) /.preProcessor(BitmapProcessor preProcessor) .resetView
37、BeforeLoading(true)/.resetViewBeforeLoading(true)/设置图片在下载前是否重置,复位设置图片在下载前是否重置,复位 .displayer(new RoundedBitmapDisplayer(20)/.displayer(new RoundedBitmapDisplayer(20)/是否设置为圆角,弧度为多少是否设置为圆角,弧度为多少 .displayer(new FadeInBitmapDisplayer(100)/.displayer(new FadeInBitmapDisplayer(100)/是否图片加载好后渐入的动画时间是否图片加载好后渐
38、入的动画时间 .build();/.build();/构建完成构建完成 按照你所需要的配置去设置,如果不需要的就可以不做配置。按照你所需要的配置去设置,如果不需要的就可以不做配置。如何使用这个图片异步加载库如何使用这个图片异步加载库注注:以上配置中的以上配置中的:1).imageScaleType(ImageScaleType imageScaleType) 是设置是设置 图片的缩放方式图片的缩放方式缩放类型缩放类型mageScaleType:EXACTLY :图像将完全按比例缩小的目标图像将完全按比例缩小的目标大小大小EXACTLY_STRETCHED:图片会缩放到目标大小图片会缩放到目标大
39、小完全完全IN_SAMPLE_INT:图像将被二次采样的整数图像将被二次采样的整数倍倍IN_SAMPLE_POWER_OF_2:图片将降低图片将降低2倍,直到下一减少步骤,使图像更小的目倍,直到下一减少步骤,使图像更小的目标标大小大小NONE:图片不会调整图片不会调整2).displayer(BitmapDisplayer displayer) 是设置是设置 图片的图片的显示方式显示方式显示方式显示方式displayer:RoundedBitmapDisplayer(int roundPixels)设置圆角设置圆角图片图片FakeBitmapDisplayer()()这个类什么都没这个类什么都
40、没做做FadeInBitmapDisplayer(int durationMillis)设置图片渐显的设置图片渐显的时间时间 SimpleBitmapDisplayer()正常显示一张正常显示一张图片之后图片之后按照需求调用按照需求调用如何使用这个图片异步加载库如何使用这个图片异步加载库1.纯粹纯粹为了加载默认配置的一个图片的为了加载默认配置的一个图片的方法方法:public void displayImage(String uri, ImageView imageView) 具体实现具体实现:ImageLoader.getInstance().displayImage(imageUrl, i
41、mageView); / imageUrl代代表图片的表图片的URL地址,地址,imageView代表承载图片的代表承载图片的IMAGEVIEW控件控件2.加载自定义配置的一个图片加载自定义配置的一个图片的的方法方法:public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) 具体实现具体实现:ImageLoader.getInstance().displayImage(imageUrl, imageView,options); / imageUrl代表图片的代表图片的URL地址,
42、地址,imageView代表承载图片的代表承载图片的IMAGEVIEW控件控件 , options代表代表DisplayImageOptions配置文件配置文件 如何使用这个图片异步加载库如何使用这个图片异步加载库3.图片加载时候带加载情况的图片加载时候带加载情况的监听监听方法方法:public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,ImageLoadingListener listener) ImageLoadingListener 用于监听图片的下载情况。用于监听图片
43、的下载情况。具体实现:具体实现:imageLoader.displayImage(imageUrl, imageView, options, new ImageLoadingListener() imageLoader.displayImage(imageUrl, imageView, options, new ImageLoadingListener() Override Override public void onLoadingStarted() public void onLoadingStarted() / /开始加载的时候执行开始加载的时候执行 Override Override
44、public void onLoadingFailed(FailReason failReason) public void onLoadingFailed(FailReason failReason) / /加载失败的时候执行加载失败的时候执行 Override Override public void onLoadingComplete(Bitmap loadedImage) public void onLoadingComplete(Bitmap loadedImage) / /加载成功的时候执行加载成功的时候执行 Override Override public void onLoad
45、ingCancelled() public void onLoadingCancelled() / /加载取消的时候执行加载取消的时候执行 ); ); 如何使用这个图片异步加载库如何使用这个图片异步加载库4.图片加载时候,带监听又带加载进度条的情况图片加载时候,带监听又带加载进度条的情况调用:调用:public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,ImageLoadingListener listener, ImageLoadingProgressListener p
46、rogressListener) 具体实现:具体实现: imageLoader.displayImage(imageUrl, imageView, options, new imageLoader.displayImage(imageUrl, imageView, options, new ImageLoadingListener() ImageLoadingListener() Override Override public void onLoadingStarted() public void onLoadingStarted() / /开始加载的时候执行开始加载的时候执行 Override Override public void onLoadingFailed(FailReason failReason) public void onLoadingFailed(FailReason failReason) / /加载失败的时候执行加载
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026汽车智能驾驶系统市场发展分析及未来趋势与竞争策略研究报告
- (2026年)安全生产教育培训试题及答案
- 2026年大连汽车职业技术学院单招综合素质考试题库含答案详解(基础题)
- 2026年大连枫叶职业技术学院单招职业倾向性测试题库带答案详解(黄金题型)
- 2026年安徽扬子职业技术学院单招综合素质考试题库附参考答案详解(典型题)
- 2026年安庆师范大学单招综合素质考试题库及参考答案详解(新)
- 2026年宁德职业技术学院单招职业技能测试题库含答案详解ab卷
- 2026年天津理工大学中环信息学院单招职业技能测试题库含答案详解(b卷)
- 2026年大同煤炭职业技术学院单招综合素质考试题库及1套完整答案详解
- 2026年大连枫叶职业技术学院单招职业倾向性测试题库附参考答案详解(完整版)
- 2026春桂美版2024小学美术二年级下册每课教案(附目录)
- 2026四川成都市西南民族大学招聘应届博士辅导员4人笔试备考试题及答案解析
- 2025-2030中国天然气发电行业发展状况与投资建议分析研究报告
- 2026年临沂职业学院单招综合素质考试题库及答案详解(基础+提升)
- 中职课件:职业道德与法治全册教案
- 广联达软件学习报告
- 任务3.3 空间数据误差校正
- GB/T 6533-2012原油中水和沉淀物的测定离心法
- GB/T 37612-2019耐蚀合金焊丝
- 自我认知与职业生涯规划课件
- 中山市二次供水工程技术规程
评论
0/150
提交评论