I've created a Grid with a RecyclerView
similar to the GoogleIO 2014's generic media player one.
Mine
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