6
\$\begingroup\$

I've created a Grid with a RecyclerView similar to the GoogleIO 2014's generic media player one.

GoogleIO generic music player GoogleIO generic music player

Mine

Mine :3

The thing is that the images doesn't load immediately, but with a delay, so I had to put that "dirty" code for add a crossfade every time an image is loaded.

Beside that, I just want the code to be the most efficient and clean possible.

RecyclerViewAdapter

public class AlbumsRecyclerViewAdapter extends CursorRecyclerAdapter<AlbumsRecyclerViewAdapter.AlbumHolder> {

    private Context mContext;
    private int mArtistColumnIndex = -1;
    private int mAlbumColumnIndex = -1;
    private int mIdColumnIndex = -1;
    private int mDefaultTextColor, mDefaultBackgroundColor;
    private int mTileElevation;
    private int mImageSize = DEFAULT_IMAGE_SIZE;
    private static float IMAGE_SIZE_MULTIPLIER = 0.50f;
    private HashMap<Long, Palette> mPaletteCache = new HashMap<>();

    private static int DEFAULT_IMAGE_SIZE = 150;

    private static final int[] ATTRS = new int[]{
            android.R.attr.textColorPrimaryInverse
    };

    public AlbumsRecyclerViewAdapter(Context context, Cursor c) {
        super(c);
        mContext = context;
        final TypedArray typedArray = context.obtainStyledAttributes(ATTRS);
        mDefaultTextColor = typedArray.getColor(0, Color.WHITE);
        typedArray.recycle();
        mTileElevation = context.getResources().getDimensionPixelSize(R.dimen.tile_elevation);
        mDefaultBackgroundColor = ContextCompat.getColor(context, R.color.grid_item_background);

    }

    @Override
    public void onBindViewHolder(AlbumHolder holder, Cursor cursor) {
         holder.bind(cursor);
    }

    @Override
    public AlbumHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new AlbumHolder(LayoutInflater.from(mContext).inflate(R.layout.album_grid_item, parent, false));
    }

    public class AlbumHolder extends RecyclerView.ViewHolder {

        private TextView mName, mArtist;
        private ImageView mImage;

        public AlbumHolder(View itemView) {
            super(itemView);
            mName = (TextView) itemView.findViewById(R.id.album_grid_item_name);
            mArtist = (TextView) itemView.findViewById(R.id.album_grid_item_artist);
            mImage = (ImageView) itemView.findViewById(R.id.album_grid_item_image);
            ViewCompat.setElevation(itemView, mTileElevation);

    }

    private void resetColors() {
        mName.setTextColor(mDefaultTextColor);
        mArtist.setTextColor(mDefaultTextColor);
        itemView.setBackgroundColor(mDefaultBackgroundColor);
    }

    private void getColumnsIndices(Cursor cursor) {
        if (mAlbumColumnIndex == -1 && cursor != null) {
            mAlbumColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM);
        }
        if (mArtistColumnIndex == -1 && cursor != null) {
            mArtistColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums.ARTIST);
        }
        if (mIdColumnIndex == -1 && cursor != null) {
            mIdColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums._ID);
        }
    }

    private boolean setColorsFromCache(long albumId) {
        Palette palette = mPaletteCache.get(albumId);
        if (palette != null) {
            setColorsFromPalette(palette, false);
            return true;
        }
        return false;
    }

    private void setColorsFromPalette(Palette palette, boolean animate) {
        Palette.Swatch swatch = palette.getVibrantSwatch();
        if (swatch != null) {
            if (animate) {
                ViewUtils.setBackgroundColorWithAnimation(itemView, mDefaultBackgroundColor, palette.getVibrantColor(swatch.getRgb()));
                ViewUtils.setTextColorWithAnimation(mName, mDefaultTextColor, palette.getVibrantSwatch().getBodyTextColor());
                ViewUtils.setTextColorWithAnimation(mArtist, mDefaultTextColor, palette.getVibrantSwatch().getBodyTextColor());
            } else {
                itemView.setBackgroundColor(palette.getVibrantColor(swatch.getRgb()));
                mName.setTextColor(palette.getVibrantSwatch().getBodyTextColor());
                mArtist.setTextColor(palette.getVibrantSwatch().getBodyTextColor());
            }
        }
    }


    public void bind(Cursor cursor) {
        if (mImageSize == DEFAULT_IMAGE_SIZE) {
            mImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @SuppressLint("NewApi")
                @SuppressWarnings("deprecation")
                @Override
                public void onGlobalLayout() {
                    mImageSize = (int) (mImage.getWidth() * IMAGE_SIZE_MULTIPLIER);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
                        mImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    else
                        mImage.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                }
            });
        }
        resetColors();
        getColumnsIndices(cursor);
        if (cursor != null) {
            final long albumId = cursor.getLong(mIdColumnIndex);
            final boolean colorsFromCache = setColorsFromCache(albumId);
            String name = cursor.getString(mAlbumColumnIndex);
            String artist = cursor.getString(mArtistColumnIndex);
            mName.setText(name);
            mArtist.setText(artist);
            Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
            Uri uri = ContentUris.withAppendedId(sArtworkUri, albumId);
            Glide.with(mContext)
                    .load(uri)
                    .asBitmap()
                    .override(mImageSize, mImageSize)
                    .listener(new RequestListener<Uri, Bitmap>() {
                        @Override
                        public boolean onException(Exception e, Uri model, Target<Bitmap> target, boolean isFirstResource) {
                            return false;
                        }

                        @Override
                        public boolean onResourceReady(Bitmap resource, Uri model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
                            if (!colorsFromCache)
                                Palette
                                        .from(resource)
                                        .generate(new Palette
                                                          .PaletteAsyncListener() {
                                                      @Override
                                                      public void onGenerated(Palette palette) {
                                                          setColorsFromPalette(palette, true);
                                                          mPaletteCache.put(albumId, palette);
                                                      }
                                                  }

                                        );
                            ViewUtils.setImageBitmapWithAnimation(mImage, resource);
                            return true;
                        }
                    })
                    .into(mImage);
            itemView.setOnClickListener(new View.OnClickListener()

                                        {
                                            @Override
                                            public void onClick(View v) {

                                            }
                                        }

            );
        }

    }

}
}

ViewUtils

public class ViewUtils {

public static int DEFAULT_ANIMATION_DURATION = 150;

//This class should not be instantiated
private ViewUtils() {
}

@SuppressWarnings("deprecation")
public static void setBackground(View view, Drawable drawable) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        view.setBackgroundDrawable(drawable);
    } else {
        view.setBackground(drawable);
    }
}

public static void setBackgroundColorWithAnimation(View view, int fromColor, int toColor) {
    ColorDrawable[] colors = {new ColorDrawable(fromColor), new ColorDrawable(toColor)};
    TransitionDrawable transitionDrawable = new TransitionDrawable(colors);
    setBackground(view, transitionDrawable);
    transitionDrawable.startTransition(DEFAULT_ANIMATION_DURATION);
}

public static void setImageBitmapWithAnimation(ImageView imageView, Bitmap bitmap) {
    final TransitionDrawable transitionDrawable =
            new TransitionDrawable(new Drawable[]{
                    new ColorDrawable(Color.TRANSPARENT),
                    new BitmapDrawable(imageView.getContext().getResources(), bitmap)
            });
    imageView.setImageDrawable(transitionDrawable);
    transitionDrawable.startTransition(DEFAULT_ANIMATION_DURATION);

}

public static void setTextColorWithAnimation(final TextView textView, int fromColor, int toColor) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(fromColor, toColor);
    anim.setEvaluator(new ArgbEvaluator());
    anim.setDuration(DEFAULT_ANIMATION_DURATION);
    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            textView.setTextColor((int) animation.getAnimatedValue());
        }
    });
    anim.start();
}

}

CursorRecyclerAdapter

https://gist.github.com/quanturium/46541c81aae2a916e31d

\$\endgroup\$
2
  • 1
    \$\begingroup\$ -1 for Pharrell but +2 for the stuff that looks good ;-) \$\endgroup\$
    – rolfl
    Commented Sep 18, 2015 at 19:16
  • \$\begingroup\$ @rolfl Haha, that's the generic music player from GoogleIO, i'll upload screenshot of mine. ;) \$\endgroup\$ Commented Sep 18, 2015 at 19:39

1 Answer 1

2
\$\begingroup\$
private void getColumnsIndices(Cursor cursor) {
    if (mAlbumColumnIndex == -1 && cursor != null) {
        mAlbumColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM);
    }
    if (mArtistColumnIndex == -1 && cursor != null) {
        mArtistColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums.ARTIST);
    }
    if (mIdColumnIndex == -1 && cursor != null) {
        mIdColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums._ID);
    }
}

If the cursor is null, you don't need to do anything in this function. So turn it into a guard clause:

private void getColumnsIndices(Cursor cursor) {
    if (cursor == null) { return; }

    if (mAlbumColumnIndex == -1) {
        mAlbumColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM);
    }
    if (mArtistColumnIndex == -1) {
        mArtistColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums.ARTIST);
    }
    if (mIdColumnIndex == -1) {
        mIdColumnIndex = cursor.getColumnIndex(MediaStore.Audio.Albums._ID);
    }
}

private void setColorsFromPalette(Palette palette, boolean animate) {
    Palette.Swatch swatch = palette.getVibrantSwatch();
    if (swatch != null) {
        if (animate) {
            ViewUtils.setBackgroundColorWithAnimation(itemView, mDefaultBackgroundColor, palette.getVibrantColor(swatch.getRgb()));
            ViewUtils.setTextColorWithAnimation(mName, mDefaultTextColor, palette.getVibrantSwatch().getBodyTextColor());
            ViewUtils.setTextColorWithAnimation(mArtist, mDefaultTextColor, palette.getVibrantSwatch().getBodyTextColor());
        } else {
            itemView.setBackgroundColor(palette.getVibrantColor(swatch.getRgb()));
            mName.setTextColor(palette.getVibrantSwatch().getBodyTextColor());
            mArtist.setTextColor(palette.getVibrantSwatch().getBodyTextColor());
        }
    }
}

Here you make 4 calls (but only 2 at most per method invocation) to palette.getVibrantSwatch().getBodyTextColor(), which is a bit of a waste to me. Try storing the result of the method call in a temporary variable. Alternatively, consider extracting changing of color values to separate functions; one for animated color changes, one for static color changes. Arguments would be the body text color and the RGB from the swatch.

\$\endgroup\$
1
  • \$\begingroup\$ Well, it turns out that the actual implementation of the Cursor caches the column indices, so I completly removed that part. Anyway, thanks for the advices for the setColorsFromPalette method. I'll accept this answer. \$\endgroup\$ Commented Apr 9, 2016 at 21:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.