├── .DS_Store
├── .gitignore
├── ReadMe.md
├── SmartTouchLayout-master.iml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── test
│ │ └── smarttouchlayout_master
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── test
│ │ │ └── smarttouchlayout_master
│ │ │ ├── AlbumAdapter.java
│ │ │ ├── DataBean.java
│ │ │ ├── Fragment1.java
│ │ │ ├── Fragment2.java
│ │ │ ├── Fragment3.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MyFragmentPagerAdapter.java
│ │ │ └── ViewPageActivity.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ ├── img1.jpeg
│ │ ├── img2.jpeg
│ │ ├── img3.jpeg
│ │ ├── img4.jpeg
│ │ ├── img5.jpeg
│ │ ├── img6.jpeg
│ │ └── img7.jpg
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── activity_view_page.xml
│ │ ├── album_list_item.xml
│ │ ├── fragment_fragment1.xml
│ │ ├── fragment_fragment2.xml
│ │ └── fragment_fragment3.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ ├── styles.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── test
│ └── smarttouchlayout_master
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
├── settings.gradle
└── smartviewlibrary
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── jagger
│ └── smartviewlibrary
│ └── ExampleInstrumentedTest.java
├── main
├── AndroidManifest.xml
└── java
│ └── com
│ └── jagger
│ └── smartviewlibrary
│ └── SmartTouchLayout.java
└── test
└── java
└── com
└── jagger
└── smartviewlibrary
└── ExampleUnitTest.java
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .idea
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 | 智能识别手势布局-SmartTouchLayout
2 | ============================
3 |
4 | 需求来源:
5 | -------
6 | 产品需要实现跟微信朋友圈看大图可下滑退出的效果,
7 | 但项目中不仅有大图,图中还有按钮,有文本,有视频等等,总之布局很复杂
8 | 反正这些他不管,就是要下滑退出,再缩回上一层界面小图片位置。
9 |
10 | 找了不少DEMO,基本都是只满足图片,视频实现这个功能, 那就自己来
11 |
12 |
13 |
14 |
15 | SmartTouchLayout
16 | -----------------
17 | 多手势识别的布局, 只要在布局里的VIEW,就支持:
18 | 双指、双击缩放;单指滑动;下滑退出;单击退出;不影响子控件事件;与ViewPage不冲突;
19 |
20 | 无脑调用:
21 | FrameLayout怎么用,它就怎么用。
22 |
23 |
24 |
25 |
26 | 疑车不能无据,直接上图
27 | -----------------
28 |
29 | 
30 |
31 | 双指、双击缩放;下滑退出到指定位置;
32 |
33 |
34 |
35 | 
36 |
37 | 不影响子控件事件;不指定位置时,下滑到底部消失;
38 |
39 |
40 |
41 | 
42 |
43 | 与ViewPage不冲突;
44 |
45 |
46 |
47 |
48 | \----------------------------------------\
49 | 美图看完了,可否打赏个 ⭐️
50 |
51 | \----------------------------------------\
52 |
53 |
54 |
55 |
56 | 下载体验
57 | -----------------
58 |
59 |
60 | 
61 |
62 |
63 |
64 | 如何使用
65 | -----------------
66 | 1.引用
67 |
68 | ```
69 | implementation 'com.jagger:SmartTouchLayout:1.0.4'
70 | ```
71 |
72 |
73 | 2.直接在layout.xml文件中使用
74 | 使用方式跟FrameLayout一样
75 |
76 | ```
77 |
81 |
82 | ...
83 |
84 |
85 | ```
86 |
87 | 3.属性设置
88 |
设置结束时动画飞到哪去,可指定位置和大小,效果如图1;不设置,则飞到底部如图2;
89 | ```
90 | /**
91 | * 设置结束时,动画回到什么位置和大小
92 | * @param w view.getWidth() 结束时的宽
93 | * @param h view.getHeight() 结束时的高
94 | * @param left view location[0] 结束时相对屏幕的X坐标
95 | * @param top view location[1] 结束时相对屏幕的Y坐标
96 | * @param scaleSide 结束时以宽/高拉伸
97 | */
98 | public void setEndViewLocalSize(int w, int h, int left, int top, EndViewScaleSide scaleSide)
99 | ```
100 | 设置是否需要支持下滑关闭
101 | ```
102 | smartTouchLayout.setMoveExitEnable(true);
103 | ```
104 | 设置是否需要支持缩放
105 | ```
106 | smartTouchLayout.setZoomEnable(true);
107 | ```
108 |
109 | 4.最后要把归属的Activity设置为透明
110 | ```
111 |
112 |
117 | ```
118 |
119 |
120 |
121 | 代码解读
122 | -----------------
123 | 通过计算点击的时间差,判断是单击还是双击
124 |
125 | ```java
126 |
127 | private void checkClickDown(MotionEvent ev){
128 | if (0 == mInTouchEventCount.touchCount) { // 第一次按下时,开始统计
129 | //Log.i(TAG , "checkClickDown 第一次按下时,开始统计" );
130 | postDelayed(mInTouchEventCount, DOUBLE_CLICK_TIME_OFFSET);
131 | }
132 | }
133 |
134 | private void checkClickUp(float clickX, float clickY){
135 | //Log.i(TAG , "checkClickUp clickX:" + clickX + ",clickY:" + clickY);
136 | // 一次点击事件要有按下和抬起, 有抬起必有按下, 所以只需要在ACTION_UP中处理
137 | if (!mInTouchEventCount.isLongClick) {
138 | mInTouchEventCount.touchCount++;
139 |
140 | if(mInTouchEventCount.touchCount == 1){
141 | firstClickX = clickX;
142 | firstClickY = clickY;
143 | //Log.i(TAG , "checkClickUp 点击第一下");
144 | }else if(mInTouchEventCount.touchCount == 2){
145 | secondClickX = clickX;
146 | secondClickY = clickY;
147 |
148 | float xOff = Math.abs(firstClickX - secondClickX);
149 | float yOff = Math.abs(firstClickY - secondClickY);
150 | //两次点击距离相近
151 | if(xOff < 60 && yOff < 60 ){
152 | //Double click 成立
153 | //Log.i(TAG , "checkClickUp Double click 成立");
154 | }else{
155 | //Double click 不成立,当单击处理
156 | mInTouchEventCount.touchCount = 1;
157 | //Log.i(TAG , "checkClickUp Double click 不成立,当单击处理");
158 | }
159 | }else{
160 | mInTouchEventCount.touchCount = 0;
161 | //Log.i(TAG , "checkClickUp 复原");
162 | }
163 | }else {
164 | // 长按复原
165 | mInTouchEventCount.isLongClick = false;
166 | //Log.i(TAG , "checkClickUp 长按复原");
167 | }
168 | }
169 |
170 | private class TouchEventCountThread implements Runnable {
171 | public int touchCount = 0;
172 | public boolean isLongClick = false;
173 |
174 | @Override
175 | public void run() {
176 | Message msg = new Message();
177 | if(0 == touchCount){ // long click
178 | isLongClick = true;
179 | } else {
180 | msg.arg1 = touchCount;
181 | mTouchEventHandler.sendMessage(msg);
182 | touchCount = 0;
183 | }
184 | //Log.i(TAG , "TouchEventCountThread 结束:" + touchCount);
185 | }
186 | }
187 |
188 | private class TouchEventHandler extends Handler {
189 | @Override
190 | public void handleMessage(Message msg) {
191 | //Log.i(TAG, "touch " + msg.arg1 + " time.");
192 | if(msg.arg1 == 1){
193 | onSingleClicked(oldX, oldY);
194 | }else{
195 | onDoubleClicked(oldX, oldY);
196 | }
197 | }
198 | }
199 | ```
200 |
201 |
202 | 如果单击,判断把事件向子VIEW传递还是自已处理
203 |
204 | ```java
205 | public boolean onInterceptTouchEvent(MotionEvent ev) {
206 |
207 | final int action = ev.getAction();
208 | if(action == MotionEvent.ACTION_MOVE && mTouchState == TOUCH_MYSELF){
209 | //Log.i(TAG, "拦截 为自己处理");
210 | return true;
211 | }
212 |
213 | switch (action) {
214 | case MotionEvent.ACTION_DOWN:
215 | //判断单双击
216 | checkClickDown(ev);
217 |
218 | //
219 | oldX = ev.getRawX();
220 | oldY = ev.getRawY();
221 | mTouchState = (isZooming || isMoving) ? TOUCH_MYSELF : TOUCH_TO_CHILDREN;
222 | //Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN oldX:" + oldX + ",mTouchState:" + mTouchState);
223 | break;
224 | case MotionEvent.ACTION_MOVE:
225 | // 是否进行了滑动,设置滑动状态
226 | float tMoveX = ev.getRawX() - oldX;
227 | final float xDiff = Math.abs(tMoveX);
228 |
229 | float tMoveY = ev.getRawY() - oldY;
230 | final float yDiff = Math.abs(tMoveY);
231 |
232 | if (yDiff > mTouchSlop || xDiff > mTouchSlop) {
233 | mTouchState = TOUCH_MYSELF;
234 | }
235 | break;
236 | case MotionEvent.ACTION_CANCEL:
237 | case MotionEvent.ACTION_UP:
238 | mTouchState = TOUCH_TO_CHILDREN;
239 | break;
240 | }
241 |
242 | // origin do
243 | return mTouchState != TOUCH_TO_CHILDREN;
244 | }
245 | ```
246 |
247 | 处理缩放
248 |
249 | ```java
250 | @Override
251 | public boolean onScale(ScaleGestureDetector detector) {
252 | float scaleFactor = detector.getScaleFactor();
253 |
254 | if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
255 | return false;
256 |
257 | //双指缩放中
258 | isZooming = true;
259 | mCurrentScale *= scaleFactor;
260 | if(mCurrentScale < MIN_SCALE){
261 | mCurrentScale = MIN_SCALE;
262 | }
263 | setScaleX(mCurrentScale);
264 | setScaleY(mCurrentScale);
265 |
266 | //返回 true 会一闪一闪的
267 | return false;
268 | }
269 |
270 | @Override
271 | public void onScaleEnd(ScaleGestureDetector detector) {
272 | super.onScaleEnd(detector);
273 |
274 | //Log.i(TAG, "onScaleEnd" );
275 | scaleEnd();
276 | }
277 |
278 | };
279 |
280 | ```
281 |
282 | 在 onTouchEvent() 中处理滑动事件,
283 | 缩放时自己处理滑动事件,
284 | 非缩放时把滑动事件向父传递,所以 ViewPage 会处理左右滑动事件
285 |
286 | ```
287 | if(isZooming){
288 | //缩放时, 自己处理MOVE事件
289 | getParent().requestDisallowInterceptTouchEvent(true);
290 | }else {
291 | //非缩放时, 由父控件处理MOVE事件
292 | getParent().requestDisallowInterceptTouchEvent(false);
293 | ...
294 | }
295 | ```
296 |
297 |
298 | 滑动和缩放过程中,处理边界回弹 checkBorder()
299 |
300 | ```java
301 | float overRightOffset = location[0] - (mCurrentScale*getWidth() - originalRight)*-1;
302 | float overBottomOffset = location[1] - (mCurrentScale*getHeight() - originalBottom)*-1;
303 |
304 | if(location[0] > 0){
305 | //是否越入左边界
306 | moveX4Zooming = ((1-mCurrentScale) * getWidth())/2 * -1; //放大后相对于 原来大小的 X 坐标的偏移
307 | //是否越入上,下边界
308 | checkTopBottomBorder(location[1], overBottomOffset);
309 | animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming);
310 | }else if(overRightOffset < 0){
311 | //是否越入右边界
312 | moveX4Zooming += Math.abs(overRightOffset); //放大前的 X=0
313 | //是否越入上,下边界
314 | checkTopBottomBorder(location[1], overBottomOffset);
315 | animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming);
316 | }else{
317 | //是否越入上,下边界
318 | checkTopBottomBorder(location[1], overBottomOffset);
319 | animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming);
320 | }
321 | ```
322 |
323 |
324 |
325 |
326 | 如果为哥你节省了几天宝贵的时间,为何不打赏一杯柠檬茶呢?
327 | -----------------
328 |
329 |
330 | 
331 |
332 |
--------------------------------------------------------------------------------
/SmartTouchLayout-master.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.test.smarttouchlayout_master"
9 | minSdkVersion 16
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | implementation 'androidx.appcompat:appcompat:1.0.0'
30 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
34 | implementation "androidx.recyclerview:recyclerview:1.0.0"
35 |
36 | //modules
37 | implementation project(':smartviewlibrary')
38 | // implementation 'com.jagger:SmartTouchLayout:1.0.2'
39 | }
40 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/test/smarttouchlayout_master/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import android.content.Context;
4 | import androidx.test.platform.app.InstrumentationRegistry;
5 | import androidx.test.ext.junit.runners.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
23 |
24 | assertEquals("com.test.smarttouchlayout_master", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/smarttouchlayout_master/AlbumAdapter.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.recyclerview.widget.RecyclerView;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * 相册
16 | * @author Jagger 2021-1-20
17 | */
18 | public class AlbumAdapter extends RecyclerView.Adapter{
19 | private Context mContext;
20 | private List mList;
21 | private onItemEventListener mOnItemEventListener;
22 |
23 | /**
24 | * 事件接口
25 | */
26 | public interface onItemEventListener{
27 | void onItemClicked(DataBean dataBean, int position);
28 | }
29 |
30 | public AlbumAdapter(Context context, List list){
31 | this.mContext = context;
32 | this.mList = list;
33 | }
34 |
35 | public void setOnItemEventListener(onItemEventListener listener){
36 | this.mOnItemEventListener = listener;
37 | }
38 |
39 |
40 | @Override
41 | public int getItemViewType(int position) {
42 | // TODO Auto-generated method stub
43 | return 0;
44 | }
45 |
46 | @NonNull
47 | @Override
48 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
49 | View view = LayoutInflater.from(mContext).inflate(R.layout.album_list_item, null);
50 | ViewHolderPhotoUnLock holder = new ViewHolderPhotoUnLock(view);
51 | return holder;
52 | }
53 |
54 | @Override
55 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
56 | final DataBean item = mList.get(position);
57 | ((ViewHolderPhotoUnLock)holder).img_photo.setImageResource(item.resId);
58 |
59 | holder.itemView.setOnClickListener(new View.OnClickListener() {
60 | @Override
61 | public void onClick(View v) {
62 | if(mOnItemEventListener != null){
63 | mOnItemEventListener.onItemClicked(item, position);
64 | }
65 | }
66 | });
67 | }
68 |
69 | @Override
70 | public int getItemCount() {
71 | return mList.size();
72 | }
73 |
74 |
75 | /**
76 | * 图片
77 | */
78 | private class ViewHolderPhotoUnLock extends RecyclerView.ViewHolder {
79 | public ImageView img_photo;
80 |
81 | public ViewHolderPhotoUnLock(@NonNull View itemView) {
82 | super(itemView);
83 | img_photo = itemView.findViewById(R.id.img_photo);
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/smarttouchlayout_master/DataBean.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class DataBean implements Parcelable {
7 | public int resId;
8 | public int localX;
9 | public int localY;
10 | public int width;
11 | public int height;
12 |
13 | public DataBean(){}
14 |
15 | protected DataBean(Parcel in) {
16 | resId = in.readInt();
17 | localX = in.readInt();
18 | localY = in.readInt();
19 | width = in.readInt();
20 | height = in.readInt();
21 | }
22 |
23 | @Override
24 | public void writeToParcel(Parcel dest, int flags) {
25 | dest.writeInt(resId);
26 | dest.writeInt(localX);
27 | dest.writeInt(localY);
28 | dest.writeInt(width);
29 | dest.writeInt(height);
30 | }
31 |
32 | @Override
33 | public int describeContents() {
34 | return 0;
35 | }
36 |
37 | public static final Creator CREATOR = new Creator() {
38 | @Override
39 | public DataBean createFromParcel(Parcel in) {
40 | return new DataBean(in);
41 | }
42 |
43 | @Override
44 | public DataBean[] newArray(int size) {
45 | return new DataBean[size];
46 | }
47 | };
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/smarttouchlayout_master/Fragment1.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import android.os.Bundle;
4 | import androidx.fragment.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.jagger.smartviewlibrary.SmartTouchLayout;
11 |
12 | public class Fragment1 extends Fragment {
13 | public static final String ALBUM_ITEM = "ALBUM_ITEM";
14 | private DataBean dataBean;
15 |
16 | protected static Bundle getBundle(DataBean dataBean){
17 | Bundle bundle = new Bundle();
18 | bundle.putParcelable(ALBUM_ITEM, dataBean);
19 | return bundle;
20 | }
21 |
22 | public static Fragment1 newFragment(DataBean dataBean) {
23 | Fragment1 fragment1 = new Fragment1();
24 | fragment1.setArguments(getBundle(dataBean));
25 | return fragment1;
26 | }
27 |
28 | @Override
29 | public void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 |
32 | Bundle bundle = getArguments();
33 | if (bundle != null) {
34 | if (bundle.containsKey(ALBUM_ITEM)) {
35 | dataBean = bundle.getParcelable(ALBUM_ITEM);
36 | }
37 | }
38 | }
39 |
40 | @Override
41 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
42 | Bundle savedInstanceState) {
43 | // Inflate the layout for this fragment
44 | View customView = inflater.inflate(R.layout.fragment_fragment1, container, false);
45 | ((ImageView)customView.findViewById(R.id.img_photo)).setImageResource(dataBean.resId);
46 |
47 | SmartTouchLayout stl = customView.findViewById(R.id.stl);
48 |
49 | stl.setEndViewLocalSize(dataBean.width, dataBean.height, dataBean.localX, dataBean.localY, SmartTouchLayout.EndViewScaleSide.Width);
50 | stl.setMoveExitEnable(true);
51 | stl.setZoomEnable(true);
52 |
53 | return customView;
54 | }
55 |
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/smarttouchlayout_master/Fragment2.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import android.os.Bundle;
4 | import androidx.fragment.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.Toast;
10 |
11 | import com.jagger.smartviewlibrary.SmartTouchLayout;
12 |
13 | public class Fragment2 extends Fragment {
14 |
15 | public static final String ALBUM_ITEM = "ALBUM_ITEM";
16 | private DataBean dataBean;
17 |
18 | protected static Bundle getBundle(DataBean dataBean){
19 | Bundle bundle = new Bundle();
20 | bundle.putParcelable(ALBUM_ITEM, dataBean);
21 | return bundle;
22 | }
23 |
24 | public static Fragment2 newFragment(DataBean dataBean) {
25 | Fragment2 fragment2 = new Fragment2();
26 | fragment2.setArguments(getBundle(dataBean));
27 | return fragment2;
28 | }
29 |
30 | @Override
31 | public void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 |
34 | Bundle bundle = getArguments();
35 | if (bundle != null) {
36 | if (bundle.containsKey(ALBUM_ITEM)) {
37 | dataBean = bundle.getParcelable(ALBUM_ITEM);
38 | }
39 | }
40 | }
41 |
42 | @Override
43 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
44 | Bundle savedInstanceState) {
45 | // Inflate the layout for this fragment
46 | View customView = inflater.inflate(R.layout.fragment_fragment2, container, false);
47 | ((ImageView)customView.findViewById(R.id.img_photo)).setImageResource(dataBean.resId);
48 | customView.findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
49 | @Override
50 | public void onClick(View v) {
51 | Toast.makeText(getActivity(), "点击事件穿透到子控件" , Toast.LENGTH_LONG).show();
52 | }
53 | });
54 |
55 |
56 | SmartTouchLayout stl = customView.findViewById(R.id.stl);
57 | // 不赋值,向底部消失
58 | // stl.setEndViewLocalSize(dataBean.width, dataBean.height, dataBean.localX, dataBean.localY, SmartTouchLayout.EndViewScaleSide.Width);
59 | stl.setMoveExitEnable(true);
60 | stl.setZoomEnable(true);
61 |
62 | return customView;
63 | }
64 |
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/smarttouchlayout_master/Fragment3.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import android.os.Bundle;
4 | import androidx.fragment.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 |
10 | import com.jagger.smartviewlibrary.SmartTouchLayout;
11 |
12 | public class Fragment3 extends Fragment {
13 |
14 | public static final String ALBUM_ITEM = "ALBUM_ITEM";
15 | private DataBean dataBean;
16 |
17 | protected static Bundle getBundle(DataBean dataBean){
18 | Bundle bundle = new Bundle();
19 | bundle.putParcelable(ALBUM_ITEM, dataBean);
20 | return bundle;
21 | }
22 |
23 | public static Fragment3 newFragment(DataBean dataBean) {
24 | Fragment3 fragment3 = new Fragment3();
25 | fragment3.setArguments(getBundle(dataBean));
26 | return fragment3;
27 | }
28 |
29 | @Override
30 | public void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 |
33 | Bundle bundle = getArguments();
34 | if (bundle != null) {
35 | if (bundle.containsKey(ALBUM_ITEM)) {
36 | dataBean = bundle.getParcelable(ALBUM_ITEM);
37 | }
38 | }
39 | }
40 |
41 | @Override
42 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
43 | Bundle savedInstanceState) {
44 | // Inflate the layout for this fragment
45 | View customView = inflater.inflate(R.layout.fragment_fragment3, container, false);
46 | ((ImageView)customView.findViewById(R.id.img_photo)).setImageResource(dataBean.resId);
47 |
48 | SmartTouchLayout stl = customView.findViewById(R.id.stl);
49 |
50 | // stl.setEndViewLocalSize(dataBean.width, dataBean.height, dataBean.localX, dataBean.localY, SmartTouchLayout.EndViewScaleSide.Width);
51 | // 不可滑动关闭
52 | stl.setMoveExitEnable(false);
53 | // 不可缩放
54 | stl.setZoomEnable(false);
55 | return customView;
56 | }
57 |
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/smarttouchlayout_master/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 | import androidx.recyclerview.widget.GridLayoutManager;
5 | import androidx.recyclerview.widget.RecyclerView;
6 |
7 | import android.os.Bundle;
8 |
9 | import java.util.ArrayList;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 | private RecyclerView rcv_album;
13 | private AlbumAdapter mAdapter;
14 | private ArrayList mList = new ArrayList<>();
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_main);
20 | initView();
21 | initData();
22 | }
23 |
24 | private void initView() {
25 | GridLayoutManager manager = new GridLayoutManager(MainActivity.this,3, GridLayoutManager.VERTICAL, false);
26 | rcv_album = findViewById(R.id.rcv_album);
27 | rcv_album.setLayoutManager(manager);
28 |
29 | mAdapter = new AlbumAdapter(MainActivity.this, mList);
30 | mAdapter.setOnItemEventListener(new AlbumAdapter.onItemEventListener() {
31 | @Override
32 | public void onItemClicked(DataBean dataBean, int position) {
33 | //记录小图坐标
34 | for(int i = 0 ; i < mList.size(); i++){
35 | int[]location = new int[2];
36 | rcv_album.findViewHolderForAdapterPosition(i).itemView.getLocationOnScreen(location);
37 | mList.get(i).localX = location[0];
38 | mList.get(i).localY = location[1];
39 | mList.get(i).width = rcv_album.findViewHolderForAdapterPosition(i).itemView.getWidth();
40 | mList.get(i).height = rcv_album.findViewHolderForAdapterPosition(i).itemView.getHeight();
41 | }
42 |
43 | ViewPageActivity.launchActivity(MainActivity.this, mList, position);
44 | }
45 | });
46 | rcv_album.setAdapter(mAdapter);
47 |
48 | }
49 |
50 | private void initData(){
51 | DataBean bean1 = new DataBean();
52 | bean1.resId = R.drawable.img1;
53 |
54 | DataBean bean2 = new DataBean();
55 | bean2.resId = R.drawable.img2;
56 |
57 | DataBean bean3 = new DataBean();
58 | bean3.resId = R.drawable.img3;
59 |
60 | DataBean bean4 = new DataBean();
61 | bean4.resId = R.drawable.img4;
62 |
63 | DataBean bean5 = new DataBean();
64 | bean5.resId = R.drawable.img5;
65 |
66 | DataBean bean6 = new DataBean();
67 | bean6.resId = R.drawable.img6;
68 |
69 | DataBean bean7 = new DataBean();
70 | bean7.resId = R.drawable.img7;
71 |
72 | mList.add(bean1);
73 | mList.add(bean2);
74 | mList.add(bean3);
75 | mList.add(bean4);
76 | mList.add(bean5);
77 | mList.add(bean6);
78 | mList.add(bean7);
79 |
80 | mAdapter.notifyDataSetChanged();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/smarttouchlayout_master/MyFragmentPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import androidx.fragment.app.Fragment;
4 | import androidx.fragment.app.FragmentManager;
5 | import androidx.fragment.app.FragmentStatePagerAdapter;
6 |
7 | import java.util.List;
8 |
9 | public class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {
10 |
11 | List mFragments;
12 |
13 |
14 | public MyFragmentPagerAdapter(FragmentManager fm, List fragments) {
15 | super(fm);
16 | mFragments = fragments;
17 | }
18 |
19 | @Override
20 | public Fragment getItem(int position) {
21 | return mFragments.get(position);
22 | }
23 |
24 | @Override
25 | public int getCount() {
26 | return mFragments.size();
27 | }
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/smarttouchlayout_master/ViewPageActivity.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import androidx.fragment.app.Fragment;
4 | import androidx.viewpager.widget.ViewPager;
5 | import androidx.appcompat.app.AppCompatActivity;
6 |
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.os.Bundle;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | public class ViewPageActivity extends AppCompatActivity {
15 | private static final String ALBUM_LIST = "ALBUM_LIST";
16 | private static final String ALBUM_CURR_INDEX = "ALBUM_CURR_INDEX";
17 |
18 | private List fragmentList;
19 | private ViewPager viewPager;
20 | private List mAttachList = null;
21 | private int mCurrIndex = 0;
22 |
23 | public static void launchActivity(Context context , ArrayList anchorAlbumItems, int selectIndex) {
24 | Intent intent = null;
25 | intent = new Intent(context, ViewPageActivity.class);
26 |
27 | intent.putParcelableArrayListExtra(ALBUM_LIST, anchorAlbumItems);
28 | intent.putExtra(ALBUM_CURR_INDEX, selectIndex);
29 |
30 | if (intent != null) {
31 | context.startActivity(intent);
32 | }
33 | }
34 |
35 | @Override
36 | protected void onCreate(Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 | setContentView(R.layout.activity_view_page);
39 |
40 | Bundle bundle = getIntent().getExtras();
41 | if (bundle != null) {
42 | if (bundle.containsKey(ALBUM_LIST)) {
43 | mAttachList = bundle.getParcelableArrayList(ALBUM_LIST);
44 | }
45 |
46 | if (bundle.containsKey(ALBUM_CURR_INDEX)) {
47 | mCurrIndex = bundle.getInt(ALBUM_CURR_INDEX);
48 | }
49 | }
50 |
51 | initViewPager();
52 | }
53 |
54 | private void initViewPager() {
55 | viewPager = findViewById(R.id.viewPager);
56 | //
57 | fragmentList = new ArrayList<>();
58 | fragmentList.add(Fragment1.newFragment(mAttachList.get(0)));
59 | fragmentList.add(Fragment2.newFragment(mAttachList.get(1)));
60 | fragmentList.add(Fragment3.newFragment(mAttachList.get(2)));
61 | fragmentList.add(Fragment1.newFragment(mAttachList.get(3)));
62 | fragmentList.add(Fragment1.newFragment(mAttachList.get(4)));
63 | fragmentList.add(Fragment1.newFragment(mAttachList.get(5)));
64 | fragmentList.add(Fragment1.newFragment(mAttachList.get(6)));
65 | //
66 | MyFragmentPagerAdapter myFragmentPager = new MyFragmentPagerAdapter(getSupportFragmentManager(), fragmentList);
67 | viewPager.setAdapter(myFragmentPager);
68 |
69 | viewPager.setCurrentItem(mCurrIndex, true);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/drawable/img1.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/drawable/img2.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/drawable/img3.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/drawable/img4.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img5.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/drawable/img5.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img6.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/drawable/img6.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/drawable/img7.jpg
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_view_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/album_list_item.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
16 |
17 |
18 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_fragment1.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_fragment2.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
17 |
18 |
25 |
26 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_fragment3.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
15 |
16 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SmartTouchLayout-master
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/test/java/com/test/smarttouchlayout_master/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.test.smarttouchlayout_master;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.6.3'
12 |
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | classpath 'com.novoda:bintray-release:+'
17 | }
18 | }
19 |
20 | allprojects {
21 | repositories {
22 | google()
23 | jcenter()
24 |
25 | }
26 | }
27 |
28 | //加上这些
29 | tasks.withType(Javadoc) {
30 | options{ encoding "UTF-8"
31 | charSet 'UTF-8'
32 | links "http://docs.oracle.com/javase/7/docs/api"
33 | }
34 | }
35 |
36 | task clean(type: Delete) {
37 | delete rootProject.buildDir
38 | }
39 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | android.enableJetifier=true
10 | android.useAndroidX=true
11 | org.gradle.jvmargs=-Xmx1536m
12 | # When configured, Gradle will run in incubating parallel mode.
13 | # This option should only be used with decoupled projects. More details, visit
14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
15 | # org.gradle.parallel=true
16 |
17 |
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jan 25 10:55:25 CST 2021
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-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file is automatically generated by Android Studio.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file should *NOT* be checked into Version Control Systems,
5 | # as it contains information specific to your local configuration.
6 | #
7 | # Location of the SDK. This is only used by Gradle.
8 | # For customization when using a Version Control System, please read the
9 | # header note.
10 | sdk.dir=/Users/evening424/AndroidTool/sdk
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='SmartTouchLayout-master'
2 | include ':app'
3 | include ':smartviewlibrary'
4 |
--------------------------------------------------------------------------------
/smartviewlibrary/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/smartviewlibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.novoda.bintray-release'
3 |
4 | android {
5 | compileSdkVersion 29
6 | buildToolsVersion "29.0.3"
7 |
8 | defaultConfig {
9 | minSdkVersion 16
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | consumerProguardFiles 'consumer-rules.pro'
16 | }
17 |
18 | lintOptions {
19 | abortOnError false
20 | }
21 |
22 | publish {
23 | userOrg = 'jagger424' //bintray注册的用户名
24 | groupId = 'com.jagger' //compile引用时的第1部分groupId
25 | artifactId = 'SmartTouchLayout' //compile引用时的第2部分项目名
26 | publishVersion = '1.0.4' //compile引用时的第3部分版本号
27 | desc = 'This is layout for touch extend FrameLayout '
28 | website = 'https://github.com/evening424/SmartTouchLayout'
29 | }
30 |
31 | buildTypes {
32 | release {
33 | minifyEnabled false
34 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
35 | }
36 | }
37 |
38 | }
39 |
40 | dependencies {
41 | implementation fileTree(dir: 'libs', include: ['*.jar'])
42 |
43 | implementation 'androidx.appcompat:appcompat:1.2.0'
44 | testImplementation 'junit:junit:4.12'
45 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
47 | }
48 |
--------------------------------------------------------------------------------
/smartviewlibrary/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evening424/SmartTouchLayout/36cbf4fe15609ef195a90a3561936e9318d83bc5/smartviewlibrary/consumer-rules.pro
--------------------------------------------------------------------------------
/smartviewlibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/smartviewlibrary/src/androidTest/java/com/jagger/smartviewlibrary/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.jagger.smartviewlibrary;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.jagger.smartviewlibrary.test", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/smartviewlibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/smartviewlibrary/src/main/java/com/jagger/smartviewlibrary/SmartTouchLayout.java:
--------------------------------------------------------------------------------
1 | package com.jagger.smartviewlibrary;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 | import android.annotation.SuppressLint;
6 | import android.app.Activity;
7 | import android.content.Context;
8 | import android.graphics.Color;
9 | import android.os.Build;
10 | import android.os.Handler;
11 | import android.os.Message;
12 | import android.util.AttributeSet;
13 | import android.util.DisplayMetrics;
14 | import android.util.Log;
15 | import android.view.GestureDetector;
16 | import android.view.MotionEvent;
17 | import android.view.ScaleGestureDetector;
18 | import android.view.ViewConfiguration;
19 | import android.view.ViewGroup;
20 | import android.widget.FrameLayout;
21 |
22 | /**
23 | * 手势触控布局, 布局中所有子View跟随布局改变;
24 | * 支持下滑退出,退回上一层位置(前提要把Activity背景设为透明);双指缩放;双击缩放;支持 ViewPage;
25 | * 提供手势开关,事件回调接口
26 | *
27 | * 缺点:
28 | * ViewPage 会截取横移手势,所以在ViewPage中, 初始状态下横向双指缩放会触发ViewPage滑动
29 | *
30 | * @author Jagger 2021-1-12
31 | */
32 | public class SmartTouchLayout extends FrameLayout implements GestureDetector.OnGestureListener {
33 |
34 | private String TAG = "Jagger--->"; //把"//Log."替换为"Log."开打全局日志
35 | private final float MAX_SCALE = 4f;
36 | private final float MIN_SCALE = 0.7f;
37 | private final int DOUBLE_CLICK_TIME_OFFSET = 300; //ms
38 | private final int DRAG_DOWN_EXIT_Y_OFF = 300; //下滑触发关闭Y距离
39 |
40 | //滑动
41 | private int originalLeft, originalRight, originalBottom; // 原始边界
42 | //退出时需要缩放到的位置
43 | private boolean isSetEndViewLocal = false; // 是否指定滑动退出位置
44 | private int endViewWidthSet, endViewHeightSet; //结束动画飞回上一层Activity某个View的大小 设入
45 | private int endViewLeftSet, endViewTopSet; //结束动画飞回上一层Activity某个View的 getLeft,getTop位置 设入
46 | private int endViewLeft, endViewTop; //结束动画飞回上一层Activity某个View的 getLeft,getTop位置 计算后
47 | private float endViewScale;//, endViewScaleY; // 求出上一层Activity某个View的缩放比例
48 | private EndViewScaleSide endViewScaleSide;
49 | public enum EndViewScaleSide {
50 | Width,
51 | Height
52 | }
53 |
54 | private int screenHeight , screenWidth; //设备屏幕高度
55 | private float oldX, oldY; //手机放在屏幕的坐标
56 | private float movX, movY; //移动中在屏幕上的坐标
57 | private float alphaPercent = 1f; //背景颜色透明度
58 | private boolean isFinish = false; //是否执行关闭页面的操作
59 | private onEventListener eventListener = null;
60 | private GestureDetector detector;
61 |
62 | //缩放
63 | //移动前两指直接的距离
64 | private float mCurrentScale = 1f;
65 | private ScaleGestureDetector mScaleDetector;
66 |
67 | //状态
68 | private boolean isMoving = false;
69 | private boolean isZooming = false;
70 | private boolean isLockMoveInZooming = false;
71 |
72 | //开关
73 | private boolean isMoveExitEnable = true;
74 | private boolean isZoomEnable = true;
75 |
76 | //接口
77 | public interface onEventListener {
78 | /**
79 | * 当下滑触发 Activity finish()
80 | */
81 | void onActivityFinish();
82 |
83 | /**
84 | * 当 View 销毁时
85 | */
86 | void onViewDestroy();
87 | }
88 |
89 | public SmartTouchLayout(Context context) {
90 | super(context);
91 | initView(context, null, 0);
92 | }
93 |
94 | public SmartTouchLayout(Context context, AttributeSet attrs) {
95 | super(context, attrs);
96 | initView(context, attrs, 0);
97 | }
98 |
99 | public SmartTouchLayout(Context context, AttributeSet attrs, int defStyleAttr) {
100 | super(context, attrs, defStyleAttr);
101 | initView(context, attrs, defStyleAttr);
102 | }
103 |
104 | private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
105 | screenHeight = getScreenHeight(context);
106 | screenWidth = getScreenWidth(context);
107 | detector = new GestureDetector(context, this);
108 | endViewScaleSide = EndViewScaleSide.Width;
109 | //
110 | setScaleDetector();
111 | }
112 |
113 | /**
114 | * 获取屏幕宽度
115 | *
116 | * @param context
117 | * @return
118 | */
119 | private int getScreenWidth(Context context) {
120 | DisplayMetrics dm = new DisplayMetrics();
121 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
122 | ((Activity) context).getWindowManager().getDefaultDisplay().getRealMetrics(dm);
123 | }else{
124 | ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
125 | }
126 | return dm.widthPixels;
127 | }
128 |
129 | /**
130 | * 获取屏幕高度
131 | * 实际显示区域指定包含系统装饰的内容的显示部分 : getRealSize(Point),getRealMetrics(DisplayMetrics)。
132 | * 应用程序显示区域指定可能包含应用程序窗口的显示部分,不包括系统装饰。 应用程序显示区域可以小于实际显示区域,因为系统减去诸如状态栏之类的装饰元素所需的空间。 使用以下方法查询应用程序显示区域:getSize(Point),getRectSize(Rect)和getMetrics(DisplayMetrics)。
133 | * @param context
134 | * @return
135 | */
136 | private int getScreenHeight(Context context) {
137 | DisplayMetrics dm = new DisplayMetrics();
138 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
139 | ((Activity) context).getWindowManager().getDefaultDisplay().getRealMetrics(dm);
140 | }else{
141 | ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
142 | }
143 | return dm.heightPixels;
144 | }
145 |
146 | //-------------------------------- 公开接口 start --------------------------------
147 | /**
148 | * 设置滑动完成后的回调方法
149 | */
150 | public void setEventListener(onEventListener eventListener) {
151 | this.eventListener = eventListener;
152 | }
153 |
154 | /**
155 | * 设置结束时,动画回到什么位置和大小
156 | * @param w view.getWidth() 结束时的宽
157 | * @param h view.getHeight() 结束时的高
158 | * @param left view location[0] 结束时相对屏幕的X坐标
159 | * @param top view location[1] 结束时相对屏幕的Y坐标
160 | * @param scaleSide 结束时以宽/高拉伸
161 | */
162 | public void setEndViewLocalSize(int w, int h, int left, int top, EndViewScaleSide scaleSide) {
163 | endViewWidthSet = w;
164 | endViewHeightSet = h;
165 | endViewLeftSet = left;
166 | endViewTopSet = top;
167 | isSetEndViewLocal = true;
168 | endViewScaleSide = scaleSide;
169 | }
170 |
171 | public boolean isMoveExitEnable() {
172 | return isMoveExitEnable;
173 | }
174 |
175 | /**
176 | * 能否滑动关闭
177 | * @param moveExitEnable
178 | */
179 | public void setMoveExitEnable(boolean moveExitEnable) {
180 | isMoveExitEnable = moveExitEnable;
181 | }
182 |
183 | public boolean isZoomEnable() {
184 | return isZoomEnable;
185 | }
186 |
187 | /**
188 | * 是否缩放
189 | * @param zoomEnable
190 | */
191 | public void setZoomEnable(boolean zoomEnable) {
192 | isZoomEnable = zoomEnable;
193 | }
194 |
195 | //-------------------------------- 公开接口 end --------------------------------
196 |
197 | //-------------------------------- 触摸事件分发 start --------------------------------
198 |
199 | private static final int TOUCH_TO_CHILDREN = 0;
200 | private static final int TOUCH_MYSELF = 1;
201 | private int mTouchState = TOUCH_TO_CHILDREN;
202 | private int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); // 触发移动的像素距?
203 | private long firstTouchTime = 0;
204 | @Override
205 | public boolean onInterceptTouchEvent(MotionEvent ev) {
206 |
207 | final int action = ev.getAction();
208 | if(action == MotionEvent.ACTION_MOVE && mTouchState == TOUCH_MYSELF){
209 | //Log.i(TAG, "拦截 为自己处理");
210 | return true;
211 | }
212 |
213 | switch (action) {
214 | case MotionEvent.ACTION_DOWN:
215 | //判断单双击
216 | checkClickDown(ev);
217 |
218 | //
219 | oldX = ev.getRawX();
220 | oldY = ev.getRawY();
221 | mTouchState = (isZooming || isMoving) ? TOUCH_MYSELF : TOUCH_TO_CHILDREN;
222 | //Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN oldX:" + oldX + ",mTouchState:" + mTouchState);
223 | break;
224 | case MotionEvent.ACTION_MOVE:
225 | // 是否进行了滑动,设置滑动状态
226 | float tMoveX = ev.getRawX() - oldX;
227 | final float xDiff = Math.abs(tMoveX);
228 |
229 | float tMoveY = ev.getRawY() - oldY;
230 | final float yDiff = Math.abs(tMoveY);
231 |
232 | if (yDiff > mTouchSlop || xDiff > mTouchSlop) {
233 | mTouchState = TOUCH_MYSELF;
234 | }
235 | break;
236 | case MotionEvent.ACTION_CANCEL:
237 | case MotionEvent.ACTION_UP:
238 | mTouchState = TOUCH_TO_CHILDREN;
239 | break;
240 | }
241 |
242 | // origin do
243 | return mTouchState != TOUCH_TO_CHILDREN;
244 | }
245 |
246 | //-------------------------------- 触摸事件分发 end --------------------------------
247 |
248 |
249 | @SuppressLint("ClickableViewAccessibility")
250 | @Override
251 | public boolean onTouchEvent(MotionEvent event) {
252 | // 都不需要直接返回
253 | if(!isZoomEnable && !isMoveExitEnable){
254 | return true;
255 | }
256 |
257 | //可缩放
258 | if(isZoomEnable){
259 | mScaleDetector.onTouchEvent(event);
260 | }
261 |
262 | if (!mScaleDetector.isInProgress()) {
263 | detector.onTouchEvent(event);
264 |
265 | // Log.e(TAG, "onTouchEvent: " + event.getAction());
266 | switch (event.getAction()) {
267 | case MotionEvent.ACTION_DOWN:
268 | oldX = event.getRawX();
269 | oldY = event.getRawY();
270 | //Log.e(TAG, "onTouchEvent ACTION_DOWN oldX:" + oldX);
271 | break;
272 | case MotionEvent.ACTION_MOVE:
273 | isFinish = false;
274 | movX = event.getRawX() - oldX;
275 | movY = event.getRawY() - oldY;
276 | //Log.e(TAG, "onTouchEvent ACTION_MOVE movX:" + movX);
277 | if(isZooming){
278 | //缩放时, 自己处理MOVE事件
279 | getParent().requestDisallowInterceptTouchEvent(true);
280 | }else {
281 | //非缩放时, 由父控件处理MOVE事件
282 | getParent().requestDisallowInterceptTouchEvent(false);
283 |
284 | // 如果父不处理,且不是缩放过程中由双指变成单指手势,则由自己处理MOVE事件
285 | if(!isLockMoveInZooming){
286 | if(!isMoving && movY < 0) {
287 | //不在滑动过程中的向上滑动手势 不接收
288 | break;
289 | }
290 | animMoving(movX, movY);
291 | }
292 | }
293 |
294 | if (Math.abs(movX) > Math.abs(movY)) {
295 | if (movX < 0) {
296 | // //Log.e(TAG, "左滑动");
297 | } else {
298 | // //Log.e(TAG, "右滑动");
299 | }
300 | } else {
301 | if (movY < 0) {
302 | // //Log.e(TAG, "上滑动");
303 | } else {
304 | // //Log.e(TAG, "下滑动");
305 |
306 | //可下滑关闭
307 | if(isMoveExitEnable && !isZooming && !isLockMoveInZooming) {
308 | //下滑超出屏幕多少
309 | // int[] location = new int[2];
310 | // getLocationOnScreen(location);
311 | // //Log.e(TAG, "下滑动:" + location[1] + ",退出界线" + (screenHeight * 6 / 10));
312 | // if (location[1] > (screenHeight * 6 / 10)) {
313 | // isFinish = true;
314 | // }
315 |
316 | //下滑距离
317 | if(movY > DRAG_DOWN_EXIT_Y_OFF){
318 | isFinish = true;
319 | }
320 | }
321 | }
322 | }
323 | break;
324 | case MotionEvent.ACTION_POINTER_2_UP:
325 | case MotionEvent.ACTION_POINTER_2_DOWN:
326 | case MotionEvent.ACTION_POINTER_UP:
327 | //双指状态下, 其中一只手指松开, 不处理滑动
328 | //Log.e(TAG, "onTouchEvent isZooming:" + isZooming);
329 | isLockMoveInZooming = true;
330 | break;
331 | case MotionEvent.ACTION_UP:
332 | // 点击松开 不需要break
333 | checkClickUp(event.getRawX(), event.getRawY());
334 | case MotionEvent.ACTION_CANCEL:
335 | //滑动松开
336 | if (isFinish) {
337 | isFinish = false;
338 | //Log.e(TAG, "页面返回");
339 | animEnding();
340 | } else {
341 |
342 | if(isZooming){
343 | checkBorder();
344 | }else{
345 | animRecovering();
346 | }
347 |
348 | }
349 | isMoving = false;
350 | isLockMoveInZooming = false;
351 | break;
352 | }
353 | }
354 | return true;
355 | }
356 |
357 | //-------------------------------- 动画 start --------------------------------
358 |
359 | /**
360 | * 非缩放状态下,还原位置
361 | */
362 | private void animRecovering() {
363 | // Log.i(TAG , "animRecovering");
364 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
365 | animate().setDuration(200)
366 | .scaleX(1)
367 | .scaleY(1)
368 | .translationX(0) //移到某个坐标 相对于自己原始坐标
369 | .translationY(0)
370 | .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
371 | @Override
372 | public void onAnimationUpdate(ValueAnimator animation) {
373 | if (alphaPercent < animation.getAnimatedFraction()) {
374 | ((ViewGroup) getParent()).setBackgroundColor(convertPercentToBlackAlphaColor(animation.getAnimatedFraction()));
375 | }
376 | }
377 | })
378 | .start();
379 | }else{
380 | animate().setDuration(200)
381 | .scaleX(1)
382 | .scaleY(1)
383 | .translationX(0) //移到某个坐标 相对于自己原始坐标
384 | .translationY(0)
385 | .start();
386 | ((ViewGroup) getParent()).setBackgroundColor(convertPercentToBlackAlphaColor(100));
387 | }
388 | }
389 |
390 | /**
391 | * 非缩放状态下,滑动
392 | * @param deltaX
393 | * @param deltaY
394 | */
395 | private void animMoving(float deltaX, float deltaY) {
396 | if (Math.abs(movY) < (screenHeight / 4)) {
397 | float scale = 1 - Math.abs(movY) / screenHeight;
398 | alphaPercent = 1 - Math.abs(deltaY) / (screenHeight / 2);
399 |
400 | setScaleX(scale);
401 | setScaleY(scale);
402 | ((ViewGroup) getParent()).setBackgroundColor(convertPercentToBlackAlphaColor(alphaPercent));
403 | }
404 | //Log.e(TAG, "setupMoving x:" + deltaX + ",y:" + deltaY);
405 | setTranslationX( deltaX);
406 | setTranslationY( deltaY);
407 | isMoving = true;
408 | }
409 |
410 | /**
411 | * 缩放状态下,回弹到边缘
412 | * @param deltaX
413 | * @param deltaY
414 | */
415 | private void animZoomingMoveToBorder(float deltaX, float deltaY) {
416 | animate().setDuration(200)
417 | .translationX(deltaX) //移到某个坐标 相对于自己原始坐标
418 | .translationY(deltaY)
419 | .start();
420 | }
421 |
422 | /**
423 | * 缩放状态下,回弹到水平居中
424 | */
425 | private void animZoomingMoveToHorizontalCenter(){
426 | moveY4Zooming = 0;
427 | animate().setDuration(200)
428 | .translationY(0)
429 | .start();
430 | }
431 |
432 | private boolean isEndingAnimRunning = false;
433 | /**
434 | * 下滑关闭, 回弹到指定位置
435 | */
436 | private void animEnding() {
437 | if(isEndingAnimRunning){
438 | return;
439 | }
440 |
441 | // Log.i(TAG , "animEnding");
442 | isEndingAnimRunning = true;
443 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
444 | animate().setDuration(250)
445 | .scaleX(endViewScale)
446 | .scaleY(endViewScale)
447 | .translationX(endViewLeft) //移到某个坐标
448 | .translationY(endViewTop)
449 | .alpha(30)
450 | .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
451 | @Override
452 | public void onAnimationUpdate(ValueAnimator animation) {
453 | if (0 < animation.getAnimatedFraction() || animation.getAnimatedFraction() < 1) {
454 | float endAlphaPercent = (1 -animation.getAnimatedFraction());
455 | if(endAlphaPercent < alphaPercent){
456 | ((ViewGroup) getParent()).setBackgroundColor(convertPercentToBlackAlphaColor(endAlphaPercent));
457 | }
458 | }
459 | }
460 | })
461 | .setListener(new Animator.AnimatorListener() {
462 | @Override
463 | public void onAnimationStart(Animator animator) {
464 |
465 | }
466 |
467 | @Override
468 | public void onAnimationEnd(Animator animator) {
469 | onDestroy();
470 | finishActivity();
471 | }
472 |
473 | @Override
474 | public void onAnimationCancel(Animator animator) {
475 |
476 | }
477 |
478 | @Override
479 | public void onAnimationRepeat(Animator animator) {
480 |
481 | }
482 | })
483 | .start();
484 | }else{
485 | animate().setDuration(1000)
486 | .scaleX(endViewScale)
487 | .scaleY(endViewScale)
488 | .translationX(endViewLeft) //移到某个坐标
489 | .translationY(endViewTop)
490 | .alpha(30)
491 | .setListener(new Animator.AnimatorListener() {
492 | @Override
493 | public void onAnimationStart(Animator animator) {
494 |
495 | }
496 |
497 | @Override
498 | public void onAnimationEnd(Animator animator) {
499 | onDestroy();
500 | finishActivity();
501 | }
502 |
503 | @Override
504 | public void onAnimationCancel(Animator animator) {
505 |
506 | }
507 |
508 | @Override
509 | public void onAnimationRepeat(Animator animator) {
510 |
511 | }
512 | })
513 | .start();
514 | }
515 | }
516 |
517 | /**
518 | * 双击,放大动画
519 | */
520 | private void animZoomingDoubleClick() {
521 | animate().setDuration(200)
522 | .scaleX(mCurrentScale)
523 | .scaleY(mCurrentScale)
524 | .translationX(moveX4Zooming) //移到某个坐标 相对于自己原始坐标
525 | .translationY(moveY4Zooming)
526 | .start();
527 | }
528 |
529 | //-------------------------------- 动画 end --------------------------------
530 |
531 | /**
532 | * 设置背景颜色透明度
533 | */
534 | protected int convertPercentToBlackAlphaColor(float percent) {
535 | percent = Math.min(1, Math.max(0, percent));
536 | int intAlpha = (int) (percent * 255);
537 | String stringAlpha = Integer.toHexString(intAlpha).toLowerCase();
538 | String color = "#" + (stringAlpha.length() < 2 ? "0" : "") + stringAlpha + "000000";
539 | return Color.parseColor(color);
540 | }
541 |
542 | //-------------------------------- 缩放处理 start --------------------------------
543 |
544 | /**
545 | * 设入缩放Detector
546 | */
547 | private void setScaleDetector(){
548 | ScaleGestureDetector.OnScaleGestureListener scaleListener = new ScaleGestureDetector
549 | .SimpleOnScaleGestureListener() {
550 |
551 | @Override
552 | public boolean onScale(ScaleGestureDetector detector) {
553 | float scaleFactor = detector.getScaleFactor();
554 |
555 | if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
556 | return false;
557 |
558 | //双指缩放中
559 | isZooming = true;
560 | mCurrentScale *= scaleFactor;
561 | if(mCurrentScale < MIN_SCALE){
562 | mCurrentScale = MIN_SCALE;
563 | }
564 | setScaleX(mCurrentScale);
565 | setScaleY(mCurrentScale);
566 |
567 | //返回 true 会一闪一闪的
568 | return false;
569 | }
570 |
571 | @Override
572 | public void onScaleEnd(ScaleGestureDetector detector) {
573 | super.onScaleEnd(detector);
574 |
575 | //Log.i(TAG, "onScaleEnd" );
576 | scaleEnd();
577 | }
578 |
579 | };
580 | mScaleDetector = new ScaleGestureDetector(getContext(), scaleListener);
581 | }
582 |
583 | /**
584 | *
585 | */
586 | private void scaleEnd(){
587 | if (mCurrentScale <= 1f) {
588 | reset();
589 | isZooming = false;
590 | }else if (mCurrentScale > MAX_SCALE) {
591 | setMaxSize();
592 | }
593 | }
594 |
595 | /**
596 | * 缩小过于小时,回复原来大小
597 | */
598 | private void reset() {
599 | mCurrentScale = 1f;
600 | setScaleX(mCurrentScale);
601 | setScaleY(mCurrentScale);
602 | isZooming = false;
603 | }
604 |
605 | /**
606 | * 放大过于大时,回复最大比例
607 | */
608 | private void setMaxSize() {
609 | mCurrentScale = MAX_SCALE;
610 | setScaleX(mCurrentScale);
611 | setScaleY(mCurrentScale);
612 | }
613 |
614 | /**
615 | * 检查边界是否移到边界以内
616 | */
617 | private void checkBorder() {
618 | int[]location = new int[2];
619 | getLocationOnScreen(location);
620 | //Log.i(TAG, "checkBorder 当前X:" + location[0] + ",移动过的X:" + moveX4Zooming);
621 |
622 | float overRightOffset = location[0] - (mCurrentScale*getWidth() - originalRight)*-1;
623 | float overBottomOffset = location[1] - (mCurrentScale*getHeight() - originalBottom)*-1;
624 |
625 | if(location[0] > 0){
626 | //是否越入左边界
627 | moveX4Zooming = ((1-mCurrentScale) * getWidth())/2 * -1; //放大后相对于 原来大小的 X 坐标的偏移
628 | //是否越入上,下边界
629 | checkTopBottomBorder(location[1], overBottomOffset);
630 | animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming);
631 | }else if(overRightOffset < 0){
632 | //是否越入右边界
633 | moveX4Zooming += Math.abs(overRightOffset); //放大前的 X=0
634 | //是否越入上,下边界
635 | checkTopBottomBorder(location[1], overBottomOffset);
636 | animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming);
637 | }else{
638 | //是否越入上,下边界
639 | checkTopBottomBorder(location[1], overBottomOffset);
640 | animZoomingMoveToBorder(moveX4Zooming, moveY4Zooming);
641 | }
642 | }
643 |
644 | /**
645 | * 是否越入上,下边界
646 | * @param top
647 | * @param bottomOffset
648 | */
649 | private void checkTopBottomBorder(float top, float bottomOffset) {
650 | //是否越入右、上边界
651 | if(top > 0){
652 | //是否越入上边界
653 | moveY4Zooming = ((1-mCurrentScale) * getHeight())/2 * -1; //放大后相对于 原来大小的 Y 坐标的偏移
654 | }else if(bottomOffset < 0){
655 | //是否越入下边界
656 | moveY4Zooming += Math.abs(bottomOffset); //放大前的 Y=0
657 | }
658 | }
659 |
660 | //缩放中时移动的X,Y坐标跟随缩放比例改变,跟非缩放状态的movX, movY分开处理
661 | private float moveX4Zooming = 0 , moveY4Zooming = 0;
662 | @Override
663 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
664 | if(isZooming && !isLockMoveInZooming){
665 | //缩放中的滑动处理
666 | if(e1 != null && e2 != null){
667 | //Log.i(TAG, "onScroll e1 X:" + e1.getX() + ",e2 X:" + e2.getX());
668 | //Log.i(TAG, "onScroll e1 Y:" + e1.getY() + ",e2 Y:" + e2.getY());
669 | this.moveX4Zooming = this.moveX4Zooming - (e1.getX()-e2.getX()); //e1.getX() 相对于VIEW自身的坐标系的X坐标
670 | this.moveY4Zooming = this.moveY4Zooming - (e1.getY()-e2.getY());
671 | //Log.i(TAG, "onScroll dx x:" + moveX4Zooming + ",dy:" + moveY4Zooming);
672 | setTranslationX(moveX4Zooming);
673 | setTranslationY(moveY4Zooming);
674 | }
675 | }
676 | return true;
677 | }
678 |
679 | //-------------------------------- 缩放处理 end --------------------------------
680 |
681 |
682 | //-------------------------------- 单,双击处理 start --------------------------------
683 |
684 | //BUG:如果手指快速滑动,会判断为单击事件
685 |
686 | private float firstClickX, firstClickY, secondClickX, secondClickY;
687 | // 统计?ms内的点击次数
688 | private TouchEventCountThread mInTouchEventCount = new TouchEventCountThread();
689 | // 根据TouchEventCountThread统计到的点击次数, perform单击还是双击事件
690 | private TouchEventHandler mTouchEventHandler = new TouchEventHandler();
691 |
692 | private void checkClickDown(MotionEvent ev){
693 | if (0 == mInTouchEventCount.touchCount) { // 第一次按下时,开始统计
694 | //Log.i(TAG , "checkClickDown 第一次按下时,开始统计" );
695 | postDelayed(mInTouchEventCount, DOUBLE_CLICK_TIME_OFFSET);
696 | }
697 | }
698 |
699 | private void checkClickUp(float clickX, float clickY){
700 | // Log.i(TAG , "checkClickUp clickX:" + clickX + ",clickY:" + clickY);
701 | // 一次点击事件要有按下和抬起, 有抬起必有按下, 所以只需要在ACTION_UP中处理
702 | if (!mInTouchEventCount.isLongClick) {
703 |
704 | // 累加点击数
705 | mInTouchEventCount.touchCount++;
706 |
707 | if(mInTouchEventCount.touchCount == 1){
708 | firstClickX = clickX;
709 | firstClickY = clickY;
710 | // Log.i(TAG , "checkClickUp 点击第一下");
711 | }else if(mInTouchEventCount.touchCount == 2){
712 | secondClickX = clickX;
713 | secondClickY = clickY;
714 |
715 | float xOff = Math.abs(firstClickX - secondClickX);
716 | float yOff = Math.abs(firstClickY - secondClickY);
717 | //两次点击距离相近
718 | if(xOff < 60 && yOff < 60 ){
719 | //Double click 成立
720 | // Log.i(TAG , "checkClickUp Double click 成立");
721 | }else{
722 | //Double click 不成立,当单击处理
723 | mInTouchEventCount.touchCount = 1;
724 | // Log.i(TAG , "checkClickUp Double click 不成立,当单击处理");
725 | }
726 | }else{
727 | mInTouchEventCount.touchCount = 0;
728 | // Log.i(TAG , "checkClickUp 复原");
729 | }
730 | }else {
731 | // 长按复原
732 | mInTouchEventCount.isLongClick = false;
733 | // Log.i(TAG , "checkClickUp 长按复原");
734 | }
735 | }
736 |
737 | private class TouchEventCountThread implements Runnable {
738 | public int touchCount = 0;
739 | public boolean isLongClick = false;
740 |
741 | @Override
742 | public void run() {
743 | Message msg = new Message();
744 | if(0 == touchCount){ // long click
745 | isLongClick = true;
746 | } else {
747 | msg.arg1 = touchCount;
748 | mTouchEventHandler.sendMessage(msg);
749 | touchCount = 0;
750 | }
751 |
752 | // Log.i(TAG , "TouchEventCountThread 结束:" + touchCount);
753 | }
754 | }
755 |
756 | private class TouchEventHandler extends Handler {
757 | @Override
758 | public void handleMessage(Message msg) {
759 | // Log.i(TAG, "touch " + msg.arg1 + " time.");
760 | if(msg.arg1 == 1){
761 | onSingleClicked(oldX, oldY);
762 | }else{
763 | onDoubleClicked(oldX, oldY);
764 | }
765 | }
766 | }
767 |
768 | /**
769 | * 单击事件
770 | * @param clickX
771 | * @param clickY
772 | */
773 | private void onSingleClicked(float clickX, float clickY){
774 | // Log.i(TAG , "onSingleClicked isZooming:" + isZooming + ",isLockMoveInZooming:" + isLockMoveInZooming);
775 | if(!isZooming && !isLockMoveInZooming){
776 | // isFinish = true;
777 | animEnding();
778 | }
779 | }
780 |
781 | /**
782 | * 双击事件
783 | * @param clickX
784 | * @param clickY
785 | */
786 | private void onDoubleClicked(float clickX, float clickY){
787 | if(mCurrentScale < MAX_SCALE){
788 | mCurrentScale ++;
789 | if(mCurrentScale > MAX_SCALE){
790 | mCurrentScale = MAX_SCALE;
791 | }
792 | }else{
793 | mCurrentScale = 1.0f;
794 | }
795 | isZooming = true;
796 |
797 | if (mCurrentScale > 1) {
798 | this.moveX4Zooming = this.moveX4Zooming - (clickX - getWidth() / 2f); //双击VIEW中的X与VIEW中点的距离
799 | this.moveY4Zooming = this.moveY4Zooming - (clickY - getHeight() / 2f);
800 | // this.moveY4Zooming = 0;
801 | //Logd(TAG, "onDoubleClicked--- clickX:" + clickX + ",getWidth()/2f:" + getWidth()/2f + ",moveX4Zooming:" + moveX4Zooming);
802 | animZoomingDoubleClick();
803 | } else {
804 | this.moveX4Zooming = 0;
805 | this.moveY4Zooming = 0;
806 | animZoomingDoubleClick();
807 | isZooming = false;
808 | }
809 | }
810 |
811 | //-------------------------------- 单,双击处理 end --------------------------------
812 | /**
813 | * 通知销毁VIEW
814 | */
815 | private void onDestroy(){
816 | if (eventListener != null) {
817 | eventListener.onViewDestroy();
818 | }
819 | }
820 |
821 | /**
822 | * 退出Activity
823 | */
824 | private void finishActivity(){
825 | if(eventListener != null){
826 | eventListener.onActivityFinish();
827 | }
828 | ((Activity)(getContext())).finish();
829 | ((Activity)(getContext())).overridePendingTransition(0,0);
830 | }
831 |
832 | @Override
833 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
834 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
835 |
836 | ((ViewGroup) getParent()).setBackgroundColor(convertPercentToBlackAlphaColor(100));
837 |
838 | // 如果不指定滑动退出位置则缩小为原来的70%滑到屏幕底部退出
839 | if(!isSetEndViewLocal){
840 | endViewWidthSet = (int)(getMeasuredWidth() * 0.7);
841 | endViewHeightSet = (int)(getMeasuredHeight() * 0.7);
842 | }
843 |
844 | //计算结束动画缩放比例
845 | // endViewScale = endViewWidthSet*1.0f / getMeasuredWidth();
846 | // endViewScaleY = endViewHeightSet*1.0f / getMeasuredHeight();
847 | if(endViewScaleSide == EndViewScaleSide.Width){
848 | endViewScale = endViewWidthSet*1.0f / getMeasuredWidth();
849 | }else{
850 | endViewScale = endViewHeightSet*1.0f / getMeasuredHeight();
851 | }
852 |
853 | //Log.d(TAG, "getMeasuredWidth:" + getMeasuredWidth() + ",getMeasuredHeight:" + getMeasuredHeight());
854 | //Log.d(TAG, "endViewScaleX:" + endViewScaleX + ",endViewScaleY:" + endViewScaleY);
855 | }
856 |
857 | @Override
858 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
859 | super.onLayout(changed, left, top, right, bottom);
860 | //当前VIEW处于屏幕坐标(如果在ViewPage中使用,X则处于屏幕外)
861 | int[]location = new int[2];
862 | getLocationOnScreen(location);
863 | // Log.d(TAG, "onLayout getX:" + location[0] + ",getY:" + location[1] + ",top:" + top);
864 |
865 | //自己处于屏幕的Y - 自己处于Activity的Y,计算出Activity左上角Y坐标,不会因为是否有TitleBar、ActionBar以致结束的位置不准
866 | int activityYOff = Math.abs(location[1] - top) ;
867 |
868 | //相对于Activity的坐标
869 | //Logd(TAG, "-------- left:" + left + ",top:" + top + ",right:" + right);
870 | originalLeft = left;
871 | originalRight = right;
872 | originalBottom = bottom;
873 |
874 | // 如果不指定滑动退出位置则缩小为原来的70%滑到屏幕底部居垂直中位置退出
875 | if(!isSetEndViewLocal){
876 | endViewLeftSet = screenWidth/2 - endViewWidthSet/2;
877 | endViewTopSet = screenHeight;
878 | }
879 |
880 | //计算结束的VIEW相对于当前VIEW的位置偏移
881 | //Logd(TAG, "onLayout endViewX1:" + endViewLeftSet + ",endViewY1:" + endViewTopSet);
882 | endViewLeft = endViewLeftSet - left;
883 | endViewTop = endViewTopSet - top;
884 | //Logd(TAG, "onLayout endViewX2:" + endViewLeft + ",endViewY2:" + endViewTop);
885 |
886 | // 减去缩放后长宽 (左上角重合)
887 | endViewLeft -= ((1- endViewScale) * getWidth())/2;
888 | endViewTop -= ((1-endViewScale) * getHeight())/2;
889 | //Logd(TAG, "onLayout endViewX3:" + endViewLeft + ",endViewY3:" + endViewTop);
890 |
891 | // 再减去缩放后长宽差 (中心点重合)
892 | endViewLeft -= ((endViewScale * getWidth()) - endViewWidthSet) /2 ;
893 | endViewTop -= ((endViewScale * getHeight()) - endViewHeightSet) /2 ;
894 |
895 | // 再减去Activity的Y相对于屏幕的偏移
896 | endViewTop -= activityYOff;
897 | }
898 |
899 | @Override
900 | public boolean onDown(MotionEvent e) {
901 | return false;
902 | }
903 |
904 | @Override
905 | public void onShowPress(MotionEvent e) {
906 |
907 | }
908 |
909 | @Override
910 | public boolean onSingleTapUp(MotionEvent e) {
911 | // //Log.i(TAG, "onSingleTapUp isDoubleClickedZooming:" + isDoubleClickedZooming + ",isZooming:" + isZooming);
912 | return false;
913 | }
914 |
915 | @Override
916 | public void onLongPress(MotionEvent e) {
917 |
918 | }
919 |
920 | @Override
921 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
922 | return true;
923 | }
924 |
925 | }
926 |
--------------------------------------------------------------------------------
/smartviewlibrary/src/test/java/com/jagger/smartviewlibrary/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.jagger.smartviewlibrary;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------