I am using a RecyclerView with a LinearLayoutManager to populate an ArrayList of objects. What I need is how to implement the onFocus event when I change the focus from item to item? onClick event works fine, but the onFocus event has no effect.
XML:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listchannels"
android:layout_width="600dp"
android:layout_height="560dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="30dp">
<android.support.v7.widget.RecyclerView
android:id="#+id/channelList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="left"
android:focusable="true"
android:focusableInTouchMode="true"/>
</FrameLayout>
My coding implementation of onFocus within the ViewHolder class in the ListAdapter:
#Override
public void onFocusChange(View v, boolean hasFocus) {
int position = getAdapterPosition();
if(hasFocus) {
Toast.makeText(this.ctx, "Position : " + position, Toast.LENGTH_SHORT).show();
}
}
The onFocus event here has no effect, it doesn't work. Is it possible to implement the onFocus event on a RecyclerView?
Check if this helps. I found somewhat similar to your issue.
https://stackoverflow.com/a/33190656/2779404
If not, let me try providing another approaches.
Related
I have a RecyclerView with a CardView as the root element of the row item layout. I managed to have a ripple effect to CardView using below list item layout as described here.
Row item layout:
<androidx.cardview.widget.CardView 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="wrap_content"
android:layout_margin="#dimen/margin_8dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="#color/cyan"
app:cardCornerRadius="20dp"
app:cardElevation="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/transparent"
android:padding="#dimen/margin_8dp">
<!-- Underlying views-->
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
The problem is that to have a click action on a row item in the RecyclerView adapter requires a couple of taps, the first tap for selecting the item and showing the ripple effect, and the second tap for the actual click to trigger View.OnClickListener.
Using android:focusableInTouchMode = "true" attribute in CardView is the reason for the double tap; but disabling it, also disable the ripple effect consequently.
I tried to use a custom Ripple effect as in this answer, but doesn't work. Also tried to have the ripple effect on the underlying CardView root element as in this answer, but still nothing new.
Similar thing for using the button style as in here.
And I have a transnational itemView click listener in RecyclerView adapter within the view holder
class ViewHolder extends RecyclerView.ViewHolder {
ViewHolder(#NonNull final View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
}
Replaced View.OnClickListener with View.OnFocusChangeListener, and when the row item gets a focus after it's been clicked, I run the same code as in onClick callback.
But that doesn't give the ripple effect a chance to show up, so posted that with some delay.
itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus)
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Do whatever done in onClick()
}
}, 10); // delay to give the ripple effect a chance to show
}
});
I wish if there's another solution without any delay.
I solved this by totally removing android:focusableInTouchMode = "true" & android:foreground from the CardView, and adding the ripple effect to the root item within the CardView with android:background="?android:attr/selectableItemBackground"
<androidx.cardview.widget.CardView 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="wrap_content"
android:layout_margin="#dimen/margin_8dp"
app:cardBackgroundColor="#color/cyan"
app:cardCornerRadius="20dp"
app:cardElevation="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:padding="#dimen/margin_8dp">
<!-- Underlying views-->
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Issue: When i set android:focusable="false", android:focusableInTouchMode="false" to list item editTexview, editText is not focusable, if i remove those 2 line code listView setOnItemClickListener won't work. My requirement is should work both.
My Custom List Item 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/comment_bg"
android:orientation="vertical">
<EditText
android:id="#+id/list_item_post_edittext_add_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/layout_margin_10"
android:background="#drawable/comment_bg"
android:drawableEnd="#mipmap/ic_send_dark_48"
android:drawablePadding="#dimen/layout_margin_10"
android:drawableRight="#mipmap/ic_send_dark_48"
android:hint="Add a Comment..."
android:inputType="text"
android:padding="#dimen/layout_margin_10"
android:textColor="#color/colorPrimaryDark"
android:textColorHint="#color/colorGray"
android:textSize="12sp" />
</LinearLayout/>
Set onTouchlistener on view of individual item in adapter class. Also you have to manage the state of list such that when list is being scrolled, onTouch should be disabled and when not scrolled, onTouch should work as usual. It works fine on recycler view in my project. Should work with ListView as well.
itemView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(!isScrolling)
{
Toast.makeText(mContext, "Item clicked ", Toast.LENGTH_SHORT).show();
}
return false;
}
});
and to manage isScrolling flag,
rv.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(newState == SCROLL_STATE_DRAGGING)
{
isScrolling=true;
}
else
{
isScrolling=false;
}
}
});
I have a view and I want to show it when I click on button/layout and hide it when I touch somewhere else. How can I do it?
I wrote some code in dispatchTouchEvent(Motion Event) and it's working. But, I think there must be another way to do it.
You can fill the outside of your RecyclerView with another clickable view and implement setOnTouchListener method for that view. Here's an example:
Let's say we have got RecyclerView at the top of our RelativeLayout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scrollbars="vertical" />
<!--View below is just to fill the remaining space. We will use this view to catch outside touch-->
<View
android:id="#+id/outside_detector"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/recyclerView"
android:clickable="true"
android:focusable="true"/>
</RelativeLayout>
And we want to hide and show our recyclerview when we click outside of RecyclerView:
((View) findViewById(R.id.outside_detector)).setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
if(arg1.getAction() == MotionEvent.ACTION_DOWN){
if(recyclerView.getVisibility() == View.VISIBLE){
recyclerView.setVisibility(View.INVISIBLE);
}else{
recyclerView.setVisibility(View.VISIBLE);
}
}
return true;
}
});
If you want to show recyclerview on button click, then just write recyclerView.setVisibility(View.VISIBLE) method inside button ClickListener!
Hope this helps!
I have a ListView and every item in this listview contains a gridview and a header textview. Now I want to recognize when the user clicks anywhere on the whole item, so I tried to set an onItemClickListener. Unfortunately this doesn't seem to work because I didn't get the debug log I added to the click method.
My ListView looks like the following
<ListView
android:id="#+id/listview_previous_issues"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:layout_marginBottom="#dimen/article_separator_margin_topbottom"
android:divider="#null"
android:dividerHeight="0dp" />
And the GridView of the list items are this:
<GridView
android:id="#+id/read_news"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="auto_fit"
android:columnWidth="#dimen/settings_and_issues_read_issues"
android:stretchMode="columnWidth"
android:verticalSpacing="#dimen/paddingSmall"
android:horizontalSpacing="#dimen/paddingSmall"
android:listSelector="#00000000"
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"/>
Update: The call of onItemClickListener
mIssueList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d("debug", "Klicke auf Ressort mit der Id " + pRessorts.get(position).mRessortId);
//Setze die Ressort-ID welche Topnews angezeigt werden
SharedPreferenceHelper.setLongForKey(Constants.PREFS_SHOWN_TOPNEWS_ID, pRessorts.get(position).mRessortId);
//Refreshe die Ansicht
mContext.refresh();
}
});
I think that the GridView is getting the click event instead of the listview, but I'm not sure if this is correct. Can anyone point out what I have to do to get my clickevents at the listview?
So, I was able to solve the problem on my own. Maybe this is helpful for anyone:
I override the dispatchTouchEvent method of my custom gridview, so that it doesn't get the touch event
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
return false;
}
Additionally, instead of an onItemClickListener I set a simple ClickListener at each listitem in the getView method of my ListAdapter and it worked.
I'm using the excellent drag-sort-listview by Carl Bauer (https://github.com/bauerca/drag-sort-listview) to implement a drag-sort enabled listview. However, my requirement is not to need a drag handle on the list, but instead to allow the user to drag the list items using the item itself.
I've gotten that part to work, by setting the #id/drag property to the list item itself.
However, it has a side-effect of not responding to itemClick and itemLongClick events.
Is there any way to get the item clicks / long clicks to work without having a separate draggable layout?
For reference, my code looks like below -
ListView.xml:
<com.mobeta.android.dslv.DragSortListView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dslv="http://schemas.android.com/apk/res/com.myproject"
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
dslv:collapsed_height="1dp"
dslv:drag_scroll_start="0.33"
dslv:max_drag_scroll_speed="0.5" />
ItemView.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="#dimen/list_item_height"
android:orientation="horizontal">
<CheckBox
android:id="#+id/check_box"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"/>
<TextView
android:id="#+id/drag"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:padding="#dimen/list_padding"
android:gravity="center_vertical" />
</LinearLayout>
Activity.java:
DragSortListView listView = (DragSortListView) view.findViewById(R.id.list);
listView.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
Toast.makeText(arg0.getContext(), ((TextView)arg1).getText(), Toast.LENGTH_SHORT).show();
return false;
}
});
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View itemView, int index,
long id) {
Toast.makeText(getView().getContext(), ((TextView)itemView).getText(), Toast.LENGTH_SHORT).show();
}
});
As a bonus, if anyone can help enable multiple-select in addition to click/longclick, it would be much appreciated.
Thanks!
To be able to use OnItemClick and OnItemLongClick in your list you need to set this parameter to the com.mobeta.android.dslv.DragSortListView layout.
dslv:drag_start_mode="onMove"