├── .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 | ![](https://github.com/evening424/resource/blob/master/Gif/Video_20210129_055202_927.gif) 30 |
31 | 双指、双击缩放;下滑退出到指定位置; 32 |
33 |
34 | 35 | ![](https://github.com/evening424/resource/blob/master/Gif/Video_20210129_055219_439.gif) 36 |
37 | 不影响子控件事件;不指定位置时,下滑到底部消失; 38 |
39 |
40 | 41 | ![](https://github.com/evening424/resource/blob/master/Gif/Video_20210129_055226_655.gif) 42 |
43 | 与ViewPage不冲突; 44 |
45 |
46 |
47 | 48 | \----------------------------------------\ 49 | 美图看完了,可否打赏个 ⭐️ 50 |
51 | \----------------------------------------\ 52 |
53 |
54 | 55 | 56 | 下载体验 57 | ----------------- 58 |
59 | 60 | ![](https://github.com/evening424/resource/blob/master/images/SmartTouchLayout_Demo_v1.0.4_download.png?raw=true) 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 | ![](https://github.com/evening424/resource/blob/master/images/WechatIMG1.jpeg) 331 | 332 | -------------------------------------------------------------------------------- /SmartTouchLayout-master.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 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 |