Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add load image uri and make item layout responsive #24

Conversation

radityarin
Copy link
Contributor

i try to add load image uri and make item layout responsive, please check @adityapp @gifff @zainfikrih

thanks.

@gifff gifff added the enhancement New feature or request label Jun 12, 2020
Copy link

@adityapp adityapp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one of the reasons why we set the image type to Drawable is we want to giving flexibilities to users to choose which library they use, like Picasso or Glide. do you remember that? @gifff @zainfikrih

void bind(final MenuItem menuItem, boolean isImageUri) {
title.setText(menuItem.getTitle());
if (isImageUri) {
String toHttpsUri = menuItem.getImageUri().replace("http://","https://");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think, should we replace http to https? which better, throw an exception to giving error message or just replace like this? @zainfikrih @gifff

@gifff
Copy link
Contributor

gifff commented Jun 13, 2020

Basically the issue with this implementation is that it's not scalable when you want to add more kind of image source (example: you want to load the image from local file path).

One way to solve the issue is to use generic for MenuItem and utilize Glide to load the image source.

Something like this:

public class MenuItem<T> {
    private String title;
    private T imageSource;

    public MenuItem(String title, T imageSource){
        this.title = title;
        this.imageSource = imageSource;
    }

    public String getTitle() {
        return title;
    }

    public T getImageSource() {
        return imageSource;
    }
}

and then for the adapter, you do something like this:

class ViewHolder extends RecyclerView.ViewHolder {

  // ...

    void bind(final MenuItem<?> menuItem) {
        title.setText(menuItem.getTitle());
        RequestManager glideRequest = Glide.with(itemView);
        RequestBuilder<Drawable> glideRequestBuilder = null;

        if (menuItem.getImageSource() instanceof String) {
            glideRequestBuilder = glideRequest.load(menuItem.getImageSource());
        } else  if (menuItem.getImageSource() instanceof Uri) {
            glideRequestBuilder = glideRequest.load(menuItem.getImageSource());
        } else  if (menuItem.getImageSource() instanceof Drawable) {
            glideRequestBuilder = glideRequest.load(menuItem.getImageSource());
        } else  if (menuItem.getImageSource() instanceof Bitmap) {
            glideRequestBuilder = glideRequest.load(menuItem.getImageSource());
        }

        if (glideRequestBuilder != null) {
            glideRequestBuilder.into(image);
        }

        imageOverlay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (gridMenuOnClickListener != null) {
                    gridMenuOnClickListener.onClick(getAdapterPosition());
                }
            }
        });
    }
}

and the list will look like:

final ArrayList<MenuItem<?>> list = new ArrayList<>();
list.add(new MenuItem<>("satu", getResources().getDrawable(R.drawable.ic_launcher_background)));
list.add(new MenuItem<>("dua", getResources().getDrawable(R.drawable.ic_launcher_background)));
list.add(new MenuItem<>("lima", "https://cdn3.iconfinder.com/data/icons/capsocial-round/500/youtube3-128.png"));
list.add(new MenuItem<>("enam","https://cdn3.iconfinder.com/data/icons/capsocial-round/500/facebook-128.png"));

However, this library will be heavily dependent on Glide and give the users no flexibility to choose their own image loader library.

@radityarin
Copy link
Contributor Author

radityarin commented Jun 13, 2020

Basically the issue with this implementation is that it's not scalable when you want to add more kind of image source (example: you want to load the image from local file path).

One way to solve the issue is to use generic for MenuItem and utilize Glide to load the image source.

Something like this:

public class MenuItem<T> {
    private String title;
    private T imageSource;

    public MenuItem(String title, T imageSource){
        this.title = title;
        this.imageSource = imageSource;
    }

    public String getTitle() {
        return title;
    }

    public T getImageSource() {
        return imageSource;
    }
}

and then for the adapter, you do something like this:

class ViewHolder extends RecyclerView.ViewHolder {

  // ...

    void bind(final MenuItem<?> menuItem) {
        title.setText(menuItem.getTitle());
        RequestManager glideRequest = Glide.with(itemView);
        RequestBuilder<Drawable> glideRequestBuilder = null;

        if (menuItem.getImageSource() instanceof String) {
            glideRequestBuilder = glideRequest.load(menuItem.getImageSource());
        } else  if (menuItem.getImageSource() instanceof Uri) {
            glideRequestBuilder = glideRequest.load(menuItem.getImageSource());
        } else  if (menuItem.getImageSource() instanceof Drawable) {
            glideRequestBuilder = glideRequest.load(menuItem.getImageSource());
        } else  if (menuItem.getImageSource() instanceof Bitmap) {
            glideRequestBuilder = glideRequest.load(menuItem.getImageSource());
        }

        if (glideRequestBuilder != null) {
            glideRequestBuilder.into(image);
        }

        imageOverlay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (gridMenuOnClickListener != null) {
                    gridMenuOnClickListener.onClick(getAdapterPosition());
                }
            }
        });
    }
}

and the list will look like:

final ArrayList<MenuItem<?>> list = new ArrayList<>();
list.add(new MenuItem<>("satu", getResources().getDrawable(R.drawable.ic_launcher_background)));
list.add(new MenuItem<>("dua", getResources().getDrawable(R.drawable.ic_launcher_background)));
list.add(new MenuItem<>("lima", "https://cdn3.iconfinder.com/data/icons/capsocial-round/500/youtube3-128.png"));
list.add(new MenuItem<>("enam","https://cdn3.iconfinder.com/data/icons/capsocial-round/500/facebook-128.png"));

However, this library will be heavily dependent on Glide and give the users no flexibility to choose their own image loader library.

Thanks for the input mas gif, i'll edit the MenuItem to Generic Types.
I forgot uri means not only url but local file path is also uri too.
And for the Glide, i also thought using glide will make this library heavily dependent too, but in the end i decided to use it because CircleImageView is using library too, so i thinks its fine at the moment. So how do yo suggest, is it ok if we use Glide library or make a new classes that load image from uri?
and also how's your opinion about replacing http to https to avoid permission of cleartext http traffic? is it necessary or just let the developer who use this lib to add their own network config?
Thanks mas gif @gifff .

@99ridho
Copy link

99ridho commented Jun 13, 2020

@gifff solutions is also good, but if we want to add another image source type, it will require many changes on ViewHolder.

I was thought bout another solutions, we can wrap ImageSource using contract something like this. This is will give us flexibilty to decide what source that can be use to make a drawable:

interface ImageProvider {
    Drawable getDrawable();
}

public class MenuItem {
    private String title;
    private ImageProvider imageProvider;

    public MenuItem(String title, ImageProvider imageProvider){
        this.title = title;
        this.imageProvider = imageProvider;
    }

    public String getTitle() {
        return title;
    }

    public Drawable getDrawable() {
        return imageProvider.getDrawable();
    }
}

class NetworkImageProvider implements ImageProvider {

    private URL url;

    NetworkImageProvider(URL url) {
        this.url = url;
    }

    Drawable getDrawable() {
        // return drawable from url
    }
}

class LocalImageProvider implements ImageProvider {
    private String filePath;

    LocalImageProvider(String filePath) {
        this.filePath = filePath;
    }

    Drawable getDrawable() {
        // return drawable from filepath
    }
}

class AnotherImageProvider implements ImageProvider {
    Drawable getDrawable() {
        // whaterver you want
    }
}

Example:

NetworkImageProvider np = new NetworkImageProvider("https://foo.com/image.jpg");
LocalImageProvider lp = new LocalImageProvider("/media/imgs/kuda.jpg");

MenuItem item = new MenuItem("Foo", np);
MenuItem kuda = new MenuItem("Kuda", lp);

wdyt?

@gifff
Copy link
Contributor

gifff commented Jun 13, 2020

@radityarin

And for the Glide, i also thought using glide will make this library heavily dependent too, but in the end i decided to use it because CircleImageView is using library too, so i thinks its fine at the moment. So how do yo suggest, is it ok if we use Glide library or make a new classes that load image from uri?

Fair enough. But if we could make the image loading system pluggable, isn't it better to let the user configure the loading implementation (such as caching, transformation, etc.) ?
But to me, such implementation requires re-designing the interface.

and also how's your opinion about replacing http to https to avoid permission of cleartext http traffic? is it necessary or just let the developer who use this lib to add their own network config?

About this, I think we should leave the choices to the user. It will reduce the chance of unexpected behaviour in the user side.

@99ridho To me, it is an elegant solution 👍

@radityarin
Copy link
Contributor Author

I dont know what the term is, but i think its more structured, clean and just like mas @gifff said its elegant too. Thanks mas @99ridho for the input, i'll try to implement it.

@adityapp
Copy link

@radityarin yes please redesign like @99ridho suggestion, I agree with that to make this project scalable

@radityarin
Copy link
Contributor Author

radityarin commented Jun 13, 2020

@radityarin yes please redesign like @99ridho suggestion, I agree with that to make this project scalable

@adityapp speaking of which, this redesign is going to make current test not working, because the changes in the model MenuItem, what should i do? is it fine if i delete it first or should i edit it? thanks.

@99ridho
Copy link

99ridho commented Jun 13, 2020

i think refactoring is also modify the test too

@adityapp
Copy link

yes, if you change the contract you should change the test also

…Drawable, Local, and Network drawable that implements ImageProvider
@radityarin
Copy link
Contributor Author

i try to implement mas @99ridho suggestion, please check the new commit @gifff @adityapp
thanks.

Comment on lines -9 to +13
public MenuItem(String title, Drawable image){
public MenuItem(String title, ImageProvider imageProvider) {
this.title = title;
this.image = image;
this.imageProvider = imageProvider;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep the previous constructor? That way, we don't introduce breaking-change to the previous users.

Do something like this:

public MenuItem(String title, Drawable image) {
    this(title, new DrawableImageProvider(image));
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added mas @gifff , please check, thanks.

@@ -2,7 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/square_layout"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_width="match_parent"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, why this is changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because if the size of every icon is different, then the size of the displayed icon will be different. so i change it to match_parent, to match all the icon to same size.

match_parent
image

wrap_content
image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Cool 👍

Copy link

@adityapp adityapp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good jobs man, Thank you 🍻

@@ -13,4 +13,5 @@
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:spanCount="4" />

</LinearLayout>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add newlines after last tag

@SuppressLint("StaticFieldLeak")
private Context context;

public LocalImageTask(ImageProvider imageProvider, Context context) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think will be better if you put context in first params to be consistency

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question tho, why use @SuppressLint("StaticFieldLeak") on line 17? it just suppress the warning about the possibility of memory leaked given inside AS 🤔 . Adding WeekReference<Context> would be great solution i guess

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks mas andri for the input, should i make a new pull request of this?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeeep, it would be great broo @radityarin
cc: @gifff @adityapp

@SuppressLint("StaticFieldLeak")
private Context context;

public NetworkImageTask(ImageProvider imageProvider, Context context) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be better if the first param is context

@radityarin
Copy link
Contributor Author

Good jobs man, Thank you 🍻

thanks for the review, i switched the parameter position, thanks mas @adityapp .

@adityapp adityapp merged commit d6f767d into bccfilkom:master Jun 14, 2020
@gifff gifff mentioned this pull request Jun 14, 2020
radityarin added a commit to radityarin/gridmenu that referenced this pull request Jun 15, 2020
add load image uri and make item layout responsive (bccfilkom#24)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants