I have a "details" fragment which have a lot of textviews, relativelayouts etc. These are wrapped inside a NestedScrollView:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:orientation="vertical"
android:paddingBottom="10dp">
<android.support.v4.widget.NestedScrollView
android:id="#+id/movie_details_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent">
// Here are the textviews, relativelayouts etc...
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="#+id/edit_movies_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_edit_white_24dp"
/>
</android.support.design.widget.CoordinatorLayout>
Now i want to to add a FAB at the bottom of the screen (not at the bottom of the nestedscrollview) which also scroll down when i scroll through the nestedscrollview. But with my code the FAB is always at the bottom of the nestedscrollview. So when i scroll all the way down the FAB appears. I want that the FAB is always visible in the right bottom corner...
EDIT
I forgot to mention that i use fading action bar (https://github.com/ManuelPeinado/FadingActionBar) but a bit edited.
Relevant code:
m_FadingActionBarHelper.createView(getContext()); // this will create the view with header content etc.
The createView:
public final View createView(LayoutInflater inflater) {
//
// Prepare everything
mInflater = inflater;
if (mContentView == null) {
mContentView = inflater.inflate(mContentLayoutResId, null); // this will load my view which i already posted.
}
if (mHeaderView == null) {
mHeaderView = inflater.inflate(mHeaderLayoutResId, null, false);
}
// See if we are in a ListView, WebView or ScrollView scenario
ListView listView = (ListView) mContentView.findViewById(android.R.id.list);
View root;
if (listView != null) {
root = createListView(listView);
} else if (mContentView instanceof CDMObservableWebViewWithHeader){
root = createWebView();
} else {
root = createScrollView(); // this will be called in my example
}
if (mHeaderOverlayView == null && mHeaderOverlayLayoutResId != 0) {
mHeaderOverlayView = inflater.inflate(mHeaderOverlayLayoutResId, mMarginView, false);
}
if (mHeaderOverlayView != null) {
mMarginView.addView(mHeaderOverlayView);
}
// Use measured height here as an estimate of the header height, later on after the layout is complete
// we'll use the actual height
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
mHeaderView.measure(widthMeasureSpec, heightMeasureSpec);
updateHeaderHeight(mHeaderView.getMeasuredHeight());
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int headerHeight = mHeaderContainer.getHeight();
if ((!mFirstGlobalLayoutPerformed || (headerHeight != mLastHeaderHeight)) && headerHeight != 0) {
updateHeaderHeight(headerHeight);
mFirstGlobalLayoutPerformed = true;
}
}
});
return root;
}
The createScrollView:
private View createScrollView() {
ViewGroup scrollViewContainer = (ViewGroup) mInflater.inflate(R.layout.fab__scrollview_container, null);
CDMObservableScrollView scrollView = (CDMObservableScrollView) scrollViewContainer.findViewById(R.id.fab__scroll_view);
scrollView.setOnScrollChangedCallback(mOnScrollChangedListener);
ViewGroup contentContainer = (ViewGroup) scrollViewContainer.findViewById(R.id.fab__container);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mContentView.setLayoutParams(layoutParams);
contentContainer.addView(mContentView);
mHeaderContainer = (FrameLayout) scrollViewContainer.findViewById(R.id.fab__header_container);
initializeGradient(mHeaderContainer);
mHeaderContainer.addView(mHeaderView, 0);
mMarginView = (FrameLayout) contentContainer.findViewById(R.id.fab__content_top_margin);
return scrollViewContainer;
}
The xml which will be loaded:
<denis.de.meperdia.fadingactionbar.CDMRootLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/fab__header_container"/>
<denis.de.meperdia.fadingactionbar.CDMObservableScrollView
android:id="#+id/fab__scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/fab__container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/fab__content_top_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"/>
</LinearLayout>
</denis.de.meperdia.fadingactionbar.CDMObservableScrollView>
</denis.de.meperdia.fadingactionbar.CDMRootLayout>
The class CDMRootLayout:
public class CDMRootLayout extends CoordinatorLayout {
private View mHeaderContainer;
private View mListViewBackground;
private boolean mInitialized = false;
public CDMRootLayout(Context context) {
super(context);
}
public CDMRootLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CDMRootLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//at first find headerViewContainer and listViewBackground
if(mHeaderContainer == null)
mHeaderContainer = findViewById(R.id.fab__header_container);
if(mListViewBackground == null)
mListViewBackground = findViewById(R.id.fab__listview_background);
//if there's no headerViewContainer then fallback to standard FrameLayout
if(mHeaderContainer == null) {
super.onLayout(changed, left, top, right, bottom);
return;
}
if(!mInitialized) {
super.onLayout(changed, left, top, right, bottom);
//if mListViewBackground not exists or mListViewBackground exists
//and its top is at headercontainer height then view is initialized
if(mListViewBackground == null || mListViewBackground.getTop() == mHeaderContainer.getHeight())
mInitialized = true;
return;
}
//get last header and listViewBackground position
int headerTopPrevious = mHeaderContainer.getTop();
int listViewBackgroundTopPrevious = mListViewBackground != null ? mListViewBackground.getTop() : 0;
//relayout
super.onLayout(changed, left, top, right, bottom);
//revert header top position
int headerTopCurrent = mHeaderContainer.getTop();
if(headerTopCurrent != headerTopPrevious) {
mHeaderContainer.offsetTopAndBottom(headerTopPrevious - headerTopCurrent);
}
//revert listViewBackground top position
int listViewBackgroundTopCurrent = mListViewBackground != null ? mListViewBackground.getTop() : 0;
if(listViewBackgroundTopCurrent != listViewBackgroundTopPrevious) {
mListViewBackground.offsetTopAndBottom(listViewBackgroundTopPrevious - listViewBackgroundTopCurrent);
}
}
}
And the class CDMObservableScrollView:
public class CDMObservableScrollView extends ScrollView implements CDMObservableScrollable {
// Edge-effects don't mix well with the translucent action bar in Android 2.X
private boolean mDisableEdgeEffects = true;
private CDMOnScrollChangedCallback mOnScrollChangedListener;
public CDMObservableScrollView(Context context) {
super(context);
}
public CDMObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CDMObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mOnScrollChangedListener != null) {
mOnScrollChangedListener.onScroll(l, t);
}
}
#Override
protected float getTopFadingEdgeStrength() {
// http://stackoverflow.com/a/6894270/244576
if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return 0.0f;
}
return super.getTopFadingEdgeStrength();
}
#Override
protected float getBottomFadingEdgeStrength() {
// http://stackoverflow.com/a/6894270/244576
if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return 0.0f;
}
return super.getBottomFadingEdgeStrength();
}
#Override
public void setOnScrollChangedCallback(CDMOnScrollChangedCallback callback) {
mOnScrollChangedListener = callback;
}
}
EDIT 2
I can delimit the problem now:
If i these lines the FAB works as i want:
<denis.de.meperdia.fadingactionbar.CDMObservableScrollView
android:id="#+id/fab__scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
But then the synchonization of my fading action bar is destroyed...
Sorry for that much code but it's really complicated to understand without this.
Import the app namespace, surround the NestedScrollView with a RelativeLayout and then set the RelativeLayout as anchor for FAB.
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" //Import app namespace
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:orientation="vertical"
android:paddingBottom="10dp">
<android.support.v4.widget.NestedScrollView
android:id="#+id/mainview"
android:layout_width="match_parent"
android:layout_height="match_parent">
// Here are the textviews, relativelayouts etc...
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="#+id/edit_movies_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_edit_white_24dp"
app:layout_anchor="#id/mainview" //attributes of app namespace
app:layout_anchorGravity="bottom|end"
/>
</android.support.design.widget.CoordinatorLayout>
Replace android:layout_gravity="bottom|end"
with this android:layout_gravity="bottom|right"
Add below line in NestedScrollView.
app:layout_behavior="#string/appbar_scrolling_view_behavior"
You can easily do it with Coordinate Layout anchor property to attach your fab to any of your view node
CoordinatorLayout and use the layout_anchor and layout_anchorGravity attributes.
<ListView
android:id="#+id/lvToDoList"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="#drawable/ic_done"
app:layout_anchor="#id/lvToDoList"
app:layout_anchorGravity="bottom|right|end" />
This will make FAB to aatch to list view at bottom. You can do same with any other layout/View.
Finally i solved this problem by moving the FloatingActionButton from the content layout to the outside.
My container layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<denis.de.meperdia.fadingactionbar.CDMRootLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/fab__header_container"/>
<denis.de.meperdia.fadingactionbar.CDMObservableScrollView
android:id="#+id/fab__scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/fab__container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/fab__content_top_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"/>
</LinearLayout>
</denis.de.meperdia.fadingactionbar.CDMObservableScrollView>
<android.support.design.widget.CoordinatorLayout
android:id="#+id/fab__floating_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.design.widget.CoordinatorLayout>
</denis.de.meperdia.fadingactionbar.CDMRootLayout>
I added a container for the FloatingActionButton which i fill dynamically by loading it from another file. The moving problem of the FloatingActionButton is solved now. There's a little other problem but i opened a new question for this.
EDIT
Changed my solution. I had the problem that if i want to show a snackbar, the FloatingActionButton didn't scroll correctly. I add the FloatingActionButton now programmatically to the root view. Now it works correctly.
try this example:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.internet.Main2Activity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include
layout="#layout/content_main2"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:srcCompat="#android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
content_main2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.internet.Main2Activity"
tools:showIn="#layout/activity_main2">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="asdds" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
Related
I'm trying to add a parallel effect in between bottom sheet layout and map view.but google log in map view is hidden when expending the bottom sheet layout. [Google logo is hidden]
show google logo when bottom sheet collapse
I'm using below code:
layout
<android.support.design.widget.CoordinatorLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/Green"
tools:context=".ui.activities.ChooseLocationActivity">
<RelativeLayout
android:id="#+id/map_rlyt"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.koya.android.ui.widget.CollapseBehavior"
app:layout_anchor="#+id/bottom_sheet_layout">
<fragment
android:id="#+id/g_map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
<RelativeLayout
android:id="#+id/category_activity_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/category_search_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/custom_edit_text_margin_top"
android:layout_marginLeft="#dimen/custom_edit_text_margin_left_right"
android:layout_marginRight="#dimen/custom_edit_text_margin_left_right"
android:background="#android:color/transparent">
<android.support.v7.widget.AppCompatImageView
android:id="#+id/ic_back_iv"
android:padding="#dimen/location_activity_back_button_padding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="#mipmap/ic_back_black"/>
<EditText
android:id="#+id/editTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/custom_edit_text_padding"
android:textColor="#color/black"
android:drawablePadding="8dp"
android:imeOptions="actionDone"
android:inputType="text"
android:maxLines="1"
android:drawableLeft="#mipmap/ic_search_gray"
android:textCursorDrawable="#drawable/edit_text_cursor_drawable"
android:drawableRight="#mipmap/ic_close_gray"
android:textSize="#dimen/custom_edit_text_text_size"
android:background="#drawable/drawable_custom_edittext"
/>
</LinearLayout>
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"/>
<Button
android:id="#+id/redo_search_in_map_area_button"
style="#style/CategoryChooseButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/category_search_box"
android:layout_centerInParent="true"
android:layout_marginTop="#dimen/fifteen_text_size"
android:animateLayoutChanges="true"
android:paddingEnd="#dimen/redo_search_button_padding"
android:paddingStart="#dimen/redo_search_button_padding"
android:text="#string/redo_search_in_map_area"
android:textAllCaps="false"
android:visibility="gone"
/>
</RelativeLayout>
<include
android:id="#+id/bottom_sheet_layout"
layout="#layout/bottom_sheet"/>
<Button
android:id="#+id/select_location_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="#string/use_selected_location"
android:visibility="gone"
tools:visibility="visible"
style="#style/CategoryChooseButtonStyle"/>
<include android:id="#+id/error_layout"
layout="#layout/error_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
/>
And Implement Coordinatorlayout.Behavior
public class CollapseBehavior<V extends ViewGroup> extends CoordinatorLayout.Behavior<V>{
public CollapseBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
if (isBottomSheet(dependency)) {
BottomSheetBehavior behavior = ((BottomSheetBehavior) ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior());
int peekHeight = behavior.getPeekHeight();
// The default peek height is -1, which
// gets resolved to a 16:9 ratio with the parent
final int actualPeek = peekHeight >= 0 ? peekHeight : (int) (((parent.getHeight() * 1.0) / (16.0)) * 9.0);
if (dependency.getTop() >= actualPeek) {
// Only perform translations when the
// view is between "hidden" and "collapsed" states
final int dy = dependency.getTop() - parent.getHeight();
ViewCompat.setTranslationY(child, dy/2);
return true;
}
}
return false;
}
private static boolean isBottomSheet(#NonNull
View view) {
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp instanceof CoordinatorLayout.LayoutParams) {
return ((CoordinatorLayout.LayoutParams) lp)
.getBehavior() instanceof BottomSheetBehavior;
}
return false;
}
}
I need this type of scrolling in my application -->
https://streamable.com/g0tf9
How to scroll google map when bottom sheet expend with parallax effect? I would appreciate it a lot!
Thanks in Advance.
I have a RecyclerView with other widgets in this type of layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="in.joey.joseph.aapnidukan.activities.CheckOutActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorPrimary"
android:minHeight="?attr/actionBarSize">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/backIV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#drawable/back_image"
android:padding="16dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="CheckOut"
android:textSize="18sp"
android:fontFamily="casual"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:textColor="#android:color/white"/>
</RelativeLayout>
</android.support.v7.widget.Toolbar>
<RelativeLayout
android:id="#+id/nonEmptyState"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/toolbar"
android:visibility="visible">
<in.joey.joseph.aapnidukan.utils.EmptyRecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/checkOutRV"
/>
<RelativeLayout
android:id="#+id/cartTotalLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/checkOutRV"
android:layout_alignParentRight="true"
android:gravity="right"
android:layout_marginRight="20dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="right"
android:layout_marginRight="20dp"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Total: "
android:textSize="18sp"
android:textColor="#android:color/black"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="₹"
android:textSize="18sp"
android:textColor="#android:color/black"
android:layout_marginLeft="10dp"/>
<TextView
android:id="#+id/totalPriceTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="8999"
android:textSize="18sp"
android:textColor="#color/colorPrimary"
android:layout_marginLeft="5dp"/>
</LinearLayout>
</RelativeLayout>
<Button
android:id="#+id/billingBtn"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#color/colorAccent"
android:text="Proceed to Billing and Address"
android:textColor="#android:color/white"
android:textAllCaps="false"
android:textSize="20sp"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
<TextView
android:id="#+id/emptyState"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Items to Cart Before CheckOut"
android:textSize="18sp"
android:fontFamily="casual"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:textColor="#android:color/black"
android:visibility="visible"/>
I have created a custom RecyclerView to handle empty states but it works fine for only the RecyclerView. The other views like the button are still visible when the list is empty. I have tried to hide it in my custom RecyclerView class like this:
private void checkNotEmpty(){
if (nonEmptyView != null && getAdapter() != null){
boolean nonEmptyViewVisible = getAdapter().getItemCount() > 0;
nonEmptyView.setVisibility(nonEmptyViewVisible ? VISIBLE : GONE);
setVisibility(nonEmptyViewVisible ? GONE : VISIBLE);
}
}
Here's the full class code for the RecyclerView:
public class EmptyRecyclerView extends RecyclerView {
private View emptyView, nonEmptyView;
public EmptyRecyclerView(Context context) {
super(context);
}
public EmptyRecyclerView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public EmptyRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private final AdapterDataObserver dataObserver = new AdapterDataObserver() {
#Override
public void onChanged() {
checkEmpty();
}
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
checkEmpty();
}
#Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
checkEmpty();
}
};
private void checkEmpty() {
if (emptyView != null && getAdapter() != null){
boolean emptyViewVisible = getAdapter().getItemCount() == 0;
emptyView.setVisibility(emptyViewVisible ? VISIBLE : GONE);
setVisibility(emptyViewVisible ? GONE : VISIBLE);
}
}
private void checkNotEmpty(){
if (nonEmptyView != null && getAdapter() != null){
boolean nonEmptyViewVisible = getAdapter().getItemCount() > 0;
nonEmptyView.setVisibility(nonEmptyViewVisible ? VISIBLE : GONE);
setVisibility(nonEmptyViewVisible ? GONE : VISIBLE);
}
}
public void setButtonVisibility(Button button){ // to toggle the button visibility
if (getAdapter().getItemCount() > 0){
button.setVisibility(View.VISIBLE);
} else {
button.setVisibility(GONE);
}
}
#Override
public void setAdapter(Adapter adapter) {
Adapter oldAdapter = getAdapter();
if (oldAdapter != null){
oldAdapter.unregisterAdapterDataObserver(dataObserver);
}
super.setAdapter(adapter);
if (adapter != null){
adapter.registerAdapterDataObserver(dataObserver);
}
checkEmpty();
}
public void setEmptyView(View view){
this.emptyView = view;
checkEmpty();
}
public void setNonEmptyView(View view2){
this.nonEmptyView = view2;
checkNotEmpty();
}
}
And in my activity, I have tried this:
init();
checkOutRV.setEmptyView(emptyState);
checkOutRV.setNonEmptyView(nonEmptyState);
private void init() {
nonEmptyState = findViewById(R.id.nonEmptyState);
emptyState = findViewById(R.id.emptyState);
checkOutRV = findViewById(R.id.checkOutRV);
checkOutRV.setHasFixedSize(true);
checkOutRV.setLayoutManager(new LinearLayoutManager(this));
}
The list has no data right now as I have not made my API call yet, but still, the emptyStateTextView and the buttons are still visible. How do I fix this?
By default keep your layout INVISIBLE and show a layout that shows that the Recyclerview has no data or something, and then when you make your API calls and receive the data then hide the layout which show that the recyclerview in empty and set your data layout view to VISIBLE which you want to show.
UPDATED ANSWER
You need to register Data observer to listen to data changes from sync adapter.
mRecyclerViewAdapter.registerAdapterDataObserver(myObserver);
RecyclerView.AdapterDataObserver are a result of which notify methods you call. So for instance if you call notifyItemInserted() after you add an item to your adapter then onItemRangeInserted() will get called
A more detailed example
protected void setupRecyclerView() {
mAdapter = new MyAdapter(mItemList);
mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onChanged() {
super.onChanged();
checkAdapterIsEmpty();
}
});
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(mAdapter);
checkAdapterIsEmpty();
}
Also don't forget to unregister the observer.
In the checkNonEmpty() method you have called getAdapter() which returns null as you have not used setAdapter() on recycler view .
Create an adapter for your recycler view and initialise it with an empty list , then setAdapter() on your view.
Then you can use these statements
checkOutRV.setEmptyView(emptyState);
checkOutRV.setNonEmptyView(nonEmptyState);
Hope it helps !!
Try this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout >
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
>
...
</android.support.v7.widget.Toolbar>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/toolbar"
android:visibility="visible">
<RelativeLayout
android:id="#+id/nonEmptyState"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Existing layout like recyclerview, button -->
</RelativeLayout>
<RelativeLayout
android:id="#+id/emptyStateLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/emptyState"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:fontFamily="casual"
android:gravity="center"
android:text="Add Items to Cart Before CheckOut"
android:textColor="#android:color/black"
android:textSize="18sp"
android:visibility="visible"/>
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
Just set emptyStateLayout VISIBLE when RecyclerView is empty, otherwise GONE
I'm using android:windowSoftInputMode="stateHidden|adjustResize" to push the EditText and RecyclerView up when keyboard appears, but it only works on the EditText. RecyclerView stays in the same position (thus some elements are not visible). What am I doing wrong?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="co.bla.bla.ChatActivity"
android:id="#+id/chat_root">
<include android:id="#+id/toolbar" layout="#layout/toolbar"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/chat_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/message_input_wrapper"
android:scrollbars="vertical"
android:paddingBottom="20dp"
android:layout_below="#id/toolbar"
/>
<LinearLayout
android:id="#+id/message_input_wrapper"
android:layout_width="match_parent"
android:layout_height="60dp"
android:padding="6dp"
android:orientation="horizontal"
android:weightSum="4"
android:background="#android:color/white"
android:layout_alignParentBottom="true">
<EditText
android:id="#+id/message_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="#string/chat_input_hint"
android:layout_gravity="center_vertical"
android:layout_weight="3"
android:background="#android:color/transparent"/>
<Button
android:id="#+id/message_send_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#string/chat_button_text"
android:layout_weight="1"
android:onClick="onButtonClicked"/>
</LinearLayout>
</RelativeLayout>
I don't know how adjustResize exactly works, but I assume when using android:layout_above in this case, the RecyclerView and LinearLayout "stick" together and should be pushed up together?
In your activity manifest:
android:windowSoftInputMode="adjustResize"
Then add an OnLayoutChangeListener to yout recyclerView:
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right,int bottom, int oldLeft, int oldTop,int oldRight, int oldBottom)
{
recyclerView.scrollToPosition(recyclerView.getAdapter().getItemCount()-1);
}
});
This works for me :)
set a soft keyboard listener:
private boolean mLastKeyBoardVisible = false;
private void addOnSoftKeyBoardVisibleListener(final OnSoftKeyBoardVisibleListener listener) {
final View decorView = getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
int displayHight = rect.bottom - rect.top;
int hight = decorView.getHeight();
boolean visible = (double) displayHight / hight < 0.8;
if(visible != mLastKeyBoardVisible){
listener.onSoftKeyBoardVisible(visible);
}
mLastKeyBoardVisible = visible;
}
});
}
private OnSoftKeyBoardVisibleListener onSoftKeyBoardVisibleListener = new OnSoftKeyBoardVisibleListener() {
#Override
public void onSoftKeyBoardVisible(boolean visible) {
scrollToBottom(visible);
}
};
synchronized private void scrollToBottom(boolean visible) {
if (visible) {
if (mMessageList.size() != messageAdapter.getItemCount() && mMessageList.size() > 0) {
messageAdapter.notifyDataSetChanged();
messageListView.scrollToPosition(mMessageList.size() - 1);
} else if (mMessageList.size() > 0) {
messageListView.scrollToPosition(mMessageList.size() - 1);
}
}
}
I am trying to incorporate 2 lists "sections" into a LinearLayout via height weights. The issue that I am having is that, though the container for the "section" is displayed with the proper height (where the background is sub_gray), and the adapter count is correct, the height of my actual list rows is 0 which makes my list appear invisible.
LIST ITEM
Below is the code that my list rows/items directly work with. To avoid the wall of code getting too long, I will include my code for one of the lists seeing as they are both behaving the same.
layout_quick_guide_checklist_item
<?xml version="1.0" encoding="utf-8"?>
<com.my_project.project.QuickGuideChecklistListItemView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
android:background="#color/sub_gray"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/checkbox_checklist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_gravity="center"
android:checked="true"
android:clickable="false" />
<TextView
android:id="#+id/text_field"
style="#style/default_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="#string/height_wrap_string"/>
</com.my_project.project.QuickGuideChecklistListItemView>
QuickGuideChecklistListItemView
public class QuickGuideChecklistListItemView extends LinearLayout{
private TextView listText;
public QuickGuideChecklistListItemView(Context context) {
super(context);
}
public QuickGuideChecklistListItemView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public QuickGuideChecklistListItemView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public static QuickGuideChecklistListItemView inflate(ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) parent.getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
QuickGuideChecklistListItemView itemView = (QuickGuideChecklistListItemView)
inflater.inflate(R.layout.layout_quick_guide_checklist_item, parent, false);
return itemView;
}
#Override
public void onFinishInflate(){
super.onFinishInflate();
listText = (TextView) findViewById(R.id.text_field);
}
public void setText(String text){
listText.setText(text);
}
}
COMPLETE LIST
Now I take the above code and use it within my activity to create a list (recall; there are actually two lists in my activity, constructed similarly, and both resulting in row heights of 0).
QuickGuideDetailActivity
public class QuickGuideDetailActivity extends NavigationDrawerActivity implements OnClickListener{
private final String TAG = getClass().getSimpleName();
private QuickGuideModel selectedQuickGuide;
private QuickGuideChecklistAdapter checklistAdapter;
private QuickGuideTipAdapter tipsAdapter;
private TextView quickGuidesTitle, quickGuidesDescription, inflaterButton;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
selectedQuickGuide = QuickGuidesActivity.selectedQuickGuide;
buildLists();
}
#Override
protected int getLayoutId(){
return R.layout.sub_activity_quick_guide;
}
public void checkAdapterSize(View v){
String checklistString = (checklistAdapter == null)? "null": "" + checklistAdapter.getCount();
String tipString = (tipsAdapter == null)? "null": "" + tipsAdapter.getCount();
Toast.makeText(this, checklistString + " : " + tipString,
Toast.LENGTH_SHORT).show();
}
private void buildLists(){
TextView checklistTitle = (TextView) findViewById(R.id.checklist_title_bar);
checklistTitle.setText(getResources().getString(R.string.title_checklist));
ArrayList<String> checklistStrings = new ArrayList<String>();
for(Object guide : selectedQuickGuide.checklist){
checklistStrings.add((String) guide.toString());
}
checklistAdapter = new QuickGuideChecklistAdapter(checklistStrings);
ListView checklist = (ListView) findViewById(R.id.checklist_list);
checklist.setAdapter(checklistAdapter);
Log.d(TAG, "List Size - Checklist = " + selectedQuickGuide.checklist.length);
TextView tipsTitle = (TextView) findViewById(R.id.tips_title_bar);
tipsTitle.setText(getResources().getString(R.string.title_tips));
tipsAdapter = new QuickGuideTipAdapter(selectedQuickGuide.topTips);
ListView tips = (ListView) findViewById(R.id.tips_list);
tips.setAdapter(tipsAdapter);
Log.d(TAG, "List Size - Tips = " + selectedQuickGuide.topTips.length);
}
}
sub_activity_quick_guide
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
style="#style/default_container"
android:layout_margin="15dp"
android:orientation="vertical" >
<include
android:id="#+id/expandable_section"
layout="#layout/section_expandable_details" />
<LinearLayout
style="#style/default_container"
android:orientation="vertical"
android:weightSum="2">
<LinearLayout
android:id="#+id/checklist_section"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="8dp"
android:background="#color/sub_gray"
android:onClick="checkAdapterSize" >
<View
android:layout_width="1dp"
android:layout_height="15dp" />
<TextView
android:id="#+id/checklist_title_bar"
style="#style/default_label_text_view"
android:background="#color/random_blue"
android:text="#string/loading" />
<ListView
android:id="#+id/checklist_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#color/off_white"
android:dividerHeight="1dp" />
</LinearLayout>
<LinearLayout
android:id="#+id/tips_list_section"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="7dp"
android:background="#color/sub_gray"
android:onClick="checkAdapterSize" >
<View
android:layout_width="1dp"
android:layout_height="15dp" />
<TextView
android:id="#+id/tips_title_bar"
style="#style/default_label_text_view"
android:background="#color/random_blue"
android:text="#string/loading" />
<ListView
android:id="#+id/tips_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#color/off_white"
android:dividerHeight="1dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="#+id/nav_drawer_list_view"
android:layout_width="#dimen/drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#android:color/white" />
</android.support.v4.widget.DrawerLayout>
QuickGuideChecklistAdapter
public class QuickGuideChecklistAdapter extends BaseAdapter{
private ArrayList<String> guides;
private List<Boolean> checklistItemTicked = new ArrayList<Boolean>();
public QuickGuideChecklistAdapter(ArrayList<String> guides){
this.guides = guides;
for(int i = 0; i < guides.size(); i++)
checklistItemTicked.add(false);
}
#Override
public int getCount() {
return guides.size();
}
#Override
public String getItem(int position) {
if(position < 0 || position >= guides.size())
throw new IndexOutOfBoundsException("Position is out of range of list with size " + guides.size());
return guides.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
QuickGuideChecklistListItemView itemView = (QuickGuideChecklistListItemView)convertView;
if (null == itemView)
itemView = QuickGuideChecklistListItemView.inflate(parent);
itemView.setText(guides.get(position));
Log.d(getClass().getSimpleName(), "Setting text to " + guides.get(position) + " in position " + position);
return itemView;
}
}
Sorry for the wall of code, but I have tried adjusting everything and just can't seem to get anything to work. So just as a final bit:
The adapters are set, getCount() returns the valid count of items in the list and clicking on the layouts in order to call checkAdapterSize() also results in a Toast with the proper values.
The first view is "inflated" but results in a view height of 0.
The container layouts for my list "sections" (checklist_section in sub_activity_quick_guide for example) have a height which can be seen by the sub_gray background.
I have tried changing height attributes from match_parent to wrap_content and vice versa with no luck. I have tried explicitly declaring a minHeight for my list items and all that good stuff. I just can't seem to ever get a list row item to actually inflate.
Funny how I always manage to figure out an answer to my own questions right after I post a question on SO after days of trying :P
Anyway, the issue was in placing my ListViews inside of LinearLayouts with heights determined by weight. I removed the LinearLayout wrappers for my "sections" and applied the weights to my ListViews directly, voila... good to go.
New complete working Activity layout
sub_activity_quick_guide
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
style="#style/default_container"
android:layout_margin="15dp"
android:orientation="vertical"
android:weightSum="2" >
<include
android:id="#+id/expandable_section"
layout="#layout/section_expandable_details" />
<View
android:layout_width="1dp"
android:layout_height="15dp" />
<TextView
android:id="#+id/checklist_title_bar"
style="#style/default_label_text_view"
android:background="#color/random_blue"
android:text="#string/loading" />
<ListView
android:id="#+id/checklist_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="#color/off_white"
android:dividerHeight="1dp" />
<View
android:layout_width="1dp"
android:layout_height="15dp" />
<TextView
android:id="#+id/tips_title_bar"
style="#style/default_label_text_view"
android:background="#color/random_blue"
android:text="#string/loading" />
<ListView
android:id="#+id/tips_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="#color/off_white"
android:dividerHeight="1dp" />
</LinearLayout>
<RelativeLayout
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="#+id/nav_drawer_list_view"
android:layout_width="#dimen/drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#android:color/white" />
</android.support.v4.widget.DrawerLayout>
Hope it helps someone in the future, didn't realize weights would mess with it like that but apparently ListViews don't like weighted containers.
i have 3 layers, all of them are RelativeLayouts or based on it:
content-class
overlay-class
infobox-layout
In my code i generate an Overlay-object which inflates a infobox-layout. And then i add the Overlayer to the Content-Layer.
My goal is to center (vertical and horizontal) the Info-box in parent (=overlay) but it doesn't work. It's TOP/LEFT. This is the default behavior, I think.
So here is my code.
I add the Overlayer to the Content-Layer like this:
private void openContent (String url,final ContentLayer content) {
overlay = new Overlay(context, url);
content.addView(overlay);
}
Here is the Overlayer:
public class Overlay extends RelativeLayout {
// some stuff
public Overlay(Context context, String url) {
super(context);
this.context = context;
this.url = url;
init(context, null, -1);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
this.setTag(context.getResources().getString(R.string.overlay));
RelativeLayout.LayoutParams lp = new RelativeLayoutout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
this.setLayoutParams(lp);
this.setBackgroundColor(context.getResources().getColor(R.color.black));
this.getBackground().setAlpha(80);
isMax = getValueInPrefs();
inflateLayout();
}
private void inflateLayout () {
String service = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(service);
inflater.inflate(R.layout.infobox_wrapper, this, true);
}
// some stuff
}
Last but no least, here is my infobox-layout, called infobox_wrapper.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/epaper_webview_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"> <!-- DOESN'T WORK!! -->
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#color/grau_actionbar">
<Button
android:id="#+id/header_button_close"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="X"
android:layout_alignParentRight="true"
android:layout_margin="5dp"/>
<Button
android:id="#+id/header_button_min_max"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="M"
android:layout_toLeftOf="#id/header_button_close"
android:layout_margin="5dp"/>
</RelativeLayout>
<RelativeLayout
android:id="#+id/webview_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/grau_actionbar"
android:layout_below="#id/header">
<WebView
android:id="#+id/epaper_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
</RelativeLayout>
I am thankful for any help or hint.
I solved the problem like this:
I deleted android:layout_centerInParent="true" in RelativeLayout(#+id/epaper_webview_wrapper) and set it dynamically in openContent-method:
private void openContent (String url,final ContentLayer content) {
overlay = new Overlay(context, url);
RelativeLayout epaper_webview_wrapper= (RelativeLayout)overlay.findViewById(R.id.epaper_webview_wrapper);
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)epaper_webview_wrapper.getLayoutParams();
lp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
epaper_webview_wrapper.setLayoutParams(lp);
content.addView(overlay);
}