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;
}
}
Related
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.
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 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.
Hello I am new in Android development, and I've read various threads which talked about the fact that a ListView should not be inside a Scrollview and need to wrap in linear or relative layout.
But I have a this layout
<com.itmind.spac.spacapp.custom_extends.CustomScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scrollViewreceipt"
>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/include1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
layout="#layout/toolbar" />
<Spinner
android:id="#+id/customers_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:theme="#android:style/Theme.Holo.Light.DarkActionBar"
/>
<Spinner
android:id="#+id/venues_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:theme="#android:style/Theme.Holo.Light.DarkActionBar"
/>
<Spinner
android:id="#+id/workgroup_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:theme="#android:style/Theme.Holo.Light.DarkActionBar"
/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="150dp">
<ListView
android:id="#+id/activityList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:minHeight="150dp"
/>
</LinearLayout >
<android.gesture.GestureOverlayView
android:id="#+id/signaturePad"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_weight="5"
android:background="#d3d3d3"
android:eventsInterceptionEnabled="true"
android:fadeEnabled="false"
android:gestureColor="#333"
android:gestureStrokeLengthThreshold="0.1"
android:gestureStrokeType="multiple"
android:fadeOffset="5000"
android:orientation="vertical" >
</android.gesture.GestureOverlayView>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="testImage"
android:text="testami"/>
</LinearLayout>
And this is my custoScrollView
public class CustomScrollView extends ScrollView {
private boolean enableScrolling = true;
public boolean isEnableScrolling() {
return enableScrolling;
}
public void setEnableScrolling(boolean enableScrolling) {
this.enableScrolling = enableScrolling;
}
public CustomScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomScrollView(Context context) {
super(context);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (isEnableScrolling()) {
return super.onInterceptTouchEvent(ev);
} else {
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (isEnableScrolling()) {
return super.onTouchEvent(ev);
} else {
return false;
}
}
}
I need customclass scroll view beacause I can set enable scroll when i touch on
so in my activity that implements GestureOverlayView.OnGestureListener I have:
#Override
public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
myScrollView.setEnableScrolling(false);
}
#Override
public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
myScrollView.setEnableScrolling(true);
}
So many elements and I need to scrollView for view all elements.
Inside my scrollView I have a listView that I populate with data retrieve from db.
My problem is that my Listview collapse, that shows one line and I see and scroll in one row.
I tried with min-height but doesn't work, and if I put a layout height in Listview, I cant scroll this list.
How can I do this?
I would a list View in a long page so I have to use scrollview or are there solutions?
Below given custom ListView class will make height of your Listview based on its content inside ScrollView. Use it instead of ListView in your xml-layout file same way you use your CustomScrollView.
public class CustomListView extends ListView {
// private boolean expanded;
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomListView(Context context) {
super(context);
}
public CustomListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
Hope this will help you.
Note :- As per this link of Android developer website, We should never use a ScrollView with a ListView, because ListView takes care of its own vertical scrolling.
I am Having a 10 Image Views Inside a Scroll View
once the Image is clicked I want to perform an action
But when I am trying to scroll the Imags in the ScrollView Touch_down and Touch_UP together are considered as a click
Help me out
I know the solution is easy
But I think I am missing some Logic
I am Putting My Code Here
public class CustomScrollView extends ScrollView {
public CustomScrollView(Context context) {
super(context);
}
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
#Override
public boolean onTouchEvent(MotionEvent p_event) {
if (p_event.getAction() == MotionEvent.ACTION_MOVE && getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
return super.onTouchEvent(p_event);
}
}
In this ScrollView I have added an ImageView
well, i'm gonna try to answer..
i think (in my opinion), you should use onclick only for the ImageView, so when you scrolling on the ScrollView, that ImageView won't get clicked..
here a sample on my code
on the layout.xml
<ImageView
android:id="#+id/ur_img_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#string/description"
android:onClick="onClickEvent"
android:src="#drawable/yourDrawable"
/>
then create the event on your activity.java import android.view.View, create a method similar to the one you use in the .xml file, (on the example android:onClick="onClickEvent")
public void onClickEvent(View v){
//do your event here
}
that's it.