简易Gallery

简易Gallery

最近做了一个活动壁纸的需求,涉及到图片文件这块,发现图片如果想在系统的图库里展示需要主动通知发送一个广播,于是我就想着做一个能显示所有图片的Gallery。
1.判断读取sd卡权限。

 protected boolean applypermission() {
        //检查是否已经给了权限
        int checkpermission = ContextCompat.checkSelfPermission(getApplicationContext(),
                Manifest.permission.READ_EXTERNAL_STORAGE);
        if (checkpermission != PackageManager.PERMISSION_GRANTED) {//没有给权限
            //参数分别是当前活动,权限字符串数组,requestcode
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
        } else {
            return true;
        }
        return false;
    }

这里如果没有权限,就申请;再看返回,

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //grantResults数组与权限字符串数组对应,里面存放权限申请结果
        permissionsResult(grantResults[0] == PackageManager.PERMISSION_GRANTED);
    }

    protected void permissionsResult(boolean result) {
    }

2.主界面的相册列表
简易Gallery
这是Activity,读取sd卡 /storage/emulated/0/下面的目录,并找出该目录下所有图片的数量,如果数量为0,则不显示该目录


import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.widget.Toast;

import com.gallery.base.BaseActivity;
import com.gallery.bean.FileCountBean;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends BaseActivity {

    private RecyclerView mRecyclerView;
    private FileListAdapter mAdapter;
    private List<FileCountBean> mData = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (applypermission()) {
            getFileList();
        }
        mRecyclerView = (RecyclerView) findViewById(R.id.file_list);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        mAdapter = new FileListAdapter(this, mData);
        mRecyclerView.setAdapter(mAdapter);
    }

    @Override
    protected void permissionsResult(boolean result) {
        if (result) {
            getFileList();
        } else {
            showDialog();
        }
    }

    protected void showDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("没有读取sd卡的权限,可以到设置中打开");
        builder.setPositiveButton("ok", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                finish();
            }
        });
        builder.show();
    }

    private List<FileCountBean> getFileList() {
        List<File> fileList = new ArrayList<>();
        List<FileCountBean> beans = new ArrayList<>();
        String filePath = Environment.getExternalStorageDirectory().toString() + File.separator;
        File fileAll = new File(filePath);
        if (fileAll.isDirectory()) {
            File[] files = fileAll.listFiles();
            for (File file : files) {
                Log.d(TAG, "file = " + file.getPath());
            }
            fileList = Arrays.asList(files);
        }
        for (final File file : fileList) {
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    int count = getCount(file.getPath());
                    if (count != 0) {
                        mData.add(new FileCountBean(file.getPath(), file.getName(), count));
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mAdapter.notifyDataSetChanged();
                            }
                        });
                    }
                }
            }.start();
        }
        return beans;
    }

    private int getCount(String path) {
        return getGallery(new File(path)).size();
    }
}

适配器:


import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.gallery.bean.FileCountBean;

import java.util.List;

public class FileListAdapter extends RecyclerView.Adapter<FileListAdapter.ViewHolder> {

    private List<FileCountBean> mData;
    private Context mContext;

    public FileListAdapter(Context context, List<FileCountBean> files) {
        mData = files;
        mContext = context;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.file_list_item, null);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        holder.fileName.setText(mData.get(position).getName());
        holder.count.setText(mData.get(position).getCount() + "");
        holder.file_path.setText(mData.get(position).getPath());
        holder.fileItem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(mContext, ThreadShowActivity.class);
                intent.putExtra("path", mData.get(position).getPath());
                mContext.startActivity(intent);
            }
        });
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        TextView fileName, file_path;
        TextView count;
        View fileItem;

        public ViewHolder(View itemView) {
            super(itemView);
            fileName = itemView.findViewById(R.id.file_item_tv);
            count = itemView.findViewById(R.id.file_item_tv_count);
            fileItem = itemView.findViewById(R.id.file_item);
            file_path = itemView.findViewById(R.id.file_path);
        }
    }

}

3.某一个相册的具体图片的列表:
简易Gallery
这里主要用到的就是递归,将某一相册中所有的图片找出来


import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;

import com.gallery.base.BaseActivity;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class ThreadShowActivity extends BaseActivity {

    private static final String TAG = "MainActivityTAG";
    public static final int GALLERY_COLUM = 2;
    private RecyclerView mRecyclerView;
    private ThreadShowAdapter mAdapter;
    private List<PicBean> mData = new ArrayList<>();
    private TextView gallery_count, gallery_empty;

    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                gallery_empty.setText("no data");
            } else {
                gallery_empty.setVisibility(View.GONE);
                mAdapter.notifyDataSetChanged();
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gallery);
        if (!applypermission()) {
            finish();
            return;
        }
        getData();
        mRecyclerView = (RecyclerView) findViewById(R.id.gallery_grid);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, GALLERY_COLUM));
        mAdapter = new ThreadShowAdapter(this, mData, new ThreadShowAdapter.OnPositionCallback() {

            @Override
            public void onPosition(int position) {
                gallery_count.setText(position + mData.get(position).getPath());
            }
        });
        mRecyclerView.setAdapter(mAdapter);
        gallery_count = (TextView) findViewById(R.id.gallery_count);
        gallery_empty = (TextView) findViewById(R.id.gallery_empty);
    }

    private void getData() {
        new Thread() {
            @Override
            public void run() {
                super.run();
                String path = getIntent().getStringExtra("path");
                mData.addAll(getPicBeans(getGallery(new File(path))));
                if (mData.size() == 0) {
                    mHandler.sendEmptyMessage(0);
                } else {
                    mHandler.sendEmptyMessage(1);
                }
            }
        }.start();
    }

    private List<PicBean> getPicBeans(List<String> paths) {
        List<PicBean> beans = new ArrayList<>();
        for (String path : paths) {
            if (isCorrect(path)) {
                PicBean bean = new PicBean(path);
                beans.add(bean);
            }
        }
        return beans;
    }

    private boolean isCorrect(String path) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        int h = options.outHeight;
        int w = options.outWidth;
        return true;
    }

}

适配器:
a.这里因为涉及到图片的列表展示,所以用缓存来处理;
b.在子线程中加载图片,因为图片很多所以为了避免不必要的线程开销,使用了线程池,因为在实际展示的时候,我自己的手机大概看到了7000张图片的时候,内存溢出了。
c.用到了BitmapFactory.Options,因为我们这一步是列表展示,所以要对图片的大小进行处理,所以先利用这个options加载出bitmap的宽高,然后再加载图片具体的内容


import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadShowAdapter extends RecyclerView.Adapter<ThreadShowAdapter.ViewHolder> {
    private final int mScreenWidth;
    private final ExecutorService mExecutorService;
    private Thread mWorkThread;
    private Context mContext;
    private List<PicBean> mData;
    private int mLastPosition;
    private LruCache<String, Bitmap> mMemoryCache;

    public ThreadShowAdapter(Context context, List<PicBean> data, OnPositionCallback callback) {
        mOnPositionCallback = callback;
        mContext = context;
        mData = data;
        long maxMemory = Runtime.getRuntime().maxMemory();
        mMemoryCache = new LruCache<String, Bitmap>((int) maxMemory) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
        DisplayMetrics dm2 = context.getResources().getDisplayMetrics();
        mScreenWidth = dm2.widthPixels;
        mExecutorService = Executors.newFixedThreadPool(5);

    }

    public List<PicBean> getData() {
        return mData;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.gallery_item, null);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        if (mOnPositionCallback != null) {
            mOnPositionCallback.onPosition(position);
        }
        PicBean bean = mData.get(position);
        getBitmap(holder.iv, bean.getPath());
        holder.position.setText(position + "");
        holder.iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(mContext, GalleryDetailsActivity.class);
                intent.putExtra("path", mData.get(position).getPath());
                mContext.startActivity(intent);
            }
        });
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }

    private void getBitmap(final HeightImageView iv, final String path) {
        Bitmap b = mMemoryCache.get(path);
        if (b != null) {
            iv.setBitmap(b);
            return;
        }
        mWorkThread = new Thread() {
            @Override
            public void run() {
                super.run();
                Bitmap bitmap = getBitmapReal(path);
                iv.setBitmap(bitmap);
                mMemoryCache.put(path, bitmap);
            }
        };
        mExecutorService.submit(mWorkThread);
    }

    private Bitmap getBitmapReal(String path) {
        Bitmap bitmap = null;
        if (bitmap != null) {
            return bitmap;
        }
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        bitmap = BitmapFactory.decodeFile(path, options);

        options.inJustDecodeBounds = false;
        options.inSampleSize = getScale(options.outWidth, options.outHeight);
        bitmap = BitmapFactory.decodeFile(path, options);
        return bitmap;
    }

    private int getScale(int w, int h) {
        int base = mScreenWidth / ThreadShowActivity.GALLERY_COLUM;
        int wSize = w / base;
        int hSize = h / base;

        return Math.min(wSize, hSize);
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        HeightImageView iv;
        TextView position;

        public ViewHolder(View itemView) {
            super(itemView);
            iv = itemView.findViewById(R.id.gallery_item_iv);
            position = itemView.findViewById(R.id.gallery_item_position);
        }

    }

    public interface OnPositionCallback {

        void onPosition(int position);

    }

    private OnPositionCallback mOnPositionCallback;
}

4.某一图片的单独详情展示:
简易Gallery
这里没有做很多处理,仅仅是大概的一个展示,放大缩小做的都不够好,还有如果有很大的图片,可能会报错


import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;

public class GalleryDetailsActivity extends Activity {

    public static final int GALLERY_COLUM = 2;
    private MyZoomImageView gallery_details;
    private TextView details_info;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gallery_details);
        gallery_details = findViewById(R.id.gallery_details);
        details_info = findViewById(R.id.details_info);
        String path = getIntent().getStringExtra("path");
        if (TextUtils.isEmpty(path)) {
            finish();
            return;
        }
        Bitmap bitmap = getBitmapReal(path);
        if (bitmap == null) {
            finish();
            return;
        }
        gallery_details.setImageBitmap(bitmap);
    }

    private Bitmap getBitmapReal(String path) {
        Bitmap bitmap = null;
        if (bitmap != null) {
            return bitmap;
        }
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        bitmap = BitmapFactory.decodeFile(path, options);

        options.inJustDecodeBounds = false;
        options.inSampleSize = getScale(options.outWidth, options.outHeight);
        bitmap = BitmapFactory.decodeFile(path, options);
        return bitmap;
    }

    private int getScale(int w, int h) {
        DisplayMetrics dm2 = getResources().getDisplayMetrics();
        int base = dm2.widthPixels;
        int wSize = w / base;
        int hSize = h / base;
        details_info.setText(w + "," + h + "," + base);
        int scale = 1;
        if (h / w > 10 || w / h > 10) {
            scale = 2;
        }
        return Math.min(wSize, hSize) * scale;
    }

}

最后吐槽一下,我自己手机里面共有17000+的图片,其中相册能看到的大概1000+,其余的:
酷狗音乐 1000,
Android 7000+,
腾讯 4700+.

完整的项目我放到github上了,有兴趣可以下载下来看看,https://github.com/sunhaolyg/Gallery