first,sorry for my English.
I'm new in Android,I have a activity to show news,the content is from web server and the content contains images.I used a TextView to display it and I want to the images fit the sreen,so I wrote these codes.
public class CustomImageGetter implements Html.ImageGetter {
private Activity context;
private Picasso picasso;
private TextView textView;
public CustomImageGetter(Activity context, Picasso picasso, TextView textView) {
this.context = context;
this.picasso = picasso;
this.textView = textView;
}
#Override
public Drawable getDrawable(final String source) {
final PlaceHolderBitmapDrawable result = new PlaceHolderBitmapDrawable();
new AsyncTask<Void, Void, Bitmap>() {
#Override
protected Bitmap doInBackground(Void... params) {
try {
return picasso
.load(Config.WEB_SERVER + source.substring(1))
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.get();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
try {
BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap);
DisplayMetrics metrics = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int width = metrics.widthPixels;
int height = width * bitmap.getHeight() / bitmap.getWidth();
drawable.setBounds(0, 0, width, height);
result.setDrawable(drawable);
result.setBounds(0, 0, width, height);
textView.setText(textView.getText());
} catch (Exception e) {
e.printStackTrace();
}
}
}.execute((Void) null);
return result;
}
private static class PlaceHolderBitmapDrawable extends BitmapDrawable {
protected Drawable drawable;
#Override
public void draw(Canvas canvas) {
if (drawable != null)
drawable.draw(canvas);
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
}
}
}
The image's width is correct but the height is too short in android 4.4 but in android L is normal.
The left picture runs in android 4.4 and the right runs in android L.
How can I fix the problem?
Related
According to this question I used a custom ImageGatter class to display images that I'm getting from a server in TextView using Picasso
public class PicassoImageGetter implements Html.ImageGetter {
private TextView textView = null;
Context mContext;
public PicassoImageGetter() {
}
public PicassoImageGetter(TextView target, Context context) {
textView = target;
mContext = context;
}
#Override
public Drawable getDrawable(String source) {
BitmapDrawablePlaceHolder drawable = new BitmapDrawablePlaceHolder();
Picasso.get().load(source).into(drawable);
return drawable;
}
private class BitmapDrawablePlaceHolder extends BitmapDrawable implements com.squareup.picasso.Target {
protected Drawable drawable;
#Override
public void draw(final Canvas canvas) {
if (drawable != null) {
drawable.draw(canvas);
}
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
drawable.setBounds(0, 0, width, height);
setBounds(0, 0, width, height);
if (textView != null) {
textView.setText(textView.getText());
}
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setDrawable(new BitmapDrawable(mContext.getResources(), bitmap));
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
setDrawable(errorDrawable);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
}
}
and used like this
imageGetter = new PicassoImageGetter(contentTextView, this);
Spannable html;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
html = (Spannable) Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY, imageGetter, null);
} else {
html = (Spannable) Html.fromHtml(content, imageGetter, null);
}
contentTextView.setText(html);
I need to catch this images into internal or external storage to display it if there's no connection, but I don't know the path or file name.
You need to implement picasso cache by your own. This should be like this:
public class PicassoCache {
private static Picasso picassoInstance = null;
private PicassoCache(Context context) {
Downloader downloader = new OkHttp3Downloader(context, Integer.MAX_VALUE);
Picasso.Builder builder = new Picasso.Builder(context);
builder.downloader(downloader);
picassoInstance = builder.build();
}
public static Picasso getPicassoInstance(Context context) {
if (picassoInstance == null) {
new PicassoCache(context);
return picassoInstance;
}
return picassoInstance;
}
}
Then in your code:
#Override
public Drawable getDrawable(String source) {
BitmapDrawablePlaceHolder drawable = new BitmapDrawablePlaceHolder();
PicassoCache.getPicassoInstance(getContext()).load(source).into(drawable);
return drawable;
}
OkHttp3Downloader will install an image cache into your application cache directory. You can also use another constructor with your own provided directory public OkHttp3Downloader(final File cacheDir, final long maxSize)
As an alternative, you can use Glide. This is like picasso but also allows you to show gifs with animation and cache strategy IMHO is a bit easier.
Glide.with(context)
.load(source)
.apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL))
.into(drawable)
I use Universal Image Loader to load images in a Jsoup parsed html. The <img> tags doesn't have a static position, they can appear anywhere in the Html element. And since I want them to appear in the positions where the <img> are, I can't give them an image view.
This is the class that I'm using to load the images
public class UILImageGetter implements Html.ImageGetter, View.OnClickListener{
Context c;
TextView conatiner;
UrlImageDownloader urlDrawable;
public UILImageGetter(View textView, Context context) {
this.c = context;
this.conatiner = (TextView) textView;
}
#Override
public Drawable getDrawable(String source) {
urlDrawable = new UrlImageDownloader(c.getResources(), source);
if (Build.VERSION.SDK_INT >= 21) {
urlDrawable.mDrawable = c.getResources().getDrawable(R.drawable.default_thumb,null);
} else {
urlDrawable.mDrawable = c.getResources().getDrawable(R.drawable.default_thumb);
}
ImageLoader.getInstance().loadImage(source, new SimpleListener(urlDrawable));
return urlDrawable;
}
#Override
public void onClick(View v) {
}
private class SimpleListener extends SimpleImageLoadingListener {
UrlImageDownloader mUrlImageDownloader;
public SimpleListener(UrlImageDownloader downloader) {
super();
mUrlImageDownloader= downloader;
}
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
int width = loadedImage.getWidth();
int height = loadedImage.getHeight();
int newWidth = width;
int newHeight = height;
if (width > conatiner.getWidth()) {
newWidth = conatiner.getWidth();
newHeight = (newWidth * height) / width;
}
if (view != null) {
view.getLayoutParams().width = newWidth;
view.getLayoutParams().height = newHeight;
}
Drawable result = new BitmapDrawable(c.getResources(), loadedImage);
result.setBounds(0, 0, newWidth, newHeight);
mUrlImageDownloader.setBounds(0, 0, newWidth, newHeight);
mUrlImageDownloader.mDrawable = result;
conatiner.setHeight((conatiner.getHeight() + result.getIntrinsicHeight()));
//conatiner.invalidate();
}
}
private class UrlImageDownloader extends BitmapDrawable {
public Drawable mDrawable;
public UrlImageDownloader(Resources resources, String filepath) {
super(resources, filepath);
mDrawable = new BitmapDrawable(resources, filepath);
}
#Override
public void draw(Canvas canvas) {
if (mDrawable != null) {
mDrawable.draw(canvas);
}
}
}
}
My problem is how to set OnclickListener on the images and get them to display (in a dialog) the original height and width when clicked.
I am building an Android application, using the Xamarin framework, that displays profile photos for users. The photos may either be drawn within an oval, like you'd see on any social network, or in a rectangle based on what screen the user is on. The images I am drawing are downloaded from a web service and may be any size. Thus I must scale the photos, fix any rotation issues, and then draw them either in a rectangle or an oval.
The entire process I have works fine, but the problem I have is when I load several of the profile views into a grid view, then the performance of the scroll event becomes very choppy and slow. I also get warnings in the console saying that 30 frames have been skipped, and that I may be doing to much on the main thread.
To try and fix the issue I've pushed the loading of the images to a background task, and that is also working great. However, the actual onDraw event is drawn on the main thread as it's drawn by the OS.
I've tracked down the issue to the canvas.drawBitmap() function call. When I comment out this command then the slow scrolling issues goes away, and I no longer get the warnings in the logs. This tells me that my process is efficient enough without the draw command, but when I add the draw command I start to get performance issues.
Does anyone know how I can optimize the drawing process so that I can achieve smooth scrolling? My code is below.
This is my grid view adapter class that loads the profile photo views...
namespace AndroidApp
{
public class GridViewAdapter : ArrayAdapter<UserContact>
{
private Activity ParentActivity;
#region Interface
public interface GridViewAdapterCallback
{
void DidSelectRow(Conversation conversation);
}
#endregion
#region Initialization
private int LayoutResourceId;
private List<UserContact> Data = null;
public GridViewAdapterCallback callback;
public GridViewAdapter (Context context, int layoutResourceId, List<UserContact> data, Activity parentActivity) : base (context, layoutResourceId, data)
{
this.LayoutResourceId = layoutResourceId;
this.Data = data;
this.ParentActivity = parentActivity;
}
public void SetData (List<UserContact> data)
{
this.Data = data;
}
#endregion
#region List Delegates
public override Android.Views.View GetView (int position, Android.Views.View convertView, Android.Views.ViewGroup parent)
{
if (convertView == null)
{
LayoutInflater inflater = LayoutInflater.From (this.Context);
convertView = inflater.Inflate(this.LayoutResourceId, parent, false);
}
UserContact row = this.Data[position];
this.SetContactToGridItem (convertView, row);
return convertView;
}
#endregion
#region Setters
private void SetContactToGridItem (Android.Views.View view, UserContact contact)
{
// get view references
ProfileImageView imageView = view.FindViewById(Resource.Id.profileImageView).JavaCast<ProfileImageView>();
imageView.ProfileImageViewStyle = ProfileImageViewStyle.Square;
imageView.ResetImage ();
imageView.SetContact (contact, this.ParentActivity);
TextView textView = (TextView)view.FindViewById(Resource.Id.textView);
textView.SetText (contact.GetFullName (), TextView.BufferType.Normal);
}
#endregion
}
}
This is my main profile view class...
namespace AndroidApp
{
public enum ProfileImageViewColor
{
Default = 0,
White = 1
};
public enum ProfileImageViewStyle
{
Default = 0,
Square = 1
};
public class ProfileImageView : ImageView
{
public ProfileImageViewColor ProfileImageViewColor { get; set; }
public ProfileImageViewStyle ProfileImageViewStyle { get; set; }
private User User { get; set; }
private UserContact Contact { get; set; }
Bitmap Bitmap;
private Activity Activity { get; set; }
public ProfileImageView (System.IntPtr intPtr, Android.Runtime.JniHandleOwnership owner) : base (intPtr, owner)
{
Initialize ();
}
public ProfileImageView (Context context) : base (context)
{
Initialize ();
}
public ProfileImageView (Context context, IAttributeSet attrs) : base (context, attrs)
{
Initialize ();
}
public ProfileImageView (Context context, IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle)
{
Initialize ();
}
void Initialize ()
{
this.ProfileImageViewColor = ProfileImageViewColor.Default;
this.ProfileImageViewStyle = ProfileImageViewStyle.Default;
this.SetScaleType (ScaleType.FitCenter);
this.CropToPadding = true;
}
#region Setters
public void SetUser (User user, Activity activity)
{
this.User = user;
this.Activity = activity;
if (this.User != null)
{
byte[] imageData = this.User.GetProfilePhoto ();
if (imageData != null)
{
this.SetImageData (this.User.GetProfilePhotoPath (), imageData);
}
else
{
UserBusiness.GetUserPhoto (this.User, (Shared.Error error, User usr) => {
activity.RunOnUiThread (() => {
this.SetImageData (this.User.GetProfilePhotoPath (), this.User.GetProfilePhoto ());
});
});
}
}
}
public void SetContact (UserContact contact, Activity activity)
{
this.Contact = contact;
this.Activity = activity;
if (this.Contact != null)
{
byte[] imageData = this.Contact.GetProfilePhoto();
if (imageData != null)
{
this.SetImageData (this.Contact.GetProfilePhotoPath (), imageData);
}
else
{
UserContactPhotoDownloadManager.CreateManager ().DownloadProfilePhoto (contact, (Shared.Error error, UserContact userContact) => {
activity.RunOnUiThread (() => {
this.SetImageData (this.Contact.GetProfilePhotoPath (), this.Contact.GetProfilePhoto ());
});
});
}
}
}
public override void SetImageBitmap (Bitmap bitmap)
{
if (bitmap == null)
{
this.SetImageResource (this.GetDefaultProfileImageID ());
}
else
{
ProfileImageDrawable drawable = new ProfileImageDrawable (bitmap, this.Width, this.Height, this.ProfileImageViewStyle);
this.SetImageDrawable (drawable);
}
}
public void ResetImage ()
{
if (this.Bitmap != null)
this.Bitmap.Recycle ();
this.SetImageBitmap (null);
}
#endregion
#region Private
private void SetImageData (byte[] imageData)
{
if (this.Bitmap != null)
{
this.Bitmap.Recycle ();
this.Bitmap = null;
}
if (imageData == null)
{
this.SetImageResource (this.GetDefaultProfileImageID ());
}
else
{
this.Bitmap = BitmapFactory.DecodeByteArray (imageData, 0, imageData.Length);
ProfileImageDrawable drawable = new ProfileImageDrawable (this.Bitmap, this.Width, this.Height, this.ProfileImageViewStyle);
this.SetImageDrawable (drawable);
}
}
private void SetImageData (string filePath, byte[] imageData)
{
if (this.Bitmap != null)
{
this.Bitmap.Recycle ();
this.Bitmap = null;
}
if (imageData == null)
{
this.SetImageResource (this.GetDefaultProfileImageID ());
}
else
{
this.SetImageAsync (filePath);
}
}
private void SetImageAsync (string filePath)
{
ImageDrawTask task = new ImageDrawTask (filePath, this, (Drawable drawable) => {
this.Activity.RunOnUiThread (() => {
this.SetImageDrawable (drawable);
});
});
task.Execute ();
}
private int GetDefaultProfileImageID ()
{
if (this.ProfileImageViewColor == ProfileImageViewColor.Default)
return (int)typeof (Resource.Drawable).GetField ("profile_image_placeholder").GetValue (null);
else
return (int)typeof (Resource.Drawable).GetField ("profile_image_placeholder_white").GetValue (null);
}
#endregion
}
public class ImageDrawTask: AsyncTask {
public delegate void ImageDrawTaskCompletion (Drawable drawable);
private string FilePath;
private ProfileImageView ImageView;
private ImageDrawTaskCompletion Completion;
public ImageDrawTask (string filePath, ProfileImageView imageView, ImageDrawTaskCompletion completion)
{
this.FilePath = filePath;
this.ImageView = imageView;
this.Completion = completion;
}
protected override void OnPreExecute()
{
}
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] #params)
{
Bitmap bitmap = ImageUtilities.FixRotation (this.FilePath);
ProfileImageDrawable drawable = new ProfileImageDrawable (bitmap, this.ImageView.Width, this.ImageView.Height, this.ImageView.ProfileImageViewStyle);
Completion (drawable);
return null;
}
protected override void OnPostExecute(Java.Lang.Object result)
{
}
}
}
And finally here is my drawable class...
namespace AndroidApp
{
public class ProfileImageDrawable : Drawable
{
Bitmap Bitmap;
ProfileImageViewStyle Style;
int Width;
int Height;
private RectF DrawFrame;
public ProfileImageDrawable (Bitmap bmp, int width, int height, ProfileImageViewStyle style)
{
this.Bitmap = bmp;
this.Style = style;
this.DrawFrame = new RectF ();
this.Width = width;
this.Height = height;
}
public override void Draw (Canvas canvas)
{
if (this.Style == ProfileImageViewStyle.Square)
{
canvas.DrawBitmap (this.Bitmap, this.GetMatrix (this.Bitmap, this.Width, this.Height), null);
}
else
{
BitmapShader bmpShader = new BitmapShader (this.Bitmap, Shader.TileMode.Clamp, Shader.TileMode.Clamp);
bmpShader.SetLocalMatrix (this.GetMatrix (this.Bitmap, this.Width, this.Height));
Paint paint = new Paint () { AntiAlias = true, Dither = true };
paint.SetShader (bmpShader);
canvas.DrawOval (this.DrawFrame, paint);
}
}
protected override void OnBoundsChange (Rect bounds)
{
base.OnBoundsChange (bounds);
this.DrawFrame.Set (0, 0, bounds.Width (), bounds.Height ());
}
public override int IntrinsicWidth {
get {
return this.Width;
}
}
public override int IntrinsicHeight {
get {
return this.Height;
}
}
public override void SetAlpha (int alpha)
{
}
public override int Opacity {
get {
return (int)Format.Opaque;
}
}
public override void SetColorFilter (ColorFilter cf)
{
}
private Matrix GetMatrix (Bitmap bmp, int width, int height)
{
Matrix mtx = new Matrix ();
float scaleWidth = ((float) width) / bmp.Width;
float scaleHeight = ((float) height) / bmp.Height;
float newWidth = 0;
float newHeight = 0;
if (scaleWidth > scaleHeight)
{
mtx.PostScale (scaleWidth, scaleWidth);
newWidth = scaleWidth * bmp.Width;
newHeight = scaleWidth * bmp.Height;
}
else
{
mtx.PostScale (scaleHeight, scaleHeight);
newWidth = scaleHeight * bmp.Width;
newHeight = scaleHeight * bmp.Height;
}
mtx.PostTranslate ((width - newWidth) / 2, (height - newHeight) / 2);
return mtx;
}
}
}
I ma building a chat application and I am attempting to append an image to an EditText, through use of Picasso to get the image from a URL and the append and ImageGetter to attach the image to the EditText. However, what I have implemented below does not work, as appending messages when using the app displays nothing (but the message does show up in the database).
I have tested without using Picasso, as simply just using the ImageGetter with an image resource within the app works just fine, only it's not from a URL as is required.
What is the proper way to configure the ImageGetter and/or the append method so that this functionality will work with Picasso? Or is there a simpler way?
Append method:
public void appendToMessageHistory(final String username,
final String message) {
if (username != null && message != null) {
Picasso.with(getBaseContext())
.load("http://localhost:3000/uploads/campaign/image/2/2.jpg")
.into(new Target() {
#Override
public void onPrepareLoad(Drawable arg0) {
}
#Override
public void onBitmapLoaded(Bitmap bitmap,
LoadedFrom arg1) {
Drawable drawImage = new BitmapDrawable(
getBaseContext().getResources(), bitmap);
drawImage.setBounds(0, 0,
drawImage.getIntrinsicHeight(),
drawImage.getIntrinsicWidth());
messageHistoryText.append(Html.fromHtml("<b>"
+ username + ":" + "</b>" + "<br>"));
messageHistoryText.append(Html.fromHtml(message
+ "<hr>" + "<br>")
+ System.getProperty("line.separator") + "");
messageHistoryText.append(Html
.fromHtml("<img src = '" + drawImage
+ "'/>",
imageGetter,
null));
}
#Override
public void onBitmapFailed(Drawable arg0) {
}
});
}
}
ImageGetter:
ImageGetter imageGetter = new ImageGetter() {
Drawable imageUsed=null;
#Override
public Drawable getDrawable(String source) {
Picasso.with(getBaseContext())
.load("http://localhost:3000/uploads/campaign/image/2/2.jpg")
.into(new Target() {
#Override
public void onPrepareLoad(Drawable arg0) {
}
#Override
public void onBitmapLoaded(Bitmap bitmap,
LoadedFrom arg1) {
Drawable drawImage = new BitmapDrawable(
getBaseContext().getResources(), bitmap);
drawImage.setBounds(0, 0,
drawImage.getIntrinsicHeight(),
drawImage.getIntrinsicWidth());
imageUsed=drawImage;
}
#Override
public void onBitmapFailed(Drawable arg0) {
}
});
return imageUsed;
}
};
I couldn't get it to work with using Picasso's Target...
My workaround is:
use an AsyncTask for concurrency
use Picasso's get() method to load the image synchronously in the AsyncTask's background method
Like this:
public class PicassoImageGetter implements Html.ImageGetter {
final Resources resources;
final Picasso pablo;
final TextView textView;
public PicassoImageGetter(final TextView textView, final Resources resources, final Picasso pablo) {
this.textView = textView;
this.resources = resources;
this.pablo = pablo;
}
#Override public Drawable getDrawable(final String source) {
final BitmapDrawablePlaceHolder result = new BitmapDrawablePlaceHolder();
new AsyncTask<Void, Void, Bitmap>() {
#Override
protected Bitmap doInBackground(final Void... meh) {
try {
return pablo.load(source).get();
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(final Bitmap bitmap) {
try {
final BitmapDrawable drawable = new BitmapDrawable(resources, bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
result.setDrawable(drawable);
result.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
textView.setText(textView.getText()); // invalidate() doesn't work correctly...
} catch (Exception e) {
/* nom nom nom*/
}
}
}.execute((Void) null);
return result;
}
static class BitmapDrawablePlaceHolder extends BitmapDrawable {
protected Drawable drawable;
#Override
public void draw(final Canvas canvas) {
if (drawable != null) {
drawable.draw(canvas);
}
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
}
}
Hope this is useful.
I built upon Thomas' answer. I used the existing Picasso methods to download any images on a different thread and accounted for the placeholder Drawables as well.
public class PicassoImageGetter implements Html.ImageGetter {
private TextView textView = null;
public PicassoImageGetter() {}
public PicassoImageGetter(TextView target) {
textView = target;
}
#Override
public Drawable getDrawable(String source) {
BitmapDrawablePlaceHolder drawable = new BitmapDrawablePlaceHolder();
Context context = FeedSurferApp.getContext();
FeedSurferApp
.getPicasso()
.load(source)
.error(ResourcesCompat.getDrawable(context.getResources(), R.drawable.connection_error, context.getTheme()))
.into(drawable);
return drawable;
}
private class BitmapDrawablePlaceHolder extends BitmapDrawable implements Target {
protected Drawable drawable;
#Override
public void draw(final Canvas canvas) {
if (drawable != null) {
drawable.draw(canvas);
}
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
if (textView != null) {
textView.setText(textView.getText());
}
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setDrawable(new BitmapDrawable(FeedSurferApp.getContext().getResources(), bitmap));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
setDrawable(errorDrawable);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {}
}
}
I found a way to use ImageGetter with Picasso Target:
public Drawable getDrawable(String source) {
final BitmapDrawablePlaceHolder result = new BitmapDrawablePlaceHolder();
Picasso.with(getContext()).load(source).into(new Target() {
#Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
final BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
result.setDrawable(drawable);
result.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
content.setText(content.getText());
// cache is now warmed up
}
#Override public void onBitmapFailed(Drawable errorDrawable) { }
#Override public void onPrepareLoad(Drawable placeHolderDrawable) { }
});
return result;
}
This uses the cache and is no longer a synchronous call requiring the AsyncTask.
Credit to: Fetch images with Callback in Picasso?
Another simple way to use Picasso is to create a custom TextView and make it implement Target interface of Picasso.
Then you can do something as simple as this.
Picasso.get().load("Some_img_url").placeholder(R.drawable.ic_launcher_foreground).into(tv)
Below is the class
class TextViewWithImageLoader #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr), Target {
enum class Direction { START, END, TOP, BOTTOM }
private var mWidthInPixels: Int = 0
private var mHeightInPixels: Int = 0
private var mDirection: Direction = Direction.START //default value
// This method initialize the required parameters for the TextView to load the image
fun setupImageLoader(widthInPixels: Int, heightInPixels: Int, direction: Direction) {
mWidthInPixels = widthInPixels
mHeightInPixels = heightInPixels
mDirection = direction
}
// sets the size of the drawable
private fun setDrawableBounds(drawable: Drawable) {
drawable.setBounds(0, 0, mWidthInPixels, mHeightInPixels)
}
// Sets the initial placeholder drawable
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
//checking if height and width are valid
if (placeHolderDrawable != null && mWidthInPixels > 0 && mHeightInPixels > 0) {
setDrawableBounds(placeHolderDrawable)
setDrawable(placeHolderDrawable)
}
}
// set the drawable based on the Direction enum
private fun setDrawable(placeHolderDrawable: Drawable?) {
when (mDirection) {
Direction.START -> setCompoundDrawables(placeHolderDrawable, null, null, null);
Direction.END -> setCompoundDrawables(null, null, placeHolderDrawable, null);
Direction.TOP -> setCompoundDrawables(null, placeHolderDrawable, null, null);
Direction.BOTTOM -> setCompoundDrawables(null, null, null, placeHolderDrawable);
}
}
//In this method we receive the image from the url
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
//checking if height and width are valid
if (mWidthInPixels > 0 && mHeightInPixels > 0) {
val drawable = BitmapDrawable(resources, bitmap)
setDrawableBounds(drawable)
setDrawable(drawable)
}
}
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
//Do nothing as we are already setting a default value in onPrepareLoad method
// you can add your logic here if required
}
}
You can use this class directly or if you currently have your own custom view then just extend this class.
You can either go through this medium article for more details
OR
You can see the sample project on github
I'm trying to develop an Android 3.1 tablet application.
This app will have a lot of images, and I have followed Processing Bitmaps Off the UI Thread tutorial, but I've done something wrong because I get:
java.lang.ClassCastException: android.widget.AbsListView$LayoutParams cannot be cast to android.widget.Gallery$LayoutParams
This is my code:
I set up Gallery on an Activity
mFactGallery = (Gallery)mView.findViewById(R.id.factGallery);
mFactGallery.setAdapter(new ImageGalleryAdapter(mActivity, ImageView.ScaleType.FIT_END, 180, 90));
ImageGalleryAdapter.java
public class ImageGalleryAdapter extends BaseAdapter
{
private ArrayList<String> mImagesPath;
private Context mContext;
private int mWidth;
private int mHeight;
public ArrayList<String> getmImagesPath()
{
return mImagesPath;
}
public void setmImagesPath(ArrayList<String> mImagesPath)
{
this.mImagesPath = mImagesPath;
}
public void addImage(String imagePath)
{
mImagesPath.add(imagePath);
}
public ImageGalleryAdapter(Context context, ImageView.ScaleType scaleType, int width, int height)
{
mContext = context;
mWidth = width;
mHeight = height;
mScaleType = scaleType;
mImagesPath = new ArrayList<String>();
}
#Override
public int getCount()
{
return mImagesPath.size();
}
#Override
public Object getItem(int position)
{
return position;
}
#Override
public long getItemId(int position)
{
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// Get a View to display image data
ImageView imageView;
// if it's not recycled, initialize some attributes
if (convertView == null)
{
imageView = new ImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new GridView.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
else
{
imageView = (ImageView) convertView;
// Recicla el Bitmap.
Bitmap bm = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
if (bm != null)
bm.recycle();
}
String filePath = mImagesPath.get(position);
if (BitmapTools.cancelPotentialWork(filePath, imageView))
{
String[] params = {filePath, Integer.toString(mWidth), Integer.toString(mHeight)};
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable =
new AsyncDrawable(mContext.getResources(), filePath, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(params);
}
return imageView;
}
}
BitmapWorkerTask.java
public class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>
{
private final WeakReference<ImageView> imageViewReference;
public String imgPath = "";
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(String... params)
{
imgPath = params[0];
int width = Integer.valueOf(params[1]).intValue();
int height = Integer.valueOf(params[2]).intValue();
return BitmapTools.decodeSampledBitmapFromDisk(imgPath, width, height);
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap)
{
if (isCancelled())
{
bitmap = null;
}
if (imageViewReference != null && bitmap != null)
{
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask =
BitmapTools.getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null)
{
imageView.setImageBitmap(bitmap);
}
}
}
}
AsyncDrawable.java
public class AsyncDrawable extends BitmapDrawable
{
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, String filepath,
BitmapWorkerTask bitmapWorkerTask)
{
super(res, filepath);
bitmapWorkerTaskReference =
new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask()
{
return bitmapWorkerTaskReference.get();
}
}
What am I doing wrong?
replace GridView.LayoutParams with Gallery.LayoutParams as follows, that will solve your problem
imageView.setLayoutParams(new Gallery.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
In ImageGalleryAdapter.java, this is wrong:
imageView.setLayoutParams(new GridView.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
Use the correct LayoutParams class, it should be
imageView.setLayoutParams(new Gallery.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
(The superclass of LayoutParams is Gallery instead of GridView)