Move Button down when dissmising snackbar manually - android

So I have following basic code which makes sure that my button goes up when a snackbar appears:
public class MoveUpwardBehavior extends CoordinatorLayout.Behavior<View> {
private static final boolean SNACKBAR_BEHAVIOR_ENABLED;
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
static {
SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
}
}
My customlinearlayout:
public class CustomLinearLayout extends LinearLayout implements CoordinatorLayout.AttachedBehavior {
MoveUpwardBehavior mb = new MoveUpwardBehavior();
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#NonNull
#Override
public CoordinatorLayout.Behavior getBehavior() {
return mb;
}
}
And last but not least my layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layout_behavior=".pkgActivity.MoveUpwardBehaviour"
tools:context=".pkgTestforend.DriverListFragment">
<ListView
android:id="#+id/listAllDrivers"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
<com.example.dochjavatestimplementation.pkgTestforend.CustomLinearLayout
android:layout_width="match_parent"
android:id="#+id/cusLL"
android:layout_height="match_parent"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/btnOpenDriverAddFragment2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/baseline_person_add_24"
/>
</RelativeLayout>
</com.example.dochjavatestimplementation.pkgTestforend.CustomLinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
This works perfectly fine, however the issue is that button remains up, when the snackbar gets dissmissed manually:
In order to solve the issue I tried the following:
Snackbar kd = Snackbar.make(customLinearLayout, "Text to display", Snackbar.LENGTH_LONG)
.addCallback(new Snackbar.Callback() {
#Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_SWIPE) {
btnOpenDriverAddFragment2.setTranslationY(90); //lower button down manually!
}
}
#Override
public void onShown(Snackbar snackbar) {
}
});
kd.show();
However, this doesnt work very well, as the snackbar seems still be visible/button gets covered by the dissmissed snackbar?
Why is it so, that the snackbar remains basicallyvisible but dissmissed?

Changing btnOpenDriverAddFragment2.setTranslationY(90); with customLinearLayout.setTranslationY(0); will solve your issue.
Since not only the button gets moved up but also the custom linear layout which is also the parent of the button u just need to reset the y position of the parent.
U just change the y value of the button, but u forgot about the linearlayout.

Related

No scroll in Custom FooterBarLayout to Follow AppBarLayout

I have following CoordinatorLayout behavior :
public class FooterBarBehavior extends CoordinatorLayout.Behavior<FooterBarLayout> {
//Required to instantiate as a default behavior
public FooterBarBehavior() {
}
//Required to attach behavior via XML
public FooterBarBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
//This is called to determine which views this behavior depends on
#Override
public boolean layoutDependsOn(CoordinatorLayout parent,
FooterBarLayout child,
View dependency) {
//We are watching changes in the AppBarLayout
return getDependentView((ViewPager) dependency) instanceof AppBarLayout;
}
//This is called for each change to a dependent view
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent,
FooterBarLayout child,
View dependency) {
int offset = -getDependentView((ViewPager) dependency).getTop();
child.setTranslationY(offset);
return true;
}
private View getDependentView(ViewPager viewPager) {
int index = viewPager.getCurrentItem();
MainPagerAdapter adapter = ((MainPagerAdapter)viewPager.getAdapter());
ContactsFragment fragment = (ContactsFragment) adapter.getItem(index);
if(fragment.getView() == null) {
return null;
}
return fragment.getView().findViewById(R.id.appBarLayout);
}
}
Here is FooterBarLayout :
#CoordinatorLayout.DefaultBehavior(FooterBarBehavior.class)
public class FooterBarLayout extends RelativeLayout {
public FooterBarLayout(Context context) {
super(context);
}
public FooterBarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FooterBarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
It has been used in the layout as follow:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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="wrap_content"
android:layout_height="wrap_content"
tools:context=".ui.MainActivity">
<androidx.viewpager.widget.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.sample.android.contact.widget.FooterBarLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.sample.android.contact.widget.ListenableTabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:background="#drawable/rectangle_shape"
app:tabIndicatorHeight="2dp"
app:tabSelectedTextColor="#color/color1" />
<ImageView
android:id="#+id/triangle"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_above="#+id/tab_layout"
android:layout_marginBottom="#dimen/dimen_triangle_bottom_margin"
android:src="#drawable/triangle_shape"
tools:ignore="ContentDescription" />
</com.sample.android.contact.widget.FooterBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I expect that FooterBarLayout follow AppBarLayout in the fragment and hide/show based on scroll direction. The idea is taken from : https://github.com/devunwired/coordinated-effort/blob/master/app/src/main/java/com/example/android/coordinatedeffort/behaviors/FooterBarBehavior.java
But there will be no scrolling in FooterBarLayout. Do you guys have any idea to resolve this?

ViewHolder itemView is smaller than expected

ViewHolder is inflated based on this layout:
Adding listener to the whole ViewHolder, or be exact to itemView:
userSettingHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
But only the first item the imageView can active the listener. Why not the whole item, with not last two 'sub'-recycleview?
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="66dp">
<ImageView
android:id="#+id/icon"
android:layout_width="66dp"
android:layout_height="66dp"
android:padding="0dp" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rw1"
android:layout_marginStart="66dp"
android:layout_marginTop="0dp"
android:layout_marginBottom="33dp"
android:layout_height="33dp"
android:layout_width="match_parent"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/rw2"
android:layout_marginStart="66dp"
android:layout_marginTop="33dp"
android:layout_marginBottom="0dp"
android:layout_height="33dp"
android:layout_width="match_parent"/>
</RelativeLayout>
Here when you click on ImageView the touch is passed to the parent which will be itemView., But when you click on RecyclerView it consumes the touch.
I suggest you add this property to both RecyclerView in xml
android:clickable="false"
If this didn't work then you have to subclass the RecyclerView and override the onInterceptTouchEvent method.
public class ScrollThroughRecyclerView extends RecyclerView {
public ScrollThroughRecyclerView(Context context) {
super(context);
}
public ScrollThroughRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollThroughRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
Then in your xml instead of
<android.support.v7.widget.RecyclerView use <your.file.path.ScrollThroughRecyclerView
But both solutions will make the RecyclerView not clickable, and ofcourse not scrollable.

Show/Hide LinearLayout when scrolling a RecyclerView?

I have a fragment loaded into my MainActivity. On top of the fragment's layout I have a LinearLayout which I would like to show/hide as the user scrolls up/down.
Can this be achieved using the Coordinator layout or I need to do my own hack?
layout.xml:
<android.support.design.widget.CoordinatorLayout 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"
android:background="#ececec">
<android.support.v7.widget.RecyclerView
android:id="#+id/discoverRView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="5dp"
android:paddingRight="5dp">
</android.support.v7.widget.RecyclerView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#f00"
app:layout_anchor="#+id/discoverRView"
app:layout_anchorGravity="top"
app:layout_scrollFlags="scroll"
>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Maybe there are better solutions, but this can be achieved by creating a custom CoordinatorLayout.Behavior and adding it to a CustomLinearLayout:
//This is taken from a project of mine, it scrolls a Layout up if a snackbar shows up.
public class MoveUpwardBehavior extends CoordinatorLayout.Behavior<View> {
public MoveUpwardBehavior(){
//super();
}
public MoveUpwardBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public void onDependentViewRemoved(CoordinatorLayout parent, View child, View dependency) {
super.onDependentViewRemoved(parent, child, dependency);
child.setTranslationY(0);
}
}
You'll need a custom LinearLayout, but this part is easy peasy:
#CoordinatorLayout.DefaultBehavior(MoveUpwardBehavior.class)
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
Surely, you'll need to use this layout in your xml:
<com.your.project.CustomLinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#f00"
app:layout_anchor="#+id/discoverRView"
app:layout_anchorGravity="top">
</com.your.project.CustomLinearLayout>
So, I think you get the idea. You'll need to update the behaviour to depend on the scroll of your RecyclerView. If you need more help, I can try to build a working example.

CoordinatorLayout ignores margins for views with anchor

Given I'm using a layout like this:
<android.support.design.widget.CoordinatorLayout
android:id="#+id/main_content"
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"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="#dimen/flexible_space_image_height"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:statusBarScrim="#android:color/transparent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/mainView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
/>
<android.support.design.widget.FloatingActionButton
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginBottom="20dp"
app:fabSize="normal"
app:layout_anchor="#id/appbar"
app:layout_anchorGravity="bottom|center_horizontal"
/>
</android.support.design.widget.CoordinatorLayout>
Which is pretty much the standard Cheesesquare sample - except the FloatingActionButton, which I would like to move up by about 20dp.
However, this will not work. No matter if I use margin, padding etc - the button will always be centered at the edge of the anchor, like this:
How can I move the FAB up by 20dp as intended?
I suggest an elegant solution for you:
<android.support.design.widget.FloatingActionButton
...
android:translationY="-20dp"
...
/>
Try putting it in a linear layout that have padding:
<LinearLayout
width=".."
height=".."
paddingBottom="20dp"
app:layout_anchor="#id/appbar"
app:layout_anchorGravity="bottom|center_horizontal">
<android.support.design.widget.FloatingActionButton
android:layout_width="70dp"
android:layout_height="70dp"
app:fabSize="normal" />
</LinearLayout>
As there might be bugs in the design-support-lib concerning the CoordinatorLayout & margins, I wrote a FrameLayout that implements/copies the same "Behavior" like the FAB and allows to set a padding to simulate the effect:
Be sure to put it in the android.support.design.widget package as it needs to access some package-scoped classes.
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.widget.FrameLayout;
import com.company.android.R;
import java.util.List;
#CoordinatorLayout.DefaultBehavior(FrameLayoutWithBehavior.Behavior.class)
public class FrameLayoutWithBehavior extends FrameLayout {
public FrameLayoutWithBehavior(final Context context) {
super(context);
}
public FrameLayoutWithBehavior(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public FrameLayoutWithBehavior(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public FrameLayoutWithBehavior(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public static class Behavior extends android.support.design.widget.CoordinatorLayout.Behavior<FrameLayoutWithBehavior> {
private static final boolean SNACKBAR_BEHAVIOR_ENABLED;
private Rect mTmpRect;
private boolean mIsAnimatingOut;
private float mTranslationY;
public Behavior() {
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FrameLayoutWithBehavior child, View dependency) {
return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FrameLayoutWithBehavior child, View dependency) {
if (dependency instanceof Snackbar.SnackbarLayout) {
this.updateFabTranslationForSnackbar(parent, child, dependency);
} else if (dependency instanceof AppBarLayout) {
AppBarLayout appBarLayout = (AppBarLayout) dependency;
if (this.mTmpRect == null) {
this.mTmpRect = new Rect();
}
Rect rect = this.mTmpRect;
ViewGroupUtils.getDescendantRect(parent, dependency, rect);
if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
if (!this.mIsAnimatingOut && child.getVisibility() == VISIBLE) {
this.animateOut(child);
}
} else if (child.getVisibility() != VISIBLE) {
this.animateIn(child);
}
}
return false;
}
private void updateFabTranslationForSnackbar(CoordinatorLayout parent, FrameLayoutWithBehavior fab, View snackbar) {
float translationY = this.getFabTranslationYForSnackbar(parent, fab);
if (translationY != this.mTranslationY) {
ViewCompat.animate(fab)
.cancel();
if (Math.abs(translationY - this.mTranslationY) == (float) snackbar.getHeight()) {
ViewCompat.animate(fab)
.translationY(translationY)
.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
.setListener((ViewPropertyAnimatorListener) null);
} else {
ViewCompat.setTranslationY(fab, translationY);
}
this.mTranslationY = translationY;
}
}
private float getFabTranslationYForSnackbar(CoordinatorLayout parent, FrameLayoutWithBehavior fab) {
float minOffset = 0.0F;
List dependencies = parent.getDependencies(fab);
int i = 0;
for (int z = dependencies.size(); i < z; ++i) {
View view = (View) dependencies.get(i);
if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) {
minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - (float) view.getHeight());
}
}
return minOffset;
}
private void animateIn(FrameLayoutWithBehavior button) {
button.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewCompat.animate(button)
.scaleX(1.0F)
.scaleY(1.0F)
.alpha(1.0F)
.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
.withLayer()
.setListener((ViewPropertyAnimatorListener) null)
.start();
} else {
Animation anim = android.view.animation.AnimationUtils.loadAnimation(button.getContext(), R.anim.fab_in);
anim.setDuration(200L);
anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
button.startAnimation(anim);
}
}
private void animateOut(final FrameLayoutWithBehavior button) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewCompat.animate(button)
.scaleX(0.0F)
.scaleY(0.0F)
.alpha(0.0F)
.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
.withLayer()
.setListener(new ViewPropertyAnimatorListener() {
public void onAnimationStart(View view) {
Behavior.this.mIsAnimatingOut = true;
}
public void onAnimationCancel(View view) {
Behavior.this.mIsAnimatingOut = false;
}
public void onAnimationEnd(View view) {
Behavior.this.mIsAnimatingOut = false;
view.setVisibility(View.GONE);
}
})
.start();
} else {
Animation anim = android.view.animation.AnimationUtils.loadAnimation(button.getContext(), R.anim.fab_out);
anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
anim.setDuration(200L);
anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
public void onAnimationStart(Animation animation) {
Behavior.this.mIsAnimatingOut = true;
}
public void onAnimationEnd(Animation animation) {
Behavior.this.mIsAnimatingOut = false;
button.setVisibility(View.GONE);
}
});
button.startAnimation(anim);
}
}
static {
SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
}
}
Easy workaround is to anchor a random layout to where FAB was anchored, give it specific margin, and then anchor FAB to random layout, like this
<LinearLayout
android:orientation="horizontal"
android:id="#+id/fab_layout"
android:layout_width="5dp"
android:layout_height="5dp"
android:layout_marginRight="80dp"
android:layout_marginEnd="80dp"
app:layout_anchor="#id/collapsing_toolbar"
app:layout_anchorGravity="bottom|end"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/fab_margin"
android:src="#android:drawable/ic_dialog_map"
app:layout_anchor="#id/fab_layout"
app:elevation="6dp"
app:pressedTranslationZ="12dp"
/>
To anchor the FloatingActionButton below the AppBar like this:
Extend the FloatingActionButton and override offsetTopAndBottom:
public class OffsetFloatingActionButton extends FloatingActionButton
{
public OffsetFloatingActionButton(Context context)
{
this(context, null);
}
public OffsetFloatingActionButton(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public OffsetFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
super.onLayout(changed, left, top, right, bottom);
ViewCompat.offsetTopAndBottom(this, 0);
}
#Override
public void offsetTopAndBottom(int offset)
{
super.offsetTopAndBottom((int) (offset + (getHeight() * 0.5f)));
}
}
I was able to get around this issue by using both layout_anchor layout_anchorGravity along with some padding.. The anchor attribute allows you to have a View position itself relative to another view. However, it doesn't exactly work in the same way that RelativeLayout does. More on that to follow.
layout_anchor specifies which View your desired View should be positioned (i.e., this View should be placed relative to the View specified by the layout_anchor attribute). Then, layout_anchorGravity specifies which side of the relative View the current View will be positioned, using the typical Gravity values (top, bottom, center_horizontal, etc.).
The issue with using just these two attributes alone is that the center of the View with the anchors will be placed relative to the other View. For example, if you specify a FloatingActionButton to be anchored to the bottom of a TextView, what really ends up happening is that the the center of the FAB is placed along the bottom edge of the TextView.
To get around this issue, I applied some padding to the FAB, enough such that the top edge of the FAB was touching the bottom edge of the TextView:
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_anchor="#id/your_buttons_id_here"
android:layout_anchorGravity="bottom"
android:paddingTop=16dp" />
You might have to increase the padding to get the desired effect. Hope that helps!
I used app:useCompatPadding="true" with FAB and everything worked.

Parent click event not firing when recyclerview clicked

I have a RecyclerView which is in a CardView that has a couple of TextViews. The CardView has a OnClickListener set and is fired off when clicking on the TextViews, but does not fire when clicking on the RecyclerView.
Here is what the CardView looks like:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
card_view:cardCornerRadius="4dp"
card_view:cardElevation="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="100"
android:minWidth="100dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="#+id/text1"
android:textColor="#color/abc_primary_text_material_light"
android:layout_weight="1"
android:layout_gravity="center_horizontal" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:listSelector="#color/highlighted_text_material_light"
android:layout_weight="98" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#android:color/black" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/relativeSummary"
android:orientation="horizontal"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="#+id/text2"
android:textAlignment="viewEnd"
android:textColor="#color/abc_secondary_text_material_light"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:gravity="start"
android:singleLine="true"
android:layout_alignParentLeft="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="#+id/text3"
android:textColor="#color/abc_primary_text_material_light"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:gravity="end"
android:singleLine="true"
android:layout_alignParentRight="true"
android:layout_toRightOf="#+id/text2" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
I do not need a click listener on this RecyclerView and really only need the parent view's click event to fire when the RecyclerView is clicked (The same goes for the OnLongClick event). I also need the RecyclerView to scroll. Is the RecyclerView some how eating the click event and not passing it up to the parent?
There is a better solution. That is, subclass your CardView:
public class InterceptTouchCardView extends CardView {
public InterceptTouchCardView(Context context) {
super(context);
}
public InterceptTouchCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InterceptTouchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* Intercept touch event so that inner views cannot receive it.
*
* If a ViewGroup contains a RecyclerView and has an OnTouchListener or something like that,
* touch events will be directly delivered to inner RecyclerView and handled by it. As a result,
* parent ViewGroup won't receive the touch event any longer.
*/
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
}
recyclerView.setLayoutFrozen(true);
just setLayoutFrozen true after setAdapter for recyclerView
In my case, I had a CardView with a couple of buttons and a RecyclerView. With the solutions of ywwynm and Daryl the problem was that the CardView would intercept the events from all of its children views, including the buttons. But what I wanted was for the CardView to intercept the touch events of the RecyclerView only. My solution was the following:
public class UntouchableRecyclerView extends RecyclerView {
public UntouchableRecyclerView(Context context) {
super(context);
}
public UntouchableRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public UntouchableRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean onTouchEvent(MotionEvent e) {
return false;
}
}
I figured out how to get the click event to the RecyclerView's parent. My solution kind of feels like a hack, so I'm hoping that someone can come up with a better solution.
In the RecylerView.Adapter:
#Override
public ViewHolder onCreateViewHolder(final ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.my_item_layout, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(view);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewGroup.callOnClick();
}
});
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
return viewGroup.performLongClick();
}
});
return viewHolder;
}
I then had to hook up the click event on the RecyclerView:
RecyclerView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
parentView.callOnClick();
}
});
RecyclerView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
return parentView.performLongClick();
}
});
#ywwynm, you are on the right track except the solution doesn't allow the nested RecyclerView to scroll. I combined it with the solution here and came up with this solution to handle click and onLongClick events as well as to allow scrolling.
public class InterceptTouchCardView extends CardView {
private GestureDetector mGestureDetector;
private boolean mLongClicked;
public InterceptTouchCardView(Context context) {
super(context);
Initialize();
}
public InterceptTouchCardView(Context context, AttributeSet attrs) {
super(context, attrs);
Initialize();
}
public InterceptTouchCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Initialize();
}
private void Initialize() {
mGestureDetector = new GestureDetector(getContext(),
new GestureDetector.SimpleOnGestureListener() {
public boolean onDown(MotionEvent e) {
mLongClicked = false;
return true;
}
public void onLongPress(MotionEvent e) {
mLongClicked = true;
performLongClick();
}
});
}
/**
* Intercept touch event so that inner views cannot receive it.
*
* If a ViewGroup contains a RecyclerView and has an OnTouchListener or something like that,
* touch events will be directly delivered to inner RecyclerView and handled by it. As a result,
* parent ViewGroup won't receive the touch event any longer.
*
* We can't Intercept the touch event if we want to allow scrolling since ACTION_DOWN always
* happens before ACTION_MOVE. So handle touch events here since onTouchEvent won't be triggered.
*/
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mGestureDetector.onTouchEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_UP && !mLongClicked)
this.callOnClick();
return false;
}
}

Categories

Resources