├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── lnyp
│ │ └── imgdots
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── lnyp
│ │ │ └── imgdots
│ │ │ ├── activity
│ │ │ ├── ImageBrowseActivity.java
│ │ │ └── MainActivity.java
│ │ │ ├── adapter
│ │ │ └── ImgBrowsePagerAdapter.java
│ │ │ ├── bean
│ │ │ ├── ImgSimple.java
│ │ │ └── PointSimple.java
│ │ │ ├── utils
│ │ │ └── UIUtil.java
│ │ │ └── view
│ │ │ └── ImageLayout.java
│ └── res
│ │ ├── drawable
│ │ └── prod_point_img.xml
│ │ ├── layout
│ │ ├── activity_image_browse.xml
│ │ ├── activity_main.xml
│ │ ├── layout_img_browse.xml
│ │ ├── layout_img_point.xml
│ │ └── layout_imgview_point.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── left_bottom.png
│ │ ├── left_top.png
│ │ ├── right_bottom.png
│ │ ├── rigth_top.png
│ │ ├── test_1.jpg
│ │ ├── test_2.jpg
│ │ └── test_3.jpg
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── point_img.png
│ │ ├── point_img1.png
│ │ ├── point_img10.png
│ │ ├── point_img11.png
│ │ ├── point_img12.png
│ │ ├── point_img13.png
│ │ ├── point_img2.png
│ │ ├── point_img3.png
│ │ ├── point_img4.png
│ │ ├── point_img5.png
│ │ ├── point_img6.png
│ │ ├── point_img7.png
│ │ ├── point_img8.png
│ │ └── point_img9.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── lnyp
│ └── imgdots
│ └── ExampleUnitTest.java
├── build.gradle
├── circle.yml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── imgs
└── GIF.gif
├── library
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── bm
│ │ └── library
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── bm
│ └── library
│ ├── Info.java
│ ├── OnMatrixChangedListener.java
│ ├── PhotoView.java
│ └── RotateGestureDetector.java
├── newDots.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.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 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ImgDots
2 | [](https://badge.fury.io/gh/lihangleo2%2FImgdots)
3 |
4 |
5 | 在图片的特定位置显示标签,标签可以点击
6 |
7 | 效果图:
8 |
9 | 
10 |
11 | 详细介绍:
12 | https://blog.csdn.net/leol_2/article/details/80225435
13 |
14 | 如果对你有所启发,请star下吧
15 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion '28.0.2'
6 | defaultConfig {
7 | applicationId "com.lnyp.imgdots"
8 | minSdkVersion 16
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(include: ['*.jar'], dir: 'libs')
23 | testCompile 'junit:junit:4.12'
24 | implementation 'com.android.support:appcompat-v7:28.0.0'
25 | compile 'com.apkfuns.logutils:library:1.0.6'
26 | compile 'com.github.bumptech.glide:glide:3.7.0'
27 | compile project(':library')
28 | }
29 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Java\android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/lnyp/imgdots/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lnyp/imgdots/activity/ImageBrowseActivity.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots.activity;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.view.PagerAdapter;
5 | import android.support.v4.view.ViewPager;
6 | import android.support.v7.app.AppCompatActivity;
7 |
8 | import com.lnyp.imgdots.R;
9 | import com.lnyp.imgdots.adapter.ImgBrowsePagerAdapter;
10 | import com.lnyp.imgdots.bean.ImgSimple;
11 | import com.lnyp.imgdots.bean.PointSimple;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class ImageBrowseActivity extends AppCompatActivity {
17 |
18 | private ViewPager viewPagerImgs;
19 |
20 | private List imgSimples;
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 |
26 | setContentView(R.layout.activity_image_browse);
27 |
28 |
29 | viewPagerImgs = (ViewPager) this.findViewById(R.id.viewPagerImgs);
30 | viewPagerImgs.setOffscreenPageLimit(2);
31 |
32 | initData();
33 |
34 | PagerAdapter adapter = new ImgBrowsePagerAdapter(this, imgSimples);
35 | viewPagerImgs.setAdapter(adapter);
36 |
37 | }
38 |
39 | private void initData() {
40 |
41 | imgSimples = new ArrayList<>();
42 |
43 | ImgSimple imgSimple1 = new ImgSimple();
44 | imgSimple1.url = R.mipmap.test_1;
45 | imgSimple1.pic_with = 900;
46 | imgSimple1.pic_height = 1200;
47 |
48 | ArrayList pointSimples = new ArrayList<>();
49 | PointSimple pointSimple1 = new PointSimple();
50 | pointSimple1.pic_x = 900;
51 | pointSimple1.pic_y = 1200;
52 | pointSimple1.pointLeft_x = 320;
53 | pointSimple1.pointLeft_y = 30;
54 | pointSimple1.pointRight_x = 600;
55 | pointSimple1.pointRight_y = 380;
56 | pointSimples.add(pointSimple1);
57 |
58 |
59 | imgSimple1.pointSimples = pointSimples;
60 |
61 |
62 | ImgSimple imgSimple2 = new ImgSimple();
63 | imgSimple2.url = R.mipmap.test_3;
64 | imgSimple2.pic_with = 566;
65 | imgSimple2.pic_height = 800;
66 | ArrayList pointSimples2 = new ArrayList<>();
67 | PointSimple pointSimple7 = new PointSimple();
68 | pointSimple7.pic_x = 566;
69 | pointSimple7.pic_y = 800;
70 | pointSimple7.pointLeft_x = 160;
71 | pointSimple7.pointLeft_y = 165;
72 | pointSimple7.pointRight_x = 400;
73 | pointSimple7.pointRight_y = 470;
74 | pointSimples2.add(pointSimple7);
75 | imgSimple2.pointSimples = pointSimples2;
76 |
77 |
78 | ImgSimple imgSimple3 = new ImgSimple();
79 | imgSimple3.url = R.mipmap.test_2;
80 | imgSimple3.pic_with = 580;
81 | imgSimple3.pic_height = 435;
82 | ArrayList pointSimples3 = new ArrayList<>();
83 | PointSimple pointSimple11 = new PointSimple();
84 | pointSimple11.pic_x = 580;
85 | pointSimple11.pic_y = 435;
86 | pointSimple11.pointLeft_x = 300;
87 | pointSimple11.pointLeft_y = 50;
88 | pointSimple11.pointRight_x = 500;
89 | pointSimple11.pointRight_y = 250;
90 | pointSimples3.add(pointSimple11);
91 |
92 |
93 |
94 | imgSimple3.pointSimples = pointSimples3;
95 | imgSimples.add(imgSimple1);
96 | imgSimples.add(imgSimple2);
97 | imgSimples.add(imgSimple3);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lnyp/imgdots/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots.activity;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | import com.lnyp.imgdots.R;
9 |
10 | public class MainActivity extends AppCompatActivity {
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_main);
16 | }
17 |
18 | public void btnClick(View view) {
19 | startActivity(new Intent(this, ImageBrowseActivity.class));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lnyp/imgdots/adapter/ImgBrowsePagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots.adapter;
2 |
3 | import android.app.Activity;
4 | import android.support.v4.view.PagerAdapter;
5 | import android.support.v4.view.ViewPager;
6 | import android.util.DisplayMetrics;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.LinearLayout;
11 |
12 | import com.lnyp.imgdots.R;
13 | import com.lnyp.imgdots.bean.ImgSimple;
14 | import com.lnyp.imgdots.bean.PointSimple;
15 | import com.lnyp.imgdots.view.ImageLayout;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | import static android.R.attr.width;
21 |
22 | public class ImgBrowsePagerAdapter extends PagerAdapter {
23 |
24 | List imgSimples;
25 |
26 | List views;
27 |
28 | Activity mContext;
29 |
30 |
31 | public ImgBrowsePagerAdapter(Activity context, List imgSimples) {
32 |
33 | this.mContext = context;
34 | this.imgSimples = imgSimples;
35 |
36 | this.views = new ArrayList<>();
37 |
38 | DisplayMetrics dm = new DisplayMetrics();
39 | context.getWindowManager().getDefaultDisplay().getMetrics(dm);
40 |
41 | }
42 |
43 | @Override
44 | public int getCount() { // 获得size
45 | return imgSimples.size();
46 | }
47 |
48 | @Override
49 | public boolean isViewFromObject(View arg0, Object arg1) {
50 | return arg0 == arg1;
51 | }
52 |
53 | @Override
54 | public void destroyItem(ViewGroup container, int position, Object object) {
55 |
56 | ((ViewPager) container).removeView((View) object);
57 | }
58 |
59 | @Override
60 | public Object instantiateItem(ViewGroup container, int position) {
61 |
62 | LinearLayout view = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_img_browse, null);
63 | ImageLayout layoutContent = (ImageLayout) view.findViewById(R.id.layoutContent);
64 |
65 | try {
66 |
67 | Integer imgUrl = imgSimples.get(position).url;
68 | int pic_with = imgSimples.get(position).pic_with;
69 | int pic_height = imgSimples.get(position).pic_height;
70 | ArrayList pointSimples = imgSimples.get(position).pointSimples;
71 |
72 | layoutContent.setPoints(pointSimples);
73 | layoutContent.setImgBg(pic_with,pic_height,imgUrl);
74 | } catch (Exception e) {
75 | e.printStackTrace();
76 | }
77 |
78 | ((ViewPager) container).addView(view);
79 |
80 | return view;
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/lnyp/imgdots/bean/ImgSimple.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots.bean;
2 |
3 | import java.util.ArrayList;
4 |
5 | public class ImgSimple {
6 |
7 | public Integer url;
8 | public ArrayList pointSimples;
9 | public int pic_with;
10 | public int pic_height;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lnyp/imgdots/bean/PointSimple.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots.bean;
2 |
3 | /**
4 | * Created by lining on 2016/7/14.
5 | */
6 | public class PointSimple {
7 |
8 | // 标记点相对于横向的宽度的比例
9 | // public int width_scale;
10 | // 标记点相对于横向的高度的比例
11 | // public int height_scale;
12 | public int pointLeft_x;
13 | public int pointLeft_y;
14 | public int pointRight_x;
15 | public int pointRight_y;
16 | public int pic_x;
17 | public int pic_y;
18 |
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lnyp/imgdots/utils/UIUtil.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots.utils;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.os.Build;
7 | import android.telephony.TelephonyManager;
8 | import android.util.TypedValue;
9 | import android.view.WindowManager;
10 |
11 | import java.lang.reflect.Field;
12 | import java.util.Locale;
13 | import java.util.UUID;
14 |
15 |
16 |
17 | /**
18 | * Created by lihang Leo on 2016/12/10.
19 | */
20 | public class UIUtil {
21 |
22 | private static final String TAG = UIUtil.class.getName();
23 |
24 | /**
25 | * Dip to Px
26 | *
27 | * @param context
28 | * @param dipValue
29 | * @return
30 | */
31 | public static int dip2px(Context context, float dipValue) {
32 | float scale = context.getResources().getDisplayMetrics().density;
33 | return (int) (dipValue * scale + 0.5f);
34 | }
35 |
36 | /**
37 | * Px To Dip
38 | *
39 | * @param context
40 | * @param pxValue
41 | * @return
42 | */
43 | public static int px2dip(Context context, float pxValue) {
44 | float scale = context.getResources().getDisplayMetrics().density;
45 | return (int) (pxValue / scale + 0.5f);
46 | }
47 |
48 | //sp转px
49 | public static int Sp2Px(Context context, int sp) {
50 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics());
51 | }
52 |
53 |
54 | public static int getWidth(Context context) {
55 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
56 | int width = wm.getDefaultDisplay().getWidth();
57 | return width;
58 | }
59 |
60 | public static int getHeight(Context context) {
61 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
62 | int height = wm.getDefaultDisplay().getHeight();
63 | return height;
64 | }
65 |
66 | //获取手机状态栏高度
67 | public static int getStatusBarHeight(Context context) {
68 | Class> c = null;
69 | Object obj = null;
70 | Field field = null;
71 | int x = 0, statusBarHeight = 0;
72 | try {
73 | c = Class.forName("com.android.internal.R$dimen");
74 | obj = c.newInstance();
75 | field = c.getField("status_bar_height");
76 | x = Integer.parseInt(field.get(obj).toString());
77 | statusBarHeight = context.getResources().getDimensionPixelSize(x);
78 | } catch (Exception e1) {
79 | e1.printStackTrace();
80 | }
81 | return statusBarHeight;
82 | }
83 |
84 |
85 |
86 |
87 | //获得独一无二的Psuedo ID
88 | public static String getDeviceId() {
89 | String serial = null;
90 | String m_szDevIDShort = "35" +
91 | Build.BOARD.length() % 10 + Build.BRAND.length() % 10 +
92 |
93 | Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 +
94 |
95 | Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 +
96 |
97 | Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 +
98 |
99 | Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 +
100 |
101 | Build.TAGS.length() % 10 + Build.TYPE.length() % 10 +
102 |
103 | Build.USER.length() % 10; //13 位
104 |
105 | try {
106 | serial = Build.class.getField("SERIAL").get(null).toString();
107 | //API>=9 使用serial号
108 | return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
109 | } catch (Exception exception) {
110 | //serial需要一个初始化
111 | serial = "serial"; // 随便一个初始化
112 | }
113 | //使用硬件信息拼凑出来的15位号码
114 | return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
115 | }
116 |
117 |
118 | /**
119 | * 获取当前手机系统语言。
120 | *
121 | * @return 返回当前系统语言。例如:当前设置的是“中文-中国”,则返回“zh-CN”
122 | */
123 | public static String getSystemLanguage() {
124 | return Locale.getDefault().getLanguage();
125 | }
126 |
127 | /**
128 | * 获取当前系统上的语言列表(Locale列表)
129 | *
130 | * @return 语言列表
131 | */
132 | public static Locale[] getSystemLanguageList() {
133 | return Locale.getAvailableLocales();
134 | }
135 |
136 | /**
137 | * 获取当前手机系统版本号
138 | *
139 | * @return 系统版本号
140 | */
141 | public static String getSystemVersion() {
142 | return Build.VERSION.RELEASE;
143 | }
144 |
145 | /**
146 | * 获取手机型号
147 | *
148 | * @return 手机型号
149 | */
150 | public static String getSystemModel() {
151 | return Build.MODEL;
152 | }
153 |
154 | /**
155 | * 获取手机厂商
156 | *
157 | * @return 手机厂商
158 | */
159 | public static String getDeviceBrand() {
160 | return Build.BRAND;
161 | }
162 |
163 |
164 | /**
165 | * get App versionCode
166 | *
167 | * @param context
168 | * @return
169 | */
170 | public static String getVersionCode(Context context) {
171 | PackageManager packageManager = context.getPackageManager();
172 | PackageInfo packageInfo;
173 | String versionCode = "";
174 | try {
175 | packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
176 | versionCode = packageInfo.versionCode + "";
177 | } catch (PackageManager.NameNotFoundException e) {
178 | e.printStackTrace();
179 | }
180 | return versionCode;
181 | }
182 |
183 | /**
184 | * get App versionName
185 | *
186 | * @param context
187 | * @return
188 | */
189 | public static String getVersionName(Context context) {
190 | PackageManager packageManager = context.getPackageManager();
191 | PackageInfo packageInfo;
192 | String versionName = "";
193 | try {
194 | packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
195 | versionName = packageInfo.versionName;
196 | } catch (PackageManager.NameNotFoundException e) {
197 | e.printStackTrace();
198 | }
199 | return versionName;
200 | }
201 |
202 |
203 | }
204 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lnyp/imgdots/view/ImageLayout.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.RectF;
5 | import android.util.AttributeSet;
6 | import android.util.Log;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.FrameLayout;
11 | import android.widget.ImageView;
12 | import android.widget.RelativeLayout;
13 | import android.widget.Toast;
14 |
15 | import com.bm.library.Info;
16 | import com.bm.library.OnMatrixChangedListener;
17 | import com.bm.library.PhotoView;
18 | import com.bumptech.glide.Glide;
19 | import com.lnyp.imgdots.R;
20 | import com.lnyp.imgdots.bean.PointSimple;
21 | import com.lnyp.imgdots.utils.UIUtil;
22 |
23 | import java.util.ArrayList;
24 |
25 | public class ImageLayout extends FrameLayout implements View.OnClickListener {
26 |
27 | ArrayList points;
28 |
29 | FrameLayout layouPoints;
30 |
31 | PhotoView imgBg;
32 |
33 | Context mContext;
34 |
35 | public ImageLayout(Context context) {
36 | this(context, null);
37 | }
38 |
39 | public ImageLayout(Context context, AttributeSet attrs) {
40 | this(context, attrs, 0);
41 | }
42 |
43 | public ImageLayout(Context context, AttributeSet attrs, int defStyleAttr) {
44 | super(context, attrs, defStyleAttr);
45 |
46 | initView(context, attrs);
47 | }
48 |
49 |
50 | private void initView(Context context, AttributeSet attrs) {
51 |
52 | mContext = context;
53 |
54 | View imgPointLayout = inflate(context, R.layout.layout_imgview_point, this);
55 |
56 | imgBg = (PhotoView) imgPointLayout.findViewById(R.id.imgBg);
57 | imgBg.enable();
58 | imgBg.disableRotate();
59 | imgBg.setOnClickListener(this);
60 | imgBg.setOnMatrixChangeListener(new OnMatrixChangedListener() {
61 | @Override
62 | public void onMatrixChanged(RectF rect) {
63 | Log.e("是不是有了",rect.toString()+"");
64 | Info info = PhotoView.getImageViewInfo(imgBg);
65 | int left = (int) info.getmImgRect().left;
66 | int top = (int) info.getmImgRect().top;
67 | int right = (int) info.getmImgRect().right;
68 | int bottom = (int) info.getmImgRect().bottom;
69 | addPoints(left,top,right,bottom);
70 | }
71 | });
72 |
73 |
74 | layouPoints = (FrameLayout) imgPointLayout.findViewById(R.id.layouPoints);
75 | }
76 |
77 | @Override
78 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
79 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
80 | }
81 |
82 | @Override
83 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
84 | super.onSizeChanged(w, h, oldw, oldh);
85 | }
86 |
87 | public void setImgBg(int pic_with, int pic_height, Integer imgUrl) {
88 |
89 | ViewGroup.LayoutParams lp = imgBg.getLayoutParams();
90 | lp.width = UIUtil.getWidth(getContext());
91 | lp.height = UIUtil.getHeight(getContext());
92 | imgBg.setLayoutParams(lp);
93 |
94 |
95 | ViewGroup.LayoutParams lp1 = layouPoints.getLayoutParams();
96 | lp1.width = UIUtil.getWidth(getContext());
97 | lp1.height = UIUtil.getHeight(getContext());
98 | layouPoints.setLayoutParams(lp1);
99 | Glide.with(mContext).load(imgUrl).asBitmap().into(imgBg);
100 |
101 | // addPoints(UIUtil.getWidth(getContext()), UIUtil.getHeight(getContext()));
102 |
103 | }
104 |
105 | public void setPoints(ArrayList points) {
106 |
107 | this.points = points;
108 | }
109 |
110 | private void addPoints(int width, int height) {
111 |
112 | layouPoints.removeAllViews();
113 |
114 | for (int i = 0; i < points.size(); i++) {
115 |
116 | int point_left_x = points.get(i).pointLeft_x;
117 | int point_left_y = points.get(i).pointLeft_y;
118 | int pic_with = points.get(i).pic_x;
119 | int pic_height = points.get(i).pic_y;
120 |
121 | int point_rigth_x = points.get(i).pointRight_x;
122 | int point_right_y = points.get(i).pointRight_y;
123 |
124 | int view_x = point_rigth_x - point_left_x;
125 | int view_y = point_right_y - point_left_y;
126 |
127 |
128 | RelativeLayout view = (RelativeLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_img_point, this, false);
129 | RelativeLayout relativeLayout = (RelativeLayout) view.findViewById(R.id.relative_point);
130 | relativeLayout.setTag(i);
131 |
132 | RelativeLayout.LayoutParams linlayout = (RelativeLayout.LayoutParams) relativeLayout.getLayoutParams();
133 | linlayout.width = (width * view_x / pic_with);
134 | linlayout.height = UIUtil.getWidth(getContext()) * view_y / pic_with;
135 |
136 |
137 | LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
138 | layoutParams.leftMargin = (int) (width * point_left_x / pic_with);
139 | layoutParams.topMargin = (int) (UIUtil.getWidth(getContext()) * point_left_y / pic_with);
140 | relativeLayout.setOnClickListener(this);
141 | layouPoints.addView(view, layoutParams);
142 | }
143 | }
144 |
145 |
146 | private void addPoints(int left, int top, int right, int bottom) {
147 |
148 | layouPoints.removeAllViews();
149 |
150 | for (int i = 0; i < points.size(); i++) {
151 |
152 | int point_left_x = points.get(i).pointLeft_x;
153 | int point_left_y = points.get(i).pointLeft_y;
154 | int pic_with = points.get(i).pic_x;
155 | int pic_height = points.get(i).pic_y;
156 | int point_rigth_x = points.get(i).pointRight_x;
157 | int point_right_y = points.get(i).pointRight_y;
158 |
159 |
160 | //相对窗口宽度
161 | int screenWith = right - left;
162 | int screenHeight = bottom - top;
163 |
164 |
165 | int tempX = point_left_x * screenWith/ pic_with ;
166 | Log.e("我来看看是什么请撒",point_left_x+"==============="+pic_with+"`````````````"+screenWith);
167 |
168 | int tempY = point_left_y * screenHeight/ pic_height ;
169 | Log.e("我来看看是什么请撒",point_left_y+"==============="+pic_height+"`````````````"+screenHeight);
170 |
171 | //正确的坐标 左上角
172 | int trueX = left + tempX;
173 | int trueY = top + tempY;
174 | Log.e("这个Y轴是什么鬼",top+"----------------"+tempY);
175 | Log.e("我来看看是什么请撒",tempX+"==============="+tempY);
176 |
177 | //长宽
178 | int chaX = point_rigth_x - point_left_x;
179 | int chaY = point_right_y - point_left_y;
180 |
181 | int viewX = screenWith*chaX/pic_with;
182 | int viewY = screenHeight*chaY/pic_height;
183 |
184 |
185 | RelativeLayout view = (RelativeLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_img_point, this, false);
186 | RelativeLayout relativeLayout = (RelativeLayout) view.findViewById(R.id.relative_point);
187 | relativeLayout.setTag(i);
188 |
189 | RelativeLayout.LayoutParams linlayout = (RelativeLayout.LayoutParams) relativeLayout.getLayoutParams();
190 | linlayout.width = viewX;
191 | linlayout.height= viewY;
192 |
193 |
194 | LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
195 | layoutParams.leftMargin = trueX;
196 | layoutParams.topMargin = trueY;
197 | relativeLayout.setOnClickListener(this);
198 |
199 |
200 | //左上角的点
201 | ImageView imageView_top_left = new ImageView(getContext());
202 | LayoutParams leoParams_top_left = new LayoutParams(layoutParams.WRAP_CONTENT, layoutParams.WRAP_CONTENT);
203 | leoParams_top_left.leftMargin = trueX;
204 | leoParams_top_left.topMargin = trueY;
205 | imageView_top_left.setImageResource(R.mipmap.left_top);
206 |
207 | //左下角的点
208 | ImageView imageView_bottom_left = new ImageView(getContext());
209 | LayoutParams leoParams_bottom_left = new LayoutParams(layoutParams.WRAP_CONTENT, layoutParams.WRAP_CONTENT);
210 | leoParams_bottom_left.leftMargin = trueX;
211 | leoParams_bottom_left.topMargin = trueY+viewY-60;//减去图片自身高度
212 | imageView_bottom_left.setImageResource(R.mipmap.left_bottom);
213 |
214 |
215 | //右上角的点
216 | ImageView imageView_top_right = new ImageView(getContext());
217 | LayoutParams leoParams_top_right = new LayoutParams(layoutParams.WRAP_CONTENT, layoutParams.WRAP_CONTENT);
218 | leoParams_top_right.leftMargin = trueX+viewX-65;
219 | leoParams_top_right.topMargin = trueY;
220 | imageView_top_right.setImageResource(R.mipmap.rigth_top);
221 |
222 |
223 | //右下角的点
224 | ImageView imageView_bottom_right = new ImageView(getContext());
225 | LayoutParams leoParams_bottom_right = new LayoutParams(layoutParams.WRAP_CONTENT, layoutParams.WRAP_CONTENT);
226 | leoParams_bottom_right.leftMargin = trueX+viewX-65;
227 | leoParams_bottom_right.topMargin = trueY+viewY-60;
228 | imageView_bottom_right.setImageResource(R.mipmap.right_bottom);
229 |
230 |
231 | layouPoints.addView(view, layoutParams);
232 | layouPoints.addView(imageView_top_left, leoParams_top_left);
233 | layouPoints.addView(imageView_bottom_left, leoParams_bottom_left);
234 | layouPoints.addView(imageView_top_right, leoParams_top_right);
235 | layouPoints.addView(imageView_bottom_right, leoParams_bottom_right);
236 | }
237 | }
238 |
239 |
240 | @Override
241 | public void onClick(View view) {
242 | switch (view.getId()) {
243 | case R.id.imgBg:
244 | Toast.makeText(getContext(), "点击了其他地方" + ((PhotoView) view).getScale(), Toast.LENGTH_SHORT).show();
245 | // float f = ((PhotoView)view).getScale();
246 | // addPoints((int) (UIUtil.getWidth(getContext())*f), (int) (UIUtil.getHeight(getContext())*f));
247 |
248 | break;
249 |
250 | case R.id.relative_point:
251 | int pos = (int) view.getTag();
252 | Toast.makeText(getContext(), "pos : " + pos, Toast.LENGTH_SHORT).show();
253 |
254 | break;
255 | }
256 |
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/prod_point_img.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
34 |
37 |
40 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_image_browse.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_img_browse.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_img_point.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_imgview_point.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/left_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xhdpi/left_bottom.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/left_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xhdpi/left_top.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/right_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xhdpi/right_bottom.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/rigth_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xhdpi/rigth_top.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/test_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xhdpi/test_1.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/test_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xhdpi/test_2.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/test_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xhdpi/test_3.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img1.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img10.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img11.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img12.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img13.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img3.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img4.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img5.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img6.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img7.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img8.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/point_img9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxhdpi/point_img9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ImgDots
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/lnyp/imgdots/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.lnyp.imgdots;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | maven {
6 | url 'https://maven.google.com/'
7 | name 'Google'
8 | }
9 | jcenter()
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.2.0'
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | maven {
19 | url 'https://maven.google.com/'
20 | name 'Google'
21 | }
22 | jcenter()
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | node:
3 | version: 6
4 |
5 | dependencies:
6 | override:
7 | - yarn
8 | cache_directories:
9 | - ~/.cache/yarn
10 |
11 | test:
12 | override:
13 | - npm test
14 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jan 22 17:09:19 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/imgs/GIF.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/imgs/GIF.gif
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | //apply plugin: 'com.github.dcendents.android-maven'
3 | //apply plugin: 'com.jfrog.bintray'
4 |
5 | version "1.4.1"
6 |
7 | android {
8 | compileSdkVersion 23
9 | buildToolsVersion "23.0.3"
10 | resourcePrefix "photoview"
11 |
12 | defaultConfig {
13 | minSdkVersion 10
14 | targetSdkVersion 23
15 | versionCode 5
16 | versionName version
17 | }
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | compile fileTree(include: ['*.jar'], dir: 'libs')
28 | }
29 |
30 | //def siteUrl = 'https://github.com/bm-x/PhotoView'
31 | //def gitUrl = 'https://github.com/bm-x/PhotoView.git'
32 | //group = "com.bm.photoview"
33 | //install {
34 | // repositories.mavenInstaller {
35 | // //This generates POM.xml with proper parameters
36 | // pom {
37 | // project {
38 | // packaging 'aar'
39 | // // Add your description here
40 | // name 'PhotoView'
41 | // url siteUrl
42 | // // Set your license
43 | // licenses {
44 | // license {
45 | // name 'The Apache Software License, Version 2.0'
46 | // url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
47 | // }
48 | // }
49 | // developers {
50 | // developer {
51 | // id 'photoview'
52 | // name 'bmme'
53 | // email 'bmme@vip.qq.com'
54 | // }
55 | // }
56 | // scm {
57 | // connection gitUrl
58 | // developerConnection gitUrl
59 | // url siteUrl
60 | // }
61 | // }
62 | // }
63 | // }
64 | //}
65 | //
66 | //task sourcesJar(type: Jar) {
67 | // from android.sourceSets.main.java.srcDirs
68 | // classifier = 'sources'
69 | //}
70 | //task javadoc(type: Javadoc) {
71 | // options.encoding = "UTF-8"
72 | // source = android.sourceSets.main.java.srcDirs
73 | // classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
74 | //}
75 | //task javadocJar(type: Jar, dependsOn: javadoc) {
76 | // classifier = 'javadoc'
77 | // from javadoc.destinationDir
78 | //}
79 | //artifacts {
80 | // archives javadocJar
81 | // archives sourcesJar
82 | //}
83 | //Properties properties = new Properties()
84 | //properties.load(project.rootProject.file('local.properties').newDataInputStream())
85 | //bintray {
86 | // user = properties.getProperty("bintray.user")
87 | // key = properties.getProperty("bintray.apikey")
88 | // configurations = ['archives']
89 | // pkg {
90 | // repo = "maven"
91 | // name = "PhotoView"
92 | // websiteUrl = siteUrl
93 | // vcsUrl = gitUrl
94 | // licenses = ["Apache-2.0"]
95 | // publish = true
96 | // }
97 | //}
98 |
99 | // gradlew bintrayUpload
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\dev\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/library/src/androidTest/java/com/bm/library/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.bm.library;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/bm/library/Info.java:
--------------------------------------------------------------------------------
1 | package com.bm.library;
2 |
3 | import android.graphics.PointF;
4 | import android.graphics.RectF;
5 | import android.widget.ImageView;
6 |
7 | /**
8 | * Created by liuheng on 2015/8/19.
9 | */
10 | public class Info {
11 |
12 | // 内部图片在整个手机界面的位置
13 | RectF mRect = new RectF();
14 |
15 | // 控件在窗口的位置
16 | RectF mImgRect = new RectF();
17 |
18 | RectF mWidgetRect = new RectF();
19 |
20 | RectF mBaseRect = new RectF();
21 |
22 | PointF mScreenCenter = new PointF();
23 |
24 | float mScale;
25 |
26 | float mDegrees;
27 |
28 | ImageView.ScaleType mScaleType;
29 |
30 | public Info(RectF rect, RectF img, RectF widget, RectF base, PointF screenCenter, float scale, float degrees, ImageView.ScaleType scaleType) {
31 | mRect.set(rect);
32 | mImgRect.set(img);
33 | mWidgetRect.set(widget);
34 | mScale = scale;
35 | mScaleType = scaleType;
36 | mDegrees = degrees;
37 | mBaseRect.set(base);
38 | mScreenCenter.set(screenCenter);
39 | }
40 |
41 | public RectF getmImgRect() {
42 | return mImgRect;
43 | }
44 |
45 | public void setmImgRect(RectF mImgRect) {
46 | this.mImgRect = mImgRect;
47 | }
48 |
49 |
50 | public RectF getmRect() {
51 | return mRect;
52 | }
53 |
54 | public void setmRect(RectF mRect) {
55 | this.mRect = mRect;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/library/src/main/java/com/bm/library/OnMatrixChangedListener.java:
--------------------------------------------------------------------------------
1 | package com.bm.library;
2 |
3 | import android.graphics.RectF;
4 |
5 | /**
6 | * Created by Administrator on 2018/1/10.
7 | */
8 |
9 | public interface OnMatrixChangedListener {
10 | void onMatrixChanged(RectF rect);
11 | }
12 |
--------------------------------------------------------------------------------
/library/src/main/java/com/bm/library/PhotoView.java:
--------------------------------------------------------------------------------
1 | package com.bm.library;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Matrix;
6 | import android.graphics.PointF;
7 | import android.graphics.RectF;
8 | import android.graphics.drawable.Drawable;
9 | import android.util.AttributeSet;
10 | import android.util.Log;
11 | import android.view.GestureDetector;
12 | import android.view.MotionEvent;
13 | import android.view.ScaleGestureDetector;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.view.ViewParent;
17 | import android.view.animation.DecelerateInterpolator;
18 | import android.view.animation.Interpolator;
19 | import android.widget.ImageView;
20 | import android.widget.OverScroller;
21 | import android.widget.Scroller;
22 |
23 | /**
24 | * Created by liuheng on 2015/6/21.
25 | *
26 | * 如有任何意见和建议可邮件 bmme@vip.qq.com
27 | */
28 | public class PhotoView extends ImageView {
29 |
30 | private final static int MIN_ROTATE = 35;
31 | private final static int ANIMA_DURING = 340;
32 | private final static float MAX_SCALE = 2.5f;
33 |
34 | private int mMinRotate;
35 | private int mAnimaDuring;
36 | private float mMaxScale;
37 |
38 | private int MAX_OVER_SCROLL = 0;
39 | private int MAX_FLING_OVER_SCROLL = 0;
40 | private int MAX_OVER_RESISTANCE = 0;
41 | private int MAX_ANIM_FROM_WAITE = 500;
42 |
43 | private Matrix mBaseMatrix = new Matrix();
44 | private Matrix mAnimaMatrix = new Matrix();
45 | private Matrix mSynthesisMatrix = new Matrix();
46 | private Matrix mTmpMatrix = new Matrix();
47 |
48 | private RotateGestureDetector mRotateDetector;
49 | private GestureDetector mDetector;
50 | private ScaleGestureDetector mScaleDetector;
51 | private OnClickListener mClickListener;
52 |
53 | private ScaleType mScaleType;
54 |
55 | private boolean hasMultiTouch;
56 | private boolean hasDrawable;
57 | private boolean isKnowSize;
58 | private boolean hasOverTranslate;
59 | private boolean isEnable = false;
60 | private boolean isRotateEnable = false;
61 | private boolean isInit;
62 | private boolean mAdjustViewBounds;
63 | // 当前是否处于放大状态
64 | private boolean isZoonUp;
65 | private boolean canRotate;
66 |
67 | private boolean imgLargeWidth;
68 | private boolean imgLargeHeight;
69 |
70 | private float mRotateFlag;
71 | private float mDegrees;
72 | private float mScale = 1.0f;
73 | private int mTranslateX;
74 | private int mTranslateY;
75 |
76 | private float mHalfBaseRectWidth;
77 | private float mHalfBaseRectHeight;
78 |
79 | private RectF mWidgetRect = new RectF();
80 | private RectF mBaseRect = new RectF();
81 | private RectF mImgRect = new RectF();
82 | private RectF mTmpRect = new RectF();
83 | private RectF mCommonRect = new RectF();
84 |
85 | private PointF mScreenCenter = new PointF();
86 | private PointF mScaleCenter = new PointF();
87 | private PointF mRotateCenter = new PointF();
88 |
89 | private Transform mTranslate = new Transform();
90 |
91 |
92 | private RectF mClip;
93 | private Info mFromInfo;
94 | private long mInfoTime;
95 | private Runnable mCompleteCallBack;
96 |
97 | private OnLongClickListener mLongClick;
98 |
99 | public float getScale(){
100 | return mScale;
101 | }
102 |
103 |
104 | public PhotoView(Context context) {
105 | super(context);
106 | init();
107 | }
108 |
109 | public PhotoView(Context context, AttributeSet attrs) {
110 | super(context, attrs);
111 | init();
112 | }
113 |
114 | public PhotoView(Context context, AttributeSet attrs, int defStyleAttr) {
115 | super(context, attrs, defStyleAttr);
116 | init();
117 | }
118 |
119 | private void init() {
120 | super.setScaleType(ScaleType.MATRIX);
121 | if (mScaleType == null) mScaleType = ScaleType.CENTER_INSIDE;
122 | mRotateDetector = new RotateGestureDetector(mRotateListener);
123 | mDetector = new GestureDetector(getContext(), mGestureListener);
124 | mScaleDetector = new ScaleGestureDetector(getContext(), mScaleListener);
125 | float density = getResources().getDisplayMetrics().density;
126 | MAX_OVER_SCROLL = (int) (density * 30);
127 | MAX_FLING_OVER_SCROLL = (int) (density * 30);
128 | MAX_OVER_RESISTANCE = (int) (density * 140);
129 |
130 | mMinRotate = MIN_ROTATE;
131 | mAnimaDuring = ANIMA_DURING;
132 | mMaxScale = MAX_SCALE;
133 | }
134 |
135 | /**
136 | * 获取默认的动画持续时间
137 | */
138 | public int getDefaultAnimaDuring() {
139 | return ANIMA_DURING;
140 | }
141 |
142 | @Override
143 | public void setOnClickListener(OnClickListener l) {
144 | super.setOnClickListener(l);
145 | mClickListener = l;
146 | }
147 |
148 | @Override
149 | public void setScaleType(ScaleType scaleType) {
150 | if (scaleType == ScaleType.MATRIX) return;
151 |
152 | if (scaleType != mScaleType) {
153 | mScaleType = scaleType;
154 |
155 | if (isInit) {
156 | initBase();
157 | }
158 | }
159 | }
160 |
161 | @Override
162 | public void setOnLongClickListener(OnLongClickListener l) {
163 | mLongClick = l;
164 | }
165 |
166 | /**
167 | * 设置动画的插入器
168 | */
169 | public void setInterpolator(Interpolator interpolator) {
170 | mTranslate.setInterpolator(interpolator);
171 | }
172 |
173 | /**
174 | * 获取动画持续时间
175 | */
176 | public int getAnimaDuring() {
177 | return mAnimaDuring;
178 | }
179 |
180 | /**
181 | * 设置动画的持续时间
182 | */
183 | public void setAnimaDuring(int during) {
184 | mAnimaDuring = during;
185 | }
186 |
187 | /**
188 | * 设置最大可以缩放的倍数
189 | */
190 | public void setMaxScale(float maxScale) {
191 | mMaxScale = maxScale;
192 | }
193 |
194 | /**
195 | * 获取最大可以缩放的倍数
196 | */
197 | public float getMaxScale() {
198 | return mMaxScale;
199 | }
200 |
201 | /**
202 | * 启用缩放功能
203 | */
204 | public void enable() {
205 | isEnable = true;
206 | }
207 |
208 | /**
209 | * 禁用缩放功能
210 | */
211 | public void disenable() {
212 | isEnable = false;
213 | }
214 |
215 | /**
216 | * 启用旋转功能
217 | */
218 | public void enableRotate() {
219 | isRotateEnable = true;
220 | }
221 |
222 | /**
223 | * 禁用旋转功能
224 | */
225 | public void disableRotate() {
226 | isRotateEnable = false;
227 | }
228 |
229 | /**
230 | */
231 |
232 | /**
233 | * 自己加的滑动监听
234 | */
235 |
236 | //只要一移动就有的监听
237 | private OnMatrixChangedListener mMatrixChangeListener;
238 | private final RectF mDisplayRect = new RectF();
239 | public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
240 | mMatrixChangeListener = listener;
241 | }
242 |
243 | /**************************************/
244 |
245 |
246 |
247 | public void setMaxAnimFromWaiteTime(int wait) {
248 | MAX_ANIM_FROM_WAITE = wait;
249 | }
250 |
251 | @Override
252 | public void setImageResource(int resId) {
253 | Drawable drawable = null;
254 | try {
255 | drawable = getResources().getDrawable(resId);
256 | } catch (Exception e) {
257 | }
258 |
259 | setImageDrawable(drawable);
260 | }
261 |
262 | @Override
263 | public void setImageDrawable(Drawable drawable) {
264 | super.setImageDrawable(drawable);
265 |
266 | if (drawable == null) {
267 | hasDrawable = false;
268 | return;
269 | }
270 |
271 | if (!hasSize(drawable))
272 | return;
273 |
274 | if (!hasDrawable) {
275 | hasDrawable = true;
276 | }
277 |
278 | initBase();
279 | }
280 |
281 | private boolean hasSize(Drawable d) {
282 | if ((d.getIntrinsicHeight() <= 0 || d.getIntrinsicWidth() <= 0)
283 | && (d.getMinimumWidth() <= 0 || d.getMinimumHeight() <= 0)
284 | && (d.getBounds().width() <= 0 || d.getBounds().height() <= 0)) {
285 | return false;
286 | }
287 | return true;
288 | }
289 |
290 | private static int getDrawableWidth(Drawable d) {
291 | int width = d.getIntrinsicWidth();
292 | if (width <= 0) width = d.getMinimumWidth();
293 | if (width <= 0) width = d.getBounds().width();
294 | return width;
295 | }
296 |
297 | private static int getDrawableHeight(Drawable d) {
298 | int height = d.getIntrinsicHeight();
299 | if (height <= 0) height = d.getMinimumHeight();
300 | if (height <= 0) height = d.getBounds().height();
301 | return height;
302 | }
303 |
304 | private void initBase() {
305 | if (!hasDrawable) return;
306 | if (!isKnowSize) return;
307 |
308 | mBaseMatrix.reset();
309 | mAnimaMatrix.reset();
310 |
311 | isZoonUp = false;
312 |
313 | Drawable img = getDrawable();
314 |
315 | int w = getWidth();
316 | int h = getHeight();
317 | int imgw = getDrawableWidth(img);
318 | int imgh = getDrawableHeight(img);
319 |
320 | mBaseRect.set(0, 0, imgw, imgh);
321 |
322 | // 以图片中心点居中位移
323 | int tx = (w - imgw) / 2;
324 | int ty = (h - imgh) / 2;
325 |
326 | float sx = 1;
327 | float sy = 1;
328 |
329 | // 缩放,默认不超过屏幕大小
330 | if (imgw > w) {
331 | sx = (float) w / imgw;
332 | }
333 |
334 | if (imgh > h) {
335 | sy = (float) h / imgh;
336 | }
337 |
338 | float scale = sx < sy ? sx : sy;
339 |
340 | mBaseMatrix.reset();
341 | mBaseMatrix.postTranslate(tx, ty);
342 | mBaseMatrix.postScale(scale, scale, mScreenCenter.x, mScreenCenter.y);
343 | mBaseMatrix.mapRect(mBaseRect);
344 |
345 | mHalfBaseRectWidth = mBaseRect.width() / 2;
346 | mHalfBaseRectHeight = mBaseRect.height() / 2;
347 |
348 | mScaleCenter.set(mScreenCenter);
349 | mRotateCenter.set(mScaleCenter);
350 |
351 | executeTranslate();
352 |
353 | switch (mScaleType) {
354 | case CENTER:
355 | initCenter();
356 | break;
357 | case CENTER_CROP:
358 | initCenterCrop();
359 | break;
360 | case CENTER_INSIDE:
361 | initCenterInside();
362 | break;
363 | case FIT_CENTER:
364 | initFitCenter();
365 | break;
366 | case FIT_START:
367 | initFitStart();
368 | break;
369 | case FIT_END:
370 | initFitEnd();
371 | break;
372 | case FIT_XY:
373 | initFitXY();
374 | break;
375 | }
376 |
377 | isInit = true;
378 |
379 | if (mFromInfo != null && System.currentTimeMillis() - mInfoTime < MAX_ANIM_FROM_WAITE) {
380 | animaFrom(mFromInfo);
381 | }
382 |
383 | mFromInfo = null;
384 | }
385 |
386 | private void initCenter() {
387 | if (!hasDrawable) return;
388 | if (!isKnowSize) return;
389 |
390 | Drawable img = getDrawable();
391 |
392 | int imgw = getDrawableWidth(img);
393 | int imgh = getDrawableHeight(img);
394 |
395 | if (imgw > mWidgetRect.width() || imgh > mWidgetRect.height()) {
396 | float scaleX = imgw / mImgRect.width();
397 | float scaleY = imgh / mImgRect.height();
398 |
399 | mScale = scaleX > scaleY ? scaleX : scaleY;
400 |
401 | mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
402 |
403 | executeTranslate();
404 |
405 | resetBase();
406 | }
407 | }
408 |
409 | private void initCenterCrop() {
410 | if (mImgRect.width() < mWidgetRect.width() || mImgRect.height() < mWidgetRect.height()) {
411 | float scaleX = mWidgetRect.width() / mImgRect.width();
412 | float scaleY = mWidgetRect.height() / mImgRect.height();
413 |
414 | mScale = scaleX > scaleY ? scaleX : scaleY;
415 |
416 | mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
417 |
418 | executeTranslate();
419 | resetBase();
420 | }
421 | }
422 |
423 | private void initCenterInside() {
424 | if (mImgRect.width() > mWidgetRect.width() || mImgRect.height() > mWidgetRect.height()) {
425 | float scaleX = mWidgetRect.width() / mImgRect.width();
426 | float scaleY = mWidgetRect.height() / mImgRect.height();
427 |
428 | mScale = scaleX < scaleY ? scaleX : scaleY;
429 |
430 | mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
431 |
432 | executeTranslate();
433 | resetBase();
434 | }
435 | }
436 |
437 | private void initFitCenter() {
438 | if (mImgRect.width() < mWidgetRect.width()) {
439 | mScale = mWidgetRect.width() / mImgRect.width();
440 |
441 | mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
442 |
443 | executeTranslate();
444 | resetBase();
445 | }
446 | }
447 |
448 | private void initFitStart() {
449 | initFitCenter();
450 |
451 | float ty = -mImgRect.top;
452 | mAnimaMatrix.postTranslate(0, ty);
453 | executeTranslate();
454 | resetBase();
455 | mTranslateY += ty;
456 | }
457 |
458 | private void initFitEnd() {
459 | initFitCenter();
460 |
461 | float ty = (mWidgetRect.bottom - mImgRect.bottom);
462 | mTranslateY += ty;
463 | mAnimaMatrix.postTranslate(0, ty);
464 | executeTranslate();
465 | resetBase();
466 | }
467 |
468 | private void initFitXY() {
469 | float scaleX = mWidgetRect.width() / mImgRect.width();
470 | float scaleY = mWidgetRect.height() / mImgRect.height();
471 |
472 | mAnimaMatrix.postScale(scaleX, scaleY, mScreenCenter.x, mScreenCenter.y);
473 |
474 | executeTranslate();
475 | resetBase();
476 | }
477 |
478 | private void resetBase() {
479 | Drawable img = getDrawable();
480 | int imgw = getDrawableWidth(img);
481 | int imgh = getDrawableHeight(img);
482 | mBaseRect.set(0, 0, imgw, imgh);
483 | mBaseMatrix.set(mSynthesisMatrix);
484 | mBaseMatrix.mapRect(mBaseRect);
485 | mHalfBaseRectWidth = mBaseRect.width() / 2;
486 | mHalfBaseRectHeight = mBaseRect.height() / 2;
487 | mScale = 1;
488 | mTranslateX = 0;
489 | mTranslateY = 0;
490 | mAnimaMatrix.reset();
491 | }
492 |
493 |
494 |
495 |
496 |
497 | private void executeTranslate() {
498 | mSynthesisMatrix.set(mBaseMatrix);
499 | mSynthesisMatrix.postConcat(mAnimaMatrix);
500 | setImageMatrix(mSynthesisMatrix);
501 | if (mMatrixChangeListener != null) {
502 | RectF displayRect = getDisplayRect(mSynthesisMatrix);
503 | if (displayRect != null) {
504 | mMatrixChangeListener.onMatrixChanged(displayRect);
505 | }
506 | }
507 | mAnimaMatrix.mapRect(mImgRect, mBaseRect);
508 | imgLargeWidth = mImgRect.width() > mWidgetRect.width();
509 | imgLargeHeight = mImgRect.height() > mWidgetRect.height();
510 | }
511 |
512 |
513 | private RectF getDisplayRect(Matrix matrix) {
514 | Drawable d = getDrawable();
515 | if (d != null) {
516 | mDisplayRect.set(0, 0, d.getIntrinsicWidth(),
517 | d.getIntrinsicHeight());
518 | matrix.mapRect(mDisplayRect);
519 | return mDisplayRect;
520 | }
521 | return null;
522 | }
523 |
524 |
525 |
526 | @Override
527 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
528 | if (!hasDrawable) {
529 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
530 | return;
531 | }
532 |
533 | Drawable d = getDrawable();
534 | int drawableW = getDrawableWidth(d);
535 | int drawableH = getDrawableHeight(d);
536 |
537 | int pWidth = MeasureSpec.getSize(widthMeasureSpec);
538 | int pHeight = MeasureSpec.getSize(heightMeasureSpec);
539 |
540 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
541 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
542 |
543 | int width = 0;
544 | int height = 0;
545 |
546 | ViewGroup.LayoutParams p = getLayoutParams();
547 |
548 | if (p == null) {
549 | p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
550 | }
551 |
552 | if (p.width == ViewGroup.LayoutParams.MATCH_PARENT) {
553 | if (widthMode == MeasureSpec.UNSPECIFIED) {
554 | width = drawableW;
555 | } else {
556 | width = pWidth;
557 | }
558 | } else {
559 | if (widthMode == MeasureSpec.EXACTLY) {
560 | width = pWidth;
561 | } else if (widthMode == MeasureSpec.AT_MOST) {
562 | width = drawableW > pWidth ? pWidth : drawableW;
563 | } else {
564 | width = drawableW;
565 | }
566 | }
567 |
568 | if (p.height == ViewGroup.LayoutParams.MATCH_PARENT) {
569 | if (heightMode == MeasureSpec.UNSPECIFIED) {
570 | height = drawableH;
571 | } else {
572 | height = pHeight;
573 | }
574 | } else {
575 | if (heightMode == MeasureSpec.EXACTLY) {
576 | height = pHeight;
577 | } else if (heightMode == MeasureSpec.AT_MOST) {
578 | height = drawableH > pHeight ? pHeight : drawableH;
579 | } else {
580 | height = drawableH;
581 | }
582 | }
583 |
584 | if (mAdjustViewBounds && (float) drawableW / drawableH != (float) width / height) {
585 |
586 | float hScale = (float) height / drawableH;
587 | float wScale = (float) width / drawableW;
588 |
589 | float scale = hScale < wScale ? hScale : wScale;
590 | width = p.width == ViewGroup.LayoutParams.MATCH_PARENT ? width : (int) (drawableW * scale);
591 | height = p.height == ViewGroup.LayoutParams.MATCH_PARENT ? height : (int) (drawableH * scale);
592 | }
593 |
594 | setMeasuredDimension(width, height);
595 | }
596 |
597 | @Override
598 | public void setAdjustViewBounds(boolean adjustViewBounds) {
599 | super.setAdjustViewBounds(adjustViewBounds);
600 | mAdjustViewBounds = adjustViewBounds;
601 | }
602 |
603 | @Override
604 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
605 | super.onSizeChanged(w, h, oldw, oldh);
606 |
607 | mWidgetRect.set(0, 0, w, h);
608 | mScreenCenter.set(w / 2, h / 2);
609 |
610 | if (!isKnowSize) {
611 | isKnowSize = true;
612 | initBase();
613 | }
614 | }
615 |
616 | @Override
617 | public void draw(Canvas canvas) {
618 | if (mClip != null) {
619 | canvas.clipRect(mClip);
620 | mClip = null;
621 | }
622 | super.draw(canvas);
623 | }
624 |
625 | @Override
626 | public boolean dispatchTouchEvent(MotionEvent event) {
627 | if (isEnable) {
628 | final int Action = event.getActionMasked();
629 | if (event.getPointerCount() >= 2) hasMultiTouch = true;
630 |
631 | mDetector.onTouchEvent(event);
632 | if (isRotateEnable) {
633 | mRotateDetector.onTouchEvent(event);
634 | }
635 | mScaleDetector.onTouchEvent(event);
636 |
637 | if (Action == MotionEvent.ACTION_UP || Action == MotionEvent.ACTION_CANCEL) onUp();
638 |
639 | return true;
640 | } else {
641 | return super.dispatchTouchEvent(event);
642 | }
643 | }
644 |
645 | private void onUp() {
646 | if (mTranslate.isRuning) return;
647 |
648 | if (canRotate || mDegrees % 90 != 0) {
649 | float toDegrees = (int) (mDegrees / 90) * 90;
650 | float remainder = mDegrees % 90;
651 |
652 | if (remainder > 45)
653 | toDegrees += 90;
654 | else if (remainder < -45)
655 | toDegrees -= 90;
656 |
657 | mTranslate.withRotate((int) mDegrees, (int) toDegrees);
658 |
659 | mDegrees = toDegrees;
660 | }
661 |
662 | float scale = mScale;
663 |
664 | if (mScale < 1) {
665 | scale = 1;
666 | mTranslate.withScale(mScale, 1);
667 | } else if (mScale > mMaxScale) {
668 | scale = mMaxScale;
669 | mTranslate.withScale(mScale, mMaxScale);
670 | }
671 |
672 | float cx = mImgRect.left + mImgRect.width() / 2;
673 | float cy = mImgRect.top + mImgRect.height() / 2;
674 |
675 | mScaleCenter.set(cx, cy);
676 | mRotateCenter.set(cx, cy);
677 |
678 | mTranslateX = 0;
679 | mTranslateY = 0;
680 |
681 | mTmpMatrix.reset();
682 | mTmpMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
683 | mTmpMatrix.postTranslate(cx - mHalfBaseRectWidth, cy - mHalfBaseRectHeight);
684 | mTmpMatrix.postScale(scale, scale, cx, cy);
685 | mTmpMatrix.postRotate(mDegrees, cx, cy);
686 | mTmpMatrix.mapRect(mTmpRect, mBaseRect);
687 |
688 | doTranslateReset(mTmpRect);
689 | mTranslate.start();
690 | }
691 |
692 | private void doTranslateReset(RectF imgRect) {
693 | int tx = 0;
694 | int ty = 0;
695 |
696 | if (imgRect.width() <= mWidgetRect.width()) {
697 | if (!isImageCenterWidth(imgRect))
698 | tx = -(int) ((mWidgetRect.width() - imgRect.width()) / 2 - imgRect.left);
699 | } else {
700 | if (imgRect.left > mWidgetRect.left) {
701 | tx = (int) (imgRect.left - mWidgetRect.left);
702 | } else if (imgRect.right < mWidgetRect.right) {
703 | tx = (int) (imgRect.right - mWidgetRect.right);
704 | }
705 | }
706 |
707 | if (imgRect.height() <= mWidgetRect.height()) {
708 | if (!isImageCenterHeight(imgRect))
709 | ty = -(int) ((mWidgetRect.height() - imgRect.height()) / 2 - imgRect.top);
710 | } else {
711 | if (imgRect.top > mWidgetRect.top) {
712 | ty = (int) (imgRect.top - mWidgetRect.top);
713 | } else if (imgRect.bottom < mWidgetRect.bottom) {
714 | ty = (int) (imgRect.bottom - mWidgetRect.bottom);
715 | }
716 | }
717 |
718 | if (tx != 0 || ty != 0) {
719 | if (!mTranslate.mFlingScroller.isFinished()) mTranslate.mFlingScroller.abortAnimation();
720 | mTranslate.withTranslate(mTranslateX, mTranslateY, -tx, -ty);
721 | }
722 | }
723 |
724 | private boolean isImageCenterHeight(RectF rect) {
725 | return Math.abs(Math.round(rect.top) - (mWidgetRect.height() - rect.height()) / 2) < 1;
726 | }
727 |
728 | private boolean isImageCenterWidth(RectF rect) {
729 | return Math.abs(Math.round(rect.left) - (mWidgetRect.width() - rect.width()) / 2) < 1;
730 | }
731 |
732 | private OnRotateListener mRotateListener = new OnRotateListener() {
733 |
734 | @Override
735 | public void onRotate(float degrees, float focusX, float focusY) {
736 | mRotateFlag += degrees;
737 | if (canRotate) {
738 | mDegrees += degrees;
739 | mAnimaMatrix.postRotate(degrees, focusX, focusY);
740 | } else {
741 | if (Math.abs(mRotateFlag) >= mMinRotate) {
742 | canRotate = true;
743 | mRotateFlag = 0;
744 | }
745 | }
746 | }
747 | };
748 |
749 | private ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
750 | @Override
751 | public boolean onScale(ScaleGestureDetector detector) {
752 | float scaleFactor = detector.getScaleFactor();
753 |
754 | if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
755 | return false;
756 |
757 | mScale *= scaleFactor;
758 | // mScaleCenter.set(detector.getFocusX(), detector.getFocusY());
759 | Log.e("这东西一直在变的事吧",mScale+"");
760 | mAnimaMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
761 | executeTranslate();
762 | return true;
763 | }
764 |
765 | public boolean onScaleBegin(ScaleGestureDetector detector) {
766 | return true;
767 | }
768 |
769 | public void onScaleEnd(ScaleGestureDetector detector) {
770 |
771 | }
772 | };
773 |
774 | private float resistanceScrollByX(float overScroll, float detalX) {
775 | float s = detalX * (Math.abs(Math.abs(overScroll) - MAX_OVER_RESISTANCE) / (float) MAX_OVER_RESISTANCE);
776 | return s;
777 | }
778 |
779 | private float resistanceScrollByY(float overScroll, float detalY) {
780 | float s = detalY * (Math.abs(Math.abs(overScroll) - MAX_OVER_RESISTANCE) / (float) MAX_OVER_RESISTANCE);
781 | return s;
782 | }
783 |
784 | /**
785 | * 匹配两个Rect的共同部分输出到out,若无共同部分则输出0,0,0,0
786 | */
787 | private void mapRect(RectF r1, RectF r2, RectF out) {
788 |
789 | float l, r, t, b;
790 |
791 | l = r1.left > r2.left ? r1.left : r2.left;
792 | r = r1.right < r2.right ? r1.right : r2.right;
793 |
794 | if (l > r) {
795 | out.set(0, 0, 0, 0);
796 | return;
797 | }
798 |
799 | t = r1.top > r2.top ? r1.top : r2.top;
800 | b = r1.bottom < r2.bottom ? r1.bottom : r2.bottom;
801 |
802 | if (t > b) {
803 | out.set(0, 0, 0, 0);
804 | return;
805 | }
806 |
807 | out.set(l, t, r, b);
808 | }
809 |
810 | private void checkRect() {
811 | if (!hasOverTranslate) {
812 | mapRect(mWidgetRect, mImgRect, mCommonRect);
813 | }
814 | }
815 |
816 | private Runnable mClickRunnable = new Runnable() {
817 | @Override
818 | public void run() {
819 | if (mClickListener != null) {
820 | mClickListener.onClick(PhotoView.this);
821 | }
822 | }
823 | };
824 |
825 | private GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
826 |
827 | @Override
828 | public void onLongPress(MotionEvent e) {
829 | if (mLongClick != null) {
830 | mLongClick.onLongClick(PhotoView.this);
831 | }
832 | }
833 |
834 | @Override
835 | public boolean onDown(MotionEvent e) {
836 | hasOverTranslate = false;
837 | hasMultiTouch = false;
838 | canRotate = false;
839 | removeCallbacks(mClickRunnable);
840 | return false;
841 | }
842 |
843 | @Override
844 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
845 | Log.e("photoView111111111","++++++++++++++++++++++Fling");
846 |
847 | if (hasMultiTouch) return false;
848 | if (!imgLargeWidth && !imgLargeHeight) return false;
849 | if (mTranslate.isRuning) return false;
850 |
851 | float vx = velocityX;
852 | float vy = velocityY;
853 |
854 | if (Math.round(mImgRect.left) >= mWidgetRect.left || Math.round(mImgRect.right) <= mWidgetRect.right) {
855 | vx = 0;
856 | }
857 |
858 | if (Math.round(mImgRect.top) >= mWidgetRect.top || Math.round(mImgRect.bottom) <= mWidgetRect.bottom) {
859 | vy = 0;
860 | }
861 |
862 | if (canRotate || mDegrees % 90 != 0) {
863 | float toDegrees = (int) (mDegrees / 90) * 90;
864 | float remainder = mDegrees % 90;
865 |
866 | if (remainder > 45)
867 | toDegrees += 90;
868 | else if (remainder < -45)
869 | toDegrees -= 90;
870 |
871 | mTranslate.withRotate((int) mDegrees, (int) toDegrees);
872 |
873 | mDegrees = toDegrees;
874 | }
875 |
876 | doTranslateReset(mImgRect);
877 |
878 | mTranslate.withFling(vx, vy);
879 |
880 | mTranslate.start();
881 | // onUp(e2);
882 | return super.onFling(e1, e2, velocityX, velocityY);
883 | }
884 |
885 | @Override
886 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
887 | Log.e("photoView111111111","=======================onScroll");
888 |
889 | if (mTranslate.isRuning) {
890 | mTranslate.stop();
891 | }
892 |
893 | if (canScrollHorizontallySelf(distanceX)) {
894 | if (distanceX < 0 && mImgRect.left - distanceX > mWidgetRect.left)
895 | distanceX = mImgRect.left;
896 | if (distanceX > 0 && mImgRect.right - distanceX < mWidgetRect.right)
897 | distanceX = mImgRect.right - mWidgetRect.right;
898 |
899 | mAnimaMatrix.postTranslate(-distanceX, 0);
900 | mTranslateX -= distanceX;
901 | } else if (imgLargeWidth || hasMultiTouch || hasOverTranslate) {
902 | checkRect();
903 | if (!hasMultiTouch) {
904 | if (distanceX < 0 && mImgRect.left - distanceX > mCommonRect.left)
905 | distanceX = resistanceScrollByX(mImgRect.left - mCommonRect.left, distanceX);
906 | if (distanceX > 0 && mImgRect.right - distanceX < mCommonRect.right)
907 | distanceX = resistanceScrollByX(mImgRect.right - mCommonRect.right, distanceX);
908 | }
909 |
910 | mTranslateX -= distanceX;
911 | mAnimaMatrix.postTranslate(-distanceX, 0);
912 | hasOverTranslate = true;
913 | }
914 |
915 | if (canScrollVerticallySelf(distanceY)) {
916 | if (distanceY < 0 && mImgRect.top - distanceY > mWidgetRect.top)
917 | distanceY = mImgRect.top;
918 | if (distanceY > 0 && mImgRect.bottom - distanceY < mWidgetRect.bottom)
919 | distanceY = mImgRect.bottom - mWidgetRect.bottom;
920 |
921 | mAnimaMatrix.postTranslate(0, -distanceY);
922 | mTranslateY -= distanceY;
923 | } else if (imgLargeHeight || hasOverTranslate || hasMultiTouch) {
924 | checkRect();
925 | if (!hasMultiTouch) {
926 | if (distanceY < 0 && mImgRect.top - distanceY > mCommonRect.top)
927 | distanceY = resistanceScrollByY(mImgRect.top - mCommonRect.top, distanceY);
928 | if (distanceY > 0 && mImgRect.bottom - distanceY < mCommonRect.bottom)
929 | distanceY = resistanceScrollByY(mImgRect.bottom - mCommonRect.bottom, distanceY);
930 | }
931 |
932 | mAnimaMatrix.postTranslate(0, -distanceY);
933 | mTranslateY -= distanceY;
934 | hasOverTranslate = true;
935 | }
936 |
937 | executeTranslate();
938 | return true;
939 | }
940 |
941 | @Override
942 | public boolean onSingleTapUp(MotionEvent e) {
943 | postDelayed(mClickRunnable, 250);
944 | return false;
945 | }
946 |
947 | @Override
948 | public boolean onDoubleTap(MotionEvent e) {
949 |
950 | /**
951 | * 原双击
952 | * 放大图片,取消掉
953 | * */
954 |
955 | // mTranslate.stop();
956 | //
957 | // float from = 1;
958 | // float to = 1;
959 | //
960 | // float imgcx = mImgRect.left + mImgRect.width() / 2;
961 | // float imgcy = mImgRect.top + mImgRect.height() / 2;
962 | //
963 | // mScaleCenter.set(imgcx, imgcy);
964 | // mRotateCenter.set(imgcx, imgcy);
965 | // mTranslateX = 0;
966 | // mTranslateY = 0;
967 | //
968 | // if (isZoonUp) {
969 | // from = mScale;
970 | // to = 1;
971 | // } else {
972 | // from = mScale;
973 | // to = mMaxScale;
974 | //
975 | // mScaleCenter.set(e.getX(), e.getY());
976 | // }
977 | //
978 | // mTmpMatrix.reset();
979 | // mTmpMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
980 | // mTmpMatrix.postTranslate(mRotateCenter.x, mRotateCenter.y);
981 | // mTmpMatrix.postTranslate(-mHalfBaseRectWidth, -mHalfBaseRectHeight);
982 | // mTmpMatrix.postRotate(mDegrees, mRotateCenter.x, mRotateCenter.y);
983 | // mTmpMatrix.postScale(to, to, mScaleCenter.x, mScaleCenter.y);
984 | // mTmpMatrix.postTranslate(mTranslateX, mTranslateY);
985 | // mTmpMatrix.mapRect(mTmpRect, mBaseRect);
986 | // doTranslateReset(mTmpRect);
987 | //
988 | // isZoonUp = !isZoonUp;
989 | // mTranslate.withScale(from, to);
990 | // mTranslate.start();
991 |
992 | return false;
993 | }
994 | };
995 |
996 | public boolean canScrollHorizontallySelf(float direction) {
997 | if (mImgRect.width() <= mWidgetRect.width()) return false;
998 | if (direction < 0 && Math.round(mImgRect.left) - direction >= mWidgetRect.left)
999 | return false;
1000 | if (direction > 0 && Math.round(mImgRect.right) - direction <= mWidgetRect.right)
1001 | return false;
1002 | return true;
1003 | }
1004 |
1005 | public boolean canScrollVerticallySelf(float direction) {
1006 | if (mImgRect.height() <= mWidgetRect.height()) return false;
1007 | if (direction < 0 && Math.round(mImgRect.top) - direction >= mWidgetRect.top)
1008 | return false;
1009 | if (direction > 0 && Math.round(mImgRect.bottom) - direction <= mWidgetRect.bottom)
1010 | return false;
1011 | return true;
1012 | }
1013 |
1014 | @Override
1015 | public boolean canScrollHorizontally(int direction) {
1016 | if (hasMultiTouch) return true;
1017 | return canScrollHorizontallySelf(direction);
1018 | }
1019 |
1020 | @Override
1021 | public boolean canScrollVertically(int direction) {
1022 | if (hasMultiTouch) return true;
1023 | return canScrollVerticallySelf(direction);
1024 | }
1025 |
1026 | private class InterpolatorProxy implements Interpolator {
1027 |
1028 | private Interpolator mTarget;
1029 |
1030 | private InterpolatorProxy() {
1031 | mTarget = new DecelerateInterpolator();
1032 | }
1033 |
1034 | public void setTargetInterpolator(Interpolator interpolator) {
1035 | mTarget = interpolator;
1036 | }
1037 |
1038 | @Override
1039 | public float getInterpolation(float input) {
1040 | if (mTarget != null) {
1041 | return mTarget.getInterpolation(input);
1042 | }
1043 | return input;
1044 | }
1045 | }
1046 |
1047 | private class Transform implements Runnable {
1048 |
1049 | boolean isRuning;
1050 |
1051 | OverScroller mTranslateScroller;
1052 | OverScroller mFlingScroller;
1053 | Scroller mScaleScroller;
1054 | Scroller mClipScroller;
1055 | Scroller mRotateScroller;
1056 |
1057 | ClipCalculate C;
1058 |
1059 | int mLastFlingX;
1060 | int mLastFlingY;
1061 |
1062 | int mLastTranslateX;
1063 | int mLastTranslateY;
1064 |
1065 | RectF mClipRect = new RectF();
1066 |
1067 | InterpolatorProxy mInterpolatorProxy = new InterpolatorProxy();
1068 |
1069 | Transform() {
1070 | Context ctx = getContext();
1071 | mTranslateScroller = new OverScroller(ctx, mInterpolatorProxy);
1072 | mScaleScroller = new Scroller(ctx, mInterpolatorProxy);
1073 | mFlingScroller = new OverScroller(ctx, mInterpolatorProxy);
1074 | mClipScroller = new Scroller(ctx, mInterpolatorProxy);
1075 | mRotateScroller = new Scroller(ctx, mInterpolatorProxy);
1076 | }
1077 |
1078 | public void setInterpolator(Interpolator interpolator) {
1079 | mInterpolatorProxy.setTargetInterpolator(interpolator);
1080 | }
1081 |
1082 | void withTranslate(int startX, int startY, int deltaX, int deltaY) {
1083 | mLastTranslateX = 0;
1084 | mLastTranslateY = 0;
1085 | mTranslateScroller.startScroll(0, 0, deltaX, deltaY, mAnimaDuring);
1086 | }
1087 |
1088 | void withScale(float form, float to) {
1089 | mScaleScroller.startScroll((int) (form * 10000), 0, (int) ((to - form) * 10000), 0, mAnimaDuring);
1090 | }
1091 |
1092 | void withClip(float fromX, float fromY, float deltaX, float deltaY, int d, ClipCalculate c) {
1093 | mClipScroller.startScroll((int) (fromX * 10000), (int) (fromY * 10000), (int) (deltaX * 10000), (int) (deltaY * 10000), d);
1094 | C = c;
1095 | }
1096 |
1097 | void withRotate(int fromDegrees, int toDegrees) {
1098 | mRotateScroller.startScroll(fromDegrees, 0, toDegrees - fromDegrees, 0, mAnimaDuring);
1099 | }
1100 |
1101 | void withRotate(int fromDegrees, int toDegrees, int during) {
1102 | mRotateScroller.startScroll(fromDegrees, 0, toDegrees - fromDegrees, 0, during);
1103 | }
1104 |
1105 | void withFling(float velocityX, float velocityY) {
1106 | mLastFlingX = velocityX < 0 ? Integer.MAX_VALUE : 0;
1107 | int distanceX = (int) (velocityX > 0 ? Math.abs(mImgRect.left) : mImgRect.right - mWidgetRect.right);
1108 | distanceX = velocityX < 0 ? Integer.MAX_VALUE - distanceX : distanceX;
1109 | int minX = velocityX < 0 ? distanceX : 0;
1110 | int maxX = velocityX < 0 ? Integer.MAX_VALUE : distanceX;
1111 | int overX = velocityX < 0 ? Integer.MAX_VALUE - minX : distanceX;
1112 |
1113 | mLastFlingY = velocityY < 0 ? Integer.MAX_VALUE : 0;
1114 | int distanceY = (int) (velocityY > 0 ? Math.abs(mImgRect.top) : mImgRect.bottom - mWidgetRect.bottom);
1115 | distanceY = velocityY < 0 ? Integer.MAX_VALUE - distanceY : distanceY;
1116 | int minY = velocityY < 0 ? distanceY : 0;
1117 | int maxY = velocityY < 0 ? Integer.MAX_VALUE : distanceY;
1118 | int overY = velocityY < 0 ? Integer.MAX_VALUE - minY : distanceY;
1119 |
1120 | if (velocityX == 0) {
1121 | maxX = 0;
1122 | minX = 0;
1123 | }
1124 |
1125 | if (velocityY == 0) {
1126 | maxY = 0;
1127 | minY = 0;
1128 | }
1129 |
1130 | mFlingScroller.fling(mLastFlingX, mLastFlingY, (int) velocityX, (int) velocityY, minX, maxX, minY, maxY, Math.abs(overX) < MAX_FLING_OVER_SCROLL * 2 ? 0 : MAX_FLING_OVER_SCROLL, Math.abs(overY) < MAX_FLING_OVER_SCROLL * 2 ? 0 : MAX_FLING_OVER_SCROLL);
1131 | }
1132 |
1133 | void start() {
1134 | isRuning = true;
1135 | postExecute();
1136 | }
1137 |
1138 | void stop() {
1139 | removeCallbacks(this);
1140 | mTranslateScroller.abortAnimation();
1141 | mScaleScroller.abortAnimation();
1142 | mFlingScroller.abortAnimation();
1143 | mRotateScroller.abortAnimation();
1144 | isRuning = false;
1145 | }
1146 |
1147 | @Override
1148 | public void run() {
1149 |
1150 | // if (!isRuning) return;
1151 |
1152 | boolean endAnima = true;
1153 |
1154 | if (mScaleScroller.computeScrollOffset()) {
1155 | mScale = mScaleScroller.getCurrX() / 10000f;
1156 | endAnima = false;
1157 | }
1158 |
1159 | if (mTranslateScroller.computeScrollOffset()) {
1160 | int tx = mTranslateScroller.getCurrX() - mLastTranslateX;
1161 | int ty = mTranslateScroller.getCurrY() - mLastTranslateY;
1162 | mTranslateX += tx;
1163 | mTranslateY += ty;
1164 | mLastTranslateX = mTranslateScroller.getCurrX();
1165 | mLastTranslateY = mTranslateScroller.getCurrY();
1166 | endAnima = false;
1167 | }
1168 |
1169 | if (mFlingScroller.computeScrollOffset()) {
1170 | int x = mFlingScroller.getCurrX() - mLastFlingX;
1171 | int y = mFlingScroller.getCurrY() - mLastFlingY;
1172 |
1173 | mLastFlingX = mFlingScroller.getCurrX();
1174 | mLastFlingY = mFlingScroller.getCurrY();
1175 |
1176 | mTranslateX += x;
1177 | mTranslateY += y;
1178 | endAnima = false;
1179 | }
1180 |
1181 | if (mRotateScroller.computeScrollOffset()) {
1182 | mDegrees = mRotateScroller.getCurrX();
1183 | endAnima = false;
1184 | }
1185 |
1186 | if (mClipScroller.computeScrollOffset() || mClip != null) {
1187 | float sx = mClipScroller.getCurrX() / 10000f;
1188 | float sy = mClipScroller.getCurrY() / 10000f;
1189 | mTmpMatrix.setScale(sx, sy, (mImgRect.left + mImgRect.right) / 2, C.calculateTop());
1190 | mTmpMatrix.mapRect(mClipRect, mImgRect);
1191 |
1192 | if (sx == 1) {
1193 | mClipRect.left = mWidgetRect.left;
1194 | mClipRect.right = mWidgetRect.right;
1195 | }
1196 |
1197 | if (sy == 1) {
1198 | mClipRect.top = mWidgetRect.top;
1199 | mClipRect.bottom = mWidgetRect.bottom;
1200 | }
1201 |
1202 | mClip = mClipRect;
1203 | }
1204 |
1205 | if (!endAnima) {
1206 | applyAnima();
1207 | postExecute();
1208 | } else {
1209 | isRuning = false;
1210 |
1211 | // 修复动画结束后边距有些空隙,
1212 | boolean needFix = false;
1213 |
1214 | if (imgLargeWidth) {
1215 | if (mImgRect.left > 0) {
1216 | mTranslateX -= mImgRect.left;
1217 | } else if (mImgRect.right < mWidgetRect.width()) {
1218 | mTranslateX -= (int) (mWidgetRect.width() - mImgRect.right);
1219 | }
1220 | needFix = true;
1221 | }
1222 |
1223 | if (imgLargeHeight) {
1224 | if (mImgRect.top > 0) {
1225 | mTranslateY -= mImgRect.top;
1226 | } else if (mImgRect.bottom < mWidgetRect.height()) {
1227 | mTranslateY -= (int) (mWidgetRect.height() - mImgRect.bottom);
1228 | }
1229 | needFix = true;
1230 | }
1231 |
1232 | if (needFix) {
1233 | applyAnima();
1234 | }
1235 |
1236 | invalidate();
1237 |
1238 | if (mCompleteCallBack != null) {
1239 | mCompleteCallBack.run();
1240 | mCompleteCallBack = null;
1241 | }
1242 | }
1243 | }
1244 |
1245 | private void applyAnima() {
1246 | mAnimaMatrix.reset();
1247 | mAnimaMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
1248 | mAnimaMatrix.postTranslate(mRotateCenter.x, mRotateCenter.y);
1249 | mAnimaMatrix.postTranslate(-mHalfBaseRectWidth, -mHalfBaseRectHeight);
1250 | mAnimaMatrix.postRotate(mDegrees, mRotateCenter.x, mRotateCenter.y);
1251 | mAnimaMatrix.postScale(mScale, mScale, mScaleCenter.x, mScaleCenter.y);
1252 | mAnimaMatrix.postTranslate(mTranslateX, mTranslateY);
1253 | executeTranslate();
1254 | }
1255 |
1256 |
1257 | private void postExecute() {
1258 | if (isRuning) post(this);
1259 | }
1260 | }
1261 |
1262 | public Info getInfo() {
1263 | RectF rect = new RectF();
1264 | int[] p = new int[2];
1265 | getLocation(this, p);
1266 | rect.set(p[0] + mImgRect.left, p[1] + mImgRect.top, p[0] + mImgRect.right, p[1] + mImgRect.bottom);
1267 | return new Info(rect, mImgRect, mWidgetRect, mBaseRect, mScreenCenter, mScale, mDegrees, mScaleType);
1268 | }
1269 |
1270 | public static Info getImageViewInfo(ImageView imgView) {
1271 | int[] p = new int[2];
1272 | getLocation(imgView, p);
1273 |
1274 | Drawable drawable = imgView.getDrawable();
1275 |
1276 | Matrix matrix = imgView.getImageMatrix();
1277 |
1278 | int width = getDrawableWidth(drawable);
1279 | int height = getDrawableHeight(drawable);
1280 |
1281 | RectF imgRect = new RectF(0, 0, width, height);
1282 | matrix.mapRect(imgRect);
1283 |
1284 | RectF rect = new RectF(p[0] + imgRect.left, p[1] + imgRect.top, p[0] + imgRect.right, p[1] + imgRect.bottom);
1285 | Log.e("是这个吗啊",rect.top+"");
1286 | Log.e("是这个吗啊",rect.left+"");
1287 | Log.e("是这个吗啊",rect.right+"");
1288 | Log.e("是这个吗啊",rect.bottom+"");
1289 | RectF widgetRect = new RectF(0, 0, imgView.getWidth(), imgView.getHeight());
1290 | RectF baseRect = new RectF(widgetRect);
1291 | PointF screenCenter = new PointF(widgetRect.width() / 2, widgetRect.height() / 2);
1292 |
1293 | return new Info(rect, imgRect, widgetRect, baseRect, screenCenter, 1, 0, imgView.getScaleType());
1294 | }
1295 |
1296 | private static void getLocation(View target, int[] position) {
1297 |
1298 | position[0] += target.getLeft();
1299 | position[1] += target.getTop();
1300 |
1301 | ViewParent viewParent = target.getParent();
1302 | while (viewParent instanceof View) {
1303 | final View view = (View) viewParent;
1304 |
1305 | if (view.getId() == android.R.id.content) return;
1306 |
1307 | position[0] -= view.getScrollX();
1308 | position[1] -= view.getScrollY();
1309 |
1310 | position[0] += view.getLeft();
1311 | position[1] += view.getTop();
1312 |
1313 | viewParent = view.getParent();
1314 | }
1315 |
1316 | position[0] = (int) (position[0] + 0.5f);
1317 | position[1] = (int) (position[1] + 0.5f);
1318 | }
1319 |
1320 | private void reset() {
1321 | mAnimaMatrix.reset();
1322 | executeTranslate();
1323 | mScale = 1;
1324 | mTranslateX = 0;
1325 | mTranslateY = 0;
1326 | }
1327 |
1328 | public interface ClipCalculate {
1329 | float calculateTop();
1330 | }
1331 |
1332 | public class START implements ClipCalculate {
1333 | public float calculateTop() {
1334 | return mImgRect.top;
1335 | }
1336 | }
1337 |
1338 | public class END implements ClipCalculate {
1339 | public float calculateTop() {
1340 | return mImgRect.bottom;
1341 | }
1342 | }
1343 |
1344 | public class OTHER implements ClipCalculate {
1345 | public float calculateTop() {
1346 | return (mImgRect.top + mImgRect.bottom) / 2;
1347 | }
1348 | }
1349 |
1350 | /**
1351 | * 在PhotoView内部还没有图片的时候同样可以调用该方法
1352 | *
1353 | * 此时并不会播放动画,当给PhotoView设置图片后会自动播放动画。
1354 | *
1355 | * 若等待时间过长也没有给控件设置图片,则会忽略该动画,若要再次播放动画则需要重新调用该方法
1356 | * (等待的时间默认500毫秒,可以通过setMaxAnimFromWaiteTime(int)设置最大等待时间)
1357 | */
1358 | public void animaFrom(Info info) {
1359 | if (isInit) {
1360 | reset();
1361 |
1362 | Info mine = getInfo();
1363 |
1364 | float scaleX = info.mImgRect.width() / mine.mImgRect.width();
1365 | float scaleY = info.mImgRect.height() / mine.mImgRect.height();
1366 | float scale = scaleX < scaleY ? scaleX : scaleY;
1367 |
1368 | float ocx = info.mRect.left + info.mRect.width() / 2;
1369 | float ocy = info.mRect.top + info.mRect.height() / 2;
1370 |
1371 | float mcx = mine.mRect.left + mine.mRect.width() / 2;
1372 | float mcy = mine.mRect.top + mine.mRect.height() / 2;
1373 |
1374 | mAnimaMatrix.reset();
1375 | // mAnimaMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
1376 | mAnimaMatrix.postTranslate(ocx - mcx, ocy - mcy);
1377 | mAnimaMatrix.postScale(scale, scale, ocx, ocy);
1378 | mAnimaMatrix.postRotate(info.mDegrees, ocx, ocy);
1379 | executeTranslate();
1380 |
1381 | mScaleCenter.set(ocx, ocy);
1382 | mRotateCenter.set(ocx, ocy);
1383 |
1384 | mTranslate.withTranslate(0, 0, (int) -(ocx - mcx), (int) -(ocy - mcy));
1385 | mTranslate.withScale(scale, 1);
1386 | mTranslate.withRotate((int) info.mDegrees, 0);
1387 |
1388 | if (info.mWidgetRect.width() < info.mImgRect.width() || info.mWidgetRect.height() < info.mImgRect.height()) {
1389 | float clipX = info.mWidgetRect.width() / info.mImgRect.width();
1390 | float clipY = info.mWidgetRect.height() / info.mImgRect.height();
1391 | clipX = clipX > 1 ? 1 : clipX;
1392 | clipY = clipY > 1 ? 1 : clipY;
1393 |
1394 | ClipCalculate c = info.mScaleType == ScaleType.FIT_START ? new START() : info.mScaleType == ScaleType.FIT_END ? new END() : new OTHER();
1395 |
1396 | mTranslate.withClip(clipX, clipY, 1 - clipX, 1 - clipY, mAnimaDuring / 3, c);
1397 |
1398 | mTmpMatrix.setScale(clipX, clipY, (mImgRect.left + mImgRect.right) / 2, c.calculateTop());
1399 | mTmpMatrix.mapRect(mTranslate.mClipRect, mImgRect);
1400 | mClip = mTranslate.mClipRect;
1401 | }
1402 |
1403 | mTranslate.start();
1404 | } else {
1405 | mFromInfo = info;
1406 | mInfoTime = System.currentTimeMillis();
1407 | }
1408 | }
1409 |
1410 | public void animaTo(Info info, Runnable completeCallBack) {
1411 | if (isInit) {
1412 | mTranslate.stop();
1413 |
1414 | mTranslateX = 0;
1415 | mTranslateY = 0;
1416 |
1417 | float tcx = info.mRect.left + info.mRect.width() / 2;
1418 | float tcy = info.mRect.top + info.mRect.height() / 2;
1419 |
1420 | mScaleCenter.set(mImgRect.left + mImgRect.width() / 2, mImgRect.top + mImgRect.height() / 2);
1421 | mRotateCenter.set(mScaleCenter);
1422 |
1423 | // 将图片旋转回正常位置,用以计算
1424 | mAnimaMatrix.postRotate(-mDegrees, mScaleCenter.x, mScaleCenter.y);
1425 | mAnimaMatrix.mapRect(mImgRect, mBaseRect);
1426 |
1427 | // 缩放
1428 | float scaleX = info.mImgRect.width() / mBaseRect.width();
1429 | float scaleY = info.mImgRect.height() / mBaseRect.height();
1430 | float scale = scaleX > scaleY ? scaleX : scaleY;
1431 |
1432 | mAnimaMatrix.postRotate(mDegrees, mScaleCenter.x, mScaleCenter.y);
1433 | mAnimaMatrix.mapRect(mImgRect, mBaseRect);
1434 |
1435 | mDegrees = mDegrees % 360;
1436 |
1437 | mTranslate.withTranslate(0, 0, (int) (tcx - mScaleCenter.x), (int) (tcy - mScaleCenter.y));
1438 | mTranslate.withScale(mScale, scale);
1439 | mTranslate.withRotate((int) mDegrees, (int) info.mDegrees, mAnimaDuring * 2 / 3);
1440 |
1441 | if (info.mWidgetRect.width() < info.mRect.width() || info.mWidgetRect.height() < info.mRect.height()) {
1442 | float clipX = info.mWidgetRect.width() / info.mRect.width();
1443 | float clipY = info.mWidgetRect.height() / info.mRect.height();
1444 | clipX = clipX > 1 ? 1 : clipX;
1445 | clipY = clipY > 1 ? 1 : clipY;
1446 |
1447 | final float cx = clipX;
1448 | final float cy = clipY;
1449 | final ClipCalculate c = info.mScaleType == ScaleType.FIT_START ? new START() : info.mScaleType == ScaleType.FIT_END ? new END() : new OTHER();
1450 |
1451 | postDelayed(new Runnable() {
1452 | @Override
1453 | public void run() {
1454 | mTranslate.withClip(1, 1, -1 + cx, -1 + cy, mAnimaDuring / 2, c);
1455 | }
1456 | }, mAnimaDuring / 2);
1457 | }
1458 |
1459 | mCompleteCallBack = completeCallBack;
1460 | mTranslate.start();
1461 | }
1462 | }
1463 |
1464 | public void rotate(float degrees) {
1465 | mDegrees += degrees;
1466 | int centerX = (int) (mWidgetRect.left + mWidgetRect.width() / 2);
1467 | int centerY = (int) (mWidgetRect.top + mWidgetRect.height() /2);
1468 |
1469 | mAnimaMatrix.postRotate(degrees, centerX, centerY);
1470 | executeTranslate();
1471 | }
1472 |
1473 |
1474 | }
1475 |
--------------------------------------------------------------------------------
/library/src/main/java/com/bm/library/RotateGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.bm.library;
2 |
3 | import android.util.Log;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * Created by q2366 on 2015/10/12.
8 | */
9 | public class RotateGestureDetector {
10 |
11 | private static final int MAX_DEGREES_STEP = 120;
12 |
13 | private OnRotateListener mListener;
14 |
15 | private float mPrevSlope;
16 | private float mCurrSlope;
17 |
18 | private float x1;
19 | private float y1;
20 | private float x2;
21 | private float y2;
22 |
23 | public RotateGestureDetector(OnRotateListener l) {
24 | mListener = l;
25 | }
26 |
27 | public void onTouchEvent(MotionEvent event) {
28 |
29 | final int Action = event.getActionMasked();
30 |
31 | switch (Action) {
32 | case MotionEvent.ACTION_POINTER_DOWN:
33 | case MotionEvent.ACTION_POINTER_UP:
34 | if (event.getPointerCount() == 2) mPrevSlope = caculateSlope(event);
35 | break;
36 | case MotionEvent.ACTION_MOVE:
37 | if (event.getPointerCount() > 1) {
38 | mCurrSlope = caculateSlope(event);
39 |
40 | double currDegrees = Math.toDegrees(Math.atan(mCurrSlope));
41 | double prevDegrees = Math.toDegrees(Math.atan(mPrevSlope));
42 |
43 | double deltaSlope = currDegrees - prevDegrees;
44 |
45 | if (Math.abs(deltaSlope) <= MAX_DEGREES_STEP) {
46 | mListener.onRotate((float) deltaSlope, (x2 + x1) / 2, (y2 + y1) / 2);
47 | }
48 | mPrevSlope = mCurrSlope;
49 | }
50 | break;
51 | default:
52 | break;
53 | }
54 | }
55 |
56 | private float caculateSlope(MotionEvent event) {
57 | x1 = event.getX(0);
58 | y1 = event.getY(0);
59 | x2 = event.getX(1);
60 | y2 = event.getY(1);
61 | return (y2 - y1) / (x2 - x1);
62 | }
63 | }
64 |
65 | interface OnRotateListener {
66 | void onRotate(float degrees, float focusX, float focusY);
67 | }
--------------------------------------------------------------------------------
/newDots.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lihangleo2/Imgdots/c6cc8091e0f510879d4c50560d1cfe60f3e38206/newDots.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------