1.概述

现在好多的app中都需要实现照片,视频的选择功能,例如头像的修改。网上找了许多例子都不太尽如人意,决定自己实现一下,博客记录一下,也加深自己的记忆。好了,先看一下效果图吧

android 图片跟随手指移动 android图片选择_3g

android 图片跟随手指移动 android图片选择_List_02

android 图片跟随手指移动 android图片选择_3g_03

简单描述一下:

1.默认显示的是所有的照片跟视频,当然了,可以手动选择显示所有的图片还是所有视频还是所有图片和视频。也可以手动选择是单选还是多选,只要在外部通过intent传递两个参数就可以了

2.点击上面的title,也就是所有图片的字,可以显示一个popupwindow,这个popupwindow显示了手机中所有的相册列表,你可以手动选择显示那个相册。所有的图片,视频,单独做成了一个文件夹。


2.上代码

首先是图片的实体类:


public class ImageBean implements Serializable {
    private int id;
    private String path;  //路径
    private boolean isVideo;//是否为视频
    private boolean isChecked = false;//是否选中

    public Object getTragetHolder() {
        return tragetHolder;
    }

    public void setTragetHolder(Object tragetHolder) {
        this.tragetHolder = tragetHolder;
    }

    private Object  tragetHolder = null;

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }

    public boolean isVideo() {
        return isVideo;
    }

    public void setVideo(boolean video) {
        isVideo = video;
    }

    public ImageBean(String path) {
        this.path = path;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

}

图片文件夹实体类:


public class ImageFolder implements Serializable {
    /* 文件夹名 */
    private String name;
    /* 文件夹路径 */
    private String dirPath;
    /* 该文件夹下图片列表 */
    private List<ImageBean> photoList;
    /* 标识是否选中该文件夹 */
    private boolean isSelected;

    public boolean isSelected() {
        return isSelected;
    }

    public void setIsSelected(boolean isSelected) {
        this.isSelected = isSelected;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDirPath() {
        return dirPath;
    }

    public void setDirPath(String dirPath) {
        this.dirPath = dirPath;
    }

    public List<ImageBean> getPhotoList() {
        return photoList;
    }

    public void setPhotoList(List<ImageBean> photoList) {
        this.photoList = photoList;
    }

参数就不一一解释了,相信大家都能看懂类中的参数的作用。


下面是关键的一个工具类:

所有的图片,视频逻辑查询


public class PhotoUtil {
    private static Map<String, ImageFolder> folderMap = new HashMap<>();
    /**
     * 扫描所有图片
     * @param context
     * @return
     */
    public static Map<String, ImageFolder> getPhotos(Context context) {
        String allPhotosKey = "所有图片";
        ImageFolder allFolder = new ImageFolder();
        allFolder.setName(allPhotosKey);
        allFolder.setDirPath(allPhotosKey);
        allFolder.setPhotoList(new ArrayList<ImageBean>());
        folderMap.put(allPhotosKey, allFolder);

        getPhoto(context,allPhotosKey);
        return folderMap;
    }

    /**
     * 扫描所有视频
     * @param context
     * @return
     */
    public static Map<String,ImageFolder> getVideos(Context context){
        String allVideosKey = "所有视频";
        ImageFolder allFolder = new ImageFolder();
        allFolder.setName(allVideosKey);
        allFolder.setDirPath(allVideosKey);
        allFolder.setPhotoList(new ArrayList<ImageBean>());
        folderMap.put(allVideosKey, allFolder);

        getVideo(context,allVideosKey);

        return folderMap;
    }

    /**
     * 扫描所有图片和视频
     * @param context
     * @return
     */
    public static Map<String, ImageFolder> getPhotosAndVideos(Context context) {


        String allKey = "所有图片和视频";
        ImageFolder allVideoFolder = new ImageFolder();
        allVideoFolder.setName(allKey);
        allVideoFolder.setDirPath(allKey);
        allVideoFolder.setPhotoList(new ArrayList<ImageBean>());
        folderMap.put(allKey, allVideoFolder);

        getPhoto(context,allKey);
        getVideo(context,allKey);

        return folderMap;
    }

    public static void getPhoto(Context context,String allPhotosKey){
        Uri imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        ContentResolver mContentResolver = context.getContentResolver();


        String[] projImage = { MediaStore.Images.Media._ID
                , MediaStore.Images.Media.DATA
                ,MediaStore.Images.Media.SIZE
                ,MediaStore.Images.Media.DISPLAY_NAME};

        // 只查询jpeg和png的图片 //"image/mp4","image/3gp"
        Cursor mCursor = mContentResolver.query(imageUri, projImage,
                MediaStore.Images.Media.MIME_TYPE + " in(?, ?, ?)",
                new String[] { "image/jpeg", "image/png", "image/jpg"},
                MediaStore.Images.Media.DATE_MODIFIED + " desc");



        int pathIndex = mCursor
                .getColumnIndex(MediaStore.Images.Media.DATA);

        if (mCursor.moveToFirst()) {
            do {
                // 获取图片的路径
                String path = mCursor.getString(pathIndex);

                // 获取该图片的父路径名
                File parentFile = new File(path).getParentFile();
                if (parentFile == null) {
                    continue;
                }
                String dirPath = parentFile.getAbsolutePath();

                if (folderMap.containsKey(dirPath)) {
                    ImageBean photo = new ImageBean(path);
                    photo.setVideo(false);
                    ImageFolder photoFolder = folderMap.get(dirPath);
                    photoFolder.getPhotoList().add(photo);
                    folderMap.get(allPhotosKey).getPhotoList().add(photo);
                    continue;
                } else {
                    // 初始化imageFolder
                    ImageFolder photoFolder = new ImageFolder();
                    List<ImageBean> photoList = new ArrayList<>();
                    ImageBean photo = new ImageBean(path);
                    photo.setVideo(false);
                    photoList.add(photo);
                    photoFolder.setPhotoList(photoList);
                    photoFolder.setDirPath(dirPath);
                    photoFolder.setName(dirPath.substring(dirPath.lastIndexOf(File.separator) + 1, dirPath.length()));
                    folderMap.put(dirPath, photoFolder);
                    folderMap.get(allPhotosKey).getPhotoList().add(photo);
                }
            } while (mCursor.moveToNext());
        }
        mCursor.close();
    }

    public static void getVideo(Context context,String allVideoSKey) {
        Uri videoUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
        ContentResolver mContentResolver = context.getContentResolver();

        String[] projVideo = {MediaStore.Video.Thumbnails._ID
                , MediaStore.Video.Thumbnails.DATA
                , MediaStore.Video.Media.DURATION
                , MediaStore.Video.Media.SIZE
                , MediaStore.Video.Media.DISPLAY_NAME
                , MediaStore.Video.Media.DATE_MODIFIED};

        Cursor mCursor = mContentResolver.query(videoUri, projVideo,
                MediaStore.Video.Media.MIME_TYPE + " in(?, ?, ?, ?)",
                new String[]{"video/mp4", "video/3gp", "video/avi", "video/rmvb"},
                MediaStore.Video.Media.DATE_MODIFIED + " desc");

        if (mCursor != null) {
            while (mCursor.moveToNext()) {
                // 获取视频的路径
                String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Video.Media.DATA));
                // 获取该视频的父路径名
                String dirPath = new File(path).getParentFile().getAbsolutePath();

                //存储对应关系
                if(folderMap.containsKey(dirPath)){
                    ImageBean video = new ImageBean(path);
                    video.setVideo(true);
                    video.setPath(path);
                    ImageFolder photoFolder = folderMap.get(dirPath);
                    photoFolder.getPhotoList().add(video);
                    folderMap.get(allVideoSKey).getPhotoList().add(video);
                }else{
                    ImageFolder photoFolder = new ImageFolder();
                    List<ImageBean> photoList = new ArrayList<>();
                    ImageBean video = new ImageBean(path);
                    video.setVideo(true);
                    video.setPath(path);
                    photoList.add(video);
                    photoFolder.setPhotoList(photoList);
                    photoFolder.setDirPath(dirPath);
                    photoFolder.setName(dirPath.substring(dirPath.lastIndexOf(File.separator) + 1, dirPath.length()));
                    folderMap.put(dirPath, photoFolder);
                    folderMap.get(allVideoSKey).getPhotoList().add(video);
                }

            }
            mCursor.close();
        }
    }
}

首先定义一个map,key为图片文件夹名称,value为装图片的list。通过getPhoto,getVideo两个方法将手机中所有的图片视频根据不同的文件存放在map中。查询的原理其实是用过ContentResolver根据

MediaStore.Images.Media.EXTERNAL_CONTENT_URI


MediaStore.Video.Media.EXTERNAL_CONTENT_URI

查询出的。可以通过new 一个String数组在作为查询的参数,数组中定义要查询的图片或视频格式,比如.jpg,png,mp4,rmvb等。外部activity只需要调用PhotoUtil中的方法便可获得存有所有图片的map(通过文件夹分开)。


下面是activity中调用的代码:

public class ChooseImageActivity extends AppCompatActivity {
    private GridView choose_image_gridview;
    private TextView choose_image_title_text;
    private FrameLayout choose_image_title_frame;
    private Map<String, ImageFolder> mFolderMap;
    private ChooseImageMyAdapter myAdapter;
//    private ChoosePicAdapter myAdapter;
    private List<ImageBean> mPhotoLists = new ArrayList<>();
    private final static String ALL_PHOTO = "所有图片";
    private final static String ALL_VIDEO = "所有视频";
    private final static String ALL_PHOTO_AND_VIDEO = "所有图片和视频";

    private PopupWindow mListImageDirPopupWindow;
    private List<ImageFolder> ImageFolders;

    private int SHOW_MODE;
    private boolean isSingleChoose = false;//是否单选。默认为false
    private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_choose_image);
        initIntentData();
        findViewById();
        getPhotosTask.execute();
    }

    private void initIntentData() {
        SHOW_MODE = getIntent().getIntExtra("showVideo",0);
        isSingleChoose = getIntent().getBooleanExtra("singleChoose",false);
    }

    private void findViewById() {
        choose_image_gridview = (GridView) findViewById(R.id.choose_image_gridview);
        choose_image_gridview.setFocusable(true);
        choose_image_title_text = (TextView) findViewById(R.id.choose_image_title_text);
        choose_image_title_frame = (FrameLayout) findViewById(R.id.choose_image_title_frame);
        btn = (Button) findViewById(R.id.btn);

        choose_image_title_text.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(isSingleChoose){
                    myAdapter.oldImageView = null;
                    myAdapter.oldImageViewYes = null;
                    for(int i = 0;i < mPhotoLists.size();i++){
                        mPhotoLists.get(i).setChecked(false);
                    }
                }

                if(mListImageDirPopupWindow != null && mListImageDirPopupWindow.isShowing()){
                    mListImageDirPopupWindow.dismiss();
                }else{
                    myAdapter.notifyDataSetChanged();
                    showFolderPopopWindow();
                }
            }
        });
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                List<String> picPath = myAdapter.getImagePath();
                for(int i = 0;i < picPath.size();i++){
                    Log.i("xxx","path   " + picPath.get(i));
                }
            }
        });
    }

    /**
     * 获取照片的异步任务
     */
    private AsyncTask getPhotosTask = new AsyncTask() {
        @Override
        protected void onPreExecute() {
//            mProgressDialog = ProgressDialog.show(PhotoPickerActivity.this, null, "loading...");
        }

        @Override
        protected Object doInBackground(Object[] params) {
            if(SHOW_MODE == 0){//显示全部
                mFolderMap = PhotoUtil.getPhotosAndVideos(ChooseImageActivity.this.getApplicationContext());
            }else if(SHOW_MODE == 1){//仅图片
                mFolderMap = PhotoUtil.getPhotos(ChooseImageActivity.this.getApplicationContext());
            }else if(SHOW_MODE == 2){//仅视频
                mFolderMap = PhotoUtil.getVideos(ChooseImageActivity.this.getApplicationContext());
            }
            return null;
        }

        @Override
        protected void onPostExecute(Object o) {
            getPhotosSuccess();
        }
    };

    /**
     * 加载照片成功
     */
    private void getPhotosSuccess() {
        if(SHOW_MODE == 0){//图片和视频
            choose_image_title_text.setText(ALL_PHOTO_AND_VIDEO);
            mPhotoLists.addAll(mFolderMap.get(ALL_PHOTO_AND_VIDEO).getPhotoList());
        }else if(SHOW_MODE == 1){//仅图片
            choose_image_title_text.setText(ALL_PHOTO);
            mPhotoLists.addAll(mFolderMap.get(ALL_PHOTO).getPhotoList());
        }else if(SHOW_MODE == 2){//仅视频
            choose_image_title_text.setText(ALL_VIDEO);
            mPhotoLists.addAll(mFolderMap.get(ALL_VIDEO).getPhotoList());
        }


        myAdapter = new ChooseImageMyAdapter(this.getApplicationContext(), mPhotoLists,isSingleChoose);
        choose_image_gridview.setAdapter(myAdapter);

        Set<String> keys = mFolderMap.keySet();
        final List<ImageFolder> folders = new ArrayList<>();
        for (String key : keys) {
            if (ALL_PHOTO.equals(key) || ALL_PHOTO_AND_VIDEO.equals(key) || ALL_VIDEO.equals(key)) {
                ImageFolder folder = mFolderMap.get(key);
                folder.setIsSelected(true);
                folders.add(0, folder);
            } else {
                folders.add(mFolderMap.get(key));
            }
        }
        ImageFolders = folders;

        choose_image_gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                ImageBean imageBean = myAdapter.getItem(position);
                if(imageBean == null) {
                    return;
                }
                String path = imageBean.getPath();
                Toast.makeText(ChooseImageActivity.this,path,Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * 显示相册文件夹popupWindow
     */
    private void showFolderPopopWindow() {

        View popupWindowView = getLayoutInflater().inflate(R.layout.activity_choose_image_list_pop_dir, null);
        mListImageDirPopupWindow = new PopupWindow(popupWindowView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, false);
        mListImageDirPopupWindow.showAsDropDown(choose_image_title_frame);
        ListView dirList = (ListView) popupWindowView.findViewById(R.id.list_pop_dir);

        final PopDirListAdapter adapter = new PopDirListAdapter(this,ImageFolders);
        dirList.setAdapter(adapter);

        mListImageDirPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {

            @Override
            public void onDismiss() {
                WindowManager.LayoutParams lp = getWindow().getAttributes();
                lp.alpha = 1f;
                getWindow().setAttributes(lp);
            }
        });

        dirList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {

                ImageFolder folder = ImageFolders.get(position);
                adapter.notifyDataSetChanged();
                mPhotoLists.clear();
                mPhotoLists.addAll(folder.getPhotoList());

                choose_image_gridview.setAdapter(myAdapter);
                choose_image_title_text.setText(folder.getName());
                mListImageDirPopupWindow.dismiss();
            }
        });
    }

    @Override
    public void finish() {
        super.finish();
        mFolderMap.clear();
        mPhotoLists.clear();
    }




因为查询图片是耗时操作,所有需要放在子线程中进行,所以我们放在了异步任务中进行操作。查询成功后,拿到map,遍历将所有的文件夹分开放到List<ImageFolder>中,作为popupwindow的数据源。通过key,拿到所有的图片或视频作为主页显示的数据源。至此基本功能已经实现。可能还会有一些bug,欢迎大家批评指