Build a Flexible ‘One‑Tap Dismiss’ UI with AdherentLayout on Android
This article introduces the AdherentLayout component for Android, explains its customizable features, API methods, core Bézier‑curve technology, provides full implementation details with code, and demonstrates how it can recreate the popular “one‑tap dismiss” effect and other sticky‑layout scenarios.
Overview of the “One‑Tap Dismiss” feature in mobile QQ and its inspiration for a reusable UI component called AdherentLayout.
AdherentLayout
AdherentLayout is an open‑source Android component designed for sticky‑layout scenarios, offering customizable length, header size, color, breakable behavior, custom tail view, and detach event listeners.
Usage
<AdherentLayout> <!-- optional custom view --> </AdherentLayout>
API
setColor(int color) – set the sticky color.
setDismissedEnable(boolean isDismissed) – enable or disable breakable behavior.
setMaxAdherentLength(int maxAdherentLength) – set maximum sticky length.
setMinHeaderCircleRadius(int minHeaderCircleRadius) – set minimum header circle radius.
setOnAdherentListener(OnAdherentListener listener) – set detach event listener.
Core Technology
The effect relies on drawing Bézier curves; Android provides APIs for quadratic and cubic curves.
Bézier Curve
Quadratic Bézier uses one control point via quadTo(x1,y1,x2,y2); cubic Bézier uses two control points via cubicTo(...).
Implementation Details
Two cases are handled during drag:
When the drag stays within the sticky range, draw the header circle, tail circle, and the sticky body using two quadratic Bézier curves that connect the circles. The midpoint between the circle centers serves as the control point, and the header circle scales based on the distance ratio.
When the drag exceeds the range, only the tail circle is drawn.
The core drawing code:
private void drawBezier(Canvas canvas) {
// calculate angle
float atan = (float) Math.atan((mFooterCircle.y - mHeaderCircle.y) / (mFooterCircle.x - mHeaderCircle.x));
float sin = (float) Math.sin(atan);
float cos = (float) Math.cos(atan);
// four points
float headerX1 = mHeaderCircle.x - mCurrentRadius * sin;
float headerY1 = mHeaderCircle.y + mCurrentRadius * cos;
float headerX2 = mHeaderCircle.x + mCurrentRadius * sin;
float headerY2 = mHeaderCircle.y - mCurrentRadius * cos;
float footerX1 = mFooterCircle.x - mFooterCircle.radius * sin;
float footerY1 = mFooterCircle.y + mFooterCircle.radius * cos;
float footerX2 = mFooterCircle.x + mFooterCircle.radius * sin;
float footerY2 = mFooterCircle.y - mFooterCircle.radius * cos;
// control point
float anchorX = (mHeaderCircle.x + mFooterCircle.x) / 2;
float anchorY = (mHeaderCircle.y + mFooterCircle.y) / 2;
// draw path
mPath.reset();
mPath.moveTo(headerX1, headerY1);
mPath.quadTo(anchorX, anchorY, footerX1, footerY1);
mPath.lineTo(footerX2, footerY2);
mPath.quadTo(anchorX, anchorY, headerX2, headerY2);
mPath.lineTo(headerX1, headerY1);
canvas.drawPath(mPath, mPaint);
}Running Example
Conclusion
The “One‑Tap Dismiss” effect, refined through multiple product‑development iterations, is both practical and engaging, and the AdherentLayout component can be adapted to many other sticky‑layout scenarios.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
