Android
नीचे की चादरें
खोज…
परिचय
एक निचली शीट एक शीट है जो स्क्रीन के निचले किनारे से ऊपर स्लाइड करती है।
टिप्पणियों
नीचे की शीट अधिक सामग्री को प्रकट करने के लिए स्क्रीन के नीचे से स्लाइड करती है।
उन्हें v23.2.0 संस्करण में एंड्रॉइड सपोर्ट लाइब्रेरी में जोड़ा गया था।
बॉटमशीटबहेवियर गूगल मैप्स की तरह
यह उदाहरण समर्थन लाइब्रेरी 23.4.0 + पर निर्भर करता है।
BottomSheetBehavior की विशेषता है:
- एनिमेशन के साथ दो टूलबार जो नीचे शीट आंदोलनों का जवाब देते हैं।
- एक एफएबी जो "मोडल टूलबार" के पास छुपाता है (वह है जो तब दिखाई देता है जब आप ऊपर स्लाइड कर रहे होते हैं)।
- कुछ प्रकार के लंबन प्रभाव के साथ नीचे की शीट के पीछे एक पृष्ठभूमि छवि।
- टूलबार में एक शीर्षक (पाठ दृश्य) जो नीचे की शीट तक पहुंचने पर दिखाई देता है।
- नोटिफिकेशन सैटस बार इसकी पृष्ठभूमि को पारदर्शी या पूर्ण रंग में बदल सकता है।
- एक "लंगर" राज्य के साथ एक कस्टम तल शीट व्यवहार।
अब एक-एक करके उनकी जाँच करते हैं:
टूलबार
जब आप Google मानचित्र में उस दृश्य को खोलते हैं, तो आप एक टूलबार देख सकते हैं, जहाँ आप खोज कर सकते हैं, यह केवल वही है जो मैं Google मैप्स की तरह नहीं कर रहा हूँ, क्योंकि मैं इसे और अधिक सामान्य करना चाहता था। वैसे भी है कि ToolBar
एक अंदर है AppBarLayout
और इसे छिपा कर ली जब आप BottomSheet खींचना शुरू करें और इसे फिर से प्रकट होता है जब BottomSheet तक पहुँचने के COLLAPSED
राज्य।
इसे प्राप्त करने के लिए आपको इसकी आवश्यकता है:
- एक
Behavior
बनाएँ औरAppBarLayout.ScrollingViewBehavior
से इसका विस्तार करें - ओवरराइड
layoutDependsOn
औरonDependentViewChanged
तरीके। ऐसा करने से आप बॉटमशीट मूवमेंट के लिए सुनेंगे। - एनिमेशन के साथ AppBarLayout / ToolBar को छिपाने और अनसाइड करने के लिए कुछ तरीके बनाएं।
ऐसा मैंने पहले टूलबार या एक्शनबार के लिए किया था:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof NestedScrollView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
if (mChild == null) {
initValues(child, dependency);
return false;
}
float dVerticalScroll = dependency.getY() - mPreviousY;
mPreviousY = dependency.getY();
//going up
if (dVerticalScroll <= 0 && !hidden) {
dismissAppBar(child);
return true;
}
return false;
}
private void initValues(final View child, View dependency) {
mChild = child;
mInitialY = child.getY();
BottomSheetBehaviorGoogleMapsLike bottomSheetBehavior = BottomSheetBehaviorGoogleMapsLike.from(dependency);
bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, @BottomSheetBehaviorGoogleMapsLike.State int newState) {
if (newState == BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED ||
newState == BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN)
showAppBar(child);
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
private void dismissAppBar(View child){
hidden = true;
AppBarLayout appBarLayout = (AppBarLayout)child;
mToolbarAnimation = appBarLayout.animate().setDuration(mContext.getResources().getInteger(android.R.integer.config_shortAnimTime));
mToolbarAnimation.y(-(mChild.getHeight()+25)).start();
}
private void showAppBar(View child) {
hidden = false;
AppBarLayout appBarLayout = (AppBarLayout)child;
mToolbarAnimation = appBarLayout.animate().setDuration(mContext.getResources().getInteger(android.R.integer.config_mediumAnimTime));
mToolbarAnimation.y(mInitialY).start();
}
यहां आपको जरूरत होने पर पूरी फाइल दी गई है
दूसरा टूलबार या "मोडल" टूलबार:
आपको समान विधियों को ओवरराइड करना होगा, लेकिन इसमें आपको अधिक व्यवहारों का ध्यान रखना होगा:
- एनिमेशन के साथ टूलबार दिखाएँ / छिपाएँ
- स्थिति पट्टी रंग / पृष्ठभूमि बदलें
- टूलबार में बॉटमशीट शीर्षक दिखाएं / छिपाएँ
- बॉटमशीट बंद करें या उसे ढह गई स्थिति में भेजें
इस एक के लिए कोड थोड़ा व्यापक है, इसलिए मैं लिंक दूंगा
एफएबी
यह एक कस्टम व्यवहार भी है, लेकिन FloatingActionButton.Behavior
से फैलता है। FloatingActionButton.Behavior
। onDependentViewChanged
आपको यह देखना होगा कि यह "ऑफ़सेट" तक पहुँचेगा या उस बिंदु पर जहाँ आप इसे छुपाना चाहते हैं। मेरे मामले में मैं इसे तब छिपाना चाहता हूं जब यह दूसरे टूलबार के पास होता है, इसलिए मैं FAB माता-पिता (एक समन्वयकलेयआउट) में खुदाई करता हूं जो AppBarLayout की तलाश करता है जिसमें टूलबार शामिल है, फिर मैं OffSet
जैसे टूलबार स्थिति का उपयोग करता OffSet
:
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
if (offset == 0)
setOffsetValue(parent);
if (dependency.getY() <=0)
return false;
if (child.getY() <= (offset + child.getHeight()) && child.getVisibility() == View.VISIBLE)
child.hide();
else if (child.getY() > offset && child.getVisibility() != View.VISIBLE)
child.show();
return false;
}
लंबन प्रभाव के साथ बॉटमशीट के पीछे की छवि :
अन्य लोगों की तरह, यह एक कस्टम व्यवहार है, इसमें केवल "जटिल" चीज़ थोड़ी एल्गोरिथ्म है जो छवि को बॉटमशीट के लिए लंगर डाले रखती है और डिफ़ॉल्ट लंबन प्रभाव जैसी छवि के पतन से बचती है:
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
if (mYmultiplier == 0) {
initValues(child, dependency);
return true;
}
float dVerticalScroll = dependency.getY() - mPreviousY;
mPreviousY = dependency.getY();
//going up
if (dVerticalScroll <= 0 && child.getY() <= 0) {
child.setY(0);
return true;
}
//going down
if (dVerticalScroll >= 0 && dependency.getY() <= mImageHeight)
return false;
child.setY( (int)(child.getY() + (dVerticalScroll * mYmultiplier) ) );
return true;
}
लंबन प्रभाव के साथ पृष्ठभूमि छवि के लिए पूरी फ़ाइल
अब अंत के लिए: कस्टम बॉटमशीट व्यवहार
3 चरणों को प्राप्त करने के लिए, पहले आपको यह समझने की आवश्यकता है कि डिफ़ॉल्ट बॉटमशीटबेहाइवर में 5 स्टेट्स हैं: STATE_DRAGGING, STATE_SETTLING, STATE_EXPANDED, STATE_COLLAPSED, STATE_HIDDEN
और Google मैप्स व्यवहार के लिए, जो कि STATE_ANCHOR_POINT
और विस्तारित के बीच एक मध्य स्थिति जोड़ने की जरूरत है।
मैंने बिना किसी सफलता के डिफ़ॉल्ट बॉटमशीट को बढ़ाने की कोशिश की, इसलिए मैंने सिर्फ सभी कोड को कॉपी किया और जो मुझे चाहिए उसे संशोधित किया।
अगले चरणों का पालन करने के लिए मैं जो बात कर रहा हूँ, उसे प्राप्त करने के लिए:
एक जावा क्लास बनाएं और इसे
CoordinatorLayout.Behavior<V>
सेCoordinatorLayout.Behavior<V>
अपने नए के लिए डिफ़ॉल्ट
BottomSheetBehavior
फाइल से पेस्ट कोड कॉपी करें।निम्न कोड के साथ विधि
clampViewPositionVertical
को संशोधित करें:@Override public int clampViewPositionVertical(View child, int top, int dy) { return constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset); } int constrain(int amount, int low, int high) { return amount < low ? low : (amount > high ? high : amount); }
एक नई स्थिति जोड़ें
सार्वजनिक स्थैतिक अंतिम int STATE_ANCHOR_POINT = X;
अगले तरीकों को संशोधित करें:
onLayoutChild
,onStopNestedScroll
,BottomSheetBehavior<V> from(V view)
औरsetState
(वैकल्पिक)
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
// First let the parent lay it out
if (mState != STATE_DRAGGING && mState != STATE_SETTLING) {
if (ViewCompat.getFitsSystemWindows(parent) &&
!ViewCompat.getFitsSystemWindows(child)) {
ViewCompat.setFitsSystemWindows(child, true);
}
parent.onLayoutChild(child, layoutDirection);
}
// Offset the bottom sheet
mParentHeight = parent.getHeight();
mMinOffset = Math.max(0, mParentHeight - child.getHeight());
mMaxOffset = Math.max(mParentHeight - mPeekHeight, mMinOffset);
//if (mState == STATE_EXPANDED) {
// ViewCompat.offsetTopAndBottom(child, mMinOffset);
//} else if (mHideable && mState == STATE_HIDDEN...
if (mState == STATE_ANCHOR_POINT) {
ViewCompat.offsetTopAndBottom(child, mAnchorPoint);
} else if (mState == STATE_EXPANDED) {
ViewCompat.offsetTopAndBottom(child, mMinOffset);
} else if (mHideable && mState == STATE_HIDDEN) {
ViewCompat.offsetTopAndBottom(child, mParentHeight);
} else if (mState == STATE_COLLAPSED) {
ViewCompat.offsetTopAndBottom(child, mMaxOffset);
}
if (mViewDragHelper == null) {
mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
}
mViewRef = new WeakReference<>(child);
mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));
return true;
}
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
if (child.getTop() == mMinOffset) {
setStateInternal(STATE_EXPANDED);
return;
}
if (target != mNestedScrollingChildRef.get() || !mNestedScrolled) {
return;
}
int top;
int targetState;
if (mLastNestedScrollDy > 0) {
//top = mMinOffset;
//targetState = STATE_EXPANDED;
int currentTop = child.getTop();
if (currentTop > mAnchorPoint) {
top = mAnchorPoint;
targetState = STATE_ANCHOR_POINT;
}
else {
top = mMinOffset;
targetState = STATE_EXPANDED;
}
} else if (mHideable && shouldHide(child, getYVelocity())) {
top = mParentHeight;
targetState = STATE_HIDDEN;
} else if (mLastNestedScrollDy == 0) {
int currentTop = child.getTop();
if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
top = mMinOffset;
targetState = STATE_EXPANDED;
} else {
top = mMaxOffset;
targetState = STATE_COLLAPSED;
}
} else {
//top = mMaxOffset;
//targetState = STATE_COLLAPSED;
int currentTop = child.getTop();
if (currentTop > mAnchorPoint) {
top = mMaxOffset;
targetState = STATE_COLLAPSED;
}
else {
top = mAnchorPoint;
targetState = STATE_ANCHOR_POINT;
}
}
if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
setStateInternal(STATE_SETTLING);
ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
} else {
setStateInternal(targetState);
}
mNestedScrolled = false;
}
public final void setState(@State int state) {
if (state == mState) {
return;
}
if (mViewRef == null) {
// The view is not laid out yet; modify mState and let onLayoutChild handle it later
/**
* New behavior (added: state == STATE_ANCHOR_POINT ||)
*/
if (state == STATE_COLLAPSED || state == STATE_EXPANDED ||
state == STATE_ANCHOR_POINT ||
(mHideable && state == STATE_HIDDEN)) {
mState = state;
}
return;
}
V child = mViewRef.get();
if (child == null) {
return;
}
int top;
if (state == STATE_COLLAPSED) {
top = mMaxOffset;
} else if (state == STATE_ANCHOR_POINT) {
top = mAnchorPoint;
} else if (state == STATE_EXPANDED) {
top = mMinOffset;
} else if (mHideable && state == STATE_HIDDEN) {
top = mParentHeight;
} else {
throw new IllegalArgumentException("Illegal state argument: " + state);
}
setStateInternal(STATE_SETTLING);
if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
}
}
public static <V extends View> BottomSheetBehaviorGoogleMapsLike<V> from(V view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)) {
throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
}
CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
.getBehavior();
if (!(behavior instanceof BottomSheetBehaviorGoogleMapsLike)) {
throw new IllegalArgumentException(
"The view is not associated with BottomSheetBehaviorGoogleMapsLike");
}
return (BottomSheetBehaviorGoogleMapsLike<V>) behavior;
}
पूरे प्रोजेक्ट से लिंक करें जहां आप सभी कस्टम व्यवहार देख सकते हैं
और यहाँ यह है कि यह कैसा दिखता है:
[ ]
तत्काल प्रबंध
सुनिश्चित करें कि निम्न निर्भरता आपके ऐप की बिल्ड.ग्रेड फ़ाइल में निर्भरता के तहत जोड़ी गई है:
compile 'com.android.support:design:25.3.1'
फिर आप इन विकल्पों का उपयोग करके नीचे की शीट का उपयोग कर सकते हैं:
-
CoordinatorLayout
साथ इस्तेमाल किया जाने वालाBottomSheetBehavior
-
BottomSheetDialog
जो कि एक शीट शीट व्यवहार के साथ एक संवाद है -
BottomSheetDialogFragment
जो का एक विस्तार हैDialogFragment
, कि एक बनाता हैBottomSheetDialog
के बजाय एक मानक संवाद।
लगातार नीचे की चादरें
आप एक बच्चे को एक CoordinatorLayout
दृश्य के लिए एक BottomSheetBehavior
को संलग्न करके एक निरंतर तल शीट प्राप्त कर सकते हैं:
<android.support.design.widget.CoordinatorLayout >
<!-- ..... -->
<LinearLayout
android:id="@+id/bottom_sheet"
android:elevation="4dp"
android:minHeight="120dp"
app:behavior_peekHeight="120dp"
...
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<!-- ..... -->
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
फिर अपने कोड में आप उपयोग करके एक संदर्भ बना सकते हैं:
// The View with the BottomSheetBehavior
View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
BottomSheetBehavior mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
आप सेटस्टैट () पद्धति का उपयोग करके अपने बॉटमशीटबेहेयर की स्थिति सेट कर सकते हैं:
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
आप इनमें से किसी एक राज्य का उपयोग कर सकते हैं:
STATE_COLLAPSED
: यह ध्वस्त स्थिति डिफ़ॉल्ट है और नीचे के साथ लेआउट का एक हिस्सा दिखाता है। ऊंचाई कोapp:behavior_peekHeight
साथ नियंत्रित किया जा सकता हैapp:behavior_peekHeight
क्षमता (डिफ़ॉल्ट के लिए चूक)STATE_EXPANDED
: नीचे की शीट की पूरी तरह से विस्तारित स्थिति, जहां या तो पूरी नीचे की शीट दिखाई देती है (यदि इसकी ऊंचाईSTATE_EXPANDED
CoordinatorLayout
से कम है) या संपूर्णCoordinatorLayout
भरा हुआ हैSTATE_HIDDEN
: डिफ़ॉल्ट रूप से अक्षम (औरapp:behavior_hideable
साथ सक्षम:STATE_HIDDEN
विशेषता), यह सक्षम करने से उपयोगकर्ता नीचे की शीट पर पूरी तरह से नीचे की शीट को छिपाने के लिए नीचे स्वाइप कर सकते हैं
यदि आप राज्य परिवर्तन के कॉलबैक प्राप्त करना चाहते हैं, तो आप एक BottomSheetCallback
कॉलबैक जोड़ सकते हैं:
mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
// React to state change
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
// React to dragging events
}
});
बॉटमशीटडायलॉगफ्रैगमेंट के साथ मोडल बॉटम शीट
आप एक BottomSheetDialogFragment
का उपयोग करके एक मोडल बॉटम शीट को महसूस कर सकते हैं।
BottomSheetDialogFragment
एक मोडल बॉटम शीट है।
यह DialogFragment
का एक संस्करण है जो फ़्लोटिंग डायलॉग के बजाय BottomSheetDialog
का उपयोग करके एक निचली शीट दिखाता है।
बस खंड को परिभाषित करें:
public class MyBottomSheetDialogFragment extends BottomSheetDialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.my_fragment_bottom_sheet, container);
}
}
फिर टुकड़े को दिखाने के लिए इस कोड का उपयोग करें:
MyBottomSheetDialogFragment mySheetDialog = new MyBottomSheetDialogFragment();
FragmentManager fm = getSupportFragmentManager();
mySheetDialog.show(fm, "modalSheetDialog");
यह फ़्रैगमेंट एक BottomSheetDialog
।
बॉटमशीटडायलॉग के साथ मोडल बॉटम शीट
द BottomSheetDialog
एक संवाद है जिसे नीचे की शीट के रूप में स्टाइल किया गया है
महज प्रयोग करें:
//Create a new BottomSheetDialog
BottomSheetDialog dialog = new BottomSheetDialog(context);
//Inflate the layout R.layout.my_dialog_layout
dialog.setContentView(R.layout.my_dialog_layout);
//Show the dialog
dialog.show();
इस मामले में आपको एक बॉटमशीट व्यवहार संलग्न करने की आवश्यकता नहीं है।
डिफ़ॉल्ट रूप से विस्तृत मोड में निचला बॉटम डायलॉगफ़्रेग्मेंट खोलें।
बॉटमशीट डायलॉगफ्रैगमेंट डिफ़ॉल्ट रूप से STATE_COLLAPSED
में STATE_COLLAPSED
है। जिसे STATE_EXPANDED
को खोलने के लिए मजबूर किया जा सकता है और निम्नलिखित कोड टेम्पलेट की मदद से पूरी डिवाइस स्क्रीन को ले जा सकता है।
@NonNull @Override सार्वजनिक संवाद onCreateDialog (बंडल बचाया
BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
// Do something with your dialog like setContentView() or whatever
return dialog;
}
हालांकि डायलॉग एनीमेशन थोड़ा ध्यान देने योग्य है लेकिन डायलॉगफ्रेम को पूरी स्क्रीन में खोलने का काम बहुत अच्छी तरह से करता है।