Android之基于RecycleVew的相册选择器
2016-07-23 06:34
609 查看
本文主要记录一个Android自定义的相册
相册可以实现选择图片,视频,gif(或其他任何格式文件)
源码:https://github.com/CL-window/my_photo_view
看看效果
其中RecycleView的分割线修改了一下,基本上可以实现万能分割线类了
相册可以实现选择图片,视频,gif(或其他任何格式文件)
源码:https://github.com/CL-window/my_photo_view
看看效果
其中RecycleView的分割线修改了一下,基本上可以实现万能分割线类了
package com.example.photoview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v4.content.ContextCompat; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; /** * <p>Description: 分割线 (近乎万能)</p> * Created by slack on 2016/7/21 13:56 . */ public class DividerGridItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; private Drawable mDivider; private int type = 0; // 分割线种类 0:水平(默认) 1:垂直 2 :网格 private int mDividerHeight = 2;//分割线高度,默认为1px private Paint mPaint; /** * 默认分割线:高度为2px,颜色为灰色 * @param context */ public DividerGridItemDecoration(Context context) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); mDividerHeight = mDivider.getIntrinsicHeight(); a.recycle(); } /** * @param type 分割线种类 0:水平(默认) 1:垂直 2 :网格 */ public DividerGridItemDecoration(Context context, int type) { this.type = type; final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); mDividerHeight = mDivider.getIntrinsicHeight(); a.recycle(); } /** * @param drawableId 分割线图片(自定义drawable) */ public DividerGridItemDecoration(Context context, int type,int drawableId) { this.type = type; mDivider = ContextCompat.getDrawable(context, drawableId); mDividerHeight = mDivider.getIntrinsicHeight(); } /** * @param dividerHeight 分割线高度 * @param dividerColor 分割线颜色 */ public DividerGridItemDecoration(Context context, int type,int dividerHeight, int dividerColor) { this.type = type; mDividerHeight = dividerHeight; mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(dividerColor); mPaint.setStyle(Paint.Style.FILL); } //绘制分割线 @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if (type == 0) { drawHorizontal(c, parent); } else if (type == 1) { drawVertical(c, parent); }else{ drawVertical(c, parent); drawHorizontal(c, parent); } } private int getSpanCount(RecyclerView parent) { // 列数 int spanCount = -1; RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager) .getSpanCount(); } return spanCount; } //绘制横向 item 分割线 public void drawHorizontal(Canvas canvas, RecyclerView parent) { int childCount = parent.getChildCount(); final int left = parent.getPaddingLeft(); final int right = parent.getMeasuredWidth() - parent.getPaddingRight(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDividerHeight; if (mDivider != null) { mDivider.setBounds(left, top, right, bottom); mDivider.draw(canvas); } if (mPaint != null) { canvas.drawRect(left, top, right, bottom, mPaint); } } } //绘制纵向 item 分割线 public void drawVertical(Canvas canvas, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin; final int right = left + mDividerHeight; if (mDivider != null) { mDivider.setBounds(left, top, right, bottom); mDivider.draw(canvas); } if (mPaint != null) { canvas.drawRect(left, top, right, bottom, mPaint); } } } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一列,则不需要绘制右边 return true; } } return false; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一行,则不需要绘制底部 return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); // StaggeredGridLayoutManager 且纵向滚动 if (orientation == StaggeredGridLayoutManager.VERTICAL) { childCount = childCount - childCount % spanCount; // 如果是最后一行,则不需要绘制底部 if (pos >= childCount) return true; } else // StaggeredGridLayoutManager 且横向滚动 { // 如果是最后一行,则不需要绘制底部 if ((pos + 1) % spanCount == 0) { return true; } } } return false; } //获取分割线尺寸 @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); outRect.set(0, 0, 0, mDividerHeight); } }看看adapter怎么写的,图片选择时是加了一个颜色过滤器
package com.example.photoview; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.PopupWindow; import android.widget.TextView; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * <p>Description: </p> * Created by slack on 2016/7/21 13:28 . */ public class PhotoViewAdapter extends RecyclerView.Adapter<PhotoViewAdapter.PhotoViewHolder>{ protected RecyclerView mRecyclerView; private List<String> testData; // test private List<Map<String,String>> data; // title + uri private String[] fileType = {"video","photo","gif"}; private GifView gifView ; private PopupWindow popwindow ; private float downX,downY; private int selectColor; public boolean operation; // public List<Map<String,String>> selectData = new ArrayList<>(); public String[] selectPath;//感觉使用数组会好一些,处理浪费空间 private Handler mHandler; public PhotoViewAdapter(RecyclerView recyclerView, List<Map<String,String>> data) { this.mRecyclerView = recyclerView; this.data = data; gifView = new GifView(mRecyclerView.getContext()); popwindow = new PopupWindow(gifView, FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, false); popwindow.setAnimationStyle(R.style.popwin_anim_style); // 动画 selectColor = Color.parseColor("#77000000"); selectPath = new String[data.size()]; mHandler = new Handler(Looper.getMainLooper()); } // public PhotoViewAdapter(Context context, List<String> data) { // this.context = context; // this.testData = data; // } @Override public PhotoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new PhotoViewHolder(LayoutInflater.from(mRecyclerView.getContext()).inflate(R.layout.photo_item,parent,false)); } @Override public void onBindViewHolder(PhotoViewHolder holder, int position) { //缩 holder.photoView.setImageBitmap(ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(data.get(position).get("pic_path")), 300, 300)); // holder.photoInfo.setText(data.get(position).get("title").toString()); // holder.photoInfo.setText(testData.get(position)); } @Override public int getItemCount() { return data.size(); // return testData.size(); } /** * 子线程里执行,操作防止时间过长 */ public void cancleSelect(){ new Thread(new Runnable() { @Override public void run() { operation = false; for(int i = 0; i < selectPath.length; i++){ if(!TextUtils.isEmpty(selectPath[i])){ final RecyclerView.ViewHolder viewHolder = mRecyclerView.findViewHolderForAdapterPosition(i); final int opt = i; if(viewHolder != null && viewHolder instanceof PhotoViewHolder){ mHandler.post(new Runnable() { @Override public void run() { ((PhotoViewHolder) viewHolder).unSelect(opt); } }); } } } } }).start(); } class PhotoViewHolder extends RecyclerView.ViewHolder { ImageView photoView,selcetView; // TextView photoInfo; public PhotoViewHolder(View itemView) { super(itemView); photoView = (ImageView) itemView.findViewById(R.id.photo_item_img); selcetView = (ImageView) itemView.findViewById(R.id.photo_item_selcet); // photoInfo = (TextView)itemView.findViewById(R.id.photo_item_info); photoView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int pos = getAdapterPosition(); // Log.i("slack","click..." + pos + " " + testData.get(pos) ); // Log.i("slack","click..." + pos + " " + data.get(pos) ); if(operation){ if (selcetView.getVisibility() == View.GONE) { select(pos); } else { unSelect(pos); } } } }); photoView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { int pos = getAdapterPosition(); Log.i("slack","onLongClick..." + pos + " " + data.get(pos) ); if(fileType[2].equals(data.get(pos).get("file_type"))){ // popwindow show // 设置好参数之后再show gifView.setMovieResource(data.get(pos).get("pic_path")); // popwindow.showAsDropDown(view); popwindow.showAtLocation(view,Gravity.CENTER,0,0);// 显示在整个屏幕的中央 return true; } return false ; // 调用 onClick 事件,dissmiss } }); /** * 好坑呀,一旦按住时移动,ontouch事件就不执行了 * 只能通过按住点的坐标来判断了 */ photoView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { // Log.i("slack","onTouch..." + motionEvent.getX() + "," + motionEvent.getY()); switch (motionEvent.getAction()){ case MotionEvent.ACTION_DOWN: downX = motionEvent.getX(); downY = motionEvent.getY(); break; case MotionEvent.ACTION_UP: dissmissPoup(); break; default: if(isMove(motionEvent.getX(),motionEvent.getY()) ){ dissmissPoup(); } break; } return false; } }); } private void select(int pos) { photoView.setColorFilter(selectColor); selcetView.setVisibility(View.VISIBLE); selectPath[pos] = data.get(pos).get("pic_path"); // Map<String,String> map = new HashMap<>(); // map.put("select_position",pos+""); // map.put("pic_path",data.get(pos).get("pic_path")); // selectData.add(pos,map); } private void unSelect(int pos) { photoView.setColorFilter(null); selcetView.setVisibility(View.GONE); // selectData.remove() // 删还需要遍历 selectPath[pos] = ""; } } public void removeData(final int position) { data.remove(position); mHandler.post(new Runnable() { @Override public void run() { notifyItemRemoved(position); } }); } private void dissmissPoup() { if( popwindow != null && popwindow.isShowing()){ popwindow.dismiss(); } } private boolean isMove(float x, float y) { if(Math.abs(x - downX) > 0 || Math.abs(y - downY) > 0){ return true; } return false; } }gif的显示是用的前辈们的代码,mainActivtity里分别实现了图片,视频获取缩略图(直接查询数据库),获取GIF(文件夹深度遍历)
package com.example.photoview; import android.app.Dialog; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.DialogInterface; import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; import android.provider.Settings; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MainActivity extends AppCompatActivity { private String filePath; private RecyclerView mRecyclerView; private PhotoViewAdapter mPhotoViewAdapter; private List<Map<String, String>> mediaList = new ArrayList<>(); private List<Map<String, String>> imageList = new ArrayList<>(); private List<Map<String, String>> gifList = new ArrayList<>(); DividerGridItemDecoration mDividerGridItemDecoration; String[] imagesColums = new String[]{ MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, MediaStore.Images.Media.TITLE, MediaStore.Images.Media.MIME_TYPE, MediaStore.Images.Media.DATE_MODIFIED }; String[] mediaThumbColumns = new String[]{ MediaStore.Video.Thumbnails.DATA, MediaStore.Video.Thumbnails.VIDEO_ID }; String[] mediaColumns = new String[]{ MediaStore.Video.Media.DATA, MediaStore.Video.Media._ID, MediaStore.Video.Media.TITLE, MediaStore.Video.Media.MIME_TYPE, MediaStore.Images.Media.DATE_MODIFIED }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); // initData(); // scanImage(); // scanVideo(); scanGif(); } private void scanGif() { new Thread(new Runnable() { @Override public void run() { scanGif("gif", filePath); if (gifList.size() > 0) { Log.i("slack", "size:" + gifList.size()); if (gifList.size() <= 4) { mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)); mRecyclerView.removeItemDecoration(mDividerGridItemDecoration); mDividerGridItemDecoration = new DividerGridItemDecoration(MainActivity.this); mRecyclerView.addItemDecoration(mDividerGridItemDecoration); } mPhotoViewAdapter = new PhotoViewAdapter(mRecyclerView, gifList); mRecyclerView.setAdapter(mPhotoViewAdapter); } } }).start(); } // 根据文件夹路径读取里面的 gif 图片 private void scanGif(String type, String path) { File file = new File(path); if (!file.exists()) { return; } File[] files = file.listFiles(); Map<String, String> map; if (files != null) { for (File f : files) { if (!f.isDirectory()) { if (isType(type, f.getName())) { map = new HashMap<>(); map.put("pic_path", f.getAbsolutePath()); map.put("file_type", type); gifList.add(map); // Log.i("slack","path " + f.getPath() + " " + // f.getAbsolutePath()); } } else { scanGif(type, path + File.separator + f.getName()); } } } } private boolean isType(String type, String name) { if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) { return false; } if (name.length() > (type.length() + 1)) { if (type.equals(name.substring(name.lastIndexOf(".") + 1))) { return true; } } return false; } // 缩略图路径 thumbPath 文件路径filePath private void scanVideo() { getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { // SELECT * FROM TABLE limit 0, 1 SORT BY DATE_MODIFIED DESC return new CursorLoader(MainActivity.this, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, mediaColumns, null, null, MediaStore.Images.Media.DATE_MODIFIED + " DESC"); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if (data != null && !data.isClosed() && data.getCount() > 0) { Log.i("slack", "Count: " + data.getCount()); // data.moveToFirst(); String path; Map<String, String> map; File file; while (data.moveToNext()) { path = data.getString(data.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)); map = new HashMap<>(); map.put("file_path", path); // Log.i("slack", "filePath: " + path); // Log.i("slack", "title: " + data.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE) ); //获取当前Video对应的Id,然后根据该ID获取其Thumb int id = data.getInt(data.getColumnIndexOrThrow(MediaStore.Video.Media._ID)); Cursor thumbCursor = getContentResolver().query(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, mediaThumbColumns, MediaStore.Video.Thumbnails.VIDEO_ID + "=?", new String[]{id + ""}, null); if (thumbCursor.moveToFirst()) { path = thumbCursor.getString(thumbCursor.getColumnIndexOrThrow(MediaStore.Video.Thumbnails.DATA)); // Log.i("slack","thumbPath: " + path); map.put("pic_path", path); } Log.i("slack", map.get("file_path") + "," + map.get("pic_path")); mediaList.add(map); } data.close(); System.gc(); } else { Log.i("slack", "no data"); } Log.i("slack", "mediaList: " + mediaList.size()); mPhotoViewAdapter = new PhotoViewAdapter(mRecyclerView, mediaList); mRecyclerView.setAdapter(mPhotoViewAdapter); } @Override public void onLoaderReset(Loader<Cursor> loader) { } }); } private void scanImage() { getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { // SELECT * FROM TABLE limit 0, 1 SORT BY DATE_MODIFIED DESC return new CursorLoader(MainActivity.this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imagesColums, null, null, MediaStore.Images.Media.DATE_MODIFIED + " DESC"); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if (data != null && !data.isClosed() && data.getCount() > 0) { Log.i("slack", "Count: " + data.getCount()); data.moveToFirst(); String path; Map<String, String> map; File file; while (data.moveToNext()) { path = data.getString(data.getColumnIndex(MediaStore.Images.Media.DATA)); map = new HashMap<>(); map.put("pic_path", path); imageList.add(map); // Log.i("slack", "Path: " + path); // Log.i("slack", "Date: " + data.getString(data.getColumnIndex(MediaStore.Images.Media.DATE_MODIFIED))); } data.close(); System.gc(); } else { Log.i("slack", "no data"); } Log.i("slack", "imageList: " + imageList.size()); mPhotoViewAdapter = new PhotoViewAdapter(mRecyclerView, imageList); mRecyclerView.setAdapter(mPhotoViewAdapter); } @Override public void onLoaderReset(Loader<Cursor> loader) { } }); } private void initData() { // ArrayList<String> mDatas = new ArrayList<String>(); // for (int i = 'A'; i < 'z'; i++){ // mDatas.add("" + (char) i); // } // mPhotoViewAdapter = new PhotoViewAdapter(this,mDatas); } TextView right; View cancleView; private void initView() { filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Movies"; mDividerGridItemDecoration = new DividerGridItemDecoration(this, 2); mRecyclerView = (RecyclerView) findViewById(R.id.photo_recyclerview); //设置布局管理器 // mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); // 现行管理器,支持横向、纵向。 // mRecyclerView.setLayoutManager(new GridLayoutManager(this,4)); // 网格布局,没行4个 // 瀑布流式的布局 VERTICAL代表有多少列 HORIZONTAL 就代表有多少行 mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL)); //设置adapter // mRecyclerView.setAdapter(mPhotoViewAdapter); //设置Item增加、移除动画 // mRecyclerView.setItemAnimator(new DefaultItemAnimator()); //添加分割线 mRecyclerView.addItemDecoration(mDividerGridItemDecoration); right = (TextView) findViewById(R.id.head_rigth); cancleView = findViewById(R.id.cancle_view); cancleView.setVisibility(View.GONE); right.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Log.i("slack","click...right"); if (cancleView.getVisibility() == View.GONE) { cancleView.setVisibility(View.VISIBLE); right.setText("删除"); mPhotoViewAdapter.operation = true; } else { Log.i("slack", "click...del"); deleteSelect(); } } }); findViewById(R.id.cancle).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Log.i("slack","click...cancle"); cancleSelect(); } }); } private void cancleSelect() { cancleView.setVisibility(View.GONE); right.setText("操作"); mPhotoViewAdapter.cancleSelect(); } private void deleteSelect() { new AlertDialog.Builder(this).setMessage("确认删除这些吗?") .setTitle("温馨提示") .setPositiveButton("确认", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < mPhotoViewAdapter.selectPath.length; i++) { if (!TextUtils.isEmpty(mPhotoViewAdapter.selectPath[i])) { mPhotoViewAdapter.removeData(i); new File(mPhotoViewAdapter.selectPath[i]).delete(); } } } }).start(); cancleSelect(); dialog.dismiss(); } }).setNegativeButton("取消", null).show(); } }图片单击进入预览的没有做,不是本次的重点,其实list里有图片的路径,这个功能实现也简单,把那个路径传过去,把图片加载出来就可以了
相关文章推荐
- 解决RecycleView分割线不居中的三种方法
- RecycleView初尝试
- RecycleView 中的item移动到当前视图的顶部
- android获取recycleview滚动的距离
- RecycleView 的使用
- 【Android-RecyclerView】瀑布流实现
- RecyclerView中的item的match_parent属性失效问题解决方案。
- 封装Recycleview.Adapter使Recycleview具有加载更多,item点击事件等功能
- RecyclerView简单使用
- RecycleView
- RecycleView-ListView优化
- ListView分割线,RecycleView分割线
- 横向滑动的Recycleview
- RecycleView的Item数据出现随机错乱重复问题解决
- 解决ScrollView嵌套RecycleView高度不适配问题
- RecycleView使用体验(一)
- AdapterView VS RecycleView
- RecycleView横向滑动item变大
- Android RecycleView的使用
- Android 开发之RecycleView的简单使用