├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
└── markdown-navigator.xml
├── LICENSE
├── README.md
├── YCSlideLib
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── yc
│ │ └── slide
│ │ ├── LoggerUtils.java
│ │ ├── SlideAnimLayout.java
│ │ ├── SlideLayout.java
│ │ └── VerticalScrollView.java
│ └── res
│ └── values
│ ├── attrs.xml
│ └── strings.xml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── yc
│ │ └── ycshopdetaillayout
│ │ ├── FiveActivity.java
│ │ ├── MainActivity.java
│ │ ├── VerticalRecyclerView.java
│ │ ├── VerticalWebView.java
│ │ ├── first
│ │ ├── FirstActivity.java
│ │ ├── ShopDetailFragment.java
│ │ └── ShopMainFragment.java
│ │ ├── four
│ │ └── FourActivity.java
│ │ ├── second
│ │ ├── SecondActivity.java
│ │ └── ShopMain1Fragment.java
│ │ └── third
│ │ ├── NoScrollWebView.java
│ │ └── ThirdActivity.java
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_first.xml
│ ├── activity_four.xml
│ ├── activity_main.xml
│ ├── activity_second.xml
│ ├── activity_third.xml
│ ├── include_goods_details_bar.xml
│ ├── include_shop_detail.xml
│ ├── include_shop_detail3.xml
│ ├── include_shop_main.xml
│ └── shop_main.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
│ ├── icon_detail_page.png
│ ├── icon_detail_page_100_true.png
│ ├── icon_detail_page_network_payment.png
│ ├── icon_detail_page_one_to_ten.png
│ └── icon_details_page_down_loading.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── image
├── 1.jpg
├── 2.jpg
├── 3.jpg
├── app-debug.apk
└── slide.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.ap_
3 |
4 | # Files for the ART/Dalvik VM
5 | *.dex
6 |
7 | # Java class files
8 | *.class
9 |
10 | # Generated files
11 | bin/
12 | gen/
13 | out/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio Navigation editor temp files
29 | .navigation/
30 |
31 | # Android Studio captures folder
32 | captures/
33 |
34 | # IntelliJ
35 | *.iml
36 | .idea/workspace.xml
37 | .idea/tasks.xml
38 | .idea/gradle.xml
39 | .idea/assetWizardSettings.xml
40 | .idea/dictionaries
41 | .idea/libraries
42 | .idea/caches
43 |
44 | # Keystore files
45 | # Uncomment the following line if you do not want to check your keystore files in.
46 | #*.jks
47 |
48 | # External native build folder generated in Android Studio 2.2 and later
49 | .externalNativeBuild
50 |
51 | # Google Services (e.g. APIs or Firebase)
52 | google-services.json
53 |
54 | # Freeline
55 | freeline.py
56 | freeline/
57 | freeline_project_description.json
58 |
59 | # fastlane
60 | fastlane/report.xml
61 | fastlane/Preview.html
62 | fastlane/screenshots
63 | fastlane/test_output
64 | fastlane/readme.md
65 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
7 | * @author yangchong 8 | * blog : https://github.com/yangchong211/YCShopDetailLayout 9 | * time : 2018/6/6 10 | * desc : 日志 11 | * revise: 12 | *13 | */ 14 | public class LoggerUtils { 15 | 16 | private static boolean isLog = false; 17 | 18 | public static void setIsLog(boolean isLog) { 19 | LoggerUtils.isLog = isLog; 20 | } 21 | 22 | public static void i(String string){ 23 | if (isLog){ 24 | Log.i("LoggerUtils",string); 25 | } 26 | } 27 | 28 | 29 | public static void d(String string){ 30 | if (isLog){ 31 | Log.d("LoggerUtils",string); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /YCSlideLib/src/main/java/com/yc/slide/SlideAnimLayout.java: -------------------------------------------------------------------------------- 1 | package com.yc.slide; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ValueAnimator; 6 | import android.annotation.SuppressLint; 7 | import android.content.Context; 8 | import android.content.res.TypedArray; 9 | import android.os.Parcel; 10 | import android.os.Parcelable; 11 | import android.util.AttributeSet; 12 | import android.view.MotionEvent; 13 | import android.view.View; 14 | import android.view.ViewConfiguration; 15 | import android.view.ViewGroup; 16 | import android.widget.AbsListView; 17 | import android.widget.FrameLayout; 18 | import android.widget.LinearLayout; 19 | import android.widget.RelativeLayout; 20 | import androidx.core.view.ViewCompat; 21 | 22 | /** 23 | *
24 | * @author yangchong 25 | * blog : https://github.com/yangchong211/YCShopDetailLayout 26 | * time : 2018/6/6 27 | * desc : SlideAnimLayout 28 | * revise: 29 | *30 | */ 31 | public class SlideAnimLayout extends ViewGroup { 32 | 33 | private ValueAnimator animator; 34 | 35 | public enum Status { 36 | /** 37 | * 关闭 38 | */ 39 | CLOSE, 40 | /** 41 | * 打开 42 | */ 43 | OPEN; 44 | public static Status valueOf(int stats) { 45 | if (0 == stats) { 46 | return CLOSE; 47 | } else if (1 == stats) { 48 | return OPEN; 49 | } else { 50 | return CLOSE; 51 | } 52 | } 53 | } 54 | 55 | private static final int DEFAULT_DURATION = 300; 56 | private View mFrontView; 57 | private View mAnimView; 58 | private View mBehindView; 59 | 60 | private float mTouchSlop; 61 | private float mInitMotionY; 62 | private float mInitMotionX; 63 | 64 | 65 | private View mTarget; 66 | private float mSlideOffset; 67 | private Status mStatus = Status.CLOSE; 68 | private boolean isFirstShowBehindView = true; 69 | private long mDuration = DEFAULT_DURATION; 70 | private int mDefaultPanel = 0; 71 | private int animHeight; 72 | 73 | 74 | public SlideAnimLayout(Context context) { 75 | this(context, null); 76 | } 77 | 78 | public SlideAnimLayout(Context context, AttributeSet attrs) { 79 | this(context, attrs, 0); 80 | } 81 | 82 | public SlideAnimLayout(Context context, AttributeSet attrs, int defStyleAttr) { 83 | super(context, attrs, defStyleAttr); 84 | @SuppressLint("CustomViewStyleable") 85 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideLayout, defStyleAttr, 0); 86 | mDuration = a.getInt(R.styleable.SlideLayout_duration, DEFAULT_DURATION); 87 | mDefaultPanel = a.getInt(R.styleable.SlideLayout_default_panel, 0); 88 | a.recycle(); 89 | //在我们认为用户正在滚动之前,触摸可以移动的像素距离 90 | mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 91 | } 92 | 93 | 94 | /** 95 | * 打开商详页 96 | * @param smooth 97 | */ 98 | public void smoothOpen(boolean smooth) { 99 | if (mStatus != Status.OPEN) { 100 | mStatus = Status.OPEN; 101 | //控件的高度+动画布局 102 | final float height = -getMeasuredHeight() - animHeight; 103 | LoggerUtils.i("SlideLayout---smoothOpen---"+height); 104 | animatorSwitch(0, height, true, smooth ? mDuration : 0); 105 | } 106 | } 107 | 108 | 109 | /** 110 | * 关闭商详页 111 | * @param smooth 112 | */ 113 | public void smoothClose(boolean smooth) { 114 | if (mStatus != Status.CLOSE) { 115 | mStatus = Status.CLOSE; 116 | final float height = -getMeasuredHeight(); 117 | LoggerUtils.i("SlideLayout---smoothClose---"+height); 118 | animatorSwitch(height, 0, true, smooth ? mDuration : 0); 119 | } 120 | } 121 | 122 | 123 | /** 124 | * 这个方法是在结束inflate之后才会执行 125 | */ 126 | @Override 127 | protected void onFinishInflate() { 128 | super.onFinishInflate(); 129 | final int childCount = getChildCount(); 130 | if (1 >= childCount) { 131 | throw new RuntimeException("SlideDetailsLayout only accept childs more than 1!!"); 132 | } 133 | LoggerUtils.i("获取子节点的个数"+childCount); 134 | mFrontView = getChildAt(0); 135 | mAnimView = getChildAt(1); 136 | mBehindView = getChildAt(2); 137 | mAnimView.post(new Runnable() { 138 | @Override 139 | public void run() { 140 | animHeight = mAnimView.getHeight(); 141 | LoggerUtils.i("获取控件高度"+animHeight); 142 | } 143 | }); 144 | if(mDefaultPanel == 1){ 145 | post(new Runnable() { 146 | @Override 147 | public void run() { 148 | //默认效果 149 | smoothOpen(false); 150 | } 151 | }); 152 | } 153 | } 154 | 155 | @Override 156 | protected void onDetachedFromWindow() { 157 | super.onDetachedFromWindow(); 158 | setScrollStatusListener(null); 159 | setOnSlideStatusListener(null); 160 | if (animator!=null){ 161 | animator.cancel(); 162 | animator = null; 163 | } 164 | } 165 | 166 | @Override 167 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 168 | //super.onMeasure(widthMeasureSpec, heightMeasureSpec); 169 | //获取宽高 170 | final int pWidth = MeasureSpec.getSize(widthMeasureSpec); 171 | final int pHeight = MeasureSpec.getSize(heightMeasureSpec); 172 | final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(pWidth, MeasureSpec.EXACTLY); 173 | final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(pHeight, MeasureSpec.EXACTLY); 174 | View child; 175 | for (int i = 0; i < getChildCount(); i++) { 176 | child = getChildAt(i); 177 | //当控件是Gone的时候,不进行测量 178 | if (child.getVisibility() == View.GONE) { 179 | continue; 180 | } 181 | //当孩子控件是动画控件时,则特殊处理 182 | if(getChildAt(i) == mAnimView){ 183 | child.measure(0,0); 184 | int measuredHeight = child.getMeasuredHeight(); 185 | int makeMeasureSpec = MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY); 186 | LoggerUtils.i("onMeasure获取控件高度"+measuredHeight); 187 | measureChild(child, childWidthMeasureSpec, makeMeasureSpec); 188 | } else{ 189 | measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec); 190 | } 191 | } 192 | setMeasuredDimension(pWidth, pHeight); 193 | } 194 | 195 | @Override 196 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 197 | int top; 198 | int bottom; 199 | final int offset = (int) mSlideOffset; 200 | View child; 201 | for (int i = 0; i < getChildCount(); i++) { 202 | child = getChildAt(i); 203 | if (child.getVisibility() == GONE) { 204 | continue; 205 | } 206 | LoggerUtils.i("onLayout,offset---"+offset); 207 | int measuredHeight = getChildAt(1).getMeasuredHeight(); 208 | if (child == mBehindView) { 209 | top = b + offset + measuredHeight ; 210 | bottom = top + b - t + measuredHeight; 211 | LoggerUtils.i("onLayout,mBehindView---"+top+"-----"+bottom); 212 | }else if(child == mAnimView){ 213 | top = b + offset; 214 | bottom = top - t + child.getMeasuredHeight(); 215 | LoggerUtils.i("onLayout,mAnimView---"+top+"-----"+bottom); 216 | } else { 217 | top = t + offset; 218 | bottom = b + offset; 219 | LoggerUtils.i("onLayout,other---"+top+"-----"+bottom); 220 | } 221 | child.layout(l, top, r, bottom); 222 | } 223 | } 224 | 225 | 226 | /** 227 | * 分发事件:事件分发的对象是事件。注意,事件分发是向下传递的,也就是父到子的顺序。 228 | * 根据内部拦截状态,向其child或者自己分发事件 229 | * 230 | * @param ev 231 | * @return 232 | */ 233 | @Override 234 | public boolean onInterceptTouchEvent(MotionEvent ev) { 235 | ensureTarget(); 236 | if (null == mTarget) { 237 | return false; 238 | } 239 | if (!isEnabled()) { 240 | return false; 241 | } 242 | final int action = ev.getAction(); 243 | boolean shouldIntercept = false; 244 | switch (action) { 245 | case MotionEvent.ACTION_DOWN: 246 | mInitMotionX = ev.getX(); 247 | mInitMotionY = ev.getY(); 248 | shouldIntercept = false; 249 | break; 250 | case MotionEvent.ACTION_MOVE: 251 | final float x = ev.getX(); 252 | final float y = ev.getY(); 253 | final float xDiff = x - mInitMotionX; 254 | final float yDiff = y - mInitMotionY; 255 | boolean close = mStatus == Status.CLOSE && yDiff > 0; 256 | boolean open = mStatus == Status.OPEN && yDiff < 0; 257 | if (!canChildScrollVertically((int) yDiff)) { 258 | final float xDiffers = Math.abs(xDiff); 259 | final float yDiffers = Math.abs(yDiff); 260 | if (yDiffers > mTouchSlop && yDiffers >= xDiffers && !(close || open)) { 261 | shouldIntercept = true; 262 | } 263 | } 264 | break; 265 | case MotionEvent.ACTION_UP: 266 | case MotionEvent.ACTION_CANCEL: 267 | break; 268 | default: 269 | break; 270 | } 271 | return shouldIntercept; 272 | } 273 | 274 | @Override 275 | public boolean onTouchEvent(MotionEvent ev) { 276 | ensureTarget(); 277 | if (null == mTarget) { 278 | return false; 279 | } 280 | if (!isEnabled()) { 281 | return false; 282 | } 283 | boolean wantTouch = true; 284 | final int action = ev.getAction(); 285 | switch (action) { 286 | case MotionEvent.ACTION_DOWN: 287 | break; 288 | case MotionEvent.ACTION_MOVE: { 289 | //获取滑动点y轴的位移 290 | final float y = ev.getY(); 291 | final float yDiff = y - mInitMotionY; 292 | boolean childScrollVertically = canChildScrollVertically(((int) yDiff)); 293 | //在关闭状态并且滑动位移小于等于0时 294 | boolean isDiffZero = yDiff<=0 && Status.OPEN == mStatus; 295 | boolean isAnimOpen = Status.OPEN == mStatus && yDiff>=animHeight; 296 | boolean isAnimClose = Status.CLOSE == mStatus && Math.abs(yDiff)>=animHeight; 297 | if (childScrollVertically || isDiffZero) { 298 | wantTouch = false; 299 | }else if(isAnimOpen|| isAnimClose){ 300 | wantTouch = true; 301 | } else { 302 | processTouchEvent(yDiff); 303 | wantTouch = true; 304 | } 305 | break; 306 | } 307 | case MotionEvent.ACTION_UP: 308 | case MotionEvent.ACTION_CANCEL: { 309 | finishTouchEvent(); 310 | wantTouch = false; 311 | break; 312 | } 313 | default: 314 | break; 315 | } 316 | return wantTouch; 317 | } 318 | 319 | 320 | /** 321 | * 设置方法是触摸滑动的时候 322 | * @param offset offset 323 | */ 324 | private void processTouchEvent(final float offset) { 325 | if (Math.abs(offset) < mTouchSlop) { 326 | return; 327 | } 328 | final float oldOffset = mSlideOffset; 329 | if (mStatus == Status.CLOSE) { 330 | if (offset >= 0) { 331 | mSlideOffset = 0; 332 | } else { 333 | mSlideOffset = offset; 334 | } 335 | if (mSlideOffset == oldOffset) { 336 | return; 337 | } 338 | } else if (mStatus == Status.OPEN) { 339 | final float pHeight = -getMeasuredHeight(); 340 | if (offset <= 0) { 341 | mSlideOffset = pHeight; 342 | } else { 343 | mSlideOffset = pHeight- animHeight + offset; 344 | } 345 | if (mSlideOffset == oldOffset) { 346 | return; 347 | } 348 | } 349 | 350 | if (Status.CLOSE == mStatus) { 351 | if (offset <= -animHeight/2) { 352 | LoggerUtils.i("准备翻下页,已超过一半"); 353 | if(listener!=null){ 354 | listener.onStatusChanged(mStatus, true); 355 | } 356 | } else { 357 | LoggerUtils.i("准备翻下页,不超过一半"); 358 | if(listener!=null){ 359 | listener.onStatusChanged(mStatus, false); 360 | } 361 | } 362 | } else if (Status.OPEN == mStatus) { 363 | if ((offset ) >= animHeight/2) { 364 | if(listener!=null){ 365 | listener.onStatusChanged(mStatus, false); 366 | } 367 | LoggerUtils.i("准备翻上页,已超过一半:offset:"+offset+"--->pHeight:"+"--->:"+animHeight); 368 | } else { 369 | if(listener!=null){ 370 | listener.onStatusChanged(mStatus, true); 371 | } 372 | LoggerUtils.i("准备翻上页,不超过一半"+offset+"--->pHeight:"+"--->:"+animHeight); 373 | } 374 | } 375 | requestLayout(); 376 | } 377 | 378 | 379 | /** 380 | * 结束触摸 381 | */ 382 | private void finishTouchEvent() { 383 | final int pHeight = getMeasuredHeight(); 384 | LoggerUtils.i("finishTouchEvent------pHeight---"+pHeight); 385 | final float offset = mSlideOffset; 386 | boolean changed = false; 387 | if (Status.CLOSE == mStatus) { 388 | if (offset <= -animHeight /2) { 389 | mSlideOffset = -pHeight - animHeight; 390 | mStatus = Status.OPEN; 391 | changed = true; 392 | } else { 393 | mSlideOffset = 0; 394 | } 395 | LoggerUtils.i("finishTouchEvent----CLOSE--mSlideOffset---"+mSlideOffset); 396 | } else if (Status.OPEN == mStatus) { 397 | if ((offset + pHeight) >= -animHeight/2) { 398 | mSlideOffset = 0; 399 | mStatus = Status.CLOSE; 400 | changed = true; 401 | } else { 402 | mSlideOffset = -pHeight - animHeight; 403 | } 404 | LoggerUtils.i("finishTouchEvent----OPEN-----"+mSlideOffset); 405 | } 406 | animatorSwitch(offset, mSlideOffset, changed); 407 | } 408 | 409 | 410 | /** 411 | * 共同调用的方法 412 | */ 413 | private void animatorSwitch(final float start, final float end, final boolean changed) { 414 | animatorSwitch(start, end, changed, mDuration); 415 | } 416 | 417 | private void animatorSwitch(final float start, final float end, 418 | final boolean changed, final long duration) { 419 | animator = ValueAnimator.ofFloat(start, end); 420 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 421 | @Override 422 | public void onAnimationUpdate(ValueAnimator animation) { 423 | mSlideOffset = (float) animation.getAnimatedValue(); 424 | LoggerUtils.i("animatorSwitch----onAnimationUpdate-----"+mSlideOffset); 425 | requestLayout(); 426 | } 427 | }); 428 | animator.addListener(new AnimatorListenerAdapter() { 429 | @Override 430 | public void onAnimationEnd(Animator animation) { 431 | super.onAnimationEnd(animation); 432 | if (changed) { 433 | if (mStatus == Status.OPEN && isFirstShowBehindView) { 434 | isFirstShowBehindView = false; 435 | mBehindView.setVisibility(VISIBLE); 436 | } 437 | if (onSlideStatusListener!=null){ 438 | onSlideStatusListener.onStatusChanged(mStatus); 439 | } 440 | } 441 | } 442 | }); 443 | animator.setDuration(duration); 444 | animator.start(); 445 | } 446 | 447 | 448 | private void ensureTarget() { 449 | if (mStatus == Status.CLOSE) { 450 | mTarget = mFrontView; 451 | } else { 452 | mTarget = mBehindView; 453 | } 454 | } 455 | 456 | 457 | /** 458 | * 是否可以滑动,direction为负数时表示向下滑动,反之表示向上滑动。 459 | * @param direction direction 460 | * @return 461 | */ 462 | protected boolean canChildScrollVertically(int direction) { 463 | if (mTarget instanceof AbsListView) { 464 | return canListViewScroll((AbsListView) mTarget); 465 | } else if (mTarget instanceof FrameLayout || mTarget instanceof RelativeLayout || 466 | mTarget instanceof LinearLayout) { 467 | View child; 468 | for (int i = 0; i < ((ViewGroup) mTarget).getChildCount(); i++) { 469 | child = ((ViewGroup) mTarget).getChildAt(i); 470 | if (child instanceof AbsListView) { 471 | return canListViewScroll((AbsListView) child); 472 | } 473 | } 474 | } 475 | return ViewCompat.canScrollVertically(mTarget, -direction); 476 | } 477 | 478 | 479 | protected boolean canListViewScroll(AbsListView absListView) { 480 | if (mStatus == Status.OPEN) { 481 | return absListView.getChildCount() > 0 482 | && (absListView.getFirstVisiblePosition() > 0 483 | || absListView.getChildAt(0).getTop() < absListView.getPaddingTop()); 484 | } else { 485 | final int count = absListView.getChildCount(); 486 | return count > 0 && (absListView.getLastVisiblePosition() < count - 1 487 | || absListView.getChildAt(count - 1).getBottom() > absListView.getMeasuredHeight()); 488 | } 489 | } 490 | 491 | 492 | @Override 493 | protected Parcelable onSaveInstanceState() { 494 | SavedState ss = new SavedState(super.onSaveInstanceState()); 495 | ss.offset = mSlideOffset; 496 | ss.status = mStatus.ordinal(); 497 | return ss; 498 | } 499 | 500 | @Override 501 | protected void onRestoreInstanceState(Parcelable state) { 502 | SavedState ss = (SavedState) state; 503 | super.onRestoreInstanceState(ss.getSuperState()); 504 | mSlideOffset = ss.offset; 505 | mStatus = Status.valueOf(ss.status); 506 | if (mStatus == Status.OPEN) { 507 | mBehindView.setVisibility(VISIBLE); 508 | } 509 | requestLayout(); 510 | } 511 | 512 | private static class SavedState extends BaseSavedState { 513 | 514 | private float offset; 515 | private int status; 516 | 517 | SavedState(Parcel source) { 518 | super(source); 519 | offset = source.readFloat(); 520 | status = source.readInt(); 521 | } 522 | 523 | SavedState(Parcelable superState) { 524 | super(superState); 525 | } 526 | 527 | @Override 528 | public void writeToParcel(Parcel out, int flags) { 529 | super.writeToParcel(out, flags); 530 | out.writeFloat(offset); 531 | out.writeInt(status); 532 | } 533 | 534 | public static final Creator
28 | * @author yangchong 29 | * blog : https://github.com/yangchong211/YCShopDetailLayout 30 | * time : 2018/6/6 31 | * desc : SlideLayout 32 | * revise: 33 | *34 | */ 35 | public class SlideLayout extends ViewGroup { 36 | 37 | 38 | private static final float DEFAULT_PERCENT = 0.2f; 39 | private static final int DEFAULT_DURATION = 300; 40 | 41 | private View mFrontView; 42 | private View mBehindView; 43 | private View mTarget; 44 | private float mTouchSlop; 45 | private float mInitMotionY; 46 | private float mInitMotionX; 47 | private float mSlideOffset; 48 | private Status mStatus = Status.CLOSE; 49 | private boolean isFirstShowBehindView = true; 50 | private float mPercent = DEFAULT_PERCENT; 51 | private long mDuration = DEFAULT_DURATION; 52 | private int mDefaultPanel = 0; 53 | 54 | /** 55 | * 状态,使用枚举 56 | */ 57 | public enum Status { 58 | CLOSE, 59 | OPEN; 60 | public static Status valueOf(int stats) { 61 | if (0 == stats) { 62 | return CLOSE; 63 | } else if (1 == stats) { 64 | return OPEN; 65 | } else { 66 | return CLOSE; 67 | } 68 | } 69 | } 70 | 71 | public SlideLayout(Context context) { 72 | this(context, null); 73 | } 74 | 75 | public SlideLayout(Context context, AttributeSet attrs) { 76 | this(context, attrs, 0); 77 | } 78 | 79 | public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) { 80 | super(context, attrs, defStyleAttr); 81 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideLayout, defStyleAttr, 0); 82 | mPercent = a.getFloat(R.styleable.SlideLayout_percent, DEFAULT_PERCENT); 83 | mDuration = a.getInt(R.styleable.SlideLayout_duration, DEFAULT_DURATION); 84 | mDefaultPanel = a.getInt(R.styleable.SlideLayout_default_panel, 0); 85 | a.recycle(); 86 | mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 87 | } 88 | 89 | @Override 90 | protected void onFinishInflate() { 91 | super.onFinishInflate(); 92 | final int childCount = getChildCount(); 93 | if (1 >= childCount) { 94 | throw new RuntimeException("SlideDetailsLayout only accept child more than 1!!"); 95 | } 96 | mFrontView = getChildAt(0); 97 | mBehindView = getChildAt(1); 98 | if(mDefaultPanel == 1){ 99 | this.post(new Runnable() { 100 | @Override 101 | public void run() { 102 | smoothOpen(false); 103 | } 104 | }); 105 | } 106 | } 107 | 108 | /** 109 | * 测量 110 | */ 111 | @Override 112 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 113 | final int pWidth = MeasureSpec.getSize(widthMeasureSpec); 114 | final int pHeight = MeasureSpec.getSize(heightMeasureSpec); 115 | final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(pWidth, MeasureSpec.EXACTLY); 116 | final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(pHeight, MeasureSpec.EXACTLY); 117 | View child; 118 | for (int i = 0; i < getChildCount(); i++) { 119 | child = getChildAt(i); 120 | if (child.getVisibility() == GONE) { 121 | continue; 122 | } 123 | measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec); 124 | } 125 | setMeasuredDimension(pWidth, pHeight); 126 | } 127 | 128 | /** 129 | * 绘制 130 | */ 131 | @Override 132 | protected void onDraw(Canvas canvas) { 133 | super.onDraw(canvas); 134 | } 135 | 136 | /** 137 | * 布局 138 | */ 139 | @Override 140 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 141 | int top; 142 | int bottom; 143 | final int offset = (int) mSlideOffset; 144 | View child; 145 | for (int i = 0; i < getChildCount(); i++) { 146 | child = getChildAt(i); 147 | if (child.getVisibility() == GONE) { 148 | continue; 149 | } 150 | if (child == mBehindView) { 151 | top = b + offset; 152 | bottom = top + b - t; 153 | } else { 154 | top = t + offset; 155 | bottom = b + offset; 156 | } 157 | child.layout(l, top, r, bottom); 158 | } 159 | } 160 | 161 | /** 162 | * 拦截事件 163 | * @param ev ev 164 | * @return 165 | */ 166 | @Override 167 | public boolean onInterceptTouchEvent(MotionEvent ev) { 168 | ensureTarget(); 169 | if (null == mTarget) { 170 | return false; 171 | } 172 | if (!isEnabled()) { 173 | return false; 174 | } 175 | final int action = ev.getAction(); 176 | boolean shouldIntercept = false; 177 | switch (action) { 178 | case MotionEvent.ACTION_DOWN: { 179 | mInitMotionX = ev.getX(); 180 | mInitMotionY = ev.getY(); 181 | shouldIntercept = false; 182 | break; 183 | } 184 | case MotionEvent.ACTION_MOVE: { 185 | final float x = ev.getX(); 186 | final float y = ev.getY(); 187 | final float xDiff = x - mInitMotionX; 188 | final float yDiff = y - mInitMotionY; 189 | boolean close = mStatus == SlideLayout.Status.CLOSE && yDiff > 0; 190 | boolean open = mStatus == SlideLayout.Status.OPEN && yDiff < 0; 191 | if (!canChildScrollVertically((int) yDiff)) { 192 | final float xDiffers = Math.abs(xDiff); 193 | final float yDiffers = Math.abs(yDiff); 194 | if (yDiffers > mTouchSlop && yDiffers >= xDiffers && !(close || open)) { 195 | shouldIntercept = true; 196 | } 197 | } 198 | break; 199 | } 200 | case MotionEvent.ACTION_UP: 201 | case MotionEvent.ACTION_CANCEL: { 202 | shouldIntercept = false; 203 | break; 204 | } 205 | default: 206 | break; 207 | } 208 | return shouldIntercept; 209 | } 210 | 211 | /** 212 | * 触摸事件 213 | * @param ev ev 214 | * @return true表示自己处理,false表示自己处理不了 215 | */ 216 | @Override 217 | public boolean onTouchEvent(MotionEvent ev) { 218 | ensureTarget(); 219 | if (null == mTarget) { 220 | return false; 221 | } 222 | if (!isEnabled()) { 223 | return false; 224 | } 225 | boolean wantTouch = true; 226 | final int action = ev.getAction(); 227 | switch (action) { 228 | case MotionEvent.ACTION_DOWN: 229 | if (mTarget instanceof View) { 230 | wantTouch = true; 231 | } 232 | break; 233 | case MotionEvent.ACTION_MOVE: 234 | final float y = ev.getY(); 235 | final float yDiff = y - mInitMotionY; 236 | if (canChildScrollVertically(((int) yDiff))) { 237 | wantTouch = false; 238 | } else { 239 | processTouchEvent(yDiff); 240 | wantTouch = true; 241 | } 242 | break; 243 | case MotionEvent.ACTION_UP: 244 | case MotionEvent.ACTION_CANCEL: 245 | finishTouchEvent(); 246 | wantTouch = false; 247 | break; 248 | default: 249 | break; 250 | } 251 | return wantTouch; 252 | } 253 | 254 | 255 | private void processTouchEvent(final float offset) { 256 | if (Math.abs(offset) < mTouchSlop) { 257 | return; 258 | } 259 | final float oldOffset = mSlideOffset; 260 | if (mStatus == Status.CLOSE) { 261 | if (offset >= 0) { 262 | mSlideOffset = 0; 263 | } else { 264 | mSlideOffset = offset; 265 | } 266 | if (mSlideOffset == oldOffset) { 267 | return; 268 | } 269 | } else if (mStatus == Status.OPEN) { 270 | final float pHeight = -getMeasuredHeight(); 271 | if (offset <= 0) { 272 | mSlideOffset = pHeight; 273 | } else { 274 | final float newOffset = pHeight + offset; 275 | mSlideOffset = newOffset; 276 | } 277 | if (mSlideOffset == oldOffset) { 278 | return; 279 | } 280 | } 281 | requestLayout(); 282 | } 283 | 284 | 285 | private void finishTouchEvent() { 286 | final int pHeight = getMeasuredHeight(); 287 | final int percent = (int) (pHeight * mPercent); 288 | final float offset = mSlideOffset; 289 | boolean changed = false; 290 | if (Status.CLOSE == mStatus) { 291 | if (offset <= -percent) { 292 | mSlideOffset = -pHeight; 293 | mStatus = Status.OPEN; 294 | changed = true; 295 | } else { 296 | mSlideOffset = 0; 297 | } 298 | } else if (Status.OPEN == mStatus) { 299 | if ((offset + pHeight) >= percent) { 300 | mSlideOffset = 0; 301 | mStatus = Status.CLOSE; 302 | changed = true; 303 | } else { 304 | mSlideOffset = -pHeight; 305 | } 306 | } 307 | animatorSwitch(offset, mSlideOffset, changed); 308 | } 309 | 310 | 311 | private void animatorSwitch(final float start, final float end, final boolean changed) { 312 | animatorSwitch(start, end, changed, mDuration); 313 | } 314 | 315 | private void animatorSwitch(final float start, final float end, final boolean changed, final long duration) { 316 | ValueAnimator animator = ValueAnimator.ofFloat(start, end); 317 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 318 | @Override 319 | public void onAnimationUpdate(ValueAnimator animation) { 320 | mSlideOffset = (float) animation.getAnimatedValue(); 321 | requestLayout(); 322 | } 323 | }); 324 | animator.addListener(new AnimatorListenerAdapter() { 325 | @Override 326 | public void onAnimationEnd(Animator animation) { 327 | super.onAnimationEnd(animation); 328 | if (changed) { 329 | if (mStatus == Status.OPEN) { 330 | checkAndFirstOpenPanel(); 331 | } 332 | 333 | if (null != mOnSlideDetailsListener) { 334 | mOnSlideDetailsListener.onStatusChanged(mStatus); 335 | } 336 | } 337 | } 338 | }); 339 | animator.setDuration(duration); 340 | animator.start(); 341 | } 342 | 343 | private void checkAndFirstOpenPanel() { 344 | if (isFirstShowBehindView) { 345 | isFirstShowBehindView = false; 346 | mBehindView.setVisibility(VISIBLE); 347 | } 348 | } 349 | 350 | private void ensureTarget() { 351 | if (mStatus == Status.CLOSE) { 352 | mTarget = mFrontView; 353 | } else { 354 | mTarget = mBehindView; 355 | } 356 | } 357 | 358 | 359 | 360 | protected boolean canChildScrollVertically(int direction) { 361 | if (mTarget instanceof AbsListView) { 362 | return canListViewScroll((AbsListView) mTarget); 363 | } else if (mTarget instanceof FrameLayout || mTarget instanceof RelativeLayout || 364 | mTarget instanceof LinearLayout) { 365 | View child; 366 | for (int i = 0; i < ((ViewGroup) mTarget).getChildCount(); i++) { 367 | child = ((ViewGroup) mTarget).getChildAt(i); 368 | if (child instanceof AbsListView) { 369 | return canListViewScroll((AbsListView) child); 370 | } 371 | } 372 | } 373 | return ViewCompat.canScrollVertically(mTarget, -direction); 374 | } 375 | 376 | protected boolean canListViewScroll(AbsListView absListView) { 377 | if (mStatus == Status.OPEN) { 378 | return absListView.getChildCount() > 0 379 | && (absListView.getFirstVisiblePosition() > 0 380 | || absListView.getChildAt(0).getTop() < absListView.getPaddingTop()); 381 | } else { 382 | final int count = absListView.getChildCount(); 383 | return count > 0 384 | && (absListView.getLastVisiblePosition() < count - 1 385 | || absListView.getChildAt(count - 1).getBottom() > absListView.getMeasuredHeight()); 386 | } 387 | } 388 | 389 | @Override 390 | protected Parcelable onSaveInstanceState() { 391 | SavedState ss = new SavedState(super.onSaveInstanceState()); 392 | ss.offset = mSlideOffset; 393 | ss.status = mStatus.ordinal(); 394 | return ss; 395 | } 396 | 397 | @Override 398 | protected void onRestoreInstanceState(Parcelable state) { 399 | SavedState ss = (SavedState) state; 400 | super.onRestoreInstanceState(ss.getSuperState()); 401 | mSlideOffset = ss.offset; 402 | mStatus = Status.valueOf(ss.status); 403 | if (mStatus == Status.OPEN) { 404 | mBehindView.setVisibility(VISIBLE); 405 | } 406 | requestLayout(); 407 | } 408 | 409 | static class SavedState extends BaseSavedState { 410 | 411 | private float offset; 412 | private int status; 413 | 414 | SavedState(Parcel source) { 415 | super(source); 416 | offset = source.readFloat(); 417 | status = source.readInt(); 418 | } 419 | 420 | SavedState(Parcelable superState) { 421 | super(superState); 422 | } 423 | 424 | @Override 425 | public void writeToParcel(Parcel out, int flags) { 426 | super.writeToParcel(out, flags); 427 | out.writeFloat(offset); 428 | out.writeInt(status); 429 | } 430 | 431 | public static final Creator
12 | * @author yangchong 13 | * blog : https://github.com/yangchong211/YCShopDetailLayout 14 | * time : 2018/6/6 15 | * desc : 当ScrollView在最顶部或者最底部的时候,不消费事件 16 | * revise: 17 | *18 | */ 19 | public class VerticalScrollView extends ScrollView { 20 | 21 | private float downX; 22 | private float downY; 23 | 24 | public VerticalScrollView(Context context) { 25 | this(context, null); 26 | } 27 | 28 | public VerticalScrollView(Context context, AttributeSet attrs) { 29 | this(context, attrs, android.R.attr.scrollViewStyle); 30 | } 31 | 32 | public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 33 | super(context, attrs, defStyleAttr); 34 | } 35 | 36 | @Override 37 | public boolean dispatchTouchEvent(MotionEvent ev) { 38 | switch (ev.getAction()) { 39 | case MotionEvent.ACTION_DOWN: 40 | downX = ev.getX(); 41 | downY = ev.getY(); 42 | //如果滑动到了最底部,就允许继续向上滑动加载下一页,否者不允许 43 | //如果子节点不希望父进程拦截触摸事件,则为true。 44 | getParent().requestDisallowInterceptTouchEvent(true); 45 | break; 46 | case MotionEvent.ACTION_MOVE: 47 | float dx = ev.getX() - downX; 48 | float dy = ev.getY() - downY; 49 | boolean allowParentTouchEvent; 50 | if (Math.abs(dy) > Math.abs(dx)) { 51 | if (dy > 0) { 52 | //位于顶部时下拉,让父View消费事件 53 | allowParentTouchEvent = isTop(); 54 | } else { 55 | //位于底部时上拉,让父View消费事件 56 | allowParentTouchEvent = isBottom(); 57 | } 58 | } else { 59 | //水平方向滑动 60 | allowParentTouchEvent = true; 61 | } 62 | getParent().requestDisallowInterceptTouchEvent(!allowParentTouchEvent); 63 | break; 64 | default: 65 | break; 66 | } 67 | return super.dispatchTouchEvent(ev); 68 | } 69 | 70 | private boolean isTop() { 71 | return !canScrollVertically(-1); 72 | } 73 | 74 | private boolean isBottom() { 75 | return !canScrollVertically(1); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /YCSlideLib/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 |
14 | * @author yangchong 15 | * blog : 16 | * time : 2018/6/6 17 | * desc : 当WebView在最顶部或者最底部的时候,不消费事件 18 | * revise: 19 | *20 | */ 21 | public class VerticalRecyclerView extends RecyclerView { 22 | 23 | private float downX; 24 | private float downY; 25 | /** 第一个可见的item的位置 */ 26 | private int firstVisibleItemPosition; 27 | /** 第一个的位置 */ 28 | private int[] firstPositions; 29 | /** 最后一个可见的item的位置 */ 30 | private int lastVisibleItemPosition; 31 | /** 最后一个的位置 */ 32 | private int[] lastPositions; 33 | private boolean isTop; 34 | private boolean isBottom; 35 | 36 | public VerticalRecyclerView(Context context) { 37 | this(context, null); 38 | } 39 | 40 | public VerticalRecyclerView(Context context, AttributeSet attrs) { 41 | this(context, attrs, 0); 42 | } 43 | 44 | public VerticalRecyclerView(Context context, AttributeSet attrs, int defStyle) { 45 | super(context, attrs, defStyle); 46 | } 47 | 48 | @Override 49 | public boolean dispatchTouchEvent(MotionEvent ev) { 50 | RecyclerView.LayoutManager layoutManager = getLayoutManager(); 51 | if (layoutManager != null) { 52 | if (layoutManager instanceof GridLayoutManager) { 53 | lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); 54 | firstVisibleItemPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition(); 55 | } else if (layoutManager instanceof LinearLayoutManager) { 56 | lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); 57 | firstVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); 58 | } else if (layoutManager instanceof StaggeredGridLayoutManager) { 59 | StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; 60 | if (lastPositions == null) { 61 | lastPositions = new int[staggeredGridLayoutManager.getSpanCount()]; 62 | firstPositions = new int[staggeredGridLayoutManager.getSpanCount()]; 63 | } 64 | staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions); 65 | staggeredGridLayoutManager.findFirstVisibleItemPositions(firstPositions); 66 | lastVisibleItemPosition = findMax(lastPositions); 67 | firstVisibleItemPosition = findMin(firstPositions); 68 | } 69 | } else { 70 | throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); 71 | } 72 | 73 | switch (ev.getAction()) { 74 | case MotionEvent.ACTION_DOWN: 75 | downX = ev.getX(); 76 | downY = ev.getY(); 77 | //如果滑动到了最底部,就允许继续向上滑动加载下一页,否者不允许 78 | getParent().requestDisallowInterceptTouchEvent(true); 79 | break; 80 | case MotionEvent.ACTION_MOVE: 81 | float dx = ev.getX() - downX; 82 | float dy = ev.getY() - downY; 83 | boolean allowParentTouchEvent; 84 | if (Math.abs(dy) > Math.abs(dx)) { 85 | if (dy > 0) { 86 | //位于顶部时下拉,让父View消费事件 87 | allowParentTouchEvent = isTop = firstVisibleItemPosition == 0 && getChildAt(0).getTop() >= 0; 88 | } else { 89 | //位于底部时上拉,让父View消费事件 90 | int visibleItemCount = layoutManager.getChildCount(); 91 | int totalItemCount = layoutManager.getItemCount(); 92 | allowParentTouchEvent = isBottom = visibleItemCount > 0 && (lastVisibleItemPosition) >= totalItemCount - 1 && getChildAt(getChildCount() - 1).getBottom() <= getHeight(); 93 | } 94 | } else { 95 | //水平方向滑动 96 | allowParentTouchEvent = true; 97 | } 98 | getParent().requestDisallowInterceptTouchEvent(!allowParentTouchEvent); 99 | break; 100 | default: 101 | break; 102 | } 103 | return super.dispatchTouchEvent(ev); 104 | 105 | } 106 | 107 | private int findMax(int[] lastPositions) { 108 | int max = lastPositions[0]; 109 | for (int value : lastPositions) { 110 | if (value >= max) { 111 | max = value; 112 | } 113 | } 114 | return max; 115 | } 116 | 117 | private int findMin(int[] firstPositions) { 118 | int min = firstPositions[0]; 119 | for (int value : firstPositions) { 120 | if (value < min) { 121 | min = value; 122 | } 123 | } 124 | return min; 125 | } 126 | 127 | public boolean isTop() { 128 | return isTop; 129 | } 130 | 131 | public boolean isBottom() { 132 | return isBottom; 133 | } 134 | 135 | public void goTop() { 136 | scrollToPosition(0); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/VerticalWebView.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.MotionEvent; 6 | import android.webkit.WebView; 7 | 8 | 9 | /** 10 | *
11 | * @author yangchong 12 | * blog : 13 | * time : 2018/6/6 14 | * desc : 当WebView在最顶部或者最底部的时候,不消费事件 15 | * revise: 16 | *17 | */ 18 | public class VerticalWebView extends WebView { 19 | 20 | private float downX; 21 | private float downY; 22 | 23 | public VerticalWebView(Context context) { 24 | this(context, null); 25 | } 26 | 27 | public VerticalWebView(Context context, AttributeSet attrs) { 28 | this(context, attrs, android.R.attr.webViewStyle); 29 | } 30 | 31 | public VerticalWebView(Context context, AttributeSet attrs, int defStyleAttr) { 32 | super(context, attrs, defStyleAttr); 33 | } 34 | 35 | @Override 36 | public boolean dispatchTouchEvent(MotionEvent ev) { 37 | switch (ev.getAction()) { 38 | case MotionEvent.ACTION_DOWN: 39 | downX = ev.getX(); 40 | downY = ev.getY(); 41 | //如果滑动到了最底部,就允许继续向上滑动加载下一页,否者不允许 42 | getParent().requestDisallowInterceptTouchEvent(true); 43 | break; 44 | case MotionEvent.ACTION_MOVE: 45 | float dx = ev.getX() - downX; 46 | float dy = ev.getY() - downY; 47 | boolean allowParentTouchEvent; 48 | if (Math.abs(dy) > Math.abs(dx)) { 49 | if (dy > 0) { 50 | //位于顶部时下拉,让父View消费事件 51 | allowParentTouchEvent = isTop(); 52 | } else { 53 | //位于底部时上拉,让父View消费事件 54 | allowParentTouchEvent = isBottom(); 55 | } 56 | } else { 57 | //水平方向滑动 58 | allowParentTouchEvent = true; 59 | } 60 | getParent().requestDisallowInterceptTouchEvent(!allowParentTouchEvent); 61 | break; 62 | default: 63 | break; 64 | } 65 | return super.dispatchTouchEvent(ev); 66 | } 67 | 68 | private boolean isTop() { 69 | return getScrollY() <= 0; 70 | } 71 | 72 | private boolean isBottom() { 73 | return getHeight() + getScrollY() >= getContentHeight() * getScale(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/first/FirstActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout.first; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.webkit.WebSettings; 8 | import android.webkit.WebView; 9 | import android.webkit.WebViewClient; 10 | import android.widget.LinearLayout; 11 | 12 | import androidx.appcompat.app.AppCompatActivity; 13 | import androidx.fragment.app.FragmentManager; 14 | import androidx.fragment.app.FragmentTransaction; 15 | 16 | import com.yc.slide.SlideLayout; 17 | import com.ycbjie.ycshopdetaillayout.R; 18 | 19 | public class FirstActivity extends AppCompatActivity { 20 | 21 | private SlideLayout mSlideDetailsLayout; 22 | private ShopMainFragment shopMainFragment; 23 | private WebView webView; 24 | private LinearLayout mLlDetail; 25 | 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_first); 31 | 32 | initView(); 33 | initShopMainFragment(); 34 | initSlideDetailsLayout(); 35 | initWebView(); 36 | } 37 | 38 | private void initView() { 39 | mSlideDetailsLayout = findViewById(R.id.slideDetailsLayout); 40 | webView = findViewById(R.id.wb_view); 41 | mLlDetail = findViewById(R.id.ll_detail); 42 | } 43 | 44 | 45 | private void initShopMainFragment() { 46 | FragmentManager fm = getSupportFragmentManager(); 47 | FragmentTransaction fragmentTransaction = fm.beginTransaction(); 48 | if(shopMainFragment==null){ 49 | shopMainFragment = new ShopMainFragment(); 50 | fragmentTransaction 51 | .replace(R.id.fl_shop_main, shopMainFragment) 52 | .commit(); 53 | }else { 54 | fragmentTransaction.show(shopMainFragment); 55 | } 56 | } 57 | 58 | private void initSlideDetailsLayout() { 59 | mSlideDetailsLayout.setOnSlideDetailsListener(new SlideLayout.OnSlideDetailsListener() { 60 | @Override 61 | public void onStatusChanged(SlideLayout.Status status) { 62 | if (status == SlideLayout.Status.OPEN) { 63 | //当前为图文详情页 64 | Log.e("FirstActivity","下拉回到商品详情"); 65 | shopMainFragment.changBottomView(true); 66 | } else { 67 | //当前为商品详情页 68 | Log.e("FirstActivity","继续上拉,查看图文详情"); 69 | shopMainFragment.changBottomView(false); 70 | } 71 | } 72 | }); 73 | } 74 | 75 | 76 | @SuppressLint({"ObsoleteSdkInt", "SetJavaScriptEnabled"}) 77 | private void initWebView() { 78 | final WebSettings settings = webView.getSettings(); 79 | settings.setJavaScriptEnabled(true); 80 | settings.setSupportZoom(true); 81 | settings.setBuiltInZoomControls(true); 82 | settings.setUseWideViewPort(true); 83 | settings.setDomStorageEnabled(true); 84 | webView.setWebViewClient(new WebViewClient() { 85 | 86 | @Override 87 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 88 | view.loadUrl(url); 89 | return true; 90 | } 91 | }); 92 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) { 93 | new Object() { 94 | void setLoadWithOverviewMode(boolean overview) { 95 | settings.setLoadWithOverviewMode(overview); 96 | } 97 | }.setLoadWithOverviewMode(true); 98 | } 99 | 100 | settings.setCacheMode(WebSettings.LOAD_DEFAULT); 101 | 102 | getWindow().getDecorView().post(new Runnable() { 103 | @Override 104 | public void run() { 105 | webView.loadUrl("https://www.jianshu.com/p/d745ea0cb5bd"); 106 | } 107 | }); 108 | } 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/first/ShopDetailFragment.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout.first; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.webkit.WebSettings; 10 | import android.webkit.WebView; 11 | import android.webkit.WebViewClient; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.annotation.Nullable; 15 | import androidx.fragment.app.Fragment; 16 | 17 | import com.ycbjie.ycshopdetaillayout.R; 18 | 19 | 20 | public class ShopDetailFragment extends Fragment { 21 | 22 | 23 | private WebView webView; 24 | 25 | @Nullable 26 | @Override 27 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 28 | return inflater.inflate(getContentView(), container , false); 29 | } 30 | 31 | @Override 32 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 33 | super.onViewCreated(view, savedInstanceState); 34 | initView(view); 35 | initWebView(); 36 | } 37 | 38 | private int getContentView() { 39 | return R.layout.include_shop_detail; 40 | } 41 | 42 | private void initView(View view) { 43 | webView = view.findViewById(R.id.wb_view); 44 | } 45 | 46 | @SuppressLint("ObsoleteSdkInt") 47 | private void initWebView() { 48 | final WebSettings settings = webView.getSettings(); 49 | settings.setJavaScriptEnabled(true); 50 | settings.setSupportZoom(true); 51 | settings.setBuiltInZoomControls(true); 52 | settings.setUseWideViewPort(true); 53 | settings.setDomStorageEnabled(true); 54 | webView.setWebViewClient(new WebViewClient() { 55 | 56 | @Override 57 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 58 | view.loadUrl(url); 59 | return true; 60 | } 61 | }); 62 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) { 63 | new Object() { 64 | void setLoadWithOverviewMode(boolean overview) { 65 | settings.setLoadWithOverviewMode(overview); 66 | } 67 | }.setLoadWithOverviewMode(true); 68 | } 69 | 70 | settings.setCacheMode(WebSettings.LOAD_DEFAULT); 71 | 72 | webView.loadUrl("https://www.jianshu.com/p/d745ea0cb5bd"); 73 | } 74 | 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/first/ShopMainFragment.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout.first; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.NonNull; 5 | import androidx.annotation.Nullable; 6 | import androidx.fragment.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | import android.widget.LinearLayout; 12 | import android.widget.ScrollView; 13 | import android.widget.TextView; 14 | 15 | import com.ycbjie.ycshopdetaillayout.R; 16 | 17 | 18 | public class ShopMainFragment extends Fragment { 19 | 20 | private ScrollView mScrollView; 21 | private TextView mTvGoodsTitle; 22 | private TextView mTvNewPrice; 23 | private TextView mTvOldPrice; 24 | private LinearLayout mLlActivity; 25 | private LinearLayout mLlCurrentGoods; 26 | private TextView mTvCurrentGoods; 27 | private ImageView mIvEnsure; 28 | private LinearLayout mLlComment; 29 | private TextView mTvCommentCount; 30 | private TextView mTvGoodComment; 31 | private ImageView mIvCommentRight; 32 | private LinearLayout mLlEmptyComment; 33 | private LinearLayout mLlRecommend; 34 | private TextView mTvBottomView; 35 | 36 | @Nullable 37 | @Override 38 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 39 | return inflater.inflate(getContentView(), container , false); 40 | } 41 | 42 | @Override 43 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 44 | super.onViewCreated(view, savedInstanceState); 45 | initView(view); 46 | } 47 | 48 | private int getContentView() { 49 | return R.layout.include_shop_main; 50 | } 51 | 52 | 53 | private void initView(View view) { 54 | mScrollView = view.findViewById(R.id.scrollView); 55 | mTvGoodsTitle = view.findViewById(R.id.tv_goods_title); 56 | mTvNewPrice = view.findViewById(R.id.tv_new_price); 57 | mTvOldPrice = view.findViewById(R.id.tv_old_price); 58 | mLlCurrentGoods = view.findViewById(R.id.ll_current_goods); 59 | mTvCurrentGoods = view.findViewById(R.id.tv_current_goods); 60 | mIvEnsure = view.findViewById(R.id.iv_ensure); 61 | mLlComment = view.findViewById(R.id.ll_comment); 62 | mTvCommentCount = view.findViewById(R.id.tv_comment_count); 63 | mTvGoodComment = view.findViewById(R.id.tv_good_comment); 64 | mIvCommentRight = view.findViewById(R.id.iv_comment_right); 65 | mLlEmptyComment = view.findViewById(R.id.ll_empty_comment); 66 | mLlRecommend = view.findViewById(R.id.ll_recommend); 67 | mTvBottomView = view.findViewById(R.id.tv_bottom_view); 68 | 69 | } 70 | 71 | 72 | public void changBottomView(boolean isDetail) { 73 | if(isDetail){ 74 | mTvBottomView.setText("下拉回到商品详情"); 75 | }else { 76 | mTvBottomView.setText("继续上拉,查看图文详情"); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/four/FourActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout.four; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.webkit.WebSettings; 10 | import android.webkit.WebView; 11 | import android.webkit.WebViewClient; 12 | import android.widget.FrameLayout; 13 | import android.widget.ImageView; 14 | import android.widget.LinearLayout; 15 | import android.widget.ScrollView; 16 | import android.widget.TextView; 17 | 18 | import androidx.appcompat.app.AppCompatActivity; 19 | import androidx.core.widget.NestedScrollView; 20 | import androidx.fragment.app.FragmentManager; 21 | import androidx.fragment.app.FragmentTransaction; 22 | 23 | import com.yc.slide.LoggerUtils; 24 | import com.yc.slide.SlideAnimLayout; 25 | import com.yc.ycshopdetaillayout.second.ShopMain1Fragment; 26 | import com.ycbjie.ycshopdetaillayout.R; 27 | 28 | 29 | public class FourActivity extends AppCompatActivity { 30 | 31 | private SlideAnimLayout mSlideDetailsLayout; 32 | private ShopMain1Fragment shopMainFragment; 33 | private WebView webView; 34 | private LinearLayout ll_page_more; 35 | private ImageView mIvMoreImg; 36 | private TextView mTvMoreText; 37 | private TextView tvBarGoods; 38 | private TextView tvBarDetail; 39 | private LinearLayout root; 40 | private NestedScrollView scrollView; 41 | 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | setContentView(R.layout.activity_four); 46 | 47 | initView(); 48 | initListener(); 49 | initShopMainFragment(); 50 | initSlideDetailsLayout(); 51 | initWebView(); 52 | } 53 | 54 | private void initView() { 55 | root = findViewById(R.id.root); 56 | mSlideDetailsLayout = findViewById(R.id.slideDetailsLayout); 57 | ll_page_more = findViewById(R.id.ll_page_more); 58 | webView = findViewById(R.id.wb_view); 59 | mIvMoreImg = findViewById(R.id.iv_more_img); 60 | mTvMoreText = findViewById(R.id.tv_more_text); 61 | tvBarGoods = findViewById(R.id.tv_bar_goods); 62 | tvBarDetail = findViewById(R.id.tv_bar_detail); 63 | scrollView = findViewById(R.id.scrollView); 64 | } 65 | 66 | private void initListener() { 67 | View.OnClickListener onClickListener = new View.OnClickListener() { 68 | @Override 69 | public void onClick(View v) { 70 | switch (v.getId()){ 71 | case R.id.tv_bar_goods: 72 | tvBarGoods.setTextColor(Color.RED); 73 | tvBarDetail.setTextColor(Color.BLACK); 74 | mSlideDetailsLayout.smoothClose(true); 75 | break; 76 | case R.id.tv_bar_detail: 77 | tvBarGoods.setTextColor(Color.BLACK); 78 | tvBarDetail.setTextColor(Color.RED); 79 | mSlideDetailsLayout.smoothOpen(true); 80 | break; 81 | default: 82 | break; 83 | } 84 | } 85 | }; 86 | tvBarGoods.setOnClickListener(onClickListener); 87 | tvBarDetail.setOnClickListener(onClickListener); 88 | } 89 | 90 | 91 | private void initShopMainFragment() { 92 | FragmentManager fm = getSupportFragmentManager(); 93 | FragmentTransaction fragmentTransaction = fm.beginTransaction(); 94 | if(shopMainFragment==null){ 95 | shopMainFragment = new ShopMain1Fragment(); 96 | fragmentTransaction 97 | .replace(R.id.fl_shop_main2, shopMainFragment) 98 | .commit(); 99 | }else { 100 | fragmentTransaction.show(shopMainFragment); 101 | } 102 | } 103 | 104 | private void initSlideDetailsLayout() { 105 | mSlideDetailsLayout.setScrollStatusListener(new SlideAnimLayout.onScrollStatusListener() { 106 | @Override 107 | public void onStatusChanged(SlideAnimLayout.Status mNowStatus, boolean isHalf) { 108 | if(mNowStatus== SlideAnimLayout.Status.CLOSE){ 109 | //打开 110 | if(isHalf){ 111 | mTvMoreText.setText("释放,查看图文详情"); 112 | mIvMoreImg.animate().rotation(0); 113 | LoggerUtils.i("onStatusChanged---CLOSE---释放"+isHalf); 114 | }else{//关闭 115 | mTvMoreText.setText("继续上拉,查看图文详情"); 116 | mIvMoreImg.animate().rotation(180); 117 | LoggerUtils.i("onStatusChanged---CLOSE---继续上拉"+isHalf); 118 | } 119 | }else{ 120 | //打开 121 | if(isHalf){ 122 | mTvMoreText.setText("下拉回到商品详情"); 123 | mIvMoreImg.animate().rotation(0); 124 | LoggerUtils.i("onStatusChanged---OPEN---下拉回到商品详情"+isHalf); 125 | }else{//关闭 126 | mTvMoreText.setText("释放回到商品详情"); 127 | mIvMoreImg.animate().rotation(180); 128 | LoggerUtils.i("onStatusChanged---OPEN---释放回到商品详情"+isHalf); 129 | } 130 | } 131 | } 132 | }); 133 | mSlideDetailsLayout.setOnSlideStatusListener(new SlideAnimLayout.OnSlideStatusListener() { 134 | @Override 135 | public void onStatusChanged(SlideAnimLayout.Status status) { 136 | FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) 137 | root.getLayoutParams(); 138 | if (status == SlideAnimLayout.Status.OPEN) { 139 | layoutParams.topMargin = dip2px(FourActivity.this,44.0f); 140 | root.setLayoutParams(layoutParams); 141 | LoggerUtils.i("setOnSlideStatusListener---OPEN---下拉回到商品详情"); 142 | //scrollView.scrollTo(0,0); 143 | scrollView.post(new Runnable() { 144 | @Override 145 | public void run() { 146 | //ScrollView滑动到顶部 147 | scrollView.fullScroll(ScrollView.FOCUS_UP); 148 | } 149 | }); 150 | } else { 151 | layoutParams.topMargin = dip2px(FourActivity.this,0); 152 | root.setLayoutParams(layoutParams); 153 | LoggerUtils.i("setOnSlideStatusListener---CLOSE---上拉查看图文详情"); 154 | } 155 | } 156 | }); 157 | } 158 | 159 | 160 | @SuppressLint({"ObsoleteSdkInt", "SetJavaScriptEnabled"}) 161 | private void initWebView() { 162 | final WebSettings settings = webView.getSettings(); 163 | settings.setJavaScriptEnabled(true); 164 | settings.setSupportZoom(true); 165 | settings.setBuiltInZoomControls(true); 166 | settings.setUseWideViewPort(true); 167 | settings.setDomStorageEnabled(true); 168 | webView.setWebViewClient(new WebViewClient() { 169 | @Override 170 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 171 | view.loadUrl(url); 172 | return true; 173 | } 174 | }); 175 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) { 176 | new Object() { 177 | void setLoadWithOverviewMode(boolean overview) { 178 | settings.setLoadWithOverviewMode(overview); 179 | } 180 | }.setLoadWithOverviewMode(true); 181 | } 182 | 183 | settings.setCacheMode(WebSettings.LOAD_DEFAULT); 184 | 185 | getWindow().getDecorView().post(new Runnable() { 186 | @Override 187 | public void run() { 188 | webView.loadUrl("https://www.jianshu.com/p/d745ea0cb5bd"); 189 | } 190 | }); 191 | } 192 | 193 | 194 | 195 | 196 | public static int dip2px(Context context, float dpValue) { 197 | final float scale = context.getResources().getDisplayMetrics().density; 198 | return (int) (dpValue * scale + 0.5f); 199 | } 200 | 201 | } 202 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/second/SecondActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout.second; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.webkit.WebSettings; 8 | import android.webkit.WebView; 9 | import android.webkit.WebViewClient; 10 | import android.widget.ImageView; 11 | import android.widget.LinearLayout; 12 | import android.widget.TextView; 13 | 14 | import androidx.appcompat.app.AppCompatActivity; 15 | import androidx.fragment.app.FragmentManager; 16 | import androidx.fragment.app.FragmentTransaction; 17 | 18 | import com.yc.slide.SlideAnimLayout; 19 | import com.ycbjie.ycshopdetaillayout.R; 20 | 21 | 22 | public class SecondActivity extends AppCompatActivity { 23 | 24 | private SlideAnimLayout mSlideDetailsLayout; 25 | private ShopMain1Fragment shopMainFragment; 26 | private WebView webView; 27 | private LinearLayout ll_page_more; 28 | private ImageView mIvMoreImg; 29 | private TextView mTvMoreText; 30 | private boolean isBtn = true; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_second); 36 | 37 | initView(); 38 | initShopMainFragment(); 39 | initSlideDetailsLayout(); 40 | initWebView(); 41 | findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View v) { 44 | if (isBtn){ 45 | isBtn = false; 46 | mSlideDetailsLayout.smoothOpen(true); 47 | }else { 48 | isBtn = true; 49 | mSlideDetailsLayout.smoothClose(true); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | private void initView() { 56 | mSlideDetailsLayout = findViewById(R.id.slideDetailsLayout); 57 | ll_page_more = findViewById(R.id.ll_page_more); 58 | webView = findViewById(R.id.wb_view); 59 | mIvMoreImg = findViewById(R.id.iv_more_img); 60 | mTvMoreText = findViewById(R.id.tv_more_text); 61 | 62 | } 63 | 64 | 65 | private void initShopMainFragment() { 66 | FragmentManager fm = getSupportFragmentManager(); 67 | FragmentTransaction fragmentTransaction = fm.beginTransaction(); 68 | if(shopMainFragment==null){ 69 | shopMainFragment = new ShopMain1Fragment(); 70 | fragmentTransaction 71 | .replace(R.id.fl_shop_main2, shopMainFragment) 72 | .commit(); 73 | }else { 74 | fragmentTransaction.show(shopMainFragment); 75 | } 76 | } 77 | 78 | private void initSlideDetailsLayout() { 79 | mSlideDetailsLayout.setScrollStatusListener(new SlideAnimLayout.onScrollStatusListener() { 80 | @Override 81 | public void onStatusChanged(SlideAnimLayout.Status mNowStatus,boolean isHalf) { 82 | if(mNowStatus==SlideAnimLayout.Status.CLOSE){ 83 | if(isHalf){//打开 84 | mTvMoreText.setText("释放,查看图文详情"); 85 | mIvMoreImg.animate().rotation(0); 86 | }else{//关闭 87 | mTvMoreText.setText("继续上拉,查看图文详情"); 88 | mIvMoreImg.animate().rotation(180); 89 | } 90 | }else{ 91 | if(isHalf){//打开 92 | mTvMoreText.setText("下拉回到商品详情"); 93 | mIvMoreImg.animate().rotation(0); 94 | }else{//关闭 95 | mTvMoreText.setText("释放回到商品详情"); 96 | mIvMoreImg.animate().rotation(180); 97 | } 98 | } 99 | } 100 | }); 101 | } 102 | 103 | 104 | @SuppressLint({"ObsoleteSdkInt", "SetJavaScriptEnabled"}) 105 | private void initWebView() { 106 | final WebSettings settings = webView.getSettings(); 107 | settings.setJavaScriptEnabled(true); 108 | settings.setSupportZoom(true); 109 | settings.setBuiltInZoomControls(true); 110 | settings.setUseWideViewPort(true); 111 | settings.setDomStorageEnabled(true); 112 | webView.setWebViewClient(new WebViewClient() { 113 | 114 | @Override 115 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 116 | view.loadUrl(url); 117 | return true; 118 | } 119 | }); 120 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) { 121 | new Object() { 122 | void setLoadWithOverviewMode(boolean overview) { 123 | settings.setLoadWithOverviewMode(overview); 124 | } 125 | }.setLoadWithOverviewMode(true); 126 | } 127 | 128 | settings.setCacheMode(WebSettings.LOAD_DEFAULT); 129 | 130 | getWindow().getDecorView().post(new Runnable() { 131 | @Override 132 | public void run() { 133 | webView.loadUrl("https://www.jianshu.com/p/d745ea0cb5bd"); 134 | } 135 | }); 136 | } 137 | 138 | 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/second/ShopMain1Fragment.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout.second; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.NonNull; 5 | import androidx.annotation.Nullable; 6 | import androidx.fragment.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | import android.widget.LinearLayout; 12 | import android.widget.ScrollView; 13 | import android.widget.TextView; 14 | 15 | import com.ycbjie.ycshopdetaillayout.R; 16 | 17 | 18 | public class ShopMain1Fragment extends Fragment { 19 | 20 | private ScrollView mScrollView; 21 | private TextView mTvGoodsTitle; 22 | private TextView mTvNewPrice; 23 | private TextView mTvOldPrice; 24 | private LinearLayout mLlActivity; 25 | private LinearLayout mLlCurrentGoods; 26 | private TextView mTvCurrentGoods; 27 | private ImageView mIvEnsure; 28 | private LinearLayout mLlComment; 29 | private TextView mTvCommentCount; 30 | private TextView mTvGoodComment; 31 | private ImageView mIvCommentRight; 32 | private LinearLayout mLlEmptyComment; 33 | private LinearLayout mLlRecommend; 34 | private TextView mTvBottomView; 35 | 36 | @Nullable 37 | @Override 38 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 39 | return inflater.inflate(getContentView(), container , false); 40 | } 41 | 42 | @Override 43 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 44 | super.onViewCreated(view, savedInstanceState); 45 | initView(view); 46 | } 47 | 48 | private int getContentView() { 49 | return R.layout.include_shop_main; 50 | } 51 | 52 | 53 | private void initView(View view) { 54 | mScrollView = view.findViewById(R.id.scrollView); 55 | mTvGoodsTitle = view.findViewById(R.id.tv_goods_title); 56 | mTvNewPrice = view.findViewById(R.id.tv_new_price); 57 | mTvOldPrice = view.findViewById(R.id.tv_old_price); 58 | mLlCurrentGoods = view.findViewById(R.id.ll_current_goods); 59 | mTvCurrentGoods = view.findViewById(R.id.tv_current_goods); 60 | mIvEnsure = view.findViewById(R.id.iv_ensure); 61 | mLlComment = view.findViewById(R.id.ll_comment); 62 | mTvCommentCount = view.findViewById(R.id.tv_comment_count); 63 | mTvGoodComment = view.findViewById(R.id.tv_good_comment); 64 | mIvCommentRight = view.findViewById(R.id.iv_comment_right); 65 | mLlEmptyComment = view.findViewById(R.id.ll_empty_comment); 66 | mLlRecommend = view.findViewById(R.id.ll_recommend); 67 | mTvBottomView = view.findViewById(R.id.tv_bottom_view); 68 | } 69 | 70 | 71 | public void changBottomView(boolean isDetail) { 72 | if(isDetail){ 73 | mTvBottomView.setText("下拉回到商品详情"); 74 | }else { 75 | mTvBottomView.setText("继续上拉,查看图文详情"); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/third/NoScrollWebView.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout.third; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.webkit.WebView; 7 | 8 | 9 | 10 | public class NoScrollWebView extends WebView { 11 | 12 | public NoScrollWebView(Context context) { 13 | super(context); 14 | initView(); 15 | } 16 | 17 | public NoScrollWebView(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | initView(); 20 | } 21 | 22 | 23 | public NoScrollWebView(Context context, AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | initView(); 26 | } 27 | 28 | private void initView() { 29 | 30 | } 31 | 32 | @Override 33 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 34 | int mExpandSpec = View.MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, View.MeasureSpec.AT_MOST); 35 | super.onMeasure(widthMeasureSpec, mExpandSpec); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/yc/ycshopdetaillayout/third/ThirdActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycshopdetaillayout.third; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.webkit.WebSettings; 8 | import android.webkit.WebView; 9 | import android.webkit.WebViewClient; 10 | import android.widget.LinearLayout; 11 | 12 | import androidx.appcompat.app.AppCompatActivity; 13 | import androidx.fragment.app.FragmentManager; 14 | import androidx.fragment.app.FragmentTransaction; 15 | 16 | import com.yc.slide.SlideLayout; 17 | import com.ycbjie.ycshopdetaillayout.R; 18 | import com.yc.ycshopdetaillayout.first.ShopMainFragment; 19 | 20 | /** 21 | * Created by yc on 2018/7/23. 22 | */ 23 | 24 | public class ThirdActivity extends AppCompatActivity { 25 | 26 | private SlideLayout mSlideDetailsLayout; 27 | private ShopMainFragment shopMainFragment; 28 | private WebView webView; 29 | private LinearLayout mLlDetail; 30 | 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_third); 36 | 37 | initView(); 38 | initShopMainFragment(); 39 | initSlideDetailsLayout(); 40 | initWebView(); 41 | } 42 | 43 | private void initView() { 44 | mSlideDetailsLayout = findViewById(R.id.slideDetailsLayout); 45 | webView = findViewById(R.id.wb_view); 46 | mLlDetail = findViewById(R.id.ll_detail); 47 | } 48 | 49 | 50 | private void initShopMainFragment() { 51 | FragmentManager fm = getSupportFragmentManager(); 52 | FragmentTransaction fragmentTransaction = fm.beginTransaction(); 53 | if(shopMainFragment==null){ 54 | shopMainFragment = new ShopMainFragment(); 55 | fragmentTransaction 56 | .replace(R.id.fl_shop_main, shopMainFragment) 57 | .commit(); 58 | }else { 59 | fragmentTransaction.show(shopMainFragment); 60 | } 61 | } 62 | 63 | private void initSlideDetailsLayout() { 64 | mSlideDetailsLayout.setOnSlideDetailsListener(new SlideLayout.OnSlideDetailsListener() { 65 | @Override 66 | public void onStatusChanged(SlideLayout.Status status) { 67 | if (status == SlideLayout.Status.OPEN) { 68 | //当前为图文详情页 69 | Log.e("FirstActivity","图文详情页"); 70 | shopMainFragment.changBottomView(true); 71 | } else { 72 | //当前为商品详情页 73 | Log.e("FirstActivity","商品详情页"); 74 | shopMainFragment.changBottomView(false); 75 | } 76 | } 77 | }); 78 | } 79 | 80 | 81 | @SuppressLint({"ObsoleteSdkInt", "SetJavaScriptEnabled"}) 82 | private void initWebView() { 83 | final WebSettings settings = webView.getSettings(); 84 | settings.setJavaScriptEnabled(true); 85 | settings.setSupportZoom(true); 86 | settings.setBuiltInZoomControls(true); 87 | settings.setUseWideViewPort(true); 88 | settings.setDomStorageEnabled(true); 89 | webView.setWebViewClient(new WebViewClient() { 90 | 91 | @Override 92 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 93 | view.loadUrl(url); 94 | return true; 95 | } 96 | }); 97 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) { 98 | new Object() { 99 | void setLoadWithOverviewMode(boolean overview) { 100 | settings.setLoadWithOverviewMode(overview); 101 | } 102 | }.setLoadWithOverviewMode(true); 103 | } 104 | 105 | settings.setCacheMode(WebSettings.LOAD_DEFAULT); 106 | 107 | getWindow().getDecorView().post(new Runnable() { 108 | @Override 109 | public void run() { 110 | webView.loadUrl("https://www.jianshu.com/p/d745ea0cb5bd"); 111 | } 112 | }); 113 | } 114 | 115 | 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 |