大家好,又见面了,我是你们的朋友全栈君。
仿朋友圈相册图片选择以及画廊效果
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'
//rv
implementation '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.CommonTitleBar
android:id="@+id/title"
style="@style/StyleTitle"
app:centerText="@string/wechat_zone"
app:leftType="none"
app:showBottomLine="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@mipmap/home_bg_float"
android:orientation="vertical">
<EditText
android: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" />
<TextView
android: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.RecyclerView
android:id="@+id/rv_photo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dp_10" />
</LinearLayout>
<Button
android: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<>();
@Override
protected 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);
}
@Override
protected 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;
}
@Override
public 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);
}
}
@Override
public 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));
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public 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() {
@Override
public void onAlbumClickListener() {
getPermission(activity, ALBUM, REQUEST_CODE_ALBUM);
}
@Override
public 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);
}
@Override
protected 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() {
@Override
public 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.CommonTitleBar
android:id="@+id/title"
style="@style/StyleTitle"
app:centerText="@string/gallery_title"
app:showBottomLine="true" />
<com.example.viewpagegallery.MyViewPager
android:id="@+id/viewPager"
android:layout_width="240dp"
android:layout_height="400dp"
android:layout_centerInParent="true"
android:clipChildren="false" />
<TextView
tools: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<>();
@Override
protected 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); //下面会说到
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// 每当页数发生改变时重新设定一遍当前的页数和总页数
tvNumTitle.setText((position + 1) + "/" + mPhotoList.size());
}
@Override
public 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;
}
@Override
public int getCount() {
return mPhotos.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@NonNull
@Override
public 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;
}
@Override
public 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那种移动行为是允许的 */
@Override
public 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 不支持长按操作 */
@Override
public boolean isLongPressDragEnabled() {
return super.isLongPressDragEnabled();
}
/** * Item是否支持滑动 * * @return true 支持滑动操作 * false 不支持滑动操作 */
@Override
public boolean isItemViewSwipeEnabled() {
return super.isItemViewSwipeEnabled();
}
/** * 拖拽切换Item的回调 * * @param recyclerView * @param viewHolder * @param target * @return 如果Item切换了位置,返回true;反之,返回false */
@Override
public 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滑动的方向 */
@Override
public 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 拖拽中状态 */
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public 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刷新效果
@Override
public void onItemDelete(int positon) {
getData().remove(positon);
notifyItemRemoved(positon);
}
@Override
public 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)(* ̄︶ ̄)
源码下载地址
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/142884.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...