Created
September 24, 2015 13:14
-
-
Save pgloaguen/bb33b5b8e680e1894ad7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.su.polo.mediapicker; | |
import android.content.ContentResolver; | |
import android.content.Context; | |
import android.database.Cursor; | |
import android.database.MergeCursor; | |
import android.graphics.Bitmap; | |
import android.graphics.Rect; | |
import android.os.AsyncTask; | |
import android.provider.MediaStore; | |
import android.support.v4.util.LruCache; | |
import android.support.v7.widget.GridLayoutManager; | |
import android.support.v7.widget.RecyclerView; | |
import android.util.AttributeSet; | |
import android.util.TypedValue; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.widget.ImageView; | |
/** | |
* Created by polo on 24/09/15. | |
*/ | |
public class MediaPickerView extends RecyclerView { | |
private BitmapLruCache mBitmapLruCache; | |
private Cursor mDatas; | |
public MediaPickerView(Context context) { | |
super(context); | |
init(); | |
} | |
public MediaPickerView(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
init(); | |
} | |
public MediaPickerView(Context context, AttributeSet attrs, int defStyle) { | |
super(context, attrs, defStyle); | |
init(); | |
} | |
@Override | |
protected void onDetachedFromWindow() { | |
super.onDetachedFromWindow(); | |
if (getMediaPickerAdapter() != null) { | |
getMediaPickerAdapter().release(); | |
setAdapter(null); | |
} | |
if (mBitmapLruCache != null) { | |
mBitmapLruCache.evictAll(); | |
} | |
if (mDatas != null && !mDatas.isClosed()) { | |
mDatas.close(); | |
} | |
} | |
public void setOnMediaClicked(onMediaClicked onMediaClicked) { | |
getMediaPickerAdapter().setListener(onMediaClicked); | |
} | |
private void init() { | |
mBitmapLruCache = new BitmapLruCache((int) (Runtime.getRuntime().maxMemory() / 1024 / 16)); | |
setLayoutManager(new GridLayoutManager(getContext(), 3)); | |
addItemDecoration(new GridSpacingItemDecoration(3, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()), false)); | |
mDatas = loadMedia(); | |
setAdapter(new MediaPickerAdapter(mBitmapLruCache, mDatas)); | |
} | |
private Cursor loadMedia() { | |
ContentResolver cr = getContext().getContentResolver(); | |
String[] imgColumns = new String[] { | |
MediaStore.Images.ImageColumns._ID, | |
MediaStore.Images.ImageColumns.MIME_TYPE, | |
MediaStore.Images.ImageColumns.DATA | |
}; | |
String[] videoColumns = new String[] { | |
MediaStore.Video.VideoColumns._ID, | |
MediaStore.Video.VideoColumns.MIME_TYPE, | |
MediaStore.Video.VideoColumns.DATA | |
}; | |
final Cursor imgDatas = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imgColumns, null, null, null); | |
final Cursor videoDatas = cr.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoColumns, null, null, null); | |
return new MergeCursor(new Cursor[]{videoDatas, imgDatas}); | |
} | |
private MediaPickerAdapter getMediaPickerAdapter() { | |
return (MediaPickerAdapter) getAdapter(); | |
} | |
private static class MediaPickerAdapter extends Adapter<MediaViewHolder> { | |
private BitmapLruCache mBitmapLruCache; | |
private Cursor mData; | |
private onMediaClicked mListener; | |
public MediaPickerAdapter(BitmapLruCache bitmapLruCache, Cursor data) { | |
mBitmapLruCache = bitmapLruCache; | |
mData = data; | |
} | |
@Override | |
public MediaViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | |
return new MediaViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false)); | |
} | |
@Override | |
public void onBindViewHolder(MediaViewHolder holder, int position) { | |
if(mData.moveToPosition(position)) { | |
if (holder.task != null) holder.task.cancel(true); | |
final long id = mData.getLong(0); | |
final String mimeType = mData.getString(1); | |
final String uri = mData.getString(2); | |
Bitmap bitmap = mBitmapLruCache.get(id); | |
((ImageView) holder.itemView).setImageBitmap(bitmap); | |
if (bitmap == null) { | |
holder.task = | |
new DecodeBitmap( | |
mBitmapLruCache, | |
holder.itemView.getContext().getContentResolver(), | |
id, | |
(ImageView) holder.itemView, | |
mimeType) | |
.execute(); | |
} | |
holder.itemView.setOnClickListener(new OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
if (mListener != null ){ | |
if (isVideo(mimeType)) { | |
mListener.onVideoSelected(uri); | |
} else if(isGif(mimeType)) { | |
mListener.onGifSelected(uri); | |
} else { | |
mListener.onImageSelected(uri); | |
} | |
} | |
} | |
}); | |
} | |
} | |
private boolean isVideo(String mimeType) { | |
return mimeType.startsWith("video/"); | |
} | |
private boolean isImage(String mimeType) { | |
return mimeType.equals("image/"); | |
} | |
private boolean isGif(String mimeType) { | |
return mimeType.equals("image/gif"); | |
} | |
@Override | |
public int getItemCount() { | |
return mData == null ? 0 : mData.getCount(); | |
} | |
public void release() { | |
mBitmapLruCache = null; | |
mData = null; | |
} | |
public void setListener(onMediaClicked listener) { | |
mListener = listener; | |
} | |
} | |
private static class BitmapLruCache extends LruCache<Long, Bitmap> { | |
public BitmapLruCache(int maxSize) { | |
super(maxSize); | |
} | |
@Override | |
protected int sizeOf(Long key, Bitmap bitmap) { | |
return (bitmap.getRowBytes() * bitmap.getHeight())/1024; | |
} | |
} | |
private static class DecodeBitmap extends AsyncTask<Void, Void, Bitmap> { | |
private BitmapLruCache mBitmapLruCache; | |
private ContentResolver mContentResolver; | |
private long mId; | |
private ImageView mImageView; | |
private String mMimeType; | |
public DecodeBitmap(BitmapLruCache bitmapLruCache, ContentResolver contentResolver, long id, ImageView imageView, String mimeType) { | |
mBitmapLruCache = bitmapLruCache; | |
mContentResolver = contentResolver; | |
mId = id; | |
mImageView = imageView; | |
mMimeType = mimeType; | |
} | |
@Override | |
protected Bitmap doInBackground(Void... params) { | |
return decodeThumbnail(); | |
} | |
@Override | |
protected void onPostExecute(Bitmap bitmap) { | |
super.onPostExecute(bitmap); | |
if (bitmap != null) { | |
mBitmapLruCache.put(mId, bitmap); | |
} | |
mImageView.setImageBitmap(bitmap); | |
} | |
@Override | |
protected void onCancelled() { | |
super.onCancelled(); | |
if (mMimeType.startsWith("image")) { | |
MediaStore.Images.Thumbnails.cancelThumbnailRequest(mContentResolver, mId); | |
} else { | |
MediaStore.Video.Thumbnails.cancelThumbnailRequest(mContentResolver, mId); | |
} | |
} | |
private Bitmap decodeThumbnail() { | |
if (mMimeType.startsWith("image")) { | |
return MediaStore.Images.Thumbnails.getThumbnail( | |
mContentResolver, mId, | |
MediaStore.Images.Thumbnails.MINI_KIND, null); | |
} else { | |
return MediaStore.Video.Thumbnails.getThumbnail( | |
mContentResolver, mId, | |
MediaStore.Images.Thumbnails.MINI_KIND, null); | |
} | |
} | |
} | |
private static class MediaViewHolder extends ViewHolder { | |
public AsyncTask<Void, Void, Bitmap> task; | |
public MediaViewHolder(View itemView) { | |
super(itemView); | |
} | |
} | |
public interface onMediaClicked { | |
void onGifSelected(String uri); | |
void onImageSelected(String uri); | |
void onVideoSelected(String uri); | |
} | |
//http://stackoverflow.com/a/30701422 | |
private static class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { | |
private int spanCount; | |
private int spacing; | |
private boolean includeEdge; | |
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) { | |
this.spanCount = spanCount; | |
this.spacing = spacing; | |
this.includeEdge = includeEdge; | |
} | |
@Override | |
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { | |
int position = parent.getChildAdapterPosition(view); // item position | |
int column = position % spanCount; // item column | |
if (includeEdge) { | |
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) | |
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) | |
if (position < spanCount) { // top edge | |
outRect.top = spacing; | |
} | |
outRect.bottom = spacing; // item bottom | |
} else { | |
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) | |
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) | |
if (position >= spanCount) { | |
outRect.top = spacing; // item top | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment