仿朋友圈相册图片选择以及画廊效果

  • 1.效果展示
  • 2.导入相关第三方库依赖
  • 3.编写选择图片页面
    • a.编写布局
    • b.编写Activity
    • c.相册选择工具类部分代码
    • d.相册4宫图适配器
  • 4.编写画廊页面
    • a.编写画廊页面
    • b.编写Activity
    • c.画廊适配器
  • 5.新增拖拽效果,高度模仿微信朋友圈
    • a.增加拖拽处理类RecycleItemTouchHelper
    • b.在MainAcitivity里面绑定itemTouchHelper方法
  • 6.源码

1.效果展示

该demo适配Android 6、7、10。画廊效果,支持缩放效果。
视频展示:

安卓实现仿微信朋友圈以及画廊效果

部分截图:

文章有点长,如果没时间就拉到最底下下载源码,再给个一键三联哈(* ̄︶ ̄)

2.导入相关第三方库依赖

站在巨人的肩膀上,敲代码便可事半功倍。

    //图片加载框架implementation 'com.github.bumptech.glide:glide:4.11.0'annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'//黄油刀implementation 'com.jakewharton:butterknife:10.2.3'annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'// 图片选择器api 'com.zhihu.android:matisse:0.5.3-beta3'//动态权限申请implementation 'com.yanzhenjie:permission:2.0.3'//rvimplementation 'androidx.recyclerview:recyclerview:1.0.0'//rv第三方万能适配器implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'//顶部标题栏implementation 'com.wuhenzhizao:titlebar:1.0.7'//图片缩放框架implementation 'com.github.chrisbanes:PhotoView:2.3.0'

3.编写选择图片页面

a.编写布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.wuhenzhizao.titlebar.widget.CommonTitleBarandroid:id="@+id/title"style="@style/StyleTitle"app:centerText="@string/wechat_zone"app:leftType="none"app:showBottomLine="true" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="@mipmap/home_bg_float"android:orientation="vertical"><EditTextandroid:id="@+id/et_content"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@null"android:focusable="true"android:gravity="top"android:hint="@string/hint_text"android:lineSpacingExtra="9dp"android:lineSpacingMultiplier="1.2"android:maxLength="400"android:minHeight="200dp"android:paddingLeft="15dp"android:paddingTop="20dp"android:paddingRight="15dp"android:paddingBottom="20dp"android:textColor="@color/text_primary"android:textColorHint="@color/dialog_cancel_text_color"android:textSize="13sp" /><TextViewandroid:id="@+id/tv_count"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="right"android:layout_marginRight="15dp"android:layout_marginBottom="5dp"android:text="0/400"android:textColor="@color/dialog_cancel_text_color"android:textSize="14sp" /><View style="@style/StyleLine" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_photo"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="@dimen/dp_10" /></LinearLayout><Buttonandroid:id="@+id/btn_submit"android:layout_width="match_parent"android:layout_height="60dp"android:layout_margin="@dimen/dp_10"android:background="@mipmap/home_bg_float"android:text="@string/submit" />
</LinearLayout>

b.编写Activity

public class MainActivity extends AppCompatActivity implements OnItemClickListener, OnItemChildClickListener, TextWatcher {@BindView(R.id.rv_photo)RecyclerView mRvPhoto;@BindView(R.id.activity_main)LinearLayout mActivityMain;@BindView(R.id.title)CommonTitleBar mTitle;@BindView(R.id.et_content)EditText mEtContent;@BindView(R.id.tv_count)TextView mTvCount;@BindView(R.id.btn_submit)Button mBtnSubmit;private PhotoAdapter mPhotoAdapter;//点击item的时候通过判断是否有照片文件list,有值跳画廊ac,没值弹框private List<PhotoVo> mPhotoList = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);initViews();}private void initViews() {mPhotoAdapter = new PhotoAdapter();GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);mRvPhoto.setLayoutManager(gridLayoutManager);mPhotoAdapter.setList(dealWithList(mPhotoList));mRvPhoto.setAdapter(mPhotoAdapter);mPhotoAdapter.setOnItemClickListener(this);mPhotoAdapter.setOnItemChildClickListener(this);mEtContent.addTextChangedListener(this);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == Activity.RESULT_OK) {switch (requestCode) {case CameraAlbumUtil.REQUEST_CODE_TAKE_PHOTO://相机回调String contentPath = CameraAlbumUtil.getInstance().getCameraPath();mPhotoList.add(new PhotoVo(contentPath, new File(contentPath)));mPhotoAdapter.setList(dealWithList(mPhotoList));break;case CameraAlbumUtil.REQUEST_CODE_ALBUM://相册回调List<String> pathList;if (data != null) {pathList = Matisse.obtainPathResult(data);if (pathList.size() > 0) {for (int i = 0; i < pathList.size(); i++) {String s = pathList.get(i);Uri uri = FileUtil.getImageContentUri(this, pathList.get(i));mPhotoList.add(new PhotoVo(s, FileUtil.changeFile(this, uri)));}mPhotoAdapter.setList(dealWithList(mPhotoList));}}break;}}}private List<PhotoVo> dealWithList(List<PhotoVo> list) {List<PhotoVo> newList = new ArrayList<>();if (list != null) newList.addAll(list);if (newList.size() < 4) newList.add(null);//最多只能4张return newList;}@Overridepublic void onItemClick(@NonNull BaseQuickAdapter<?, ?> adapter, @NonNull View view,int position) {if (adapter.getData().get(position) == null) {CameraAlbumUtil.getInstance().showCameraAlbumDialog(this);} else {Intent intent = new Intent(this, GalleryActivity.class);intent.putExtra("photoList", (Serializable) mPhotoList);startActivity(intent);}}@Overridepublic void onItemChildClick(@NonNull BaseQuickAdapter adapter, @NonNull View view,int position) {if (view.getId() == R.id.iv_delete) {//删除按钮mPhotoList.remove(mPhotoList.get(position));mPhotoAdapter.setList(dealWithList(mPhotoList));}}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}@Overridepublic void afterTextChanged(Editable s) {mTvCount.setText(s.length() + "/400");}}

c.相册选择工具类部分代码

/*** @author LJW* @create 2020/11/16* @Describe 打开相机相册工具类*/
public class CameraAlbumUtil {public static final int REQUEST_CODE_TAKE_PHOTO = 1000;public static final int REQUEST_CODE_ALBUM = 1001;private static final String ALBUM = "ALBUM";private static MediaStoreCompat mediaStoreCompat;private CameraAlbumUtil() {}/**** @param activity*/public void showCameraAlbumDialog(Activity activity) {DialogUtil dialogUtil = new DialogUtil();dialogUtil.setClickListenerInterface(new DialogUtil.ClickListenerInterface() {@Overridepublic void onAlbumClickListener() {getPermission(activity, ALBUM, REQUEST_CODE_ALBUM);}@Overridepublic void onCameraClickListener() {getPermission(activity, "", REQUEST_CODE_TAKE_PHOTO);}});dialogUtil.showDialog(activity);}/*** @param activity    哪个界面* @param type        打开相册传"ALBUM",其他传""* @param requestCode 请求码*/public void getPermission(Activity activity, String type, int requestCode) {if (!AndPermission.hasPermissions(activity, Permission.CAMERA, Permission.WRITE_EXTERNAL_STORAGE)) {AndPermission.with(activity).runtime().permission(Permission.CAMERA, Permission.WRITE_EXTERNAL_STORAGE).onGranted(permissions -> {switch (type) {case ALBUM:gotoAlbum(activity, requestCode);break;default:gotoCamera(activity, requestCode);break;}}).onDenied(permissions -> {}).start();} else {switch (type) {case ALBUM:gotoAlbum(activity, requestCode);break;default:gotoCamera(activity, requestCode);break;}}}/*** 打开相机** @param activity* @param requestCode 请求码*/public void gotoCamera(Activity activity, int requestCode) {mediaStoreCompat = new MediaStoreCompat(activity);mediaStoreCompat.setCaptureStrategy(new CaptureStrategy(false,"com.example.viewpagegallery.fileProvider","preventpro"));mediaStoreCompat.dispatchCaptureIntent(activity, requestCode);}/*** 打开相册** @param activity* @param requestCode 请求码*/public void gotoAlbum(Activity activity, int requestCode) {Matisse.from(activity).choose(MimeType.ofAll()).countable(true).maxSelectable(4).gridExpectedSize(activity.getResources().getDimensionPixelSize(R.dimen.grid_expected_size)).restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED).thumbnailScale(0.85f).imageEngine(new GlideEngine()).showPreview(false) // Default is `true`.capture(false) //是否提供拍照功能.captureStrategy(new CaptureStrategy(true, "com.example.viewpagegallery.fileProvider"))//存储到哪里.forResult(requestCode);}/*** 获取拍照后的地址,方便上传使用** @return*/public String getCameraPath() {return mediaStoreCompat.getCurrentPhotoPath();}public static class SingleInstance {private static CameraAlbumUtil cameraAlbumSingle = new CameraAlbumUtil();}public static CameraAlbumUtil getInstance() {return SingleInstance.cameraAlbumSingle;}
}

d.相册4宫图适配器

public class PhotoAdapter extends BaseQuickAdapter<PhotoVo, BaseViewHolder> {public PhotoAdapter() {super(R.layout.item_phtoto);}@Overrideprotected void convert(@NotNull BaseViewHolder holder, PhotoVo photoVo) {if (photoVo != null && photoVo.getFile() != null) {//空图CornerTransform transformation = new CornerTransform(getContext(), ScreenUtils.dip2px(getContext(), 10));Glide.with(getContext()).load(photoVo.getFile().getPath()).transform(transformation).into((ImageView) holder.getView(R.id.iv_img));holder.setVisible(R.id.iv_empty, false);holder.setVisible(R.id.iv_delete, true);holder.setVisible(R.id.iv_img, true);} else {//有图holder.setVisible(R.id.iv_empty, true);holder.setGone(R.id.iv_delete, true);holder.setGone(R.id.iv_img, true);}holder.getView(R.id.iv_delete).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {setOnItemChildClick(v, holder.getAdapterPosition());}});}
}

4.编写画廊页面

a.编写画廊页面

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#FFC0CB"android:clipChildren="false"><com.wuhenzhizao.titlebar.widget.CommonTitleBarandroid:id="@+id/title"style="@style/StyleTitle"app:centerText="@string/gallery_title"app:showBottomLine="true" /><com.example.viewpagegallery.MyViewPagerandroid:id="@+id/viewPager"android:layout_width="240dp"android:layout_height="400dp"android:layout_centerInParent="true"android:clipChildren="false" /><TextViewtools:text="1/4"android:id="@+id/tv_num_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_gravity="center_horizontal|bottom"android:layout_marginBottom="30dp"android:textColor="#909090"android:textSize="18sp" />
</RelativeLayout>

b.编写Activity


public class GalleryActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {@BindView(R.id.viewPager)MyViewPager viewPager;@BindView(R.id.tv_num_title)TextView tvNumTitle;@BindView(R.id.title)CommonTitleBar mTitle;private GalleryAdapter mGalleryAdapter;private List<PhotoVo> mPhotoList = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_gallery);ButterKnife.bind(this);initViews();}@SuppressLint("DefaultLocale")private void initViews() {mTitle.setListener((v, action, extra) -> {switch (action) {case CommonTitleBar.ACTION_LEFT_BUTTON:finish();break;case CommonTitleBar.ACTION_RIGHT_TEXT:break;}});mPhotoList = (List<PhotoVo>) getIntent().getSerializableExtra("photoList");mGalleryAdapter = new GalleryAdapter(mPhotoList, this);tvNumTitle.setText(String.format("%d/%d", 1, mPhotoList.size()));viewPager.setAdapter(mGalleryAdapter);viewPager.addOnPageChangeListener(this);viewPager.setPageTransformer(true, new RotationPageTransForm());viewPager.setOffscreenPageLimit(2); //下面会说到}@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Overridepublic void onPageSelected(int position) {// 每当页数发生改变时重新设定一遍当前的页数和总页数tvNumTitle.setText((position + 1) + "/" + mPhotoList.size());}@Overridepublic void onPageScrollStateChanged(int state) {}
}

c.画廊适配器

public class GalleryAdapter extends PagerAdapter {private List<PhotoVo> mPhotos;private Context mContext;public GalleryAdapter(List<PhotoVo> mPhotos, Context mContext) {this.mPhotos = mPhotos;this.mContext = mContext;}@Overridepublic int getCount() {return mPhotos.size();}@Overridepublic boolean isViewFromObject(@NonNull View view, @NonNull Object object) {return view == object;}@NonNull@Overridepublic Object instantiateItem(@NonNull ViewGroup container, int position) {View view = LayoutInflater.from(mContext).inflate(R.layout.item_gallery, container, false);PhotoView photoView = view.findViewById(R.id.photo_view);Glide.with(mContext).load(mPhotos.get(position).getFile().getPath()).into(photoView);container.addView(view);return view;}@Overridepublic void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {container.removeView((View) object);}
}

5.新增拖拽效果,高度模仿微信朋友圈

a.增加拖拽处理类RecycleItemTouchHelper

public class RecycleItemTouchHelper extends ItemTouchHelper.Callback {private static final String TAG = "RecycleItemTouchHelper";private final ItemTouchHelperCallback helperCallback;public RecycleItemTouchHelper(ItemTouchHelperCallback helperCallback) {this.helperCallback = helperCallback;}/*** 设置滑动类型标记** @param recyclerView* @param viewHolder* @return 返回一个整数类型的标识,用于判断Item那种移动行为是允许的*/@Overridepublic int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {Log.e(TAG, "getMovementFlags: ");int drafFlags = 0;int swipeFlags;//START  右向左 END左向右 LEFT  向左 RIGHT向右  UP向上//如果某个值传0,表示不触发该操作,次数设置支持上下拖拽,支持向右滑动if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {drafFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;swipeFlags = 0;}return makeMovementFlags(drafFlags, 0);}/*** Item是否支持长按拖动** @return true  支持长按操作* false 不支持长按操作*/@Overridepublic boolean isLongPressDragEnabled() {return super.isLongPressDragEnabled();}/*** Item是否支持滑动** @return true  支持滑动操作* false 不支持滑动操作*/@Overridepublic boolean isItemViewSwipeEnabled() {return super.isItemViewSwipeEnabled();}/*** 拖拽切换Item的回调** @param recyclerView* @param viewHolder* @param target* @return 如果Item切换了位置,返回true;反之,返回false*/@Overridepublic boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {Log.e(TAG, "onMove: ");helperCallback.onMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());return true;}/*** 滑动Item** @param viewHolder* @param direction  Item滑动的方向*/@Overridepublic void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {Log.e(TAG, "onSwiped: ");helperCallback.onItemDelete(viewHolder.getAdapterPosition());}/*** Item被选中时候回调** @param viewHolder* @param actionState 当前Item的状态*                    ItemTouchHelper.ACTION_STATE_IDLE   闲置状态*                    ItemTouchHelper.ACTION_STATE_SWIPE  滑动中状态*                    ItemTouchHelper#ACTION_STATE_DRAG   拖拽中状态*/@Overridepublic void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {super.onSelectedChanged(viewHolder, actionState);}@Overridepublic void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {super.clearView(recyclerView, viewHolder);}public interface ItemTouchHelperCallback {void onItemDelete(int positon);void onMove(int fromPosition, int toPosition);}
}

然后在PhotoAdapter实现ItemTouchHelperCallback接口
然后重写接口里面的这两个类达到移动后的item刷新效果

 @Overridepublic void onItemDelete(int positon) {getData().remove(positon);notifyItemRemoved(positon);}@Overridepublic void onMove(int fromPosition, int toPosition) {Collections.swap(getData(),fromPosition,toPosition);//交换数据notifyItemMoved(fromPosition,toPosition);}

b.在MainAcitivity里面绑定itemTouchHelper方法

 ItemTouchHelper.Callback callback = new RecycleItemTouchHelper(mPhotoAdapter);ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);itemTouchHelper.attachToRecyclerView(mRvPhoto);

这样既可实现item的拖拽效果啦

6.源码


创作不易,给博主一键三联,可免费领取博主的爱心代码(详情联系QQ:2872960735)(* ̄︶ ̄)
源码下载地址

仿朋友圈相册图片选择以及画廊效果相关推荐

  1. 微信仿朋友圈添加图片

    微信仿朋友圈添加图片 问题: 添加过多图片时,会出现OOM. 如何动态修改图片展示栏的高度. 加号如何伴随图片的增加而后移. 如何保证最多添加照片为9张. 添加过多图片时,会出现OOM 出现第一种情况 ...

  2. Android 仿朋友圈,文字图片视频多条目,自动播放暂停

    因为太长了不能把项目搬过来,记一下关键点 多条目都会,然而这里因为微信朋友圈界面的特殊,他有一个头部,开始我是做的ScrollView+RecyclerView,后来发现在Scrollview 的包裹 ...

  3. 使用南尘的ImagePicker实现仿微信的相册图片选择以及拍照上传

    在记录之前先放上原作的GitHub项目地址:ImagePicker 南尘的框架写得相当不错,我们可以在我们的项目中添加依赖直接使用,但是我在使用的时候发现了一个小bug,就在图片预览的界面选择了图片后 ...

  4. android仿朋友圈教程,android 仿朋友圈动态 图片查看效果

    [实例简介] [实例截图] [核心代码] package com.example.imagedemo; import java.util.ArrayList; import android.os.Bu ...

  5. 从选择到上传,可能是最贴心的高仿朋友圈编辑了

    Luban-Circle-Demo 自己要用的上这个,找轮子的时候正巧发现了一个不错的图片压缩轮子鲁班;正巧原repo主用的rxjava,我一直没有时间点亮这个技能树,所以把rxjava部分扣掉了;正 ...

  6. Android 仿微信朋友圈添加图片

    github地址(欢迎下载Demo) https://github.com/zhouxu88/WXCircleAddPic 老习惯,先上图,着急用的朋友,直接带走Demo,先拿来用吧,毕竟老板催的紧, ...

  7. 使用GridView实现仿微信发朋友圈添加图片,点击预览、删除图片

    最近在项目中有一个用户反馈的功能中要上传图片,和微信发朋友圈添加图片的功能其实有点类似,所以我想着用GridView来实现这个功能,整个过程也很简单,画不多说,详细步骤见下: 1.先来看MainAct ...

  8. android怎么长按一张图片保存到相册_好看的微信朋友圈背景图片下载 让你的朋友圈封面个性起来...

    最近回老家了,由于风景比较好,发朋友圈的次数相比以前更多了一些,朋友之间的互动也明显多了一些.最近有朋友留言说,你的朋友圈封面挺个性的,也想要. 其实,之前为小伙伴们带来过好几期朋友圈封面背景图片,而 ...

  9. 安卓开发仿微信图片拖拽_仿微信朋友圈发表图片拖拽和删除功能

    原标题:仿微信朋友圈发表图片拖拽和删除功能 中国联通在香港公布了上市公司2017年中期业绩.2017年上半年,公司主要业绩指标持续向好,收入稳步回升,服务收入达到人民币1,241.1亿元,同比增长3. ...

最新文章

  1. CashTippr:比特币现金MoneyButton打赏插件
  2. 轻松搞定Retrofit不同网络请求方式的请求参数配置,及常用注解使用
  3. 注解动态赋值_Java注解是如何玩转的,面试官和我聊了半个小时
  4. Navicat的快捷键
  5. java xms xmx 默认值_JVM启动参数-Xmx的默认值是多少?
  6. Windows下配置Nginx使之支持PHP(转)
  7. RK平台计算GPIO对应的整型数
  8. 再谈设计模式之-1.单例模式
  9. opencv的sift算法
  10. ISM模型由自相关矩阵SSIM计算获得可达矩阵
  11. WE出海增长图书馆 | 世界杯豪门面纱下,不容忽视的【增长】沃土
  12. [USACO19FEB]Mowing Mischief
  13. Linux虚拟机设置全屏
  14. h5案例分享 华谊电影《老炮儿》约战
  15. 谷歌浏览器如何收藏网站 谷歌浏览器收藏网站的方法步骤
  16. 《美团机器学习实践》学习笔记:机器学习中的模型评价指标(二)——回归模型评估
  17. html中的长度单位
  18. 爬虫学习经验分享-------某点评网站
  19. 电子书《程序原本》小述
  20. 分散式车辆协同:伯克利DeepDrive无人机数据集B3D

热门文章

  1. FileNotFoundException 问题的解决
  2. 计算机考研最易985,2020考研:盘点那些易考的985院校
  3. idea报错Artifact is not deployed. Press ‘Deploy‘ to start deployment
  4. NFT价值及白皮书获取
  5. 中国第三方物流行业投资现状与未来发展态势研究报告2022-2028年
  6. 华为、苹果、三星扎堆发财报,谁的日子最不好过?
  7. oracle sql 分区查询语句_ORALCE常识及SQL基本语法
  8. 今天,阿里云亮出四张“王牌”,平头哥首次交货!
  9. 临床医生公派赴美国密歇根大学医院访学交流
  10. Chained row