21 | */
22 | public class FrescoBaseActivity extends AppCompatActivity {
23 |
24 | public Toolbar toolbar;
25 | private boolean openToolBar = true;//true:开启toolbar,false:不开启
26 | private boolean openNavigationBar;
27 |
28 | @Override
29 | protected void onCreate(@Nullable Bundle arg0) {
30 | super.onCreate(arg0);
31 | // 5.0系统以上才开启沉浸式状态栏
32 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
33 | Window window = getWindow();
34 | if (openNavigationBar) {
35 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
36 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
37 | window.setNavigationBarColor(Color.TRANSPARENT);
38 | } else {
39 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
40 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
41 | }
42 | window.setStatusBarColor(Color.TRANSPARENT);
43 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
44 | }
45 | }
46 |
47 | /**
48 | * 1、从写此方法,如果{@link FrescoBaseActivity#openToolBar} is true,open toolBar
49 | * 这里默认开启ToolBar,如果不需要开启的在{@link #setContentView(int)}之前调用这个:{@code openToolBar = false;},注意,如果不开启ToolBar,不要调用这里的toolBar对象
50 | *
51 | * @param layoutResID
52 | */
53 | public void setContentView(int layoutResID) {
54 | if (openToolBar) {
55 | ToolBarHelper mToolBarHelper = new ToolBarHelper(this, layoutResID);
56 | toolbar = mToolBarHelper.getToolBar();
57 | setContentView(mToolBarHelper.getContentView());
58 | //把toolbar设置到Activity中
59 | toolbar.setTitle(R.string.app_name);
60 | toolbar.setBackgroundColor(Awen.getToolbarBackGround());
61 | setSupportActionBar(toolbar);
62 | } else {
63 | setContentView(View.inflate(this, layoutResID, null));
64 | }
65 | }
66 |
67 | @Override
68 | public boolean onOptionsItemSelected(MenuItem item) {
69 | if (item.getItemId() == android.R.id.home) {
70 | finish();
71 | return true;
72 | }
73 | return super.onOptionsItemSelected(item);
74 | }
75 |
76 | public void setOpenToolBar(boolean openToolBar) {
77 | this.openToolBar = openToolBar;
78 | }
79 |
80 | public void setOpenNavigationBar(boolean openNavigationBar) {
81 | this.openNavigationBar = openNavigationBar;
82 | }
83 |
84 | @TargetApi(21)
85 | public void setNavigationBarColor(@ColorRes int navigationBarColor){
86 | getWindow().setNavigationBarColor(getResources().getColor(navigationBarColor));
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/FrescoImageLoader.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.text.TextUtils;
8 | import android.util.Log;
9 |
10 | import com.awen.photo.photopick.ui.VideoPlayActivity;
11 | import com.awen.photo.photopick.util.FileSizeUtil;
12 | import com.facebook.binaryresource.BinaryResource;
13 | import com.facebook.binaryresource.FileBinaryResource;
14 | import com.facebook.cache.common.CacheKey;
15 | import com.facebook.common.util.UriUtil;
16 | import com.facebook.datasource.DataSource;
17 | import com.facebook.drawee.backends.pipeline.Fresco;
18 | import com.facebook.imagepipeline.cache.DefaultCacheKeyFactory;
19 | import com.facebook.imagepipeline.core.ImagePipeline;
20 | import com.facebook.imagepipeline.core.ImagePipelineFactory;
21 | import com.facebook.imagepipeline.request.ImageRequest;
22 |
23 | import java.io.File;
24 | import java.io.IOException;
25 |
26 | /**
27 | * 以下四个方法可能是你想用的
28 | *
29 | * @see #setToolbarBackGround(int)
30 | * @see #setSaveImageLocalPath(String)
31 | * @see #getSDCacheSizeFormat()
32 | * @see #clearCaches()
33 | * Created by Awen
34 | */
35 |
36 | public final class FrescoImageLoader extends Awen {
37 |
38 | private FrescoImageLoader() {
39 | }
40 |
41 | public static void evictFromMemoryCache(String url){
42 | if (url != null && !TextUtils.isEmpty(url)) {
43 | ImagePipeline imagePipeline = Fresco.getImagePipeline();
44 | Uri uri = Uri.parse(url);
45 | if (imagePipeline.isInBitmapMemoryCache(uri)) {
46 | imagePipeline.evictFromMemoryCache(uri);
47 | }
48 | }
49 | }
50 |
51 | /**
52 | * 图片是否已经存在了
53 | */
54 | public static boolean isCached(Context context, Uri uri) {
55 | ImagePipeline imagePipeline = Fresco.getImagePipeline();
56 | DataSource dataSource = imagePipeline.isInDiskCache(uri);
57 | if (dataSource == null) {
58 | return false;
59 | }
60 | ImageRequest imageRequest = ImageRequest.fromUri(uri);
61 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(imageRequest, context);
62 | BinaryResource resource = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey);
63 | return resource != null && dataSource.getResult() != null && dataSource.getResult();
64 | }
65 |
66 | /**
67 | * 本地缓存文件
68 | */
69 | public static File getCache(Context context, Uri uri) {
70 | if (!isCached(context, uri))
71 | return null;
72 | ImageRequest imageRequest = ImageRequest.fromUri(uri);
73 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(imageRequest, context);
74 | BinaryResource resource = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey);
75 | return ((FileBinaryResource) resource).getFile();
76 | }
77 |
78 | public static File getCache(ImageRequest request) {
79 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(request, null);
80 | BinaryResource bRes = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey);
81 | return ((FileBinaryResource) bRes).getFile();
82 | }
83 |
84 | public static byte[] getByte(Context context, Uri uri) throws IOException {
85 | if (!isCached(context, uri))
86 | return null;
87 | ImageRequest imageRequest = ImageRequest.fromUri(uri);
88 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(imageRequest, null);
89 | BinaryResource bRes = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey);
90 | return bRes.read();
91 | }
92 |
93 | public static byte[] getByte(ImageRequest request) throws IOException {
94 | CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(request, null);
95 | BinaryResource bRes = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey);
96 | return bRes.read();
97 | }
98 |
99 | /**
100 | * 清空所有内存缓存
101 | */
102 | public static void clearMemoryCaches() {
103 | Fresco.getImagePipeline().clearMemoryCaches();
104 | }
105 |
106 | /**
107 | * 清空所有磁盘缓存
108 | */
109 | public static void clearDiskCaches() {
110 | Fresco.getImagePipeline().clearDiskCaches();
111 | }
112 |
113 | /**
114 | * 清除所有缓存(包括内存+磁盘)
115 | */
116 | public static void clearCaches() {
117 | clearMemoryCaches();
118 | clearDiskCaches();
119 | }
120 |
121 | /**
122 | * 获取磁盘上主缓存文件缓存的大小
123 | */
124 | public static long getMainFileCache() {
125 | Fresco.getImagePipelineFactory().getMainFileCache().trimToMinimum();
126 | return Fresco.getImagePipelineFactory().getMainFileCache().getSize();
127 | }
128 |
129 | /**
130 | * 获取磁盘上副缓存(小文件)文件缓存的大小
131 | */
132 | public static long getSmallImageFileCache() {
133 | Fresco.getImagePipelineFactory().getSmallImageFileCache().trimToMinimum();
134 | return Fresco.getImagePipelineFactory().getSmallImageFileCache().getSize();
135 | }
136 |
137 | /**
138 | * 获取磁盘上缓存文件的大小
139 | */
140 | public static long getSDCacheSize() {
141 | return getMainFileCache() + getSmallImageFileCache();
142 | }
143 |
144 | /**
145 | * 获取磁盘上缓存文件的大小,带单位,比如:20MB
146 | */
147 | public static String getSDCacheSizeFormat() {
148 | return FileSizeUtil.formatFileSize(getSDCacheSize());
149 | }
150 |
151 | public static String getFileUrl(String url) {
152 | return UriUtil.LOCAL_FILE_SCHEME + ":///" + url;
153 | }
154 |
155 | public static String getResUrl(int resId) {
156 | return UriUtil.LOCAL_RESOURCE_SCHEME + ":///" + resId;
157 | }
158 |
159 | public static String getAssetUrl(String assetId) {
160 | return UriUtil.LOCAL_ASSET_SCHEME + ":///" + assetId;
161 | }
162 |
163 | /**
164 | * 启动一个视频播放
165 | * @param activity
166 | * @param videoUrl 本地视频地址或网络视频地址
167 | */
168 | public static void startVideoActivity(Activity activity, String videoUrl){
169 | activity.startActivity(new Intent(activity, VideoPlayActivity.class)
170 | .putExtra("videoUrl", videoUrl));
171 | activity.overridePendingTransition(R.anim.image_pager_enter_animation, 0);
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/controller/ToolBarHelper.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.controller;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.LinearLayout;
8 | import androidx.appcompat.widget.Toolbar;
9 | import com.awen.photo.R;
10 |
11 | /**
12 | * Created by Awen
13 | */
14 | public class ToolBarHelper {
15 |
16 | /*base view*/
17 | private LinearLayout mContentView;
18 |
19 | /*toolbar*/
20 | private Toolbar mToolBar;
21 |
22 | public ToolBarHelper(Context context, int layoutId) {
23 | init(context,R.layout.toolbar_layout,layoutId);
24 | }
25 |
26 | public ToolBarHelper(Context context, int toolBarId, int layoutId) {
27 | init(context,toolBarId,layoutId);
28 | }
29 |
30 | private void init(Context context,int toolBarId, int layoutId){
31 | LayoutInflater mInflater = LayoutInflater.from(context);
32 | /*初始化整个内容-直接创建一个帧布局,作为视图容器的父容器*/
33 | mContentView = new LinearLayout(context);
34 | ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
35 | ViewGroup.LayoutParams.MATCH_PARENT);
36 | mContentView.setOrientation(LinearLayout.VERTICAL);
37 | mContentView.setLayoutParams(params);
38 |
39 | /*初始化toolbar-通过inflater获取toolbar的布局文件*/
40 | View toolbar = mInflater.inflate(toolBarId, mContentView);
41 | mToolBar = (Toolbar) toolbar.findViewById(R.id.toolbar);
42 |
43 | /**初始化用户定义的布局*/
44 | View mUserView = mInflater.inflate(layoutId, null);
45 | mContentView.addView(mUserView,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
46 | }
47 |
48 | public LinearLayout getContentView() {
49 | return mContentView;
50 | }
51 |
52 | public Toolbar getToolBar() {
53 | return mToolBar;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/adapter/PhotoGalleryAdapter.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.adapter;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.recyclerview.widget.RecyclerView;
13 |
14 | import com.awen.photo.R;
15 | import com.awen.photo.photopick.bean.Photo;
16 | import com.awen.photo.photopick.bean.PhotoDirectory;
17 | import com.facebook.common.util.UriUtil;
18 | import com.facebook.drawee.backends.pipeline.Fresco;
19 | import com.facebook.drawee.interfaces.DraweeController;
20 | import com.facebook.drawee.view.SimpleDraweeView;
21 | import com.facebook.imagepipeline.common.ResizeOptions;
22 | import com.facebook.imagepipeline.request.ImageRequest;
23 | import com.facebook.imagepipeline.request.ImageRequestBuilder;
24 |
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | /**
29 | * Created by Awen
30 | */
31 | public class PhotoGalleryAdapter extends RecyclerView.Adapter {
32 | private final String TAG = getClass().getSimpleName();
33 | private Context context;
34 | private int selected;
35 | private List directories = new ArrayList<>();
36 | private int imageSize;
37 |
38 | public PhotoGalleryAdapter(Context context) {
39 | this.context = context;
40 | this.imageSize = context.getResources().getDisplayMetrics().widthPixels / 6;
41 | }
42 |
43 | public void refresh(List directories) {
44 | this.directories.clear();
45 | this.directories.addAll(directories);
46 | notifyDataSetChanged();
47 | }
48 |
49 | @NonNull
50 | @Override
51 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
52 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_photo_gallery, parent, false);
53 | return new ViewHolder(view);
54 | }
55 |
56 | @Override
57 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
58 | holder.setData(getItem(position), position);
59 | }
60 |
61 | @Override
62 | public int getItemCount() {
63 | return directories.size();
64 | }
65 |
66 | private PhotoDirectory getItem(int position) {
67 | return this.directories.get(position);
68 | }
69 |
70 | private void changeSelect(int position) {
71 | this.selected = position;
72 | notifyDataSetChanged();
73 | }
74 |
75 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
76 | private SimpleDraweeView imageView;
77 | private ImageView photo_gallery_select;
78 | private TextView name, num;
79 |
80 | ViewHolder(View itemView) {
81 | super(itemView);
82 | imageView = itemView.findViewById(R.id.imageView);
83 | name = itemView.findViewById(R.id.name);
84 | num = itemView.findViewById(R.id.num);
85 | photo_gallery_select = itemView.findViewById(R.id.photo_gallery_select);
86 | imageView.getLayoutParams().height = imageSize;
87 | imageView.getLayoutParams().width = imageSize;
88 | itemView.setOnClickListener(this);
89 | }
90 |
91 | void setData(PhotoDirectory directory, int position) {
92 | if (directory == null || directory.getCoverPath() == null) {
93 | return;
94 | }
95 | if (selected == position) {
96 | photo_gallery_select.setImageResource(R.mipmap.select_icon);
97 | } else {
98 | photo_gallery_select.setImageBitmap(null);
99 | }
100 | name.setText(directory.getName());
101 | num.setText(context.getString(directory.isVideo() ? R.string.gallery_video_num : R.string.gallery_num,
102 | String.valueOf(directory.getPhotos().size())));
103 | //不设置.setResizeOptions(new ResizeOptions(imageSize, imageSize))会显示有问题
104 | ImageRequest imageRequest = ImageRequestBuilder
105 | .newBuilderWithSource(new Uri.Builder().scheme(UriUtil.LOCAL_FILE_SCHEME).path(directory.getCoverPath()).build())
106 | .setLocalThumbnailPreviewsEnabled(true)
107 | .setResizeOptions(new ResizeOptions(imageSize, imageSize)).build();
108 | DraweeController draweeController = Fresco.newDraweeControllerBuilder()
109 | .setImageRequest(imageRequest)
110 | .setOldController(imageView.getController())
111 | .build();
112 | imageView.setController(draweeController);
113 | }
114 |
115 | @Override
116 | public void onClick(View v) {
117 | int position = getAdapterPosition();
118 | if (v.getId() == R.id.photo_gallery_rl) {
119 | if (onItemClickListener != null) {
120 | changeSelect(position);
121 | onItemClickListener.onClick(getItem(position).getPhotos());
122 | }
123 | }
124 | }
125 | }
126 |
127 | private OnItemClickListener onItemClickListener;
128 |
129 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
130 | this.onItemClickListener = onItemClickListener;
131 | }
132 |
133 | public interface OnItemClickListener {
134 | void onClick(List photos);
135 | }
136 |
137 | public void destroy() {
138 | directories.clear();
139 | directories = null;
140 | onItemClickListener = null;
141 | context = null;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/bean/PageReferenceBean.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.bean;
2 |
3 | import com.awen.photo.photopick.widget.photodraweeview.PhotoDraweeView;
4 | import com.facebook.imagepipeline.request.ImageRequest;
5 |
6 | public class PageReferenceBean {
7 |
8 | private ImageRequest request;
9 | private PhotoDraweeView photoDraweeView;
10 |
11 | public PageReferenceBean(ImageRequest request, PhotoDraweeView photoDraweeView) {
12 | this.request = request;
13 | this.photoDraweeView = photoDraweeView;
14 | }
15 |
16 | public ImageRequest getRequest() {
17 | return request;
18 | }
19 |
20 | public void setRequest(ImageRequest request) {
21 | this.request = request;
22 | }
23 |
24 | public PhotoDraweeView getPhotoDraweeView() {
25 | return photoDraweeView;
26 | }
27 |
28 | public void setPhotoDraweeView(PhotoDraweeView photoDraweeView) {
29 | this.photoDraweeView = photoDraweeView;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/bean/Photo.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.bean;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | /**
7 | * Created by Awen
8 | */
9 | public class Photo implements Parcelable {
10 |
11 | private int id;
12 | private String path;
13 | private long size;//byte 字节
14 | private boolean isLongPhoto;//是否是超长图
15 | private int width; //图片真实宽度
16 | private int height;//图片真实高度
17 | private String mimeType;//图片类型:image/webp、image/jpeg、image/png、image/gif
18 | private long dateAdd;//添加的时间,用作视频跟图片组合后的排序
19 | private long duration;//视频的时长,毫秒
20 |
21 | public Photo(){}
22 |
23 | public Photo(int id, String path, long size, boolean isLongPhoto, int width, int height, String mimeType, long dateAdd, long duration) {
24 | this.id = id;
25 | this.path = path;
26 | this.size = size;
27 | this.isLongPhoto = isLongPhoto;
28 | this.width = width;
29 | this.height = height;
30 | this.mimeType = mimeType;
31 | this.dateAdd = dateAdd;
32 | this.duration = duration;
33 | }
34 |
35 | protected Photo(Parcel in) {
36 | id = in.readInt();
37 | path = in.readString();
38 | size = in.readLong();
39 | isLongPhoto = in.readByte() != 0;
40 | width = in.readInt();
41 | height = in.readInt();
42 | mimeType = in.readString();
43 | dateAdd = in.readLong();
44 | duration = in.readLong();
45 | }
46 |
47 | @Override
48 | public void writeToParcel(Parcel dest, int flags) {
49 | dest.writeInt(id);
50 | dest.writeString(path);
51 | dest.writeLong(size);
52 | dest.writeByte((byte) (isLongPhoto ? 1 : 0));
53 | dest.writeInt(width);
54 | dest.writeInt(height);
55 | dest.writeString(mimeType);
56 | dest.writeLong(dateAdd);
57 | dest.writeLong(duration);
58 | }
59 |
60 | public static final Creator CREATOR = new Creator() {
61 | @Override
62 | public Photo createFromParcel(Parcel in) {
63 | return new Photo(in);
64 | }
65 |
66 | @Override
67 | public Photo[] newArray(int size) {
68 | return new Photo[size];
69 | }
70 | };
71 |
72 | @Override
73 | public int describeContents() {
74 | return 0;
75 | }
76 |
77 | @Override
78 | public boolean equals(Object o) {
79 | if (this == o) return true;
80 | if (!(o instanceof Photo)) return false;
81 |
82 | Photo photo = (Photo) o;
83 |
84 | return id == photo.id;
85 | }
86 |
87 | @Override
88 | public int hashCode() {
89 | return id;
90 | }
91 |
92 | public String getPath() {
93 | return path;
94 | }
95 |
96 | public void setPath(String path) {
97 | this.path = path;
98 | }
99 |
100 | public int getId() {
101 | return id;
102 | }
103 |
104 | public void setId(int id) {
105 | this.id = id;
106 | }
107 |
108 | public long getSize() {
109 | return size;
110 | }
111 |
112 | public void setSize(long size) {
113 | this.size = size;
114 | }
115 |
116 | public boolean isLongPhoto() {
117 | return isLongPhoto;
118 | }
119 |
120 | public void setLongPhoto(boolean longPhoto) {
121 | isLongPhoto = longPhoto;
122 | }
123 |
124 | public int getWidth() {
125 | return width;
126 | }
127 |
128 | public void setWidth(int width) {
129 | this.width = width;
130 | }
131 |
132 | public int getHeight() {
133 | return height;
134 | }
135 |
136 | public void setHeight(int height) {
137 | this.height = height;
138 | }
139 |
140 | public String getMimeType() {
141 | return mimeType;
142 | }
143 |
144 | public void setMimeType(String mimeType) {
145 | this.mimeType = mimeType;
146 | }
147 |
148 | public long getDateAdd() {
149 | return dateAdd;
150 | }
151 |
152 | public void setDateAdd(long dateAdd) {
153 | this.dateAdd = dateAdd;
154 | }
155 |
156 | public long getDuration() {
157 | return duration;
158 | }
159 |
160 | public void setDuration(long duration) {
161 | this.duration = duration;
162 | }
163 |
164 | public boolean isVideo(){
165 | return mimeType.startsWith("video");
166 | }
167 |
168 | /**
169 | * 是否是gif图片
170 | * @return true代表是gif
171 | */
172 | public boolean isGif(){
173 | return mimeType != null && mimeType.equals("image/gif");
174 | }
175 |
176 | /**
177 | * 是否是webp图片
178 | * @return true代表是webp
179 | */
180 | public boolean isWebp(){
181 | return mimeType != null && mimeType.equals("image/webp");
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoDirectory.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.bean;
2 |
3 | import android.text.TextUtils;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * Created by Awen
10 | */
11 | public class PhotoDirectory {
12 |
13 | private String id;
14 | private String coverPath;
15 | private String name;
16 | private long dateAdded;
17 | private List photos = new ArrayList<>();
18 | private boolean isVideo;
19 |
20 | @Override
21 | public boolean equals(Object o) {
22 | if (this == o) return true;
23 | if (!(o instanceof PhotoDirectory)) return false;
24 |
25 | PhotoDirectory directory = (PhotoDirectory) o;
26 | if(TextUtils.isEmpty(id)) return false;
27 | if (!id.equals(directory.id)) return false;
28 | return name.equals(directory.name);
29 | }
30 |
31 | @Override
32 | public int hashCode() {
33 | int result = id.hashCode();
34 | result = 31 * result + name.hashCode();
35 | return result;
36 | }
37 |
38 | public String getId() {
39 | return id;
40 | }
41 |
42 | public void setId(String id) {
43 | this.id = id;
44 | }
45 |
46 | public String getCoverPath() {
47 | return coverPath;
48 | }
49 |
50 | public void setCoverPath(String coverPath) {
51 | this.coverPath = coverPath;
52 | }
53 |
54 | public String getName() {
55 | return name;
56 | }
57 |
58 | public void setName(String name) {
59 | this.name = name;
60 | }
61 |
62 | public long getDateAdded() {
63 | return dateAdded;
64 | }
65 |
66 | public void setDateAdded(long dateAdded) {
67 | this.dateAdded = dateAdded;
68 | }
69 |
70 | public List getPhotos() {
71 | return photos;
72 | }
73 |
74 | public void setPhotos(List photos) {
75 | this.photos = photos;
76 | }
77 |
78 | public void addPhoto(Photo photo) {
79 | photos.add(photo);
80 | }
81 |
82 | public boolean isVideo() {
83 | return isVideo;
84 | }
85 |
86 | public void setVideo(boolean video) {
87 | isVideo = video;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoPagerBean.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.bean;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import com.awen.photo.photopick.controller.PhotoPagerConfig;
7 |
8 | import java.util.ArrayList;
9 |
10 | /**
11 | * Created by Awen .
12 | */
13 | public class PhotoPagerBean implements Parcelable {
14 |
15 | /**
16 | * 是否开启图片保存到本地功能
17 | */
18 | private boolean saveImage;
19 | /**
20 | * 保存图片到本地的地址
21 | */
22 | private String saveImageLocalPath;
23 | /**
24 | * 展示第几张图片
25 | */
26 | private int pagePosition;
27 | /**
28 | * 大图
29 | */
30 | private ArrayList bigImgUrls;
31 | /**
32 | * 小图,可用于在展示大图前进行小图的展示
33 | */
34 | private ArrayList smallImgUrls;
35 | /**
36 | * 是否开启下滑关闭activity,默认开启。类似微信的图片浏览,可下滑关闭一样,但是没有图片归位效果
37 | */
38 | private boolean isOpenDownAnimate;
39 |
40 | private PhotoPagerConfig.Builder.OnPhotoSaveCallback onPhotoSaveCallback;
41 |
42 | public PhotoPagerBean() {
43 | }
44 |
45 |
46 | private PhotoPagerBean(Parcel in) {
47 | saveImage = in.readByte() != 0;
48 | saveImageLocalPath = in.readString();
49 | pagePosition = in.readInt();
50 | bigImgUrls = in.createStringArrayList();
51 | smallImgUrls = in.createStringArrayList();
52 | isOpenDownAnimate = in.readByte() != 0;
53 | }
54 |
55 | @Override
56 | public void writeToParcel(Parcel dest, int flags) {
57 | dest.writeByte((byte) (saveImage ? 1 : 0));
58 | dest.writeString(saveImageLocalPath);
59 | dest.writeInt(pagePosition);
60 | dest.writeStringList(bigImgUrls);
61 | dest.writeStringList(smallImgUrls);
62 | dest.writeByte((byte) (isOpenDownAnimate ? 1 : 0));
63 | }
64 |
65 | public static final Creator CREATOR = new Creator() {
66 | @Override
67 | public PhotoPagerBean createFromParcel(Parcel in) {
68 | return new PhotoPagerBean(in);
69 | }
70 |
71 | @Override
72 | public PhotoPagerBean[] newArray(int size) {
73 | return new PhotoPagerBean[size];
74 | }
75 | };
76 |
77 | @Override
78 | public int describeContents() {
79 | return 0;
80 | }
81 |
82 | public boolean isSaveImage() {
83 | return saveImage;
84 | }
85 |
86 | public void setSaveImage(boolean saveImage) {
87 | this.saveImage = saveImage;
88 | }
89 |
90 | public String getSaveImageLocalPath() {
91 | return saveImageLocalPath;
92 | }
93 |
94 | public void setSaveImageLocalPath(String saveImageLocalPath) {
95 | this.saveImageLocalPath = saveImageLocalPath;
96 | }
97 |
98 | public int getPagePosition() {
99 | return pagePosition;
100 | }
101 |
102 | public void setPagePosition(int pagePosition) {
103 | this.pagePosition = pagePosition;
104 | }
105 |
106 | public ArrayList getBigImgUrls() {
107 | return bigImgUrls;
108 | }
109 |
110 | public void setBigImgUrls(ArrayList bigImgUrls) {
111 | this.bigImgUrls = bigImgUrls;
112 | }
113 |
114 | /**
115 | * @deprecated use {@link #getSmallImgUrls()}
116 | */
117 | public ArrayList getLowImgUrls() {
118 | return smallImgUrls;
119 | }
120 |
121 | /**
122 | * @deprecated use {@link #setSmallImgUrls(ArrayList)}
123 | */
124 | public void setLowImgUrls(ArrayList lowImgUrls) {
125 | setSmallImgUrls(lowImgUrls);
126 | }
127 |
128 | public void setSmallImgUrls(ArrayList smallImgUrls) {
129 | this.smallImgUrls = smallImgUrls;
130 | }
131 |
132 | public ArrayList getSmallImgUrls() {
133 | return smallImgUrls;
134 | }
135 |
136 | /**
137 | * 一张一张大图add进ArrayList
138 | *
139 | * @param bigImageUrl 大图url
140 | */
141 | public void addSingleBigImageUrl(String bigImageUrl) {
142 | if (bigImageUrl == null) {
143 | throw new NullPointerException("bigImageUrl is null");
144 | }
145 | if (bigImgUrls == null) {
146 | bigImgUrls = new ArrayList<>();
147 | }
148 | bigImgUrls.add(bigImageUrl);
149 | }
150 |
151 | /**
152 | * 一张一张小图add进ArrayList
153 | *
154 | * @param smallImageUrl 小图url
155 | */
156 | public void addSingleSmallImageUrl(String smallImageUrl) {
157 | if (smallImageUrl == null) {
158 | throw new NullPointerException("smallImageUrl is null");
159 | }
160 | if (smallImgUrls == null) {
161 | smallImgUrls = new ArrayList<>();
162 | }
163 | smallImgUrls.add(smallImageUrl);
164 | }
165 |
166 | /**
167 | * 一张一张小图add进ArrayList
168 | *
169 | * @param lowImageUrl 小图url
170 | * @deprecated use {@link #addSingleSmallImageUrl(String)}
171 | */
172 | public void addSingleLowImageUrl(String lowImageUrl) {
173 | addSingleSmallImageUrl(lowImageUrl);
174 | }
175 |
176 | public boolean isOpenDownAnimate() {
177 | return isOpenDownAnimate;
178 | }
179 |
180 | public void setOpenDownAnimate(boolean openDownAnimate) {
181 | isOpenDownAnimate = openDownAnimate;
182 | }
183 |
184 | public PhotoPagerConfig.Builder.OnPhotoSaveCallback getOnPhotoSaveCallback() {
185 | return onPhotoSaveCallback;
186 | }
187 |
188 | public void setOnPhotoSaveCallback(PhotoPagerConfig.Builder.OnPhotoSaveCallback onPhotoSaveCallback) {
189 | this.onPhotoSaveCallback = onPhotoSaveCallback;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoPickBean.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.bean;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 | import android.util.Log;
6 |
7 | import com.awen.photo.photopick.controller.PhotoPickConfig;
8 |
9 | import java.io.Serializable;
10 |
11 | /**
12 | * Created by Awen .
13 | */
14 | public class PhotoPickBean implements Parcelable {
15 |
16 | private int maxPickSize; //最多可以选择多少张图片
17 | private int pickMode; //单选还是多选
18 | private int spanCount; //recyclerview有多少列
19 | private boolean showCamera;//是否展示拍照icon
20 | private boolean clipPhoto; //是否启动裁剪图片
21 | private boolean originalPicture;//是否选择的是原图
22 | private boolean showGif;//是否展示gif图片
23 | private int mediaType;//默认0,显示所有图片跟视频,1:只显示图片,2:只显示视频
24 | private PhotoPickConfig.Builder.OnPhotoResultCallback onPhotoResultCallback;
25 |
26 | public PhotoPickBean(){}
27 |
28 | private PhotoPickBean(Parcel in) {
29 | maxPickSize = in.readInt();
30 | pickMode = in.readInt();
31 | spanCount = in.readInt();
32 | showCamera = in.readByte() != 0;
33 | clipPhoto = in.readByte() != 0;
34 | originalPicture = in.readByte() != 0;
35 | showGif = in.readByte() != 0;
36 | mediaType = in.readInt();
37 | }
38 |
39 | @Override
40 | public void writeToParcel(Parcel dest, int flags) {
41 | dest.writeInt(maxPickSize);
42 | dest.writeInt(pickMode);
43 | dest.writeInt(spanCount);
44 | dest.writeByte((byte) (showCamera ? 1 : 0));
45 | dest.writeByte((byte) (clipPhoto ? 1 : 0));
46 | dest.writeByte((byte) (originalPicture ? 1 : 0));
47 | dest.writeByte((byte) (showGif ? 1 : 0));
48 | dest.writeInt(mediaType);
49 | }
50 |
51 | public static final Creator CREATOR = new Creator() {
52 | @Override
53 | public PhotoPickBean createFromParcel(Parcel in) {
54 | return new PhotoPickBean(in);
55 | }
56 |
57 | @Override
58 | public PhotoPickBean[] newArray(int size) {
59 | return new PhotoPickBean[size];
60 | }
61 | };
62 |
63 | @Override
64 | public int describeContents() {
65 | return 0;
66 | }
67 |
68 |
69 | public int getMaxPickSize() {
70 | return maxPickSize;
71 | }
72 |
73 | public void setMaxPickSize(int maxPickSize) {
74 | this.maxPickSize = maxPickSize;
75 | }
76 |
77 | public int getPickMode() {
78 | return pickMode;
79 | }
80 |
81 | public void setPickMode(int pickMode) {
82 | this.pickMode = pickMode;
83 | }
84 |
85 | public int getSpanCount() {
86 | return spanCount;
87 | }
88 |
89 | public void setSpanCount(int spanCount) {
90 | this.spanCount = spanCount;
91 | }
92 |
93 | public boolean isShowCamera() {
94 | return showCamera;
95 | }
96 |
97 | public void setShowCamera(boolean showCamera) {
98 | this.showCamera = showCamera;
99 | }
100 |
101 | public boolean isClipPhoto() {
102 | return clipPhoto;
103 | }
104 |
105 | public void setClipPhoto(boolean clipPhoto) {
106 | this.clipPhoto = clipPhoto;
107 | }
108 |
109 | public boolean isOriginalPicture() {
110 | return originalPicture;
111 | }
112 |
113 | public void setOriginalPicture(boolean originalPicture) {
114 | this.originalPicture = originalPicture;
115 | }
116 |
117 | public boolean isShowGif() {
118 | return showGif;
119 | }
120 |
121 | public void setShowGif(boolean showGif) {
122 | this.showGif = showGif;
123 | }
124 |
125 | public PhotoPickConfig.Builder.OnPhotoResultCallback getOnPhotoResultCallback() {
126 | return onPhotoResultCallback;
127 | }
128 |
129 | public void setOnPhotoResultCallback(PhotoPickConfig.Builder.OnPhotoResultCallback onPhotoResultCallback) {
130 | this.onPhotoResultCallback = onPhotoResultCallback;
131 | }
132 |
133 | public int getMediaType() {
134 | return mediaType;
135 | }
136 |
137 | public void setMediaType(int mediaType) {
138 | this.mediaType = mediaType;
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoPreviewBean.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.bean;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * Created by Awen
9 | */
10 |
11 | public class PhotoPreviewBean implements Parcelable {
12 |
13 | private int position;
14 | private int maxPickSize;
15 | private boolean originalPicture;//是否选择的是原图
16 | private boolean isPreview;//是否是预览
17 |
18 | public PhotoPreviewBean(){}
19 |
20 |
21 | private PhotoPreviewBean(Parcel in) {
22 | position = in.readInt();
23 | maxPickSize = in.readInt();
24 | originalPicture = in.readByte() != 0;
25 | isPreview = in.readByte() != 0;
26 | }
27 |
28 | @Override
29 | public void writeToParcel(Parcel dest, int flags) {
30 | dest.writeInt(position);
31 | dest.writeInt(maxPickSize);
32 | dest.writeByte((byte) (originalPicture ? 1 : 0));
33 | dest.writeByte((byte) (isPreview ? 1 : 0));
34 | }
35 |
36 | public static final Creator CREATOR = new Creator() {
37 | @Override
38 | public PhotoPreviewBean createFromParcel(Parcel in) {
39 | return new PhotoPreviewBean(in);
40 | }
41 |
42 | @Override
43 | public PhotoPreviewBean[] newArray(int size) {
44 | return new PhotoPreviewBean[size];
45 | }
46 | };
47 |
48 | @Override
49 | public int describeContents() {
50 | return 0;
51 | }
52 |
53 | public int getPosition() {
54 | return position;
55 | }
56 |
57 | public void setPosition(int position) {
58 | this.position = position;
59 | }
60 |
61 | public int getMaxPickSize() {
62 | return maxPickSize;
63 | }
64 |
65 | public void setMaxPickSize(int maxPickSize) {
66 | this.maxPickSize = maxPickSize;
67 | }
68 |
69 | public boolean isOriginalPicture() {
70 | return originalPicture;
71 | }
72 |
73 | public void setOriginalPicture(boolean originalPicture) {
74 | this.originalPicture = originalPicture;
75 | }
76 |
77 | public boolean isPreview() {
78 | return isPreview;
79 | }
80 |
81 | public void setPreview(boolean preview) {
82 | isPreview = preview;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/bean/PhotoResultBean.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.bean;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * 从图库选择了或裁剪了的图片
7 | * Created by Awen
8 | */
9 |
10 | public class PhotoResultBean {
11 | private boolean isOriginalPicture;//用户选择的是否是原图
12 | private ArrayList photoLists;
13 |
14 | public boolean isOriginalPicture() {
15 | return isOriginalPicture;
16 | }
17 |
18 | public void setOriginalPicture(boolean originalPicture) {
19 | isOriginalPicture = originalPicture;
20 | }
21 |
22 | public ArrayList getPhotoLists() {
23 | return photoLists;
24 | }
25 |
26 | public void setPhotoLists(ArrayList photoLists) {
27 | this.photoLists = photoLists;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/controller/PhotoPreviewConfig.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.controller;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import com.awen.photo.Awen;
7 | import com.awen.photo.R;
8 | import com.awen.photo.photopick.adapter.PhotoPickAdapter;
9 | import com.awen.photo.photopick.bean.Photo;
10 | import com.awen.photo.photopick.bean.PhotoPreviewBean;
11 | import com.awen.photo.photopick.ui.PhotoPreviewActivity;
12 |
13 | import java.util.ArrayList;
14 |
15 | /**
16 | * 仿微信图片预览
17 | * 如何使用:
18 | *
19 | * new PhotoPreviewConfig.Builder((Activity) context)
20 | * .setPosition(position)
21 | * .setMaxPickSize(maxPickSize)
22 | * .setPhotos(photos)
23 | * .setSelectPhotos(selectPhotos)
24 | * .setOriginalPicture(originalPicture)
25 | * .build();
26 | *
27 | * Created by Awen
28 | */
29 |
30 | public class PhotoPreviewConfig {
31 | public static final String EXTRA_BUNDLE = "extra_bundle";
32 | public static final String EXTRA_BEAN = "extra_bean";
33 | public static final String EXTRA_ORIGINAL_PIC = "original_picture";
34 | public final static int REQUEST_CODE = 10504;
35 |
36 | private PhotoPreviewConfig(Activity activity, Builder builder) {
37 | PhotoPreviewBean photoPreviewBean = builder.bean;
38 | if (photoPreviewBean == null) {
39 | throw new NullPointerException("Builder#photoPagerBean is null");
40 | }
41 | if (PhotoPickAdapter.selectPhotos != null && (PhotoPickAdapter.selectPhotos.size() > photoPreviewBean.getMaxPickSize())) {
42 | throw new IndexOutOfBoundsException("seleced photo size out maxPickSize size,select photo size = " + PhotoPickAdapter.selectPhotos.size() + ",maxPickSize size = " + photoPreviewBean.getMaxPickSize());
43 | }
44 | Bundle bundle = new Bundle();
45 | bundle.putParcelable(EXTRA_BEAN, photoPreviewBean);
46 | Intent intent = new Intent(activity, PhotoPreviewActivity.class);
47 | intent.putExtra(EXTRA_BUNDLE, bundle);
48 | activity.startActivityForResult(intent, REQUEST_CODE);
49 | activity.overridePendingTransition(R.anim.image_pager_enter_animation, 0);
50 | }
51 |
52 | public static class Builder {
53 | private Activity context;
54 | private PhotoPreviewBean bean;
55 |
56 | public Builder(Activity context) {
57 | Awen.checkInit();
58 | if (context == null) {
59 | throw new NullPointerException("context is null");
60 | }
61 | this.context = context;
62 | bean = new PhotoPreviewBean();
63 | }
64 |
65 | public Builder setPhotoPreviewBean(PhotoPreviewBean bean) {
66 | this.bean = bean;
67 | return this;
68 | }
69 |
70 | public Builder setPosition(int position) {
71 | if (position < 0) {
72 | position = 0;
73 | }
74 | bean.setPosition(position);
75 | return this;
76 | }
77 |
78 | public Builder setOriginalPicture(boolean originalPicture) {//是否设置原图,默认false
79 | bean.setOriginalPicture(originalPicture);
80 | return this;
81 | }
82 |
83 | public Builder setMaxPickSize(int maxPickSize) {
84 | bean.setMaxPickSize(maxPickSize);
85 | return this;
86 | }
87 |
88 | /**
89 | * 是否是预览
90 | */
91 | public Builder setPreview(boolean isPreview){
92 | bean.setPreview(isPreview);
93 | return this;
94 | }
95 |
96 | public PhotoPreviewConfig build() {
97 | return new PhotoPreviewConfig(context, this);
98 | }
99 |
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/data/Data.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.data;
2 |
3 | import android.content.Context;
4 | import android.database.Cursor;
5 | import android.text.TextUtils;
6 | import android.util.Log;
7 |
8 | import com.awen.photo.R;
9 | import com.awen.photo.photopick.bean.Photo;
10 | import com.awen.photo.photopick.bean.PhotoDirectory;
11 | import com.awen.photo.photopick.util.BitmapUtil;
12 |
13 | import java.time.Duration;
14 | import java.util.ArrayList;
15 | import java.util.Collections;
16 | import java.util.Comparator;
17 | import java.util.Date;
18 | import java.util.List;
19 |
20 | import static android.provider.BaseColumns._ID;
21 | import static android.provider.MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME;
22 | import static android.provider.MediaStore.Images.ImageColumns.BUCKET_ID;
23 | import static android.provider.MediaStore.MediaColumns.DATA;
24 | import static android.provider.MediaStore.MediaColumns.DATE_ADDED;
25 | import static android.provider.MediaStore.MediaColumns.DURATION;
26 | import static android.provider.MediaStore.MediaColumns.HEIGHT;
27 | import static android.provider.MediaStore.MediaColumns.SIZE;
28 | import static android.provider.MediaStore.MediaColumns.MIME_TYPE;
29 | import static android.provider.MediaStore.MediaColumns.WIDTH;
30 |
31 | /**
32 | * Created by Awen
33 | */
34 | public class Data {
35 | public final static int INDEX_ALL_PHOTOS = 0;
36 |
37 | /**
38 | * 获取所有图片
39 | */
40 | public static List getDataFromCursor(Context context, Cursor data) {
41 | List directories = new ArrayList<>();
42 | PhotoDirectory photoDirectoryAll = new PhotoDirectory();
43 | photoDirectoryAll.setName(context.getString(R.string.all_photo));
44 | photoDirectoryAll.setId("ALL");
45 |
46 | while (data.moveToNext()) {
47 | int imageId = data.getInt(data.getColumnIndexOrThrow(_ID));
48 | String bucketId = data.getString(data.getColumnIndexOrThrow(BUCKET_ID));
49 | String name = data.getString(data.getColumnIndexOrThrow(BUCKET_DISPLAY_NAME));
50 | String path = data.getString(data.getColumnIndexOrThrow(DATA));
51 | long size = data.getLong(data.getColumnIndexOrThrow(SIZE));
52 | String mimeType = data.getString(data.getColumnIndexOrThrow(MIME_TYPE));
53 | int width = data.getInt(data.getColumnIndexOrThrow(WIDTH));
54 | int height = data.getInt(data.getColumnIndexOrThrow(HEIGHT));
55 | long dateAdd = data.getLong(data.getColumnIndexOrThrow(DATE_ADDED));
56 |
57 | Photo photo = new Photo();
58 |
59 | if (size <= 0) {
60 | continue;
61 | }
62 | photo.setId(imageId);
63 | photo.setPath(path);
64 | photo.setSize(size);
65 | photo.setMimeType(mimeType);
66 | photo.setWidth(width);
67 | photo.setHeight(height);
68 | photo.setDateAdd(dateAdd);
69 |
70 | PhotoDirectory photoDirectory = new PhotoDirectory();
71 | photoDirectory.setId(bucketId);
72 | photoDirectory.setName(name);
73 | if (!directories.contains(photoDirectory)) {
74 | photoDirectory.setCoverPath(path);
75 | photoDirectory.addPhoto(photo);
76 | photoDirectory.setDateAdded(dateAdd);
77 | directories.add(photoDirectory);
78 | } else {
79 | directories.get(directories.indexOf(photoDirectory)).addPhoto(photo);
80 | }
81 | photoDirectoryAll.addPhoto(photo);
82 | }
83 | if (photoDirectoryAll.getPhotos().size() > 0) {
84 | photoDirectoryAll.setCoverPath(photoDirectoryAll.getPhotos().get(0).getPath());
85 | }
86 | photoDirectoryAll.setDateAdded(new Date().getTime());
87 | directories.add(INDEX_ALL_PHOTOS, photoDirectoryAll);
88 | return directories;
89 | }
90 |
91 | /**
92 | * 获取所有视频,存到一个文件夹bean里面
93 | */
94 | public static PhotoDirectory getDataFromVideoCursor(Context context, Cursor data) {
95 | PhotoDirectory videoDir = new PhotoDirectory();
96 | videoDir.setName(context.getString(R.string.all_video));
97 | videoDir.setId("ALL_VIDEO");
98 |
99 | while (data.moveToNext()) {
100 | int id = data.getInt(data.getColumnIndexOrThrow(_ID));
101 | String path = data.getString(data.getColumnIndexOrThrow(DATA));
102 | long size = data.getLong(data.getColumnIndexOrThrow(SIZE));
103 | String mimeType = data.getString(data.getColumnIndexOrThrow(MIME_TYPE));
104 | int width = data.getInt(data.getColumnIndexOrThrow(WIDTH));
105 | int height = data.getInt(data.getColumnIndexOrThrow(HEIGHT));
106 | long dateAdd = data.getLong(data.getColumnIndexOrThrow(DATE_ADDED));
107 | long duration = data.getLong(data.getColumnIndexOrThrow(DURATION));
108 |
109 | Photo photo = new Photo();
110 |
111 | if (size <= 0) {
112 | continue;
113 | }
114 | photo.setId(id);
115 | photo.setPath(path);
116 | photo.setSize(size);
117 | photo.setMimeType(mimeType);
118 | photo.setWidth(width);
119 | photo.setHeight(height);
120 | photo.setDateAdd(dateAdd);
121 | photo.setDuration(duration);
122 |
123 | if (TextUtils.isEmpty(videoDir.getCoverPath())) {
124 | videoDir.setCoverPath(path);
125 | }
126 | videoDir.addPhoto(photo);
127 | }
128 | videoDir.setDateAdded(new Date().getTime());
129 | videoDir.setVideo(true);
130 | return videoDir;
131 | }
132 |
133 | /**
134 | * 合并图片跟视频
135 | * @param parent 所有图片
136 | * @param child 所有视频
137 | */
138 | public static void mergePhotoAndVideo(Context context,PhotoDirectory parent, PhotoDirectory child) {
139 | parent.setName(context.getString(R.string.select_photo));
140 | List list = child.getPhotos();
141 | for (Photo photo : list) {
142 | parent.addPhoto(photo);
143 | }
144 | sort(parent.getPhotos());
145 | }
146 |
147 | /**
148 | * 把合并后的图片视频进行时间排序
149 | * @param list
150 | */
151 | public static void sort(List list) {
152 | Collections.sort(list, new Comparator() {
153 | @Override
154 | public int compare(Photo o1, Photo o2) {
155 | try {
156 | if (o1.getDateAdd() < o2.getDateAdd()) {
157 | return 1;
158 | } else {
159 | return -1;
160 | }
161 | } catch (Exception e) {
162 | e.printStackTrace();
163 | }
164 | return 0;
165 | }
166 | });
167 | }
168 |
169 | }
170 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/loader/MediaStoreHelper.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.loader;
2 |
3 | import android.app.Activity;
4 | import android.content.ContentResolver;
5 | import android.content.Context;
6 | import android.database.Cursor;
7 | import android.os.Bundle;
8 |
9 | import androidx.appcompat.app.AppCompatActivity;
10 | import androidx.loader.app.LoaderManager;
11 | import androidx.loader.content.Loader;
12 |
13 | import com.awen.photo.photopick.bean.PhotoPickBean;
14 | import com.awen.photo.photopick.data.Data;
15 | import com.awen.photo.photopick.bean.PhotoDirectory;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 |
21 | /**
22 | * Created by Awen
23 | */
24 | public class MediaStoreHelper {
25 |
26 | /**
27 | * 第一种方式
28 | *
29 | * @param context Activity
30 | * @param resultCallback PhotosResultCallback
31 | */
32 | public static void getPhotoDirs(final Activity context, final PhotoPickBean config, final PhotosResultCallback resultCallback) {
33 | new Thread(new Runnable() {
34 | @Override
35 | public void run() {
36 | ContentResolver contentResolver = context.getContentResolver();
37 | int mediaType = config.getMediaType();
38 | PhotoDirectory videoDir = null;
39 | List directories = null;
40 | if (mediaType != MediaType.ONLY_IMAGE) {//不是只显示图片,要查询视频
41 | //视频
42 | VideoCursorLoader vLoader = new VideoCursorLoader();
43 | Cursor vCursor = contentResolver.query(vLoader.getUri(),
44 | vLoader.getProjection(),
45 | vLoader.getSelection(),
46 | vLoader.getSelectionArgs(),
47 | vLoader.getSortOrder());
48 | if (vCursor != null) {
49 | videoDir = Data.getDataFromVideoCursor(context, vCursor);
50 | vCursor.close();
51 | }
52 | }
53 |
54 | if (mediaType != MediaType.ONLY_VIDEO) {//不是只显示视频,要查询图片
55 | //图片
56 | PhotoCursorLoader loader = new PhotoCursorLoader();
57 | loader.setShowGif(config.isShowGif());
58 | Cursor cursor = contentResolver.query(loader.getUri(),
59 | loader.getProjection(),
60 | loader.getSelection(),
61 | loader.getSelectionArgs(),
62 | loader.getSortOrder());
63 | if (cursor != null) {
64 | directories = Data.getDataFromCursor(context, cursor);
65 | cursor.close();
66 | }
67 | }
68 |
69 | if (directories == null) {
70 | directories = new ArrayList<>();
71 | }
72 | if (videoDir != null && videoDir.getPhotos().size() > 0) {
73 | if (directories.size() > 0) {
74 | directories.add(1, videoDir);
75 | Data.mergePhotoAndVideo(context,directories.get(0), videoDir);
76 | } else {
77 | directories.add(videoDir);
78 | }
79 | }
80 |
81 | if (resultCallback != null) {
82 | resultCallback.onResultCallback(directories);
83 | }
84 | }
85 | }).start();
86 | }
87 |
88 | /**
89 | * 第二种方式
90 | *
91 | * @param activity AppCompatActivity
92 | * @param args Bundle
93 | * @param resultCallback PhotosResultCallback
94 | */
95 | public static void getPhotoDirs(final AppCompatActivity activity, final Bundle args, final PhotosResultCallback resultCallback) {
96 | activity.getSupportLoaderManager()
97 | .initLoader(0, args, new PhotoDirLoaderCallbacks(activity, resultCallback));
98 |
99 | }
100 |
101 | static class PhotoDirLoaderCallbacks implements LoaderManager.LoaderCallbacks {
102 |
103 | private Context context;
104 | private PhotosResultCallback resultCallback;
105 |
106 | public PhotoDirLoaderCallbacks(Context context, PhotosResultCallback resultCallback) {
107 | this.context = context;
108 | this.resultCallback = resultCallback;
109 | }
110 |
111 | @Override
112 | public Loader onCreateLoader(int id, Bundle args) {
113 | return new PhotoDirectoryLoader(context);
114 | }
115 |
116 | @Override
117 | public void onLoadFinished(Loader loader, Cursor data) {
118 |
119 | if (data == null) return;
120 |
121 | List directories = Data.getDataFromCursor(context, data);
122 | data.close();
123 |
124 | if (resultCallback != null) {
125 | resultCallback.onResultCallback(directories);
126 | }
127 | }
128 |
129 | @Override
130 | public void onLoaderReset(Loader loader) {
131 |
132 | }
133 | }
134 |
135 |
136 | public interface PhotosResultCallback {
137 | void onResultCallback(List directories);
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/loader/MediaType.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.loader;
2 |
3 | public interface MediaType {
4 |
5 | int ALL = 0;//默认,包括图片和视频
6 |
7 | int ONLY_IMAGE = 1;//只有图片
8 |
9 | int ONLY_VIDEO = 2;//只有视频
10 | }
11 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/loader/PhotoCursorLoader.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.loader;
2 |
3 | import android.net.Uri;
4 | import android.provider.MediaStore;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.annotation.Nullable;
8 |
9 | import static android.provider.MediaStore.MediaColumns.MIME_TYPE;
10 |
11 | /**
12 | * Created by Awen
13 | */
14 | public class PhotoCursorLoader {
15 |
16 | private final static String IMAGE_JPEG = "image/jpeg";
17 | private final static String IMAGE_PNG = "image/png";
18 | private final static String IMAGE_GIF = "image/gif";
19 | private final static String IMAGE_WEBP = "image/webp";
20 |
21 | @NonNull
22 | private Uri uri;
23 | @Nullable
24 | private String[] projection;
25 | @Nullable
26 | private String selection;
27 | @Nullable
28 | private String[] selectionArgs;
29 | @Nullable
30 | private String sortOrder;
31 |
32 | private boolean showGif;
33 |
34 | public PhotoCursorLoader() {
35 | String[] IMAGE_PROJECTION = {
36 | MediaStore.Images.Media._ID,
37 | MediaStore.Images.Media.DATA,
38 | MediaStore.Images.Media.BUCKET_ID,
39 | MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
40 | MediaStore.Images.Media.DATE_ADDED,
41 | MediaStore.Images.Media.SIZE,
42 | MediaStore.Images.Media.MIME_TYPE,
43 | MediaStore.Images.Media.WIDTH,
44 | MediaStore.Images.Media.HEIGHT
45 | };
46 | //default ,默认配置
47 | setShowGif(true);//展示gif
48 | setUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
49 | setProjection(IMAGE_PROJECTION);
50 | setSelection(MIME_TYPE + "=? or " + MIME_TYPE + "=? or " + MIME_TYPE + "=? " + (showGif ? ("or " + MIME_TYPE + "=?") : ""));
51 | setShowGif(isShowGif());
52 | setSelectionArgs(selectionArgs);
53 | setSortOrder(MediaStore.Images.Media.DATE_ADDED + " DESC");
54 | }
55 |
56 | public PhotoCursorLoader(@NonNull Uri uri, @Nullable String[] projection,
57 | @Nullable String selection, @Nullable String[] selectionArgs,
58 | @Nullable String sortOrder) {
59 | this.uri = uri;
60 | this.projection = projection;
61 | this.selection = selection;
62 | this.selectionArgs = selectionArgs;
63 | this.sortOrder = sortOrder;
64 | }
65 |
66 | @NonNull
67 | public Uri getUri() {
68 | return uri;
69 | }
70 |
71 | public void setUri(@NonNull Uri uri) {
72 | this.uri = uri;
73 | }
74 |
75 | @Nullable
76 | public String[] getProjection() {
77 | return projection;
78 | }
79 |
80 | public void setProjection(@Nullable String[] projection) {
81 | this.projection = projection;
82 | }
83 |
84 | @Nullable
85 | public String getSelection() {
86 | return selection;
87 | }
88 |
89 | public void setSelection(@Nullable String selection) {
90 | this.selection = selection;
91 | }
92 |
93 | @Nullable
94 | public String[] getSelectionArgs() {
95 | return selectionArgs;
96 | }
97 |
98 | public void setSelectionArgs(@Nullable String[] selectionArgs) {
99 | this.selectionArgs = selectionArgs;
100 | }
101 |
102 | @Nullable
103 | public String getSortOrder() {
104 | return sortOrder;
105 | }
106 |
107 | public void setSortOrder(@Nullable String sortOrder) {
108 | this.sortOrder = sortOrder;
109 | }
110 |
111 | public boolean isShowGif() {
112 | return showGif;
113 | }
114 |
115 | public void setShowGif(boolean showGif) {
116 | this.showGif = showGif;
117 | if (showGif) {
118 | selectionArgs = new String[]{IMAGE_JPEG, IMAGE_PNG, IMAGE_WEBP, IMAGE_GIF};
119 | } else {
120 | selectionArgs = new String[]{IMAGE_JPEG, IMAGE_PNG, IMAGE_WEBP};
121 | }
122 | setSelection(MIME_TYPE + "=? or " + MIME_TYPE + "=? or " + MIME_TYPE + "=? " + (showGif ? ("or " + MIME_TYPE + "=?") : ""));
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/loader/PhotoDirectoryLoader.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.loader;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.provider.MediaStore.Images.Media;
6 |
7 | import androidx.loader.content.CursorLoader;
8 |
9 | import static android.provider.MediaStore.MediaColumns.MIME_TYPE;
10 |
11 | /**
12 | * Created by Awen
13 | */
14 | public class PhotoDirectoryLoader extends CursorLoader {
15 |
16 | private final static String IMAGE_JPEG = "image/jpeg";
17 | private final static String IMAGE_PNG = "image/png";
18 | private final static String IMAGE_GIF = "image/gif";
19 | private final static String IMAGE_WEBP = "image/webP";
20 |
21 | final String[] IMAGE_PROJECTION = {
22 | Media._ID,
23 | Media.DATA,
24 | Media.BUCKET_ID,
25 | Media.BUCKET_DISPLAY_NAME,
26 | Media.DATE_ADDED,
27 | Media.SIZE
28 | };
29 |
30 | public PhotoDirectoryLoader(Context context){
31 | this(context,false);
32 | }
33 |
34 | public PhotoDirectoryLoader(Context context, boolean showGif) {
35 | super(context);
36 |
37 | setProjection(IMAGE_PROJECTION);
38 | setUri(Media.EXTERNAL_CONTENT_URI);
39 | setSortOrder(Media.DATE_ADDED + " DESC");
40 |
41 | setSelection(
42 | MIME_TYPE + "=? or " + MIME_TYPE + "=? " + (showGif ? ("or " + MIME_TYPE + "=?") : ""));
43 | String[] selectionArgs;
44 | if (showGif) {
45 | selectionArgs = new String[]{IMAGE_JPEG, IMAGE_PNG, IMAGE_WEBP,IMAGE_GIF};
46 | } else {
47 | selectionArgs = new String[]{IMAGE_JPEG, IMAGE_PNG,IMAGE_WEBP};
48 | }
49 | setSelectionArgs(selectionArgs);
50 | }
51 |
52 |
53 | private PhotoDirectoryLoader(Context context, Uri uri, String[] projection, String selection,
54 | String[] selectionArgs, String sortOrder) {
55 | super(context, uri, projection, selection, selectionArgs, sortOrder);
56 | }
57 |
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/loader/VideoCursorLoader.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.loader;
2 |
3 | import android.net.Uri;
4 | import android.provider.MediaStore;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.annotation.Nullable;
8 |
9 | import static android.provider.MediaStore.MediaColumns.MIME_TYPE;
10 |
11 | /**
12 | * Created by Awen
13 | */
14 | public class VideoCursorLoader {
15 |
16 | @NonNull
17 | private Uri uri;
18 | @Nullable
19 | private String[] projection;
20 | @Nullable
21 | private String selection;
22 | @Nullable
23 | private String[] selectionArgs;
24 | @Nullable
25 | private String sortOrder;
26 |
27 | public VideoCursorLoader() {
28 | String[] PROJECTION = {
29 | MediaStore.Video.Media._ID,
30 | MediaStore.Video.Media.DATA,
31 | MediaStore.Video.Media.BUCKET_ID,
32 | MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
33 | MediaStore.Video.Media.DATE_ADDED,
34 | MediaStore.Video.Media.SIZE,
35 | MediaStore.Video.Media.MIME_TYPE,
36 | MediaStore.Video.Media.WIDTH,
37 | MediaStore.Video.Media.HEIGHT,
38 | MediaStore.Video.Media.DURATION,
39 | };
40 |
41 | //default ,默认配置
42 | setUri(MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
43 | setProjection(PROJECTION);
44 | setSelectionArgs(selectionArgs);
45 | setSortOrder(MediaStore.Video.Media.DATE_ADDED + " DESC");
46 | }
47 |
48 | public VideoCursorLoader(@NonNull Uri uri, @Nullable String[] projection,
49 | @Nullable String selection, @Nullable String[] selectionArgs,
50 | @Nullable String sortOrder) {
51 | this.uri = uri;
52 | this.projection = projection;
53 | this.selection = selection;
54 | this.selectionArgs = selectionArgs;
55 | this.sortOrder = sortOrder;
56 | }
57 |
58 | @NonNull
59 | public Uri getUri() {
60 | return uri;
61 | }
62 |
63 | public void setUri(@NonNull Uri uri) {
64 | this.uri = uri;
65 | }
66 |
67 | @Nullable
68 | public String[] getProjection() {
69 | return projection;
70 | }
71 |
72 | public void setProjection(@Nullable String[] projection) {
73 | this.projection = projection;
74 | }
75 |
76 | @Nullable
77 | public String getSelection() {
78 | return selection;
79 | }
80 |
81 | public void setSelection(@Nullable String selection) {
82 | this.selection = selection;
83 | }
84 |
85 | @Nullable
86 | public String[] getSelectionArgs() {
87 | return selectionArgs;
88 | }
89 |
90 | public void setSelectionArgs(@Nullable String[] selectionArgs) {
91 | this.selectionArgs = selectionArgs;
92 | }
93 |
94 | @Nullable
95 | public String getSortOrder() {
96 | return sortOrder;
97 | }
98 |
99 | public void setSortOrder(@Nullable String sortOrder) {
100 | this.sortOrder = sortOrder;
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/ui/ClipPictureActivity.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.ui;
2 |
3 | import android.content.Intent;
4 | import android.graphics.Bitmap;
5 | import android.os.Bundle;
6 | import android.view.Menu;
7 | import android.view.MenuItem;
8 |
9 | import com.awen.photo.FrescoBaseActivity;
10 | import com.awen.photo.R;
11 | import com.awen.photo.photopick.util.AppPathUtil;
12 | import com.awen.photo.photopick.util.ImageUtils;
13 | import com.awen.photo.photopick.widget.clipimage.ClipImageLayout;
14 |
15 | /**
16 | * 用户头像裁剪
17 | *
18 | * @author Homk-M
19 | */
20 | public class ClipPictureActivity extends FrescoBaseActivity {
21 |
22 | public static final String CLIPED_PHOTO_PATH = "cliped_photo_path"; // 已经裁剪的图片地址
23 | public static final String USER_PHOTO_PATH = "user_photo_path"; // 选择头像的本地地址
24 | private ClipImageLayout mClipImageLayout;
25 | private String photoPath;
26 | private Bitmap bitmap;
27 |
28 | @Override
29 | protected void onCreate(Bundle arg0) {
30 | super.onCreate(arg0);
31 | setContentView(R.layout.activity_clipicture);
32 | toolbar.setTitle(R.string.clip);
33 | photoPath = getIntent().getStringExtra(USER_PHOTO_PATH);
34 | if (photoPath == null) {
35 | finish();
36 | return;
37 | }
38 | int start = photoPath.lastIndexOf("/");
39 | bitmap = ImageUtils.getBitmap(photoPath, 480, 480);
40 |
41 | mClipImageLayout = (ClipImageLayout) findViewById(R.id.id_clipImageLayout);
42 | mClipImageLayout.init(this, bitmap);
43 |
44 | }
45 |
46 | @Override
47 | public boolean onCreateOptionsMenu(Menu menu) {
48 | getMenuInflater().inflate(R.menu.menu_ok, menu);
49 | MenuItem item = menu.findItem(R.id.ok);
50 | item.setTitle(R.string.clip);
51 | return true;
52 | }
53 |
54 | @Override
55 | public boolean onOptionsItemSelected(MenuItem item) {
56 | if (item.getItemId() == R.id.ok) {// 裁剪
57 | Bitmap bitmap = mClipImageLayout.clip();
58 | // 保存图片
59 | String photoName = String.valueOf(System.currentTimeMillis());
60 | // photoName = photoPath.substring(start + 1, photoPath.length());
61 | photoPath = AppPathUtil.getClipPhotoPath() + photoName;
62 | boolean isSaveStatu = ImageUtils.saveImage2(photoPath, bitmap);
63 | bitmap.recycle();
64 | Intent intent = new Intent();
65 | if (isSaveStatu) {
66 | intent.putExtra(CLIPED_PHOTO_PATH, photoPath);
67 | }
68 | setResult(RESULT_OK, intent);
69 | finish();
70 | return true;
71 | }
72 | return super.onOptionsItemSelected(item);
73 | }
74 |
75 | @Override
76 | protected void onDestroy() {
77 | super.onDestroy();
78 | if (bitmap != null && !bitmap.isRecycled()) {
79 | bitmap.recycle();
80 | }
81 | }
82 |
83 | @Override
84 | public void finish() {
85 | super.finish();
86 | overridePendingTransition(0, R.anim.bottom_out);
87 | }
88 | }
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/ui/VideoPlayActivity.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.ui;
2 |
3 | import android.net.Uri;
4 | import android.os.Bundle;
5 | import android.text.TextUtils;
6 | import android.util.Log;
7 | import android.widget.MediaController;
8 | import android.widget.Toast;
9 | import android.widget.VideoView;
10 |
11 | import androidx.annotation.Nullable;
12 |
13 | import com.awen.photo.FrescoBaseActivity;
14 | import com.awen.photo.R;
15 |
16 | /**
17 | * 使用videoView进行视频的简单播放,通过intent接收“videoUrl”,
18 | * 单独显示一个本地视频或网络视频都可以通过intent传递进来
19 | */
20 | public class VideoPlayActivity extends FrescoBaseActivity {
21 |
22 | @Override
23 | protected void onCreate(@Nullable Bundle arg0) {
24 | setOpenToolBar(false);
25 | super.onCreate(arg0);
26 | String videoUrl = getIntent().getStringExtra("videoUrl");
27 | if (TextUtils.isEmpty(videoUrl)) {
28 | Log.e("VideoPlayActivity", "videoUrl must not null");
29 | finish();
30 | return;
31 | }
32 | setContentView(R.layout.activity_video_play);
33 | toolbar = findViewById(R.id.toolbar);
34 | toolbar.setTitle("");
35 | setSupportActionBar(toolbar);
36 | VideoView videoView = findViewById(R.id.videoView);
37 | videoView.setVideoURI(Uri.parse(videoUrl));
38 | MediaController mediaController = new MediaController(this);
39 | videoView.setMediaController(mediaController);
40 | mediaController.setMediaPlayer(videoView);
41 | videoView.requestFocus();
42 | videoView.start();
43 | }
44 |
45 | @Override
46 | public void finish() {
47 | super.finish();
48 | overridePendingTransition(0, R.anim.image_pager_exit_animation);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/ui/VideoPlayLayout.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.ui;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.view.Gravity;
7 | import android.view.View;
8 | import android.widget.FrameLayout;
9 | import android.widget.ImageView;
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 |
13 | import com.awen.photo.FrescoImageLoader;
14 | import com.awen.photo.R;
15 |
16 | public class VideoPlayLayout extends FrameLayout {
17 |
18 | private Context context;
19 | private String videoPath;
20 |
21 | public VideoPlayLayout(@NonNull Context context) {
22 | super(context);
23 | init(context);
24 | }
25 |
26 | public VideoPlayLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
27 | super(context, attrs);
28 | init(context);
29 | }
30 |
31 | public VideoPlayLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
32 | super(context, attrs, defStyleAttr);
33 | init(context);
34 | }
35 |
36 | private void init(Context context) {
37 | this.context = context;
38 | }
39 |
40 | private void addPlayButton() {
41 | ImageView imageView = new ImageView(context);
42 | imageView.setImageResource(R.mipmap.video_play_icon);
43 | imageView.setScaleType(ImageView.ScaleType.FIT_XY);
44 | LayoutParams params = new LayoutParams(200, 200);
45 | params.gravity = Gravity.CENTER;
46 | imageView.setLayoutParams(params);
47 | addView(imageView);
48 | imageView.setOnClickListener(new OnClickListener() {
49 | @Override
50 | public void onClick(View v) {
51 | FrescoImageLoader.startVideoActivity((Activity) context,videoPath);
52 | }
53 | });
54 | }
55 |
56 | public void setData(View photoDraweeView, String videoPath) {
57 | this.videoPath = videoPath;
58 | addView(photoDraweeView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
59 | addPlayButton();
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/util/AppPathUtil.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.util;
2 |
3 | import android.os.Environment;
4 | import android.text.TextUtils;
5 |
6 | import com.awen.photo.Awen;
7 | import com.awen.photo.R;
8 |
9 | import java.io.File;
10 |
11 | /**
12 | * Created by Awen
13 | */
14 | public class AppPathUtil {
15 |
16 | /**
17 | * 裁剪头像
18 | *
19 | * @return
20 | */
21 | public static String getClipPhotoPath() {
22 | return getPath("clip");
23 | }
24 |
25 | /**
26 | * 保存大图到本地的路径地址
27 | *
28 | * @return String
29 | */
30 | public static String getBigBitmapCachePath() {
31 | return getPath("Photo");
32 | }
33 |
34 | private static String getPath(String str) {
35 | String path = null;
36 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
37 | path = Environment.getExternalStorageDirectory().getPath();
38 | }
39 | if (TextUtils.isEmpty(path)) {
40 | path = Awen.getContext().getCacheDir().getPath();
41 | }
42 | //地址如下:path/appname/appname_photo/
43 | String app_root_name = Awen.getContext().getString(R.string.app_root_name);
44 | path = path + File.separator + app_root_name + File.separator + app_root_name + "_" + str + File.separator;
45 | exitesFolder(path);
46 | return path;
47 | }
48 |
49 | /**
50 | * 判断文件夹是否存在,不存在则创建
51 | *
52 | * @param path
53 | */
54 | public static void exitesFolder(String path) {
55 | File file = new File(path);
56 | if (!file.exists()) {
57 | file.mkdirs();
58 | }
59 | }
60 |
61 | public static String getFileName(String url) {
62 | String fileName = url.substring(url.lastIndexOf("/") + 1, url.length());
63 | if (!fileName.endsWith(".jpg") && !fileName.endsWith(".png") && !fileName.endsWith(".jpeg") && !fileName.endsWith(".gif") && !fileName.endsWith(".webp")) {
64 | //防止有些图片没有后缀名
65 | fileName = fileName + ".jpg";
66 | }
67 | return fileName;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/util/BitmapUtil.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.util;
2 |
3 | import android.graphics.BitmapFactory;
4 | import android.util.Log;
5 |
6 | import com.awen.photo.photopick.bean.Photo;
7 |
8 | /**
9 | * Created by Awen
10 | */
11 | public class BitmapUtil {
12 | /**
13 | * 检查文件是否损坏
14 | * Check if the file is corrupted
15 | *
16 | * @return false为不损坏,true为损坏
17 | */
18 | public static boolean checkImgCorrupted(String filePath) {
19 | BitmapFactory.Options options = new BitmapFactory.Options();
20 | options.inJustDecodeBounds = true;
21 | BitmapFactory.decodeFile(filePath, options);
22 | return (options.mCancel || options.outWidth == -1 || options.outHeight == -1);
23 | }
24 |
25 | /**
26 | * 检查文件是否损坏,并且给photo添加上长宽
27 | * Check if the file is corrupted
28 | *
29 | * @return false为不损坏,true为损坏
30 | */
31 | @Deprecated
32 | public static boolean checkImgCorrupted(Photo photo, String filePath) {
33 | BitmapFactory.Options options = new BitmapFactory.Options();
34 | options.inJustDecodeBounds = true;
35 | BitmapFactory.decodeFile(filePath, options);
36 |
37 | if(options.mCancel || options.outWidth == -1 || options.outHeight == -1){
38 | return true;
39 | }else {
40 | photo.setWidth(options.outWidth);
41 | photo.setHeight(options.outHeight);
42 | return false;
43 | }
44 | }
45 |
46 | public static int[] getImageSize(String filePath) {
47 | BitmapFactory.Options options = new BitmapFactory.Options();
48 | options.inJustDecodeBounds = true;
49 | BitmapFactory.decodeFile(filePath, options);
50 | return new int[]{options.outWidth,options.outHeight};
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/util/FileSizeUtil.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.util;
2 |
3 | import java.text.DecimalFormat;
4 |
5 | /**
6 | * Created by Awen
7 | */
8 |
9 | public class FileSizeUtil {
10 |
11 | private static DecimalFormat df = new DecimalFormat("#.00");
12 | /**
13 | * 转换文件大小
14 | */
15 | public static String formatFileSize(long fileS) {
16 | if (fileS == 0) {
17 | return "0B";
18 | }
19 | String fileSizeString;
20 | if (fileS < 1024) {
21 | fileSizeString = df.format((double) fileS) + "B";
22 | } else if (fileS < 1048576) {
23 | fileSizeString = df.format((double) fileS / 1024) + "KB";
24 | } else if (fileS < 1073741824) {
25 | fileSizeString = df.format((double) fileS / 1048576) + "MB";
26 | } else {
27 | fileSizeString = df.format((double) fileS / 1073741824) + "GB";
28 | }
29 | return fileSizeString;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/util/PermissionUtil.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.util;
2 |
3 | import android.app.Activity;
4 | import android.content.DialogInterface;
5 | import android.content.Intent;
6 | import android.content.pm.PackageManager;
7 | import android.net.Uri;
8 | import android.os.Build;
9 | import android.provider.Settings;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.appcompat.app.AlertDialog;
13 | import androidx.core.app.ActivityCompat;
14 |
15 | import com.awen.photo.R;
16 |
17 |
18 | /**
19 | * Created by Awen
20 | */
21 | public class PermissionUtil {
22 |
23 | public static final String TAG = "PermissionUtil";
24 | public static final int REQUEST_CODE_ASK_PERMISSIONS = 100;
25 |
26 | public static boolean checkPermission(Activity activity, String permission) {
27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
28 | int storagePermission = ActivityCompat.checkSelfPermission(activity, permission);
29 | if (storagePermission != PackageManager.PERMISSION_GRANTED) {
30 | return false;
31 | }
32 | }
33 | return true;
34 | }
35 |
36 |
37 | public static void showPermissionDialog(final Activity activity, String permission) {
38 | if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
39 | ActivityCompat.requestPermissions(activity, new String[]{permission},
40 | PermissionUtil.REQUEST_CODE_ASK_PERMISSIONS);
41 | return;
42 | }
43 | ActivityCompat.requestPermissions(activity, new String[]{permission},
44 | PermissionUtil.REQUEST_CODE_ASK_PERMISSIONS);
45 | }
46 |
47 | public static void showSystemSettingDialog(@NonNull final Activity activity, @NonNull String tips){
48 | new AlertDialog.Builder(activity)
49 | .setMessage(tips)
50 | .setNegativeButton(R.string.cancel, null)
51 | .setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
52 | @Override
53 | public void onClick(DialogInterface dialog, int which) {
54 | startSystemSettingActivity(activity);
55 | }
56 | })
57 | .show();
58 | }
59 |
60 | public static void startSystemSettingActivity(@NonNull Activity activity){
61 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
62 | intent.setData(Uri.parse("package:" + activity.getPackageName()));
63 | activity.startActivity(intent);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/util/ViewUtil.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.util;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Point;
6 | import android.os.Build;
7 | import android.util.DisplayMetrics;
8 | import android.view.Display;
9 | import android.view.KeyCharacterMap;
10 | import android.view.KeyEvent;
11 | import android.view.ViewConfiguration;
12 |
13 | /**
14 | * Created by Awen
15 | */
16 |
17 | public class ViewUtil {
18 |
19 | public static boolean isNavigationBarShow(Activity activity){
20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
21 | Display display = activity.getWindowManager().getDefaultDisplay();
22 | Point size = new Point();
23 | Point realSize = new Point();
24 | display.getSize(size);
25 | display.getRealSize(realSize);
26 | return realSize.y!=size.y;
27 | }else {
28 | boolean menu = ViewConfiguration.get(activity).hasPermanentMenuKey();
29 | boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
30 | return menu || back;
31 | }
32 | }
33 |
34 | public static int getNavigationBarHeight(Activity activity) {
35 | if (!isNavigationBarShow(activity)){
36 | return 0;
37 | }
38 | return activity.getResources().getDimensionPixelSize(activity.getResources().getIdentifier("navigation_bar_height","dimen", "android"));
39 | }
40 |
41 |
42 | public static int getSceenHeight(Activity activity) {
43 | return activity.getWindowManager().getDefaultDisplay().getHeight()+getNavigationBarHeight(activity);
44 | }
45 |
46 | public static int getDisplayHeight(Context context){
47 | return context.getResources().getDisplayMetrics().heightPixels;
48 | }
49 |
50 | public static int[] getDisplayWidthAndHeight(Context context){
51 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
52 | return new int[]{displayMetrics.widthPixels,displayMetrics.heightPixels};
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/HackyViewPager.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.Log;
6 | import android.view.MotionEvent;
7 |
8 | import androidx.viewpager.widget.ViewPager;
9 |
10 | /**
11 | * Hacky fix for Issue #4 and
12 | * http://code.google.com/p/android/issues/detail?id=18990
13 | *
14 | * ScaleGestureDetector seems to mess up the touch events, which means that
15 | * ViewGroups which make use of onInterceptTouchEvent throw a lot of
16 | * IllegalArgumentException: pointerIndex out of range.
17 | *
18 | * There's not much I can do in my code for now, but we can mask the result by
19 | * just catching the problem and ignoring it.
20 | */
21 | public class HackyViewPager extends ViewPager {
22 |
23 | private static final String TAG = "HackyViewPager";
24 |
25 | public HackyViewPager(Context context) {
26 | super(context);
27 | }
28 |
29 | public HackyViewPager(Context context, AttributeSet attrs) {
30 | super(context, attrs);
31 | }
32 |
33 | @Override
34 | public boolean onInterceptTouchEvent(MotionEvent ev) {
35 | try {
36 | return super.onInterceptTouchEvent(ev);
37 | } catch (IllegalArgumentException e) {
38 | // 不理会
39 | Log.e(TAG, "hacky viewpager error1");
40 | return false;
41 | } catch (ArrayIndexOutOfBoundsException e) {
42 | // 不理会
43 | Log.e(TAG, "hacky viewpager error2");
44 | return false;
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/RoundProgressBar.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.ColorFilter;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.graphics.RectF;
8 | import android.graphics.Typeface;
9 | import android.graphics.drawable.Drawable;
10 | import android.util.Log;
11 |
12 | import com.facebook.drawee.drawable.DrawableUtils;
13 |
14 |
15 | /**
16 | * 圆形进度条
17 | * @author Homk-M
18 | */
19 | public class RoundProgressBar extends Drawable {
20 | private static final String TAG = "RoundProgressBar";
21 | private Paint mRingBackgroundPaint;
22 | private int mRingBackgroundColor;
23 | // 画圆环的画笔
24 | private Paint mRingPaint;
25 | // 圆环颜色
26 | private int mRingColor;
27 | // 半径
28 | private float mRadius;
29 | // 圆环半径
30 | private float mRingRadius;
31 | // 圆环宽度
32 | private float mStrokeWidth;
33 | // 圆心x坐标
34 | private int mXCenter;
35 | // 圆心y坐标
36 | private int mYCenter;
37 | // 总进度
38 | private int mTotalProgress = 10000;
39 | // 当前进度
40 | private int mProgress;
41 |
42 | private Paint textPaint;
43 | //是否显示中间的百分比进度
44 | private boolean textIsDisplayable;
45 | private int textSize;
46 | private int textColor;
47 |
48 | public RoundProgressBar() {
49 | initAttrs();
50 | }
51 |
52 | private void initAttrs() {
53 | textIsDisplayable = true;
54 | textSize = 24;
55 | textColor = 0xFFEBAC0E;
56 | mRadius = 60;
57 | mStrokeWidth = 16;
58 | mRingBackgroundColor = 0xFFdddad7;
59 | mRingColor = 0xFFEBAC0E;
60 | mRingRadius = mRadius + mStrokeWidth / 2;
61 | initVariable();
62 | }
63 |
64 | private void initVariable() {
65 | mRingBackgroundPaint = new Paint();
66 | mRingBackgroundPaint.setAntiAlias(true);
67 | mRingBackgroundPaint.setColor(mRingBackgroundColor);
68 | mRingBackgroundPaint.setStyle(Paint.Style.STROKE);
69 | mRingBackgroundPaint.setStrokeWidth(mStrokeWidth);
70 |
71 | mRingPaint = new Paint();
72 | mRingPaint.setAntiAlias(true);
73 | mRingPaint.setColor(mRingColor);
74 | mRingPaint.setStyle(Paint.Style.STROKE);
75 | mRingPaint.setStrokeWidth(mStrokeWidth);
76 |
77 | textPaint = new Paint();
78 | textPaint.setStrokeWidth(0);
79 | textPaint.setColor(textColor);
80 | textPaint.setTextSize(textSize);
81 | textPaint.setTypeface(Typeface.DEFAULT_BOLD); // 设置字体
82 | }
83 |
84 | @Override
85 | public void draw(Canvas canvas) {
86 | drawBar(canvas, mTotalProgress, mRingBackgroundPaint);
87 | drawBar(canvas, mProgress, mRingPaint);
88 | drawText(canvas, mProgress, textPaint);
89 | }
90 |
91 | private void drawBar(Canvas canvas, int level, Paint paint) {
92 | if (level > 0) {
93 | Rect bound = getBounds();
94 | mXCenter = bound.centerX();
95 | mYCenter = bound.centerY();
96 | RectF oval = new RectF();
97 | oval.left = (mXCenter - mRingRadius);
98 | oval.top = (mYCenter - mRingRadius);
99 | oval.right = mRingRadius * 2 + (mXCenter - mRingRadius);
100 | oval.bottom = mRingRadius * 2 + (mYCenter - mRingRadius);
101 | canvas.drawArc(oval, -90, ((float) level / mTotalProgress) * 360, false, paint); //
102 | }
103 | }
104 |
105 | /**
106 | * 画进度百分比
107 | */
108 | private void drawText(Canvas canvas, int level, Paint paint) {
109 | if (level > 0 && textIsDisplayable) {
110 | Rect bound = getBounds();
111 | mXCenter = bound.centerX();
112 | mYCenter = bound.centerY();
113 | int percent = (int) (((float) level / (float) mTotalProgress) * 100);// 中间的进度百分比,先转换成float在进行除法运算,不然都为0
114 | float textWidth = paint.measureText(percent + "%"); // 测量字体宽度,我们需要根据字体的宽度设置在圆环中间
115 | if (percent != 0) {
116 | canvas.drawText(percent + "%", mXCenter - textWidth / 2, mYCenter + textSize / 2, paint); // 画出进度百分比
117 | }
118 | // Log.e(TAG,"percent = " + percent);
119 | // Log.e(TAG,"x = " + (mXCenter - textWidth / 2));
120 | }
121 | }
122 |
123 | @Override
124 | protected boolean onLevelChange(int level) {
125 | mProgress = level;
126 | if (level > 0 && level < 10000) {
127 | invalidateSelf();
128 | return true;
129 | } else {
130 | return false;
131 | }
132 | }
133 |
134 | @Override
135 | public void setAlpha(int alpha) {
136 | mRingPaint.setAlpha(alpha);
137 | }
138 |
139 | @Override
140 | public void setColorFilter(ColorFilter cf) {
141 | mRingPaint.setColorFilter(cf);
142 | }
143 |
144 | @Override
145 | public int getOpacity() {
146 | return DrawableUtils.getOpacityFromColor(this.mRingPaint.getColor());
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/RoundProgressBarDrawable.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.ColorFilter;
5 | import android.graphics.Paint;
6 | import android.graphics.PixelFormat;
7 | import android.graphics.Rect;
8 | import android.graphics.RectF;
9 | import android.graphics.drawable.Drawable;
10 |
11 | import androidx.annotation.IntRange;
12 | import androidx.annotation.NonNull;
13 | import androidx.annotation.Nullable;
14 |
15 | /**
16 | * Created by Awen
17 | */
18 |
19 | public class RoundProgressBarDrawable extends Drawable {
20 | private long maxValue = 10000;
21 | private long progress;
22 | private Paint circlePaint;
23 | private float circleWidth;
24 | private int circleProgressColor;
25 | private int circleBottomColor;
26 | private int circleBottomWidth;
27 | private int circleRadius;
28 | private int interval;//两个圆之间的间隔
29 |
30 | public RoundProgressBarDrawable() {
31 | this(70);
32 | }
33 |
34 | public RoundProgressBarDrawable(int circleRadius) {
35 | circlePaint = new Paint();
36 | circlePaint.setAntiAlias(true);
37 | circlePaint.setStrokeWidth(10);
38 | circlePaint.setStrokeCap(Paint.Cap.ROUND);
39 | circleProgressColor = 0xdddddddd;
40 | circleBottomColor = 0xdddddddd;
41 | circleWidth = 8;
42 | this.circleRadius = circleRadius;
43 | interval = 5;
44 | circleBottomWidth = 2;
45 | progress = 1;
46 | }
47 |
48 | /*
49 | * 话空心圆
50 | */
51 | private void drawCircle(Canvas canvas) {
52 | Rect bounds = getBounds();
53 | int xPos = bounds.left + (bounds.width() >> 1);
54 | int yPos = bounds.bottom - (bounds.height() >> 1);
55 | circlePaint.setColor(circleBottomColor);
56 | circlePaint.setStrokeWidth(circleBottomWidth);
57 | circlePaint.setStyle(Paint.Style.STROKE);
58 | circlePaint.setShader(null);
59 | canvas.drawCircle(xPos, yPos, circleRadius + interval, circlePaint);
60 | }
61 |
62 | /*
63 | * 根据进度条画实心圆
64 | */
65 | private void drawArc(Canvas canvas) {
66 | circlePaint.setStyle(Paint.Style.FILL);
67 | Rect bounds = getBounds();
68 | int xPos = bounds.left + (bounds.width() >> 1);
69 | int yPos = bounds.bottom - (bounds.height() >> 1);
70 | RectF rectF = new RectF(xPos - circleRadius, yPos - circleRadius, xPos + circleRadius, yPos + circleRadius);
71 | float degree = (float) progress / (float) maxValue * 360;
72 | circlePaint.setStrokeWidth(circleWidth);
73 | circlePaint.setColor(circleProgressColor);
74 | canvas.drawArc(rectF, 270, degree, true, circlePaint);
75 | }
76 |
77 | @Override
78 | public void draw(@NonNull Canvas canvas) {
79 | if ((int) (((float) progress / (float) maxValue) * 100) == 100) {
80 | return;
81 | }
82 | drawCircle(canvas);
83 | drawArc(canvas);
84 | }
85 |
86 | @Override
87 | public void setAlpha(@IntRange(from = 0, to = 255) int i) {
88 |
89 | }
90 |
91 | @Override
92 | public void setColorFilter(@Nullable ColorFilter colorFilter) {
93 |
94 | }
95 |
96 | @Override
97 | public int getOpacity() {
98 | return PixelFormat.UNKNOWN;
99 | }
100 |
101 | @Override
102 | protected boolean onLevelChange(int level) {
103 | long origin = progress;
104 | progress = level;
105 | if (progress != 0 && origin != progress) {
106 | invalidateSelf();
107 | return true;
108 | } else {
109 | return false;
110 | }
111 | }
112 |
113 | public RoundProgressBarDrawable setMaxValue(long value) {
114 | this.maxValue = value;
115 | return this;
116 | }
117 |
118 | public RoundProgressBarDrawable setCircleWidth(float circleWidth) {
119 | this.circleWidth = circleWidth;
120 | return this;
121 | }
122 |
123 | public RoundProgressBarDrawable setCircleBottomWidth(int circleBottomWidth) {
124 | this.circleBottomWidth = circleBottomWidth;
125 | return this;
126 | }
127 |
128 | public RoundProgressBarDrawable setCircleRadius(int circleRadius) {
129 | this.circleRadius = circleRadius;
130 | return this;
131 | }
132 |
133 | public RoundProgressBarDrawable setInterval(int interval) {
134 | this.interval = interval;
135 | return this;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/clipimage/ClipImageBorderView.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.clipimage;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Paint.Style;
8 | import android.util.AttributeSet;
9 | import android.util.TypedValue;
10 | import android.view.View;
11 |
12 | /**
13 | * 自定义裁剪头像view
14 | *
15 | * @author Homk-M
16 | *
17 | */
18 | public class ClipImageBorderView extends View {
19 | /**
20 | * 水平方向与View的边距
21 | */
22 | private int mHorizontalPadding;
23 | /**
24 | * 垂直方向与View的边距
25 | */
26 | private int mVerticalPadding;
27 | /**
28 | * 绘制的矩形的宽度
29 | */
30 | private int mWidth;
31 | /**
32 | * 边框的颜色,默认为白色
33 | */
34 | private int mBorderColor = Color.parseColor("#FFFFFF");
35 | /**
36 | * 边框的宽度 单位dp
37 | */
38 | private int mBorderWidth = 1;
39 |
40 | private Paint mPaint;
41 |
42 | public ClipImageBorderView(Context context) {
43 | this(context, null);
44 | }
45 |
46 | public ClipImageBorderView(Context context, AttributeSet attrs) {
47 | this(context, attrs, 0);
48 | }
49 |
50 | public ClipImageBorderView(Context context, AttributeSet attrs, int defStyle) {
51 | super(context, attrs, defStyle);
52 |
53 | mBorderWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mBorderWidth, getResources().getDisplayMetrics());
54 | mPaint = new Paint();
55 | mPaint.setAntiAlias(true);
56 | }
57 |
58 | @Override
59 | protected void onDraw(Canvas canvas) {
60 | super.onDraw(canvas);
61 | // 计算矩形区域的宽度
62 | mWidth = getWidth() - 2 * mHorizontalPadding;
63 | // 计算距离屏幕垂直边界 的边距
64 | mVerticalPadding = (getHeight() - mWidth) / 2;
65 | mPaint.setColor(Color.parseColor("#aa000000"));
66 | mPaint.setStyle(Style.FILL);
67 | // 绘制左边1
68 | canvas.drawRect(0, 0, mHorizontalPadding, getHeight(), mPaint);
69 | // 绘制右边2
70 | canvas.drawRect(getWidth() - mHorizontalPadding, 0, getWidth(), getHeight(), mPaint);
71 | // 绘制上边3
72 | canvas.drawRect(mHorizontalPadding, 0, getWidth() - mHorizontalPadding, mVerticalPadding, mPaint);
73 | // 绘制下边4
74 | canvas.drawRect(mHorizontalPadding, getHeight() - mVerticalPadding, getWidth() - mHorizontalPadding, getHeight(), mPaint);
75 | // 绘制外边框
76 | mPaint.setColor(mBorderColor);
77 | mPaint.setStrokeWidth(mBorderWidth);
78 | mPaint.setStyle(Style.STROKE);
79 | canvas.drawRect(mHorizontalPadding, mVerticalPadding, getWidth() - mHorizontalPadding, getHeight() - mVerticalPadding, mPaint);
80 |
81 | }
82 |
83 | public void setHorizontalPadding(int mHorizontalPadding) {
84 | this.mHorizontalPadding = mHorizontalPadding;
85 |
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/clipimage/ClipImageLayout.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.clipimage;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.util.AttributeSet;
6 | import android.util.TypedValue;
7 | import android.widget.RelativeLayout;
8 |
9 | /**
10 | * 裁剪头像整个layout
11 | *
12 | * @author Homk-M
13 | *
14 | */
15 | public class ClipImageLayout extends RelativeLayout {
16 |
17 | private ClipZoomImageView mZoomImageView;
18 |
19 | /**
20 | * 可以提取为自定义属性
21 | */
22 | private int mHorizontalPadding = 20;
23 |
24 | public ClipImageLayout(Context context, AttributeSet attrs) {
25 | super(context, attrs);
26 |
27 | }
28 |
29 | public void init(Context context, Bitmap bitmap) {
30 | mZoomImageView = new ClipZoomImageView(context);
31 | ClipImageBorderView mClipImageView = new ClipImageBorderView(context);
32 |
33 | android.view.ViewGroup.LayoutParams lp = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT,
34 | android.view.ViewGroup.LayoutParams.MATCH_PARENT);
35 |
36 | mZoomImageView.setImageBitmap(bitmap);
37 |
38 | this.addView(mZoomImageView, lp);
39 | this.addView(mClipImageView, lp);
40 |
41 | // 计算padding的px
42 | mHorizontalPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mHorizontalPadding, getResources()
43 | .getDisplayMetrics());
44 | mZoomImageView.setHorizontalPadding(mHorizontalPadding);
45 | mClipImageView.setHorizontalPadding(mHorizontalPadding);
46 | }
47 |
48 | /**
49 | * 对外公布设置边距的方法,单位为dp
50 | *
51 | * @param mHorizontalPadding
52 | */
53 | public void setHorizontalPadding(int mHorizontalPadding) {
54 | this.mHorizontalPadding = mHorizontalPadding;
55 | }
56 |
57 | /**
58 | * 裁切图片
59 | *
60 | * @return
61 | */
62 | public Bitmap clip() {
63 | return mZoomImageView.clip();
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/DefaultOnDoubleTapListener.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | import android.graphics.RectF;
4 | import android.view.GestureDetector;
5 | import android.view.MotionEvent;
6 |
7 | import com.facebook.drawee.generic.GenericDraweeHierarchy;
8 | import com.facebook.drawee.view.DraweeView;
9 |
10 | public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener {
11 |
12 | private Attacher mAttacher;
13 |
14 | public DefaultOnDoubleTapListener(Attacher attacher) {
15 | setPhotoDraweeViewAttacher(attacher);
16 | }
17 |
18 | @Override public boolean onSingleTapConfirmed(MotionEvent e) {
19 |
20 | if (mAttacher == null) {
21 | return false;
22 | }
23 | DraweeView draweeView = mAttacher.getDraweeView();
24 | if (draweeView == null) {
25 | return false;
26 | }
27 |
28 | if (mAttacher.getOnPhotoTapListener() != null) {
29 | final RectF displayRect = mAttacher.getDisplayRect();
30 |
31 | if (null != displayRect) {
32 | final float x = e.getX(), y = e.getY();
33 | if (displayRect.contains(x, y)) {
34 | float xResult = (x - displayRect.left) / displayRect.width();
35 | float yResult = (y - displayRect.top) / displayRect.height();
36 | mAttacher.getOnPhotoTapListener().onPhotoTap(draweeView, xResult, yResult);
37 | return true;
38 | }
39 | }
40 | }
41 |
42 | if (mAttacher.getOnViewTapListener() != null) {
43 | mAttacher.getOnViewTapListener().onViewTap(draweeView, e.getX(), e.getY());
44 | return true;
45 | }
46 |
47 | return false;
48 | }
49 |
50 | @Override public boolean onDoubleTap(MotionEvent event) {
51 | if (mAttacher == null) {
52 | return false;
53 | }
54 |
55 | try {
56 | float scale = mAttacher.getScale();
57 | float x = event.getX();
58 | float y = event.getY();
59 |
60 | if (scale < mAttacher.getMediumScale()) {
61 | mAttacher.setScale(mAttacher.getMediumScale(), x, y, true);
62 | } else if (scale >= mAttacher.getMediumScale() && scale < mAttacher.getMaximumScale()) {
63 | mAttacher.setScale(mAttacher.getMaximumScale(), x, y, true);
64 | } else {
65 | mAttacher.setScale(mAttacher.getMinimumScale(), x, y, true);
66 | }
67 | } catch (Exception e) {
68 | // Can sometimes happen when getX() and getY() is called
69 | }
70 | return true;
71 | }
72 |
73 | @Override public boolean onDoubleTapEvent(MotionEvent event) {
74 | return false;
75 | }
76 |
77 | public void setPhotoDraweeViewAttacher(Attacher attacher) {
78 | mAttacher = attacher;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/IAttacher.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | import android.view.GestureDetector;
4 | import android.view.View;
5 |
6 | /**
7 | * ****************************************************************************
8 | * Copyright 2011, 2012 Chris Banes.
9 | *
10 | * Licensed under the Apache License, Version 2.0 (the "License");
11 | * you may not use this file except in compliance with the License.
12 | * You may obtain a copy of the License at
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software
17 | * distributed under the License is distributed on an "AS IS" BASIS,
18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | * See the License for the specific language governing permissions and
20 | * limitations under the License.
21 | * *****************************************************************************
22 | */
23 |
24 | public interface IAttacher {
25 |
26 | public static final float DEFAULT_MAX_SCALE = 3.0f;
27 | public static final float DEFAULT_MID_SCALE = 1.75f;
28 | public static final float DEFAULT_MIN_SCALE = 1.0f;
29 | public static final long ZOOM_DURATION = 200L;
30 |
31 | float getMinimumScale();
32 |
33 | float getMediumScale();
34 |
35 | float getMaximumScale();
36 |
37 | void setMaximumScale(float maximumScale);
38 |
39 | void setMediumScale(float mediumScale);
40 |
41 | void setMinimumScale(float minimumScale);
42 |
43 | float getScale();
44 |
45 | void setScale(float scale);
46 |
47 | void setScale(float scale, boolean animate);
48 |
49 | void setScale(float scale, float focalX, float focalY, boolean animate);
50 |
51 | void setZoomTransitionDuration(long duration);
52 |
53 | void setAllowParentInterceptOnEdge(boolean allow);
54 |
55 | void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener listener);
56 |
57 | void setOnScaleChangeListener(OnScaleChangeListener listener);
58 |
59 | void setOnLongClickListener(View.OnLongClickListener listener);
60 |
61 | void setOnPhotoTapListener(OnPhotoTapListener listener);
62 |
63 | void setOnViewTapListener(OnViewTapListener listener);
64 |
65 | OnPhotoTapListener getOnPhotoTapListener();
66 |
67 | OnViewTapListener getOnViewTapListener();
68 |
69 | void update(int imageInfoWidth, int imageInfoHeight);
70 | }
71 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnPhotoTapListener.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Interface definition for a callback to be invoked when the Photo is tapped with a single
7 | * tap.
8 | *
9 | * @author Chris Banes
10 | */
11 | public interface OnPhotoTapListener {
12 |
13 | /**
14 | * A callback to receive where the user taps on a photo. You will only receive a callback if
15 | * the user taps on the actual photo, tapping on 'whitespace' will be ignored.
16 | *
17 | * @param view - View the user tapped.
18 | * @param x - where the user tapped from the of the Drawable, as percentage of the
19 | * Drawable width.
20 | * @param y - where the user tapped from the top of the Drawable, as percentage of the
21 | * Drawable height.
22 | */
23 | void onPhotoTap(View view, float x, float y);
24 | }
25 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnScaleChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | /**
4 | * Interface definition for callback to be invoked when attached ImageView scale changes
5 | *
6 | * @author Marek Sebera
7 | */
8 | public interface OnScaleChangeListener {
9 | /**
10 | * Callback for when the scale changes
11 | *
12 | * @param scaleFactor the scale factor (<1 for zoom out, >1 for zoom in)
13 | * @param focusX focal point X position
14 | * @param focusY focal point Y position
15 | */
16 | void onScaleChange(float scaleFactor, float focusX, float focusY);
17 | }
18 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnScaleDragGestureListener.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | /**
4 | * ****************************************************************************
5 | * Copyright 2011, 2012 Chris Banes.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * *****************************************************************************
19 | */
20 |
21 | public interface OnScaleDragGestureListener {
22 | void onDrag(float dx, float dy);
23 |
24 | void onFling(float startX, float startY, float velocityX, float velocityY);
25 |
26 | void onScale(float scaleFactor, float focusX, float focusY);
27 |
28 | void onScaleEnd();
29 | }
30 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnTouchEventAndScaleChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | import android.graphics.Matrix;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * 解决多指缩放和下拉冲突的,该接口主要用于用于ScalePhotoView,因为PhotoDraweeView它的多指操作,原图缩小的时候,onTouch会被我这里中断
8 | * Created by Awen
9 | */
10 |
11 | public interface OnTouchEventAndScaleChangeListener {
12 |
13 | void onPhotoTouchEvent(MotionEvent ev);
14 |
15 | void onPhotoScaleChange(float matrixScale);
16 |
17 | void onPhotoScaleEnd(float matrixScale);
18 | }
19 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/OnViewTapListener.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Interface definition for a callback to be invoked when the ImageView is tapped with a single
7 | * tap.
8 | *
9 | * @author Chris Banes
10 | */
11 | public interface OnViewTapListener {
12 | /**
13 | * A callback to receive where the user taps on a ImageView. You will receive a callback if
14 | * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.
15 | *
16 | * @param view - View the user tapped.
17 | * @param x - where the user tapped from the left of the View.
18 | * @param y - where the user tapped from the top of the View.
19 | */
20 | void onViewTap(View view, float x, float y);
21 | }
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/PhotoDraweeView.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.util.AttributeSet;
6 | import android.view.GestureDetector;
7 | import android.view.MotionEvent;
8 |
9 | import androidx.annotation.NonNull;
10 |
11 | import com.facebook.drawee.generic.GenericDraweeHierarchy;
12 | import com.facebook.drawee.view.SimpleDraweeView;
13 |
14 | /**
15 | * FaceBook的控件SimpleDraweeView对于图片的缩放不支持,这里修改下
16 | * Created by Awen
17 | */
18 | public class PhotoDraweeView extends SimpleDraweeView implements IAttacher {
19 |
20 | private Attacher mAttacher;
21 |
22 | public PhotoDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
23 | super(context, hierarchy);
24 | init();
25 | }
26 |
27 | public PhotoDraweeView(Context context) {
28 | super(context);
29 | init();
30 | }
31 |
32 | public PhotoDraweeView(Context context, AttributeSet attrs) {
33 | super(context, attrs);
34 | init();
35 | }
36 |
37 | public PhotoDraweeView(Context context, AttributeSet attrs, int defStyle) {
38 | super(context, attrs, defStyle);
39 | init();
40 | }
41 |
42 | protected void init() {
43 | if (mAttacher == null || mAttacher.getDraweeView() == null) {
44 | mAttacher = new Attacher(this);
45 | }
46 | }
47 |
48 | @Override public boolean onTouchEvent(MotionEvent event) {
49 |
50 | return super.onTouchEvent(event);
51 | }
52 |
53 | @Override protected void onDraw(@NonNull Canvas canvas) {
54 | int saveCount = canvas.save();
55 | canvas.concat(mAttacher.getDrawMatrix());
56 | super.onDraw(canvas);
57 | canvas.restoreToCount(saveCount);
58 | }
59 |
60 | @Override protected void onAttachedToWindow() {
61 | init();
62 | super.onAttachedToWindow();
63 | }
64 |
65 | @Override protected void onDetachedFromWindow() {
66 | mAttacher.onDetachedFromWindow();
67 | super.onDetachedFromWindow();
68 | }
69 |
70 | @Override public float getMinimumScale() {
71 | return mAttacher.getMinimumScale();
72 | }
73 |
74 | @Override public float getMediumScale() {
75 | return mAttacher.getMediumScale();
76 | }
77 |
78 | @Override public float getMaximumScale() {
79 | return mAttacher.getMaximumScale();
80 | }
81 |
82 | @Override public void setMinimumScale(float minimumScale) {
83 | mAttacher.setMinimumScale(minimumScale);
84 | }
85 |
86 | @Override public void setMediumScale(float mediumScale) {
87 | mAttacher.setMediumScale(mediumScale);
88 | }
89 |
90 | @Override public void setMaximumScale(float maximumScale) {
91 | mAttacher.setMaximumScale(maximumScale);
92 | }
93 |
94 | @Override public float getScale() {
95 | return mAttacher.getScale();
96 | }
97 |
98 | @Override public void setScale(float scale) {
99 | mAttacher.setScale(scale);
100 | }
101 |
102 | @Override public void setScale(float scale, boolean animate) {
103 | mAttacher.setScale(scale, animate);
104 | }
105 |
106 | @Override public void setScale(float scale, float focalX, float focalY, boolean animate) {
107 | mAttacher.setScale(scale, focalX, focalY, animate);
108 | }
109 |
110 | @Override public void setZoomTransitionDuration(long duration) {
111 | mAttacher.setZoomTransitionDuration(duration);
112 | }
113 |
114 | @Override public void setAllowParentInterceptOnEdge(boolean allow) {
115 | mAttacher.setAllowParentInterceptOnEdge(allow);
116 | }
117 |
118 | @Override public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener listener) {
119 | mAttacher.setOnDoubleTapListener(listener);
120 | }
121 |
122 | @Override public void setOnScaleChangeListener(OnScaleChangeListener listener) {
123 | mAttacher.setOnScaleChangeListener(listener);
124 | }
125 |
126 | @Override public void setOnLongClickListener(OnLongClickListener listener) {
127 | mAttacher.setOnLongClickListener(listener);
128 | }
129 |
130 | @Override public void setOnPhotoTapListener(OnPhotoTapListener listener) {
131 | mAttacher.setOnPhotoTapListener(listener);
132 | }
133 |
134 | @Override public void setOnViewTapListener(OnViewTapListener listener) {
135 | mAttacher.setOnViewTapListener(listener);
136 | }
137 |
138 | @Override public OnPhotoTapListener getOnPhotoTapListener() {
139 | return mAttacher.getOnPhotoTapListener();
140 | }
141 |
142 | @Override public OnViewTapListener getOnViewTapListener() {
143 | return mAttacher.getOnViewTapListener();
144 | }
145 |
146 | @Override public void update(int imageInfoWidth, int imageInfoHeight) {
147 | mAttacher.update(imageInfoWidth, imageInfoHeight);
148 | }
149 |
150 | public Attacher getmAttacher() {
151 | return mAttacher;
152 | }
153 |
154 | public void setOnTouchEventAndScaleChangeListener(OnTouchEventAndScaleChangeListener onTouchEventAndScaleChangeListener) {
155 | mAttacher.setOnTouchEventAndScaleChangeListener(onTouchEventAndScaleChangeListener);
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/java/com/awen/photo/photopick/widget/photodraweeview/ScaleDragDetector.java:
--------------------------------------------------------------------------------
1 | package com.awen.photo.photopick.widget.photodraweeview;
2 |
3 | import android.content.Context;
4 | import androidx.core.view.MotionEventCompat;
5 |
6 | import android.view.MotionEvent;
7 | import android.view.ScaleGestureDetector;
8 | import android.view.VelocityTracker;
9 | import android.view.ViewConfiguration;
10 |
11 | /**
12 | * ****************************************************************************
13 | * Copyright 2011, 2012 Chris Banes.
14 | *
15 | * Licensed under the Apache License, Version 2.0 (the "License");
16 | * you may not use this file except in compliance with the License.
17 | * You may obtain a copy of the License at
18 | *
19 | * http://www.apache.org/licenses/LICENSE-2.0
20 | *
21 | * Unless required by applicable law or agreed to in writing, software
22 | * distributed under the License is distributed on an "AS IS" BASIS,
23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 | * See the License for the specific language governing permissions and
25 | * limitations under the License.
26 | * *****************************************************************************
27 | */
28 | public class ScaleDragDetector implements ScaleGestureDetector.OnScaleGestureListener {
29 |
30 | private static final int INVALID_POINTER_ID = -1;
31 |
32 | private final float mTouchSlop;
33 | private final float mMinimumVelocity;
34 | private final ScaleGestureDetector mScaleDetector;
35 | private final OnScaleDragGestureListener mScaleDragGestureListener;
36 |
37 | private VelocityTracker mVelocityTracker;
38 | private boolean mIsDragging;
39 | float mLastTouchX;
40 | float mLastTouchY;
41 | private int mActivePointerId = INVALID_POINTER_ID;
42 | private int mActivePointerIndex = 0;
43 |
44 | public ScaleDragDetector(Context context, OnScaleDragGestureListener scaleDragGestureListener) {
45 | mScaleDetector = new ScaleGestureDetector(context, this);
46 | mScaleDragGestureListener = scaleDragGestureListener;
47 |
48 | final ViewConfiguration configuration = ViewConfiguration.get(context);
49 | mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
50 | mTouchSlop = configuration.getScaledTouchSlop();
51 | }
52 |
53 | @Override public boolean onScale(ScaleGestureDetector detector) {
54 | float scaleFactor = detector.getScaleFactor();
55 |
56 | if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) {
57 | return false;
58 | }
59 |
60 | mScaleDragGestureListener.onScale(scaleFactor, detector.getFocusX(), detector.getFocusY());
61 | return true;
62 | }
63 |
64 | @Override public boolean onScaleBegin(ScaleGestureDetector detector) {
65 | return true;
66 | }
67 |
68 | @Override public void onScaleEnd(ScaleGestureDetector detector) {
69 | mScaleDragGestureListener.onScaleEnd();
70 | }
71 |
72 | public boolean isScaling() {
73 | return mScaleDetector.isInProgress();
74 | }
75 |
76 | public boolean isDragging() {
77 | return mIsDragging;
78 | }
79 |
80 | private float getActiveX(MotionEvent ev) {
81 | try {
82 | return MotionEventCompat.getX(ev, mActivePointerIndex);
83 | } catch (Exception e) {
84 | return ev.getX();
85 | }
86 | }
87 |
88 | private float getActiveY(MotionEvent ev) {
89 | try {
90 | return MotionEventCompat.getY(ev, mActivePointerIndex);
91 | } catch (Exception e) {
92 | return ev.getY();
93 | }
94 | }
95 |
96 | public boolean onTouchEvent(MotionEvent ev) {
97 | mScaleDetector.onTouchEvent(ev);
98 | final int action = MotionEventCompat.getActionMasked(ev);
99 | onTouchActivePointer(action, ev);
100 | onTouchDragEvent(action, ev);
101 | return true;
102 | }
103 |
104 | private void onTouchActivePointer(int action, MotionEvent ev) {
105 | switch (action) {
106 | case MotionEvent.ACTION_DOWN:
107 | mActivePointerId = ev.getPointerId(0);
108 | break;
109 | case MotionEvent.ACTION_CANCEL:
110 | case MotionEvent.ACTION_UP:
111 | mActivePointerId = INVALID_POINTER_ID;
112 | break;
113 | case MotionEvent.ACTION_POINTER_UP:
114 | final int pointerIndex = MotionEventCompat.getActionIndex(ev);
115 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
116 | if (pointerId == mActivePointerId) {
117 | final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
118 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
119 | mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex);
120 | mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex);
121 | }
122 | break;
123 | }
124 | mActivePointerIndex = MotionEventCompat.findPointerIndex(ev,
125 | mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0);
126 | }
127 |
128 | private void onTouchDragEvent(int action, MotionEvent ev) {
129 | switch (action) {
130 | case MotionEvent.ACTION_DOWN: {
131 | mVelocityTracker = VelocityTracker.obtain();
132 | if (null != mVelocityTracker) {
133 | mVelocityTracker.addMovement(ev);
134 | }
135 |
136 | mLastTouchX = getActiveX(ev);
137 | mLastTouchY = getActiveY(ev);
138 | mIsDragging = false;
139 | break;
140 | }
141 |
142 | case MotionEvent.ACTION_MOVE: {
143 | final float x = getActiveX(ev);
144 | final float y = getActiveY(ev);
145 | final float dx = x - mLastTouchX, dy = y - mLastTouchY;
146 |
147 | if (!mIsDragging) {
148 | mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
149 | }
150 |
151 | if (mIsDragging) {
152 | mScaleDragGestureListener.onDrag(dx, dy);
153 | mLastTouchX = x;
154 | mLastTouchY = y;
155 |
156 | if (null != mVelocityTracker) {
157 | mVelocityTracker.addMovement(ev);
158 | }
159 | }
160 | break;
161 | }
162 |
163 | case MotionEvent.ACTION_CANCEL: {
164 | if (null != mVelocityTracker) {
165 | mVelocityTracker.recycle();
166 | mVelocityTracker = null;
167 | }
168 | break;
169 | }
170 |
171 | case MotionEvent.ACTION_UP: {
172 | if (mIsDragging) {
173 | if (null != mVelocityTracker) {
174 | mLastTouchX = getActiveX(ev);
175 | mLastTouchY = getActiveY(ev);
176 |
177 | mVelocityTracker.addMovement(ev);
178 | mVelocityTracker.computeCurrentVelocity(1000);
179 |
180 | final float vX = mVelocityTracker.getXVelocity(), vY =
181 | mVelocityTracker.getYVelocity();
182 |
183 | if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
184 | mScaleDragGestureListener.onFling(mLastTouchX, mLastTouchY, -vX, -vY);
185 | }
186 | }
187 | }
188 | if (null != mVelocityTracker) {
189 | mVelocityTracker.recycle();
190 | mVelocityTracker = null;
191 | }
192 | break;
193 | }
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/anim/bottom_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/anim/bottom_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/anim/image_pager_enter_animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
17 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/anim/image_pager_exit_animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/layout/activity_clipicture.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/layout/activity_photo_detail_pager.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/layout/activity_photo_pick.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
20 |
21 |
26 |
27 |
28 |
29 |
30 |
38 |
39 |
44 |
45 |
55 |
56 |
60 |
61 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/layout/activity_photo_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
28 |
29 |
38 |
39 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/layout/activity_video_play.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
23 |
24 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/layout/item_photo_gallery.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
22 |
23 |
31 |
32 |
37 |
38 |
44 |
45 |
46 |
54 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/layout/item_photo_pick.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
24 |
25 |
30 |
31 |
37 |
38 |
46 |
47 |
48 |
55 |
56 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/layout/toolbar_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/menu/menu_ok.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/menu/menu_pick.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xhdpi/failure_image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/failure_image.jpg
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xhdpi/gif_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/gif_icon.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xhdpi/select_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/select_icon.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xhdpi/take_photo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/take_photo.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xhdpi/video_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/video_icon.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xhdpi/video_play_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xhdpi/video_play_icon.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/photoLibrary/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values-v21/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 25dp
4 | 71dp
5 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values-v26/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values-v27/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values-v29/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #80000000
8 | #00000000
9 | #D9D9D9
10 | #80ffffff
11 | #00000000
12 |
13 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 0dp
7 | 48dp
8 |
9 | 4dp
10 | 2dp
11 | 5dp
12 | 8dp
13 | 16dp
14 | 22dp
15 | 24dp
16 | 28dp
17 | 32dp
18 | 64dp
19 | 70dp
20 | 1dp
21 | 2dp
22 | 4dp
23 | 6dp
24 | 8dp
25 | 12dp
26 | 16dp
27 | 22dp
28 | 24dp
29 | 28dp
30 | 32dp
31 | 64dp
32 |
33 |
34 | 10sp
35 | 12sp
36 | 14sp
37 | 15sp
38 | 16sp
39 | 18sp
40 | 22sp
41 | 26sp
42 | 40sp
43 | 6sp
44 |
45 | 40dp
46 | 16dp
47 | 20dp
48 | 24dp
49 | 32dp
50 | 40dp
51 | 48dp
52 | 56dp
53 |
54 |
55 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PhotoPickLibrary
3 |
4 | %s张
5 | %s个
6 | SD卡不存在,无法使用此功能
7 | 所有图片
8 | 所有视频
9 | 图片和视频
10 | 图片
11 | 视频
12 | 裁剪
13 | 确定
14 | 预览
15 | 保存图片
16 | 保存失败
17 | 已保存->图库查看,图片地址: \n %s
18 | 相册
19 | 加载中…
20 | 找不到图片
21 | 找不到视频
22 | 取消
23 | 选择
24 | 发送
25 | 发送(%s/%s)
26 | 原图(%s)
27 | 视频(%s)
28 |
29 |
30 | 缺少(相机)权限,无法使用该功能,点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限
31 | 缺少(麦克风)权限,无法使用该功能,点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限
32 | 缺少(麦克风或相机)权限,无法使用该功能,点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限
33 | 缺少(读取位置信息)权限,无法使用该功能,点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限
34 | 本App需要使用(存储)和(读取本机识别码)权限,您是否同意?点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限
35 | 本App需要使用(存储)权限,您是否同意?点击"设置"去开启此权限\n您可在“设置”>“应用”>“权限”中配置权限
36 | 设置
37 |
38 |
--------------------------------------------------------------------------------
/photoLibrary/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
18 |
19 |
23 |
24 |
--------------------------------------------------------------------------------
/pictrue/304079-052c8fd0c9d22efd.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/304079-052c8fd0c9d22efd.gif
--------------------------------------------------------------------------------
/pictrue/304079-8d726553c6c0b6ba.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/304079-8d726553c6c0b6ba.gif
--------------------------------------------------------------------------------
/pictrue/304079-e4c819f695ed83c0.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/304079-e4c819f695ed83c0.gif
--------------------------------------------------------------------------------
/pictrue/WechatIMG20.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/WechatIMG20.jpeg
--------------------------------------------------------------------------------
/pictrue/WechatIMG21.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/WechatIMG21.jpeg
--------------------------------------------------------------------------------
/pictrue/device-2017-10-25-033458.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/pictrue/device-2017-10-25-033458.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':photoLibrary', ':simaple'
2 |
--------------------------------------------------------------------------------
/simaple/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/simaple/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion rootProject.ext.android.compileSdkVersion
8 | buildToolsVersion rootProject.ext.android.buildToolsVersion
9 |
10 | defaultConfig {
11 | applicationId "com.simaple"
12 | minSdkVersion 16
13 | targetSdkVersion rootProject.ext.android.targetSdkVersion
14 | versionCode 1
15 | versionName "1.0"
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
28 | implementation project(':photoLibrary')
29 | implementation "androidx.appcompat:appcompat:1.2.0"
30 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
31 | //android6.0权限工具类
32 | implementation 'com.lovedise:permissiongen:0.1.1'
33 | }
34 |
--------------------------------------------------------------------------------
/simaple/keystore/keystore密码:
--------------------------------------------------------------------------------
1 | keystore密码:123456
2 | alias:photo
3 | password:123456
--------------------------------------------------------------------------------
/simaple/keystore/photo.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/keystore/photo.jks
--------------------------------------------------------------------------------
/simaple/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/Awen/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 | #保持 Parcelable 不被混淆
19 | -keep class * implements android.os.Parcelable {
20 | public static final android.os.Parcelable$Creator *;
21 | }
22 | # app javabean classes
23 | -keep class com.awen.photo.photopick.bean.** { *; }
24 |
25 | ##---------------6.0权限 start----------------------------------
26 | -keepattributes Annotation
27 | -keepclassmembers class ** {
28 | @kr.co.namee.permissiongen.PermissionSuccess public *;
29 | @kr.co.namee.permissiongen.PermissionFail public *;
30 | }
31 | ##---------------6.0权限 end----------------------------------
32 |
33 | ##---------------fresco start----------------------------------
34 | # Keep our interfaces so they can be used by other ProGuard rules.
35 | # See http://sourceforge.net/p/proguard/bugs/466/
36 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
37 |
38 | # Do not strip any method/class that is annotated with @DoNotStrip
39 | -keep @com.facebook.common.internal.DoNotStrip class *
40 | -keepclassmembers class * {
41 | @com.facebook.common.internal.DoNotStrip *;
42 | }
43 |
44 | # Keep native methods
45 | -keepclassmembers class * {
46 | native ;
47 | }
48 | ##---------------fresco end----------------------------------
49 |
--------------------------------------------------------------------------------
/simaple/simaple-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/simaple-release.apk
--------------------------------------------------------------------------------
/simaple/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/simaple/src/main/assets/test.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/src/main/assets/test.jpeg
--------------------------------------------------------------------------------
/simaple/src/main/java/com/simaple/App.java:
--------------------------------------------------------------------------------
1 | package com.simaple;
2 |
3 | import android.app.Application;
4 |
5 | import com.awen.photo.FrescoImageLoader;
6 |
7 | /**
8 | * Created by Awen
9 | */
10 |
11 | public class App extends Application {
12 |
13 | @Override
14 | public void onCreate() {
15 | super.onCreate();
16 | FrescoImageLoader.init(this);
17 | //下面是配置toolbar颜色和存储图片地址的
18 | // FrescoImageLoader.init(this,android.R.color.black);
19 | // FrescoImageLoader.init(this,android.R.color.holo_blue_light,"/storage/xxxx/xxx");
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/simaple/src/main/java/com/simaple/CustomInsetsRelativeLayout.java:
--------------------------------------------------------------------------------
1 | package com.simaple;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.widget.RelativeLayout;
8 |
9 | /**
10 | * 解决activity设置了状态栏透明,又设置了android:windowSoftInputMode="adjustResize"的时候输入框被输入法挡住的情况
11 | * 这个作为layout的根布局(root),并设置android:fitsSystemWindows="true"
12 | * Created by Awen
13 | */
14 | public class CustomInsetsRelativeLayout extends RelativeLayout {
15 | private int[] mInsets = new int[4];
16 |
17 | public CustomInsetsRelativeLayout(Context context) {
18 | super(context);
19 | }
20 |
21 | public CustomInsetsRelativeLayout(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 | public CustomInsetsRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
26 | super(context, attrs, defStyle);
27 | }
28 |
29 | public final int[] getInsets() {
30 | return mInsets;
31 | }
32 |
33 | @Override
34 | protected final boolean fitSystemWindows(Rect insets) {
35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
36 | // Intentionally do not modify the bottom inset. For some reason,
37 | // if the bottom inset is modified, window resizing stops working.
38 | // TODO: Figure out why.
39 |
40 | mInsets[0] = insets.left;
41 | mInsets[1] = insets.top;
42 | mInsets[2] = insets.right;
43 |
44 | insets.left = 0;
45 | insets.top = 0;
46 | insets.right = 0;
47 | }
48 |
49 | return super.fitSystemWindows(insets);
50 | }
51 | }
--------------------------------------------------------------------------------
/simaple/src/main/java/com/simaple/ImageProvider.java:
--------------------------------------------------------------------------------
1 | package com.simaple;
2 |
3 | import com.awen.photo.FrescoImageLoader;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * Created by Awen
10 | */
11 | public class ImageProvider {
12 |
13 | public static List getImageUrls(){
14 | List list = new ArrayList<>();
15 | list.add(FrescoImageLoader.getResUrl(R.mipmap.resx));//长图
16 | list.add(FrescoImageLoader.getAssetUrl("test.jpeg"));
17 | // //如果加载本地图片,可按照下面的来
18 | // list.add(FrescoImageLoader.getFileUrl("/storage/emulated/0/Pictures/Screenshots/Screenshot_20170919-203120.png"));
19 | // list.add(FrescoImageLoader.getFileUrl("/storage/emulated/0/tencent/MicroMsg/WeiXin/mmexport1505817817979.jpg"));
20 | //网络长图,包括横向和纵向的长图
21 | list.add("https://user-images.githubusercontent.com/39898537/61259085-3449aa80-a7ab-11e9-90bb-0359971e3d15.jpg");
22 | list.add("https://user-images.githubusercontent.com/39898537/61259086-34e24100-a7ab-11e9-9643-2ad2f50dee6f.jpg");
23 | list.add("https://user-images.githubusercontent.com/39898537/61259087-34e24100-a7ab-11e9-9c6c-6b2d8ffa43a1.jpg");
24 | list.add("https://wx2.sinaimg.cn/mw690/005MctNqgy1fx674gpkbvj30gf4k2wzp.jpg");
25 | list.add("https://wx2.sinaimg.cn/mw690/0062Xesrgy1fx8uu4ltdyj30j635g47x.jpg");
26 | list.add("https://wx1.sinaimg.cn/mw690/0062Xesrgy1fx8uu5d856j30j64v0h7z.jpg");
27 | list.add("https://wx3.sinaimg.cn/mw690/0062Xesrgy1fx8uu55neej30j63sq4le.jpg");
28 | list.add("https://wx1.sinaimg.cn/mw690/0062Xesrgy1fx8uu5p4stj30j66d6b29.jpg");
29 | list.add("https://wx3.sinaimg.cn/mw690/0062Xesrgy1fx8uu5tkicj30j65fu4qp.jpg");
30 | list.add("https://raw.githubusercontent.com/Awent/PhotoPick-Master/master/pictrue/WechatIMG20.jpeg");
31 | list.add("https://raw.githubusercontent.com/Awent/PhotoPick-Master/master/pictrue/WechatIMG21.jpeg");
32 | //网络图片
33 | list.add("https://wx1.sinaimg.cn/mw690/7325792bly1fx9oma87k1j21900u04jf.jpg");
34 | list.add("https://wx3.sinaimg.cn/mw690/7325792bly1fx9oma3jhpj21900u04h0.jpg");
35 | list.add("https://wx2.sinaimg.cn/mw690/7325792bly1fx9oylai59j22040u0hdu.jpg");
36 | list.add("https://wx4.sinaimg.cn/mw690/0061VhPpgy1fx9x54op3oj30u00pc1kx.jpg");
37 | list.add("https://wx3.sinaimg.cn/mw690/006cZ2iWgy1fskddvgmwoj30mq0vu7ik.jpg");
38 | list.add("https://wx3.sinaimg.cn/mw690/006l0mbogy1fi68udt62wj30u010h79j.jpg");
39 | list.add("https://wx4.sinaimg.cn/mw690/006l0mbogy1fi68ud4uwwj30u00zrtdj.jpg");
40 | list.add("https://wx1.sinaimg.cn/mw690/006DQg3tly1fuvkrwjforg30b40alb29.gif");
41 | list.add("https://wx2.sinaimg.cn/mw690/006DQg3tly1fwhen1vuudg30go09e7wi.gif");
42 | list.add("https://wx1.sinaimg.cn/mw690/006DQg3tly1fuvkrxsntcg30g409xx6q.gif");
43 | list.add("https://wx1.sinaimg.cn/mw690/006DQg3tly1fuvks859e4g30dw0atqv8.gif");
44 | return list;
45 | }
46 |
47 | /**
48 | * 大图
49 | * @return
50 | */
51 | public static List getBigImgUrls(){
52 | List list = new ArrayList<>();
53 | list.add("https://wx3.sinaimg.cn/mw690/0061VhPpgy1fx8w3jn6o8j30u00qd774.jpg");
54 | list.add("https://wx4.sinaimg.cn/mw690/006mQAf4ly1fx8yoea4zuj30vy1bzk0x.jpg");
55 | list.add("https://wx1.sinaimg.cn/mw690/006mQAf4ly1fx8yoipc10j30vy1bzwnt.jpg");
56 | return list;
57 | }
58 |
59 | /**
60 | * 小图,这里随便找两张小图的
61 | * @return
62 | */
63 | public static List getSmallImgUrls(){
64 | List list = new ArrayList<>();
65 | list.add("https://wx3.sinaimg.cn/mw690/006qDXTKgy1fx8q6x78hkj30c80953yt.jpg");
66 | list.add("https://wx3.sinaimg.cn/mw690/006qDXTKgy1fx8q6x78hkj30c80953yt.jpg");
67 | list.add("https://wx3.sinaimg.cn/mw690/006qDXTKgy1fx8q6x78hkj30c80953yt.jpg");
68 | return list;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/simaple/src/main/java/com/simaple/MyPhotoBean.java:
--------------------------------------------------------------------------------
1 | package com.simaple;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | /**
7 | * Created by Awen
8 | */
9 |
10 | public class MyPhotoBean implements Parcelable {
11 |
12 | private int id;
13 | private String content;
14 |
15 | public MyPhotoBean(){}
16 |
17 | protected MyPhotoBean(Parcel in) {
18 | id = in.readInt();
19 | content = in.readString();
20 | }
21 |
22 | public int getId() {
23 | return id;
24 | }
25 |
26 | public void setId(int id) {
27 | this.id = id;
28 | }
29 |
30 | public String getContent() {
31 | return content;
32 | }
33 |
34 | public void setContent(String content) {
35 | this.content = content;
36 | }
37 |
38 | @Override
39 | public int describeContents() {
40 | return 0;
41 | }
42 |
43 | @Override
44 | public void writeToParcel(Parcel dest, int flags) {
45 | dest.writeInt(id);
46 | dest.writeString(content);
47 | }
48 |
49 | public static final Creator CREATOR = new Creator() {
50 | @Override
51 | public MyPhotoBean createFromParcel(Parcel in) {
52 | return new MyPhotoBean(in);
53 | }
54 |
55 | @Override
56 | public MyPhotoBean[] newArray(int size) {
57 | return new MyPhotoBean[size];
58 | }
59 | };
60 | }
61 |
--------------------------------------------------------------------------------
/simaple/src/main/java/com/simaple/UserBean.kt:
--------------------------------------------------------------------------------
1 | package com.simaple
2 |
3 | class UserBean {
4 | var code = 0
5 | var msg: String? = null
6 | var list: List? = null
7 |
8 | class User {
9 | var avatar: String? = null
10 | var smallAvatar: String? = null
11 | var name: String? = null
12 | var age = 0
13 | }
14 | }
--------------------------------------------------------------------------------
/simaple/src/main/java/com/simaple/activity/CustomPhotoPageActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simaple.activity
2 |
3 | import android.Manifest
4 | import android.view.View
5 | import android.widget.TextView
6 | import android.widget.Toast
7 | import androidx.appcompat.app.AlertDialog
8 | import com.awen.photo.photopick.controller.PhotoPagerConfig
9 | import com.awen.photo.photopick.ui.PhotoPagerActivity
10 | import com.simaple.R
11 | import kr.co.namee.permissiongen.PermissionFail
12 | import kr.co.namee.permissiongen.PermissionGen
13 | import kr.co.namee.permissiongen.PermissionSuccess
14 |
15 | /**
16 | * 自定义view实现下标和长按功能等,如果需要传递参数进来,参考
17 | * @see MyPhotoPagerActivity
18 | *
19 | * 如果自定义activity开启了下滑关闭功能,记得当前自定义activity的Theme要引用{@style/PhoAppTheme.Transparent}
20 | * 如下:
21 | *
24 | */
25 | class CustomPhotoPageActivity : PhotoPagerActivity() {
26 | companion object {
27 | const val requestSDCode = 100
28 | }
29 |
30 | private lateinit var indicator: TextView
31 | private var size: Int = 0
32 | private val userId: Long by lazy {
33 | bundle.getLong("user_id")//通过bundle获取传递进来的参数信息
34 | }
35 |
36 | override fun setCustomView(layoutId: Int) {
37 | super.setCustomView(R.layout.activity_kt_custom_page)//设置自定义view
38 | }
39 |
40 | override fun init() {
41 | super.init()
42 | setIndicatorVisibility(false)//需要主动隐藏默认的下标
43 | size = photoPagerBean.bigImgUrls.size
44 | indicator = customView.findViewById(R.id.indicator)//通过customView来获取自定义view的控件
45 | indicator.text = "1/$size"
46 |
47 | //保存图片回调
48 | setOnPhotoSaveCallback {
49 | AlertDialog.Builder(this)
50 | .setTitle("提示")
51 | .setMessage(if (it.isNullOrEmpty()) "保存失败" else "保存成功,保存地址:$it")
52 | .show()
53 | }
54 |
55 | }
56 |
57 | override fun onStart() {
58 | super.onStart()
59 | AlertDialog.Builder(this)
60 | .setTitle("提示")
61 | .setMessage("1、可长按保存图片\n\np:前两张为res和asset图片,会保存失败\n\n2、这是继承PhotoPagerActivity的自定义界面,在这里只展示自定义下标、长按保存功能、下滑关闭界面功能,更多功能需要自己去实现")
62 | .show()
63 | }
64 |
65 | override fun onPageSelected(position: Int) {
66 | super.onPageSelected(position)
67 | indicator.text = "${currentPosition + 1}/$size"
68 | }
69 |
70 | override fun onLongClick(view: View?): Boolean {
71 | val items = arrayOf("获取当前用户id", "获取当前图片地址", "发给朋友", "收藏", "保存图片", "举报", "取消")
72 | AlertDialog.Builder(this)
73 | .setItems(items) { dialog, which ->
74 | when (which) {
75 | 0 -> toast("通过bundle传过来的用户userId = $userId")
76 | 1 -> toast(currentImageUrl)
77 | 4 -> {//保存图片,注意这里要自己先获取写sd卡权限,具体的使用哪个库来获取权限,这里自己进行修改
78 | //以下操作会回调这两个方法:#requestPermissionSDSuccess(), #requestPermissionSDFailed()
79 | PermissionGen.needPermission(this, requestSDCode, Manifest.permission.WRITE_EXTERNAL_STORAGE)
80 | }
81 | else
82 | -> toast(items[which])
83 | }
84 | }.show()
85 | return true
86 | }
87 |
88 | private fun toast(s: String) {
89 | Toast.makeText(this, s, Toast.LENGTH_LONG).show();
90 | }
91 |
92 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
93 | //转移权限请求由PermissionGen处理
94 | PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults)
95 | }
96 |
97 | @PermissionSuccess(requestCode = requestSDCode)
98 | fun requestPermissionSDSuccess() {
99 | //调用startPermissionSDSuccess此方法可以保存图片
100 | startPermissionSDSuccess()
101 | }
102 |
103 | @PermissionFail(requestCode = requestSDCode)
104 | fun requestPermissionSDFailed() {
105 | startPermissionSDFaild()
106 | }
107 |
108 | }
--------------------------------------------------------------------------------
/simaple/src/main/java/com/simaple/activity/MyPhotoPagerActivity.java:
--------------------------------------------------------------------------------
1 | package com.simaple.activity;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 | import android.widget.Button;
6 | import android.widget.EditText;
7 | import android.widget.TextView;
8 | import android.widget.Toast;
9 |
10 | import androidx.annotation.LayoutRes;
11 |
12 | import com.awen.photo.photopick.ui.PhotoPagerActivity;
13 | import com.simaple.MyPhotoBean;
14 | import com.simaple.R;
15 |
16 | import java.util.ArrayList;
17 |
18 | /**
19 | * 自定义图片浏览界面
20 | * Created by Awen
21 | */
22 |
23 | public class MyPhotoPagerActivity extends PhotoPagerActivity {
24 |
25 | private TextView title;
26 | private EditText et;
27 | private ArrayList list;
28 |
29 | @Override
30 | protected void setCustomView(@LayoutRes int layoutId) {
31 | super.setCustomView(R.layout.activity_custom_pager);
32 | }
33 |
34 | @Override
35 | protected void init() {
36 | super.init();
37 | et = (EditText) getCustomView().findViewById(R.id.et);
38 | title = (TextView) getCustomView().findViewById(R.id.textView);
39 | // title.setText(currentPosition + "/" + photoPagerBean.getBigImgUrls().size());
40 | Button button = (Button) getCustomView().findViewById(R.id.button);
41 | button.setOnClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View view) {
44 | Toast.makeText(MyPhotoPagerActivity.this,"我是自定义的图片浏览器",Toast.LENGTH_LONG).show();
45 | }
46 | });
47 | setIndicatorVisibility(false);//设置Indicator不可见
48 |
49 | getCustomView().findViewById(R.id.back).setOnClickListener(new View.OnClickListener() {
50 | @Override
51 | public void onClick(View view) {
52 | onBackPressed();
53 | }
54 | });
55 |
56 | //获取MainActivity传递过来的数据
57 | Bundle bundle = getBundle();
58 | if(bundle != null) {
59 | list = bundle.getParcelableArrayList("test_bundle");
60 | if (list != null && !list.isEmpty()){
61 | //you can do something
62 | }
63 | }
64 | }
65 |
66 | @Override
67 | public void onPageSelected(int position) {
68 | super.onPageSelected(position);
69 | title.setText(currentPosition + "/" + photoPagerBean.getBigImgUrls().size());
70 | Toast.makeText(MyPhotoPagerActivity.this,"当前位置的图片地址 = " + photoPagerBean.getBigImgUrls().get(position),Toast.LENGTH_LONG).show();
71 | if (list != null && !list.isEmpty()){
72 | et.setText(list.get(position).getContent());
73 | }
74 | }
75 |
76 | @Override
77 | protected boolean onSingleClick() {
78 | Toast.makeText(MyPhotoPagerActivity.this,"单击图片",Toast.LENGTH_LONG).show();
79 | return true;
80 | }
81 |
82 | @Override
83 | public boolean onLongClick(View view) {
84 | Toast.makeText(MyPhotoPagerActivity.this,"长按图片",Toast.LENGTH_LONG).show();
85 | //saveImage();//保存图片到图库
86 | return true;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/simaple/src/main/res/layout/activity_custom_pager.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
20 |
21 |
27 |
28 |
37 |
38 |
39 |
45 |
46 |
52 |
53 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/simaple/src/main/res/layout/activity_kt_custom_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
--------------------------------------------------------------------------------
/simaple/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
21 |
22 |
28 |
29 |
35 |
36 |
42 |
43 |
44 |
45 |
51 |
52 |
58 |
59 |
65 |
66 |
72 |
73 |
79 |
80 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/simaple/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simaple/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simaple/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simaple/src/main/res/mipmap-xhdpi/resx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/src/main/res/mipmap-xhdpi/resx.jpg
--------------------------------------------------------------------------------
/simaple/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simaple/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/simaple/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/simaple/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/simaple/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/simaple/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/simaple/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PhotoPick-Simaple
3 |
4 |
--------------------------------------------------------------------------------
/simaple/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/v1.096-README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Awent/PhotoPick-Master/45a0b06b93516a05c9371cb4cde81ebfd5b0fd22/v1.096-README.md
--------------------------------------------------------------------------------