ViewHolder itemView is smaller than expected - android

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.

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?

ListView collapse in CustomScrollView

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.

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.

How to open An Activity by Clicking On FrameLayout?

In fact i know how to open an activity by clicking on a button
But i can't do it in frame layout
i try this code but it didn't help me.
FrameLayout ff=(FrameLayout)findViewById(R.id.btn);
ff.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent=new Intent(listActivity.this,bookActivity.class);
startActivity(intent);
}
});
that's all
EDITES:
this my activity_main :
<LinearLayout
android:id="#+id/linbook"
android:layout_margin="15dp"
android:layout_toRightOf="#+id/linjozve"
android:layout_width="130dp"
android:layout_height="180dp"
android:orientation="vertical">
<FrameLayout
android:id="#+id/ketabbtn"
android:layout_width="130dp"
android:layout_height="130dp">
<ImageButton
android:layout_weight="1"
android:id="#+id/btntopright"
android:layout_width="130dp"
android:layout_height="130dp"
android:background="#drawable/rgreen"/>
<ImageButton
android:background="#drawable/book"
android:layout_gravity="center"
android:layout_width="80dp"
android:layout_height="80dp" />
</FrameLayout>
<TextView
android:layout_marginTop="15dp"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="book"/>
</LinearLayout>
thanks
Try to set android:clickable in the xml.
If that doesn't work try to create a custom layout:
By default, onInterceptTouchEvent returns false. So include following code and use this CustomClickableFrameLayout instead of framelayout
public class CustomClickableFrameLayout extends FrameLayout {
private OnClickListener mOnClickListener;
#Override
public void setOnClickListener(OnClickListener l) {
super.setOnClickListener(l);
mOnClickListener = l;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mOnClickListener != null;
}
// Standard constructors — just pass everything
public CustomClickableFrameLayout (final Context context) {
super(context);
}
public CustomClickableFrameLayout (final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public CustomClickableFrameLayout (final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomClickableFrameLayout (final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
The problem is it is quite hard to click on a FrameLayout if it is covered by other views. In your case, the framelayout and the imagebutton have the same size 130x130. This explains why the touch cannot reach the layout.
To make sure you can click it, add some padding between the layout and its childs. (I reduced the size of its child)
<FrameLayout
android:padding="10dp"
android:id="#+id/ketabbtn"
android:layout_width="130dp"
android:layout_height="130dp">
<ImageButton
android:layout_weight="1"
android:id="#+id/btntopright"
android:layout_width="120dp"
android:layout_height="120dp"
android:background="#drawable/rgreen"/>
<ImageButton
android:background="#drawable/book"
android:layout_gravity="center"
android:layout_width="80dp"
android:layout_height="80dp" />
</FrameLayout>
Also, you used wrong ID. So change this:
FrameLayout ff=(FrameLayout)findViewById(R.id.btn);
To this:
FrameLayout ff=(FrameLayout)findViewById(R.id.ketabbtn);
So you can click the padding space to trigger the onClick event.

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