趁热打铁,使用内容解析者(ContentResolver)来实现一个图片选择器,大体的功能样式仿照的数字尾巴,( ̄▽ ̄)”只是粗仿……
下面是运行效果(GIF图片超过2MB就不能上传了/(ㄒoㄒ)/~~):
MediaStore这个类是android系统提供的一个多媒体数据库,android中多媒体信息都可以从这里提取。这个MediaStore包括了多媒体数据库的所有信息,包括音频,视频和图像。操作就和使用内容解析这获取其它数据的过程一样,android把所有的多媒体数据库接口进行了封装,所有的数据库不用自己进行创建,直接调用利用ContentResolver去掉用那些封装好的接口就可以进行数据库的操作了。
可以通过查看系统上层源码来查看数据封装的方式,以及在data目录下查看数据库,查看各种格式的文件在数据库中的存储方式,方便对后面的操作的理解。
下面叙述一下实现的过程:
目录结构:
ImageView的加强,重新自定义了一个可以选择的ImageView
MyCustomImageView.java
package com.example.imageselector;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;
/**
* Created by 春水碧于天 on 2017/1/18.
*/
public class MyCustomImageView extends ImageView {
private int mViewWidth; //当前ImageView的宽
private int mViewHeight; //当前ImageView的高
private Paint mPaint_stroke;
private Paint mPaint_fill;
private Paint mPaint_text;
private String mDrawText; //要绘制的文本
private Boolean isChoice = false;
private onCheckedChangeListener mCheckedChangeListener;
private boolean isCanTouch = true;
public MyCustomImageView(Context context) {
super(context);
InitPaint();
}
public MyCustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
InitPaint();
}
public MyCustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
InitPaint();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mViewWidth = w;
mViewHeight = h;
}
public void setDrawText(String mDrawText){
this.mDrawText = mDrawText;
invalidate(); //重新绘制
}
public void setChecked(boolean isChecked){
isChoice = isChecked;
invalidate();
}
public void setIsCanTouch(boolean isCanTouch){
this.isCanTouch = isCanTouch;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
/**
* 如果没有选中的话,按下选中
* 如果已经选中的话,按下清除内容
*/
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
if(!isChoice){ //如果没有选中
isChoice = true;
invalidate();
}else if(isChoice){ //如果已经选中了
isChoice = false;
invalidate();
}
//回调
mCheckedChangeListener.isChecked(isChoice);
Log.i("tag","手指按下了");
break;
case MotionEvent.ACTION_UP:
Log.i("tag","手指抬起了");
break;
}
if(isCanTouch){
return true;
}else{
return false;
}
}
//初始化画笔
public void InitPaint(){
mPaint_stroke = new Paint(Paint.ANTI_ALIAS_FLAG); //设置抗锯齿
mPaint_stroke.setStyle(Paint.Style.STROKE);//设置填充模式为描边
mPaint_stroke.setStrokeWidth(8f);//设置画笔的宽度
mPaint_stroke.setColor(Color.BLUE); //设置画笔的颜色
mPaint_fill = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint_fill.setStyle(Paint.Style.FILL);//设置填充模式为描边
mPaint_fill.setColor(Color.BLUE); //设置画笔的颜色
mPaint_text = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint_text.setTextSize(40f);
mPaint_text.setColor(Color.rgb(255,255,255));
mPaint_text.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(isChoice){
//绘制矩形边框
RectF rectF = new RectF(0f,0f,mViewWidth-10,mViewHeight);
canvas.drawRect(rectF, mPaint_stroke);
float mCircleX =mViewWidth*(2/3.0f)+(mViewWidth/6.0f);
float mCircleY =mViewHeight*(1/6.0f);
float mRadius = (mViewHeight/6.0f)*0.7f;
//绘制圆
canvas.drawCircle(mCircleX,mCircleY,mRadius, mPaint_fill);
//绘制文字
if(mDrawText!=null) {
canvas.drawText(mDrawText, mCircleX, mCircleY + 13f, mPaint_text);
}
}
}
/**
*设置选中后的回调监听
*/
public void SetOnCheckedChangeListener(onCheckedChangeListener mCheckedChangeListener){
this.mCheckedChangeListener = mCheckedChangeListener;
}
public interface onCheckedChangeListener{
void isChecked(boolean isSelected);
}
}
图片选择器的核心就是对图片的扫描,这面就使用内容解析者,获取安卓系统通过内容提供者暴露进出的数据库的增删改查的接口,进行操数的操作,后面只需要根据自己的需要筛选出需要的文件夹,以及对应格式的图片,下面的查询是对所有图片的查询没,有在查询的时候的筛根据条件进行晒选。
MainActivity.java
package com.example.imageselector;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
private ArrayList<FileBean> FileBeans = null;
private Handler mHandler = null;
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InitUI();
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 1){
listView.setAdapter(new myAdapter(MainActivity.this,FileBeans));
}
}
};
/**
* 在线程中处理扫描文件的耗时操作
*/
new Thread(new Runnable() {
Set<String> DirSet = null;
@Override
public void run() {
if (Environment.getExternalStorageDirectory() == null){
Log.i("tag","外部存储卡为空");
return;
}
//通过内容解析者获取手机中所有图片的路径
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver resolver = MainActivity.this.getContentResolver();
//查询所有的图片信息,根据修改的时间排序
Cursor cursor = resolver.query(uri, null, null, null, MediaStore.Images.Media.DATE_MODIFIED);
DirSet = new HashSet<String>(); //用于记录已经扫描过的文件夹,避免重复扫描
FileBeans = new ArrayList<FileBean>(); //存放图片文件夹信息
FileBean fileBean = null;
while(cursor.moveToNext()){
String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
if(filePath.endsWith(".gif") || filePath.endsWith(".GIF")) continue; //过滤掉包含GIF图片的文件夹
File Parentfile = new File(filePath).getParentFile(); //获取文件所在的文件夹
if(Parentfile==null) continue;
if(DirSet.contains(Parentfile.toString())) continue; //如果文件夹里的内容已经遍历过就执行下一次的循环
String[] list = Parentfile.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (name.endsWith(".png") || name.endsWith(".jpeg") || name.endsWith(".jpg") ) {
return true;
}
return false;
}
});
DirSet.add(Parentfile.toString());
int fileNums = list.length;
fileBean = new FileBean();
fileBean.setFileList(list); //封装一个文件夹中包含的所有图片
fileBean.setFileNums(fileNums); //封装一个文件夹中的图片个数
fileBean.setFileParent(Parentfile);
fileBean.setFirstImage(filePath); //一个文件夹中对应的第一张图片
FileBeans.add(fileBean);
}
mHandler.sendEmptyMessage(1);
cursor.close();
}
}).start();
listView.setOnItemClickListener(this);
}
private void InitUI() {
listView = (ListView) findViewById(R.id.listview);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
FileBean fileBean = (FileBean) parent.getItemAtPosition(position);
Intent intent = new Intent(MainActivity.this,ShowImageActivity.class);
intent.putExtra("fileBean", (Serializable) fileBean);
startActivity(intent);
}
}
用于显示文件夹的listview适配器:myAdapter.java
/**
* Created by 春水碧于天 on 2017/1/17.
*/
public class myAdapter extends BaseAdapter {
private ArrayList<FileBean> BeanList;
private Context mContext;
public myAdapter(Context mContext,ArrayList<FileBean> BeanList) {
this.BeanList = BeanList;
this.mContext = mContext;
}
@Override
public int getCount() {
return BeanList.size();
}
@Override
public Object getItem(int position) {
return BeanList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHoder viewHolder = null;
if(convertView == null){
viewHolder = new ViewHoder();
convertView= View.inflate(mContext,R.layout.listview_item,null);
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
viewHolder.tv_nums = (TextView) convertView.findViewById(R.id.tv_num);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHoder) convertView.getTag();
}
String []path = BeanList.get(position).getFileParent().toString().split("/");
int index = path.length-1;
viewHolder.tv_name.setText(path[index]);
viewHolder.tv_nums.setText(BeanList.get(position).getFileNums()+"");
Glide.with(mContext).load(BeanList.get(position).getFirstImage()).into(viewHolder.imageView);
return convertView;
}
static class ViewHoder {
public TextView tv_name;
public TextView tv_nums;
public ImageView imageView;
}
}
Glide.with(mContext).load(BeanList.get(position).getFirstImage()).into(viewHolder.imageView);
这里的图片加载使用的是谷歌推荐的Glide,惊喜的发现GIF图也是可以加载的,总的来说使用起来还是蛮优雅的,一行代码搞定,Glide和Android的Picasso使用起来api惊人的相似……
ShowImageView.java
public class ShowImageActivity extends AppCompatActivity {
private GridView gridView;
private TextView tv_cancle;
private TextView tv_complete;
private HashMap<Integer, Integer> mChoicePosition = new HashMap<Integer, Integer>(); //记录选择的图片对应的下标数(GridView复用的问题)
private HashMap<Integer, ImageView> mChoiceImage = new HashMap<Integer, ImageView>(); //记录选择的图片
private FileBean fileBean;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_image);
InitUI();
fileBean = (FileBean) getIntent().getSerializableExtra("fileBean");
gridView.setAdapter(new MyGridAdapter(fileBean, this));
//点击完成执行的相应操作
tv_complete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String AllChoiceImagePath[] = getChoiceImage(); //获取选择的图片存放在数组中
}
});
}
private String[] getChoiceImage() {
Iterator<Map.Entry<Integer, Integer>> iterator = mChoicePosition.entrySet().iterator();
int index[] = new int[mChoicePosition.size()];
int i=0;
while(iterator.hasNext()){
Map.Entry entry = (Map.Entry)iterator.next();
index[i++] = (int) entry.getKey(); //根据key(position)获取图片
}
Arrays.sort(index); //排序
String ImagePath[] = new String[mChoicePosition.size()]; //存放选择的图片的路径
for (int num:index) {
ImagePath[num] = fileBean.getFileParent().toString()+fileBean.getFileList()[num]; //根据选择的下标获取fileBean对象中存储的图片的路径,存放在数组中
}
for(String imagePath:ImagePath){
Log.i("tag","ImagePath:"+imagePath);
}
return ImagePath;
}
private void InitUI() {
gridView = (GridView) findViewById(R.id.gridView);
tv_cancle = (TextView) findViewById(R.id.tv_cancle);
tv_complete = (TextView) findViewById(R.id.tv_complete);
}
/**
* GridView的适配器
*/
public class MyGridAdapter extends BaseAdapter {
private FileBean fileBean;
private Context mContext;
private int CurrentNum = 1;
public MyGridAdapter(FileBean fileBean, Context mContext) {
this.fileBean = fileBean;
this.mContext = mContext;
}
@Override
public int getCount() {
return fileBean.getFileList().length;
}
@Override
public Object getItem(int position) {
return fileBean.getFileParent() + fileBean.getFileList()[position];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = View.inflate(mContext, R.layout.grid_item, null);
viewHolder = new ViewHolder();
viewHolder.imageViewGrid = (MyCustomImageView) convertView.findViewById(R.id.imageViewGrid);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
final File file = new File(fileBean.getFileParent(), fileBean.getFileList()[position]);
Glide.with(mContext).load(file).into(viewHolder.imageViewGrid);
final ViewHolder ViewHolder = viewHolder;
viewHolder.imageViewGrid.SetOnCheckedChangeListener(new MyCustomImageView.onCheckedChangeListener() {
@Override
public void isChecked(boolean isSelected) {
//点击图片进行选择的时候调用
if (CurrentNum < 10) {
if (isSelected) {
//将选中的图片记录到map集合中
mChoicePosition.put(position, CurrentNum);
mChoiceImage.put(position, ViewHolder.imageViewGrid);
ViewHolder.imageViewGrid.setDrawText(CurrentNum + "");
CurrentNum++;
} else {
Log.i("tag", "CurrentNum--:" + CurrentNum);
Log.i("tag", "Position==>" + position);
ChangeIndex(position);//根据选择动态改变图片对应的下标数
}
} else {
if (isSelected) { //如果是未选中状态就将其状态置为未选中状态
ViewHolder.imageViewGrid.setChecked(false);
} else {
ChangeIndex(position);
}
if (CurrentNum == 10) {
Toast.makeText(ShowImageActivity.this, "最多选择9张图片", Toast.LENGTH_SHORT).show();
}
}
if (CurrentNum == 0) CurrentNum = 1;
int num = mChoicePosition.size();
if(num==0){
tv_complete.setText("完成");
}
else{
tv_complete.setText("完成 "+num+"/9");
}
}
/**
* 根据选择动态改变图片对应的下标数
*/
private void ChangeIndex(int position) {
if (mChoicePosition.containsKey(position)) {
int num = mChoicePosition.get(position);
Iterator iter = mChoicePosition.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
int key = (int) entry.getKey();
int val = (int) entry.getValue();
Log.i("tag", "key: " + key);
Log.i("tag", "value: " + val);
//将比当前点击的下标大的数进行减一操作
if (val > num) {
val = val - 1;
mChoicePosition.put(key, val);
MyCustomImageView iv = (MyCustomImageView) mChoiceImage.get(key);
iv.setDrawText(val + "");
}
}
mChoicePosition.remove(position);
mChoiceImage.remove(position);
ViewHolder.imageViewGrid.setDrawText(CurrentNum + "");
CurrentNum--;
}
}
});
if (mChoicePosition.get(position) != null) {
viewHolder.imageViewGrid.setChecked(true);
viewHolder.imageViewGrid.setDrawText(String.valueOf(mChoicePosition.get(position)));
} else {
viewHolder.imageViewGrid.setChecked(false);
}
return convertView;
}
class ViewHolder {
public MyCustomImageView imageViewGrid;
}
}
}
由于GridView的复用,这里采用Map集合的方式将选中的图片的位置的下标记录到Map集合中,然后再根据map集合中的数据将选择的那个图片对应的Index设置给ImageView,防止ImageView复用造成的界面的错乱。
FileBean.java
/**
* Created by 春水碧于天 on 2017/1/17.
*/
public class FileBean implements Serializable{
private String firstImage;//第一张图片的文件名
private File fileParent; //文件的父路径
private int fileNums; //文件的数量
private String[] fileList;
private int ChoiceImage = -1; //设置选择的图片的下标数
public String getFirstImage() {
return firstImage;
}
public void setFirstImage(String firstImage) {
this.firstImage = firstImage;
}
public File getFileParent() {
return fileParent;
}
public void setFileParent(File fileParent) {
this.fileParent = fileParent;
}
public String[] getFileList() {
return fileList;
}
public void setFileList(String[] fileList) {
this.fileList = fileList;
}
public int getFileNums() {
return fileNums;
}
public void setFileNums(int fileNums) {
this.fileNums = fileNums;
}
}
布局就不朝上面贴了,反正挺简单的。
(被有些逻辑恶心到了……期待医生的群英传,更上一层楼!!)