ViewPager stealing Horizontal Touch Events from Child Fragments - java

So I have a ViewPager, the ViewPager contains two tabs, each tab contains a RecyclerView, cards can be added and removed from each RecyclerView. Each RecyclerView is contained within a Fragment. Each RecyclerViewuses an ItemTouchHelper to manage onSwiped() events. Cards within in the RecyclerView have several behaviors:
Swiping to the right opens an editor for the card
Swiping to the left deletes the Card
Holding down on the card and dragging vertically up or down re-positions the card.
My problem is that the ViewPager is intercepting the onSwiped() method for each RecyclerView, however, vertical touch events are still recieved properly by each RecyclerView. Obviously, this boils down to a touch conflict problem. The ViewPager wants the horizontal touch events for changing tabs, and the RecyclerView wants the horizontal touch events for swiping the cards. However, the ViewPager is stealing all of the touch events. I have disabled horizontal swiping on the ViewPager (the only way the user can change the tab is by tapping on the tab indicator), but the RecyclerView still won't receive any horizontal touch events.
Here's the ViewPager with disabled swiping:
public class NoSwipePager extends ViewPager {
public NoSwipePager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return false;
}
}
Here's a Fragment class:
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.pit_tab, container, false);
container.getParent().requestDisallowInterceptTouchEvent(true);
RecyclerView rv = (RecyclerView) view.findViewById(R.id.movie_recycler_view);
return view;
}
Here is the class that manages the RecyclerView:
public EditManager(RecyclerView rv, Context act, EditListener listener, ArrayList<Element> elements, boolean editing, boolean pit) {
//rv = (RecyclerView) act.findViewById(R.id.movie_recycler_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(act);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
rv.setLayoutManager(linearLayoutManager);
((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false);
if(elements != null) elementsAdapter = new ElementsAdapter(listener, act, new LinkedList<>(elements), editing);
else elementsAdapter = new ElementsAdapter(listener, act, null, editing);
rv.setAdapter(elementsAdapter);
ItemTouchHelper.Callback callback = new ElementTouchHelper(elementsAdapter);
ItemTouchHelper helper = new ItemTouchHelper(callback);
helper.attachToRecyclerView(rv);
}
And finally, here's the method that should be being called, but isn't:
#Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) {
if(direction == ItemTouchHelper.RIGHT) {
mElementsAdapter.postEdit(mElementsAdapter.getElement(viewHolder.getAdapterPosition()));
return;
}
}

Try replacing your NoSwipePager with this:
public class NoSwipePager extends ViewPager {
public NoSwipePager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected boolean canScroll(View v, boolean checkV, int dx, int x,
int y) {
if (v instanceof RecyclerView) {
return(true);
}
return(super.canScroll(v, checkV, dx, x, y));
}
}
I have used this approach successfully for a ViewPager with maps in the pages (substituting SurfaceView for the RecyclerView). My guess is that you will need to replace the v instanceof RecyclerView test with something more sophisticated (and custom to your app). But I think canScroll() is more likely to give you what you want than blocking onInterceptTouchEvent().

Related

How to add dynamic views to android layout in recyclerview adapter

enter image description here
I have a list of parents that each has zero or many children
I use recyclerview to show the parents.
I want to show parents as header of each recyclerview item and below show all the children
what is the best way to show the children?
is it suitable to show children in a listview or add dynamic views for each child if so how to do that?
I have attach the image of layout, please check what I mean
thanks
enter image description here
I think, may this helpful bcz, in every position there are dynamicly generate item view respectably.
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
linearLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
IAFTbind.rcvFilters.setLayoutManager(linearLayoutManager);
IAFTbind.rcvFilters.setAdapter(new RecyclerView.Adapter() {
public int checkedPosition = 0;
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ImageView imageView = new ImageView(getContext());
imageView.setScaleX(.9f);
imageView.setScaleY(.9f);
imageView.setLayoutParams(new ViewGroup.LayoutParams(size, size));
return new RecyclerView.ViewHolder(imageView) {
#Override
public String toString() {
return super.toString();
}
};
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int p = holder.getAdapterPosition();
((ImageView) holder.itemView).setBackground(new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(fBits.get(p), size, size, false)));
// ((ImageView) holder.itemView).setBackground(new BitmapDrawable(getResources(), bitmaps.get(position)));
int cp = this.checkedPosition;
if (cp == -1) {
((ImageView) holder.itemView).setImageResource(0);
} else if (cp == p) {
((ImageView) holder.itemView).setImageResource(R.drawable.draw_tool_selecter_strock);
} else {
((ImageView) holder.itemView).setImageResource(0);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((ImageView) holder.itemView).setImageResource(R.drawable.draw_tool_selecter_strock);
checkedPosition = p;
notifyDataSetChanged();
changeBitmapEffect(p);
}
});
}
#Override
public int getItemCount() {
return fBits.size();
}
});
Problem statement
You want to add childs dynamically as per the screenshot you shared.
Solution
So here I can see 2 recycler views, one inside another. The first recycler view is for parents which have 2 items
Parents name
Another recycler view for children
What you need to do is, you need to pass the updated list of item to the parent's recyclerview adapter, which will send the updated list of item to childs recyclerview adapter and the childs recyclerview adapter will check using the diffUtil about any changes and it will reflect the changes in the recyclerview.

ViewPager2 flashes/reloads on swipe

I'm trying to build an Android application using the new ViewPager2. I've added two ViewPagers, separated by a view, and when you swipe, both viewpagers should move. Both view pagers are moving correctly but upon completion of the gesture, the swiped view flashes and the non swiped view reloads, as demonstrated by the attached gif. Here is my code for the Activity, ViewPagerAdapter, and Fragment. Any help is appreciated
public class MainActivity extends FragmentActivity {
ActivityMainBinding viewBinding;
MyPager adapter1, adapter2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
List<String> data = populateData();
adapter1 = new MyPager(this, data);
adapter2 = new MyPager(this, data);
viewBinding.viewPager.setAdapter(adapter1);
viewBinding.viewPager2.setAdapter(adapter2);
viewBinding.viewPager2.setOffscreenPageLimit(data.size());
viewBinding.viewPager.setOffscreenPageLimit(data.size());
viewBinding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
viewBinding.viewPager2.scrollTo(positionOffsetPixels, 0);
}
#Override
public void onPageScrollStateChanged(final int state) {
viewBinding.viewPager2.setCurrentItem(viewBinding.viewPager.getCurrentItem(), true);
}
});
viewBinding.viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
viewBinding.viewPager.scrollTo(positionOffsetPixels, 0);
}
#Override
public void onPageScrollStateChanged(final int state) {
viewBinding.viewPager.setCurrentItem(viewBinding.viewPager2.getCurrentItem(), true);
}
});
}
private List<String> populateData() {
List<String> data = new ArrayList<>();
for (int x = 0; x < 10; x++) {
String derril = "derril " + x;
data.add(derril);
}
return data;
}
}
public class MyPager extends FragmentStateAdapter {
List<String> data;
public MyPager(#NonNull FragmentActivity fragmentActivity, List<String> data) {
super(fragmentActivity);
this.data = data;
}
#NonNull
#Override
public Fragment createFragment(int position) {
return DerrilFragment.newInstance(data.get(position));
}
#Override
public int getItemCount() {
return data.size();
}
}
public class DerrilFragment extends Fragment {
PizzaBinding viewBinding;
String data;
private DerrilFragment(String data) {
this.data = data;
}
public static DerrilFragment newInstance(String data) {
return new DerrilFragment(data);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
viewBinding = DataBindingUtil.inflate(inflater, R.layout.pizza, container, false);
viewBinding.text.setText(data);
return viewBinding.getRoot();
}
}
You can implement by RecyclerView.
class MainActivity : AppCompatActivity() {
private var isFirstFocused = false
private val helper: SnapHelper = LinearSnapHelper()
private val helper2: SnapHelper = LinearSnapHelper()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recycler1.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (isFirstFocused) recycler2.scrollBy(dx, dy)
}
})
recycler2.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (!isFirstFocused) recycler1.scrollBy(dx, dy)
}
})
recycler2.setOnTouchListener { v, event ->
if (event.action != null && event.action == MotionEvent.ACTION_DOWN ) {
isFirstFocused = false
return#setOnTouchListener true
}
return#setOnTouchListener false
}
recycler1.setOnTouchListener { v, event ->
if (event.action != null && event.action == MotionEvent.ACTION_DOWN ) {
isFirstFocused = true
return#setOnTouchListener true
}
return#setOnTouchListener false
}
helper.attachToRecyclerView(recycler1)
helper2.attachToRecyclerView(recycler2)
val manager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
val manager2 = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
recycler1.layoutManager = manager
recycler2.layoutManager = manager2
recycler2.adapter = Adapter(items)
recycler1.adapter = Adapter(items)
}
private val items = listOf("One", "Two", "Three")
}
class Adapter(val list: List<String>) : RecyclerView.Adapter<Adapter.Holder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.activity_listview,
parent,
false
)
return Holder(view)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.itemView.text_view.text = list[position]
}
override fun getItemCount() = list.size
class Holder(view: View) : RecyclerView.ViewHolder(view)
}
You can try to use adapter.setOffscreenPageLimit = n. It sets the number of pages that should be retained to either side of the currently visible page(s). Pages beyond this limit will be recreated from the adapter when needed If you have a limited number of pages.
More details are here.
In your setCurrentItem() usages (under onPageScrollStateChanged()), set the second parameter to false
This will disable animation (not permanently) in the ViewPager when you set a page programatically, which is the reason you see the flash-like animation.
To know why:
Consider two ViewPagers A & B
You scroll from one page to another in A, which induces the same scroll in ViewPager B. Once you completely scroll from one page to another in A, the currentItem is internally set by A, but not for B.
Now, you inform B about the page change by calling setCurrentItem([index], true), which, irrespective of your scroll, will animate the page change introducing the flash (your induced scroll is reset to normal, and page animation for one page to another is started)
this looks a bit suspicious that you are calling setCurrentItem inside onPageScrollStateChanged for every passed state... try to add some if and setCurrentItem only when state==ViewPager2.SCROLL_STATE_IDLE
list of states:
ViewPager2.SCROLL_STATE_IDLE
ViewPager2.SCROLL_STATE_DRAGGING
ViewPager2.SCROLL_STATE_SETTLING
ViewPager2.SCROLL_STATE_DRAGGING state is handled in onPageScrolled method (sharing position offset aka. dragging by finger)
ViewPager2.SCROLL_STATE_SETTLING is auto-scroll (snap) of ViewPager2 to closest page - in here I'm not shure that ViewPager2 dragged "by code" (sharing position with scrollTo) will also enter in this state causing same snap behavior (so also onPageScrolled?) ... worth testing, put some logs in there
also: you are dragging one ViewPager2 and in onPageScrolled you are calling scrollTo on second ViewPager2. this may lead to call onPageScrolled call on this second listener, which is calling first ViewPager2 scrollTo method and so on... looped. consider dynamic unregistering listener when user will start drag one ViewPager2 and reregistering when movement ends (SCROLL_STATE_IDLE state)
I think the problem is that in 'onPageScrolled' method of both ViewPagers you are scrolling other pager. So, both pages might be scrolling each other at the same. For example, if you are scrolling ViewPager_1 then its onPageScrolled method will scroll ViewPager_2 and as ViewPager_2 is now scrolled then its onPageScrolled method will be called and that will scroll ViewPager_1 and vice versa.
So, You may need to consider applying the check that if touch is holded ny ViewPager_1 then code inside onPageScrolled method of ViewPager_2 should not be executed. Or if you want to avoid those checks. Then registerOnPageChangeCallback/unregisterOnPageChangeCallback the listener of other ViewPager when one holds the touch.
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
viewBinding.viewPager.scrollTo(positionOffsetPixels, 0);
}

Is there a way on the new navigation component on back pressed to restore the state on recyclerview

What I'm using:
MVP
Navigation Component
Single Activity
Fragments
RecyclerView
Realm
Problem:
I have a MainActivity that hosts all the fragments for the application flow.
There's one fragment that has a recyclerview, the more I scroll the more API requests are made. Whenever I press one item to navigate to a different fragment and press back to return to the list, the list only has a small piece of the previous loaded recyclerview, I want everything.
Is it possible to do this with the navigation component?
Can I prevent onCreateView being called whenever I press back?
I've tried to save the View in a static variable, have a onSaveInstance (but it never gets called)
I'm using navigation this way:
#Override
public void onClick(View view) {
Navigation.findNavController(view)
.navigate(R.id.action_tripsFragment_to_tripDetailsFragment, bundle);
}
MainFragment:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater,
#Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.trips_fragment, container, false);
mPresenter = new TripsFragmentPresenterImpl(this);
ButterKnife.bind(this, v);
navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
mLinearLayoutManager = new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false);
mAdapter = new PaginationAdapter(getContext());
mAdapter.setOnItemClickListener(onItemClickListener);
mPresenter.setLoadingTripsProgressBar(true);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.addOnScrollListener(new PaginationScrollListener(mLinearLayoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
loadNextPage();
}
#Override
public int getTotalPageCount() {
return currentPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
loadFirstPage();
return v;
}
What I expect:
Whenever I press back from TripDetails to Trips I want to get my recyclerview as it was, with the previous loaded items at the correct position.
Current results:
Whenever I press back from TripDetails to Trips the recyclerview cuts the items to only the items that are visible, meaning that we'll lose items previous loaded in previous pages.
For future reference I've managed to solve the issue by creating a ModelView to keep the data independent of the lifecycle of the fragment

android - OnClickListener not working for first click in recyclerview

I am working in a chat application for Android and I am using RecyclerView for listing the messages.
I have written the adapter, but I am having a problem with detecting when an element(TextView in this case) inside the layout is clicked.
This is my adapter:
public class ChatRoomThreadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private class ViewHolder extends RecyclerView.ViewHolder {
TextView message, timestamp;
private ViewHolder(View view) {
super(view);
message = (TextView) itemView.findViewById(R.id.message);
timestamp = (TextView) itemView.findViewById(R.id.timestamp);
}
}
public ChatRoomThreadAdapter(Context mContext, ArrayList<Message> messageArrayList, String userId) {
this.mContext = mContext;
this.messageArrayList = messageArrayList;
this.userId = userId;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
((ViewHolder) holder).message.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (((ViewHolder) holder).timestamp.getVisibility() == View.GONE) {
((ViewHolder) holder).timestamp.setVisibility(View.VISIBLE);
} else {
((ViewHolder) holder).timestamp.setVisibility(View.GONE);
}
}
});
}
#Override
public int getItemCount() {
return messageArrayList.size();
}
}
The current onClick works but I have to click twice on the message in order for the onClick to trigger. I have been searching and trying endless solutions for 3 days in order to fix this, but none of the solutions on the internet have worked so far.
Make sure you have both focusableInTouchMode & focusable disabled on the button. The first click will get the focus and the second click executes the onClickListener.
.
After scroll, recycler view items are not clickable and the issue is still open https://issuetracker.google.com/issues/66996774
Found a way to force click if the scroll state is still SCROLL_STATE_SETTLING
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.recyclerview.widget.RecyclerView
class WrapperRecyclerView(context: Context, attributeSet: AttributeSet?, defStyle: Int) :
RecyclerView(context, attributeSet, defStyle) {
constructor(context: Context) : this(context, null, 0)
constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet, 0)
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
val eventConsumed = super.onInterceptTouchEvent(event)
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> if (scrollState == SCROLL_STATE_SETTLING) {
parent.requestDisallowInterceptTouchEvent(false)
// only if it touched the top or the bottom.
if (!canScrollVertically(-1) || !canScrollVertically(1)) {
// stop scroll to enable child view to get the touch event
stopScroll()
// do not consume the event
return false
}
}
}
return eventConsumed
}
}
The following class will fix this issue :
class WrapperRecyclerView(context: Context, attributeSet: AttributeSet?, defStyle: Int) :
RecyclerView(context, attributeSet, defStyle) {
constructor(context: Context) : this(context, null, 0)
constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet, 0)
override fun onScrollStateChanged(state: Int) {
super.onScrollStateChanged(state)
if (state == RecyclerView.SCROLL_STATE_SETTLING) {
this.stopScroll();
}
}
}
For me focusableInTouchMode & focusable doesn't work.
Add this line to RecyclerView :
android:nestedScrollingEnabled="false"
Enjoy¡
A tip for those trying to debug this kind of scrolling state problem in recycler view. Create your own MyRecyclerView extending RecyclerView, and override fun onInterceptTouchEvent there just to add logging:
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
Log.d(TAG, "MyRecyclerView intercepted a touch event: ${event!!.action}")
Log.d(TAG, "MyRecyclerView is in state: ${scrollState}")
return super.onInterceptTouchEvent(event)
}
It helped me to see that my recycler view was stuck in SCROLL_STATE_DRAGGING.
I believe, for child views to receive clicks coming from recycler view, the recycler view has to be in SCROLL_STATE_IDLE state.
So my solution to the problem was based on what #Rocky suggested,
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
val eventConsumed = super.onInterceptTouchEvent(event)
/*
* Fix the following problem.
*
* When the recycler view received a click, it was still in the DRAGGING mode.
* It consumed the click, instead of passing it down to the child view.
* The user had to click a child view again, to be able to interact with it.
*/
when (event!!.actionMasked) {
MotionEvent.ACTION_DOWN -> {
if (scrollState == SCROLL_STATE_DRAGGING) {
stopScroll()
return false
}
}
}
return eventConsumed
}

Android: removing recycler view through fragment

Question:
I'm adding views into a recyclerview with a click. When I click a view it opens a DialogFragment, how do I remove that view through the DialogFragment (by clickign on a button inside it)?
Adapter:
public class SubjectsAdapter extends RecyclerView.Adapter<SubjectsAdapter.ViewHolder> {
public List<String> items = new ArrayList<>();
public Activity mcontext;
public SubjectsAdapter(Activity context) {
this.mcontext=context;
}
public void addItem(String name) {
items.add(name);
notifyItemInserted(items.size() - 1);
}
public void removeItem(int position) {
items.remove(position);
notifyItemRemoved(position);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.grid_item_button, parent, false);
view.requestFocus();
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setButtonName(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
int i = 100;
public EditText EditName;
class ViewHolder extends RecyclerView.ViewHolder{
public Button GridButton;
public SharedPreferences prefs;
public ViewHolder(View itemView) {
super(itemView);
GridButton = (Button) itemView.findViewById(R.id.grid_button);
EditName = (EditText) itemView.findViewById(R.id.editName);
ClassName = (TextView) itemView.findViewById(R.id.ClassName);
prefs = mcontext.getPreferences(Context.MODE_PRIVATE);
GridButton.setId(++i);
EditName.requestFocus();
//Showing the DialogFragment
GridButton.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Fragment_Subject_Edit editFragment = Fragment_Subject_Edit.newInstance();
Bundle data = new Bundle();
data.putInt("ID", v.getId());
editFragment.setArguments(data);
editFragment.show(mcontext.getFragmentManager(), "Title");
return false;
}
});
}
public void setButtonName(String buttonName) {
GridButton.setText(buttonName);
}
}
}
Adding views in the activity:
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
final SubjectsAdapter adapter = new SubjectsAdapter(this);
recyclerView.addItemDecoration(new SampleItemDecoration());
recyclerView.setAdapter(adapter);
recyclerView.setItemViewCacheSize(15);
recyclerView.setNestedScrollingEnabled(false);
#Override
public void onClick(View v) {
adapter.addItem(prefs.getString("key1", null));
}
What I have from Lucas Crawford answer, although I'm not getting it right:
1:
public Activity mcontext;
public View.OnLongClickListener LongClicking;
public SubjectsAdapter(Activity context, View.OnLongClickListener longClick) {
this.mcontext = context;
this.LongClicking = longClick;
}
2:
View.OnLongClickListener LongClicker;
...
...
...
adapter = new SubjectsAdapter(this, LongClicker);
recyclerView.setAdapter(adapter);
3:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.grid_item_button, parent, false);
view.setOnLongClickListener(LongClicking);
return new ViewHolder(view);
}
4:
fm = getFragmentManager();
ClassEditor = new Fragment_Subject_Edit();
LongClicker = new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Bundle data = new Bundle();
data.putInt("ID", v.getId());
ClassEditor.setArguments(data);
ClassEditor.show(fm, "Title");
return false;
}
};
Nothing happens when I longClick the button, nor anywhere in the view, what's wrong with these steps, and how to do number 5?
Easy Solution
Instead of assigning the onLongClickListener in the ViewHolder, I would assign the click listener as part of the constructor in the adapter's creation. This way, when you create the view holder in onCreateViewHolder, you give the new view for the view holder the adapter's passed click listener. This is better since it decouples the click event from the ViewHolder's work, as well as letting the activity that created the adapter handle what happens when clicked. (also the dialog fragment created is tied to an Activity lifecycle, why not create it there!).
Next, add a click listener to the dialog fragment's creation, possible as a member variable with a getter/setter. The button in the fragment is then assigned that click listener when you are inflating the dialog fragments view. This way, you do all the click event listening within your Activity, and use the same strategy in both the adapter and dialog fragment.
Can provide code IF required. I hope that makes sense.
Here is a list of what I suggested:
Add a click listener to the constructor of the recycler view adapter.
Pass an onLongClickListener object when you are creating the adapter within your activity.
Assign your view the click listener passed to the adapter in onCreateViewHolder.
Within the click listener created in your activity for the adapter, create the dialog fragment and assign a NEW click listener to handle the button press you desire. This click listener also is apart of the Activity.
When inflating the views in the dialog fragment, give the target button the listener you assigned to the dialog fragment.
The logic of removing the view (or item) from the RecyclerView adapter would be in the click listener you assign to the new dialog fragment. You need a reference to the item currently being removed as well (which you do via arguments).
Advanced Solution
A more advanced solution would be to use something like EventBus which handles listening for events. It cleans up a lot of code to use this. Another one is Otto by Square. It does the same thing, and I personally use Otto for event driven listening rather than passing around click listeners. You can decouple the need for a click listener being passed to your recycler's adapter by just setting a click listener in the adapter that posts an event that your activity is listening for, which then triggers the dialog fragment creation. The dialog fragment would then do the same thing by creating and assigning a new listener within the fragment that posts another event that your activity is listening for related to removing the particular adapter item.

Categories