├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── mipmap-xhdpi
│ │ │ ├── add_icon.png
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── menu
│ │ │ └── menu_pop.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── layout
│ │ │ ├── activity_main.xml
│ │ │ └── item_img.xml
│ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ └── drawable
│ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ └── com
│ │ │ └── giftedcat
│ │ │ └── dynamic
│ │ │ ├── listener
│ │ │ └── OnAddPicturesListener.java
│ │ │ ├── activity
│ │ │ └── BaseActivity.java
│ │ │ ├── MainActivity.java
│ │ │ └── adapter
│ │ │ └── NineGridAdapter.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── pictureLibrary
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── public.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── styles.xml
│ │ │ ├── strings.xml
│ │ │ └── attrs.xml
│ │ ├── drawable-xxhdpi
│ │ │ ├── mis_asv.png
│ │ │ ├── mis_asy.png
│ │ │ ├── mis_btn_selected.png
│ │ │ ├── mis_default_check.png
│ │ │ ├── mis_default_error.png
│ │ │ ├── mis_btn_unselected.png
│ │ │ └── mis_text_indicator.png
│ │ ├── drawable
│ │ │ ├── ic_empty_photo.png
│ │ │ └── mis_action_btn.xml
│ │ ├── drawable-xhdpi
│ │ │ ├── mis_btn_back.png
│ │ │ ├── mis_ic_menu_back.png
│ │ │ ├── mis_default_check_s.png
│ │ │ └── mis_selector_indicator.png
│ │ ├── mipmap-xhdpi
│ │ │ └── banner_default.png
│ │ ├── color
│ │ │ ├── mis_default_text_color.xml
│ │ │ └── mis_folder_text_color.xml
│ │ └── layout
│ │ │ ├── mis_list_item_camera.xml
│ │ │ ├── mis_list_item_image.xml
│ │ │ ├── mis_activity_default.xml
│ │ │ ├── mis_cmp_customer_actionbar.xml
│ │ │ ├── mis_fragment_multi_image.xml
│ │ │ └── mis_list_item_folder.xml
│ │ ├── java
│ │ └── com
│ │ │ └── giftedcat
│ │ │ └── picture
│ │ │ └── lib
│ │ │ ├── selector
│ │ │ ├── Explain.java
│ │ │ ├── bean
│ │ │ │ ├── Folder.java
│ │ │ │ └── Image.java
│ │ │ ├── view
│ │ │ │ ├── SquareFrameLayout.java
│ │ │ │ └── SquaredImageView.java
│ │ │ ├── utils
│ │ │ │ ├── TimeUtils.java
│ │ │ │ ├── ScreenUtils.java
│ │ │ │ └── PermissionUtils.java
│ │ │ ├── MultiImageSelector.java
│ │ │ ├── adapter
│ │ │ │ ├── FolderAdapter.java
│ │ │ │ └── ImageGridAdapter.java
│ │ │ └── MultiImageSelectorActivity.java
│ │ │ ├── photoview
│ │ │ ├── Explain.java
│ │ │ ├── style
│ │ │ │ ├── IIndexIndicator.java
│ │ │ │ ├── IProgressIndicator.java
│ │ │ │ ├── index
│ │ │ │ │ ├── NumberIndexIndicator.java
│ │ │ │ │ └── CircleIndexIndicator.java
│ │ │ │ └── progress
│ │ │ │ │ ├── ProgressBarIndicator.java
│ │ │ │ │ └── ProgressPieIndicator.java
│ │ │ ├── view
│ │ │ │ ├── image
│ │ │ │ │ ├── PhotoInfo.java
│ │ │ │ │ └── RotateGestureDetector.java
│ │ │ │ └── indicator
│ │ │ │ │ ├── NumberIndicator.java
│ │ │ │ │ └── CircleIndicator.java
│ │ │ ├── loader
│ │ │ │ └── ImageLoader.java
│ │ │ ├── transfer
│ │ │ │ ├── TransferAdapter.java
│ │ │ │ ├── LocalThumbState.java
│ │ │ │ ├── RemoteThumbState.java
│ │ │ │ ├── EmptyThumbState.java
│ │ │ │ ├── TransferState.java
│ │ │ │ ├── DragCloseGesture.java
│ │ │ │ ├── Transferee.java
│ │ │ │ └── TransferLayout.java
│ │ │ └── GlideImageLoader.java
│ │ │ └── PictureUseHelpr.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── encodings.xml
├── vcs.xml
├── misc.xml
├── runConfigurations.xml
└── gradle.xml
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/pictureLibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':pictureLibrary'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DynamicPublishing
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/add_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-xhdpi/add_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/values/public.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable-xxhdpi/mis_asv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/drawable-xxhdpi/mis_asv.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable-xxhdpi/mis_asy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/drawable-xxhdpi/mis_asy.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable/ic_empty_photo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/drawable/ic_empty_photo.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable-xhdpi/mis_btn_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/drawable-xhdpi/mis_btn_back.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/mipmap-xhdpi/banner_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/mipmap-xhdpi/banner_default.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #21282C
5 |
6 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable-xhdpi/mis_ic_menu_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/drawable-xhdpi/mis_ic_menu_back.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable-xxhdpi/mis_btn_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/drawable-xxhdpi/mis_btn_selected.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable-xxhdpi/mis_default_check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/drawable-xxhdpi/mis_default_check.png
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable-xxhdpi/mis_default_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Giftedcat/DynamicPublishing/HEAD/pictureLibrary/src/main/res/drawable-xxhdpi/mis_default_error.png
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_pop.xml:
--------------------------------------------------------------------------------
1 |
2 |
images;
16 |
17 | @Override
18 | public boolean equals(Object o) {
19 | try {
20 | Folder other = (Folder) o;
21 | return TextUtils.equals(other.path, path);
22 | }catch (ClassCastException e){
23 | e.printStackTrace();
24 | }
25 | return super.equals(o);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/layout/mis_list_item_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
16 |
17 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/view/SquareFrameLayout.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector.view;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.FrameLayout;
6 |
7 | /**
8 | * Created by nereo on 15/11/10.
9 | */
10 | public class SquareFrameLayout extends FrameLayout{
11 | public SquareFrameLayout(Context context) {
12 | super(context);
13 | }
14 |
15 | public SquareFrameLayout(Context context, AttributeSet attrs) {
16 | super(context, attrs);
17 | }
18 |
19 | @Override
20 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
21 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
22 | setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/bean/Image.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector.bean;
2 |
3 | import android.text.TextUtils;
4 |
5 | /**
6 | * 图片实体
7 | * Created by Nereo on 2015/4/7.
8 | */
9 | public class Image {
10 | public String path;
11 | public String name;
12 | public long time;
13 |
14 | public Image(String path, String name, long time){
15 | this.path = path;
16 | this.name = name;
17 | this.time = time;
18 | }
19 |
20 | @Override
21 | public boolean equals(Object o) {
22 | try {
23 | Image other = (Image) o;
24 | return TextUtils.equals(this.path, other.path);
25 | }catch (ClassCastException e){
26 | e.printStackTrace();
27 | }
28 | return super.equals(o);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/view/SquaredImageView.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector.view;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 |
6 | import androidx.appcompat.widget.AppCompatImageView;
7 |
8 | /** An image view which always remains square with respect to its width. */
9 | class SquaredImageView extends AppCompatImageView {
10 | public SquaredImageView(Context context) {
11 | super(context);
12 | }
13 |
14 | public SquaredImageView(Context context, AttributeSet attrs) {
15 | super(context, attrs);
16 | }
17 |
18 | @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
19 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
20 | setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pictureLibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/style/IIndexIndicator.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.style;
2 |
3 | import android.widget.FrameLayout;
4 |
5 | import androidx.viewpager.widget.ViewPager;
6 |
7 | /**
8 | * 图片索引指示器接口,实现 IIndexIndicator 可扩展自己的图片指示器组件
9 | *
10 | * email: 196425254@qq.com
11 | */
12 | public interface IIndexIndicator {
13 |
14 | /**
15 | * 在父容器上添加一个图片索引指示器 UI 组件
16 | *
17 | * @param parent TransferImage
18 | */
19 | void attach(FrameLayout parent);
20 |
21 | /**
22 | * 显示图片索引指示器 UI 组件
23 | *
24 | * @param viewPager TransferImage
25 | */
26 | void onShow(ViewPager viewPager);
27 |
28 | /**
29 | * 隐藏图片索引指示器 UI 组件
30 | */
31 | void onHide();
32 |
33 | /**
34 | * 移除图片索引指示器 UI 组件
35 | */
36 | void onRemove();
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_img.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 所有照片
3 | 预览
4 | 没有系统相机
5 | 已经达到最高选择数量
6 | 完成
7 | 张
8 | 拍摄照片
9 | 图片错误
10 | 无权限
11 | 权限拒绝
12 | 好
13 | 拒绝
14 | 浏览图片需要您提供浏览存储的权限
15 | 保存拍照图片需要您提供写存储权限
16 |
17 | %1$s(%2$d/%3$d)
18 |
19 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/utils/TimeUtils.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector.utils;
2 |
3 | import java.io.File;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Date;
6 | import java.util.Locale;
7 |
8 | /**
9 | * 时间处理工具
10 | * Created by Nereo on 2015/4/8.
11 | */
12 | public class TimeUtils {
13 |
14 | public static String timeFormat(long timeMillis, String pattern){
15 | SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.CHINA);
16 | return format.format(new Date(timeMillis));
17 | }
18 |
19 | public static String formatPhotoDate(long time){
20 | return timeFormat(time, "yyyy-MM-dd");
21 | }
22 |
23 | public static String formatPhotoDate(String path){
24 | File file = new File(path);
25 | if(file.exists()){
26 | long time = file.lastModified();
27 | return formatPhotoDate(time);
28 | }
29 | return "1970-01-01";
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/utils/ScreenUtils.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector.utils;
2 |
3 | import android.content.Context;
4 | import android.graphics.Point;
5 | import android.os.Build;
6 | import android.view.Display;
7 | import android.view.WindowManager;
8 |
9 | /**
10 | * 屏幕工具
11 | * Created by nereo on 15/11/19.
12 | * Updated by nereo on 2016/1/19.
13 | */
14 | public class ScreenUtils {
15 |
16 | public static Point getScreenSize(Context context){
17 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
18 | Display display = wm.getDefaultDisplay();
19 | Point out = new Point();
20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
21 | display.getSize(out);
22 | }else{
23 | int width = display.getWidth();
24 | int height = display.getHeight();
25 | out.set(width, height);
26 | }
27 | return out;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/drawable/mis_action_btn.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 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/view/image/PhotoInfo.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.view.image;
2 |
3 | import android.graphics.PointF;
4 | import android.graphics.RectF;
5 | import android.widget.ImageView;
6 |
7 | public class PhotoInfo {
8 |
9 | // 内部图片在整个手机界面的位置
10 | RectF mRect = new RectF();
11 |
12 | // 控件在窗口的位置
13 | RectF mImgRect = new RectF();
14 |
15 | RectF mWidgetRect = new RectF();
16 |
17 | RectF mBaseRect = new RectF();
18 |
19 | PointF mScreenCenter = new PointF();
20 |
21 | float mScale;
22 |
23 | float mDegrees;
24 |
25 | ImageView.ScaleType mScaleType;
26 |
27 | public PhotoInfo(RectF rect, RectF img, RectF widget, RectF base, PointF screenCenter, float scale, float degrees, ImageView.ScaleType scaleType) {
28 | mRect.set(rect);
29 | mImgRect.set(img);
30 | mWidgetRect.set(widget);
31 | mScale = scale;
32 | mScaleType = scaleType;
33 | mDegrees = degrees;
34 | mBaseRect.set(base);
35 | mScreenCenter.set(screenCenter);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/style/IProgressIndicator.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.style;
2 |
3 | import android.widget.FrameLayout;
4 |
5 | /**
6 | * 图片加载进度组件接口,实现 IProgressIndicator 可扩展自己的图片加载进度组件
7 | *
8 | * email: 196425254@qq.com
9 | */
10 | public interface IProgressIndicator {
11 |
12 | /**
13 | * 在父容器上附加一个图片加载进度 UI 控件
14 | *
15 | * @param position 当前图片的索引
16 | * @param parent 父容器
17 | */
18 | void attach(int position, FrameLayout parent);
19 |
20 | /**
21 | * 图片加载进度 UI 控件初始化
22 | *
23 | * @param position 索引下标
24 | */
25 | void onStart(int position);
26 |
27 | /**
28 | * 图片加载进度 UI 控件显示对应的进度
29 | *
30 | * @param position 索引下标
31 | * @param progress 进度值(0 - 100)
32 | */
33 | void onProgress(int position, int progress);
34 |
35 | /**
36 | * 隐藏 position 索引位置的图片加载进度 UI 控件
37 | *
38 | * @param position 索引下标
39 | */
40 | void hideView(int position);
41 |
42 | /**
43 | * 图片加载完成, 移除图片加载进度 UI 控件
44 | *
45 | * @param position 索引下标
46 | */
47 | void onFinish(int position);
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/giftedcat/dynamic/activity/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.dynamic.activity;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.view.Window;
8 | import android.view.WindowManager;
9 |
10 | public class BaseActivity extends Activity {
11 |
12 | public Context context;
13 | public Activity instans;
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 |
19 | context = this;
20 | instans = this;
21 | setTranslucentStatus(true);
22 | }
23 |
24 | /**
25 | * 设置沉浸式状态栏
26 | * */
27 | public void setTranslucentStatus(boolean b) {
28 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
29 | //版本小于4.4
30 | return;
31 | }
32 | Window win = getWindow();
33 | WindowManager.LayoutParams winParams = win.getAttributes();
34 | final int bit = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
35 | if (b) {
36 | winParams.flags |= bit;
37 | } else {
38 | winParams.flags &= ~bit;
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/layout/mis_list_item_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
19 |
20 |
28 |
29 |
--------------------------------------------------------------------------------
/pictureLibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "29.0.0"
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 15
10 | targetSdkVersion 28
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 |
30 | implementation 'androidx.appcompat:appcompat:1.0.0'
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'androidx.test:runner:1.1.0'
33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
34 |
35 | implementation 'com.github.bumptech.glide:glide:4.9.0'
36 | annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
37 |
38 | implementation 'jp.wasabeef:glide-transformations:4.0.0'
39 | implementation 'jp.co.cyberagent.android:gpuimage:2.0.4'
40 |
41 | implementation 'com.squareup.picasso:picasso:2.4.0'
42 | api 'androidx.recyclerview:recyclerview:1.0.0'
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/utils/PermissionUtils.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector.utils;
2 |
3 | import android.app.Activity;
4 | import android.content.DialogInterface;
5 |
6 | import androidx.appcompat.app.AlertDialog;
7 | import androidx.core.app.ActivityCompat;
8 |
9 | import com.example.easylib.R;
10 |
11 | public class PermissionUtils {
12 |
13 | /**
14 | * 请求权限
15 | * */
16 | public static void requestPermission(final Activity context, final String permission, String rationale, final int requestCode) {
17 | if (ActivityCompat.shouldShowRequestPermissionRationale(context, permission)) {
18 | new AlertDialog.Builder(context)
19 | .setTitle(R.string.mis_permission_dialog_title)
20 | .setMessage(rationale)
21 | .setPositiveButton(R.string.mis_permission_dialog_ok, new DialogInterface.OnClickListener() {
22 | @Override
23 | public void onClick(DialogInterface dialog, int which) {
24 | ActivityCompat.requestPermissions(context, new String[]{permission}, requestCode);
25 | }
26 | })
27 | .setNegativeButton(R.string.mis_permission_dialog_cancel, null)
28 | .create().show();
29 | } else {
30 | ActivityCompat.requestPermissions(context, new String[]{permission}, requestCode);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.2"
6 | defaultConfig {
7 | applicationId "com.giftedcat.dynamic"
8 | minSdkVersion 19
9 | targetSdkVersion 29
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 |
21 | compileOptions {
22 | sourceCompatibility JavaVersion.VERSION_1_8
23 | targetCompatibility JavaVersion.VERSION_1_8
24 | }
25 |
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 | implementation 'androidx.appcompat:appcompat:1.1.0'
31 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
32 | testImplementation 'junit:junit:4.12'
33 | androidTestImplementation 'androidx.test:runner:1.2.0'
34 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
35 |
36 | implementation project(':pictureLibrary')
37 |
38 | implementation 'com.zhy:base-rvadapter:3.0.3'
39 |
40 | api 'com.jakewharton:butterknife:10.2.1'
41 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
42 |
43 | implementation 'com.github.bumptech.glide:glide:4.5.0'
44 | annotationProcessor 'com.github.bumptech.glide:compiler:4.5.0'
45 |
46 | //气泡弹窗
47 | implementation 'me.kareluo.ui:popmenu:1.1.0'
48 |
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/layout/mis_activity_default.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
32 |
33 |
34 |
35 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/layout/mis_cmp_customer_actionbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
25 |
26 |
43 |
44 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/layout/mis_fragment_multi_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
25 |
26 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/style/index/NumberIndexIndicator.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.style.index;
2 |
3 | import android.view.Gravity;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.FrameLayout;
7 |
8 | import androidx.viewpager.widget.ViewPager;
9 |
10 | import com.giftedcat.picture.lib.photoview.style.IIndexIndicator;
11 | import com.giftedcat.picture.lib.photoview.view.indicator.NumberIndicator;
12 |
13 | import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
14 |
15 | /**
16 | * 图片翻页时使用 {@link NumberIndicator} 去指示当前图片的位置
17 | *
18 | * Created by Hitomis on 2017/4/23 0023.
19 | *
20 | * email: 196425254@qq.com
21 | */
22 | public class NumberIndexIndicator implements IIndexIndicator {
23 |
24 | private NumberIndicator numberIndicator;
25 |
26 | @Override
27 | public void attach(FrameLayout parent) {
28 | FrameLayout.LayoutParams indexLp = new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
29 | indexLp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
30 | indexLp.topMargin = 30;
31 |
32 | numberIndicator = new NumberIndicator(parent.getContext());
33 | numberIndicator.setLayoutParams(indexLp);
34 |
35 | parent.addView(numberIndicator);
36 | }
37 |
38 | @Override
39 | public void onShow(ViewPager viewPager) {
40 | numberIndicator.setVisibility(View.VISIBLE);
41 | numberIndicator.setViewPager(viewPager);
42 | }
43 |
44 | @Override
45 | public void onHide() {
46 | if (numberIndicator == null) return;
47 | numberIndicator.setVisibility(View.GONE);
48 | }
49 |
50 | @Override
51 | public void onRemove() {
52 | if (numberIndicator == null) return;
53 | ViewGroup vg = (ViewGroup) numberIndicator.getParent();
54 | if (vg != null) {
55 | vg.removeView(numberIndicator);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/style/index/CircleIndexIndicator.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.style.index;
2 |
3 | import android.view.Gravity;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.FrameLayout;
7 |
8 | import androidx.viewpager.widget.ViewPager;
9 |
10 | import com.giftedcat.picture.lib.photoview.style.IIndexIndicator;
11 | import com.giftedcat.picture.lib.photoview.view.indicator.CircleIndicator;
12 |
13 | import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
14 |
15 | /**
16 | * 图片翻页时使用 {@link CircleIndicator} 去指示当前图片的位置
17 | *
18 | * Created by hitomi on 2017/2/4.
19 | *
20 | * email: 196425254@qq.com
21 | */
22 | public class CircleIndexIndicator implements IIndexIndicator {
23 |
24 | private CircleIndicator circleIndicator;
25 |
26 | @Override
27 | public void attach(FrameLayout parent) {
28 | FrameLayout.LayoutParams indexLp = new FrameLayout.LayoutParams(WRAP_CONTENT, 48);
29 | indexLp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
30 | indexLp.bottomMargin = 10;
31 |
32 | circleIndicator = new CircleIndicator(parent.getContext());
33 | circleIndicator.setGravity(Gravity.CENTER_VERTICAL);
34 | circleIndicator.setLayoutParams(indexLp);
35 |
36 | parent.addView(circleIndicator);
37 | }
38 |
39 | @Override
40 | public void onShow(ViewPager viewPager) {
41 | circleIndicator.setVisibility(View.VISIBLE);
42 | circleIndicator.setViewPager(viewPager);
43 | }
44 |
45 | @Override
46 | public void onHide() {
47 | if (circleIndicator == null) return;
48 | circleIndicator.setVisibility(View.GONE);
49 | }
50 |
51 | @Override
52 | public void onRemove() {
53 | if (circleIndicator == null) return;
54 | ViewGroup vg = (ViewGroup) circleIndicator.getParent();
55 | if (vg != null) {
56 | vg.removeView(circleIndicator);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/loader/ImageLoader.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.loader;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.drawable.Drawable;
5 | import android.widget.ImageView;
6 |
7 | import androidx.annotation.Nullable;
8 | import androidx.annotation.UiThread;
9 |
10 | import java.io.File;
11 |
12 | /**
13 | * 图片加载器接口,实现 ImageLoader 可扩展自己的图片加载器
14 | * Created by hitomi on 2017/1/20.
15 | *
16 | * email: 196425254@qq.com
17 | */
18 | public interface ImageLoader {
19 | /**
20 | * 状态码,取消加载原图
21 | */
22 | int STATUS_DISPLAY_CANCEL = -1;
23 | /**
24 | * 状态码,加载原图失败
25 | */
26 | int STATUS_DISPLAY_FAILED = 0;
27 | /**
28 | * 状态码,加载原图成功
29 | */
30 | int STATUS_DISPLAY_SUCCESS = 1;
31 |
32 | /**
33 | * 加载并显示原图
34 | *
35 | * @param imageUrl 图片地址
36 | * @param imageView 用于图片加载成功后显示的 ImageView
37 | * @param placeholder 加载完成之前显示的占位图
38 | * @param sourceCallback 图片加载过程的回调
39 | */
40 | void showImage(String imageUrl, ImageView imageView, Drawable placeholder, @Nullable final SourceCallback sourceCallback);
41 |
42 | /**
43 | * 异步加载图片
44 | *
45 | * @param imageUrl 图片地址
46 | * @param callback 片加载完成的回调
47 | */
48 | void loadImageAsync(String imageUrl, final ThumbnailCallback callback);
49 |
50 | /**
51 | * 从本地同步加载图片(imageUrl 对应的图片是已经加载过的),返回 Bitmap
52 | */
53 | Bitmap loadImageSync(String imageUrl);
54 |
55 | /**
56 | * 获取 url 关联的图片缓存
57 | */
58 | File getCache(String url);
59 |
60 | /**
61 | * 清除 ImageLoader 缓存
62 | */
63 | void clearCache();
64 |
65 | interface SourceCallback {
66 | @UiThread
67 | void onStart();
68 |
69 | @UiThread
70 | void onProgress(int progress);
71 |
72 | @UiThread
73 | void onDelivered(int status, @Nullable File source);
74 | }
75 |
76 | interface ThumbnailCallback {
77 | @UiThread
78 | void onFinish(Bitmap bitmap);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/view/image/RotateGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.view.image;
2 |
3 | import android.view.MotionEvent;
4 |
5 | public class RotateGestureDetector {
6 |
7 | private static final int MAX_DEGREES_STEP = 120;
8 |
9 | private OnRotateListener mListener;
10 |
11 | private float mPrevSlope;
12 | private float mCurrSlope;
13 |
14 | private float x1;
15 | private float y1;
16 | private float x2;
17 | private float y2;
18 |
19 | public RotateGestureDetector(OnRotateListener l) {
20 | mListener = l;
21 | }
22 |
23 | public void onTouchEvent(MotionEvent event) {
24 |
25 | final int Action = event.getActionMasked();
26 |
27 | switch (Action) {
28 | case MotionEvent.ACTION_POINTER_DOWN:
29 | case MotionEvent.ACTION_POINTER_UP:
30 | if (event.getPointerCount() == 2) mPrevSlope = calculateSlope(event);
31 | break;
32 | case MotionEvent.ACTION_MOVE:
33 | if (event.getPointerCount() > 1) {
34 | mCurrSlope = calculateSlope(event);
35 |
36 | double currDegrees = Math.toDegrees(Math.atan(mCurrSlope));
37 | double prevDegrees = Math.toDegrees(Math.atan(mPrevSlope));
38 |
39 | double deltaSlope = currDegrees - prevDegrees;
40 |
41 | if (Math.abs(deltaSlope) <= MAX_DEGREES_STEP) {
42 | mListener.onRotate((float) deltaSlope, (x2 + x1) / 2, (y2 + y1) / 2);
43 | }
44 | mPrevSlope = mCurrSlope;
45 | }
46 | break;
47 | default:
48 | break;
49 | }
50 | }
51 |
52 | private float calculateSlope(MotionEvent event) {
53 | x1 = event.getX(0);
54 | y1 = event.getY(0);
55 | x2 = event.getX(1);
56 | y2 = event.getY(1);
57 | return (y2 - y1) / (x2 - x1);
58 | }
59 | }
60 |
61 | interface OnRotateListener {
62 | void onRotate(float degrees, float focusX, float focusY);
63 | }
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/style/progress/ProgressBarIndicator.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.style.progress;
2 |
3 | import android.content.Context;
4 | import android.util.SparseArray;
5 | import android.view.Gravity;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.FrameLayout;
9 | import android.widget.ProgressBar;
10 |
11 | import com.giftedcat.picture.lib.photoview.style.IProgressIndicator;
12 |
13 | /**
14 | * 图片加载时使用 Android 默认的 ProgressBar
15 | *
16 | * email: 196425254@qq.com
17 | */
18 | public class ProgressBarIndicator implements IProgressIndicator {
19 |
20 | private SparseArray progressBarArray = new SparseArray<>();
21 |
22 | private int dip2Px(Context context, float dpValue) {
23 | final float scale = context.getResources().getDisplayMetrics().density;
24 | return (int) (dpValue * scale + 0.5f);
25 | }
26 |
27 | @Override
28 | public void attach(int position, FrameLayout parent) {
29 | Context context = parent.getContext();
30 |
31 | int progressSize = dip2Px(context, 50);
32 | FrameLayout.LayoutParams progressLp = new FrameLayout.LayoutParams(
33 | progressSize, progressSize);
34 | progressLp.gravity = Gravity.CENTER;
35 |
36 | ProgressBar progressBar = new ProgressBar(context);
37 | progressBar.setLayoutParams(progressLp);
38 |
39 | parent.addView(progressBar, parent.getChildCount());
40 | progressBarArray.put(position, progressBar);
41 | }
42 |
43 | @Override
44 | public void hideView(int position) {
45 | ProgressBar progressBar = progressBarArray.get(position);
46 | if (progressBar != null)
47 | progressBar.setVisibility(View.GONE);
48 | }
49 |
50 | @Override
51 | public void onStart(int position) {
52 | }
53 |
54 | @Override
55 | public void onProgress(int position, int progress) {
56 | }
57 |
58 | @Override
59 | public void onFinish(int position) {
60 | ProgressBar progressBar = progressBarArray.get(position);
61 | if (progressBar == null) return;
62 |
63 | ViewGroup vg = (ViewGroup) progressBar.getParent();
64 | ;
65 | if (vg != null) {
66 | vg.removeView(progressBar);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/view/indicator/NumberIndicator.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.view.indicator;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.util.AttributeSet;
6 |
7 | import androidx.appcompat.widget.AppCompatTextView;
8 | import androidx.viewpager.widget.ViewPager;
9 |
10 | import java.util.Locale;
11 |
12 | /**
13 | * 数字索引指示器
14 | *
15 | * Created by Hitomis on 2017/4/23 0023.
16 | *
17 | * email: 196425254@qq.com
18 | */
19 | public class NumberIndicator extends AppCompatTextView {
20 |
21 | private static final String STR_NUM_FORMAT = "%s/%s";
22 |
23 | private ViewPager mViewPager;
24 | private final ViewPager.OnPageChangeListener mInternalPageChangeListener = new ViewPager.OnPageChangeListener() {
25 |
26 | @Override
27 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
28 | }
29 |
30 | @Override
31 | public void onPageSelected(int position) {
32 |
33 | if (mViewPager.getAdapter() == null || mViewPager.getAdapter().getCount() <= 0)
34 | return;
35 |
36 | setText(String.format(Locale.getDefault(),
37 | STR_NUM_FORMAT,
38 | position + 1,
39 | mViewPager.getAdapter().getCount()));
40 | }
41 |
42 | @Override
43 | public void onPageScrollStateChanged(int state) {
44 | }
45 | };
46 |
47 | public NumberIndicator(Context context) {
48 | this(context, null);
49 | }
50 |
51 | public NumberIndicator(Context context, AttributeSet attrs) {
52 | this(context, attrs, 0);
53 | }
54 |
55 | public NumberIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
56 | super(context, attrs, defStyleAttr);
57 | initNumberIndicator();
58 | }
59 |
60 | private void initNumberIndicator() {
61 | setTextColor(Color.WHITE);
62 | setTextSize(18);
63 | }
64 |
65 | public void setViewPager(ViewPager viewPager) {
66 | if (viewPager != null && viewPager.getAdapter() != null) {
67 | mViewPager = viewPager;
68 | mViewPager.removeOnPageChangeListener(mInternalPageChangeListener);
69 | mViewPager.addOnPageChangeListener(mInternalPageChangeListener);
70 | mInternalPageChangeListener.onPageSelected(mViewPager.getCurrentItem());
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/PictureUseHelpr.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 |
6 | import androidx.recyclerview.widget.RecyclerView;
7 |
8 | import com.giftedcat.picture.lib.photoview.GlideImageLoader;
9 | import com.giftedcat.picture.lib.photoview.style.index.NumberIndexIndicator;
10 | import com.giftedcat.picture.lib.photoview.style.progress.ProgressBarIndicator;
11 | import com.giftedcat.picture.lib.photoview.transfer.TransferConfig;
12 | import com.giftedcat.picture.lib.photoview.transfer.Transferee;
13 | import com.giftedcat.picture.lib.selector.MultiImageSelector;
14 |
15 | import java.util.List;
16 |
17 | public class PictureUseHelpr {
18 |
19 | private Context context;
20 |
21 | private Activity instans;
22 |
23 | /** 选择图片的集合*/
24 | private List mSelect;
25 |
26 | /** 最大图片数量*/
27 | private int maxNum = 9;
28 |
29 | protected Transferee transferee;
30 | protected TransferConfig config;
31 |
32 | public PictureUseHelpr(Activity instans) {
33 | this.instans = instans;
34 | this.context = instans;
35 | }
36 |
37 | public static PictureUseHelpr init(Activity instans){
38 | return new PictureUseHelpr(instans);
39 | }
40 |
41 | public PictureUseHelpr setMaxNum(int maxNum){
42 | this.maxNum = maxNum;
43 | return this;
44 | }
45 |
46 | public PictureUseHelpr origin(List images){
47 | this.mSelect = images;
48 | return this;
49 | }
50 |
51 | public void show(int position){
52 | config.setNowThumbnailIndex(position);
53 | config.setSourceImageList(mSelect);
54 | transferee.apply(config).show();
55 | }
56 |
57 | /**
58 | * 初始化大图查看控件
59 | */
60 | public PictureUseHelpr bindRecyclerView(RecyclerView recyclerView, int imageId){
61 | transferee = Transferee.getDefault(context);
62 | config = TransferConfig.build()
63 | .setSourceImageList(mSelect)
64 | .setProgressIndicator(new ProgressBarIndicator())
65 | .setIndexIndicator(new NumberIndexIndicator())
66 | .setImageLoader(GlideImageLoader.with(context.getApplicationContext()))
67 | .setJustLoadHitImage(true)
68 | .bindRecyclerView(recyclerView, imageId);
69 | return this;
70 | }
71 |
72 |
73 | /**
74 | * 选择添加图片
75 | */
76 | public void pickImage(int requestCode) {
77 | MultiImageSelector selector = MultiImageSelector.create(context);
78 | selector.showCamera(true);
79 | selector.count(maxNum);
80 | selector.multi();
81 | selector.origin(mSelect);
82 | selector.start(instans, requestCode);
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/com/giftedcat/dynamic/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.dynamic;
2 |
3 | import androidx.recyclerview.widget.GridLayoutManager;
4 | import androidx.recyclerview.widget.RecyclerView;
5 |
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 |
9 | import com.giftedcat.dynamic.activity.BaseActivity;
10 | import com.giftedcat.dynamic.adapter.NineGridAdapter;
11 | import com.giftedcat.dynamic.listener.OnAddPicturesListener;
12 | import com.giftedcat.picture.lib.selector.MultiImageSelector;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | import butterknife.BindView;
18 | import butterknife.ButterKnife;
19 | import butterknife.Unbinder;
20 |
21 | public class MainActivity extends BaseActivity {
22 |
23 | private static final int REQUEST_IMAGE = 2;
24 | private int maxNum = 9;
25 |
26 | Unbinder unbinder;
27 |
28 | @BindView(R.id.rv_images)
29 | RecyclerView rvImages;
30 |
31 | NineGridAdapter adapter;
32 |
33 | List mSelectList;
34 |
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_main);
40 | unbinder = ButterKnife.bind(this);
41 |
42 | mSelectList = new ArrayList<>();
43 | initView();
44 | }
45 |
46 | private void initView() {
47 | rvImages.setLayoutManager(new GridLayoutManager(this, 3));
48 | adapter = new NineGridAdapter(MainActivity.this, mSelectList, rvImages);
49 | adapter.setMaxSize(maxNum);
50 | rvImages.setAdapter(adapter);
51 | adapter.setOnAddPicturesListener(new OnAddPicturesListener() {
52 | @Override
53 | public void onAdd() {
54 | pickImage();
55 | }
56 | });
57 | }
58 |
59 | /**
60 | * 选择需添加的图片
61 | */
62 | private void pickImage() {
63 | MultiImageSelector selector = MultiImageSelector.create(context);
64 | selector.showCamera(true);
65 | selector.count(maxNum);
66 | selector.multi();
67 | selector.origin(mSelectList);
68 | selector.start(instans, REQUEST_IMAGE);
69 | }
70 |
71 | @Override
72 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
73 | super.onActivityResult(requestCode, resultCode, data);
74 | if (requestCode == REQUEST_IMAGE) {
75 | if (resultCode == RESULT_OK) {
76 | List select = data.getStringArrayListExtra(MultiImageSelector.EXTRA_RESULT);
77 | mSelectList.clear();
78 | mSelectList.addAll(select);
79 | adapter.notifyDataSetChanged();
80 | }
81 | }
82 | }
83 |
84 | @Override
85 | protected void onDestroy() {
86 | super.onDestroy();
87 |
88 | unbinder.unbind();
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/layout/mis_list_item_folder.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
22 |
23 |
32 |
33 |
40 |
41 |
50 |
51 |
61 |
62 |
63 |
64 |
74 |
75 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/style/progress/ProgressPieIndicator.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.style.progress;
2 |
3 | /**
4 | * 图片加载时使用饼状并带进度百分比显示的进度组件
5 | * ProgressPieView
6 | *
7 | * email: 196425254@qq.com
8 | */
9 | public class ProgressPieIndicator {
10 |
11 | // private SparseArray progressPieArray = new SparseArray<>();
12 | //
13 | // private int dip2Px(Context context, float dpValue) {
14 | // final float scale = context.getResources().getDisplayMetrics().density;
15 | // return (int) (dpValue * scale + 0.5f);
16 | // }
17 | //
18 | // @Override
19 | // public void attach(int position, FrameLayout parent) {
20 | // Context context = parent.getContext();
21 | //
22 | // int progressSize = dip2Px(context, 50);
23 | // FrameLayout.LayoutParams progressLp = new FrameLayout.LayoutParams(
24 | // progressSize, progressSize);
25 | // progressLp.gravity = Gravity.CENTER;
26 | //
27 | // ProgressPieView progressPieView = new ProgressPieView(context);
28 | // progressPieView.setTextSize(13);
29 | // progressPieView.setStrokeWidth(1);
30 | // progressPieView.setTextColor(Color.WHITE);
31 | // progressPieView.setProgressFillType(ProgressPieView.FILL_TYPE_RADIAL);
32 | // progressPieView.setBackgroundColor(Color.TRANSPARENT);
33 | // progressPieView.setProgressColor(Color.parseColor("#BBFFFFFF"));
34 | // progressPieView.setStrokeColor(Color.WHITE);
35 | // progressPieView.setLayoutParams(progressLp);
36 | //
37 | // parent.addView(progressPieView, parent.getChildCount());
38 | // progressPieArray.put(position, progressPieView);
39 | // }
40 | //
41 | // @Override
42 | // public void hideView(int position) {
43 | // ProgressPieView progressPieView = progressPieArray.get(position);
44 | // if (progressPieView != null)
45 | // progressPieView.setVisibility(View.GONE);
46 | // }
47 | //
48 | // @Override
49 | // public void onStart(int position) {
50 | // ProgressPieView progressPieView = progressPieArray.get(position);
51 | // progressPieView.setProgress(0);
52 | // progressPieView.setText(String.format(Locale.getDefault(), "%d%%", 0));
53 | // }
54 | //
55 | // @Override
56 | // public void onProgress(int position, int progress) {
57 | // if (progress < 0 || progress > 100) return;
58 | //
59 | // ProgressPieView progressPieView = progressPieArray.get(position);
60 | // progressPieView.setProgress(progress);
61 | // progressPieView.setText(String.format(Locale.getDefault(), "%d%%", progress));
62 | // }
63 | //
64 | // @Override
65 | // public void onFinish(int position) {
66 | // ProgressPieView progressPieView = progressPieArray.get(position);
67 | // if (progressPieView == null) return;
68 | //
69 | // ViewGroup vg = (ViewGroup) progressPieView.getParent();
70 | // ;
71 | // if (vg != null) {
72 | // vg.removeView(progressPieView);
73 | // }
74 | // }
75 | }
76 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/res/values/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 |
35 |
36 |
40 |
41 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/MultiImageSelector.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.pm.PackageManager;
8 | import android.os.Build;
9 | import android.widget.Toast;
10 |
11 | import androidx.core.content.ContextCompat;
12 | import androidx.fragment.app.Fragment;
13 |
14 | import com.example.easylib.R;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | /**
20 | * 图片选择器
21 | * Created by nereo on 16/3/17.
22 | */
23 | public class MultiImageSelector {
24 |
25 | public static final String EXTRA_RESULT = MultiImageSelectorActivity.EXTRA_RESULT;
26 |
27 | private boolean mShowCamera = true;
28 | private int mMaxCount = 9;
29 | private int mMode = MultiImageSelectorActivity.MODE_MULTI;
30 | private List mOriginData;
31 | private static MultiImageSelector sSelector;
32 |
33 | @Deprecated
34 | private MultiImageSelector(Context context){
35 |
36 | }
37 |
38 | private MultiImageSelector(){}
39 |
40 | @Deprecated
41 | public static MultiImageSelector create(Context context){
42 | if(sSelector == null){
43 | sSelector = new MultiImageSelector(context);
44 | }
45 | return sSelector;
46 | }
47 |
48 | public static MultiImageSelector create(){
49 | if(sSelector == null){
50 | sSelector = new MultiImageSelector();
51 | }
52 | return sSelector;
53 | }
54 |
55 | public MultiImageSelector showCamera(boolean show){
56 | mShowCamera = show;
57 | return sSelector;
58 | }
59 |
60 | public MultiImageSelector count(int count){
61 | mMaxCount = count;
62 | return sSelector;
63 | }
64 |
65 | public MultiImageSelector single(){
66 | mMode = MultiImageSelectorActivity.MODE_SINGLE;
67 | return sSelector;
68 | }
69 |
70 | public MultiImageSelector multi(){
71 | mMode = MultiImageSelectorActivity.MODE_MULTI;
72 | return sSelector;
73 | }
74 |
75 | public MultiImageSelector origin(List images){
76 | mOriginData = images;
77 | return sSelector;
78 | }
79 |
80 | public void start(Activity activity, int requestCode){
81 | final Context context = activity;
82 | activity.startActivityForResult(createIntent(context), requestCode);
83 | }
84 |
85 | public void start(Fragment fragment, int requestCode){
86 | final Context context = fragment.getContext();
87 | if(hasPermission(context)) {
88 | fragment.startActivityForResult(createIntent(context), requestCode);
89 | }else{
90 | Toast.makeText(context, R.string.mis_error_no_permission, Toast.LENGTH_SHORT).show();
91 | }
92 | }
93 |
94 | private boolean hasPermission(Context context){
95 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){
96 | // Permission was added in API Level 16
97 | return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
98 | == PackageManager.PERMISSION_GRANTED;
99 | }
100 | return true;
101 | }
102 |
103 | private Intent createIntent(Context context){
104 | Intent intent = new Intent(context, MultiImageSelectorActivity.class);
105 | intent.putExtra(MultiImageSelectorActivity.EXTRA_SHOW_CAMERA, mShowCamera);
106 | intent.putExtra(MultiImageSelectorActivity.EXTRA_SELECT_COUNT, mMaxCount);
107 | if(mOriginData != null){
108 | intent.putStringArrayListExtra(MultiImageSelectorActivity.EXTRA_DEFAULT_SELECTED_LIST, (ArrayList) mOriginData);
109 | }
110 | intent.putExtra(MultiImageSelectorActivity.EXTRA_SELECT_MODE, mMode);
111 | return intent;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/transfer/TransferAdapter.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.transfer;
2 |
3 | import android.content.Context;
4 | import android.util.SparseArray;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.FrameLayout;
8 | import android.widget.ImageView;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.viewpager.widget.PagerAdapter;
12 |
13 | import com.giftedcat.picture.lib.photoview.view.image.TransferImage;
14 |
15 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
16 |
17 | /**
18 | * 展示大图组件 ViewPager 的图片数据适配器
19 | *
20 | * Created by hitomi on 2017/1/23.
21 | *
22 | * email: 196425254@qq.com
23 | */
24 | class TransferAdapter extends PagerAdapter {
25 |
26 | private TransferLayout transfer;
27 | private int showIndex;
28 | private int imageSize;
29 |
30 | private OnInstantiateItemListener onInstantListener;
31 | private SparseArray containLayoutArray;
32 |
33 | TransferAdapter(TransferLayout transfer, int imageSize, int nowThumbnailIndex) {
34 | this.transfer = transfer;
35 | this.imageSize = imageSize;
36 | this.showIndex = nowThumbnailIndex + 1 == imageSize
37 | ? nowThumbnailIndex - 1 : nowThumbnailIndex + 1;
38 | this.showIndex = showIndex < 0 ? 0 : showIndex;
39 |
40 | containLayoutArray = new SparseArray<>();
41 | }
42 |
43 | @Override
44 | public int getCount() {
45 | return imageSize;
46 | }
47 |
48 | @Override
49 | public boolean isViewFromObject(View view, Object object) {
50 | return view == object;
51 | }
52 |
53 | @Override
54 | public void destroyItem(ViewGroup container, int position, Object object) {
55 | container.removeView((View) object);
56 | }
57 |
58 | /**
59 | * 获取指定索引页面中的 TransferImage
60 | *
61 | * @param position
62 | * @return
63 | */
64 | TransferImage getImageItem(int position) {
65 | TransferImage transImage = null;
66 |
67 | FrameLayout parentLayout = containLayoutArray.get(position);
68 | if (parentLayout != null) {
69 | int childCount = parentLayout.getChildCount();
70 | for (int i = 0; i < childCount; i++) {
71 | View view = parentLayout.getChildAt(i);
72 | if (view instanceof ImageView) {
73 | transImage = (TransferImage) view;
74 | break;
75 | }
76 | }
77 | }
78 |
79 | return transImage;
80 | }
81 |
82 | FrameLayout getParentItem(int position) {
83 | return containLayoutArray.get(position);
84 | }
85 |
86 | void setOnInstantListener(OnInstantiateItemListener listener) {
87 | this.onInstantListener = listener;
88 | }
89 |
90 | @Override
91 | public Object instantiateItem(ViewGroup container, int position) {
92 | // ViewPager instantiateItem 顺序:按 position 递减 OffscreenPageLimit,
93 | // 再从 positon 递增 OffscreenPageLimit 的次序创建页面
94 | FrameLayout parentLayout = containLayoutArray.get(position);
95 |
96 | if (parentLayout == null) {
97 | parentLayout = newParentLayout(container, position);
98 | containLayoutArray.put(position, parentLayout);
99 |
100 | if (position == showIndex && onInstantListener != null)
101 | onInstantListener.onComplete();
102 | }
103 |
104 | container.addView(parentLayout);
105 | return parentLayout;
106 | }
107 |
108 | @NonNull
109 | private FrameLayout newParentLayout(ViewGroup container, final int pos) {
110 | Context context = container.getContext();
111 | TransferConfig config = transfer.getTransConfig();
112 |
113 | // create inner ImageView
114 | TransferImage imageView = new TransferImage(context);
115 | imageView.setDuration(config.getDuration());
116 | imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
117 | imageView.setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
118 |
119 | // create outer ParentLayout
120 | FrameLayout parentLayout = new FrameLayout(context);
121 | parentLayout.setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
122 | parentLayout.addView(imageView);
123 |
124 | if (config.isJustLoadHitImage())
125 | transfer.getTransferState(pos).prepareTransfer(imageView, pos);
126 |
127 | return parentLayout;
128 | }
129 |
130 | interface OnInstantiateItemListener {
131 | void onComplete();
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/transfer/LocalThumbState.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.transfer;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.drawable.BitmapDrawable;
5 | import android.graphics.drawable.Drawable;
6 | import android.widget.ImageView;
7 |
8 | import com.giftedcat.picture.lib.photoview.loader.ImageLoader;
9 | import com.giftedcat.picture.lib.photoview.view.image.TransferImage;
10 |
11 | import java.io.File;
12 | import java.util.List;
13 |
14 | /**
15 | * 高清图图片已经加载过了,使用高清图作为缩略图。
16 | * 同时使用 {@link TransferImage#CATE_ANIMA_TOGETHER} 动画类型展示图片
17 | *
18 | * Created by hitomi on 2017/5/4.
19 | *
20 | * email: 196425254@qq.com
21 | */
22 | class LocalThumbState extends TransferState {
23 |
24 | LocalThumbState(TransferLayout transfer) {
25 | super(transfer);
26 | }
27 |
28 | @Override
29 | public void prepareTransfer(final TransferImage transImage, final int position) {
30 | final TransferConfig config = transfer.getTransConfig();
31 | ImageLoader imageLoader = config.getImageLoader();
32 | String imgUrl = config.getSourceImageList().get(position);
33 | imageLoader.showImage(imgUrl, transImage, config.getMissDrawable(transfer.getContext()), null);
34 | }
35 |
36 | @Override
37 | public TransferImage createTransferIn(final int position) {
38 | TransferConfig config = transfer.getTransConfig();
39 |
40 | TransferImage transImage = createTransferImage(
41 | config.getOriginImageList().get(position));
42 | transformThumbnail(config.getSourceImageList().get(position), transImage, true);
43 | transfer.addView(transImage, 1);
44 |
45 | return transImage;
46 | }
47 |
48 | @Override
49 | public void transferLoad(final int position) {
50 | final TransferConfig config = transfer.getTransConfig();
51 | final String imgUrl = config.getSourceImageList().get(position);
52 | final TransferImage targetImage = transfer.transAdapter.getImageItem(position);
53 |
54 | if (config.isJustLoadHitImage()) {
55 | // 如果用户设置了 JustLoadHitImage 属性,说明在 prepareTransfer 中已经
56 | // 对 TransferImage 裁剪且设置了占位图, 所以这里直接加载原图即可
57 | loadSourceImage(imgUrl, targetImage, targetImage.getDrawable(), position);
58 | } else {
59 | config.getImageLoader().loadImageAsync(imgUrl, new ImageLoader.ThumbnailCallback() {
60 | @Override
61 | public void onFinish(Bitmap bitmap) {
62 | Drawable placeholder = null;
63 | if (bitmap == null)
64 | placeholder = config.getMissDrawable(transfer.getContext());
65 | else
66 | placeholder = new BitmapDrawable(transfer.getContext().getResources(), bitmap);
67 |
68 | loadSourceImage(imgUrl, targetImage, placeholder, position);
69 | }
70 | });
71 | }
72 | }
73 |
74 | private void loadSourceImage(final String imgUrl, final TransferImage targetImage, Drawable drawable, final int position) {
75 | final TransferConfig config = transfer.getTransConfig();
76 |
77 | config.getImageLoader().showImage(imgUrl, targetImage, drawable, new ImageLoader.SourceCallback() {
78 |
79 | @Override
80 | public void onStart() {
81 | }
82 |
83 | @Override
84 | public void onProgress(int progress) {
85 | }
86 |
87 | @Override
88 | public void onDelivered(int status, File source) {
89 | switch (status) {
90 | case ImageLoader.STATUS_DISPLAY_SUCCESS:
91 | if (TransferImage.STATE_TRANS_CLIP == targetImage.getState())
92 | targetImage.transformIn(TransferImage.STAGE_SCALE);
93 | startPreview(targetImage, source, imgUrl, config, position);
94 | break;
95 | case ImageLoader.STATUS_DISPLAY_CANCEL:
96 | if (targetImage.getDrawable() != null) {
97 | startPreview(targetImage, source, imgUrl, config, position);
98 | }
99 | break;
100 | case ImageLoader.STATUS_DISPLAY_FAILED: // 加载失败,显示加载错误的占位图
101 | targetImage.setImageDrawable(config.getErrorDrawable(transfer.getContext()));
102 | break;
103 | }
104 | }
105 | });
106 | }
107 |
108 | @Override
109 | public TransferImage transferOut(final int position) {
110 | TransferImage transImage = null;
111 |
112 | TransferConfig config = transfer.getTransConfig();
113 | List originImageList = config.getOriginImageList();
114 |
115 | if (position <= originImageList.size() - 1 && originImageList.get(position) != null) {
116 | transImage = createTransferImage(originImageList.get(position));
117 | transformThumbnail(config.getSourceImageList().get(position), transImage, false);
118 |
119 | transfer.addView(transImage, 1);
120 | }
121 |
122 | return transImage;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/GlideImageLoader.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.drawable.Drawable;
7 | import android.widget.ImageView;
8 |
9 | import androidx.annotation.Nullable;
10 |
11 | import com.bumptech.glide.Glide;
12 | import com.bumptech.glide.load.DataSource;
13 | import com.bumptech.glide.load.engine.GlideException;
14 | import com.bumptech.glide.request.RequestListener;
15 | import com.bumptech.glide.request.target.Target;
16 | import com.giftedcat.picture.lib.photoview.loader.ImageLoader;
17 |
18 | import java.io.File;
19 | import java.util.HashMap;
20 | import java.util.Map;
21 |
22 | import com.giftedcat.picture.lib.selector.utils.FileUtils;
23 |
24 | public class GlideImageLoader implements ImageLoader {
25 |
26 |
27 | private Context context;
28 | private Map callbackMap;
29 |
30 | private static final String CACHE_DIR = "TransGlide";
31 |
32 | private GlideImageLoader(Context context) {
33 | this.context = context;
34 | this.callbackMap = new HashMap<>();
35 | }
36 |
37 | public static GlideImageLoader with(Context context) {
38 | return new GlideImageLoader(context);
39 | }
40 |
41 | @Override
42 | public void showImage(final String imageUrl, final ImageView imageView, final Drawable placeholder, final SourceCallback sourceCallback) {
43 | callbackMap.put(imageUrl, sourceCallback);
44 | if (sourceCallback != null) sourceCallback.onStart();
45 | Glide.with(imageView).download(imageUrl).listener(new RequestListener() {
46 | @Override
47 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
48 | SourceCallback callback = callbackMap.get(imageUrl);
49 | if (callback != null) callback.onDelivered(STATUS_DISPLAY_FAILED, null);
50 | return false;
51 | }
52 |
53 | @Override
54 | public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
55 | if (!imageUrl.endsWith(".gif")) // gif 图片需要 transferee 内部渲染,所以这里不作显示
56 | Glide.with(context).load(resource).into(imageView);
57 | checkSaveFile(resource, getFileName(imageUrl));
58 | SourceCallback callback = callbackMap.get(imageUrl);
59 | if (callback != null) {
60 | callback.onDelivered(STATUS_DISPLAY_SUCCESS, resource);
61 | callbackMap.remove(imageUrl);
62 | }
63 | return false;
64 | }
65 | }).preload();
66 | }
67 |
68 | @Override
69 | public void loadImageAsync(final String imageUrl, final ThumbnailCallback callback) {
70 | Glide.with(context).download(imageUrl).listener(new RequestListener() {
71 | @Override
72 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
73 | if (callback != null)
74 | callback.onFinish(null);
75 | return false;
76 | }
77 |
78 | @Override
79 | public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
80 | checkSaveFile(resource, getFileName(imageUrl));
81 | if (callback != null)
82 | callback.onFinish(BitmapFactory.decodeFile(resource.getAbsolutePath()));
83 | return false;
84 | }
85 | }).preload();
86 | }
87 |
88 | @Override
89 | public Bitmap loadImageSync(String imageUrl) {
90 | return BitmapFactory.decodeFile(getCache(imageUrl).getAbsolutePath());
91 | }
92 |
93 | @Override
94 | public File getCache(String url) {
95 | File cacheFile = new File(getCacheDir(), getFileName(url));
96 | return cacheFile.exists() ? cacheFile : null;
97 | }
98 |
99 | @Override
100 | public void clearCache() {
101 | Glide.get(context).clearMemory();
102 | new Thread(new Runnable() {
103 | @Override
104 | public void run() {
105 | Glide.get(context).clearDiskCache();
106 | FileUtils.delete(getCacheDir());
107 | }
108 | }).start();
109 | }
110 |
111 | private File getCacheDir() {
112 | File cacheDir = new File(context.getCacheDir(), CACHE_DIR);
113 | if (!cacheDir.exists()) cacheDir.mkdirs();
114 | return cacheDir;
115 | }
116 |
117 | private String getFileName(String imageUrl) {
118 | String[] nameArray = imageUrl.split("/");
119 | return nameArray[nameArray.length - 1];
120 | }
121 |
122 | private void checkSaveFile(final File file, final String fileName) {
123 | final File cacheDir = getCacheDir();
124 | boolean exists = FileUtils.isFileExists(new File(cacheDir, fileName));
125 | if (!exists) {
126 | new Thread(new Runnable() {
127 | @Override
128 | public void run() {
129 | File targetFile = new File(cacheDir, fileName);
130 | FileUtils.copy(file, targetFile);
131 | }
132 | }).start();
133 | }
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/app/src/main/java/com/giftedcat/dynamic/adapter/NineGridAdapter.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.dynamic.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.widget.ImageView;
6 |
7 | import androidx.appcompat.view.menu.MenuBuilder;
8 | import androidx.recyclerview.widget.RecyclerView;
9 |
10 | import com.bumptech.glide.Glide;
11 | import com.giftedcat.dynamic.R;
12 | import com.giftedcat.dynamic.listener.OnAddPicturesListener;
13 | import com.giftedcat.picture.lib.photoview.GlideImageLoader;
14 | import com.giftedcat.picture.lib.photoview.style.index.NumberIndexIndicator;
15 | import com.giftedcat.picture.lib.photoview.style.progress.ProgressBarIndicator;
16 | import com.giftedcat.picture.lib.photoview.transfer.TransferConfig;
17 | import com.giftedcat.picture.lib.photoview.transfer.Transferee;
18 | import com.zhy.adapter.recyclerview.CommonAdapter;
19 | import com.zhy.adapter.recyclerview.base.ViewHolder;
20 |
21 | import java.util.List;
22 |
23 | import me.kareluo.ui.OptionMenu;
24 | import me.kareluo.ui.OptionMenuView;
25 | import me.kareluo.ui.PopupMenuView;
26 | import me.kareluo.ui.PopupView;
27 |
28 | public class NineGridAdapter extends CommonAdapter {
29 |
30 | /**
31 | * 删除小弹窗
32 | */
33 | PopupMenuView menuView;
34 |
35 | private Context context;
36 |
37 | OnAddPicturesListener listener;
38 |
39 | private int deletePosition;
40 |
41 | protected Transferee transferee;
42 |
43 | protected TransferConfig config;
44 |
45 | public NineGridAdapter(Context context, List selectPath, RecyclerView rvImages) {
46 | super(context, R.layout.item_img, selectPath);
47 | this.context = context;
48 |
49 | selectPath.add("");
50 | initDeleteMenu();
51 | initTransfer(rvImages);
52 | }
53 |
54 | /**
55 | * 设置最大图片数量
56 | * */
57 | public void setMaxSize(int maxNum) {
58 | config.setMax(maxNum);
59 | }
60 |
61 | /**
62 | * 设置点击添加按钮的监听
63 | */
64 | public void setOnAddPicturesListener(OnAddPicturesListener listener) {
65 | this.listener = listener;
66 | }
67 |
68 |
69 | /**
70 | * 初始化大图查看控件
71 | */
72 | private void initTransfer(RecyclerView rvImages) {
73 | transferee = Transferee.getDefault(context);
74 | config = TransferConfig.build()
75 | .setSourceImageList(getDatas())
76 | .setProgressIndicator(new ProgressBarIndicator())
77 | .setIndexIndicator(new NumberIndexIndicator())
78 | .setImageLoader(GlideImageLoader.with(context.getApplicationContext()))
79 | .setJustLoadHitImage(true)
80 | .bindRecyclerView(rvImages, R.id.iv_thum);
81 | }
82 |
83 | /**
84 | * 初始化图片删除小弹窗
85 | */
86 | private void initDeleteMenu() {
87 | menuView = new PopupMenuView(context, R.menu.menu_pop, new MenuBuilder(context));
88 | menuView.setSites(PopupView.SITE_TOP);
89 | menuView.setOnMenuClickListener(new OptionMenuView.OnOptionMenuClickListener() {
90 | @Override
91 | public boolean onOptionMenuClick(int position, OptionMenu menu) {
92 | getDatas().remove(deletePosition);
93 | if (!getDatas().get(getDatas().size() - 1).equals("")) {
94 | //列表最后一张不是添加按钮时,加入添加按钮
95 | getDatas().add("");
96 | }
97 | notifyDataSetChanged();
98 | return true;
99 | }
100 | });
101 | }
102 |
103 | @Override
104 | protected void convert(ViewHolder viewHolder, String item, final int position) {
105 | ImageView ivThum = viewHolder.getView(R.id.iv_thum);
106 | ImageView ivAdd = viewHolder.getView(R.id.iv_add);
107 | if (item.equals("")) {
108 | //item为添加按钮
109 | ivThum.setVisibility(View.GONE);
110 | ivAdd.setVisibility(View.VISIBLE);
111 | } else {
112 | //item为普通图片
113 | ivThum.setVisibility(View.VISIBLE);
114 | ivAdd.setVisibility(View.GONE);
115 | }
116 | Glide.with(mContext).load(item).into(ivThum);
117 | ivThum.setOnClickListener(new PicturesClickListener(position));
118 | ivAdd.setOnClickListener(new PicturesClickListener(position));
119 |
120 | ivThum.setOnLongClickListener(new View.OnLongClickListener() {
121 | @Override
122 | public boolean onLongClick(View view) {
123 | deletePosition = position;
124 | //最上面的三个删除按钮是往下的 其他的都是往上的
125 | if (position < 3) {
126 | menuView.setSites(PopupView.SITE_BOTTOM);
127 | } else {
128 | menuView.setSites(PopupView.SITE_TOP);
129 | }
130 | menuView.show(view);
131 | return false;
132 | }
133 | });
134 | }
135 |
136 | /**
137 | * 图片点击事件
138 | */
139 | private class PicturesClickListener implements View.OnClickListener {
140 |
141 | int position;
142 |
143 | public PicturesClickListener(int position) {
144 | this.position = position;
145 | }
146 |
147 | @Override
148 | public void onClick(View view) {
149 | switch (view.getId()) {
150 | case R.id.iv_thum:
151 | //点击图片
152 | config.setNowThumbnailIndex(position);
153 | config.setSourceImageList(getDatas());
154 | transferee.apply(config).show();
155 | break;
156 | case R.id.iv_add:
157 | //点击添加按钮
158 | if (listener != null)
159 | listener.onAdd();
160 | break;
161 | }
162 | }
163 | }
164 |
165 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/adapter/FolderAdapter.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import com.example.easylib.R;
12 | import com.squareup.picasso.Picasso;
13 |
14 | import java.io.File;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | import com.giftedcat.picture.lib.selector.bean.Folder;
19 |
20 | /**
21 | * 文件夹Adapter
22 | * Created by Nereo on 2015/4/7.
23 | * Updated by nereo on 2016/1/19.
24 | */
25 | public class FolderAdapter extends BaseAdapter {
26 |
27 | private Context mContext;
28 | private LayoutInflater mInflater;
29 |
30 | private List mFolders = new ArrayList<>();
31 |
32 | int mImageSize;
33 |
34 | int lastSelected = 0;
35 |
36 | public FolderAdapter(Context context){
37 | mContext = context;
38 | mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
39 | mImageSize = mContext.getResources().getDimensionPixelOffset(R.dimen.mis_folder_cover_size);
40 | }
41 |
42 | /**
43 | * 设置数据集
44 | * @param folders
45 | */
46 | public void setData(List folders) {
47 | if(folders != null && folders.size()>0){
48 | mFolders = folders;
49 | }else{
50 | mFolders.clear();
51 | }
52 | notifyDataSetChanged();
53 | }
54 |
55 | @Override
56 | public int getCount() {
57 | return mFolders.size()+1;
58 | }
59 |
60 | @Override
61 | public Folder getItem(int i) {
62 | if(i == 0) return null;
63 | return mFolders.get(i-1);
64 | }
65 |
66 | @Override
67 | public long getItemId(int i) {
68 | return i;
69 | }
70 |
71 | @Override
72 | public View getView(int i, View view, ViewGroup viewGroup) {
73 | ViewHolder holder;
74 | if(view == null){
75 | view = mInflater.inflate(R.layout.mis_list_item_folder, viewGroup, false);
76 | holder = new ViewHolder(view);
77 | }else{
78 | holder = (ViewHolder) view.getTag();
79 | }
80 | if (holder != null) {
81 | if(i == 0){
82 | holder.name.setText(R.string.mis_folder_all);
83 | holder.path.setText("/sdcard");
84 | holder.size.setText(String.format("%d%s",
85 | getTotalImageSize(), mContext.getResources().getString(R.string.mis_photo_unit)));
86 | if(mFolders.size()>0){
87 | Folder f = mFolders.get(0);
88 | if (f != null) {
89 | Picasso.with(mContext)
90 | .load(new File(f.cover.path))
91 | .error(R.drawable.mis_default_error)
92 | .resizeDimen(R.dimen.mis_folder_cover_size, R.dimen.mis_folder_cover_size)
93 | .centerCrop()
94 | .into(holder.cover);
95 | }else{
96 | holder.cover.setImageResource(R.drawable.mis_default_error);
97 | }
98 | }
99 | }else {
100 | holder.bindData(getItem(i));
101 | }
102 | if(lastSelected == i){
103 | holder.indicator.setVisibility(View.VISIBLE);
104 | }else{
105 | holder.indicator.setVisibility(View.INVISIBLE);
106 | }
107 | }
108 | return view;
109 | }
110 |
111 | private int getTotalImageSize(){
112 | int result = 0;
113 | if(mFolders != null && mFolders.size()>0){
114 | for (Folder f: mFolders){
115 | result += f.images.size();
116 | }
117 | }
118 | return result;
119 | }
120 |
121 | public void setSelectIndex(int i) {
122 | if(lastSelected == i) return;
123 |
124 | lastSelected = i;
125 | notifyDataSetChanged();
126 | }
127 |
128 | public int getSelectIndex(){
129 | return lastSelected;
130 | }
131 |
132 | class ViewHolder{
133 | ImageView cover;
134 | TextView name;
135 | TextView path;
136 | TextView size;
137 | ImageView indicator;
138 | ViewHolder(View view){
139 | cover = (ImageView)view.findViewById(R.id.cover);
140 | name = (TextView) view.findViewById(R.id.name);
141 | path = (TextView) view.findViewById(R.id.path);
142 | size = (TextView) view.findViewById(R.id.size);
143 | indicator = (ImageView) view.findViewById(R.id.indicator);
144 | view.setTag(this);
145 | }
146 |
147 | void bindData(Folder data) {
148 | if(data == null){
149 | return;
150 | }
151 | name.setText(data.name);
152 | path.setText(data.path);
153 | if (data.images != null) {
154 | size.setText(String.format("%d%s", data.images.size(), mContext.getResources().getString(R.string.mis_photo_unit)));
155 | }else{
156 | size.setText("*"+mContext.getResources().getString(R.string.mis_photo_unit));
157 | }
158 | if (data.cover != null) {
159 | // 显示图片
160 | Picasso.with(mContext)
161 | .load(new File(data.cover.path))
162 | .placeholder(R.drawable.mis_default_error)
163 | .resizeDimen(R.dimen.mis_folder_cover_size, R.dimen.mis_folder_cover_size)
164 | .centerCrop()
165 | .into(cover);
166 | }else{
167 | cover.setImageResource(R.drawable.mis_default_error);
168 | }
169 | }
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/transfer/RemoteThumbState.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.transfer;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.drawable.BitmapDrawable;
5 | import android.graphics.drawable.Drawable;
6 | import android.widget.ImageView;
7 |
8 | import com.giftedcat.picture.lib.photoview.loader.ImageLoader;
9 | import com.giftedcat.picture.lib.photoview.style.IProgressIndicator;
10 | import com.giftedcat.picture.lib.photoview.view.image.TransferImage;
11 |
12 | import java.io.File;
13 | import java.util.List;
14 |
15 | /**
16 | * 用户指定了缩略图路径,使用该路径加载缩略图,
17 | * 并使用 {@link TransferImage#CATE_ANIMA_TOGETHER} 动画类型展示图片
18 | *
19 | * Created by hitomi on 2017/5/4.
20 | *
21 | * email: 196425254@qq.com
22 | */
23 | @Deprecated
24 | class RemoteThumbState extends TransferState {
25 |
26 | RemoteThumbState(TransferLayout transfer) {
27 | super(transfer);
28 | }
29 |
30 | @Override
31 | public void prepareTransfer(final TransferImage transImage, int position) {
32 | final TransferConfig config = transfer.getTransConfig();
33 |
34 | ImageLoader imageLoader = config.getImageLoader();
35 | String imgUrl = config.getThumbnailImageList().get(position);
36 |
37 | if (imageLoader.getCache(imgUrl) != null) {
38 | imageLoader.showImage(imgUrl, transImage,
39 | config.getMissDrawable(transfer.getContext()), null);
40 | } else {
41 | transImage.setImageDrawable(config.getMissDrawable(transfer.getContext()));
42 | }
43 | }
44 |
45 | @Override
46 | public TransferImage createTransferIn(final int position) {
47 | TransferConfig config = transfer.getTransConfig();
48 |
49 | TransferImage transImage = createTransferImage(
50 | config.getOriginImageList().get(position));
51 | transformThumbnail(config.getThumbnailImageList().get(position), transImage, true);
52 | transfer.addView(transImage, 1);
53 |
54 | return transImage;
55 | }
56 |
57 | @Override
58 | public void transferLoad(final int position) {
59 | final TransferConfig config = transfer.getTransConfig();
60 | final TransferImage targetImage = transfer.transAdapter.getImageItem(position);
61 | final ImageLoader imageLoader = config.getImageLoader();
62 |
63 | if (config.isJustLoadHitImage()) {
64 | // 如果用户设置了 JustLoadHitImage 属性,说明在 prepareTransfer 中已经
65 | // 对 TransferImage 裁剪且设置了占位图, 所以这里直接加载原图即可
66 | loadSourceImage(targetImage.getDrawable(), position, targetImage);
67 | } else {
68 | String thumbUrl = config.getThumbnailImageList().get(position);
69 |
70 | if (imageLoader.getCache(thumbUrl) != null) {
71 | imageLoader.loadImageAsync(thumbUrl, new ImageLoader.ThumbnailCallback() {
72 |
73 | @Override
74 | public void onFinish(Bitmap bitmap) {
75 | Drawable placeholder = null;
76 | if (bitmap == null)
77 | placeholder = config.getMissDrawable(transfer.getContext());
78 | else
79 | placeholder = new BitmapDrawable(transfer.getContext().getResources(), bitmap);
80 |
81 | loadSourceImage(placeholder, position, targetImage);
82 | }
83 | });
84 | } else {
85 | loadSourceImage(config.getMissDrawable(transfer.getContext()),
86 | position, targetImage);
87 | }
88 | }
89 | }
90 |
91 | private void loadSourceImage(Drawable drawable, final int position, final TransferImage targetImage) {
92 | final TransferConfig config = transfer.getTransConfig();
93 | final ImageLoader imageLoader = config.getImageLoader();
94 | final String sourceUrl = config.getSourceImageList().get(position);
95 | final IProgressIndicator progressIndicator = config.getProgressIndicator();
96 | progressIndicator.attach(position, transfer.transAdapter.getParentItem(position));
97 | imageLoader.showImage(sourceUrl, targetImage, drawable, new ImageLoader.SourceCallback() {
98 |
99 | @Override
100 | public void onStart() {
101 | progressIndicator.onStart(position);
102 | }
103 |
104 | @Override
105 | public void onProgress(int progress) {
106 | progressIndicator.onProgress(position, progress);
107 | }
108 |
109 | @Override
110 | public void onDelivered(int status, File source) {
111 | progressIndicator.onFinish(position); // onFinish 只是说明下载完毕,并没更新图像
112 | switch (status) {
113 | case ImageLoader.STATUS_DISPLAY_SUCCESS:
114 | // 启用 TransferImage 的手势缩放功能
115 | targetImage.enable();
116 | // 绑定点击关闭 Transferee
117 | transfer.bindOnOperationListener(targetImage, sourceUrl, position);
118 | break;
119 | case ImageLoader.STATUS_DISPLAY_FAILED: // 加载失败,显示加载错误的占位图
120 | targetImage.setImageDrawable(config.getErrorDrawable(transfer.getContext()));
121 | break;
122 | }
123 | }
124 | });
125 | }
126 |
127 | @Override
128 | public TransferImage transferOut(final int position) {
129 | TransferImage transImage = null;
130 |
131 | TransferConfig config = transfer.getTransConfig();
132 | List originImageList = config.getOriginImageList();
133 |
134 | if (position <= originImageList.size() - 1 && originImageList.get(position) != null) {
135 | transImage = createTransferImage(originImageList.get(position));
136 | transformThumbnail(config.getThumbnailImageList().get(position), transImage, false);
137 |
138 | transfer.addView(transImage, 1);
139 | }
140 |
141 | return transImage;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/transfer/EmptyThumbState.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.transfer;
2 |
3 | import android.graphics.drawable.Drawable;
4 | import android.util.DisplayMetrics;
5 | import android.widget.ImageView;
6 |
7 | import com.giftedcat.picture.lib.photoview.loader.ImageLoader;
8 | import com.giftedcat.picture.lib.photoview.style.IProgressIndicator;
9 | import com.giftedcat.picture.lib.photoview.view.image.TransferImage;
10 |
11 | import java.io.File;
12 | import java.util.List;
13 |
14 | /**
15 | * 高清图尚未加载,使用原 ImageView 中显示的图片作为缩略图。
16 | * 同时使用 {@link TransferImage#CATE_ANIMA_APART} 动画类型展示图片
17 | *
18 | * Created by hitomi on 2017/5/4.
19 | *
20 | * email: 196425254@qq.com
21 | */
22 | class EmptyThumbState extends TransferState {
23 |
24 | EmptyThumbState(TransferLayout transfer) {
25 | super(transfer);
26 | }
27 |
28 | @Override
29 | public void prepareTransfer(final TransferImage transImage, final int position) {
30 | transImage.setImageDrawable(clipAndGetPlachHolder(transImage, position));
31 | }
32 |
33 | @Override
34 | public TransferImage createTransferIn(final int position) {
35 | ImageView originImage = transfer.getTransConfig()
36 | .getOriginImageList().get(position);
37 |
38 | TransferImage transImage = createTransferImage(originImage);
39 | transImage.setImageDrawable(originImage.getDrawable());
40 | transImage.transformIn(TransferImage.STAGE_TRANSLATE);
41 | transfer.addView(transImage, 1);
42 |
43 | return transImage;
44 | }
45 |
46 | @Override
47 | public void transferLoad(final int position) {
48 | TransferAdapter adapter = transfer.transAdapter;
49 | final TransferConfig config = transfer.getTransConfig();
50 | final String imgUrl = config.getSourceImageList().get(position);
51 | final TransferImage targetImage = adapter.getImageItem(position);
52 |
53 | Drawable placeHolder;
54 | if (config.isJustLoadHitImage()) {
55 | // 如果用户设置了 JustLoadHitImage 属性,说明在 prepareTransfer 中已经
56 | // 对 TransferImage 裁剪过了, 所以只需要获取 Drawable 作为占位图即可
57 | placeHolder = getPlaceHolder(position);
58 | } else {
59 | placeHolder = clipAndGetPlachHolder(targetImage, position);
60 | }
61 |
62 | final IProgressIndicator progressIndicator = config.getProgressIndicator();
63 | progressIndicator.attach(position, adapter.getParentItem(position));
64 |
65 | config.getImageLoader().showImage(imgUrl, targetImage,
66 | placeHolder, new ImageLoader.SourceCallback() {
67 |
68 | @Override
69 | public void onStart() {
70 | progressIndicator.onStart(position);
71 | }
72 |
73 | @Override
74 | public void onProgress(int progress) {
75 | progressIndicator.onProgress(position, progress);
76 | }
77 |
78 | @Override
79 | public void onDelivered(int status, File source) {
80 | progressIndicator.onFinish(position); // onFinish 只是说明下载完毕,并没更新图像
81 | switch (status) {
82 | case ImageLoader.STATUS_DISPLAY_SUCCESS: // 加载成功
83 | targetImage.transformIn(TransferImage.STAGE_SCALE);
84 | startPreview(targetImage, source, imgUrl, config, position);
85 | break;
86 | case ImageLoader.STATUS_DISPLAY_CANCEL:
87 | if (targetImage.getDrawable() != null) {
88 | startPreview(targetImage, source, imgUrl, config, position);
89 | }
90 | break;
91 | case ImageLoader.STATUS_DISPLAY_FAILED: // 加载失败,显示加载错误的占位图
92 | targetImage.setImageDrawable(config.getErrorDrawable(transfer.getContext()));
93 | break;
94 | }
95 | }
96 | });
97 | }
98 |
99 | @Override
100 | public TransferImage transferOut(final int position) {
101 | TransferImage transImage = null;
102 |
103 | TransferConfig config = transfer.getTransConfig();
104 | List originImageList = config.getOriginImageList();
105 |
106 | if (position <= originImageList.size() - 1 && originImageList.get(position) != null) {
107 | transImage = createTransferImage(originImageList.get(position));
108 | Drawable thumbnailDrawable = transfer.transAdapter.getImageItem(
109 | config.getNowThumbnailIndex()).getDrawable();
110 | transImage.setImageDrawable(thumbnailDrawable);
111 | transImage.transformOut(TransferImage.STAGE_TRANSLATE);
112 |
113 | transfer.addView(transImage, 1);
114 | }
115 |
116 | return transImage;
117 | }
118 |
119 | /**
120 | * 获取 position 位置处的 占位图,如果 position 超出下标,获取 MissDrawable
121 | *
122 | * @param position 图片索引
123 | * @return 占位图
124 | */
125 | private Drawable getPlaceHolder(int position) {
126 | Drawable placeHolder;
127 |
128 | TransferConfig config = transfer.getTransConfig();
129 | ImageView originImage = config.getOriginImageList().get(position);
130 | if (originImage != null) {
131 | placeHolder = originImage.getDrawable();
132 | } else {
133 | placeHolder = config.getMissDrawable(transfer.getContext());
134 | }
135 |
136 | return placeHolder;
137 | }
138 |
139 | /**
140 | * 裁剪用于显示 PlachHolder 的 TransferImage
141 | *
142 | * @param targetImage 被裁剪的 TransferImage
143 | * @param position 图片索引
144 | * @return 被裁减的 TransferImage 中显示的 Drawable
145 | */
146 | private Drawable clipAndGetPlachHolder(TransferImage targetImage, int position) {
147 | TransferConfig config = transfer.getTransConfig();
148 |
149 | Drawable placeHolder = getPlaceHolder(position);
150 | int[] clipSize = new int[2];
151 | ImageView originImage = config.getOriginImageList().get(position);
152 | if (originImage != null) {
153 | clipSize[0] = originImage.getWidth();
154 | clipSize[1] = originImage.getHeight();
155 | }
156 |
157 | clipTargetImage(targetImage, placeHolder, clipSize);
158 | return placeHolder;
159 | }
160 |
161 | /**
162 | * 裁剪 ImageView 显示图片的区域
163 | *
164 | * @param targetImage 被裁减的 ImageView
165 | * @param originDrawable 缩略图 Drawable
166 | * @param clipSize 裁剪的尺寸数组
167 | */
168 | private void clipTargetImage(TransferImage targetImage, Drawable originDrawable, int[] clipSize) {
169 | DisplayMetrics displayMetrics = transfer.getContext().getResources().getDisplayMetrics();
170 | int width = displayMetrics.widthPixels;
171 | int height = getTransImageLocalY(displayMetrics.heightPixels);
172 |
173 | targetImage.setOriginalInfo(
174 | originDrawable,
175 | clipSize[0], clipSize[1],
176 | width, height);
177 |
178 | targetImage.transClip();
179 | }
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/adapter/ImageGridAdapter.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector.adapter;
2 |
3 | import android.content.Context;
4 | import android.graphics.Point;
5 | import android.os.Build;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.view.WindowManager;
10 | import android.widget.BaseAdapter;
11 | import android.widget.ImageView;
12 |
13 | import com.example.easylib.R;
14 | import com.squareup.picasso.Picasso;
15 |
16 | import java.io.File;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | import com.giftedcat.picture.lib.selector.MultiImageSelectorFragment;
21 | import com.giftedcat.picture.lib.selector.bean.Image;
22 |
23 | /**
24 | * 图片Adapter
25 | * Created by Nereo on 2015/4/7.
26 | * Updated by nereo on 2016/1/19.
27 | */
28 | public class ImageGridAdapter extends BaseAdapter {
29 |
30 | private static final int TYPE_CAMERA = 0;
31 | private static final int TYPE_NORMAL = 1;
32 |
33 | private Context mContext;
34 |
35 | private LayoutInflater mInflater;
36 | private boolean showCamera = true;
37 | private boolean showSelectIndicator = true;
38 |
39 | private List mImages = new ArrayList<>();
40 | private List mSelectedImages = new ArrayList<>();
41 |
42 | final int mGridWidth;
43 |
44 | public ImageGridAdapter(Context context, boolean showCamera, int column){
45 | mContext = context;
46 | mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
47 | this.showCamera = showCamera;
48 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
49 | int width = 0;
50 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
51 | Point size = new Point();
52 | wm.getDefaultDisplay().getSize(size);
53 | width = size.x;
54 | }else{
55 | width = wm.getDefaultDisplay().getWidth();
56 | }
57 | mGridWidth = width / column;
58 | }
59 | /**
60 | * 显示选择指示器
61 | * @param b
62 | */
63 | public void showSelectIndicator(boolean b) {
64 | showSelectIndicator = b;
65 | }
66 |
67 | public void setShowCamera(boolean b){
68 | if(showCamera == b) return;
69 |
70 | showCamera = b;
71 | notifyDataSetChanged();
72 | }
73 |
74 | public boolean isShowCamera(){
75 | return showCamera;
76 | }
77 |
78 | /**
79 | * 选择某个图片,改变选择状态
80 | * @param image
81 | */
82 | public void select(Image image) {
83 | if(mSelectedImages.contains(image)){
84 | mSelectedImages.remove(image);
85 | }else{
86 | mSelectedImages.add(image);
87 | }
88 | notifyDataSetChanged();
89 | }
90 |
91 | /**
92 | * 通过图片路径设置默认选择
93 | * @param resultList
94 | */
95 | public void setDefaultSelected(ArrayList resultList) {
96 | for(String path : resultList){
97 | Image image = getImageByPath(path);
98 | if(image != null){
99 | mSelectedImages.add(image);
100 | }
101 | }
102 | if(mSelectedImages.size() > 0){
103 | notifyDataSetChanged();
104 | }
105 | }
106 |
107 | private Image getImageByPath(String path){
108 | if(mImages != null && mImages.size()>0){
109 | for(Image image : mImages){
110 | if(image.path.equalsIgnoreCase(path)){
111 | return image;
112 | }
113 | }
114 | }
115 | return null;
116 | }
117 |
118 | /**
119 | * 设置数据集
120 | * @param images
121 | */
122 | public void setData(List images) {
123 | mSelectedImages.clear();
124 |
125 | if(images != null && images.size()>0){
126 | mImages = images;
127 | }else{
128 | mImages.clear();
129 | }
130 | notifyDataSetChanged();
131 | }
132 |
133 | @Override
134 | public int getViewTypeCount() {
135 | return 2;
136 | }
137 |
138 | @Override
139 | public int getItemViewType(int position) {
140 | if(showCamera){
141 | return position==0 ? TYPE_CAMERA : TYPE_NORMAL;
142 | }
143 | return TYPE_NORMAL;
144 | }
145 |
146 | @Override
147 | public int getCount() {
148 | return showCamera ? mImages.size()+1 : mImages.size();
149 | }
150 |
151 | @Override
152 | public Image getItem(int i) {
153 | if(showCamera){
154 | if(i == 0){
155 | return null;
156 | }
157 | return mImages.get(i-1);
158 | }else{
159 | return mImages.get(i);
160 | }
161 | }
162 |
163 | @Override
164 | public long getItemId(int i) {
165 | return i;
166 | }
167 |
168 | @Override
169 | public View getView(int i, View view, ViewGroup viewGroup) {
170 |
171 | if(isShowCamera()){
172 | if(i == 0){
173 | view = mInflater.inflate(R.layout.mis_list_item_camera, viewGroup, false);
174 | return view;
175 | }
176 | }
177 |
178 | ViewHolder holder;
179 | if(view == null){
180 | view = mInflater.inflate(R.layout.mis_list_item_image, viewGroup, false);
181 | holder = new ViewHolder(view);
182 | }else{
183 | holder = (ViewHolder) view.getTag();
184 | }
185 |
186 | if(holder != null) {
187 | holder.bindData(getItem(i));
188 | }
189 |
190 | return view;
191 | }
192 |
193 | class ViewHolder {
194 | ImageView image;
195 | ImageView indicator;
196 | View mask;
197 |
198 | ViewHolder(View view){
199 | image = (ImageView) view.findViewById(R.id.image);
200 | indicator = (ImageView) view.findViewById(R.id.checkmark);
201 | mask = view.findViewById(R.id.mask);
202 | view.setTag(this);
203 | }
204 |
205 | void bindData(final Image data){
206 | if(data == null) return;
207 | // 处理单选和多选状态
208 | if(showSelectIndicator){
209 | indicator.setVisibility(View.VISIBLE);
210 | if(mSelectedImages.contains(data)){
211 | // 设置选中状态
212 | indicator.setImageResource(R.drawable.mis_btn_selected);
213 | mask.setVisibility(View.VISIBLE);
214 | }else{
215 | // 未选择
216 | indicator.setImageResource(R.drawable.mis_btn_unselected);
217 | mask.setVisibility(View.GONE);
218 | }
219 | }else{
220 | indicator.setVisibility(View.GONE);
221 | }
222 | File imageFile = new File(data.path);
223 | if (imageFile.exists()) {
224 | // 显示图片
225 | Picasso.with(mContext)
226 | .load(imageFile)
227 | .placeholder(R.drawable.mis_default_error)
228 | .tag(MultiImageSelectorFragment.TAG)
229 | .resize(mGridWidth, mGridWidth)
230 | .centerCrop()
231 | .into(image);
232 | }else{
233 | image.setImageResource(R.drawable.mis_default_error);
234 | }
235 | }
236 | }
237 |
238 | }
239 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/transfer/TransferState.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.transfer;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.os.Build;
6 | import android.view.View;
7 | import android.widget.FrameLayout;
8 | import android.widget.ImageView;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | import com.giftedcat.picture.lib.photoview.loader.ImageLoader;
13 | import com.giftedcat.picture.lib.photoview.view.image.TransferImage;
14 |
15 | import java.io.File;
16 | import java.lang.reflect.Field;
17 |
18 |
19 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 | import static android.widget.ImageView.ScaleType.FIT_CENTER;
21 |
22 | /**
23 | * 由于用户配置的参数不同 (例如 使用不同的 ImageLoader / 是否指定了 thumbnailImageList 参数值)
24 | * 使得 Transferee 所表现的行为不同,所以采用一组策略算法来实现以下不同的功能:
25 | *
26 | * - 1. 图片进入 Transferee 的过渡动画
27 | * - 2. 图片加载时不同的表现形式
28 | * - 3. 图片从 Transferee 中出去的过渡动画
29 | *
30 | * Created by hitomi on 2017/5/4.
31 | *
32 | * email: 196425254@qq.com
33 | */
34 | abstract class TransferState {
35 |
36 | public Context context;
37 |
38 | protected TransferLayout transfer;
39 |
40 | TransferState(TransferLayout transfer) {
41 | this.transfer = transfer;
42 | this.context = transfer.getContext();
43 | }
44 |
45 | /**
46 | * 由于 4.4 以下版本状态栏不可修改,所以兼容 4.4 以下版本的全屏模式时,要去除状态栏的高度
47 | *
48 | * @param oldY
49 | * @return
50 | */
51 | int getTransImageLocalY(int oldY) {
52 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
53 | return oldY;
54 | }
55 | return oldY - getStatusBarHeight();
56 | }
57 |
58 | /**
59 | * 获取状态栏高度
60 | *
61 | * @return 状态栏高度
62 | */
63 | int getStatusBarHeight() {
64 | try {
65 | Class> c = Class.forName("com.android.internal.R$dimen");
66 | Object object = c.newInstance();
67 | Field field = c.getField("status_bar_height");
68 | int x = (Integer) field.get(object);
69 | return transfer.getContext().getResources().getDimensionPixelSize(x);
70 | } catch (Exception e) {
71 | return 0;
72 | }
73 | }
74 |
75 | /**
76 | * 获取 View 在屏幕坐标系中的坐标
77 | *
78 | * @param view 需要定位位置的 View
79 | * @return 坐标系数组
80 | */
81 | int[] getViewLocation(View view) {
82 | int[] location = new int[2];
83 | view.getLocationInWindow(location);
84 | return location;
85 | }
86 |
87 | /**
88 | * 依据 originImage 在屏幕中的坐标和宽高信息创建一个 TransferImage
89 | *
90 | * @param originImage 缩略图 ImageView
91 | * @return TransferImage
92 | */
93 | @NonNull
94 | TransferImage createTransferImage(ImageView originImage) {
95 | TransferConfig config = transfer.getTransConfig();
96 | int[] location = getViewLocation(originImage);
97 |
98 | TransferImage transImage = new TransferImage(transfer.getContext());
99 | transImage.setScaleType(FIT_CENTER);
100 | transImage.setOriginalInfo(location[0], getTransImageLocalY(location[1]),
101 | originImage.getWidth(), originImage.getHeight());
102 | transImage.setDuration(config.getDuration());
103 | transImage.setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
104 | transImage.setOnTransferListener(transfer.transListener);
105 |
106 | return transImage;
107 | }
108 |
109 | /**
110 | * 加载 imageUrl 所关联的图片到 TransferImage 并启动 TransferImage 中的过渡动画
111 | *
112 | * @param imageUrl 当前缩略图路径
113 | * @param transImage {@link #createTransferImage(ImageView)} 方法创建的 TransferImage
114 | * @param in true : 从缩略图到高清图动画, false : 从高清图到缩略图动画
115 | */
116 | void transformThumbnail(String imageUrl, final TransferImage transImage, final boolean in) {
117 | final TransferConfig config = transfer.getTransConfig();
118 |
119 | ImageLoader imageLoader = config.getImageLoader();
120 |
121 | if (this instanceof RemoteThumbState) { // RemoteThumbState
122 |
123 | if (imageLoader.getCache(imageUrl) != null) { // 缩略图已加载过
124 | loadThumbnail(imageUrl, transImage, in);
125 | } else { // 缩略图 未加载过,则使用用户配置的缺省占位图
126 | transImage.setImageDrawable(config.getMissDrawable(transfer.getContext()));
127 | if (in)
128 | transImage.transformIn();
129 | else
130 | transImage.transformOut();
131 | }
132 |
133 | } else { // LocalThumbState
134 | loadThumbnail(imageUrl, transImage, in);
135 | }
136 | }
137 |
138 | /**
139 | * 图片加载完毕,开启预览
140 | *
141 | * @param targetImage 预览图片
142 | * @param imgUrl 图片url
143 | * @param config 设置
144 | * @param position 索引
145 | */
146 | void startPreview(TransferImage targetImage, File source, String imgUrl, TransferConfig config, int position) {
147 | // 启用 TransferImage 的手势缩放功能
148 | targetImage.enable();
149 | if (imgUrl.endsWith("gif")) {
150 | File cache = source == null ? config.getImageLoader().getCache(imgUrl) : source;
151 | if (cache != null) {
152 | // try {
153 | // targetImage.setImageDrawable(new GifDrawable(cache.getPath()));
154 | // } catch (IOException ignored) {
155 | // }
156 | }
157 | }
158 | // 绑定点击关闭 Transferee
159 | transfer.bindOnOperationListener(targetImage, imgUrl, position);
160 | }
161 |
162 | /**
163 | * 加载 imageUrl 所关联的图片到 TransferImage 中
164 | *
165 | * @param imageUrl 图片路径
166 | * @param transImage
167 | * @param in true: 表示从缩略图到 Transferee, false: 从 Transferee 到缩略图
168 | */
169 | private void loadThumbnail(String imageUrl, final TransferImage transImage, final boolean in) {
170 | final TransferConfig config = transfer.getTransConfig();
171 | ImageLoader imageLoader = config.getImageLoader();
172 | Bitmap drawable = imageLoader.loadImageSync(imageUrl);
173 | if (drawable == null)
174 | transImage.setImageDrawable(config.getMissDrawable(transfer.getContext()));
175 | else
176 | transImage.setImageBitmap(drawable);
177 |
178 | if (in)
179 | transImage.transformIn();
180 | else
181 | transImage.transformOut();
182 | }
183 |
184 | /**
185 | * 当用户使用 justLoadHitImage 属
186 | * 性时,需要使用 prepareTransfer 方法提前让 ViewPager 对应
187 | * position 处的 TransferImage 剪裁并设置占位图
188 | *
189 | * @param transImage ViewPager 中 position 位置处的 TransferImage
190 | * @param position 当前点击的图片索引
191 | */
192 | public abstract void prepareTransfer(TransferImage transImage, final int position);
193 |
194 | /**
195 | * 创建一个 TransferImage 放置在 Transferee 中指定位置,并播放从缩略图到 Transferee 的过渡动画
196 | *
197 | * @param position 进入到 Transferee 之前,用户在图片列表中点击的图片的索引
198 | * @return 创建的 TransferImage
199 | */
200 | public abstract TransferImage createTransferIn(final int position);
201 |
202 | /**
203 | * 从网络或者从 {@link ImageLoader} 指定的缓存中加载 SourceImageList.get(position) 对应的图片
204 | *
205 | * @param position 原图片路径索引
206 | */
207 | public abstract void transferLoad(final int position);
208 |
209 | /**
210 | * 创建一个 TransferImage 放置在 Transferee 中指定位置,并播放从 Transferee 到 缩略图的过渡动画
211 | *
212 | * @param position 当前点击的图片索引
213 | * @return 创建的 TransferImage
214 | */
215 | public abstract TransferImage transferOut(final int position);
216 |
217 | }
218 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/transfer/DragCloseGesture.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.transfer;
2 |
3 | import android.animation.AnimatorSet;
4 | import android.animation.ObjectAnimator;
5 | import android.animation.ValueAnimator;
6 | import android.graphics.RectF;
7 | import android.os.Build;
8 | import android.view.MotionEvent;
9 | import android.view.VelocityTracker;
10 | import android.view.View;
11 | import android.view.ViewConfiguration;
12 | import android.widget.FrameLayout;
13 | import android.widget.ImageView;
14 |
15 | import androidx.viewpager.widget.ViewPager;
16 |
17 | import com.giftedcat.picture.lib.photoview.view.image.TransferImage;
18 |
19 | import java.lang.reflect.Field;
20 |
21 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
22 | import static android.widget.ImageView.ScaleType.FIT_CENTER;
23 |
24 | /**
25 | * Created by Vans Z on 2019-11-05.
26 | */
27 | class DragCloseGesture {
28 |
29 | private TransferLayout transferLayout;
30 | private VelocityTracker velocityTracker;
31 | private float preX;
32 | private float preY;
33 | private float scale; // 拖拽图片缩放值
34 | private int touchSlop;
35 |
36 |
37 | DragCloseGesture(TransferLayout transferLayout) {
38 | this.transferLayout = transferLayout;
39 | touchSlop = ViewConfiguration.get(transferLayout.getContext()).getScaledEdgeSlop();
40 | }
41 |
42 | boolean onInterceptTouchEvent(MotionEvent ev) {
43 | if (ev.getPointerCount() == 1) {
44 | switch (ev.getAction()) {
45 | case MotionEvent.ACTION_DOWN:
46 | preX = ev.getRawX();
47 | preY = ev.getRawY();
48 | if (null == velocityTracker) {
49 | velocityTracker = VelocityTracker.obtain();
50 | } else {
51 | velocityTracker.clear();
52 | }
53 | velocityTracker.addMovement(ev);
54 | break;
55 | case MotionEvent.ACTION_MOVE:
56 | float diffY = ev.getRawY() - preY;
57 | float diffX = Math.abs(ev.getRawX() - preX);
58 | TransferImage currentImage = transferLayout.getCurrentImage();
59 | if (diffX < touchSlop && diffY > touchSlop && currentImage.isScrollTop()) {
60 | return true;
61 | }
62 | break;
63 | case MotionEvent.ACTION_UP:
64 | preY = 0;
65 | break;
66 | }
67 | }
68 | return false;
69 | }
70 |
71 | void onTouchEvent(MotionEvent event) {
72 | switch (event.getAction()) {
73 | case MotionEvent.ACTION_DOWN:
74 | preX = event.getRawX();
75 | preY = event.getRawY();
76 | break;
77 | case MotionEvent.ACTION_MOVE:
78 | velocityTracker.addMovement(event);
79 | float diffX = event.getRawX() - preX;
80 | float diffY = event.getRawY() - preY;
81 | float absDiffY = Math.abs(diffY);
82 | scale = 1 - absDiffY / transferLayout.getHeight() * .75f;
83 |
84 | if (absDiffY < 350) {
85 | transferLayout.alpha = 255 - (absDiffY / 350) * 25;
86 | } else {
87 | transferLayout.alpha = 230 - (absDiffY - 350) * 1.35f / transferLayout.getHeight() * 255;
88 | }
89 |
90 | transferLayout.alpha = transferLayout.alpha < 0 ? 0 : transferLayout.alpha;
91 |
92 | ViewPager transViewPager = transferLayout.transViewPager;
93 | if (transViewPager.getTranslationY() >= 0) {
94 | transferLayout.setBackgroundColor(transferLayout.getBackgroundColorByAlpha(transferLayout.alpha));
95 | transViewPager.setTranslationX(diffX);
96 | transViewPager.setTranslationY(diffY);
97 | transViewPager.setScaleX(scale);
98 | transViewPager.setScaleY(scale);
99 | } else {
100 | transferLayout.setBackgroundColor(transferLayout.getTransConfig().getBackgroundColor());
101 | transViewPager.setTranslationX(diffX);
102 | transViewPager.setTranslationY(diffY);
103 | }
104 | break;
105 | case MotionEvent.ACTION_UP:
106 | velocityTracker.addMovement(event);
107 | velocityTracker.computeCurrentVelocity(1000);
108 | float velocityY = velocityTracker.getYVelocity();
109 | if (velocityY > 100) {
110 | int pos = transferLayout.getTransConfig().getNowThumbnailIndex();
111 | ImageView originImage = transferLayout.getTransConfig().getOriginImageList().get(pos);
112 | if (originImage == null) { // 走扩散消失动画
113 | transferLayout.diffusionTransfer(pos);
114 | } else { // 走过渡动画
115 | startTransformAnima(pos, originImage);
116 | }
117 | } else {
118 | startFlingAndRollbackAnimation();
119 | }
120 |
121 | preX = 0;
122 | preY = 0;
123 | break;
124 | case MotionEvent.ACTION_CANCEL:
125 | if (null != velocityTracker) {
126 | velocityTracker.recycle();
127 | velocityTracker = null;
128 | }
129 | break;
130 | }
131 | }
132 |
133 | private void startTransformAnima(int pos, ImageView originImage) {
134 | ViewPager transViewPagerUp = transferLayout.transViewPager;
135 | transViewPagerUp.setVisibility(View.INVISIBLE);
136 | int[] location = new int[2];
137 | originImage.getLocationInWindow(location);
138 |
139 |
140 | int x = location[0];
141 | int y = Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT ? location[1] : location[1] - getStatusBarHeight();
142 | int width = originImage.getWidth();
143 | int height = originImage.getHeight();
144 |
145 |
146 | TransferImage transImage = new TransferImage(transferLayout.getContext());
147 | transImage.setScaleType(FIT_CENTER);
148 | transImage.setOriginalInfo(x, y, width, height);
149 | transImage.setDuration(300);
150 | transImage.setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
151 | transImage.setOnTransferListener(transferLayout.transListener);
152 | transImage.setImageDrawable(transferLayout.transAdapter.getImageItem(pos).getDrawable());
153 |
154 |
155 | TransferImage currTransImage = transferLayout.getCurrentImage();
156 | float realWidth = currTransImage.getDeformedWidth() * scale;
157 | float realHeight = currTransImage.getDeformedHeight() * scale;
158 | float left = transViewPagerUp.getTranslationX() + (transferLayout.getWidth() - realWidth) * .5f;
159 | float top = transViewPagerUp.getTranslationY() + (transferLayout.getHeight() - realHeight) * .5f;
160 | RectF rectF = new RectF(left, top, realWidth, realHeight);
161 | transImage.transformSpecOut(rectF, scale);
162 | transferLayout.addView(transImage, 1);
163 | }
164 |
165 | private int getStatusBarHeight() {
166 | try {
167 | Class> c = Class.forName("com.android.internal.R$dimen");
168 | Object object = c.newInstance();
169 | Field field = c.getField("status_bar_height");
170 | int x = (Integer) field.get(object);
171 | return transferLayout.getContext().getResources().getDimensionPixelSize(x);
172 | } catch (Exception e) {
173 | return 0;
174 | }
175 | }
176 |
177 | private void startFlingAndRollbackAnimation() {
178 | ViewPager transViewPager = transferLayout.transViewPager;
179 | ValueAnimator bgColor = ObjectAnimator.ofFloat(null, "alpha", transferLayout.alpha, 255.f);
180 | ObjectAnimator scaleX = ObjectAnimator.ofFloat(transViewPager, "scaleX", transViewPager.getScaleX(), 1.0f);
181 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(transViewPager, "scaleY", transViewPager.getScaleX(), 1.0f);
182 | ObjectAnimator transX = ObjectAnimator.ofFloat(transViewPager, "translationX", transViewPager.getTranslationX(), 0);
183 | ObjectAnimator transY = ObjectAnimator.ofFloat(transViewPager, "translationY", transViewPager.getTranslationY(), 0);
184 |
185 | bgColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
186 | @Override
187 | public void onAnimationUpdate(ValueAnimator animation) {
188 | float value = Float.parseFloat(animation.getAnimatedValue().toString());
189 | transferLayout.setBackgroundColor(transferLayout.getBackgroundColorByAlpha(value));
190 | }
191 | });
192 |
193 | AnimatorSet animatorSet = new AnimatorSet();
194 | animatorSet.playTogether(bgColor, scaleX, scaleY, transX, transY);
195 | animatorSet.start();
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/selector/MultiImageSelectorActivity.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.selector;
2 |
3 | import android.Manifest;
4 | import android.content.Intent;
5 | import android.content.pm.PackageManager;
6 | import android.graphics.Color;
7 | import android.net.Uri;
8 | import android.os.Build;
9 | import android.os.Bundle;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 | import android.widget.Button;
13 |
14 | import androidx.annotation.NonNull;
15 | import androidx.appcompat.app.ActionBar;
16 | import androidx.appcompat.app.AppCompatActivity;
17 | import androidx.appcompat.widget.Toolbar;
18 | import androidx.core.app.ActivityCompat;
19 | import androidx.fragment.app.Fragment;
20 |
21 | import com.example.easylib.R;
22 |
23 | import java.io.File;
24 | import java.util.ArrayList;
25 |
26 | import com.giftedcat.picture.lib.selector.utils.PermissionUtils;
27 |
28 | /**
29 | * Multi image selector
30 | * Created by Nereo on 2015/4/7.
31 | * Updated by nereo on 2016/1/19.
32 | * Updated by nereo on 2016/5/18.
33 | */
34 | public class MultiImageSelectorActivity extends AppCompatActivity
35 | implements MultiImageSelectorFragment.Callback {
36 |
37 | /**
38 | * 权限申请返回
39 | */
40 | protected static final int REQUEST_STORAGE_READ_ACCESS_PERMISSION = 101;
41 |
42 | // Single choice
43 | public static final int MODE_SINGLE = 0;
44 | // Multi choice
45 | public static final int MODE_MULTI = 1;
46 |
47 | /**
48 | * Max image size,int,{@link #DEFAULT_IMAGE_SIZE} by default
49 | */
50 | public static final String EXTRA_SELECT_COUNT = "max_select_count";
51 | /**
52 | * Select mode,{@link #MODE_MULTI} by default
53 | */
54 | public static final String EXTRA_SELECT_MODE = "select_count_mode";
55 | /**
56 | * Whether show camera,true by default
57 | */
58 | public static final String EXTRA_SHOW_CAMERA = "show_camera";
59 | /**
60 | * Result data set,ArrayList<String>
61 | */
62 | public static final String EXTRA_RESULT = "select_result";
63 | /**
64 | * Original data set
65 | */
66 | public static final String EXTRA_DEFAULT_SELECTED_LIST = "default_list";
67 | // Default image size
68 | private static final int DEFAULT_IMAGE_SIZE = 9;
69 |
70 | private ArrayList resultList = new ArrayList<>();
71 | private Button mSubmitButton;
72 | private int mDefaultCount = DEFAULT_IMAGE_SIZE;
73 |
74 | Bundle savedInstanceState;
75 |
76 | @Override
77 | protected void onCreate(Bundle savedInstanceState) {
78 | super.onCreate(savedInstanceState);
79 | setTheme(R.style.MIS_NO_ACTIONBAR);
80 | setContentView(R.layout.mis_activity_default);
81 |
82 | this.savedInstanceState = savedInstanceState;
83 |
84 | checkPermission();
85 |
86 | }
87 |
88 | /**
89 | * 初始化
90 | */
91 | private void init() {
92 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
93 | getWindow().setStatusBarColor(Color.BLACK);
94 | }
95 |
96 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
97 | if (toolbar != null) {
98 | setSupportActionBar(toolbar);
99 | }
100 |
101 | final ActionBar actionBar = getSupportActionBar();
102 | if (actionBar != null) {
103 | actionBar.setDisplayHomeAsUpEnabled(true);
104 | }
105 |
106 | final Intent intent = getIntent();
107 | mDefaultCount = intent.getIntExtra(EXTRA_SELECT_COUNT, DEFAULT_IMAGE_SIZE);
108 | final int mode = intent.getIntExtra(EXTRA_SELECT_MODE, MODE_MULTI);
109 | final boolean isShow = intent.getBooleanExtra(EXTRA_SHOW_CAMERA, true);
110 | if (mode == MODE_MULTI && intent.hasExtra(EXTRA_DEFAULT_SELECTED_LIST)) {
111 | resultList = intent.getStringArrayListExtra(EXTRA_DEFAULT_SELECTED_LIST);
112 | if (resultList.size() != 0) {
113 | //选择图片的时候,从列表中移除添加按钮
114 | resultList.remove(resultList.size() - 1);
115 | }
116 | }
117 |
118 | mSubmitButton = (Button) findViewById(R.id.commit);
119 | if (mode == MODE_MULTI) {
120 | updateDoneText(resultList);
121 | mSubmitButton.setVisibility(View.VISIBLE);
122 | mSubmitButton.setOnClickListener(new View.OnClickListener() {
123 | @Override
124 | public void onClick(View view) {
125 | if (resultList != null && resultList.size() > 0) {
126 | // Notify success
127 | backWithData();
128 | } else {
129 | backWithData();
130 | }
131 | }
132 | });
133 | } else {
134 | mSubmitButton.setVisibility(View.GONE);
135 | }
136 |
137 | if (savedInstanceState == null) {
138 | Bundle bundle = new Bundle();
139 | bundle.putInt(MultiImageSelectorFragment.EXTRA_SELECT_COUNT, mDefaultCount);
140 | bundle.putInt(MultiImageSelectorFragment.EXTRA_SELECT_MODE, mode);
141 | bundle.putBoolean(MultiImageSelectorFragment.EXTRA_SHOW_CAMERA, isShow);
142 | bundle.putStringArrayList(MultiImageSelectorFragment.EXTRA_DEFAULT_SELECTED_LIST, resultList);
143 |
144 | getSupportFragmentManager().beginTransaction()
145 | .add(R.id.image_grid, Fragment.instantiate(this, MultiImageSelectorFragment.class.getName(), bundle))
146 | .commit();
147 | }
148 | }
149 |
150 | /**
151 | * 检查读写存储的权限
152 | */
153 | private void checkPermission() {
154 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
155 | && ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)//判断是否有读取本地存储的权限
156 | != PackageManager.PERMISSION_GRANTED) {
157 | //请求用户同意权限
158 | PermissionUtils.requestPermission(MultiImageSelectorActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE,
159 | getString(R.string.mis_permission_rationale),
160 | REQUEST_STORAGE_READ_ACCESS_PERMISSION);
161 | } else {
162 | init();
163 | }
164 | }
165 |
166 | /**
167 | * 携带数据返回
168 | */
169 | private void backWithData() {
170 | Intent data = new Intent();
171 | if (resultList.size() != mDefaultCount) {
172 | //如果选择的图片没有达到最大值,仍然显示添加按钮
173 | resultList.add("");
174 | }
175 | data.putStringArrayListExtra(EXTRA_RESULT, resultList);
176 | setResult(RESULT_OK, data);
177 | finish();
178 | }
179 |
180 | /**
181 | * 无数据返回
182 | */
183 | private void backWithoutData() {
184 | setResult(RESULT_CANCELED);
185 | finish();
186 | }
187 |
188 | @Override
189 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
190 | if (requestCode == REQUEST_STORAGE_READ_ACCESS_PERMISSION) {
191 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
192 | init();
193 | }
194 | } else {
195 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
196 | }
197 | }
198 |
199 | @Override
200 | public boolean onOptionsItemSelected(MenuItem item) {
201 | switch (item.getItemId()) {
202 | case android.R.id.home:
203 | backWithoutData();
204 | return true;
205 | }
206 | return super.onOptionsItemSelected(item);
207 | }
208 |
209 | /**
210 | * Update done button by select image data
211 | *
212 | * @param resultList selected image data
213 | */
214 | private void updateDoneText(ArrayList resultList) {
215 | int size = 0;
216 | if (resultList == null || resultList.size() <= 0) {
217 | mSubmitButton.setText(R.string.mis_action_done);
218 | mSubmitButton.setEnabled(false);
219 | } else {
220 | size = resultList.size();
221 | mSubmitButton.setEnabled(true);
222 | }
223 | mSubmitButton.setText(getString(R.string.mis_action_button_string,
224 | getString(R.string.mis_action_done), size, mDefaultCount));
225 | }
226 |
227 | @Override
228 | public void onSingleImageSelected(String path) {
229 | resultList.add(path);
230 | backWithData();
231 | }
232 |
233 | @Override
234 | public void onImageSelected(String path) {
235 | if (!resultList.contains(path)) {
236 | resultList.add(path);
237 | }
238 | updateDoneText(resultList);
239 | }
240 |
241 | @Override
242 | public void onImageUnselected(String path) {
243 | if (resultList.contains(path)) {
244 | resultList.remove(path);
245 | }
246 | updateDoneText(resultList);
247 | }
248 |
249 | @Override
250 | public void onCameraShot(File imageFile) {
251 | if (imageFile != null) {
252 | // notify system the image has change
253 | sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(imageFile)));
254 |
255 | Intent data = new Intent();
256 | resultList.add(imageFile.getAbsolutePath());
257 |
258 | backWithData();
259 | }
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android仿微信朋友圈发布动态功能
2 |
3 | # 导入
4 | ```
5 | implementation 'com.github.Giftedcat:DynamicPublishing:1.0.0'
6 | ```
7 | # 使用
8 | #### (一)初始化
9 | ```
10 | helpr = PictureUseHelpr.init(this).
11 | setMaxNum(9).
12 | origin(mSelect).
13 | bindRecyclerView(rvImages, R.id.iv_thum);
14 | ```
15 | #### (二)选择图片
16 | ```
17 | helpr.pickImage(REQUEST_IMAGE);
18 |
19 | @Override
20 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
21 | super.onActivityResult(requestCode, resultCode, data);
22 | if (requestCode == REQUEST_IMAGE) {
23 | if (resultCode == RESULT_OK) {
24 | List select = data.getStringArrayListExtra(MultiImageSelector.EXTRA_RESULT);
25 | mSelect.clear();
26 | mSelect.addAll(select);
27 | adapter.notifyDataSetChanged();
28 | }
29 | }
30 | }
31 | ```
32 |
33 | #### (三)显示大图
34 | ```
35 | helpr.show(position);
36 | ```
37 |
38 |
39 | # 一、前言
40 |
41 | 应工作上的要求,需要有一个类似于微信朋友圈发动态上传图片的功能,想起曾经已经做过了,但奈何不忍看自己以前写的代码的惨状,觉得重新封装一个使用方便,易于维护的类似功能的类,自己之后用起来也顺手,当然也方便一下大家,这样可以加快我们工作的效率,让我们有更多的时间学习(划水)。
42 |
43 | 
44 |
45 | 功能上的话,目前有添加图片、查看大图、删除图片
46 |
47 | # 二、效果图
48 | 先贴一下效果图吧
49 |
50 | 
51 |
52 | # 三、实现功能
53 | 库有用到butterknife和显示图片的glide
54 | 适配器用的rvadapter,加一个删除功能的气泡弹窗
55 | 引入一下相关依赖:
56 | ```
57 | api 'com.jakewharton:butterknife:10.2.1'
58 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
59 |
60 | implementation 'com.github.bumptech.glide:glide:4.5.0'
61 | annotationProcessor 'com.github.bumptech.glide:compiler:4.5.0'
62 |
63 | //气泡弹窗
64 | implementation 'me.kareluo.ui:popmenu:1.1.0'
65 | implementation 'com.zhy:base-rvadapter:3.0.3'
66 | ```
67 | ####(一)布局文件
68 | 主页面上的话,只有一个RecyclerView,他的LayoutManager设为GridLayoutManager,一行为3个
69 | ```
70 |
71 |
77 |
78 |
83 |
84 |
85 | ```
86 |
87 | 子布局的话,有两个ImageView,一个是普通的图片,另外一个固定为那个添加的按钮
88 | ```
89 |
90 |
97 |
98 |
104 |
105 |
112 |
113 |
114 |
115 | ```
116 |
117 | #### (二)Activity代码
118 |
119 | 先来看一下如何在我们的页面里使用我已经写好的这个adapter
120 |
121 | 首先初始化一下控件和adapter,给adapter设置一个点击添加图片的监听
122 | ```
123 | private void initView() {
124 | rvImages.setLayoutManager(new GridLayoutManager(this, 3));
125 | adapter = new NineGridAdapter(MainActivity.this, mSelectList, rvImages);
126 | adapter.setMaxSize(maxNum);
127 | rvImages.setAdapter(adapter);
128 | adapter.setOnAddPicturesListener(new OnAddPicturesListener() {
129 | @Override
130 | public void onAdd() {
131 | pickImage();
132 | }
133 | });
134 | }
135 | ```
136 |
137 | 选择图片页面的启动
138 | ```
139 | private void pickImage() {
140 | MultiImageSelector selector = MultiImageSelector.create(context);
141 | selector.showCamera(true);
142 | selector.count(maxNum);
143 | selector.multi();
144 | selector.origin(mSelectList);
145 | selector.start(instans, REQUEST_IMAGE);
146 | }
147 | ```
148 |
149 | 拿到选择图片页面的返回数据,添加至List中
150 | ```
151 | @Override
152 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
153 | super.onActivityResult(requestCode, resultCode, data);
154 | if (requestCode == REQUEST_IMAGE) {
155 | if (resultCode == RESULT_OK) {
156 | List select = data.getStringArrayListExtra(MultiImageSelector.EXTRA_RESULT);
157 | mSelectList.clear();
158 | mSelectList.addAll(select);
159 | adapter.notifyDataSetChanged();
160 | }
161 | }
162 | }
163 | ```
164 |
165 | #### (三)适配器代码
166 |
167 | 首先在构造函数中,往list里添加一个空串,为添加按钮占位。之后初始化一下删除气泡按钮跟大图显示的控件
168 | ```
169 | public NineGridAdapter(Context context, List selectPath, RecyclerView rvImages) {
170 | super(context, R.layout.item_img, selectPath);
171 | this.context = context;
172 |
173 | selectPath.add("");
174 | initDeleteMenu();
175 | initTransfer(rvImages);
176 | }
177 | ```
178 | 两个控件初始化的代码
179 | ```
180 | /**
181 | * 初始化大图查看控件
182 | */
183 | private void initTransfer(RecyclerView rvImages) {
184 | transferee = Transferee.getDefault(context);
185 | config = TransferConfig.build()
186 | .setSourceImageList(getDatas())
187 | .setProgressIndicator(new ProgressBarIndicator())
188 | .setIndexIndicator(new NumberIndexIndicator())
189 | .setImageLoader(GlideImageLoader.with(context.getApplicationContext()))
190 | .setJustLoadHitImage(true)
191 | .bindRecyclerView(rvImages, R.id.iv_thum);
192 | }
193 |
194 | /**
195 | * 初始化图片删除小弹窗
196 | */
197 | private void initDeleteMenu() {
198 | menuView = new PopupMenuView(context, R.menu.menu_pop, new MenuBuilder(context));
199 | menuView.setSites(PopupView.SITE_TOP);
200 | menuView.setOnMenuClickListener(new OptionMenuView.OnOptionMenuClickListener() {
201 | @Override
202 | public boolean onOptionMenuClick(int position, OptionMenu menu) {
203 | getDatas().remove(deletePosition);
204 | if (!getDatas().get(getDatas().size() - 1).equals("")) {
205 | //列表最后一张不是添加按钮时,加入添加按钮
206 | getDatas().add("");
207 | }
208 | notifyDataSetChanged();
209 | return true;
210 | }
211 | });
212 | }
213 | ```
214 | 在item填充的函数中完成图片的显示,点击和长按的监听
215 | ```
216 | @Override
217 | protected void convert(ViewHolder viewHolder, String item, final int position) {
218 | ImageView ivThum = viewHolder.getView(R.id.iv_thum);
219 | ImageView ivAdd = viewHolder.getView(R.id.iv_add);
220 | if (item.equals("")) {
221 | //item为添加按钮
222 | ivThum.setVisibility(View.GONE);
223 | ivAdd.setVisibility(View.VISIBLE);
224 | } else {
225 | //item为普通图片
226 | ivThum.setVisibility(View.VISIBLE);
227 | ivAdd.setVisibility(View.GONE);
228 | }
229 | Glide.with(mContext).load(item).into(ivThum);
230 | ivThum.setOnClickListener(new PicturesClickListener(position));
231 | ivAdd.setOnClickListener(new PicturesClickListener(position));
232 |
233 | ivThum.setOnLongClickListener(new View.OnLongClickListener() {
234 | @Override
235 | public boolean onLongClick(View view) {
236 | deletePosition = position;
237 | //最上面的三个删除按钮是往下的 其他的都是往上的
238 | if (position < 3) {
239 | menuView.setSites(PopupView.SITE_BOTTOM);
240 | } else {
241 | menuView.setSites(PopupView.SITE_TOP);
242 | }
243 | menuView.show(view);
244 | return false;
245 | }
246 | });
247 | }
248 | ```
249 | 点击事件代码
250 | ```
251 | private class PicturesClickListener implements View.OnClickListener {
252 |
253 | int position;
254 |
255 | public PicturesClickListener(int position) {
256 | this.position = position;
257 | }
258 |
259 | @Override
260 | public void onClick(View view) {
261 | switch (view.getId()) {
262 | case R.id.iv_thum:
263 | //点击图片
264 | config.setNowThumbnailIndex(position);
265 | config.setSourceImageList(getDatas());
266 | transferee.apply(config).show();
267 | break;
268 | case R.id.iv_add:
269 | //点击添加按钮
270 | if (listener != null)
271 | listener.onAdd();
272 | break;
273 | }
274 | }
275 | }
276 | ```
277 | # 四、关于老v7项目的问题
278 | 前段时间也是刚把自己的项目从v7手动改为androidx的,忙活了半天,后来发现android studio有一键将v7项目改为新的androidx的项目,算是给自己记个笔记
279 | 
280 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/view/indicator/CircleIndicator.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.view.indicator;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorSet;
5 | import android.animation.ObjectAnimator;
6 | import android.content.Context;
7 | import android.content.res.TypedArray;
8 | import android.database.DataSetObserver;
9 | import android.graphics.Color;
10 | import android.graphics.drawable.GradientDrawable;
11 | import android.util.AttributeSet;
12 | import android.view.Gravity;
13 | import android.view.View;
14 | import android.view.animation.Interpolator;
15 | import android.widget.LinearLayout;
16 |
17 | import androidx.viewpager.widget.ViewPager;
18 |
19 | import com.example.easylib.R;
20 |
21 | /**
22 | * 圆形小点索引指示器
23 | *
24 | * email: 196425254@qq.com
25 | */
26 | public class CircleIndicator extends LinearLayout {
27 |
28 | private final static int DEFAULT_INDICATOR_WIDTH = 5;
29 |
30 | private ViewPager mViewpager;
31 | private GradientDrawable mIndicatorBackground;
32 |
33 | private Animator mAnimatorOut;
34 | private Animator mAnimatorIn;
35 | private Animator mImmediateAnimatorOut;
36 | private Animator mImmediateAnimatorIn;
37 |
38 | private int mIndicatorMargin = -1;
39 | private int mIndicatorWidth = -1;
40 | private int mIndicatorHeight = -1;
41 |
42 | private int mLastPosition = -1;
43 | private final ViewPager.OnPageChangeListener mInternalPageChangeListener = new ViewPager.OnPageChangeListener() {
44 |
45 | @Override
46 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
47 | }
48 |
49 | @Override
50 | public void onPageSelected(int position) {
51 |
52 | if (mViewpager.getAdapter() == null || mViewpager.getAdapter().getCount() <= 0) {
53 | return;
54 | }
55 |
56 | if (mAnimatorIn.isRunning()) {
57 | mAnimatorIn.end();
58 | mAnimatorIn.cancel();
59 | }
60 |
61 | if (mAnimatorOut.isRunning()) {
62 | mAnimatorOut.end();
63 | mAnimatorOut.cancel();
64 | }
65 |
66 | View currentIndicator;
67 | if (mLastPosition >= 0 && (currentIndicator = getChildAt(mLastPosition)) != null) {
68 | currentIndicator.setBackgroundDrawable(mIndicatorBackground);
69 | mAnimatorIn.setTarget(currentIndicator);
70 | mAnimatorIn.start();
71 | }
72 |
73 | View selectedIndicator = getChildAt(position);
74 | if (selectedIndicator != null) {
75 | selectedIndicator.setBackgroundDrawable(mIndicatorBackground);
76 | mAnimatorOut.setTarget(selectedIndicator);
77 | mAnimatorOut.start();
78 | }
79 | mLastPosition = position;
80 | }
81 |
82 | @Override
83 | public void onPageScrollStateChanged(int state) {
84 | }
85 | };
86 |
87 | private DataSetObserver mInternalDataSetObserver = new DataSetObserver() {
88 | @Override
89 | public void onChanged() {
90 | super.onChanged();
91 | if (mViewpager == null) {
92 | return;
93 | }
94 |
95 | int newCount = mViewpager.getAdapter().getCount();
96 | int currentCount = getChildCount();
97 |
98 | if (newCount == currentCount) { // No change
99 | return;
100 | } else if (mLastPosition < newCount) {
101 | mLastPosition = mViewpager.getCurrentItem();
102 | } else {
103 | mLastPosition = -1;
104 | }
105 |
106 | createIndicators();
107 | }
108 | };
109 |
110 | public CircleIndicator(Context context) {
111 | this(context, null);
112 | }
113 |
114 | public CircleIndicator(Context context, AttributeSet attrs) {
115 | this(context, attrs, 0);
116 | }
117 |
118 | public CircleIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
119 | super(context, attrs, defStyleAttr);
120 | init(context, attrs);
121 | }
122 |
123 | private void init(Context context, AttributeSet attrs) {
124 | mIndicatorBackground = new GradientDrawable();
125 | mIndicatorBackground.setShape(GradientDrawable.OVAL);
126 | mIndicatorBackground.setColor(Color.WHITE);
127 |
128 | handleTypedArray(context, attrs);
129 | checkIndicatorConfig(context);
130 | }
131 |
132 | private void handleTypedArray(Context context, AttributeSet attrs) {
133 | if (attrs == null) {
134 | return;
135 | }
136 |
137 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicator);
138 | mIndicatorWidth =
139 | typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_width, -1);
140 | mIndicatorHeight =
141 | typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_height, -1);
142 | mIndicatorMargin =
143 | typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_margin, -1);
144 |
145 | int orientation = typedArray.getInt(R.styleable.CircleIndicator_ci_orientation, -1);
146 | setOrientation(orientation == VERTICAL ? VERTICAL : HORIZONTAL);
147 |
148 | int gravity = typedArray.getInt(R.styleable.CircleIndicator_ci_gravity, -1);
149 | setGravity(gravity >= 0 ? gravity : Gravity.CENTER);
150 |
151 | typedArray.recycle();
152 | }
153 |
154 | /**
155 | * Create and configure Indicator in Java code.
156 | */
157 | public void configureIndicator(int indicatorWidth, int indicatorHeight, int indicatorMargin) {
158 | mIndicatorWidth = indicatorWidth;
159 | mIndicatorHeight = indicatorHeight;
160 | mIndicatorMargin = indicatorMargin;
161 |
162 | checkIndicatorConfig(getContext());
163 | }
164 |
165 | private void checkIndicatorConfig(Context context) {
166 | mIndicatorWidth = (mIndicatorWidth < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorWidth;
167 | mIndicatorHeight =
168 | (mIndicatorHeight < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorHeight;
169 | mIndicatorMargin =
170 | (mIndicatorMargin < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorMargin;
171 |
172 | mAnimatorOut = createAnimatorOut();
173 | mImmediateAnimatorOut = createAnimatorOut();
174 | mImmediateAnimatorOut.setDuration(0);
175 |
176 | mAnimatorIn = createAnimatorIn(context);
177 | mImmediateAnimatorIn = createAnimatorIn(context);
178 | mImmediateAnimatorIn.setDuration(0);
179 | }
180 |
181 | private Animator createAnimatorOut() {
182 | ObjectAnimator alphaAnima = ObjectAnimator.ofFloat(null, "alpha", .5f, 1.f);
183 | ObjectAnimator scaleX = ObjectAnimator.ofFloat(null, "scaleX", 1.0f, 1.8f);
184 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(null, "scaleY", 1.0f, 1.8f);
185 |
186 | AnimatorSet animatorOut = new AnimatorSet();
187 | animatorOut.play(alphaAnima).with(scaleX).with(scaleY);
188 | return animatorOut;
189 | }
190 |
191 | private Animator createAnimatorIn(Context context) {
192 | Animator animatorIn = createAnimatorOut();
193 | animatorIn.setInterpolator(new ReverseInterpolator());
194 | return animatorIn;
195 | }
196 |
197 | public void setViewPager(ViewPager viewPager) {
198 | mViewpager = viewPager;
199 | if (mViewpager != null && mViewpager.getAdapter() != null) {
200 | mLastPosition = -1;
201 | createIndicators();
202 | mViewpager.removeOnPageChangeListener(mInternalPageChangeListener);
203 | mViewpager.addOnPageChangeListener(mInternalPageChangeListener);
204 | mInternalPageChangeListener.onPageSelected(mViewpager.getCurrentItem());
205 | }
206 | }
207 |
208 | public DataSetObserver getDataSetObserver() {
209 | return mInternalDataSetObserver;
210 | }
211 |
212 | /**
213 | * @deprecated User ViewPager addOnPageChangeListener
214 | */
215 | @Deprecated
216 | public void setOnPageChangeListener(ViewPager.OnPageChangeListener onPageChangeListener) {
217 | if (mViewpager == null) {
218 | throw new NullPointerException("can not find Viewpager , setViewPager first");
219 | }
220 | mViewpager.removeOnPageChangeListener(onPageChangeListener);
221 | mViewpager.addOnPageChangeListener(onPageChangeListener);
222 | }
223 |
224 | private void createIndicators() {
225 | removeAllViews();
226 | int count = mViewpager.getAdapter().getCount();
227 | if (count <= 0) {
228 | return;
229 | }
230 | int currentItem = mViewpager.getCurrentItem();
231 | int orientation = getOrientation();
232 |
233 | for (int i = 0; i < count; i++) {
234 | if (currentItem == i) {
235 | addIndicator(orientation, mImmediateAnimatorOut);
236 | } else {
237 | addIndicator(orientation, mImmediateAnimatorIn);
238 | }
239 | }
240 | }
241 |
242 | private void addIndicator(int orientation, Animator animator) {
243 | if (animator.isRunning()) {
244 | animator.end();
245 | animator.cancel();
246 | }
247 |
248 | View indicator = new View(getContext());
249 | indicator.setBackgroundDrawable(mIndicatorBackground);
250 | addView(indicator, mIndicatorWidth, mIndicatorHeight);
251 | LayoutParams lp = (LayoutParams) indicator.getLayoutParams();
252 |
253 | if (orientation == HORIZONTAL) {
254 | lp.leftMargin = mIndicatorMargin;
255 | lp.rightMargin = mIndicatorMargin;
256 | } else {
257 | lp.topMargin = mIndicatorMargin;
258 | lp.bottomMargin = mIndicatorMargin;
259 | }
260 |
261 | indicator.setLayoutParams(lp);
262 |
263 | animator.setTarget(indicator);
264 | animator.start();
265 | }
266 |
267 | public int dip2px(float dpValue) {
268 | final float scale = getResources().getDisplayMetrics().density;
269 | return (int) (dpValue * scale + 0.5f);
270 | }
271 |
272 | private class ReverseInterpolator implements Interpolator {
273 | @Override
274 | public float getInterpolation(float value) {
275 | return Math.abs(1.0f - value);
276 | }
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/transfer/Transferee.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.transfer;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.os.Build;
7 | import android.view.KeyEvent;
8 | import android.widget.AbsListView;
9 | import android.widget.ImageView;
10 |
11 | import androidx.appcompat.app.AlertDialog;
12 | import androidx.recyclerview.widget.GridLayoutManager;
13 | import androidx.recyclerview.widget.LinearLayoutManager;
14 | import androidx.recyclerview.widget.RecyclerView;
15 |
16 | import com.giftedcat.picture.lib.photoview.style.index.CircleIndexIndicator;
17 | import com.giftedcat.picture.lib.photoview.style.progress.ProgressBarIndicator;
18 |
19 | import java.io.File;
20 | import java.util.ArrayList;
21 | import java.util.List;
22 |
23 | /**
24 | * Main workflow:
25 | * 1、点击缩略图展示缩略图到 transferee 过渡动画
26 | * 2、显示下载高清图片进度
27 | * 3、加载完成显示高清图片
28 | * 4、高清图支持手势缩放
29 | * 5、关闭 transferee 展示 transferee 到原缩略图的过渡动画
30 | * Created by hitomi on 2017/1/19.
31 | *
32 | * email: 196425254@qq.com
33 | */
34 | public class Transferee implements DialogInterface.OnShowListener,
35 | DialogInterface.OnKeyListener,
36 | TransferLayout.OnLayoutResetListener {
37 |
38 | private Context context;
39 | private Dialog transDialog;
40 |
41 | private TransferLayout transLayout;
42 | private TransferConfig transConfig;
43 | private OnTransfereeStateChangeListener transListener;
44 |
45 | // 因为Dialog的关闭有动画延迟,固不能使用 dialog.isShowing, 去判断 transferee 的显示逻辑
46 | private boolean shown;
47 |
48 | /**
49 | * 构造方法私有化,通过{@link #getDefault(Context)} 创建 transferee
50 | *
51 | * @param context 上下文环境
52 | */
53 | private Transferee(Context context) {
54 | this.context = context;
55 | createLayout();
56 | createDialog();
57 | }
58 |
59 | /**
60 | * @param context
61 | * @return {@link Transferee}
62 | */
63 | public static Transferee getDefault(Context context) {
64 | return new Transferee(context);
65 | }
66 |
67 | private void createLayout() {
68 | transLayout = new TransferLayout(context);
69 | transLayout.setOnLayoutResetListener(this);
70 | }
71 |
72 | private void createDialog() {
73 | transDialog = new AlertDialog.Builder(context, getDialogStyle())
74 | .setView(transLayout)
75 | .create();
76 | transDialog.setOnShowListener(this);
77 | transDialog.setOnKeyListener(this);
78 | }
79 |
80 | /**
81 | * 兼容4.4以下的全屏 Dialog 样式
82 | *
83 | * @return The style of the dialog
84 | */
85 | private int getDialogStyle() {
86 | int dialogStyle;
87 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
88 | dialogStyle = android.R.style.Theme_Translucent_NoTitleBar_Fullscreen;
89 | } else {
90 | dialogStyle = android.R.style.Theme_Translucent_NoTitleBar;
91 | }
92 | return dialogStyle;
93 | }
94 |
95 | /**
96 | * 检查参数,如果必须参数缺少,就使用缺省参数或者抛出异常
97 | */
98 | private void checkConfig() {
99 | if (transConfig.isSourceEmpty())
100 | throw new IllegalArgumentException("The parameter sourceImageList can't be empty");
101 |
102 | if (transConfig.getImageLoader() == null)
103 | throw new IllegalArgumentException("Need to specify an ImageLoader");
104 |
105 | transConfig.setNowThumbnailIndex(transConfig.getNowThumbnailIndex() < 0
106 | ? 0 : transConfig.getNowThumbnailIndex());
107 |
108 | transConfig.setOffscreenPageLimit(transConfig.getOffscreenPageLimit() <= 0
109 | ? 1 : transConfig.getOffscreenPageLimit());
110 |
111 | transConfig.setDuration(transConfig.getDuration() <= 0
112 | ? 300 : transConfig.getDuration());
113 |
114 | transConfig.setProgressIndicator(transConfig.getProgressIndicator() == null
115 | ? new ProgressBarIndicator() : transConfig.getProgressIndicator());
116 |
117 | transConfig.setIndexIndicator(transConfig.getIndexIndicator() == null
118 | ? new CircleIndexIndicator() : transConfig.getIndexIndicator());
119 | }
120 |
121 | private void fillOriginImages() {
122 | List originImageList = new ArrayList<>();
123 | if (transConfig.getRecyclerView() != null) {
124 | fillByRecyclerView(originImageList);
125 | } else if (transConfig.getListView() != null) {
126 | fillByListView(originImageList);
127 | } else if (transConfig.getImageView() != null) {
128 | originImageList.add(transConfig.getImageView());
129 | }
130 | transConfig.setOriginImageList(originImageList);
131 | }
132 |
133 | private void fillByRecyclerView(final List originImageList) {
134 | RecyclerView recyclerView = transConfig.getRecyclerView();
135 | int childCount = recyclerView.getChildCount();
136 | for (int i = 0; i < childCount; i++) {
137 | ImageView originImage = recyclerView.getChildAt(i)
138 | .findViewById(transConfig.getImageId());
139 | if (originImage != null)
140 | originImageList.add(originImage);
141 | }
142 |
143 | RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
144 | int firstPos = 0, lastPos = 0;
145 | int totalCount = layoutManager.getItemCount();
146 | if (layoutManager instanceof GridLayoutManager) {
147 | GridLayoutManager gridLayMan = (GridLayoutManager) layoutManager;
148 | firstPos = gridLayMan.findFirstVisibleItemPosition();
149 | lastPos = gridLayMan.findLastVisibleItemPosition();
150 | } else if (layoutManager instanceof LinearLayoutManager) {
151 | LinearLayoutManager linLayMan = (LinearLayoutManager) layoutManager;
152 | firstPos = linLayMan.findFirstVisibleItemPosition();
153 | lastPos = linLayMan.findLastVisibleItemPosition();
154 | }
155 | fillPlaceHolder(originImageList, totalCount, firstPos, lastPos);
156 | }
157 |
158 | private void fillByListView(final List originImageList) {
159 | AbsListView absListView = transConfig.getListView();
160 | int childCount = absListView.getChildCount();
161 | for (int i = 0; i < childCount; i++) {
162 | ImageView originImage = ((ImageView) absListView.getChildAt(i)
163 | .findViewById(transConfig.getImageId()));
164 | originImageList.add(originImage);
165 | }
166 |
167 | int firstPos = absListView.getFirstVisiblePosition();
168 | int lastPos = absListView.getLastVisiblePosition();
169 | int totalCount = absListView.getCount();
170 | fillPlaceHolder(originImageList, totalCount, firstPos, lastPos);
171 | }
172 |
173 | private void fillPlaceHolder(List originImageList, int totalCount, int firstPos, int lastPos) {
174 | if (firstPos > 0) {
175 | for (int pos = firstPos; pos > 0; pos--) {
176 | originImageList.add(0, null);
177 | }
178 | }
179 | if (lastPos < totalCount) {
180 | for (int i = (totalCount - 1 - lastPos); i > 0; i--) {
181 | originImageList.add(null);
182 | }
183 | }
184 | }
185 |
186 | /**
187 | * 配置 transferee 参数对象
188 | *
189 | * @param config 参数对象
190 | * @return transferee
191 | */
192 | public Transferee apply(TransferConfig config) {
193 | if (!shown) {
194 | transConfig = config;
195 | fillOriginImages();
196 | checkConfig();
197 | transLayout.apply(config);
198 | }
199 | return this;
200 | }
201 |
202 | /**
203 | * transferee 是否显示
204 | *
205 | * @return true :显示, false :关闭
206 | */
207 | public boolean isShown() {
208 | return shown;
209 | }
210 |
211 | /**
212 | * 显示 transferee
213 | */
214 | public void show() {
215 | if (shown) return;
216 | transDialog.show();
217 | if (transListener != null)
218 | transListener.onShow();
219 |
220 | shown = true;
221 | }
222 |
223 | /**
224 | * 显示 transferee, 并设置 OnTransfereeChangeListener
225 | *
226 | * @param listener {@link OnTransfereeStateChangeListener}
227 | */
228 | public void show(OnTransfereeStateChangeListener listener) {
229 | if (shown) return;
230 | transDialog.show();
231 | transListener = listener;
232 | transListener.onShow();
233 |
234 | shown = true;
235 | }
236 |
237 | /**
238 | * 关闭 transferee
239 | */
240 | public void dismiss() {
241 | if (!shown) return;
242 | transLayout.dismiss(transConfig.getNowThumbnailIndex());
243 | shown = false;
244 | }
245 |
246 | /**
247 | * 获取图片文件
248 | */
249 | public File getImageFile(String imageUri) {
250 | return transConfig.getImageLoader().getCache(imageUri);
251 | }
252 |
253 | /**
254 | * 清除 transferee 缓存
255 | */
256 | public void clear() {
257 | if (transConfig != null && transConfig.getImageLoader() != null) {
258 | transConfig.getImageLoader().clearCache();
259 | }
260 | }
261 |
262 | @Override
263 | public void onShow(DialogInterface dialog) {
264 | transLayout.show();
265 | }
266 |
267 | @Override
268 | public void onReset() {
269 | transDialog.dismiss();
270 | if (transListener != null)
271 | transListener.onDismiss();
272 |
273 | shown = false;
274 | }
275 |
276 | @Override
277 | public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
278 | if (keyCode == KeyEvent.KEYCODE_BACK &&
279 | event.getAction() == KeyEvent.ACTION_UP &&
280 | !event.isCanceled()) {
281 | dismiss();
282 | }
283 | return true;
284 | }
285 |
286 | /**
287 | * 设置 Transferee 显示和关闭的监听器
288 | *
289 | * @param listener {@link OnTransfereeStateChangeListener}
290 | */
291 | public void setOnTransfereeStateChangeListener(OnTransfereeStateChangeListener listener) {
292 | transListener = listener;
293 | }
294 |
295 | /**
296 | * Transferee 显示的时候调用 {@link OnTransfereeStateChangeListener#onShow()}
297 | *
298 | * Transferee 关闭的时候调用 {@link OnTransfereeStateChangeListener#onDismiss()}
299 | */
300 | public interface OnTransfereeStateChangeListener {
301 | void onShow();
302 |
303 | void onDismiss();
304 | }
305 |
306 | public interface OnTransfereeLongClickListener {
307 | void onLongClick(ImageView imageView, String imageUri, int pos);
308 | }
309 |
310 | }
311 |
--------------------------------------------------------------------------------
/pictureLibrary/src/main/java/com/giftedcat/picture/lib/photoview/transfer/TransferLayout.java:
--------------------------------------------------------------------------------
1 | package com.giftedcat.picture.lib.photoview.transfer;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.PropertyValuesHolder;
6 | import android.animation.ValueAnimator;
7 | import android.content.Context;
8 | import android.graphics.Color;
9 | import android.view.MotionEvent;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.view.animation.AccelerateDecelerateInterpolator;
13 | import android.widget.FrameLayout;
14 | import android.widget.ImageView;
15 |
16 | import androidx.viewpager.widget.ViewPager;
17 |
18 | import com.giftedcat.picture.lib.photoview.style.IIndexIndicator;
19 | import com.giftedcat.picture.lib.photoview.view.image.TransferImage;
20 |
21 | import java.util.HashSet;
22 | import java.util.Set;
23 |
24 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
25 |
26 | /**
27 | * Transferee 中 Dialog 显示的内容
28 | *
29 | * 所有过渡动画的展示,图片的加载都是在这个 FrameLayout 中实现
30 | *
31 | * Created by Hitomis on 2017/4/23 0023.
32 | *
33 | * email: 196425254@qq.com
34 | */
35 | class TransferLayout extends FrameLayout {
36 | private Context context;
37 |
38 | private TransferImage transImage;
39 | private TransferConfig transConfig;
40 | private DragCloseGesture dragCloseGesture;
41 |
42 | private OnLayoutResetListener layoutResetListener;
43 | private Set loadedIndexSet;
44 |
45 | TransferAdapter transAdapter;
46 | ViewPager transViewPager;
47 | float alpha; // [0.f , 255.f]
48 |
49 | /**
50 | * 构造方法
51 | *
52 | * @param context 上下文环境
53 | */
54 | TransferLayout(Context context) {
55 | super(context);
56 |
57 | this.context = context;
58 | this.loadedIndexSet = new HashSet<>();
59 | }
60 |
61 | /**
62 | * ViewPager 页面切换监听器 => 当页面切换时,根据相邻优先加载的规则去加载图片
63 | */
64 | private ViewPager.OnPageChangeListener transChangeListener = new ViewPager.SimpleOnPageChangeListener() {
65 | @Override
66 | public void onPageSelected(int position) {
67 | transConfig.setNowThumbnailIndex(position);
68 |
69 | if (transConfig.isJustLoadHitImage()) {
70 | loadSourceImageOffset(position, 0);
71 | } else {
72 | for (int i = 1; i <= transConfig.getOffscreenPageLimit(); i++) {
73 | loadSourceImageOffset(position, i);
74 | }
75 | }
76 | }
77 | };
78 | /**
79 | * TransferAdapter 中对应页面创建完成监听器
80 | */
81 | private TransferAdapter.OnInstantiateItemListener instantListener = new TransferAdapter.OnInstantiateItemListener() {
82 | @Override
83 | public void onComplete() {
84 | transViewPager.addOnPageChangeListener(transChangeListener);
85 |
86 | int position = transConfig.getNowThumbnailIndex();
87 | if (transConfig.isJustLoadHitImage()) {
88 | loadSourceImageOffset(position, 0);
89 | } else {
90 | loadSourceImageOffset(position, 1);
91 | }
92 |
93 | }
94 | };
95 | /**
96 | * TransferImage 伸/缩动画执行完成监听器
97 | */
98 | TransferImage.OnTransferListener transListener = new TransferImage.OnTransferListener() {
99 | @Override
100 | public void onTransferStart(int state, int cate, int stage) {
101 | }
102 |
103 | @Override
104 | public void onTransferUpdate(int state, float fraction) {
105 | alpha = (state == TransferImage.STATE_TRANS_SPEC_OUT ? alpha : 255) * fraction;
106 | setBackgroundColor(getBackgroundColorByAlpha(alpha));
107 | }
108 |
109 | @Override
110 | public void onTransferComplete(int state, int cate, int stage) {
111 | if (cate == TransferImage.CATE_ANIMA_TOGETHER) {
112 | switch (state) {
113 | case TransferImage.STATE_TRANS_IN: // 伸展动画执行完毕
114 | addIndexIndicator();
115 | addCustomView();
116 | transViewPager.setVisibility(View.VISIBLE);
117 | removeFromParent(transImage);
118 | break;
119 | case TransferImage.STATE_TRANS_OUT: // 缩小动画执行完毕
120 | case TransferImage.STATE_TRANS_SPEC_OUT:
121 | resetTransfer();
122 | break;
123 | }
124 | } else { // 如果动画是分离的
125 | switch (state) {
126 | case TransferImage.STATE_TRANS_IN:
127 | if (stage == TransferImage.STAGE_TRANSLATE) {
128 | // 第一阶段位移动画执行完毕
129 | addIndexIndicator();
130 | addCustomView();
131 | transViewPager.setVisibility(View.VISIBLE);
132 | removeFromParent(transImage);
133 | }
134 | break;
135 | case TransferImage.STATE_TRANS_OUT:
136 | if (stage == TransferImage.STAGE_TRANSLATE) {
137 | // 位移动画执行完毕
138 | resetTransfer();
139 | }
140 | break;
141 | }
142 | }
143 | }
144 | };
145 |
146 | @Override
147 | public boolean onInterceptTouchEvent(MotionEvent ev) {
148 | if (ev.getPointerCount() == 1) {
149 | if (dragCloseGesture != null && dragCloseGesture.onInterceptTouchEvent(ev)) {
150 | return true;
151 | }
152 | }
153 | return super.onInterceptTouchEvent(ev);
154 | }
155 |
156 | @Override
157 | public boolean onTouchEvent(MotionEvent event) {
158 | if (dragCloseGesture != null)
159 | dragCloseGesture.onTouchEvent(event);
160 | return super.onTouchEvent(event);
161 | }
162 |
163 |
164 | /**
165 | * 获取带透明度的颜色值
166 | *
167 | * @param alpha [1, 255]
168 | * @return color int value
169 | */
170 | int getBackgroundColorByAlpha(float alpha) {
171 | int bgColor = transConfig.getBackgroundColor();
172 | return Color.argb(Math.round(alpha), Color.red(bgColor), Color.green(bgColor), Color.blue(bgColor));
173 | }
174 |
175 | /**
176 | * 加载 [position - offset] 到 [position + offset] 范围内有效索引位置的图片
177 | *
178 | * @param position 当前显示图片的索引
179 | * @param offset postion 左右便宜量
180 | */
181 | private void loadSourceImageOffset(int position, int offset) {
182 | int left = position - offset;
183 | int right = position + offset;
184 |
185 | if (!loadedIndexSet.contains(position)) {
186 | loadSourceImage(position);
187 | loadedIndexSet.add(position);
188 | }
189 | if (left >= 0 && !loadedIndexSet.contains(left)) {
190 | loadSourceImage(left);
191 | loadedIndexSet.add(left);
192 | }
193 | if (right < transConfig.getSourceImageList().size() && !loadedIndexSet.contains(right)) {
194 | loadSourceImage(right);
195 | loadedIndexSet.add(right);
196 | }
197 | }
198 |
199 | /**
200 | * 加载索引位置为 position 处的图片
201 | *
202 | * @param position 当前有效的索引
203 | */
204 | private void loadSourceImage(int position) {
205 | getTransferState(position).transferLoad(position);
206 | }
207 |
208 | /**
209 | * 重置 TransferLayout 布局中的内容
210 | */
211 | private void resetTransfer() {
212 | loadedIndexSet.clear();
213 | removeIndexIndicator();
214 | removeAllViews();
215 | layoutResetListener.onReset();
216 | }
217 |
218 | /**
219 | * 创建 ViewPager 并添加到 TransferLayout 中
220 | */
221 | private void createTransferViewPager() {
222 | transAdapter = new TransferAdapter(this,
223 | transConfig.getSourceImageList().size(),
224 | transConfig.getNowThumbnailIndex());
225 | transAdapter.setOnInstantListener(instantListener);
226 |
227 | transViewPager = new ViewPager(context);
228 | // 先隐藏,待 ViewPager 下标为 config.getCurrOriginIndex() 的页面创建完毕再显示
229 | transViewPager.setVisibility(View.INVISIBLE);
230 | transViewPager.setOffscreenPageLimit(transConfig.getOffscreenPageLimit() + 1);
231 | transViewPager.setAdapter(transAdapter);
232 | transViewPager.setCurrentItem(transConfig.getNowThumbnailIndex());
233 |
234 | addView(transViewPager, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
235 | }
236 |
237 | /**
238 | * 将 view 从 view 的父布局中移除
239 | *
240 | * @param view 待移除的 view
241 | */
242 | private void removeFromParent(View view) {
243 | ViewGroup vg = (ViewGroup) view.getParent();
244 | if (vg != null)
245 | vg.removeView(view);
246 | }
247 |
248 | @Override
249 | protected void onDetachedFromWindow() {
250 | super.onDetachedFromWindow();
251 | // unregister PageChangeListener
252 | transViewPager.removeOnPageChangeListener(transChangeListener);
253 | }
254 |
255 | TransferConfig getTransConfig() {
256 | return transConfig;
257 | }
258 |
259 | TransferImage getCurrentImage() {
260 | return transAdapter.getImageItem(transViewPager.getCurrentItem());
261 | }
262 |
263 |
264 | /**
265 | * 初始化 TransferLayout 中的各个组件,并执行图片从缩略图到 Transferee 进入动画
266 | */
267 | void show() {
268 | createTransferViewPager();
269 |
270 | int nowThumbnailIndex = transConfig.getNowThumbnailIndex();
271 | TransferState transferState = getTransferState(nowThumbnailIndex);
272 | transImage = transferState.createTransferIn(nowThumbnailIndex);
273 | }
274 |
275 | /**
276 | * 依据当前有效索引 position 创建并返回一个 {@link TransferState}
277 | *
278 | * @param position 前有效索引
279 | * @return {@link TransferState}
280 | */
281 | TransferState getTransferState(int position) {
282 | TransferState transferState;
283 |
284 | if (!transConfig.isThumbnailEmpty()) { // 客户端指定了缩略图路径集合
285 | transferState = new RemoteThumbState(this);
286 | } else {
287 | String url = transConfig.getSourceImageList().get(position);
288 |
289 | // 即使是网络图片,但是之前已经加载到本地,那么也是本地图片
290 | if (transConfig.getImageLoader().getCache(url) != null) {
291 | transferState = new LocalThumbState(this);
292 | } else {
293 | transferState = new EmptyThumbState(this);
294 | }
295 | }
296 |
297 | return transferState;
298 | }
299 |
300 | /**
301 | * 为加载完的成图片ImageView 绑定点 Transferee 操作事件
302 | *
303 | * @param imageView 加载完成的 ImageView
304 | * @param pos 关闭 Transferee 时图片所在的索引
305 | */
306 | void bindOnOperationListener(final ImageView imageView, final String imageUri, final int pos) {
307 | // bind click dismiss listener
308 | imageView.setOnClickListener(new OnClickListener() {
309 | @Override
310 | public void onClick(View v) {
311 | dismiss(pos);
312 | }
313 | });
314 |
315 | // bind long click listener
316 | if (transConfig.getLongClickListener() != null)
317 | imageView.setOnLongClickListener(new OnLongClickListener() {
318 | @Override
319 | public boolean onLongClick(View v) {
320 | transConfig.getLongClickListener().onLongClick(imageView, imageUri, pos);
321 | return false;
322 | }
323 | });
324 | }
325 |
326 | /**
327 | * 开启 Transferee 关闭动画,并隐藏 transferLayout 中的各个组件
328 | *
329 | * @param pos 关闭 Transferee 时图片所在的索引
330 | */
331 | void dismiss(int pos) {
332 | if (transImage != null && transImage.getState()
333 | == TransferImage.STATE_TRANS_OUT) // 防止双击
334 | return;
335 |
336 | transImage = getTransferState(pos).transferOut(pos);
337 |
338 | if (transImage == null)
339 | diffusionTransfer(pos);
340 | else
341 | transViewPager.setVisibility(View.INVISIBLE);
342 |
343 | hideIndexIndicator();
344 |
345 | }
346 |
347 | /**
348 | * 扩散消失动画
349 | *
350 | * @param pos 动画作用于 pos 索引位置的图片
351 | */
352 | void diffusionTransfer(int pos) {
353 | transImage = transAdapter.getImageItem(pos);
354 | transImage.setState(TransferImage.STATE_TRANS_OUT);
355 | transImage.disable();
356 |
357 | ValueAnimator valueAnimator = new ValueAnimator();
358 | valueAnimator.setDuration(transConfig.getDuration());
359 | valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
360 |
361 | PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofFloat("alpha", alpha, 0.f);
362 | PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 1.f, 1.2f);
363 | valueAnimator.setValues(alphaHolder, scaleXHolder);
364 |
365 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
366 | @Override
367 | public void onAnimationUpdate(ValueAnimator animation) {
368 | float alpha = (Float) animation.getAnimatedValue("alpha");
369 | float scale = (Float) animation.getAnimatedValue("scaleX");
370 |
371 | setBackgroundColor(getBackgroundColorByAlpha(alpha));
372 | transImage.setAlpha(alpha / 255.f);
373 | transImage.setScaleX(scale);
374 | transImage.setScaleY(scale);
375 | }
376 | });
377 | valueAnimator.addListener(new AnimatorListenerAdapter() {
378 | @Override
379 | public void onAnimationEnd(Animator animation) {
380 | resetTransfer();
381 | }
382 | });
383 |
384 | valueAnimator.start();
385 | }
386 |
387 | /**
388 | * 配置参数
389 | *
390 | * @param config 参数对象
391 | */
392 | void apply(TransferConfig config) {
393 | transConfig = config;
394 | if (transConfig.isEnableDragClose())
395 | this.dragCloseGesture = new DragCloseGesture(this);
396 | }
397 |
398 | /**
399 | * 绑定 TransferLayout 内容重置时回调监听器
400 | *
401 | * @param listener 重置回调监听器
402 | */
403 | void setOnLayoutResetListener(OnLayoutResetListener listener) {
404 | layoutResetListener = listener;
405 | }
406 |
407 | /**
408 | * 在 TransferImage 面板中添加下标指示器 UI 组件
409 | */
410 | private void addIndexIndicator() {
411 | IIndexIndicator indexIndicator = transConfig.getIndexIndicator();
412 | if (indexIndicator != null && transConfig.getSourceImageList().size() >= 2) {
413 | indexIndicator.attach(this);
414 | indexIndicator.onShow(transViewPager);
415 | }
416 | }
417 |
418 | /**
419 | * 隐藏下标指示器 UI 组件
420 | */
421 | private void hideIndexIndicator() {
422 | IIndexIndicator indexIndicator = transConfig.getIndexIndicator();
423 | if (indexIndicator != null && transConfig.getSourceImageList().size() >= 2) {
424 | indexIndicator.onHide();
425 | }
426 | }
427 |
428 | private void addCustomView() {
429 | View customView = transConfig.getCustomView();
430 | if (customView != null) {
431 | addView(customView);
432 | }
433 | }
434 |
435 | /**
436 | * 从 TransferImage 面板中移除下标指示器 UI 组件
437 | */
438 | private void removeIndexIndicator() {
439 | IIndexIndicator indexIndicator = transConfig.getIndexIndicator();
440 | if (indexIndicator != null && transConfig.getSourceImageList().size() >= 2) {
441 | indexIndicator.onRemove();
442 | }
443 | }
444 |
445 | /**
446 | * TransferLayout 中内容重置时监听器
447 | */
448 | interface OnLayoutResetListener {
449 | /**
450 | * 调用于:当关闭动画执行完毕,TransferLayout 中所有内容已经重置(清空)时
451 | */
452 | void onReset();
453 | }
454 |
455 | }
456 |
--------------------------------------------------------------------------------