مقدمه
در ادامه مباحث مرتبط با اتصال برنامههای اندروید به اینترنت و وب، این بار به سراغ عکسها و تصاویر میرویم.
دانلود کردن عکسها از اینترنت و نمایش آنها در برنامه یکی از کارهای بسیار رایج در برنامههای اندروید است. این کار همانقدر که رایج و فراگیر است، سخت و پیچیده هم هست! برقراری اتصال اینترنتی، دانلود عکس، قرار دادن آن در ImageView، کش کردن دادههای عکس برای صرفهجویی در مصرف منابع، مدیریت خطاهای احتمالی و …
همانطور که میدانید نظر من و بسیاری از برنامهنویسان اندروید این است که به جای درگیر شدن در پیادهسازی جزئیات این فرایندهای پیچیده، بهتر است از کتابخانههای موجود استفاده کنیم. برای دانلود و دستکاری عکسها در اندروید کتابخانههای زیادی وجود دارد که معروفترین آنها Picasso و Glide هستند. در این مطلب نحوه کار کردن با Glide را به شما آموزش خواهم داد.
نصب Glide
برای نصب Glide کافی است خط زیر را به build.gradle برنامه خود اضافه کنید:
compile 'com.github.bumptech.glide:glide:3.7.0'
دانلود عکس از اینترنت
برای کار کردن با Glide در سادهترین شکل ممکن، باید سه پارامتر را به Glide بدهیم:
۱- Context: کتابخانه Glide از این نمونه Context برای کارهای داخلی خود استفاده میکند. اگر در یک Activity هستید، میتوانید خود Activity را با استفاده از عملگر this به Glide بدهید. اگر در fragment هستید، متد getActivity همین کار را برای شما میکند. اگر در یک View از Glide استفاده میکنید، میتوانید از متد getContext برای این منظور استفاده کنید؛
۲- Url عکسی که میخواهیم دانلود کنیم؛
۳- نمونه ImageView ای که میخواهیم عکس را در آن نمایش دهیم.
فرض کنید میخواهیم لوگوی اسمارتلب را در برنامه نمایش دهیم. نشانی این عکس این است:
https://smartlab.ir/wp-content/uploads/2016/08/smartlab-header2.png
اگر یک ImageView با شناسه smartlabLogoImageView در فایل Layout برنامه تعریف کرده باشید، برای دانلود لوگوی اسمارتلب و نمایش آن در این ImageView به شکل زیر عمل میکنیم:
ImageView smartlabLogoImageView = (ImageView) findViewById(R.id.smartlabLogoImageView ); String imageUrl = "https://smartlab.ir/wp-content/uploads/2016/08/smartlab-header2.png"; Glide .with(context) .load(imageUrl) .into(smartlabLogoImageView);
بسیار ساده است!
اگر فقط بخواهید یک عکس را دانلود و آن را در یک ImageView نمایش بدهید، همین کار کافی است!
لود کردن از Resource ها
برای لود کردن عکسی که جزو منابع (Resource) های برنامه است، به جای دادن URL به Glide کافی است شناسه عکس را به Glide بدهیم:
int resourceId = R.mipmap.ic_launcher; Glide .with(context) .load(resourceId) .into(imageView);
ممکن است بگویید که این کار با ImageView هم امکانپذیر است. فایده انجام این کار با Glide در امکانات پیشرفته Glide است که در ادامه مطلب با آنها آشنا خواهید شد.
لود کردن از فایل
در Glide میتوان عکس را از فایل لود کرد. فایده این کار برای وقتی است که کاربر مثلاً عکس را از فایل اکسپلورر یا گالری لود میکند. برای این کار باید یک شیء File به Glide بدهیم:
File file = new File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Running.jpg"); Glide .with(context) .load(file) .into(imageView);
لود کردن از Uri
اگر از هر طریقی در اندروید به Uri یک عکس دسترسی دارید، میتوانید آن عکس را با استفاده از Glide نمایش دهید:
Uri uri = resourceIdToUri(context, R.mipmap.ic_launcher); Glide .with(context) .load(uri) .into(imageView);
استفاده از جانگهدار یا placeholder
قبل از این که عکس لود شود ImageView ای که قرار است عکس در آن نمایش داده شود، خالی است. این موضوع از نظر طراحی اصلاً مطلوب نیست. بهتر است ابتدا یک عکس در آن نمایش داده شود تا کاربر بداند که در این محل تصویری قرار است نمایش داده شود. برای این کار در Glide از متد placeholder استفاده میشود. کافی است شناسه تصویر موقت یا یک شیء drawable را به این متد بدهید:
Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .placeholder(R.mipmap.ic_launcher) // can also be a drawable .into(imageView);
اعلام خطا
در مواقعی ممکن است Glide به هر دلیلی (مثل قطعی ارتباط اینترنتی، پاسخگو نبودن سرور و …) نتواند عکس را دانلود کند. در چنین مواقعی بهتر است به کاربر اعلام کنیم که دانلود کردن عکس یا تصویر ناموفق بوده است. برای این کار میتوان از تصویری استفاده کرد که نشاندهنده بروز خطا باشد. مثل استفاده از جانگهدار، میتوان با استفاده از متد error و ارسال یک شناسه یا یک شیء Drawable به این متد، به Glide اعلام کرد که در صورت بروز خطا در لود کردن عکس، از این تصویر استفاده کند:
Glide .with(context) .load("http://futurestud.io/non_existing_image.png") .placeholder(R.mipmap.ic_launcher) .error(R.mipmap.future_studio_launcher) .into(imageView);
تغییر دادن آرام عکسها
اگر از یک placeholder یا جانگهدار استفاده میکنید، بعد از لود شدن تصویر، این جانگهدار باید جای خود را به تصویر اصلی بدهد. اگر این فرایند ناگهانی باشد، ممکن است باعث چشمآزار شدن برنامه بشود. اگر بخواهیم این فرایند جایگزینی تصویر به جای جانگهدار آرام و به صورت محو باشد، از متد crossfade (به معنی محو شدگی) استفاده میکنیم:
Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .placeholder(R.mipmap.ic_launcher) .error(R.mipmap.future_studio_launcher) .crossFade() .into(imageView);
اگر بخواهیم مدت زمان این محو شدگی و انتقال را (که به صورت پیشفرض ۳۰۰ هزارم ثانیه است) تغییر دهیم، باید زمان مطلوب را به هزارم ثانیه به این متد بدهیم. در نسخههای آخر Glide این محوشدگی به صورت پیشفرض فعال است.
تغییر دادن بلادرنگ تصویر
اگر بخواهیم تصویر لود شده بدون محوشدگی سریعاً و بلادرنگ جایگزین جانگهدار (placeholder) بشود (بر عکس حالت قبل) کافی است متد dontAnimate را فراخوانی کنیم:
Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .placeholder(R.mipmap.ic_launcher) .error(R.mipmap.future_studio_launcher) .dontAnimate() .into(imageView);
برای این کار حتماً یک دلیل معقول و منطقی داشته باشید!
تغییر اندازه عکس
برای صرفهجویی در منابع اینترنتی و نیز منابع سختافزاری، بهترین کار این است که سرور عکس را در اندازه مورد نیاز برنامه ارسال کند. اگر به هر دلیلی خواستید اندازه عکس را قبل از نمایش تغییر بدهید، کافی است از متد override استفاده کنید و اندازه افقی و عمودی عکس را به پیکسل به این متد بدهید:
Glide .with(context) .load(UsageExampleListViewAdapter.eatFoodyImages[0]) .override(600, 200) .into(imageView);
توجه کنید که اندازه به پیکسل هستند و نه dip یا dp.
نکته مهم این است که این متد نسبت بین طول و ارتفاع عکس را نگه نمیدارد و اگر میخواهید نسبتهای بین طول و ارتفاع عکس حفظ شود باید از متدهای دیگری استفاده کنید.
تغییر اندازه عکس با حفظ نسبتهای طول و عرض
همانطور که دیدید متد override نسبتهای عکس را حفظ نمیکند و این موضوع ممکن است باعث زشت شدن عکس شود. اگر بخواهید اندازه عکس را بدون بر هم خوردن نسبتهای آن تغییر دهید، Glide دو متد دیگر در اختیار شما میگذارد:
CenterCrop
یک تکنیک تغییر اندازه عکس است. در این روش با استفاده از یک الگوریتم ساده اندازه عکس به گونهای تغییر میکند که عکس تمام فضایی که برایش در نظر گرفته شده است را پر میکند ولی نسبت طول و ارتفاع آن تغییر نمیکند. در این روش ممکن است بخشهایی از عکس بریده شده و نمایش داده نشود.
علت اصلی این است که نسبتهای عکس با نسبتهای ImageView ای که قرار است آن را نمایش دهد یکی نیست. اگر این نسبتها برابر باشد (مثلاً نسبت ۱:۱) همه تصویر در ImageView دیده خواهد شد و چیزی از تصویر بریده نمیشود.
تصویر زیر اتفاقی که در centerCrop رخ میدهد را نشان میدهد:
برای استفاده از این تکنیک کافی است متد centerCrop را فراخوانی کنیم:
Glide .with(context) .load(imageUrl) .centerCrop() .into(imageView);
FitCenter
در این روش تغییر اندازه تصویر، نسبت تصویر بدون تغییر میماند ولی به اندازهای عکس کوچک میشود که کل عکس در ImageView نمایش داده شود. در این حالت ممکن است عکس کل فضای ImageView را پر نکند.
تصویر زیر اتفاقی که در fitCenter رخ میدهد را نشان میدهد:
برای استفاده از روش کافی است متد fitCenter را فراخوانی کنید:
Glide .with(context) .load(imageUrl) .fitCenter() .into(imageView);
نمایش تصویر Gif
اندروید به صورت پیشفرض از تصاویر گیف یا Gif پشتیبانی نمیکند. اما همانطور که این روزها در پیامرسانها میبینید، گیف به یکی از کاربردیترین فرمتها تبدیل شده است و همه این برنامهها میتوانند Gif را پشتیبانی کنند. یکی از امکانات فوقالعاده کتابخانه Glide توانایی نمایش دادن عکسهای به فرمت گیف است.
اگر مطمئن هستید که عکسی که میخواهید نمایش دهید یک فایل گیف است، برای دانلود و نمایش آن نیاز به کار اضافهای ندارید و به صورت عادی از Glide استفاده میکنید:
String gifUrl = "http://someurl/some.gif"; Glide .with(context) .load(gifUrl) .into(imageView);
به همین سادگی! Glide تصویر گیف را دانلود کرده و پس از اتمام دانلود شروع میکند به پخش کردن. یکی از خوبیهای Glide این است که هیچ تمایزی بین عکسهای عادی و گیف نمیگذارد و همه متدهای آن برای این فرمت هم کار میکند:
Glide .with(context) .load(gifUrl) .placeholder(R.drawable.cupcake) .error(R.drawable.full_cake) .into(imageViewGif);
یکی از مسائلی که در این حالت پیش میآید این است که شما انتظار دارید تصویر حتماً گیف باشد. ولی همان طور که میبینید Glide هیچ تفاوتی بین گیف و عکس عادی نمیگذارد. برای مجبور کردن Glide به این که فایل دریافتی را حتماً چک کند تا از گیف بودن آن مطمئن شود، متد asGif است. اگر عکس دریافتی گیف نباشد این تابع خطا میدهد:
Glide .with(context) .load(gifUrl) .asGif() .error(R.drawable.full_cake) .into(imageViewGif);
مواقع بسیاری هم هست که شما لیستی از تصاویر دارید که در میان آنها هم تصاویر عادی هست و هم تصاویر گیف و شما نمیخواهید تصاویر گیف به صورت خودکار شروع کنند به پخش شدن. در عوض میخواهید یک فریم عادی از تصویر را نشان دهید. در این حالت از متد asBitmap استفاده میکنید. این متد باعث میشود که اگر تصویر دریافتی گیف باشد Glide فریم اول آن را نشان دهد و تصویر به صورت فیلم پخش نمیشود. استفاده از این تابع هم بسیار ساده است:
Glide .with(context) .load(gifUrl) .asBitmap() .error(R.drawable.full_cake) .into(imageViewGif);
تصویر انگشتی
تصویر انگشتی یا thumbnail نسخه کوچکتر تصویر اصلی است. فرض کنید در یک لیست عکسها را در اندازه ۲۵۰ * ۲۵۰ نمایش میدهید ولی در صفحه نمایش جزئیات عکس را در ابعاد بزرگتر مثلاً ۱۰۰۰ * ۱۰۰۰ نمایش دهید. کاربر انتظار دارد به محض این که روی آیتم لیست کلیک میکند عکس را ببیند. Glide این امکان را فراهم میکند تا بتوان یک نسخه انگشتی از عکس را سریعاً به کاربر نشان داد. میتوانید نسبت عکس انگشتی به عکس اصلی را به Glide بدهید و بعد همه کارها را Glide برای شما انجام میدهد:
Glide .with(context) .load(imageUrl) .thumbnail(0.25f) .into(imageView);
مقداری که به متد thumbnail میدهیم نسبت عکس انگشتی به عکس اصلی است.
اولویت دادن به درخواستها
یکی از امکانات بسیار ارزشمند Glide این است که میتوانید برای دانلود عکسها اولویت تعیین کنید. مثلاً فرض کنید میخواهید مشخصات یک کالا و نظر کاربران درباره آن را در برنامه نشان بدهید. تبعاً دانلود عکس پروفایل کاربران اولویت پایینتری از عکس خود محصول دارد. قطعاً میخواهیم عکس محصول بلافاصله دانلود شده و نمایش داده شود و پس از آن به سراغ عکس پروفایل کاربران برویم. Glide چهار اولویت تعریف کرده است:
- Prority.IMMEDIATE
- Priority.HIGH
- Priority.NORMAL
- Priority.LOW
البته توچه داشته باشید که این اولویتها فقط یک راهنما برای Glide هستند و Glide سعی میکند با در نظر گرفتن آنها بهترین سرعت ممکن در لود کردن تصاویر را داشته باشد اما این اولویتها به هیچ وجه تضمین نمیکند که عکسها طبق ترتیبی که اولویت مشخص کرده است لود شوند.
Glide .with(context) .load(imageUrl) .priority(Priority.HIGH) .into(imageView);
خب مقدمات کار با Glide را فرا گرفتید و با همین اطلاعات درباره Glide میتوانید به راحتی همه کار با عکس و تصویر دریافتی از اینترنت بکنید. هنوز کار ما با اینترنت تمام نشده است!
شما عالی هستید!
عالی بود سپاس
خیلی عالی و مفید بود…
تشکر
سلام .خسته نباشید .مطالبتون عالی و روان و قابل فهمه .مچکرم .
برای ارسال و دریافت ویدیو کتابخانه وجود داره ؟؟؟ یا همین کتابخانه های معرفی شده مثل okHTTP میشه اینکارو انجام داد؟؟؟؟
برای استریم ویدئو میتونید از خود VideoView که از ویوهای اندروید هست استفاده کنید.
با سلام
فرق این کتابخونه با okhttp چیه؟
و اینکه کدومشون بهترن؟
با تشکر
این دو تا هیچ شباهتی به هم ندارن!
شرکت اسکوئر که OkHttp و Retrofit رو منتشر کرده یک کتابخانه هم داره به نام Picasso که امکاناتش تقریباً با Glide یکی است.
پیکاسو بهتر از گلاید نیست؟؟
تفاوتهایی دارند ولی آن قدری نیست که باعث برتری یکی به دیگری بشه. بیشتر سلیقهای است.
یک فرق بزرگ وجود دارد که به نظرم مزیت بزرگی برای Glide است ، آنهم اینکه فایلهای gif فقط در Glide پشتیبانی میشود.
با سلام و تشکر فراوان بابت مطالب خوبتون
یه سوال داشتم. آیا توی اندروید هم میشه نقشه تصویری ایجاد کرد. (مثل map در html) یعنی با استفاده از ویژگی های coords و shape بتونیم مثل html در اندروید هم نقشه تصویری ایجاد کنیم.
سلام
ممنون از اطلاعات عالی شما
من کد واسه دانلود عکس رو میزنم بدون اینکه خطا بده یا هیچ پیامی، imageview رو خالی نشون میده
کسی میدونه چرا؟؟؟
آدرس عکس رو در مرورگر بزنید لود میشه؟
سلام
اگه بخوایم چند تا عکس رو نمایش بدیم باید چکار کنیم ؟
من ی آرایه از نوع Integer تعریف کردم و همه عکسام رو ریختم داخلش .
اما وقتی glide رو لود میکنم که از این آرایه بخونه کرش میکنه و این ارور رو میده
Unknown type class [Ljava.lang.Integer;. You must provide a Model of a type for which there is a registered ModelLoader, if you are using a custom model, you must first call Glide#register with a ModelLoaderFactory for your custom model class
راهنماییم کنین تازه کارم 🙂
قسمتهای اصلی کدتون رو بنویسید تا بشه خطای اصلی رو پیدا کرد.
سلام
implementation ‘com.github.bumptech.glide:glide:4.7.1’
annotationProcessor ‘com.github.bumptech.glide:compiler:4.7.1’
من کتابخونه glide رو اضافه کردم سینک شد ولی هنگام اجرای برنامه کرش میکنه چرا؟
اینجا رو ببینید.
سلام.من از نسخه ی ۴ Glide استفاده میکنم.
Url ای که استفاده میکنم https هست.متاسفانه عکسی لود نمیشه در حالی که همون Url توی مرور گر باز میشه.
توی نت سرچ کردم و دیدم که Glide برای پشتیبانی از کانفیگ ssl باید شخصی سازی بشه.
https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates
اما متاسفانه این روش هم بدون اینکه خطایی رو نشون بده، عکس رو لود نمیکنه.
از نسخه ی ۳ Glide استفاده کردم و داخل همون url ای که ذکر کردم راه حل متفاوت رو اجرا کردم.اما اینبار خطای
Unable to find GlideModule implementation
رو دریافت میکنم. راه حلی سراغ داردید؟حتی اگه Glide هم این امکان رو نداره هر کتابخونه ی دیگه ای که بشه باهاش عکس هایی با آدرس https رو بشه لود کرد لطفا معرفی کنید.
سلام
بنظر من از کتابخونه picasso استفاده کتابخونه خوبیه ولی فک نکنم کل قابلیت های این glide روداشته باشه اما خوبه من خودم باهاش httpsلود کردم و هیچ مشکلی نداشته
خسته نباشید لطفا راجب کش کردن این کتابخونه مطالبی قرار بدین ممنون میشم