第一步:先定义两个Activity,一个获取数据并用来展示,一个展示媒体库图片并提供选择
MainActivity
PackageImageActivity
第二步: Android6.0以后的动态申请权限。
private void checkReadPermission() {
int readPermission = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
int writePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (readPermission != PackageManager.PERMISSION_GRANTED && writePermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, READ_PERMISSION_CORD);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == READ_PERMISSION_CORD) {
if (grantResults.length == 2 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "拥有权限");
}
}
}
第三步 从MainActivity 跳转到 PackageImageActivity
public void pickImages(View view) {
Intent intent = new Intent(MainActivity.this, PackageImage.class);
startActivity(intent);
}
4. 第四步 使用LoaderManager 异步获取数据
private void initLoaderManager() {
imageItemList.clear();
LoaderManager loaderManager = LoaderManager.getInstance(this);
loaderManager.initLoader(LOADER_ID, null, new LoaderManager.LoaderCallbacks() {
/**
* 因为图库图片可能较多,不能在主线程进行耗时操作
* 在子线程进行操作
* @param id
* @param args
* @return 一个CurSor 装载
*/
@NonNull
@Override
public Loader onCreateLoader(int id, @Nullable Bundle args) {
if (id == LOADER_ID) {
return new CursorLoader(PackageImage.this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media._ID}, null, null, MediaStore.Images.Media.DATE_ADDED+" DESC");
}
return null;
}
/**
* 装载完成后调用
* @param loader 一个对象
* @param cursor 光标,包含所有图片信息。
*/
@Override
public void onLoadFinished(@NonNull Loader loader, Cursor cursor) {
if (cursor != null) {
while (cursor.moveToNext()) {
String id = cursor.getString(0);
// **因为Android 10.0 以后进行分区存储。我们即使申请权限,也不能访问SD卡的内容。
// 因此如果这里获取path 后面使用Glide 加载图片会报FileNoFOUND错误。所以我们通过ID获取Uri**
Uri uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,id);
imageItemList.add(uri);
adapter.setData(imageItemList);
}
}
}
/**
* 重新装载是调用
* @param loader
*/
@Override
public void onLoaderReset(@NonNull Loader loader) {
}
});
}
第五步 使用recyclerView 将获取的图片信息,展现出来
@NonNull
@Override
public InnerHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false);
//使用该方法,获取屏幕长度,将每一个图片尺寸设置x/3
Point point = SizeUtils.getScreenSize(view.getContext());
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(point.x / 3, point.y / 3);
view.setLayoutParams(layoutParams);
return new InnerHolder(view);
}
第六步 在Adapter 里面暴露一个接口,用于将数据传出来
public void setOnItemSelectedNum(onItemSelectedNum onItemSelectedNum){
this.mOnItemSelectedNum = onItemSelectedNum;
}
public interface onItemSelectedNum{
void onItemSelectedChange(List list);
}
第七步 设计事件,用于表示选中图片
@Override
public void onBindViewHolder(@NonNull InnerHolder holder, int position) {
View view = holder.itemView;
ImageView imageView = view.findViewById(R.id.iv);
final View visibility = view.findViewById(R.id.visibility);
final CheckBox select = view.findViewById(R.id.select);
final Uri uri = mimageItems.get(position);
Glide.with(imageView.getContext()).load(uri).into(imageView);
// 先根据状态来渲染,防止紊乱
if (mselect.contains(uri)) {
select.setButtonDrawable(R.drawable.image1);
visibility.setVisibility(View.VISIBLE);
} else {
select.setButtonDrawable(R.drawable.image2);
visibility.setVisibility(View.INVISIBLE);
}
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 如果已经没被选择,添加
if (!mselect.contains(uri)) {
if(mselect.size()>=9){
Toast.makeText(select.getContext(),"最多选择"+MAX_SELECTED_PIC+"张图片",Toast.LENGTH_SHORT).show();
}else{
mselect.add(uri);
select.setButtonDrawable(R.drawable.image1);
visibility.setVisibility(View.VISIBLE);
}
} else {
// 如果已经被选择,删除,更改可见度
mselect.remove(uri);
select.setButtonDrawable(R.drawable.image2);
visibility.setVisibility(View.INVISIBLE);
}
mOnItemSelectedNum.onItemSelectedChange(mselect);
}
});
}
第八步 选择完成,将图片传回。需要使前后数据一致。我们使用一个PickerConfig(单例模式) 来实现。 在两个Activity都初始化它
public class PickerConfig {
//最大选择图片数量
private int max_selected_pic = 1;
private OnItemSelectedFinished monItemSelectedFinished = null;
public OnItemSelectedFinished getMonItemSelectedFinished() {
return monItemSelectedFinished;
}
private PickerConfig(){}
private static PickerConfig pickerConfig;
public static PickerConfig getInstance(){
if(pickerConfig==null){
pickerConfig =new PickerConfig();
}
return pickerConfig;
}
public void setOnItemSelectedFinished(OnItemSelectedFinished onItemSelectedFinished){
this.monItemSelectedFinished = onItemSelectedFinished;
}
public interface OnItemSelectedFinished{
void onItemSelectedFinished(List list);
}
public int getMax_selected_pic() {
return max_selected_pic;
}
public void setMax_selected_pic(int max_selected_pic) {
this.max_selected_pic = max_selected_pic;
}
}
第九步, 两个Activity初始化上面方法。 选择完成 ,数据返回
public void finishedPic(View view){
//需要获取数据
List list = adapter.getMselect();
//把数据通知给其他地方
PickerConfig.OnItemSelectedFinished monItemSelectedFinished = mpickerConfig.getMonItemSelectedFinished();
if(monItemSelectedFinished!=null){
monItemSelectedFinished.onItemSelectedFinished(list);
}
//结束
finish();
}
第十步,将获取的数据在MainActivity 使用recyclView实现。
可以如果返回图片少于3个,就按几列展示。大于三列的三列显示
private void initConfig() {
PickerConfig pickerConfig = PickerConfig.getInstance();
pickerConfig.setMax_selected_pic(MAX_SELECTED_PIC);
pickerConfig.setOnItemSelectedFinished(new PickerConfig.OnItemSelectedFinished() {
@Override
public void onItemSelectedFinished(List list) {
int horNum =1;
if(list.size()<3){
horNum =list.size();
}else{
horNum =3;
}
GridLayoutManager gridLayoutManager =new GridLayoutManager(MainActivity.this,horNum);
recyclerView.setLayoutManager(gridLayoutManager);
mainAdapter.setData(list,horNum);
}
});
}
1. Android10.0 为了保存隐私,不允许访问SD卡的等内容。我们需要使用
MediaStore.Images.Media 得到uri 不能使用path路径
2. Glide4.x 以上版本需要
implementation ‘com.github.bumptech.glide:glide:4.11.0’
annotationProcessor ‘com.github.bumptech.glide:compiler:4.11.0’
并且创建
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
}
4. 如果没有报错,但图片没有加载出来。可能是因为适配器的设置长宽参数不合适