paths = data.getStringArrayListExtra(
99 | PicturePickerActivity.RESULT_EXTRA_PICKED_PICTURES);
100 | if (paths != null) {
101 | pickerCallback.onPickedComplete(paths);
102 | } else {
103 | Log.e(TAG, "Picked path from PicturePickerActivity is null.");
104 | }
105 | break;
106 | default:
107 | break;
108 | }
109 | }
110 | });
111 | PicturePickerActivity.startActivityForResult(mActivity, callbackFragment, mConfig);
112 | }
113 |
114 | private void verify() {
115 | // 1. 验证是否实现了图片加载器
116 | if (PictureLoader.getPictureLoader() == null) {
117 | throw new UnsupportedOperationException("PictureLoader.load -> please invoke setPictureLoader first");
118 | }
119 | // 2. 若开启了裁剪, 则只能选中一张图片
120 | if (mConfig.isCropSupport()) {
121 | mConfig.rebuild()
122 | .setThreshold(1)
123 | .setPickedPictures(null)
124 | .build();
125 | }
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/fragment/CallbackFragment.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.fragment;
2 |
3 | import android.app.Activity;
4 | import android.app.Fragment;
5 | import android.app.FragmentManager;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.support.annotation.NonNull;
9 |
10 | /**
11 | * 用于回调 startActivityForResult 的过度 Fragment
12 | *
13 | * @author Sharry Contact me.
14 | * @version 1.0
15 | * @since 2018/11/30 15:46
16 | */
17 | public class CallbackFragment extends Fragment {
18 |
19 | public static final String TAG = CallbackFragment.class.getSimpleName();
20 |
21 | /**
22 | * 获取一个添加到 Activity 中的 Fragment 的实例
23 | *
24 | * @param bind The activity associated with this fragment.
25 | * @return an instance of CallbackFragment.
26 | */
27 | public static CallbackFragment getInstance(@NonNull Activity bind) {
28 | CallbackFragment callbackFragment = findFragmentFromActivity(bind);
29 | if (callbackFragment == null) {
30 | callbackFragment = CallbackFragment.newInstance();
31 | FragmentManager fragmentManager = bind.getFragmentManager();
32 | fragmentManager.beginTransaction()
33 | .add(callbackFragment, TAG)
34 | .commitAllowingStateLoss();
35 | fragmentManager.executePendingTransactions();
36 | }
37 | return callbackFragment;
38 | }
39 |
40 | /**
41 | * 在 Activity 中通过 TAG 去寻找我们添加的 Fragment
42 | */
43 | private static CallbackFragment findFragmentFromActivity(@NonNull Activity activity) {
44 | return (CallbackFragment) activity.getFragmentManager().findFragmentByTag(TAG);
45 | }
46 |
47 | private static CallbackFragment newInstance() {
48 | return new CallbackFragment();
49 | }
50 |
51 | private Callback mCallback;
52 |
53 | @Override
54 | public void onCreate(Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | setRetainInstance(true);
57 | }
58 |
59 | /**
60 | * 设置图片选择回调
61 | */
62 | public void setCallback(Callback callback) {
63 | this.mCallback = callback;
64 | }
65 |
66 | @Override
67 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
68 | super.onActivityResult(requestCode, resultCode, data);
69 | if (null != mCallback) {
70 | mCallback.onActivityResult(requestCode, resultCode, data);
71 | }
72 | }
73 |
74 | /**
75 | * The callback associated with this Fragment.
76 | */
77 | public interface Callback {
78 |
79 | void onActivityResult(int requestCode, int resultCode, Intent data);
80 |
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/loader/IPictureLoader.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.loader;
2 |
3 | import android.content.Context;
4 | import android.widget.ImageView;
5 |
6 | /**
7 | * 图片加载的接口, 由外界实现图片加载的策略
8 | *
9 | * @author Sharry Contact me.
10 | * @version 1.0
11 | * @since 2018/9/18 16:18
12 | */
13 | public interface IPictureLoader {
14 | void load(Context context, String uri, ImageView imageView);
15 | }
16 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/loader/PictureLoader.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.loader;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.widget.ImageView;
6 |
7 | /**
8 | * PicturePicker 加载图片的工具类
9 | *
10 | * @author Sharry Contact me.
11 | * @version 1.0
12 | * @since 2018/6/21 16:19
13 | */
14 | public class PictureLoader {
15 |
16 | private static IPictureLoader mPictureLoader;
17 |
18 | public static void setPictureLoader(@NonNull IPictureLoader loader) {
19 | mPictureLoader = loader;
20 | }
21 |
22 | public static IPictureLoader getPictureLoader() {
23 | return mPictureLoader;
24 | }
25 |
26 | public static void load(Context context, String uri, ImageView imageView) {
27 | if (mPictureLoader == null) {
28 | throw new UnsupportedOperationException("PictureLoader.load -> please invoke setPictureLoader first");
29 | }
30 | mPictureLoader.load(context, uri, imageView);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/permission/PermissionsCallback.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.permission;
2 |
3 | /**
4 | * 权限请求的回调
5 | *
6 | * @author Sharry Contact me.
7 | * @version 1.0
8 | * @since 2018/1/5 16:22
9 | */
10 | public interface PermissionsCallback {
11 | void onResult(boolean granted);
12 | }
13 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/permission/PermissionsFragment.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.permission;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Fragment;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.content.pm.PackageManager;
8 | import android.net.Uri;
9 | import android.os.Build;
10 | import android.os.Bundle;
11 | import android.provider.Settings;
12 | import android.support.annotation.NonNull;
13 | import android.support.v7.app.AlertDialog;
14 | import android.util.Log;
15 |
16 | /**
17 | * 执行权限请求的 Fragment
18 | *
19 | * @author Sharry Contact me.
20 | * @version 1.0
21 | * @since 2018/1/5 16:22
22 | */
23 | public class PermissionsFragment extends Fragment {
24 |
25 | private static final int REQUEST_CODE_PERMISSIONS = 0x001234;
26 | private static final int REQUEST_CODE_SETTING = 0x0012345;
27 | private PermissionsCallback mCallback;
28 | private String[] mPermissions;
29 |
30 | public static PermissionsFragment getInstance() {
31 | return new PermissionsFragment();
32 | }
33 |
34 | @Override
35 | public void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setRetainInstance(true);
38 | }
39 |
40 | @TargetApi(Build.VERSION_CODES.M)
41 | void requestPermissions(@NonNull String[] permissions, PermissionsCallback callback) {
42 | mPermissions = permissions;
43 | mCallback = callback;
44 | requestPermissions(mPermissions, REQUEST_CODE_PERMISSIONS);
45 | }
46 |
47 | @Override
48 | @TargetApi(Build.VERSION_CODES.M)
49 | public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
50 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
51 | if (requestCode != REQUEST_CODE_PERMISSIONS) return;
52 | boolean isAllGranted = true;
53 | for (int i = 0, size = permissions.length; i < size; i++) {
54 | if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
55 | log("onRequestPermissionsResult: " + permissions[i] + " is Granted");
56 | } else {
57 | log("onRequestPermissionsResult: " + permissions[i] + " is Denied");
58 | isAllGranted = false;
59 | }
60 | }
61 | if (isAllGranted) {
62 | mCallback.onResult(true);
63 | } else {
64 | showPermissionDeniedDialog();
65 | }
66 | }
67 |
68 | @TargetApi(Build.VERSION_CODES.M)
69 | boolean isGranted(String permission) {
70 | return getActivity().checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
71 | }
72 |
73 | @TargetApi(Build.VERSION_CODES.M)
74 | boolean isRevoked(String permission) {
75 | return getActivity().getPackageManager().isPermissionRevokedByPolicy(permission, getActivity().getPackageName());
76 | }
77 |
78 | @TargetApi(Build.VERSION_CODES.M)
79 | private void showPermissionDeniedDialog() {
80 | //启动当前App的系统设置界面
81 | AlertDialog dialog = new AlertDialog.Builder(getContext())
82 | .setTitle("帮助")
83 | .setMessage("当前应用缺少必要权限")
84 | .setNegativeButton("取消", new DialogInterface.OnClickListener() {
85 | @Override
86 | public void onClick(DialogInterface dialog, int which) {
87 | mCallback.onResult(false);
88 | dialog.dismiss();
89 | }
90 | })
91 | .setPositiveButton("设置", new DialogInterface.OnClickListener() {
92 | @Override
93 | public void onClick(DialogInterface dialog, int which) {
94 | // 启动当前App的系统设置界面
95 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
96 | intent.setData(Uri.parse("package:" + getContext().getPackageName()));
97 | startActivityForResult(intent, REQUEST_CODE_SETTING);
98 | }
99 | }).create();
100 | dialog.show();
101 | }
102 |
103 | @Override
104 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
105 | super.onActivityResult(requestCode, resultCode, data);
106 | if (requestCode != REQUEST_CODE_SETTING) return;
107 | // 从设置页面回来时, 重新检测一次申请的权限是否都已经授权
108 | boolean isAllGranted = true;
109 | for (String permission : mPermissions) {
110 | if (!isGranted(permission)) {
111 | isAllGranted = false;
112 | break;
113 | }
114 | }
115 | mCallback.onResult(isAllGranted);
116 | }
117 |
118 | void log(String message) {
119 | Log.i(PermissionsManager.TAG, message);
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/permission/PermissionsManager.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.permission;
2 |
3 | import android.app.Activity;
4 | import android.app.FragmentManager;
5 | import android.content.Context;
6 | import android.os.Build;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * 权限请求管理类
13 | * Thanks RxPermissions
14 | *
15 | * @author Sharry Contact me.
16 | * @version 1.0
17 | * @since 2018/1/5 16:23
18 | */
19 | /*@hind*/
20 | public class PermissionsManager {
21 |
22 | public static final String TAG = PermissionsManager.class.getSimpleName();
23 | private PermissionsFragment mPermissionsFragment;
24 | private String[] mPermissions;
25 |
26 | public static PermissionsManager getManager(Context context) {
27 | if (context instanceof Activity) {
28 | Activity activity = (Activity) context;
29 | return new PermissionsManager(activity);
30 | } else {
31 | throw new IllegalArgumentException("PermissionsManager.getManager -> Context can not cast to Activity");
32 | }
33 | }
34 |
35 | private PermissionsManager(Activity activity) {
36 | mPermissionsFragment = getPermissionsFragment(activity);
37 | }
38 |
39 | /**
40 | * 添加需要请求的权限
41 | */
42 | public PermissionsManager request(String... permissions) {
43 | return requestArray(permissions);
44 | }
45 |
46 | /**
47 | * 添加需要请求的权限(Kotlin 不支持从不定长参数转为 Array)
48 | */
49 | public PermissionsManager requestArray(String[] permissions) {
50 | ensure(permissions);
51 | mPermissions = permissions;
52 | return this;
53 | }
54 |
55 | /**
56 | * 执行权限请求
57 | */
58 | public void execute(PermissionsCallback permissionsCallback) {
59 | if (permissionsCallback == null) {
60 | throw new IllegalArgumentException("PermissionsManager.execute -> PermissionsCallback must not be null");
61 | }
62 | requestImplementation(mPermissions, permissionsCallback);
63 | }
64 |
65 | /**
66 | * 判断权限是否被授权
67 | */
68 | public boolean isGranted(String permission) {
69 | return !isMarshmallow() || mPermissionsFragment.isGranted(permission);
70 | }
71 |
72 | /**
73 | * 判断权限是否被撤回
74 | *
75 | * Always false if SDK < 23.
76 | */
77 | @SuppressWarnings("WeakerAccess")
78 | public boolean isRevoked(String permission) {
79 | return isMarshmallow() && mPermissionsFragment.isRevoked(permission);
80 | }
81 |
82 | /**
83 | * 获取 PermissionsFragment
84 | */
85 | private PermissionsFragment getPermissionsFragment(Activity activity) {
86 | PermissionsFragment permissionsFragment = findPermissionsFragment(activity);
87 | if (permissionsFragment == null) {
88 | permissionsFragment = PermissionsFragment.getInstance();
89 | FragmentManager fragmentManager = activity.getFragmentManager();
90 | fragmentManager.beginTransaction().add(permissionsFragment, TAG).commitAllowingStateLoss();
91 | fragmentManager.executePendingTransactions();
92 | }
93 | return permissionsFragment;
94 | }
95 |
96 | /**
97 | * 在 Activity 中通过 TAG 去寻找我们添加的 Fragment
98 | */
99 | private PermissionsFragment findPermissionsFragment(Activity activity) {
100 | return (PermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);
101 | }
102 |
103 |
104 | /**
105 | * 验证发起请求的权限是否有效
106 | */
107 | private void ensure(String[] permissions) {
108 | if (permissions == null || permissions.length == 0) {
109 | throw new IllegalArgumentException("PermissionsManager.request -> requestEach requires at least one input permission");
110 | }
111 | }
112 |
113 | /**
114 | * 执行权限请求
115 | *
116 | * @param permissions
117 | * @param callback
118 | */
119 | private void requestImplementation(String[] permissions, PermissionsCallback callback) {
120 | List unrequestedPermissions = new ArrayList<>();
121 | for (String permission : permissions) {
122 | mPermissionsFragment.log("Requesting permission -> " + permission);
123 | if (isGranted(permission)) {
124 | // Already granted, or not Android M
125 | // Return a granted Permission object.
126 | continue;
127 | }
128 | if (isRevoked(permission)) {
129 | // Revoked by a policy, return a denied Permission object.
130 | continue;
131 | }
132 | unrequestedPermissions.add(permission);
133 | }
134 | if (!unrequestedPermissions.isEmpty()) {
135 | // 细节, toArray的时候指定了数组的长度
136 | String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(
137 | new String[unrequestedPermissions.size()]);
138 | mPermissionsFragment.requestPermissions(unrequestedPermissionsArray, callback);
139 | } else {
140 | callback.onResult(true);
141 | }
142 | }
143 |
144 | private boolean isMarshmallow() {
145 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/utils/ColorUtil.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.utils;
2 |
3 | import android.support.annotation.ColorInt;
4 |
5 | /**
6 | * 处理颜色相关的工具类
7 | *
8 | * @author Sharry Contact me.
9 | * @version 1.0
10 | * @since 2018/9/22 17:39
11 | */
12 | public class ColorUtil {
13 |
14 | /**
15 | * Get ARGB color range in [primitiveColor, destColor]
16 | *
17 | * @param fraction range[0, 1]
18 | * @param primitiveColor color associated with primitive changed
19 | * @param destColor color associated with dest changed
20 | */
21 | public static int gradualChanged(float fraction, @ColorInt int primitiveColor, @ColorInt int destColor) {
22 | int startInt = (Integer) primitiveColor;
23 | float startA = ((startInt >> 24) & 0xff) / 255.0f;
24 | float startR = ((startInt >> 16) & 0xff) / 255.0f;
25 | float startG = ((startInt >> 8) & 0xff) / 255.0f;
26 | float startB = (startInt & 0xff) / 255.0f;
27 |
28 | int endInt = (Integer) destColor;
29 | float endA = ((endInt >> 24) & 0xff) / 255.0f;
30 | float endR = ((endInt >> 16) & 0xff) / 255.0f;
31 | float endG = ((endInt >> 8) & 0xff) / 255.0f;
32 | float endB = (endInt & 0xff) / 255.0f;
33 |
34 | // convert from sRGB to linear
35 | startR = (float) Math.pow(startR, 2.2);
36 | startG = (float) Math.pow(startG, 2.2);
37 | startB = (float) Math.pow(startB, 2.2);
38 |
39 | endR = (float) Math.pow(endR, 2.2);
40 | endG = (float) Math.pow(endG, 2.2);
41 | endB = (float) Math.pow(endB, 2.2);
42 |
43 | // compute the interpolated color in linear space
44 | float a = startA + fraction * (endA - startA);
45 | float r = startR + fraction * (endR - startR);
46 | float g = startG + fraction * (endG - startG);
47 | float b = startB + fraction * (endB - startB);
48 |
49 | // convert back to sRGB in the [0..255] range
50 | a = a * 255.0f;
51 | r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
52 | g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
53 | b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
54 |
55 | return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/utils/FileUtil.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.utils;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.media.MediaScannerConnection;
6 | import android.net.Uri;
7 | import android.os.Build;
8 | import android.os.Environment;
9 | import android.support.v4.content.FileProvider;
10 | import android.text.TextUtils;
11 | import android.text.format.DateFormat;
12 | import android.util.Log;
13 |
14 | import java.io.File;
15 | import java.io.IOException;
16 | import java.util.Calendar;
17 | import java.util.Locale;
18 |
19 | /**
20 | * 处理文件相关的工具类
21 | *
22 | * @author Sharry Contact me.
23 | * @version 1.0
24 | * @since 2018/9/22 17:39
25 | */
26 | public class FileUtil {
27 |
28 | private static final String TAG = FileUtil.class.getSimpleName();
29 |
30 | /**
31 | * 刷新文件管理器
32 | */
33 | public static void freshMediaStore(Context context, File file) {
34 | MediaScanner.refresh(context, file);
35 | }
36 |
37 | /**
38 | * 获取 URI
39 | */
40 | public static Uri getUriFromFile(Context context, String authority, File file) {
41 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
42 | FileProvider.getUriForFile(context, authority, file) : Uri.fromFile(file);
43 | }
44 |
45 | /**
46 | * 创建默认文件目录(包名的最后一个字段/系统相册的目录)
47 | */
48 | public static File createDefaultDirectory(Context context) {
49 | // 获取默认路径
50 | File defaultDir = TextUtils.isEmpty(getDefaultName(context)) ?
51 | Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) :
52 | new File(Environment.getExternalStorageDirectory(), getDefaultName(context));
53 | if (!defaultDir.exists()) defaultDir.mkdirs();
54 | return defaultDir;
55 | }
56 |
57 | /**
58 | * 根据目的文件路径, 创建临时文件
59 | *
60 | * @param directoryPath 目标文件路径
61 | * @return 创建的文件
62 | */
63 | public static File createTempFileByDestDirectory(String directoryPath) {
64 | // 获取临时文件目录
65 | File tempDirectory = new File(directoryPath);
66 | if (!tempDirectory.exists()) tempDirectory.mkdirs();
67 | // 创建临时文件
68 | String tempFileName = "temp_file_" + DateFormat.format("yyyyMMdd_HHmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg";
69 | File tempFile = new File(tempDirectory, tempFileName);
70 | try {
71 | if (tempFile.exists()) tempFile.delete();
72 | tempFile.createNewFile();
73 | Log.i(TAG, "create temp file directory success -> " + tempFile.getAbsolutePath());
74 | } catch (IOException e) {
75 | Log.e(TAG, "create temp file directory failed ->" + tempFile.getAbsolutePath(), e);
76 | }
77 | return tempFile;
78 | }
79 |
80 | /**
81 | * 创建拍照文件
82 | *
83 | * @param directoryPath 文件目录路径
84 | */
85 | public static File createCameraDestFile(String directoryPath) {
86 | // 获取默认路径
87 | File dir = new File(directoryPath);
88 | if (!dir.exists()) dir.mkdirs();
89 | // 创建拍照目标文件
90 | String fileName = "camera_" + DateFormat.format("yyyyMMdd_HHmmss",
91 | Calendar.getInstance(Locale.CHINA)) + ".jpg";
92 | File cameraFile = new File(dir, fileName);
93 | try {
94 | if (cameraFile.exists()) cameraFile.delete();
95 | cameraFile.createNewFile();
96 | Log.i(TAG, "create camera file success -> " + cameraFile.getAbsolutePath());
97 | } catch (IOException e) {
98 | Log.e(TAG, "create camera file failed -> " + cameraFile.getAbsolutePath(), e);
99 | }
100 | return cameraFile;
101 | }
102 |
103 | /**
104 | * 创建拍照文件
105 | *
106 | * @param directoryPath 文件目录路径
107 | */
108 | public static File createCropDestFile(String directoryPath) {
109 | // 获取默认路径
110 | File dir = new File(directoryPath);
111 | if (!dir.exists()) dir.mkdirs();
112 | // 创建拍照目标文件
113 | String fileName = "crop_" + DateFormat.format("yyyyMMdd_HHmmss",
114 | Calendar.getInstance(Locale.CHINA)) + ".jpg";
115 | File cropFile = new File(dir, fileName);
116 | try {
117 | if (cropFile.exists()) cropFile.delete();
118 | cropFile.createNewFile();
119 | Log.i(TAG, "create crop file success -> " + cropFile.getAbsolutePath());
120 | } catch (IOException e) {
121 | Log.e(TAG, "create crop file failed -> " + cropFile.getAbsolutePath(), e);
122 | }
123 | return cropFile;
124 | }
125 |
126 | /**
127 | * 创建默认的 FileProvider 的 Authority
128 | */
129 | public static String getDefaultFileProviderAuthority(Context context) {
130 | return context.getPackageName() + ".FileProvider";
131 | }
132 |
133 | /**
134 | * 获取默认文件名(包名的最后一个字段)
135 | */
136 | private static String getDefaultName(Context context) {
137 | String packageName = context.getPackageName();
138 | if (!TextUtils.isEmpty(packageName)) {
139 | int indexLastDot = packageName.lastIndexOf(".");
140 | if (indexLastDot != -1) {
141 | return packageName.substring(indexLastDot + 1);
142 | }
143 | }
144 | return null;
145 | }
146 |
147 | /**
148 | * 用于通知文件管理器变更
149 | */
150 | private static class MediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {
151 |
152 | private static final String TAG = MediaScanner.class.getSimpleName();
153 |
154 | private static void refresh(Context context, File file) {
155 | // 4.0 以上的系统使用 MediaScanner 更新
156 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
157 | new MediaScanner(context, file);
158 | } else {
159 | Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
160 | intent.setData(Uri.fromFile(file));
161 | context.sendBroadcast(intent);
162 | }
163 | }
164 |
165 | private File mFile;
166 | private MediaScannerConnection mMsc;
167 |
168 | private MediaScanner(Context context, File file) {
169 | this.mFile = file;
170 | if (verify()) {
171 | this.mMsc = new MediaScannerConnection(context, this);
172 | mMsc.connect();
173 | }
174 | }
175 |
176 | @Override
177 | public void onMediaScannerConnected() {
178 | mMsc.scanFile(mFile.getAbsolutePath(), null);
179 | }
180 |
181 | @Override
182 | public void onScanCompleted(String path, Uri uri) {
183 | mMsc.disconnect();
184 | }
185 |
186 | /**
187 | * 验证文件的合法性
188 | */
189 | private boolean verify() {
190 | if (!mFile.exists()) {
191 | Log.e(TAG, "Verify failed, scanner target file not exist!");
192 | return false;
193 | }
194 | return true;
195 | }
196 |
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/utils/PictureUtil.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.utils;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Matrix;
6 | import android.media.ExifInterface;
7 | import android.text.TextUtils;
8 |
9 | import java.io.File;
10 | import java.io.FileOutputStream;
11 | import java.io.IOException;
12 |
13 | /**
14 | * 处理图片相关的工具类
15 | *
16 | * @author Sharry Contact me.
17 | * @version 1.0
18 | * @since 2018/9/18 16:23
19 | */
20 | public class PictureUtil {
21 |
22 | /**
23 | * 图片压缩
24 | */
25 | public static void doCompress(String originPath, String destPath, int quality) throws IOException {
26 | if (TextUtils.isEmpty(originPath)) {
27 | throw new IllegalArgumentException("PictureUtil.doCompress -> parameter originFilePath must not be null!");
28 | }
29 | if (TextUtils.isEmpty(destPath)) {
30 | throw new IllegalArgumentException("PictureUtil.doCompress -> parameter destPath must not be null!");
31 | }
32 | // 1. 邻近采样压缩尺寸(Nearest Neighbour Resampling Compress)
33 | BitmapFactory.Options options = getBitmapOptions(originPath);
34 | Bitmap bitmap = BitmapFactory.decodeFile(originPath, options);
35 | if (bitmap == null) {
36 | return;
37 | }
38 | // 2. 旋转一下 Bitmap
39 | bitmap = rotateBitmap(bitmap, readPictureAngle(originPath));
40 | // 3. 质量压缩(Quality Compress)
41 | qualityCompress(bitmap, quality, destPath);
42 | }
43 |
44 | /**
45 | * 解析图片文件的宽高与目标宽高, 获取 Bitmap.Options
46 | *
47 | * @param filePath 文件路径
48 | * @return 获取 Bitmap.Options
49 | */
50 | private static BitmapFactory.Options getBitmapOptions(String filePath) {
51 | BitmapFactory.Options options = new BitmapFactory.Options();
52 | options.inJustDecodeBounds = true;
53 | BitmapFactory.decodeFile(filePath, options);
54 | options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight);
55 | options.inJustDecodeBounds = false;
56 | return options;
57 | }
58 |
59 | /**
60 | * 根据主流屏幕自适应计算采样率
61 | *
62 | * @param srcWidth 原始宽度
63 | * @param srcHeight 原始高度
64 | * @return 采样率
65 | */
66 | private static int calculateSampleSize(int srcWidth, int srcHeight) {
67 | //将 srcWidth 和 srcHeight 设置为偶数,方便除法计算
68 | srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
69 | srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;
70 |
71 | int longSide = Math.max(srcWidth, srcHeight);
72 | int shortSide = Math.min(srcWidth, srcHeight);
73 |
74 | float scale = ((float) shortSide / longSide);
75 | if (scale <= 1 && scale > 0.5625) {
76 | if (longSide < 1664) {
77 | return 1;
78 | } else if (longSide >= 1664 && longSide < 4990) {
79 | return 2;
80 | } else if (longSide > 4990 && longSide < 10240) {
81 | return 4;
82 | } else {
83 | return longSide / 1280 == 0 ? 1 : longSide / 1280;
84 | }
85 | } else if (scale <= 0.5625 && scale > 0.5) {
86 | return longSide / 1280 == 0 ? 1 : longSide / 1280;
87 | } else {
88 | return (int) Math.ceil(longSide / (1280.0 / scale));
89 | }
90 | }
91 |
92 | /**
93 | * Bitmap 质量压缩
94 | *
95 | * @param srcBitmap 原始 Bitmap
96 | * @param quality 压缩质量
97 | * @param destFilePath 压缩后的文件
98 | */
99 | private static void qualityCompress(Bitmap srcBitmap, int quality, String destFilePath) throws IOException {
100 | File file = new File(destFilePath);
101 | if (file.exists()) {
102 | file.delete();
103 | }
104 | file.createNewFile();
105 | // 进行质量压缩
106 | FileOutputStream out = new FileOutputStream(file);
107 | // 采用有损的 jpeg 图片压缩
108 | srcBitmap.compress(Bitmap.CompressFormat.JPEG, quality, out);
109 | out.flush();
110 | out.close();
111 | }
112 |
113 | /**
114 | * 旋转 Bitmap
115 | *
116 | * @param bitmap 原始 bitmap
117 | * @param angle 旋转的角度
118 | */
119 | private static Bitmap rotateBitmap(Bitmap bitmap, int angle) {
120 | if (angle == 0) {
121 | return bitmap;
122 | }
123 | //旋转图片 动作
124 | Matrix matrix = new Matrix();
125 | matrix.postRotate(angle);
126 | // 旋转后的 Bitmap
127 | return Bitmap.createBitmap(bitmap, 0, 0,
128 | bitmap.getWidth(), bitmap.getHeight(), matrix, true);
129 | }
130 |
131 | /**
132 | * 读取图片文件旋转的角度
133 | *
134 | * @param imagePath 文件路径
135 | */
136 | private static int readPictureAngle(String imagePath) throws IOException {
137 | int degree = 0;
138 | ExifInterface exifInterface = new ExifInterface(imagePath);
139 | int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
140 | switch (orientation) {
141 | case ExifInterface.ORIENTATION_ROTATE_90:
142 | degree = 90;
143 | break;
144 | case ExifInterface.ORIENTATION_ROTATE_180:
145 | degree = 180;
146 | break;
147 | case ExifInterface.ORIENTATION_ROTATE_270:
148 | degree = 270;
149 | break;
150 | default:
151 | break;
152 | }
153 | return degree;
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/support/utils/VersionUtil.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.support.utils;
2 |
3 | import android.os.Build;
4 |
5 | /**
6 | * 版本控制相关的工具类
7 | *
8 | * @author Sharry Contact me.
9 | * @version 1.0
10 | * @since 2018/9/22 17:46
11 | */
12 | public class VersionUtil {
13 |
14 | public static boolean isLollipop() {
15 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/watcher/PictureWatcherContract.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.watcher;
2 |
3 | import android.support.annotation.Nullable;
4 | import android.support.annotation.StringRes;
5 |
6 | import java.util.ArrayList;
7 |
8 | /**
9 | * Created by Sharry on 2018/6/13.
10 | * Email: SharryChooCHN@Gmail.com
11 | * Version: 1.0
12 | * Description: PicturePicture MVP 的约束
13 | */
14 | interface PictureWatcherContract {
15 | interface IView {
16 | /**
17 | * 展示 Toolbar 左部文本
18 | */
19 | void displayToolbarLeftText(CharSequence content);
20 |
21 | /**
22 | * 设置顶部选择指示器的可见性
23 | */
24 | void setToolbarCheckedIndicatorVisibility(boolean isShowCheckedIndicator);
25 |
26 | /**
27 | * 设置顶部选择指示器的颜色
28 | *
29 | * @param indicatorBorderCheckedColor 边框选中颜色
30 | * @param indicatorBorderUncheckedColor 边框为选中颜色
31 | * @param indicatorSolidColor 填充颜色
32 | * @param indicatorTextColor 文本颜色
33 | */
34 | void setToolbarCheckedIndicatorColors(int indicatorBorderCheckedColor, int indicatorBorderUncheckedColor,
35 | int indicatorSolidColor, int indicatorTextColor);
36 |
37 | /**
38 | * 设置 Toolbar 上指示器是否被选中
39 | */
40 | void setToolbarIndicatorChecked(boolean isChecked);
41 |
42 | /**
43 | * 展示 Toolbar 指示器上的文本
44 | */
45 | void displayToolbarIndicatorText(CharSequence indicatorText);
46 |
47 | /**
48 | * 设置底部图片预览的 RecyclerView 的 Adapter
49 | */
50 | void setPreviewAdapter(WatcherPreviewAdapter adapter);
51 |
52 | /**
53 | * 展示确认文本
54 | */
55 | void displayPreviewEnsureText(CharSequence content);
56 |
57 | /**
58 | * 创建 PhotoViews
59 | */
60 | void createPhotoViews(int photoViewCount);
61 |
62 | /**
63 | * 展示指定位置的图片
64 | *
65 | * @param pictureUris 需要展示的图片集合
66 | * @param curPosition 指定位置的图片
67 | */
68 | void displayPictureAt(ArrayList pictureUris, int curPosition);
69 |
70 | /**
71 | * 通知选中的图片被移除了
72 | */
73 | void notifyBottomPicturesRemoved(String removedPath, int removedIndex);
74 |
75 | /**
76 | * 通知图片插入了
77 | */
78 | void notifyBottomPictureAdded(String insertPath, int addedIndex);
79 |
80 | /**
81 | * 展示消息通知
82 | */
83 | void showMsg(String msg);
84 |
85 | /**
86 | * 从资源文件获取 String
87 | */
88 | String getString(@StringRes int resId);
89 |
90 | /**
91 | * 展示底部预览视图
92 | */
93 | void showBottomPreview();
94 |
95 | /**
96 | * 隐藏底部预览视图
97 | */
98 | void dismissBottomPreview();
99 |
100 | /**
101 | * 让图片预览滚动到指定位置
102 | *
103 | * @param position the position for scroll to.
104 | */
105 | void previewPicturesSmoothScrollToPosition(int position);
106 |
107 | /**
108 | * 处理 View 的 finish 操作
109 | */
110 | void finish();
111 |
112 | /**
113 | * 设置返回值在 finish 之前
114 | *
115 | * @param pickedPaths 用户选中的图片
116 | * @param isEnsurePressed 是否点击了确认按钮
117 | */
118 | void setResultBeforeFinish(@Nullable ArrayList pickedPaths, boolean isEnsurePressed);
119 |
120 | /**
121 | * 展示共享元素入场动画
122 | *
123 | * @param elementData shared element data.
124 | */
125 | void showSharedElementEnter(SharedElementData elementData);
126 |
127 | /**
128 | * 展示共享元素退场动画
129 | *
130 | * @param elementData shared element data.
131 | */
132 | void showSharedElementExitAndFinish(SharedElementData elementData);
133 | }
134 |
135 |
136 | interface IPresenter {
137 |
138 | /**
139 | * 获取数据
140 | */
141 | void setup();
142 |
143 | /**
144 | * 处理页面的滑动
145 | */
146 | void handlePagerChanged(int position);
147 |
148 | /**
149 | * 处理确认按钮点击
150 | */
151 | void handleEnsureClick();
152 |
153 | /**
154 | * 处理 Toolbar 上索引的点击
155 | */
156 | void handleToolbarCheckedIndicatorClick(boolean checked);
157 |
158 | /**
159 | * 处理返回事件
160 | */
161 | void handleBackPressed();
162 |
163 | /**
164 | * 处理 View 的 finish
165 | */
166 | void handleSetResultBeforeFinish();
167 | }
168 | }
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/watcher/PictureWatcherManager.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.watcher;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.view.View;
10 |
11 | import com.sharry.picturepicker.support.fragment.CallbackFragment;
12 | import com.sharry.picturepicker.support.loader.IPictureLoader;
13 | import com.sharry.picturepicker.support.loader.PictureLoader;
14 | import com.sharry.picturepicker.support.permission.PermissionsCallback;
15 | import com.sharry.picturepicker.support.permission.PermissionsManager;
16 |
17 | import java.util.ArrayList;
18 |
19 | /**
20 | * Created by Sharry on 2018/6/19.
21 | * Email: SharryChooCHN@Gmail.com
22 | * Version: 1.0
23 | * Description: 图片查看器的管理类
24 | */
25 | public class PictureWatcherManager {
26 |
27 | public static final String TAG = PictureWatcherManager.class.getSimpleName();
28 |
29 | public static PictureWatcherManager with(@NonNull Context context) {
30 | if (context instanceof Activity) {
31 | Activity activity = (Activity) context;
32 | return new PictureWatcherManager(activity);
33 | } else {
34 | throw new IllegalArgumentException("PictureWatcherManager.with -> Context can not cast to Activity");
35 | }
36 | }
37 |
38 | private String[] mPermissions = {
39 | Manifest.permission.WRITE_EXTERNAL_STORAGE,
40 | Manifest.permission.READ_EXTERNAL_STORAGE
41 | };
42 | private Activity mActivity;
43 | private WatcherConfig mConfig;
44 | private View mTransitionView;
45 |
46 | private PictureWatcherManager(Activity activity) {
47 | this.mActivity = activity;
48 | }
49 |
50 | /**
51 | * 设置共享元素
52 | */
53 | public PictureWatcherManager setSharedElement(View transitionView) {
54 | mTransitionView = transitionView;
55 | return this;
56 | }
57 |
58 | /**
59 | * 设置图片预览的配置
60 | */
61 | public PictureWatcherManager setConfig(@NonNull WatcherConfig config) {
62 | this.mConfig = config;
63 | return this;
64 | }
65 |
66 | /**
67 | * 设置图片加载方案
68 | */
69 | public PictureWatcherManager setPictureLoader(@NonNull IPictureLoader loader) {
70 | PictureLoader.setPictureLoader(loader);
71 | return this;
72 | }
73 |
74 | /**
75 | * 调用图片查看器的方法
76 | */
77 | public void start() {
78 | startForResult(null);
79 | }
80 |
81 | /**
82 | * 调用图片查看器, 一般用于相册
83 | */
84 | public void startForResult(@Nullable final WatcherCallback callback) {
85 | // 请求权限
86 | PermissionsManager.getManager(mActivity)
87 | .request(mPermissions)
88 | .execute(new PermissionsCallback() {
89 | @Override
90 | public void onResult(boolean granted) {
91 | if (granted) {
92 | startForResultActual(callback);
93 | }
94 | }
95 | });
96 | }
97 |
98 | /**
99 | * 真正执行 Activity 的启动
100 | */
101 | private void startForResultActual(@Nullable final WatcherCallback callback) {
102 | verify();
103 | CallbackFragment callbackFragment = CallbackFragment.getInstance(mActivity);
104 | callbackFragment.setCallback(new CallbackFragment.Callback() {
105 | @Override
106 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
107 | if (resultCode != Activity.RESULT_OK || null == data || null == callback) {
108 | return;
109 | }
110 | switch (requestCode) {
111 | case PictureWatcherActivity.REQUEST_CODE:
112 | ArrayList paths = data.getStringArrayListExtra(
113 | PictureWatcherActivity.RESULT_EXTRA_PICKED_PICTURES);
114 | boolean isEnsure = data.getBooleanExtra(
115 | PictureWatcherActivity.RESULT_EXTRA_IS_PICKED_ENSURE, false);
116 | callback.onWatcherPickedComplete(isEnsure, paths);
117 | break;
118 | default:
119 | break;
120 | }
121 | }
122 | });
123 | PictureWatcherActivity.startActivityForResult(mActivity, callbackFragment, mConfig, mTransitionView);
124 | }
125 |
126 | /**
127 | * 验证 Activity 启动参数
128 | */
129 | private void verify() {
130 | if (PictureLoader.getPictureLoader() == null) {
131 | throw new UnsupportedOperationException("PictureLoader.load -> please invoke setPictureLoader first");
132 | }
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/watcher/SharedElementData.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.watcher;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 | import android.support.annotation.NonNull;
6 | import android.view.View;
7 |
8 | /**
9 | * @author Sharry Contact me.
10 | * @version 1.0
11 | * @since 3/15/2019 3:27 PM
12 | */
13 | public class SharedElementData implements Parcelable {
14 |
15 | static SharedElementData parseFrom(@NonNull View sharedElement, int sharedElementPosition) {
16 | SharedElementData result = new SharedElementData();
17 | int[] locations = new int[2];
18 | sharedElement.getLocationOnScreen(locations);
19 | result.startX = locations[0];
20 | result.startY = locations[1];
21 | result.width = sharedElement.getWidth();
22 | result.height = sharedElement.getHeight();
23 | result.sharedPosition = sharedElementPosition;
24 | return result;
25 | }
26 |
27 | public int startX;
28 | public int startY;
29 | public int width;
30 | public int height;
31 | public int sharedPosition;
32 |
33 | private SharedElementData() {
34 |
35 | }
36 |
37 | protected SharedElementData(Parcel in) {
38 | startX = in.readInt();
39 | startY = in.readInt();
40 | width = in.readInt();
41 | height = in.readInt();
42 | sharedPosition = in.readInt();
43 | }
44 |
45 | @Override
46 | public void writeToParcel(Parcel dest, int flags) {
47 | dest.writeInt(startX);
48 | dest.writeInt(startY);
49 | dest.writeInt(width);
50 | dest.writeInt(height);
51 | dest.writeInt(sharedPosition);
52 | }
53 |
54 | @Override
55 | public int describeContents() {
56 | return 0;
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | return "SharedElementData{" +
62 | "startX=" + startX +
63 | ", startY=" + startY +
64 | ", width=" + width +
65 | ", height=" + height +
66 | ", sharedPosition='" + sharedPosition + '\'' +
67 | '}';
68 | }
69 |
70 | public static final Creator CREATOR = new Creator() {
71 | @Override
72 | public SharedElementData createFromParcel(Parcel in) {
73 | return new SharedElementData(in);
74 | }
75 |
76 | @Override
77 | public SharedElementData[] newArray(int size) {
78 | return new SharedElementData[size];
79 | }
80 | };
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/watcher/WatcherCallback.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.watcher;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * Created by Sharry on 2018/6/13.
7 | * Email: SharryChooCHN@Gmail.com
8 | * Version: 1.0
9 | * Description: 图片选择器的回调
10 | */
11 | public interface WatcherCallback {
12 |
13 | /**
14 | * The callback method will call when pick picture from watcher complete.
15 | *
16 | * @param isEnsure is clicked ensure button.
17 | * @param pickedPictures picked pictures.
18 | */
19 | void onWatcherPickedComplete(boolean isEnsure, ArrayList pickedPictures);
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/watcher/WatcherConfig.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.watcher;
2 |
3 | import android.graphics.Color;
4 | import android.os.Parcel;
5 | import android.os.Parcelable;
6 | import android.support.annotation.ColorInt;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 |
10 | import java.util.ArrayList;
11 |
12 | /**
13 | * 图片查看器相关的配置
14 | *
15 | * @author Sharry Contact me.
16 | * @version 1.0
17 | * @since 2018/9/22 17:56
18 | */
19 | public class WatcherConfig implements Parcelable {
20 |
21 | public static Builder Builder() {
22 | return new Builder();
23 | }
24 |
25 | public static final Creator CREATOR = new Creator() {
26 | @Override
27 | public WatcherConfig createFromParcel(Parcel in) {
28 | return new WatcherConfig(in);
29 | }
30 |
31 | @Override
32 | public WatcherConfig[] newArray(int size) {
33 | return new WatcherConfig[size];
34 | }
35 | };
36 |
37 | // 图片选择的相关配置
38 | private ArrayList pictureUris; // 需要展示的集合
39 | private ArrayList userPickedSet; // 图片选中的集合: 根据这个判断是否提供图片选择功能
40 | private int threshold;// 阈值
41 | private int indicatorTextColor = Color.WHITE; // 指示器背景色
42 | private int indicatorSolidColor = Color.parseColor("#ff64b6f6"); // 指示器选中的填充色
43 | private int indicatorBorderCheckedColor = indicatorSolidColor; // 指示器边框选中的颜色
44 | private int indicatorBorderUncheckedColor = Color.WHITE; // 指示器边框未被选中的颜色
45 | private int position; // 定位展示的位置
46 |
47 | public WatcherConfig() {
48 | }
49 |
50 | protected WatcherConfig(Parcel in) {
51 | pictureUris = in.createStringArrayList();
52 | userPickedSet = in.createStringArrayList();
53 | threshold = in.readInt();
54 | indicatorTextColor = in.readInt();
55 | indicatorSolidColor = in.readInt();
56 | indicatorBorderCheckedColor = in.readInt();
57 | indicatorBorderUncheckedColor = in.readInt();
58 | position = in.readInt();
59 | }
60 |
61 | @Override
62 | public void writeToParcel(Parcel dest, int flags) {
63 | dest.writeStringList(pictureUris);
64 | dest.writeStringList(userPickedSet);
65 | dest.writeInt(threshold);
66 | dest.writeInt(indicatorTextColor);
67 | dest.writeInt(indicatorSolidColor);
68 | dest.writeInt(indicatorBorderCheckedColor);
69 | dest.writeInt(indicatorBorderUncheckedColor);
70 | dest.writeInt(position);
71 | }
72 |
73 | @Override
74 | public int describeContents() {
75 | return 0;
76 | }
77 |
78 | @NonNull
79 | public ArrayList getPictureUris() {
80 | return pictureUris;
81 | }
82 |
83 | @Nullable
84 | public ArrayList getUserPickedSet() {
85 | return userPickedSet;
86 | }
87 |
88 | public int getThreshold() {
89 | return threshold;
90 | }
91 |
92 | public int getIndicatorTextColor() {
93 | return indicatorTextColor;
94 | }
95 |
96 | public int getIndicatorSolidColor() {
97 | return indicatorSolidColor;
98 | }
99 |
100 | public int getIndicatorBorderCheckedColor() {
101 | return indicatorBorderCheckedColor;
102 | }
103 |
104 | public int getIndicatorBorderUncheckedColor() {
105 | return indicatorBorderUncheckedColor;
106 | }
107 |
108 | public int getPosition() {
109 | return position;
110 | }
111 |
112 | public boolean isPickerSupport() {
113 | return userPickedSet != null;
114 | }
115 |
116 | public Builder rebuild() {
117 | return new Builder(this);
118 | }
119 |
120 | public static class Builder {
121 |
122 | private WatcherConfig mConfig;
123 |
124 | private Builder() {
125 | mConfig = new WatcherConfig();
126 | }
127 |
128 | private Builder(@NonNull WatcherConfig config) {
129 | this.mConfig = config;
130 | }
131 |
132 | /**
133 | * 选择的最大阈值
134 | */
135 | public Builder setThreshold(int threshold) {
136 | mConfig.threshold = threshold;
137 | return this;
138 | }
139 |
140 | /**
141 | * 需要展示的 URI
142 | */
143 | public Builder setPictureUri(@NonNull String uri) {
144 | ArrayList pictureUris = new ArrayList<>();
145 | pictureUris.add(uri);
146 | setPictureUris(pictureUris, 0);
147 | return this;
148 | }
149 |
150 | /**
151 | * 需要展示的 URI 集合
152 | *
153 | * @param pictureUris 数据集合
154 | * @param position 展示的位置
155 | */
156 | public Builder setPictureUris(@NonNull ArrayList pictureUris, int position) {
157 | mConfig.pictureUris = pictureUris;
158 | mConfig.position = position;
159 | return this;
160 | }
161 |
162 | /**
163 | * 设置用户已经选中的图片, 会与 {@link #pictureUris} 比较, 在右上角打钩
164 | * 若为 null, 则不提供图片选择的功能
165 | *
166 | * @param pickedPictures 已选中的图片
167 | */
168 | public Builder setUserPickedSet(@Nullable ArrayList pickedPictures) {
169 | mConfig.userPickedSet = pickedPictures;
170 | return this;
171 | }
172 |
173 | /**
174 | * 设置选择索引的边框颜色
175 | *
176 | * @param textColor 边框的颜色
177 | */
178 | public Builder setIndicatorTextColor(@ColorInt int textColor) {
179 | mConfig.indicatorTextColor = textColor;
180 | return this;
181 | }
182 |
183 | /**
184 | * 设置选择索引的边框颜色
185 | *
186 | * @param solidColor 边框的颜色
187 | */
188 | public Builder setIndicatorSolidColor(@ColorInt int solidColor) {
189 | mConfig.indicatorSolidColor = solidColor;
190 | return this;
191 | }
192 |
193 | /**
194 | * 设置选择索引的边框颜色
195 | *
196 | * @param checkedColor 选中的边框颜色的 Res Id
197 | * @param uncheckedColor 未选中的边框颜色的Res Id
198 | */
199 | public Builder setIndicatorBorderColor(@ColorInt int checkedColor, @ColorInt int uncheckedColor) {
200 | mConfig.indicatorBorderCheckedColor = checkedColor;
201 | mConfig.indicatorBorderUncheckedColor = uncheckedColor;
202 | return this;
203 | }
204 |
205 | public WatcherConfig build() {
206 | return mConfig;
207 | }
208 |
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/watcher/WatcherPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.watcher;
2 |
3 | import android.support.v4.view.PagerAdapter;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * Created by Sharry on 2018/5/28.
11 | * Email: SharryChooCHN@Gmail.com
12 | * Version: 1.0
13 | * Description: 图片查看器的 Adapter
14 | */
15 | class WatcherPagerAdapter extends PagerAdapter {
16 |
17 | private List extends View> mViews;
18 |
19 | WatcherPagerAdapter(List extends View> children) {
20 | this.mViews = children;
21 | }
22 |
23 | /**
24 | * 获取子级布局的数量
25 | */
26 | @Override
27 | public int getCount() {
28 | return mViews.size();
29 | }
30 |
31 | /**
32 | * 判断某个 View 对象是否为当前被添加到 ViewPager 容器中的对象
33 | */
34 | @Override
35 | public boolean isViewFromObject(View view, Object object) {
36 | return view == object;
37 | }
38 |
39 | /**
40 | * 实例化 ViewPager 容器中指定的 position 位置需要显示的 View 对象
41 | */
42 | @Override
43 | public Object instantiateItem(ViewGroup container, int position) {
44 | View view = mViews.get(position);
45 | container.addView(view);
46 | return view;
47 | }
48 |
49 | /**
50 | * 在ViewPager中移除指定的 position 位置的 view 对象
51 | */
52 | @Override
53 | public void destroyItem(ViewGroup container, int position, Object object) {
54 | View view = mViews.get(position);
55 | container.removeView(view);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/watcher/WatcherPreviewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.watcher;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 |
9 | import com.sharry.picturepicker.support.loader.PictureLoader;
10 |
11 | import java.util.ArrayList;
12 |
13 | /**
14 | * 选中视图预览页面的 Adapter
15 | *
16 | * @author Sharry Contact me.
17 | * @version 1.0
18 | * @since 2018/9/22 23:23
19 | */
20 | class WatcherPreviewAdapter extends RecyclerView.Adapter {
21 |
22 | private final ArrayList userPickedSet;
23 | private final AdapterInteraction interaction;
24 |
25 | public WatcherPreviewAdapter(ArrayList userPickedSet, AdapterInteraction interaction) {
26 | this.userPickedSet = userPickedSet;
27 | this.interaction = interaction;
28 | }
29 |
30 | @NonNull
31 | @Override
32 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
33 | ImageView iv = new ImageView(parent.getContext());
34 | int size = parent.getHeight();
35 | iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
36 | iv.setLayoutParams(new ViewGroup.LayoutParams(size, size));
37 | iv.setPadding(size / 20, size / 20, size / 20, size / 20);
38 | return new ViewHolder(iv);
39 | }
40 |
41 | @Override
42 | public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
43 | PictureLoader.load(holder.ivPicture.getContext(), userPickedSet.get(position), holder.ivPicture);
44 | }
45 |
46 | @Override
47 | public int getItemCount() {
48 | return userPickedSet.size();
49 | }
50 |
51 | class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
52 |
53 | final ImageView ivPicture;
54 |
55 | ViewHolder(View itemView) {
56 | super(itemView);
57 | this.ivPicture = (ImageView) itemView;
58 | ivPicture.setOnClickListener(this);
59 | }
60 |
61 | @Override
62 | public void onClick(View v) {
63 | int position = getAdapterPosition();
64 | interaction.onPreviewItemClicked((ImageView) v, userPickedSet.get(position), position);
65 | }
66 | }
67 |
68 | public interface AdapterInteraction {
69 |
70 | void onPreviewItemClicked(ImageView imageView, String uri, int position);
71 |
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/CheckedIndicatorView.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.graphics.Point;
11 | import android.support.annotation.ColorInt;
12 | import android.support.v7.widget.AppCompatTextView;
13 | import android.util.AttributeSet;
14 | import android.util.TypedValue;
15 | import android.view.Gravity;
16 | import android.view.View;
17 | import android.view.animation.AnticipateInterpolator;
18 | import android.view.animation.OvershootInterpolator;
19 |
20 | /**
21 | * 用于展示图片被选中的索引 View
22 | *
23 | * @author Sharry Contact me.
24 | * @version 1.0
25 | * @since 2018/6/14 16:24
26 | */
27 | public class CheckedIndicatorView extends AppCompatTextView {
28 |
29 | // Dimension
30 | private int mBorderWidth;// 边框的宽度
31 | private int mBorderMargin;// 边框与填充部分的间距
32 | private int mRadius;// 绘制的半径
33 | private float mAnimPercent = 0f;
34 |
35 | // Color
36 | private final int INVALIDATE_VALUE = -1;
37 | private int mUncheckedBorderColor = Color.WHITE;// 未选中时边框的颜色
38 | private int mCheckedBorderColor = mUncheckedBorderColor;// 选中时的边框颜色
39 | private int mSolidColor = Color.BLUE;// 选中时内部填充的颜色
40 |
41 | // Paint
42 | private Paint mBorderPaint;
43 | private Paint mSolidPaint;
44 | private Point mCenterPoint;
45 |
46 | // 用于控制的变量
47 | private boolean mIsChecked = false;
48 | private boolean mIsAnimatorStarted = false;
49 |
50 | public CheckedIndicatorView(Context context) {
51 | this(context, null);
52 | }
53 |
54 | public CheckedIndicatorView(Context context, AttributeSet attrs) {
55 | this(context, attrs, 0);
56 | }
57 |
58 | public CheckedIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
59 | super(context, attrs, defStyleAttr);
60 | setGravity(Gravity.CENTER);
61 | init();
62 | }
63 |
64 | private void init() {
65 | mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
66 | mBorderPaint.setStyle(Paint.Style.STROKE);
67 | mBorderPaint.setColor(mUncheckedBorderColor);
68 | mSolidPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
69 | mSolidPaint.setColor(mSolidColor);
70 | mCenterPoint = new Point();
71 | // 设置一个默认的点击事件
72 | setOnClickListener(new OnClickListener() {
73 | @Override
74 | public void onClick(View v) {
75 | setChecked(!mIsChecked);
76 | }
77 | });
78 | }
79 |
80 | public void setChecked(boolean isChecked) {
81 | if (isChecked != mIsChecked) {
82 | if (mIsChecked) executeAnimator(false);
83 | else executeAnimator(true);
84 | }
85 | }
86 |
87 | public void setCheckedWithoutAnimator(boolean isChecked) {
88 | mIsChecked = isChecked;
89 | mAnimPercent = mIsChecked ? 1 : 0;
90 | invalidate();
91 | }
92 |
93 | public boolean isChecked() {
94 | return mIsChecked;
95 | }
96 |
97 | /**
98 | * 设置边框的颜色
99 | */
100 | public void setBorderColor(@ColorInt int checkedColor, @ColorInt int uncheckedColor) {
101 | if (checkedColor != INVALIDATE_VALUE) mCheckedBorderColor = checkedColor;
102 | if (uncheckedColor != INVALIDATE_VALUE) mUncheckedBorderColor = uncheckedColor;
103 | }
104 |
105 | /**
106 | * 设置填充的颜色
107 | */
108 | public void setSolidColor(@ColorInt int solidColor) {
109 | if (solidColor != INVALIDATE_VALUE) mSolidColor = solidColor;
110 | }
111 |
112 | /**
113 | * 动态配置字体的尺寸
114 | */
115 | public void setTextSize(int dip) {
116 | setTextSize(TypedValue.COMPLEX_UNIT_DIP, dip);
117 | }
118 |
119 | @Override
120 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
121 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
122 | // 可用的宽度
123 | int validateWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
124 | // 可用的高度
125 | int validateHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
126 | // 绘制的中心点
127 | mCenterPoint.x = getPaddingLeft() + validateWidth / 2;
128 | mCenterPoint.y = getPaddingTop() + validateHeight / 2;
129 | // 内圆的半径
130 | mRadius = Math.min(validateWidth, validateHeight) / 2;
131 | // 外部边框的宽度
132 | mBorderWidth = mRadius / 8;
133 | // 外部边框距离内圆的距离
134 | mBorderMargin = mBorderWidth;
135 | }
136 |
137 | @Override
138 | protected void onDraw(Canvas canvas) {
139 | // 绘制外环
140 | mBorderPaint.setColor(mIsChecked ? mCheckedBorderColor : mUncheckedBorderColor);
141 | mBorderPaint.setStrokeWidth(mBorderWidth);
142 | canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mRadius - mBorderWidth / 2, mBorderPaint);
143 | // 绘制内环
144 | mSolidPaint.setColor(mIsChecked ? mSolidColor : mUncheckedBorderColor);
145 | canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mAnimPercent
146 | * (mRadius - mBorderWidth - mBorderMargin), mSolidPaint);
147 | // 绘制文本
148 | if (mIsChecked) {
149 | super.onDraw(canvas);
150 | }
151 | }
152 |
153 | /**
154 | * 执行动画效果
155 | *
156 | * @param destIsChecked 最终选中的状态
157 | */
158 | private void executeAnimator(final boolean destIsChecked) {
159 | if (mIsAnimatorStarted) return;
160 | int start = destIsChecked ? 0 : 1;
161 | int end = destIsChecked ? 1 : 0;
162 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end).setDuration(destIsChecked ? 300 : 200);
163 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
164 | @Override
165 | public void onAnimationUpdate(ValueAnimator animation) {
166 | mAnimPercent = (float) animation.getAnimatedValue();
167 | invalidate();
168 | }
169 | });
170 | valueAnimator.addListener(new AnimatorListenerAdapter() {
171 | @Override
172 | public void onAnimationStart(Animator animation) {
173 | mIsChecked = destIsChecked;
174 | mIsAnimatorStarted = true;
175 | }
176 |
177 | @Override
178 | public void onAnimationEnd(Animator animation) {
179 | mIsAnimatorStarted = false;
180 | }
181 | });
182 | if (destIsChecked) {
183 | valueAnimator.setInterpolator(new OvershootInterpolator(2f));
184 | } else {
185 | valueAnimator.setInterpolator(new AnticipateInterpolator(2f));
186 | }
187 | valueAnimator.start();
188 | }
189 |
190 | }
191 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/PicturePickerFabBehavior.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.AnimatorSet;
6 | import android.animation.ObjectAnimator;
7 | import android.content.Context;
8 | import android.support.annotation.NonNull;
9 | import android.support.design.widget.CoordinatorLayout;
10 | import android.support.design.widget.FloatingActionButton;
11 | import android.support.v4.view.ViewCompat;
12 | import android.support.v7.widget.RecyclerView;
13 | import android.util.AttributeSet;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 |
17 | /**
18 | * FloatingActionButton 的 CoordinateLayout 的 Behavior 动画
19 | * Used in xml {@code #R.layout.libpicturepicker_activity_picture_picker}
20 | *
21 | * @author Sharry Contact me.
22 | * @version 1.1
23 | * @since 2018/9/18 16:25
24 | */
25 | public class PicturePickerFabBehavior extends CoordinatorLayout.Behavior {
26 |
27 | public static PicturePickerFabBehavior from(View view) {
28 | ViewGroup.LayoutParams params = view.getLayoutParams();
29 | if (!(params instanceof CoordinatorLayout.LayoutParams)) {
30 | throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
31 | } else {
32 | CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params).getBehavior();
33 | if (!(behavior instanceof PicturePickerFabBehavior)) {
34 | throw new IllegalArgumentException("The view is not associated with PicturePickerFabBehavior");
35 | } else {
36 | return (PicturePickerFabBehavior) behavior;
37 | }
38 | }
39 | }
40 |
41 | private AnimatorSet mAppearAnimatorSet;
42 | private AnimatorSet mDismissAnimatorSet;
43 | private boolean mIsValid = true;
44 |
45 | public PicturePickerFabBehavior() {
46 | }
47 |
48 | public PicturePickerFabBehavior(Context context, AttributeSet attrs) {
49 | super(context, attrs);
50 | }
51 |
52 | /**
53 | * 设置这个 behavior 是否可用
54 | *
55 | * @param isValid if true the behavior is valid, if false the behavior is invalided.
56 | */
57 | public void setBehaviorValid(boolean isValid) {
58 | mIsValid = isValid;
59 | }
60 |
61 | /**
62 | * 设置依赖的控件
63 | */
64 | @Override
65 | public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
66 | return dependency instanceof RecyclerView;
67 | }
68 |
69 | @Override
70 | public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
71 | @NonNull FloatingActionButton child,
72 | @NonNull View directTargetChild,
73 | @NonNull View target, int axes, int type) {
74 | return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
75 | }
76 |
77 | @Override
78 | public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child,
79 | View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
80 | int dyUnconsumed, int type) {
81 | if (!mIsValid) return;
82 | // 上滑
83 | if (dyConsumed > 0 && dyUnconsumed == 0) {
84 | setAnimator(child, true);
85 | }
86 |
87 | // 到了边界还在上滑
88 | if (dyConsumed == 0 && dyUnconsumed > 0) {
89 | setAnimator(child, true);
90 | }
91 |
92 | // 下滑
93 | if (dyConsumed < 0 && dyUnconsumed == 0) {
94 | setAnimator(child, false);
95 | }
96 |
97 | // 到了边界, 还在下滑
98 | if (dyConsumed == 0 && dyUnconsumed < 0) {
99 | setAnimator(child, false);
100 | }
101 |
102 | }
103 |
104 | /**
105 | * 处理动画效果
106 | */
107 | private void setAnimator(View target, final boolean isUp) {
108 | if (getAppearAnimator(target).isRunning() || getDismissAnimator(target).isRunning()) return;
109 | if (isUp) {// 处理上滑显示
110 | if (target.getVisibility() == View.INVISIBLE) {
111 | getAppearAnimator(target).start();
112 | }
113 | } else {// 处理下滑消失
114 | if (target.getVisibility() == View.VISIBLE) {
115 | getDismissAnimator(target).start();
116 | }
117 | }
118 | }
119 |
120 | /**
121 | * 获取呈现动画
122 | */
123 | private AnimatorSet getAppearAnimator(final View target) {
124 | if (mAppearAnimatorSet == null) {
125 | mAppearAnimatorSet = new AnimatorSet();
126 | mAppearAnimatorSet.playTogether(
127 | ObjectAnimator.ofFloat(target, "scaleX", 0f, 1f),
128 | ObjectAnimator.ofFloat(target, "scaleY", 0f, 1f)
129 | );
130 | mAppearAnimatorSet.addListener(new AnimatorListenerAdapter() {
131 | @Override
132 | public void onAnimationStart(Animator animation) {
133 | target.setVisibility(View.VISIBLE);
134 | }
135 | });
136 | mAppearAnimatorSet.setDuration(200);
137 | }
138 | return mAppearAnimatorSet;
139 | }
140 |
141 | /**
142 | * 获取消失动画
143 | */
144 | private AnimatorSet getDismissAnimator(final View target) {
145 | // 当且仅当处于 上滑状态, Animator动画结束, 且fab为可见状态时才执行下列方法
146 | if (mDismissAnimatorSet == null) {
147 | mDismissAnimatorSet = new AnimatorSet();
148 | mDismissAnimatorSet.playTogether(
149 | ObjectAnimator.ofFloat(target, "scaleX", 1f, 0f),
150 | ObjectAnimator.ofFloat(target, "scaleY", 1f, 0f)
151 | );
152 | mDismissAnimatorSet.addListener(new AnimatorListenerAdapter() {
153 | @Override
154 | public void onAnimationStart(Animator animation) {
155 | target.setVisibility(View.VISIBLE);
156 | }
157 |
158 | @Override
159 | public void onAnimationEnd(Animator animation) {
160 | target.setVisibility(View.INVISIBLE);
161 | }
162 | });
163 | mDismissAnimatorSet.setDuration(200);
164 | }
165 | return mDismissAnimatorSet;
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/Compat.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.sharry.picturepicker.widget.photoview;
17 |
18 | import android.annotation.TargetApi;
19 | import android.os.Build.VERSION;
20 | import android.os.Build.VERSION_CODES;
21 | import android.view.View;
22 |
23 | class Compat {
24 |
25 | private static final int SIXTY_FPS_INTERVAL = 1000 / 60;
26 |
27 | public static void postOnAnimation(View view, Runnable runnable) {
28 | if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
29 | postOnAnimationJellyBean(view, runnable);
30 | } else {
31 | view.postDelayed(runnable, SIXTY_FPS_INTERVAL);
32 | }
33 | }
34 |
35 | @TargetApi(16)
36 | private static void postOnAnimationJellyBean(View view, Runnable runnable) {
37 | view.postOnAnimation(runnable);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/OnGestureListener.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.sharry.picturepicker.widget.photoview;
17 |
18 | interface OnGestureListener {
19 |
20 | void onDrag(float dx, float dy);
21 |
22 | void onFling(float startX, float startY, float velocityX,
23 | float velocityY);
24 |
25 | void onScale(float scaleFactor, float focusX, float focusY);
26 |
27 | }
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/OnMatrixChangedListener.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.photoview;
2 |
3 | import android.graphics.RectF;
4 |
5 | /**
6 | * Interface definition for a callback to be invoked when the internal Matrix has changed for
7 | * this View.
8 | */
9 | public interface OnMatrixChangedListener {
10 |
11 | /**
12 | * Callback for when the Matrix displaying the Drawable has changed. This could be because
13 | * the View's bounds have changed, or the user has zoomed.
14 | *
15 | * @param rect - Rectangle displaying the Drawable's new bounds.
16 | */
17 | void onMatrixChanged(RectF rect);
18 | }
19 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/OnOutsidePhotoTapListener.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.photoview;
2 |
3 | import android.widget.ImageView;
4 |
5 | /**
6 | * Callback when the user tapped outside of the photo
7 | */
8 | public interface OnOutsidePhotoTapListener {
9 |
10 | /**
11 | * The outside of the photo has been tapped
12 | */
13 | void onOutsidePhotoTap(ImageView imageView);
14 | }
15 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/OnPhotoTapListener.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.photoview;
2 |
3 | import android.widget.ImageView;
4 |
5 | /**
6 | * A callback to be invoked when the Photo is tapped with a single
7 | * tap.
8 | */
9 | public interface OnPhotoTapListener {
10 |
11 | /**
12 | * A callback to receive where the user taps on a photo. You will only receive a callback if
13 | * the user taps on the actual photo, tapping on 'whitespace' will be ignored.
14 | *
15 | * @param view ImageView the user tapped.
16 | * @param x where the user tapped from the of the Drawable, as percentage of the
17 | * Drawable width.
18 | * @param y where the user tapped from the top of the Drawable, as percentage of the
19 | * Drawable height.
20 | */
21 | void onPhotoTap(ImageView view, float x, float y);
22 | }
23 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/OnScaleChangedListener.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.photoview;
2 |
3 |
4 | /**
5 | * Interface definition for callback to be invoked when attached ImageView scale changes
6 | */
7 | public interface OnScaleChangedListener {
8 |
9 | /**
10 | * Callback for when the scale changes
11 | *
12 | * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 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 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/OnSingleFlingListener.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.photoview;
2 |
3 | import android.view.MotionEvent;
4 |
5 | /**
6 | * A callback to be invoked when the ImageView is flung with a single
7 | * touch
8 | */
9 | public interface OnSingleFlingListener {
10 |
11 | /**
12 | * A callback to receive where the user flings on a ImageView. You will receive a callback if
13 | * the user flings anywhere on the view.
14 | *
15 | * @param e1 MotionEvent the user first touch.
16 | * @param e2 MotionEvent the user last touch.
17 | * @param velocityX distance of user's horizontal fling.
18 | * @param velocityY distance of user's vertical fling.
19 | */
20 | boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
21 | }
22 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/OnViewDragListener.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.photoview;
2 |
3 | /**
4 | * Interface definition for a callback to be invoked when the photo is experiencing a drag event
5 | */
6 | public interface OnViewDragListener {
7 |
8 | /**
9 | * Callback for when the photo is experiencing a drag event. This cannot be invoked when the
10 | * user is scaling.
11 | *
12 | * @param dx The change of the coordinates in the x-direction
13 | * @param dy The change of the coordinates in the y-direction
14 | */
15 | void onDrag(float dx, float dy);
16 | }
17 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/OnViewTapListener.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.photoview;
2 |
3 | import android.view.View;
4 |
5 | public interface OnViewTapListener {
6 |
7 | /**
8 | * A callback to receive where the user taps on a ImageView. You will receive a callback if
9 | * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.
10 | *
11 | * @param view - View the user tapped.
12 | * @param x - where the user tapped from the left of the View.
13 | * @param y - where the user tapped from the top of the View.
14 | */
15 | void onViewTap(View view, float x, float y);
16 | }
17 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/photoview/Util.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.photoview;
2 |
3 | import android.view.MotionEvent;
4 | import android.widget.ImageView;
5 |
6 | class Util {
7 |
8 | static void checkZoomLevels(float minZoom, float midZoom,
9 | float maxZoom) {
10 | if (minZoom >= midZoom) {
11 | throw new IllegalArgumentException(
12 | "Minimum zoom has to be less than Medium zoom. Call setMinimumZoom() with a more appropriate value");
13 | } else if (midZoom >= maxZoom) {
14 | throw new IllegalArgumentException(
15 | "Medium zoom has to be less than Maximum zoom. Call setMaximumZoom() with a more appropriate value");
16 | }
17 | }
18 |
19 | static boolean hasDrawable(ImageView imageView) {
20 | return imageView.getDrawable() != null;
21 | }
22 |
23 | static boolean isSupportedScaleType(final ImageView.ScaleType scaleType) {
24 | if (scaleType == null) {
25 | return false;
26 | }
27 | switch (scaleType) {
28 | case MATRIX:
29 | throw new IllegalStateException("Matrix scale type is not supported");
30 | }
31 | return true;
32 | }
33 |
34 | static int getPointerIndex(int action) {
35 | return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/toolbar/AppBarHelper.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.toolbar;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.graphics.Color;
7 | import android.os.Build;
8 | import android.util.TypedValue;
9 | import android.view.View;
10 | import android.view.Window;
11 |
12 | /**
13 | * 变更 App bar 的风格的帮助类
14 | *
15 | * @author Sharry Contact me.
16 | * @version 1.0
17 | * @since 2018/8/27 23:41
18 | */
19 | class AppBarHelper {
20 |
21 | /**
22 | * Get AppBarHelper instance with this factory method.
23 | */
24 | static AppBarHelper with(Context context) {
25 | return new AppBarHelper(context);
26 | }
27 |
28 | private static final int DEFAULT_OPTIONS = 0;
29 | private int mOptions = DEFAULT_OPTIONS;
30 | private Activity mActivity;
31 | private Window mWindow;
32 |
33 | private AppBarHelper(Context context) {
34 | if (context instanceof Activity) {
35 | mActivity = (Activity) context;
36 | mWindow = mActivity.getWindow();
37 | } else {
38 | throw new IllegalArgumentException("Please ensure context instance of Activity.");
39 | }
40 | }
41 |
42 | /**
43 | * 设置StatusBar的风格
44 | */
45 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
46 | AppBarHelper setStatusBarStyle(Style style) {
47 | if (!Utils.isLollipop()) return this;
48 | switch (style) {
49 | // 设置状态栏为全透明
50 | case TRANSPARENT: {
51 | int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
52 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
53 | mOptions = mOptions | option;
54 | mWindow.setStatusBarColor(Color.TRANSPARENT);
55 | break;
56 | }
57 | // 设置状态栏为半透明
58 | case TRANSLUCENCE: {
59 | int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
60 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
61 | mOptions = mOptions | option;
62 | mWindow.setStatusBarColor(Utils.alphaColor(Color.BLACK, 0.3f));
63 | break;
64 | }
65 | // 隐藏状态栏
66 | case HIDE: {
67 | int option = View.SYSTEM_UI_FLAG_FULLSCREEN;
68 | mOptions = mOptions | option;
69 | break;
70 | }
71 | // 清除透明状态栏
72 | case DEFAULT: {
73 | mOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
74 | //获取当前Application主题中的状态栏Color
75 | TypedValue typedValue = new TypedValue();
76 | mActivity.getTheme().resolveAttribute(android.R.attr.colorPrimaryDark, typedValue, true);
77 | int color = typedValue.data;
78 | mWindow.setStatusBarColor(color);
79 | }
80 | }
81 | return this;
82 | }
83 |
84 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
85 | AppBarHelper setStatusBarColor(int color) {
86 | if (!Utils.isLollipop()) return this;
87 | mOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
88 | mWindow.setStatusBarColor(color);
89 | return this;
90 | }
91 |
92 | /**
93 | * 设置NavigationBar的风格
94 | */
95 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
96 | AppBarHelper setNavigationBarStyle(Style style) {
97 | if (!Utils.isLollipop()) return this;
98 | switch (style) {
99 | // 设置导航栏为全透明
100 | case TRANSPARENT: {
101 | int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
102 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
103 | mOptions = mOptions | option;
104 | mWindow.setNavigationBarColor(Color.TRANSPARENT);
105 | break;
106 | }
107 | // 设置导航栏为半透明
108 | case TRANSLUCENCE: {
109 | int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
110 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
111 | mOptions = mOptions | option;
112 | mWindow.setNavigationBarColor(Utils.alphaColor(Color.BLACK, 0.3f));
113 | break;
114 | }
115 | //隐藏导航栏
116 | case HIDE: {
117 | int option = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
118 | mOptions = mOptions | option;
119 | break;
120 | }
121 | case DEFAULT: {
122 | mOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
123 | //获取当前Activity主题中的color
124 | TypedValue typedValue = new TypedValue();
125 | mActivity.getTheme().resolveAttribute(android.R.attr.colorPrimaryDark, typedValue, true);
126 | int color = typedValue.data;
127 | mWindow.setNavigationBarColor(color);
128 | }
129 | }
130 | return this;
131 | }
132 |
133 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
134 | AppBarHelper setNavigationBarColor(int color) {
135 | if (!Utils.isLollipop()) return this;
136 | mOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
137 | mWindow.setNavigationBarColor(color);
138 | return this;
139 | }
140 |
141 | /**
142 | * 隐藏所有Bar(全屏模式)
143 | */
144 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
145 | AppBarHelper setAllBarsHide() {
146 | if (!Utils.isLollipop()) return this;
147 | int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
148 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
149 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
150 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
151 | | View.SYSTEM_UI_FLAG_FULLSCREEN
152 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
153 | mOptions = option;
154 | return this;
155 | }
156 |
157 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
158 | void apply() {
159 | if (!Utils.isLollipop()) return;
160 | if (mOptions != DEFAULT_OPTIONS) {
161 | View decorView = mWindow.getDecorView();
162 | decorView.setSystemUiVisibility(mOptions);
163 | }
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/toolbar/ImageViewOptions.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.toolbar;
2 |
3 | import android.support.annotation.Dimension;
4 | import android.support.annotation.DrawableRes;
5 | import android.support.annotation.NonNull;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import static android.support.annotation.Dimension.PX;
11 |
12 | /**
13 | * Options associated with ImageView.
14 | *
15 | * @author Sharry Contact me.
16 | * @version 1.0
17 | * @since 2018/9/28 8:50
18 | */
19 | public class ImageViewOptions implements Options {
20 |
21 | /*
22 | Constants
23 | */
24 | static final int UN_INITIALIZE_RES_ID = -1;
25 | static final ImageView.ScaleType DEFAULT_SCALE_TYPE = ImageView.ScaleType.CENTER_CROP;
26 | static final int DEFAULT_WIDTH = ViewGroup.LayoutParams.WRAP_CONTENT;
27 | static final int DEFAULT_Height = ViewGroup.LayoutParams.WRAP_CONTENT;
28 | static final int DEFAULT_PADDING = 0;
29 | /*
30 | Fields associated with image menu.
31 | */
32 | @DrawableRes
33 | int drawableResId = UN_INITIALIZE_RES_ID;
34 | ImageView.ScaleType scaleType = DEFAULT_SCALE_TYPE;
35 | // Widget padding
36 | @Dimension(unit = PX)
37 | int paddingLeft = DEFAULT_PADDING;
38 | @Dimension(unit = PX)
39 | int paddingRight = DEFAULT_PADDING;
40 | // Layout params
41 | @Dimension(unit = PX)
42 | int widthExcludePadding = DEFAULT_WIDTH;
43 | @Dimension(unit = PX)
44 | int heightExcludePadding = DEFAULT_Height;
45 | // listener callback.
46 | View.OnClickListener listener = null;
47 |
48 | /**
49 | * U can get Builder instance from here.
50 | */
51 | public static Builder Builder() {
52 | return new Builder();
53 | }
54 |
55 | private ImageViewOptions() {
56 | }
57 |
58 | /**
59 | * U can rebuild Options instance from here.
60 | */
61 | public Builder newBuilder() {
62 | return new Builder(this);
63 | }
64 |
65 | @Override
66 | public void completion(ImageView view) {
67 | // Set padding.
68 | view.setPadding(paddingLeft, 0, paddingRight, 0);
69 | // Set the layout parameters associated with this textView.
70 | int validWidth = Utils.isLayoutParamsSpecialValue(widthExcludePadding) ? widthExcludePadding :
71 | widthExcludePadding + view.getPaddingLeft() + view.getPaddingRight();
72 | int validHeight = Utils.isLayoutParamsSpecialValue(heightExcludePadding) ? heightExcludePadding :
73 | heightExcludePadding + view.getPaddingTop() + view.getPaddingBottom();
74 | ViewGroup.LayoutParams params = view.getLayoutParams();
75 | if (null == params) {
76 | params = new ViewGroup.LayoutParams(validWidth, validHeight);
77 | } else {
78 | params.width = validWidth;
79 | params.height = validHeight;
80 | }
81 | view.setLayoutParams(params);
82 | // Set OnClickListener
83 | if (null != listener) {
84 | view.setOnClickListener(listener);
85 | }
86 | // Set some fields associated with this imageView.
87 | view.setImageResource(drawableResId);
88 | view.setScaleType(scaleType);
89 | }
90 |
91 | /**
92 | * Copy values from other instance.
93 | */
94 | private void copyFrom(@NonNull ImageViewOptions other) {
95 | this.drawableResId = other.drawableResId;
96 | this.scaleType = other.scaleType;
97 | this.paddingLeft = other.paddingLeft;
98 | this.paddingRight = other.paddingRight;
99 | this.heightExcludePadding = other.heightExcludePadding;
100 | this.widthExcludePadding = other.widthExcludePadding;
101 | this.listener = other.listener;
102 | }
103 |
104 | /**
105 | * Builder Options instance more easier.
106 | */
107 | public static class Builder {
108 |
109 | private ImageViewOptions op;
110 |
111 | private Builder() {
112 | op = new ImageViewOptions();
113 | }
114 |
115 | private Builder(@NonNull ImageViewOptions other) {
116 | this();
117 | op.copyFrom(other);
118 | }
119 |
120 | public Builder setDrawableResId(@DrawableRes int drawableResId) {
121 | op.drawableResId = drawableResId;
122 | return this;
123 | }
124 |
125 | public Builder setScaleType(ImageView.ScaleType scaleType) {
126 | op.scaleType = scaleType;
127 | return this;
128 | }
129 |
130 | public Builder setPaddingLeft(@Dimension(unit = PX) int paddingLeft) {
131 | op.paddingLeft = paddingLeft;
132 | return this;
133 | }
134 |
135 | public Builder setPaddingRight(@Dimension(unit = PX) int paddingRight) {
136 | op.paddingRight = paddingRight;
137 | return this;
138 | }
139 |
140 | public Builder setWidthWithoutPadding(@Dimension(unit = PX) int widthExcludePadding) {
141 | op.widthExcludePadding = widthExcludePadding;
142 | return this;
143 | }
144 |
145 | public Builder setHeightWithoutPadding(@Dimension(unit = PX) int heightExcludePadding) {
146 | op.heightExcludePadding = heightExcludePadding;
147 | return this;
148 | }
149 |
150 | public Builder setListener(View.OnClickListener listener) {
151 | op.listener = listener;
152 | return this;
153 | }
154 |
155 | public ImageViewOptions build() {
156 | return op;
157 | }
158 |
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/toolbar/Options.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.toolbar;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Options associated with
7 | *
8 | * @author Sharry Contact me.
9 | * @version 1.0
10 | * @since 2018/9/28 8:43
11 | */
12 | public interface Options {
13 |
14 | /**
15 | * U can use this options to completion view
16 | */
17 | void completion(T view);
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/toolbar/Style.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.toolbar;
2 |
3 | /**
4 | * StatusBar/NavigationBar 的样式
5 | *
6 | * @author Sharry Contact me.
7 | * @version 1.2
8 | * @since 2017/10/10 13:52
9 | */
10 | public enum Style {
11 |
12 | TRANSPARENT(0),
13 | TRANSLUCENCE(1),
14 | HIDE(3),
15 | DEFAULT(4);
16 |
17 | int val;
18 |
19 | Style(int val) {
20 | this.val = val;
21 | }
22 |
23 | int getVal() {
24 | return val;
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/toolbar/TextViewOptions.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.toolbar;
2 |
3 | import android.graphics.Color;
4 | import android.support.annotation.ColorInt;
5 | import android.support.annotation.Dimension;
6 | import android.support.annotation.NonNull;
7 | import android.text.TextUtils;
8 | import android.util.TypedValue;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.TextView;
12 |
13 | import static android.support.annotation.Dimension.PX;
14 | import static android.support.annotation.Dimension.SP;
15 |
16 | /**
17 | * Options associated with TextView.
18 | *
19 | * @author Sharry Contact me.
20 | * @version 1.0
21 | * @since 2018/9/28 8:49
22 | */
23 | public class TextViewOptions implements Options {
24 |
25 | /**
26 | * U can get Builder instance from here.
27 | */
28 | public static Builder Builder() {
29 | return new Builder();
30 | }
31 |
32 | /*
33 | Constants
34 | */
35 | static final int UN_INITIALIZE_TEXT_SIZE = 0;
36 | static final int DEFAULT_TEXT_COLOR = Color.WHITE;
37 | static final int DEFAULT_TITLE_TEXT_SIZE = 18;
38 | static final int DEFAULT_MENU_TEXT_SIZE = 13;
39 | static final int DEFAULT_MAX_EMS = 8;
40 | static final int DEFAULT_LINES = 1;
41 | static final int DEFAULT_PADDING = 0;
42 | static final TextUtils.TruncateAt DEFAULT_ELLIPSIZE = TextUtils.TruncateAt.END;
43 |
44 | /*
45 | Fields
46 | */
47 | CharSequence text;
48 | @Dimension(unit = SP)
49 | int textSize = UN_INITIALIZE_TEXT_SIZE;
50 | @ColorInt
51 | int textColor = DEFAULT_TEXT_COLOR;
52 | int maxEms = DEFAULT_MAX_EMS;
53 | int lines = DEFAULT_LINES;
54 | TextUtils.TruncateAt ellipsize = DEFAULT_ELLIPSIZE;
55 | // Widget padding
56 | @Dimension(unit = PX)
57 | int paddingLeft = DEFAULT_PADDING;
58 | @Dimension(unit = PX)
59 | int paddingRight = DEFAULT_PADDING;
60 | // listener callback.
61 | View.OnClickListener listener = null;
62 |
63 | private TextViewOptions() {
64 | }
65 |
66 | /**
67 | * U can rebuild Options instance from here.
68 | */
69 | public Builder newBuilder() {
70 | return new Builder(this);
71 | }
72 |
73 | @Override
74 | public void completion(TextView textView) {
75 | // Set padding.
76 | textView.setPadding(paddingLeft, 0, paddingRight, 0);
77 | ViewGroup.LayoutParams params = textView.getLayoutParams();
78 | if (null == params) {
79 | params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
80 | ViewGroup.LayoutParams.MATCH_PARENT);
81 | } else {
82 | params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
83 | params.height = ViewGroup.LayoutParams.MATCH_PARENT;
84 | }
85 | textView.setLayoutParams(params);
86 | // Set OnClickListener
87 | if (null != listener) {
88 | textView.setOnClickListener(listener);
89 | }
90 | // Set some fields associated with this textView.
91 | textView.setText(text);
92 | textView.setTextColor(textColor);
93 | textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
94 | textView.setMaxEms(maxEms);
95 | textView.setLines(lines);
96 | textView.setEllipsize(ellipsize);
97 | }
98 |
99 | /**
100 | * Copy values from other instance.
101 | */
102 | private void copyFrom(@NonNull TextViewOptions other) {
103 | this.text = other.text;
104 | this.textSize = other.textSize;
105 | this.textColor = other.textColor;
106 | this.maxEms = other.maxEms;
107 | this.lines = other.lines;
108 | this.ellipsize = other.ellipsize;
109 | this.paddingLeft = other.paddingLeft;
110 | this.paddingRight = other.paddingRight;
111 | this.listener = other.listener;
112 | }
113 |
114 | /**
115 | * Builder TextOptions instance more easier.
116 | */
117 | public static class Builder {
118 |
119 | private TextViewOptions op;
120 |
121 | private Builder() {
122 | op = new TextViewOptions();
123 | }
124 |
125 | private Builder(@NonNull TextViewOptions other) {
126 | this();
127 | op.copyFrom(other);
128 | }
129 |
130 | public Builder setText(@NonNull CharSequence text) {
131 | op.text = text;
132 | return this;
133 | }
134 |
135 | public Builder setTextSize(@Dimension(unit = SP) int textSize) {
136 | op.textSize = textSize;
137 | return this;
138 | }
139 |
140 | public Builder setTextColor(@ColorInt int textColor) {
141 | op.textColor = textColor;
142 | return this;
143 | }
144 |
145 | public Builder setMaxEms(int maxEms) {
146 | op.maxEms = maxEms;
147 | return this;
148 | }
149 |
150 | public Builder setLines(int lines) {
151 | op.lines = lines;
152 | return this;
153 | }
154 |
155 | public Builder setEllipsize(TextUtils.TruncateAt ellipsize) {
156 | op.ellipsize = ellipsize;
157 | return this;
158 | }
159 |
160 | public Builder setPaddingLeft(@Dimension(unit = PX) int paddingLeft) {
161 | op.paddingLeft = paddingLeft;
162 | return this;
163 | }
164 |
165 | public Builder setPaddingRight(@Dimension(unit = PX) int paddingRight) {
166 | op.paddingRight = paddingRight;
167 | return this;
168 | }
169 |
170 | public Builder setListener(View.OnClickListener listener) {
171 | op.listener = listener;
172 | return this;
173 | }
174 |
175 | public TextViewOptions build() {
176 | if (null == op.text) {
177 | throw new UnsupportedOperationException("Please ensure text field nonnull.");
178 | }
179 | return op;
180 | }
181 |
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/toolbar/Utils.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.toolbar;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 | import android.util.TypedValue;
6 | import android.view.ViewGroup;
7 |
8 | import java.util.Collection;
9 |
10 | /**
11 | * @author Sharry Contact me.
12 | * @version 1.0
13 | * @since 2018/8/27 23:21
14 | */
15 | class Utils {
16 |
17 | Utils() {
18 | throw new UnsupportedOperationException(this + " cannot be instantiated");
19 | }
20 |
21 | /**
22 | * 判断是否为 5.0 以上的系统
23 | *
24 | * @return if true is over Lollipop, false is below Lollipop.
25 | */
26 | static boolean isLollipop() {
27 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
28 | }
29 |
30 | /**
31 | * 判断 Map 是否为空
32 | */
33 | static boolean isNotEmpty(Collection collection) {
34 | return null != collection && collection.size() != 0;
35 | }
36 |
37 | /**
38 | * 是否为 LayoutParams 特殊的参数
39 | */
40 | static boolean isLayoutParamsSpecialValue(int paramsValue) {
41 | return ViewGroup.LayoutParams.MATCH_PARENT == paramsValue
42 | || ViewGroup.LayoutParams.WRAP_CONTENT == paramsValue;
43 | }
44 |
45 | /**
46 | * @param baseColor 需要进行透明的Color
47 | * @param alphaPercent 透明图(0-1)
48 | */
49 | static int alphaColor(int baseColor, float alphaPercent) {
50 | if (alphaPercent < 0) alphaPercent = 0f;
51 | if (alphaPercent > 1) alphaPercent = 1f;
52 | // 计算基础透明度
53 | int baseAlpha = (baseColor & 0xff000000) >>> 24;
54 | // 根基需求计算透明度
55 | int alpha = (int) (baseAlpha * alphaPercent);
56 | // 根基透明度拼接新的color
57 | return alpha << 24 | (baseColor & 0xffffff);
58 | }
59 |
60 | /**
61 | * Dip convert 2 pixel
62 | */
63 | static int dp2px(Context context, float dp) {
64 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
65 | context.getResources().getDisplayMetrics());
66 | }
67 |
68 | /**
69 | * Pixel convert 2 dip
70 | */
71 | static int px2dp(Context context, float px) {
72 | float scale = context.getResources().getDisplayMetrics().density;
73 | return (int) (px / scale + 0.5f);
74 | }
75 |
76 | /**
77 | * Get action bar heightExcludePadding associated with the app.
78 | */
79 | static int getActionBarHeight(Context context) {
80 | TypedValue typedValue = new TypedValue();
81 | // 将属性解析到TypedValue中
82 | context.getTheme().resolveAttribute(android.R.attr.actionBarSize, typedValue, true);
83 | return TypedValue.complexToDimensionPixelSize(typedValue.data,
84 | context.getResources().getDisplayMetrics());
85 | }
86 |
87 | /**
88 | * Get status bar heightExcludePadding associated with the app.
89 | */
90 | static int getStatusBarHeight(Context context) {
91 | int resourceId = context.getResources().getIdentifier("status_bar_height",
92 | "dimen", "android");
93 | return resourceId > 0 ? context.getResources()
94 | .getDimensionPixelSize(resourceId) : 0;
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/java/com/sharry/picturepicker/widget/toolbar/ViewOptions.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker.widget.toolbar;
2 |
3 | import android.support.annotation.Dimension;
4 | import android.support.annotation.IntDef;
5 | import android.support.annotation.NonNull;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import java.lang.annotation.Retention;
10 | import java.lang.annotation.RetentionPolicy;
11 |
12 | import static android.support.annotation.Dimension.PX;
13 |
14 | /**
15 | * Options associated with view.
16 | *
17 | * @author Sharry Contact me.
18 | * @version 1.0
19 | * @since 2018/9/28 8:48
20 | */
21 | public class ViewOptions implements Options {
22 |
23 | /*
24 | Constants
25 | */
26 | static final int DEFAULT_VISIBILITY = View.VISIBLE;
27 | static final int DEFAULT_WIDTH = ViewGroup.LayoutParams.WRAP_CONTENT;
28 | static final int DEFAULT_HEIGHT = ViewGroup.LayoutParams.WRAP_CONTENT;
29 | static final int DEFAULT_PADDING = 0;
30 |
31 | @IntDef({View.VISIBLE, View.INVISIBLE, View.GONE})
32 | @Retention(RetentionPolicy.SOURCE)
33 | @interface Visibility {
34 | }
35 |
36 | int visibility = DEFAULT_VISIBILITY;
37 | // Widget padding
38 | @Dimension(unit = PX)
39 | int paddingLeft = DEFAULT_PADDING;
40 | @Dimension(unit = PX)
41 | int paddingTop = DEFAULT_PADDING;
42 | @Dimension(unit = PX)
43 | int paddingRight = DEFAULT_PADDING;
44 | @Dimension(unit = PX)
45 | int paddingBottom = DEFAULT_PADDING;
46 | // Layout params
47 | @Dimension(unit = PX)
48 | int widthExcludePadding = DEFAULT_WIDTH;
49 | @Dimension(unit = PX)
50 | int heightExcludePadding = DEFAULT_HEIGHT;
51 | // listener callback.
52 | View.OnClickListener listener = null;
53 |
54 | private ViewOptions() {
55 | }
56 |
57 | public Builder newBuilder() {
58 | return new Builder(this);
59 | }
60 |
61 | @Override
62 | public void completion(View view) {
63 | view.setVisibility(visibility);
64 | // Set padding.
65 | view.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
66 | // Set the layout parameters associated with this textView.
67 | int validWidth = Utils.isLayoutParamsSpecialValue(widthExcludePadding) ? widthExcludePadding :
68 | widthExcludePadding + view.getPaddingLeft() + view.getPaddingRight();
69 | int validHeight = Utils.isLayoutParamsSpecialValue(heightExcludePadding) ? heightExcludePadding :
70 | heightExcludePadding + view.getPaddingTop() + view.getPaddingBottom();
71 | ViewGroup.LayoutParams params = view.getLayoutParams();
72 | if (null == params) {
73 | params = new ViewGroup.LayoutParams(validWidth, validHeight);
74 | } else {
75 | params.width = validWidth;
76 | params.height = validHeight;
77 | }
78 | view.setLayoutParams(params);
79 | // Set OnClickListener
80 | if (null != listener) {
81 | view.setOnClickListener(listener);
82 | }
83 | }
84 |
85 | /**
86 | * Copy values from other instance.
87 | */
88 | private void copyFrom(ViewOptions other) {
89 | this.visibility = other.visibility;
90 | this.paddingLeft = other.paddingLeft;
91 | this.paddingTop = other.paddingTop;
92 | this.paddingRight = other.paddingRight;
93 | this.paddingBottom = other.paddingBottom;
94 | this.widthExcludePadding = other.widthExcludePadding;
95 | this.heightExcludePadding = other.heightExcludePadding;
96 | }
97 |
98 | /**
99 | * Builder TextOptions instance more easier.
100 | */
101 | public static class Builder {
102 |
103 | private ViewOptions op;
104 |
105 | public Builder() {
106 | op = new ViewOptions();
107 | }
108 |
109 | private Builder(@NonNull ViewOptions other) {
110 | this();
111 | op.copyFrom(other);
112 | }
113 |
114 | public Builder setVisibility(@Visibility int visibility) {
115 | op.visibility = visibility;
116 | return this;
117 | }
118 |
119 | public Builder setPaddingLeft(@Dimension(unit = PX) int paddingLeft) {
120 | op.paddingLeft = paddingLeft;
121 | return this;
122 | }
123 |
124 | public Builder setPaddingTop(@Dimension(unit = PX) int paddingTop) {
125 | op.paddingTop = paddingTop;
126 | return this;
127 | }
128 |
129 | public Builder setPaddingRight(@Dimension(unit = PX) int paddingRight) {
130 | op.paddingRight = paddingRight;
131 | return this;
132 | }
133 |
134 | public Builder setPaddingBottom(@Dimension(unit = PX) int paddingBottom) {
135 | op.paddingBottom = paddingBottom;
136 | return this;
137 | }
138 |
139 | public Builder setWidthExcludePadding(@Dimension(unit = PX) int widthExcludePadding) {
140 | op.widthExcludePadding = widthExcludePadding;
141 | return this;
142 | }
143 |
144 | public Builder setHeightExcludePadding(@Dimension(unit = PX) int widthExcludePadding) {
145 | op.heightExcludePadding = widthExcludePadding;
146 | return this;
147 | }
148 |
149 | public Builder setListener(View.OnClickListener listener) {
150 | op.listener = listener;
151 | return this;
152 | }
153 |
154 | public Options build() {
155 | return op;
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/drawable/libpicturepicker_common_arrow_right_white.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/drawable/libpicturepicker_picker_bottom_indicator.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/drawable/libpicturepicker_picker_camera.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/drawable/libpicturepicker_picker_fab.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/layout/libpicturepicker_activity_picture_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
11 |
12 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
39 |
40 |
45 |
46 |
53 |
54 |
66 |
67 |
79 |
80 |
81 |
82 |
87 |
88 |
89 |
90 |
91 |
101 |
102 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/layout/libpicturepicker_activity_picture_watcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
23 |
24 |
33 |
34 |
38 |
39 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/layout/libpicturepicker_recycle_item_activity_picture_picker_folder.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/layout/libpicturepicker_recycle_item_activity_picture_picker_picture.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
20 |
21 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/values-zh/libpicturepicker_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 确认
6 | 预览
7 | 所有图片
8 | SD 卡根目录
9 | 最多只可选择
10 | 张图片
11 | 获取相册数据失败
12 | 至少选择一张图片
13 | @string/libpicturepicker_picker_tips_ensure_failed
14 |
15 |
16 | @string/libpicturepicker_picker_tips_over_threshold_prefix
17 | @string/libpicturepicker_picker_tips_over_threshold_suffix
18 | @string/libpicturepicker_picker_ensure
19 | @string/libpicturepicker_picker_tips_ensure_failed
20 |
21 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/values/libpicturepicker_attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/values/libpicturepicker_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ensure
6 | Preview
7 | All Pictures
8 | Storage Card
9 | Pick a maximum of
10 | pictures.
11 | Fetch album data failed.
12 | Please pick at least one picture.
13 | @string/libpicturepicker_picker_tips_ensure_failed
14 | com.sharry.picturepicker.widget.PicturePickerFabBehavior
15 |
16 |
17 | @string/libpicturepicker_picker_tips_over_threshold_prefix
18 | @string/libpicturepicker_picker_tips_over_threshold_suffix
19 | @string/libpicturepicker_picker_ensure
20 | @string/libpicturepicker_picker_tips_ensure_failed
21 |
22 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/values/libpicturepicker_themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/main/res/values/libpricturepicker_colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #ff00b0ff
6 | #ff00b0ff
7 | #ff64b6f6
8 |
9 |
10 | #a9000000
11 | #ffffffff
12 | #ffffffff
13 | #ff333333
14 | #ffffffff
15 |
16 |
17 | #ff000000
18 | #a9000000
19 | #ffffffff
20 |
21 |
--------------------------------------------------------------------------------
/lib-picturepicker/src/test/java/com/sharry/picturepicker/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.sharry.picturepicker;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':lib-picturepicker'
2 |
--------------------------------------------------------------------------------