2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > Android 九宫格布局(图片上传 预览)

Android 九宫格布局(图片上传 预览)

时间:2022-10-27 06:37:29

相关推荐

Android 九宫格布局(图片上传 预览)

前言:

利用RecyclerView展示朋友圈UI布局,包含展示、预览、删除等功能

效果图

1、在项目app\build.gradle添加依赖

//图片加载implementation 'com.github.bumptech.glide:glide:4.8.0'//初始化控件找IDimplementation 'com.jakewharton:butterknife:10.2.1'annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'//图片选择器implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.5.8'//权限请求implementation 'com.hjq:xxpermissions:6.5'

build.gradle主项目中添加:maven { url ‘https://jitpack.io’ }

allprojects {repositories {google()jcenter()maven { url 'https://jitpack.io' }}}

具体使用:

①、MainActivity:

public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";@BindView(R.id.edit)EditText edit;@BindView(R.id.recycler)RecyclerView recycler;private SelectPlotAdapter adapter;private ArrayList<String> allSelectList;//所有的图片集合private ArrayList<String> categoryLists;//查看图片集合private List<LocalMedia> selectList = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);if (null == allSelectList) {allSelectList = new ArrayList();}if (null == categoryLists) {categoryLists = new ArrayList();}Tools.requestPermissions(MainActivity.this);initAdapter();}private void initAdapter() {//最多9张图片adapter = new SelectPlotAdapter(this, 9);recycler.setLayoutManager(new GridLayoutManager(this, 3));adapter.setImageList(allSelectList);recycler.setAdapter(adapter);adapter.setListener(new SelectPlotAdapter.CallbackListener() {@Overridepublic void add() {//可添加的最大张数=9-当前已选的张数int size = 9 - allSelectList.size();Tools.galleryPictures(MainActivity.this, size);}@Overridepublic void delete(int position) {allSelectList.remove(position);categoryLists.remove(position);adapter.setImageList(allSelectList);//再set所有集合}@Overridepublic void item(int position, List<String> mList) {selectList.clear();for (int i = 0; i < allSelectList.size(); i++) {LocalMedia localMedia = new LocalMedia();localMedia.setPath(allSelectList.get(i));selectList.add(localMedia);}//①、图片选择器自带预览PictureSelector.create(MainActivity.this).themeStyle(R.style.picture_default_style).isNotPreviewDownload(true)//是否显示保存弹框.imageEngine(GlideEngine.createGlideEngine()) // 选择器展示不出图片则添加.openExternalPreview(position, selectList);//②:自定义布局预览//Tools.startPhotoViewActivity(MainActivity.this, categoryLists, position);}});}@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == RESULT_OK) {// 结果回调List<LocalMedia> selectList = PictureSelector.obtainMultipleResult(data);showSelectPic(selectList);}}private void showSelectPic(List<LocalMedia> result) {for (int i = 0; i < result.size(); i++) {String path;//判断是否10.0以上if (Build.VERSION.SDK_INT >= 29) {path = result.get(i).getAndroidQToPath();} else {path = result.get(i).getPath();}allSelectList.add(path);categoryLists.add(path);Log.e(TAG, "图片链接: " + path);}adapter.setImageList(allSelectList);}@OnClick({R.id.upload})public void onClick(View view) {switch (view.getId()) {case R.id.upload:String content = edit.getText().toString();if (TextUtils.isEmpty(content)) {Toast.makeText(this, "请输入上传内容", Toast.LENGTH_LONG).show();return;}if (allSelectList.size() == 0) {Toast.makeText(this, "请选择图片进行上传", Toast.LENGTH_LONG).show();return;}Log.e(TAG, "内容: " + content);Log.e(TAG, "图片: " + allSelectList.toString());break;}}}

②、SelectPlotAdapter 展示适配器

public class SelectPlotAdapter extends RecyclerView.Adapter<SelectPlotAdapter.SelectHolder> {private Context mContext;private List<String> mediaDtoList;//最大图片张数private int picMax;public void setImageList(List<String> mList) {this.mediaDtoList = mList;notifyDataSetChanged();}public SelectPlotAdapter(Context mContext, int max) {this.mContext = mContext;this.picMax = max;}@Overridepublic SelectHolder onCreateViewHolder(ViewGroup parent, int viewType) {return new SelectHolder(LayoutInflater.from(mContext).inflate(R.layout.item_gallery, parent, false));}@Overridepublic void onBindViewHolder(SelectHolder holder, final int position) {//当前位置大于图片数量并且小于最大减1if (position >= mediaDtoList.size() && position <= (picMax - 1)) {//显示添加图片按钮、并隐藏删除按钮Tools.showGlide(mContext,holder.gallery,"");holder.delete.setVisibility(View.GONE);} else {//显示本地或网络图片,并显示删除按钮Tools.showGlide(mContext,holder.gallery,mediaDtoList.get(position));holder.delete.setVisibility(View.VISIBLE);}//按钮删除事件holder.delete.setOnClickListener(v -> {//传入position删除第几张listener.delete(position);});holder.gallery.setOnClickListener(v -> {//添加新图片点击事件(回调activity)if (position >= mediaDtoList.size() && position <= (picMax - 1)) {listener.add();} else {//点击查看图片事件,并将最新list传入actiuvitylistener.item(position, mediaDtoList);}});}@Overridepublic int getItemCount() {if (mediaDtoList == null || mediaDtoList.size() == 0) {return 1;} else {return this.mediaDtoList.size() >= picMax ? picMax : this.mediaDtoList.size() + 1;}}public class SelectHolder extends RecyclerView.ViewHolder {private ImageView gallery;private ImageView delete;public SelectHolder(View itemView) {super(itemView);gallery = itemView.findViewById(R.id.im_show_gallery);delete = itemView.findViewById(R.id.iv_del);}}private CallbackListener listener;public void setListener(CallbackListener listener) {this.listener = listener;}public interface CallbackListener {//图片添加事件void add();//删除第几张图片void delete(int position);//图片点击void item(int position, List<String> mList);}}

由于Butterknife需使用Java 8故需在依赖处添加

// Butterknife requires Java pileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}

③、自定义图片预览MyImageAdapter(需要则添加)

public class MyImageAdapter extends PagerAdapter {private List<String> imageUrls;private Activity mContext;public MyImageAdapter(Activity context, List<String> imageUrls) {this.imageUrls = imageUrls;this.mContext = context;}@Overridepublic Object instantiateItem(ViewGroup container, int position) {String url = imageUrls.get(position);PhotoView photoView = new PhotoView(mContext);Tools.showGlide(mContext, photoView, url);container.addView(photoView);photoView.setOnClickListener(v -> mContext.finish());return photoView;}@Overridepublic int getCount() {return imageUrls != null ? imageUrls.size() : 0;}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView((View) object);}@Overridepublic int getItemPosition(Object object) {return POSITION_NONE;}}

④、工具类Tools:

public class Tools {/*** 请求权限*/public static void requestPermissions(final AppCompatActivity activity) {XXPermissions.with(activity)// 可设置被拒绝后继续申请,直到用户授权或者永久拒绝//.constantRequest()// 支持请求6.0悬浮窗权限8.0请求安装权限//.permission(Permission.SYSTEM_ALERT_WINDOW, Permission.REQUEST_INSTALL_PACKAGES)// 不指定权限则自动获取清单中的危险权限.permission(Permission.Group.STORAGE).permission(Permission.CAMERA).request(new OnPermission() {@Overridepublic void hasPermission(List<String> granted, boolean all) {}@Overridepublic void noPermission(List<String> denied, boolean quick) {}});}/*** 打开图库*/public static void openGallery(AppCompatActivity activity, int maxSize) {PictureSelector.create(activity).openGallery(PictureMimeType.ofImage())//全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()//.theme()//主题样式(不设置为默认样式) 也可参考demo values/styles下 例如:R.style.picture.white.style.maxSelectNum(maxSize)// 最大图片选择数量 int.minSelectNum(1)// 最小选择数量 int.imageSpanCount(3)// 每行显示个数 int.imageEngine(GlideEngine.createGlideEngine())//.selectionMode()// 多选 or 单选 PictureConfig.MULTIPLE or PictureConfig.SINGLE//.isPreviewImage(true)// 是否可预览图片 true or false//.isPreviewVideo()// 是否可预览视频 true or false//.freeStyleCropEnabled() // 是否可播放音频 true or false.isCamera(false)// 是否显示拍照按钮 true or false//.imageFormat(PictureMimeType.PNG)// 拍照保存图片格式后缀,默认jpeg.isZoomAnim(true)// 图片列表点击 缩放效果 默认true//.setOutputCameraPath("/CustomPath")// 自定义拍照保存路径,可不填.isEnableCrop(true)// 是否裁剪 true or false//.isCompress(true)// 是否压缩 true or false//.withAspectRatio()// int 裁剪比例 如16:9 3:2 3:4 1:1 可自定义//.hideBottomControls()// 是否显示uCrop工具栏,默认不显示 true or false//.isGif(false)// 是否显示gif图片 true or false//.compressSavePath(getPath())//压缩图片保存地址.freeStyleCropEnabled(true)// 裁剪框是否可拖拽 true or false//.circleDimmedLayer()// 是否圆形裁剪 true or false//.showCropFrame()// 是否显示裁剪矩形边框 圆形裁剪时建议设为false true or false//.showCropGrid()// 是否显示裁剪矩形网格 圆形裁剪时建议设为false true or false//.isOpenClickSound(false)// 是否开启点击声音 true or false//.selectionData()// 是否传入已选图片 List<LocalMedia> list//.isPreviewEggs()// 预览图片时 是否增强左右滑动图片体验(图片滑动一半即可看到上一张是否选中) true or false//.cutOutQuality(90)// 裁剪压缩质量 默认90 int.minimumCompressSize(100)// 小于100kb的图片不压缩//.synOrAsy(true)//同步true或异步false 压缩 默认同步//.cropImageWideHigh()// 裁剪宽高比,设置如果大于图片本身宽高则无效 int//.rotateEnabled(true) // 裁剪是否可旋转图片 true or false.scaleEnabled(true)// 裁剪是否可放大缩小图片 true or false//.videoQuality()// 视频录制质量 0 or 1 int//.videoMaxSecond(15)// 显示多少秒以内的视频or音频也可适用 int//.videoMinSecond(10)// 显示多少秒以内的视频or音频也可适用 int//.recordVideoSecond()//视频秒数录制 默认60s int.forResult(PictureConfig.CHOOSE_REQUEST);//结果回调onActivityResult code}/*** 打开拍照*/public static void takingPictures(AppCompatActivity activity) {PictureSelector.create(activity).openCamera(PictureMimeType.ofImage()).forResult(PictureConfig.REQUEST_CAMERA);}/*** 打开图库+拍照按钮*/public static void galleryPictures(AppCompatActivity activity, int maxSize) {PictureSelector.create(activity).openGallery(PictureMimeType.ofImage())//全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()//.theme()//主题样式(不设置为默认样式) 也可参考demo values/styles下 例如:R.style.picture.white.style.maxSelectNum(maxSize)// 最大图片选择数量 int.minSelectNum(1)// 最小选择数量 int.imageEngine(GlideEngine.createGlideEngine()).imageSpanCount(3)// 每行显示个数 int.isCamera(true)// 是否显示拍照按钮 true or false.isZoomAnim(true)// 图片列表点击 缩放效果 默认true//.isEnableCrop(true)// 是否裁剪 true or false.isCompress(true)// 是否压缩 true or false.minimumCompressSize(100)// 小于100kb的图片不压缩.forResult(PictureConfig.CHOOSE_REQUEST);//结果回调onActivityResult code}/*** 加载圆角图片** @param context 上下文* @param view 图片控件* @param url网络图片地址*/public static void showGlide(Context context, ImageView view, String url) {RequestOptions options = new RequestOptions().error(R.mipmap.ic_add).transform(new GlideRoundTransform(context,5));Glide.with(context).load(url).apply(options).into(view);}/*** 跳转到查看图片界面* 上下文、list、点击的位置*/public static void startPhotoViewActivity(Activity context, ArrayList<String> mList, int position) {Intent intent = new Intent(context, PhotoViewActivity.class);Bundle bundle = new Bundle();bundle.putStringArrayList("list", mList);intent.putExtras(bundle);intent.putExtra("position", position);context.startActivity(intent);}}

⑤、自定义PhotoViewActivity(需要则添加):

public class PhotoViewActivity extends AppCompatActivity {@BindView(R.id.viewpager)ViewPager viewpager;@BindView(R.id.mTvImageCount)TextView mTvImageCount;//点击的下标private int currentPosition;private List<String> urlLists;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_photo_view);ButterKnife.bind(this);initData();}private void initData() {if (null == urlLists) {urlLists = new ArrayList<>();}//获得点击的位置currentPosition = getIntent().getIntExtra("position", 0);//图片集合urlLists = getIntent().getStringArrayListExtra("list");MyImageAdapter adapter = new MyImageAdapter(this, urlLists);viewpager.setAdapter(adapter);viewpager.setCurrentItem(currentPosition);mTvImageCount.setText(currentPosition + 1 + "/" + urlLists.size());viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Overridepublic void onPageSelected(int position) {mTvImageCount.setText(position + 1 + "/" + urlLists.size());}@Overridepublic void onPageScrollStateChanged(int state) {}});}}

⑥:布局相关文件:

1、activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><EditTextandroid:id="@+id/edit"android:layout_width="match_parent"android:layout_height="200dp"android:gravity="top"android:hint="请输入上传内容"android:padding="15dp" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:layout_margin="15dp" /><Buttonandroid:id="@+id/upload"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="20dp"android:text="提交" /></LinearLayout>

2、item_gallery.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"android:layout_width="100dp"android:layout_height="100dp"android:layout_marginBottom="5dp"android:paddingRight="5dp"><ImageViewandroid:id="@+id/im_show_gallery"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop" /><ImageViewandroid:id="@+id/iv_del"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentEnd="true"android:paddingBottom="5dp"android:paddingStart="5dp"android:src="@drawable/picture_icon_delete_photo" /></RelativeLayout>

picture_icon_delete_photo为自带删除按钮,如需更换自行修改

3、activity_photo_view.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000000"tools:context=".PhotoViewActivity"><androidx.viewpager.widget.ViewPagerandroid:layout_centerInParent="true"android:id="@+id/viewpager"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="30dp"android:layout_marginBottom="30dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="#ffffff"android:textSize="16sp"android:id="@+id/mTvImageCount"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_marginBottom="5dp"android:text="1/1" /></RelativeLayout>

⑦、app\src\main\AndroidManifest.xml添加相应权限

<uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

扩展相关

1、图片圆角GlideRoundTransform:

public class GlideRoundTransform extends BitmapTransformation {private static float radius = 0f;public GlideRoundTransform(Context context) {this(context, 4);}/*** 圆角大小* @param context* @param dp*/public GlideRoundTransform(Context context, int dp) {//super(context);this.radius = Resources.getSystem().getDisplayMetrics().density * dp;}@Overrideprotected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {Bitmap bitmap = TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight);return roundCrop(pool, bitmap);}private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {if (source == null) return null;Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);if (result == null) {result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);}Canvas canvas = new Canvas(result);Paint paint = new Paint();paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));paint.setAntiAlias(true);RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());canvas.drawRoundRect(rectF, radius, radius, paint);return result;}public String getId() {return getClass().getName() + Math.round(radius);}@Overridepublic void updateDiskCacheKey(MessageDigest messageDigest) {}}

2、图片选择器加载图片失败GlideEngine:

public class GlideEngine implements ImageEngine {/*** 加载图片** @param context* @param url* @param imageView*/@Overridepublic void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {Glide.with(context).load(url).into(imageView);}/*** 加载网络图片适配长图方案* # 注意:此方法只有加载网络图片才会回调** @param context* @param url* @param imageView* @param longImageView* @param callback网络图片加载回调监听 {link after version 2.5.1 Please use the #OnImageCompleteCallback#}*/@Overridepublic void loadImage(@NonNull Context context, @NonNull String url,@NonNull ImageView imageView,SubsamplingScaleImageView longImageView, OnImageCompleteCallback callback) {Glide.with(context).asBitmap().load(url).into(new ImageViewTarget<Bitmap>(imageView) {@Overridepublic void onLoadStarted(@Nullable Drawable placeholder) {super.onLoadStarted(placeholder);if (callback != null) {callback.onShowLoading();}}@Overridepublic void onLoadFailed(@Nullable Drawable errorDrawable) {super.onLoadFailed(errorDrawable);if (callback != null) {callback.onHideLoading();}}@Overrideprotected void setResource(@Nullable Bitmap resource) {if (callback != null) {callback.onHideLoading();}if (resource != null) {boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),resource.getHeight());longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);if (eqLongImage) {// 加载长图longImageView.setQuickScaleEnabled(true);longImageView.setZoomEnabled(true);longImageView.setPanEnabled(true);longImageView.setDoubleTapZoomDuration(100);longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);longImageView.setImage(ImageSource.bitmap(resource),new ImageViewState(0, new PointF(0, 0), 0));} else {// 普通图片imageView.setImageBitmap(resource);}}}});}/*** 加载网络图片适配长图方案* # 注意:此方法只有加载网络图片才会回调** @param context* @param url* @param imageView* @param longImageView* @ 已废弃*/@Overridepublic void loadImage(@NonNull Context context, @NonNull String url,@NonNull ImageView imageView,SubsamplingScaleImageView longImageView) {Glide.with(context).asBitmap().load(url).into(new ImageViewTarget<Bitmap>(imageView) {@Overrideprotected void setResource(@Nullable Bitmap resource) {if (resource != null) {boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),resource.getHeight());longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);if (eqLongImage) {// 加载长图longImageView.setQuickScaleEnabled(true);longImageView.setZoomEnabled(true);longImageView.setPanEnabled(true);longImageView.setDoubleTapZoomDuration(100);longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);longImageView.setImage(ImageSource.bitmap(resource),new ImageViewState(0, new PointF(0, 0), 0));} else {// 普通图片imageView.setImageBitmap(resource);}}}});}/*** 加载相册目录** @param context 上下文* @param url 图片路径* @param imageView 承载图片ImageView*/@Overridepublic void loadFolderImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {Glide.with(context).asBitmap().load(url).apply(new RequestOptions().placeholder(R.drawable.picture_image_placeholder)).into(new BitmapImageViewTarget(imageView) {@Overrideprotected void setResource(Bitmap resource) {RoundedBitmapDrawable circularBitmapDrawable =RoundedBitmapDrawableFactory.create(context.getResources(), resource);circularBitmapDrawable.setCornerRadius(8);imageView.setImageDrawable(circularBitmapDrawable);}});}/*** 加载gif** @param context 上下文* @param url 图片路径* @param imageView 承载图片ImageView*/@Overridepublic void loadAsGifImage(@NonNull Context context, @NonNull String url,@NonNull ImageView imageView) {Glide.with(context).asGif().load(url).into(imageView);}/*** 加载图片列表图片** @param context 上下文* @param url 图片路径* @param imageView 承载图片ImageView*/@Overridepublic void loadGridImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {Glide.with(context).load(url).apply(new RequestOptions().placeholder(R.drawable.picture_image_placeholder)).into(imageView);}private GlideEngine() {}private static GlideEngine instance;public static GlideEngine createGlideEngine() {if (null == instance) {synchronized (GlideEngine.class) {if (null == instance) {instance = new GlideEngine();}}}return instance;}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。