### Android Check and First Open Panel Visibility Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Checks if the panel is being opened for the first time. If so, it sets the visibility of the 'behind' view to VISIBLE to ensure it's displayed correctly. ```java /** * Whether the closed pannel is opened at first time. * If open first, we should set the behind view's visibility as VISIBLE. */ private void checkAndFirstOpenPanel() { if (isFirstShowBehindView) { isFirstShowBehindView = false; mBehindView.setVisibility(VISIBLE); } } ``` -------------------------------- ### SlideDetailsLayout Custom ViewGroup Implementation Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md This Java code defines the `SlideDetailsLayout` custom ViewGroup, which allows switching between two child views (`mFrontView` and `mBehindView`) with smooth animations. It includes an `OnSlideDetailsListener` interface for status changes and `smoothOpen`/`smoothClose` methods to control its state. The layout handles touch events, measures its children, and manages attributes to provide a versatile sliding panel effect for Android applications. ```Java @SuppressWarnings("unused") public class SlideDetailsLayout extends ViewGroup { /** * Callback for panel OPEN-CLOSE status changed. */ public interface OnSlideDetailsListener { /** * Called after status changed. * * @param status {@link Status} */ void onStatucChanged(Status status); } public enum Status { /** Panel is closed */ CLOSE, /** Panel is opened */ OPEN; public static Status valueOf(int stats) { if (0 == stats) { return CLOSE; } else if (1 == stats) { return OPEN; } else { return CLOSE; } } } private static final float DEFAULT_PERCENT = 0.2f; private static final int DEFAULT_DURATION = 300; private View mFrontView; private View mBehindView; private float mTouchSlop; private float mInitMotionY; private float mInitMotionX; private View mTarget; private float mSlideOffset; private Status mStatus = Status.CLOSE; private boolean isFirstShowBehindView = true; private float mPercent = DEFAULT_PERCENT; private long mDuration = DEFAULT_DURATION; private int mDefaultPanel = 0; private OnSlideDetailsListener mOnSlideDetailsListener; public SlideDetailsLayout(Context context) { this(context, null); } public SlideDetailsLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideDetailsLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideDetailsLayout, defStyleAttr, 0); mPercent = a.getFloat(R.styleable.SlideDetailsLayout_percent, DEFAULT_PERCENT); mDuration = a.getInt(R.styleable.SlideDetailsLayout_duration, DEFAULT_DURATION); mDefaultPanel = a.getInt(R.styleable.SlideDetailsLayout_default_panel, 0); a.recycle(); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } /** * Set the callback of panel OPEN-CLOSE status. * * @param listener {@link OnSlideDetailsListener} */ public void setOnSlideDetailsListener(OnSlideDetailsListener listener) { this.mOnSlideDetailsListener = listener; } /** * Open pannel smoothly. * * @param smooth true, smoothly. false otherwise. */ public void smoothOpen(boolean smooth) { if (mStatus != Status.OPEN) { mStatus = Status.OPEN; final float height = -getMeasuredHeight(); animatorSwitch(0, height, true, smooth ? mDuration : 0); } } /** * Close pannel smoothly. * * @param smooth true, smoothly. false otherwise. */ public void smoothClose(boolean smooth) { if (mStatus != Status.CLOSE) { mStatus = Status.CLOSE; final float height = -getMeasuredHeight(); animatorSwitch(height, 0, true, smooth ? mDuration : 0); } } /** * Set the float value for indicate the moment of switch panel * * @param percent (0.0, 1.0) */ public void setPercent(float percent) { this.mPercent = percent; } @Override protected LayoutParams generateDefaultLayoutParams() { return new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } @Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new MarginLayoutParams(p); } @Override protected void onFinishInflate() { super.onFinishInflate(); final int childCount = getChildCount(); if (1 >= childCount) { throw new RuntimeException("SlideDetailsLayout only accept childs more than 1!!"); } mFrontView = getChildAt(0); mBehindView = getChildAt(1); // set behindview's visibility to GONE before show. //mBehindView.setVisibility(GONE); if(mDefaultPanel == 1){ post(new Runnable() { @Override public void run() { smoothOpen(false); } }); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int pWidth = MeasureSpec.getSize(widthMeasureSpec); ``` -------------------------------- ### Android Panel Animation Control with ValueAnimator Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Provides overloaded methods to initiate a ValueAnimator for smooth panel transitions. It updates the slide offset during animation and triggers callbacks upon animation completion, handling state changes and listener notifications. ```java private void animatorSwitch(final float start, final float end) { animatorSwitch(start, end, true, mDuration); } private void animatorSwitch(final float start, final float end, final long duration) { animatorSwitch(start, end, true, duration); } private void animatorSwitch(final float start, final float end, final boolean changed) { animatorSwitch(start, end, changed, mDuration); } private void animatorSwitch(final float start, final float end, final boolean changed, final long duration) { ValueAnimator animator = ValueAnimator.ofFloat(start, end); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mSlideOffset = (float) animation.getAnimatedValue(); requestLayout(); } }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (changed) { if (mStatus == Status.OPEN) { checkAndFirstOpenPanel(); } if (null != mOnSlideDetailsListener) { mOnSlideDetailsListener.onStatucChanged(mStatus); } } } }); animator.setDuration(duration); animator.start(); } ``` -------------------------------- ### Android Panel Gesture End State Management Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Determines the final state of the panel (OPEN or CLOSE) after a touch gesture ends. It calculates whether the panel should snap to fully open or closed based on a percentage threshold and triggers the animation. ```java /** * Called after gesture is ending. */ private void finishTouchEvent() { final int pHeight = getMeasuredHeight(); final int percent = (int) (pHeight * mPercent); final float offset = mSlideOffset; boolean changed = false; if (Status.CLOSE == mStatus) { if (offset <= -percent) { mSlideOffset = -pHeight; mStatus = Status.OPEN; changed = true; } else { // keep panel closed mSlideOffset = 0; } } else if (Status.OPEN == mStatus) { if ((offset + pHeight) >= percent) { mSlideOffset = 0; mStatus = Status.CLOSE; changed = true; } else { // keep panel opened mSlideOffset = -pHeight; } } animatorSwitch(offset, mSlideOffset, changed); } ``` -------------------------------- ### Process Touch Displacement for Android Sliding Panel Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md This private `processTouchEvent` method updates the `mSlideOffset` based on the vertical displacement from a touch event. It includes a threshold check (`mTouchSlop`) to ignore minor movements and specific logic for when the panel is in a `CLOSE` status, preventing it from sliding down if pulled in that direction. ```Java /** * @param offset Displacement in vertically. */ private void processTouchEvent(final float offset) { if (Math.abs(offset) < mTouchSlop) { return; } final float oldOffset = mSlideOffset; // pull up to open if (mStatus == Status.CLOSE) { // reset if pull down if (offset >= 0) { mSlideOffset = 0; } else { mSlideOffset = offset; } } ``` -------------------------------- ### Handle Touch Events for Android Custom View Sliding Panel Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md The `onTouchEvent` method processes touch events once they are intercepted by the custom view. It handles `ACTION_DOWN` to potentially claim touch, `ACTION_MOVE` to update the sliding offset via `processTouchEvent` if the child is not scrolling vertically, and `ACTION_UP`/`ACTION_CANCEL` to finalize the touch interaction by calling `finishTouchEvent`. ```Java @Override public boolean onTouchEvent(MotionEvent ev) { ensureTarget(); if (null == mTarget) { return false; } if (!isEnabled()) { return false; } boolean wantTouch = true; final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: { // if target is a view, we want the DOWN action. if (mTarget instanceof View) { wantTouch = true; } break; } case MotionEvent.ACTION_MOVE: { final float y = ev.getY(); final float yDiff = y - mInitMotionY; if (canChildScrollVertically(((int) yDiff))) { wantTouch = false; } else { processTouchEvent(yDiff); wantTouch = true; } break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { finishTouchEvent(); wantTouch = false; break; } } return wantTouch; } ``` -------------------------------- ### Android Panel Target View Selection Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Determines the current target view for interaction based on the panel's status. If the panel is closed, the front view is the target; otherwise, the behind view is the target. ```java /** * When pulling, target view changed by the panel status. If panel opened, the target is behind view. * Front view is for otherwise. */ private void ensureTarget() { if (mStatus == Status.CLOSE) { mTarget = mFrontView; } else { mTarget = mBehindView; } } ``` -------------------------------- ### Android Custom View SavedState Class Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Defines a static inner class `SavedState` that extends `BaseSavedState` to manage and persist the custom view's state (offset and status). It includes constructors for reading from and writing to a `Parcel`, and a `CREATOR` implementation required for deserialization of `Parcelable` objects. ```Java static class SavedState extends BaseSavedState { private float offset; private int status; /** * Constructor used when reading from a parcel. Reads the state of the superclass. * * @param source */ public SavedState(Parcel source) { super(source); offset = source.readFloat(); status = source.readInt(); } /** * Constructor called by derived classes when creating their SavedState objects * * @param superState The state of the superclass of this view */ public SavedState(Parcelable superState) { super(superState); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeFloat(offset); out.writeInt(status); } public static final Creator CREATOR = new Creator() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } ``` -------------------------------- ### Intercept Touch Events for Android Custom View Sliding Panel Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md This `onInterceptTouchEvent` method determines whether the custom view should intercept touch events. It checks if the view is enabled and has a target, then analyzes touch movement (vertical vs. horizontal displacement) and the current panel status (`CLOSE` or `OPEN`) to decide if the view should take control of the scroll gesture. ```Java @Override public boolean onInterceptTouchEvent(MotionEvent ev) { ensureTarget(); if (null == mTarget) { return false; } if (!isEnabled()) { return false; } final int aciton = MotionEventCompat.getActionMasked(ev); boolean shouldIntercept = false; switch (aciton) { case MotionEvent.ACTION_DOWN: { mInitMotionX = ev.getX(); mInitMotionY = ev.getY(); shouldIntercept = false; break; } case MotionEvent.ACTION_MOVE: { final float x = ev.getX(); final float y = ev.getY(); final float xDiff = x - mInitMotionX; final float yDiff = y - mInitMotionY; if (canChildScrollVertically((int) yDiff)) { shouldIntercept = false; } else { final float xDiffabs = Math.abs(xDiff); final float yDiffabs = Math.abs(yDiff); // intercept rules: // 1. The vertical displacement is larger than the horizontal displacement; // 2. Panel stauts is CLOSE:slide up // 3. Panel status is OPEN:slide down if (yDiffabs > mTouchSlop && yDiffabs >= xDiffabs && !(mStatus == Status.CLOSE && yDiff > 0 || mStatus == Status.OPEN && yDiff < 0)) { shouldIntercept = true; } } break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { shouldIntercept = false; break; } } return shouldIntercept; } ``` -------------------------------- ### Android Custom WebView for Scroll Interception (ItemWebView) Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md This Java code defines `ItemWebView`, a custom `WebView` class. It extends `WebView` and overrides `onTouchEvent` and `onScrollChanged` to manage touch event interception with its parent. The primary goal is to allow the parent `SlideDetailsLayout` to regain touch control when the `WebView` is scrolled to its very top, preventing accidental scrolls of the parent when the user intends to scroll the `WebView` itself. ```Java /** * 商品详情页底部的webview */ public class ItemWebView extends WebView { public float oldY; private int t; private float oldX; public ItemWebView(Context context) { super(context); } public ItemWebView(Context context, AttributeSet attrs) { super(context, attrs); } public ItemWebView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: float Y = ev.getY(); float Ys = Y - oldY; float X = ev.getX(); //滑动到顶部让父控件重新获得触摸事件 if (Ys > 0 && t == 0) { getParent().getParent().requestDisallowInterceptTouchEvent(false); } break; case MotionEvent.ACTION_DOWN: getParent().getParent().requestDisallowInterceptTouchEvent(true); oldY = ev.getY(); oldX = ev.getX(); break; case MotionEvent.ACTION_UP: getParent().getParent().requestDisallowInterceptTouchEvent(true); break; default: break; } return super.onTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { this.t = t; super.onScrollChanged(l, t, oldl, oldt); } } ``` -------------------------------- ### Android Custom ListView for Scroll Interception (ItemListView) Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md This Java code defines `ItemListView`, a custom `ListView` class. Similar to `ItemWebView`, it extends `ListView` and implements `AbsListView.OnScrollListener` to manage touch event interception with its parent. It ensures that the parent `SlideDetailsLayout` regains touch control when the `ListView` is scrolled to its very top, providing a consistent scrolling experience. ```Java /** * 商品详情页底部的ListView */ public class ItemListView extends ListView implements AbsListView.OnScrollListener { private float oldX, oldY; private int currentPosition; public ItemListView(Context context) { super(context); setOnScrollListener(this); } public ItemListView(Context context, AttributeSet attrs) { super(context, attrs); setOnScrollListener(this); } public ItemListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setOnScrollListener(this); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: float Y = ev.getY(); float Ys = Y - oldY; float X = ev.getX(); int [] location = new int [2]; getLocationInWindow(location); //滑动到顶部让父控件重新获得触摸事件 if (Ys > 0 && currentPosition == 0) { getParent().getParent().requestDisallowInterceptTouchEvent(false); } break; case MotionEvent.ACTION_DOWN: getParent().getParent().requestDisallowInterceptTouchEvent(true); oldY = ev.getY(); oldX = ev.getX(); break; case MotionEvent.ACTION_UP: getParent().getParent().requestDisallowInterceptTouchEvent(true); break; default: break; } return super.onTouchEvent(ev); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { currentPosition = getFirstVisiblePosition(); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } } ``` -------------------------------- ### Android Panel Slide Offset Calculation Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Calculates and updates the panel's slide offset based on its current status (OPEN or CLOSE) and touch input. It prevents unnecessary layout requests if the offset hasn't changed. ```java } if (mSlideOffset == oldOffset) { return; } // pull down to close } else if (mStatus == Status.OPEN) { final float pHeight = -getMeasuredHeight(); // reset if pull up if (offset <= 0) { mSlideOffset = pHeight; } else { final float newOffset = pHeight + offset; mSlideOffset = newOffset; } if (mSlideOffset == oldOffset) { return; } } // relayout requestLayout(); ``` -------------------------------- ### Restore Custom View Instance State Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Overrides the `onRestoreInstanceState` method to retrieve and apply the previously saved state to the custom view. It casts the incoming `Parcelable` to `SavedState`, updates the slide offset and status, and adjusts view visibility based on the restored status before requesting a layout update. ```Java @Override protected void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); mSlideOffset = ss.offset; mStatus = Status.valueOf(ss.status); if (mStatus == Status.OPEN) { mBehindView.setVisibility(VISIBLE); } requestLayout(); } ``` -------------------------------- ### Save Custom View Instance State Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Overrides the `onSaveInstanceState` method to persist the custom view's internal state. It creates a `SavedState` object, populating it with the current slide offset and status, ensuring these values are retained across configuration changes. ```Java @Override protected Parcelable onSaveInstanceState() { SavedState ss = new SavedState(super.onSaveInstanceState()); ss.offset = mSlideOffset; ss.status = mStatus.ordinal(); return ss; } ``` -------------------------------- ### Layout Child Views in Android Custom ViewGroup Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md The `onLayout` method positions child views within the custom Android `ViewGroup`. It calculates the `top` and `bottom` coordinates for each child, applying an `mSlideOffset` to create a sliding effect, particularly for a 'behind' view, while ensuring other children also adjust their positions. ```Java @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int left = l; final int right = r; int top; int bottom; final int offset = (int) mSlideOffset; View child; for (int i = 0; i < getChildCount(); i++) { child = getChildAt(i); // skip layout if (child.getVisibility() == GONE) { continue; } if (child == mBehindView) { top = b + offset; bottom = top + b - t; } else { top = t + offset; bottom = b + offset; } child.layout(left, top, right, bottom); } } ``` -------------------------------- ### Check AbsListView Scrollability Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Checks if an `AbsListView` (like `ListView` or `GridView`) can scroll, considering its current status (OPEN/CLOSED) and the visibility of its child items. It handles both scrolling up (when OPEN) and scrolling down (when CLOSED) based on the list's content and dimensions. ```Java protected boolean canListViewSroll(AbsListView absListView) { if (mStatus == Status.OPEN) { return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) .getTop() < absListView.getPaddingTop()); } else { final int count = absListView.getChildCount(); return count > 0 && (absListView.getLastVisiblePosition() < count - 1 || absListView.getChildAt(count - 1) .getBottom() > absListView.getMeasuredHeight()); } } ``` -------------------------------- ### Check Vertical Scrollability for Generic View Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Determines if a target view can scroll vertically in a given direction, leveraging `ViewCompat.canScrollVertically` for compatibility across Android versions. ```Java return ViewCompat.canScrollVertically(mTarget, -direction); ``` -------------------------------- ### Measure Child Views in Android Custom ViewGroup Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md This `onMeasure` method calculates the dimensions for child views within a custom Android `ViewGroup`. It ensures all children are measured with the exact width and height provided by the parent, skipping any views that are set to `GONE` visibility. ```Java final int pHeight = MeasureSpec.getSize(heightMeasureSpec); final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(pWidth, MeasureSpec.EXACTLY); final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(pHeight, MeasureSpec.EXACTLY); View child; for (int i = 0; i < getChildCount(); i++) { child = getChildAt(i); // skip measure if gone if (child.getVisibility() == GONE) { continue; } measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec); } setMeasuredDimension(pWidth, pHeight); } ``` -------------------------------- ### Android Check Child View Vertical Scrollability Source: https://github.com/hexianqiao3755/goodsinfopage/blob/master/README.md Checks if a child view within the panel can scroll vertically in a given direction. It handles different view types like AbsListView, FrameLayout, RelativeLayout, and LinearLayout, and uses ViewCompat for compatibility. ```java /** * Check child view can srcollable in vertical direction. * * @param direction Negative to check scrolling up, positive to check scrolling down. * * @return true if this view can be scrolled in the specified direction, false otherwise. */ protected boolean canChildScrollVertically(int direction) { if (mTarget instanceof AbsListView) { return canListViewSroll((AbsListView) mTarget); } else if (mTarget instanceof FrameLayout || mTarget instanceof RelativeLayout || mTarget instanceof LinearLayout) { View child; for (int i = 0; i < ((ViewGroup) mTarget).getChildCount(); i++) { child = ((ViewGroup) mTarget).getChildAt(i); if (child instanceof AbsListView) { return canListViewSroll((AbsListView) child); } } } if (android.os.Build.VERSION.SDK_INT < 14) { return ViewCompat.canScrollVertically(mTarget, -direction) || mTarget.getScrollY() > 0; } else { ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.