Grasping the fragment of a viewpager - java

I have a view pager with 2 fragments. One of them has a blue screen that should be hidden once the fragment is selected. So, I want to grasp a pointer to this fragment that I can call a certain method on once the user selects this fragment. I have implemented the addOnPageChangeListener interface. I want to call that method, to hide the blue screen, in the onPageSelected call.
My question is how can I grasp a pointer to the current selected fragment?
I have tried the following:
AnswersFragment fragment = (AnswersFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + this.pager + ":" + position);
if (fragment == null)
fragment = new AnswersFragment();
But it always returns null.
I have also tried to keep an instance variable called currentAnswerFragment, but it doesn't necessarily point to the current fragment as the view pager keeps track of 3 fragments at the same time.
Also, my model is not an array (or 2) of fragments. I call a function that initializes a fragment and return it. With that said, I can't use the index of the current item to get the fragment being displayed.

You need to override onPageSelected method and fire respected method on desired screen, for example:
#Override
public void onPageSelected(int position) {
if (position == 0) {
callForPage();
} else if (position == 1) {
callForPage1();
}
}
and you can use following code to go to selected screen.
viewPager.setCurrentItem(pageposition,false);
setCurrentItem takes two parameters: item number and boolean for smooth scrolling.
UPDATE
make a PageAdapter class for example
public class PagerAdapter extends FragmentPagerAdapter {
Context mcontext;
public PagerAdapter(FragmentManager fm, Context context) {
super(fm);
mcontext = context;
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new FragmentA();
}
else if(position==1){
return new FragmentB();
}
else
return new FragmentC();
}
#Override
public int getCount() {
return 3;
}
}
Then in your Activity class
mViewPager.setAdapter(new PagerAdapter(getSupportFragmentManager(), this));
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
///Here you handle the pointer thing
}
#Override
public void onPageScrollStateChanged(int state) {
}
});

Edit:
A better implementation of the solution I came up with is found here
After a while of debugging, I figured out a solution.
By keeping an external data structure of the previous fragments visited. In my case I kept a HashMap as a normal array won't work. In some cases I call the pager.setCurrentItem(position). Meaning that I jump fragments. So a HashMap is more appropriate.

Related

How to detect each RecyclerView item after it is displayed

I want to detect each item in my RecylerView after it is displayed to the user.
Basically, I am trying to play a sound on each item after it is loaded on the screen.
But I am not able to detect whenever each item is loaded on the screen! Is there any method I have to call to detect each item rendered
E.g 1st RecyclerView item displayed -> play sound
2st RecyclerView item displayed -> play sound...
My Adapter class looks like this -
public class AdapterListAnimation extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Multiples> items = new ArrayList<>();
private Context ctx;
private OnItemClickListener mOnItemClickListener;
private int animation_type = 0;
.........
.........
I am calling this initComponent() method from onCreated() method. Can you give advice on what should I do to achieve my goal as described above?
private void initComponent() {
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
items = DataGenerator.getPeopleData(this,of,value);
setAdapter();
/* MediaPlayer mp=MediaPlayer.create(this, R.raw.sword);
if (mp.isPlaying()) {
mp.stop();
mp.release();
mp = MediaPlayer.create(this, R.raw.sword);
} mp.start();*/
}
private void setAdapter() {
// Set data and list adapter
mAdapter = new AdapterListAnimation(this, items, animation_type);
recyclerView.setAdapter(mAdapter);
// on item list clicked
mAdapter.setOnItemClickListener(new AdapterListAnimation.OnItemClickListener() {
#Override
public void onItemClick(View view, com.math.multiplication.model.Multiples obj, int position) {
Snackbar.make(parent_view, "Item " + obj.first + " clicked", Snackbar.LENGTH_SHORT).show();
}
});
}
you need to override onViewAttachedToWindow and onViewDetachedFromWindow. but for detecting holer type you need getItemViewType() just like that:
public class PostAdapter extends RecyclerView.Adapter {
#Override
public int getItemViewType(int position) {
switch (types.get(position)){
case 1:
return 1;
case 2:
return 2;
default:
return position;
}
}
#Override
public void onViewAttachedToWindow(#NonNull RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder);
if (holder.getItemViewType() == 1){
//play song
}
}
#Override
public void onViewDetachedFromWindow(#NonNull RecyclerView.ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
if (holder.getItemViewType() == 1){
//pause song
}
}
You need to add listener for TTS. Then update your RecyclerView to show right image when speech starts and ends.
I've created a test project to show how it can be implemented. Here you can see how it works. Here is my github repository.
Here is main part of my MainActivity class.
private void initTts() {
tts = new TextToSpeech(this, this);
tts.setLanguage(Locale.US);
tts.setOnUtteranceProgressListener(new MyListener());
}
#Override
public void onInit(int status) {
playSound(0);
}
private void playSound(int index) {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, String.valueOf(index));
tts.speak(data.get(index), TextToSpeech.QUEUE_ADD, hashMap);
}
class MyListener extends UtteranceProgressListener {
#Override
public void onStart(String utteranceId) {
int currentIndex = Integer.parseInt(utteranceId);
mMainAdapter.setCurrentPosition(currentIndex);
handler.post(new Runnable() {
#Override
public void run() {
mMainAdapter.notifyDataSetChanged();
}
});
}
#Override
public void onDone(String utteranceId) {
int currentIndex = Integer.parseInt(utteranceId);
mMainAdapter.setCurrentPosition(-1);
handler.post(new Runnable() {
#Override
public void run() {
mMainAdapter.notifyDataSetChanged();
}
});
if (currentIndex < data.size() - 1) {
playSound(currentIndex + 1);
}
}
#Override
public void onError(String utteranceId) {
}
}
You can simply use onViewAttachedToWindow(VH) in your adapter.
See https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#onViewAttachedToWindow(VH)
Update:
As you know RecyclerView will be call OnBindViewHolder only once for each item.
RecyclerView will not call this method again if the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined.
So you can use onViewAttachedToWindow
(onViewAttachedToWindow) Called when a view created by this adapter has been attached to a window.
This can be used as a reasonable signal that the view is about to be seen by the user. If the adapter previously freed any resources in onViewDetachedFromWindow those resources should be restored here.
You can use it like this:
public class Adapter extends RecyclerView.Adapter<MyViewHolder> {
// rest of your code
#Override
public void onViewAttachedToWindow(MyViewHolder holder) {
super.onViewAttachedToWindow(holder);
// play your sound
}
}
Hope it helps!
If I understood your problem correctly, you want to play a sound when an item in the RecyclerView is loaded.
Hence I would like to think of a workaround here. You might consider having an item added after the sound for the previous item has been played.
I would like to provide a pseudo code for what I am trying to explain. You might consider using the MediaPlayer.OnCompletionListener for listening if your sound has stopped playing and then add the next item to the RecyclerView and then call notifyDataSetChanged on your adapter to load the next item in your RecyclerView.
Hence you might want the following modification in your adapter first.
// Declare a function in your adapter which adds new item in the RecyclerView
public void addItem(Multiples item) {
items.add(item);
notifyItemInserted(items.size());
}
Now in your Activity where you set up the RecyclerView, you need to have the following arrays.
ArrayList<Multiples> allItems = new ArrayList<Multiples>(); // This list will contain all the data
int soundToBePlayed = -1; // Initialize the value with -1
ArrayList<Integer> soundList = getAllSoundsInAList();
Modify your functions as follows.
private void initComponent() {
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
allItems = DataGenerator.getPeopleData(this,of,value);
setAdapter();
}
private void setAdapter() {
mAdapter = new AdapterListAnimation(this, animation_type); // Do not pass the items list
recyclerView.setAdapter(mAdapter);
// .. Your item click listener goes here
}
As the items was removed from the constructor, you need to modify the constructor of your adapter as well. Just remove the variable which takes the items. Because we will be adding elements to the items later.
Then you need to write another function which needs to be called in your onCreate function of your activity after your call your initComponent function. The function should look like the following.
private void addItemInRecyclerViewAndPlaySound() {
soundToBePlayed++;
adapter.addItem(allItems.get(soundToBePlayed));
recyclerView.scrollToPosition(adapter.getItemCount() - 1);
Integer soundId = soundList.get(soundToBePlayed);
MediaPlayer mp = MediaPlayer.create(this, soundId);
if (mp.isPlaying()) {
mp.stop();
mp.release();
mp = MediaPlayer.create(this, soundId);
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
// Call this function again to add next item
// and play the next sound at the same time
if(soundToBePlayed < allItems.size() - 1)
addItemInRecyclerViewAndPlaySound();
}
});
}
mp.start();
}
I have not tested the code, however, I think you get the idea already. This can be implemented gracefully using a interface inside the adapter as well. The imeplemntation technique is your choice.

Stop FragmentPagerAdapter from creating all its fragments at once

So I have bottom navigation bar with 4 fragments for each tab, and inside each one I call an API request to fetch some data, but the problem is each time I press any tab of the bar, at least two of the fragments gets created and they call their own method and by extension they fires the API request..! I just want the fragment that I select to be instantiated.!
I know the adapter behaves like this to pre-render the fragment to ensure better transaction between tabs and whatnot..! but I really can't afford to call multiple api calls with each select..!
Adapter
public class My_PagerAdapter extends FragmentPagerAdapter {
// I've tried FragmentStatePagerAdapter but same thing
public My_PagerAdapter (FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
new MyFragment_1();
case 1:
new MyFragment_2();
case 2:
new MyFragment_3();
case 3:
new MyFragment_4();
}
}
#Override
public int getCount() {
return 4;
}
}
Edit
This how I call the adapter..
ViewPager viewPager = main.findViewById(R.id.vp);
viewPager.setOffscreenPageLimit(1);
viewPager.setAdapter(new My_PagerAdapter (getChildFragmentManager()));
navigationTabBar.setModels(models); // just UI stuff for each tab offered by the bottom navigation bar library,
navigationTabBar.setViewPager(viewPager);
I ran into this same exact issue on a project that I'm working on
The solution for me was to add the API calls on the OnResume method in each fragment.
That way they will only be triggered when the fragment is fully visible.
Check out the fragment lifecycle
Ok this is exactly an issue that i faced. The solution i have does not stop the viewpager from creating the fragments but it will stop the calls to network api.
Heres the gist:
1) Create an interface
public interface ViewPagerLifeCycleManagerInterface {
void onResumeAndShowFragment();
void onPauseAndHideFragment();
//void refreshFragment();
}
2) modify your FragmentPagerAdapter to override the onInstantiateItem method
Here each Fragment will have a weakReference declared inside the Adapter class in order to store a reference to the fragments created
#Override
public Object instantiateItem(ViewGroup container, int position){
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
switch (position){
case 0:
xyzWeakReference=null;
xyzFragmentWeakReference=new WeakReference<>((xyz)createdFragment);
break;
case 1:
xyz1WeakReference=null;
xyz1WeakReference=new WeakReference<>((xyz1WeakReference)createdFragment);
break;
}
return createdFragment;
};
3) Inside the FragmentPagerAdapter, add the following method in order to fetch the weak reference of the fragment in picture
public Fragment getFragmentAtGivenPosition(int i){
switch (i){
case 0:
if(xyzFragmentWeakReference == null){
return null;
}
return xyzFragmentWeakReference.get();
case 1:
if(xyz1FragmentWeakReference == null){
return null;
}
return xyz1FragmentWeakReference.get();
}
}
4) Now in the activity where the TabLayout is created and the view pager instantiated, attach a listener to the TabLayout for listening to tab changes
tabLayout_bookmarks.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(final TabLayout.Tab tab) {
//let the instantiateItem have some time to be called by the adapter
currentFragmentIndex = tab.getPosition();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ViewPagerLifeCycleManagerInterface currentFragment = (ViewPagerLifeCycleManagerInterface)btca.getFragmentAtGivenPosition(tab.getPosition());
if(currentFragment!=null){
currentFragment.onResumeAndShowFragment();
}else{
//Log.d("FragmentCreate","Current fragment is null and fucked up in adapter");
//if it is null ... that means the adapter hasn't yet called instantiate item ... this internally calls get item any way
//.....
//This shouldn't really hit but in case it does ... keep a handler in order to ensure that everything is created
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ViewPagerLifeCycleManagerInterface localFragment = (ViewPagerLifeCycleManagerInterface)btca.getItem(tab.getPosition());
//getItem never returns a null fragment unless supplied a horrendous value for position
//by the time these 50 ms pass, the instantiate item should surely have been called
//else it will be an empty space ... no crash though
localFragment.onResumeAndShowFragment();
}
},50);
}
}
},100);
}
#Override
public void onTabUnselected(final TabLayout.Tab tab) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ViewPagerLifeCycleManagerInterface currentFragment = (ViewPagerLifeCycleManagerInterface)btca.getFragmentAtGivenPosition(tab.getPosition());
if(currentFragment!=null){
currentFragment.onPauseAndHideFragment();
}else{
//Log.d("FragmentCreateTab","the fucking fragment was null");
//if it is null ... that means the adapter hasn't yet called instantiate item ... this internally calls get item any way
//.....
//This shouldn't really hit but in case it does ... keep a handler in order to ensure that everything is created
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ViewPagerLifeCycleManagerInterface localFragment = (ViewPagerLifeCycleManagerInterface)btca.getItem(tab.getPosition());
//getItem never returns a null fragment unless supplied a horrendous value for position
//by the time these 50 ms pass, the instantiate item should surely have been called
//else it will be an empty space ... no crash though
localFragment.onPauseAndHideFragment();
}
},50);
}
}
},100);
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
//do nothing
}
});
5) In each of the Fragments inside the Viewpager, implement the Interface we created in step 1 and override the methods.
Create a boolean variable in each fragment amIVisible... This will help decide when the fragment is visible and when it can call the network api
a) here for the first fragment in viewpager, i.e at 0 index, the network api call has to happen immediately after the view gets created. This fragment is obviously visible by default. This is written inside onCreateView method
if(dataList!=null && dataList.size()==0) {
if (savedInstanceState==null) {
//your api call to load from net
} else {
if (savedInstanceState.getBoolean("savedState")) {
//If you have saved data in state save, load it here
} else {
//only fire the async if the current fragment is the one visible, else the onResumeAndShowFragment will trigger the same async when it becomes visible
if (savedInstanceState.getBoolean("amIVisible")) {
//Load data from net
}
}
}
}
The other methods are as follows for the first fragment
#Override
public void onResumeAndShowFragment() {
amIVisible=true;
if(dataList!=null && dataList.size()==0){
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Load data from net if data was not found,
//This basically means auto refresh when user scrolls back and the fragment had no data
}
},400);
}
}
#Override
public void onPauseAndHideFragment() {
amIVisible=false;
}
Here i have overriden onSaveInstanceState method and saved the value of amIVisible and savedState is a boolean which indicates if the list has at least 1 item or not.
b) For the other fragments, Data will be loaded by the following process
if(savedInstance!=null){
if (savedInstance.getBoolean("savedState")) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//load data from saved State
}
},100);
} else {
//only fire the async if the current fragment is the one visible, else the onResumeAndShowFragment will trigger the same async when it becomes visible
if (savedInstance.getBoolean("amIVisible")) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//load data from net
}
},100);
}
}
}
The interface methods are the same for the other fragments.
This is pretty complicated but does the job. The weak references inside the adapter even allow garbage collection and avoid context leaks.

PagerAdapter changed adapters content with notifying

So first things first, here's the error that I'm getting:
java.lang.IllegalStateException: The application's PagerAdapter
changed the adapter's contents without calling
PagerAdapter#notifyDataSetChanged! Expected adapter item count: 18,
found: 28
I'm using a RecyclerView in my main activity and that has a List<Objects> as the dataset, that's all fine.
From that activity I call the second activity when a RecyclerView item is clicked that is basically a gallery implemented using a ViewPager using this code:
public void startSlideActivity(final int position) {
DataTransferer.get().storeItems(feed);
Intent i = new Intent(context, SlideActivity.class);
...
i.putExtra("POSITION", position);
context.startActivity(i);
}
My data is too large to transfer through an intent (using Parcelable or otherwise) so I'm using a singleton to hold and transfer my list, here's the code:
public class DataTransferer {
private static volatile DataTransferer singleton;
private List<Thing> items;
public static DataTransferer get(){
if (singleton == null) {
synchronized (DataTransferer.class) {
singleton = new DataTransferer();
}
}
return singleton;
}
private DataTransferer(){
}
public void storeItems(List<Thing> items){
this.items = items;
}
public List<Thing> getStoredItems(){
return items;
}
}
In the second activity I set the adapter and retrieve the list like so:
feed = DataTransferer.get().getStoredItems();
final int position = this.getIntent().getIntExtra("POSITION", 0);
adapter = new FeedPagerAdapter(SlideActivity.this, feed);
viewPager.setAdapter(adapter);
adapter.notifyDataSetChanged();
viewPager.setCurrentItem(position);
viewPager.setOffscreenPageLimit(4);
And finally here's in my PagerAdapter code:
public class FeedPagerAdapter extends PagerAdapter {
#BindView(R.id.item_image_view) ImageView image;
private final SlideActivity host;
private List<Thing> items;
public FeedPagerAdapter(SlideActivity host, List<Thing> items){
this.host = host;
this.items = items;
notifyDataSetChanged();
}
#Override
public Object instantiateItem(ViewGroup parent, int position) {
View view = LayoutInflater.from(host).inflate(R.layout.item, parent, false);
ButterKnife.bind(this, view);
...
parent.addView(view);
return view;
}
#Override
public int getCount() {
return items.size();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
I've tried notifying the dataset in onResume and onPause and getItemCount in the adapter also, same problem.
Back to the main activity, this data is loaded over the network and adds items to the list when the load finishes. If I start my application and click on the item as soon they start to populate the RecyclerView, it opens the second activity and I get the crash. If I start the app and wait a second and click a RecyclerView item, it works as intended.
If anyone has any suggestions on how I can wait to make sure the list is stable when starting the second activity or a better way to implement a grid based RecyclerView gallery to open a viewpager type layout with the same dataset I would really appreciate it.
It's because you are using this line viewPager.setOffscreenPageLimit(4);, It's mean viewpager won't re-create the screen in all 4 pages. However, there is a function to detect if your screen has been visible completely, it's call setUserVisibleHint(). You just need to use like below:
//For the Fragment case
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
if (isVisibleToUser) {
//TODO notify your recyclerview data over here
}
}
EDIT:
For the Activity case: If targeting API level 14 or above, one can use
android.app.Application.ActivityLifecycleCallbacks
public class MyApplication extends Application implements ActivityLifecycleCallbacks {
private static boolean isInterestingActivityVisible;
#Override
public void onCreate() {
super.onCreate();
// Register to be notified of activity state changes
registerActivityLifecycleCallbacks(this);
....
}
public boolean isInterestingActivityVisible() {
return isInterestingActivityVisible;
}
#Override
public void onActivityResumed(Activity activity) {
if (activity instanceof MyInterestingActivity) {
isInterestingActivityVisible = true;
}
}
#Override
public void onActivityStopped(Activity activity) {
if (activity instanceof MyInterestingActivity) {
isInterestingActivityVisible = false;
}
}
// Other state change callback stubs
....
}
Register your application class in AndroidManifest.xml:
<application
android:name="your.app.package.MyApplication"
android:icon="#drawable/icon"
android:label="#string/app_name" >
Add onPause and onResume to every Activity in the project:
#Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
#Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
In your finish() method, you want to use isActivityVisible() to check if the activity is visible or not. There you can also check if the user has selected an option or not. Continue when both conditions are met.

RecyclerView onItemClickListener and View.onClickListener inside adapter

i'm developing a simple File Manager. This File Manager is inside a Fragment and, obviously, it use a RecyclerView to show folders and files. The layout that manage the row of list is composed by: 1 ImageView, 1 TextView (to show folder/file name) and one ImageView to select options (such as: rename, delete, etc.).
To manage click on RecyclerView i implemented a custom OnItemClickListener interface. The code is:
public class OnRecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
GestureDetector gestureDetector;
OnItemClickListener listener;
public interface OnItemClickListener {
public void onItemClick(View view, int position);
}
public OnRecyclerItemClickListener(Context context, OnItemClickListener listener) {
this.listener = listener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if(childView != null && listener != null && gestureDetector.onTouchEvent(e)) {
((OnItemClickListener) listener).onItemClick(childView, view.getChildAdapterPosition(childView));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
// TODO Auto-generated method stub
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean arg0) {
// TODO Auto-generated method stub
}
}
So, in the Fragment:
recyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(activity, new OnRecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
// TODO Auto-generated method stub
}
}));
All works fine but now i have a problem: to manage the options when the user click on ImageView don't work properly because the RecyclerView.OnItemClickListener win on imageView.setOnClickListener. When the user click on the ImageView a PopupMenu is showed BUT always disappear because occur simultaneously RecyclerView.OnItemClickListener AND ImageView.OnClickListener. The first win to the second. How can i solve this?
You should change your click functionality to being handled within the ViewHolder you could then choose where to place your click listener. Doing this will allow you to place a clickable view inside your layout with the clickable ImageView being over the top of that. Below is a short example of doing this. You will need to fill in the gaps, as well as setting up the Interface properly. Let me know if you need any more help.
public interface ViewHolderListener {
void itemClicked(int position);
void imageClicked(int position);
}
public void fillView(ViewHolderItem item) {
clickableView.setOnClickListener(new View.OnClickListener {
viewHolderListener.itemClicked(getAdapterPosition());
});
yourImage.setOnClickListener(new View.OnClickListener {
viewHolderListener.imageClicked(getAdapterPosition());
});
}
At First you need to understand that the whole view will be come under Recycler View (Imageview +textview ). Now If you want to have two separate events for both Recycler view and ImageView click the ideal way should be you write them off in Adapter class of the Recyler view rather than creating an interface.
In the adapter class you write the onclick listener of the recycler view itself. like:
view.setonClickListener(new View.OnClickListener(){
onClick){}
}
and for ImageView you write something like
imageview.setonClickListener(new View.OnClickListener(){
onClick){
// This code will not call --View.onClick of your Recycler View--}
}
It is the only way that both can operate separately.

How to get click, double tap, and long click gestures for a view inside a gridview?

I have ImageViews inside of a GridView, I had been using an OnItemClickListener along with an OnItemLongClickListener set on the GridView to open the image on a larger page and to delete the item respectively. Now, I have to implement rearranging of the ImageViews in the GridView, so I plan to move the deletion function to a double tap gesture, (please do not lecture me on android style guidelines (including the possibility of contextual actionbars, which I suggested), as this is what my boss asks for to emulate functions inside our ios app) in order to reserve long click for the drag and drop. I set an OnTouchListener on each view in the getView of my custom adapter, feeding a GestureDetecter with a listener extending SimpleOnGestureListener the given MotionEvent with onTouchEvent. I know what to do up to that point, but when I included (onDown of course, to get other callbacks) onDoubleTap, onSingleTapConfirmed, and onLongPressed all taps were interpreted as long clicks. And when I removed the both callback methods to be replaced with their listener counterparts once again (ie OnItemClickListeners) I received those two gestures but not the double tap, which makes sense, as double taps start out as a single tap unless you wait for a bit less than a second to confirm them as singles rather than potential doubles. I also tried placing the OnItemClickListener, but not the OnItemLongClickListener, with the callback in the extended SimpleOnGestureListener. In this case, only long presses were ever interpreted, but other gestures caused no response. Here is my code as it stands now, and do note that I returned false in the onTouchEvent in order to allow others (itemclicklisteners) to consume the events following the attempts made in the GestureDetector.
public class MainBoardGridAdapter extends GenericBoardGridAdapter implements OnItemLongClickListener {
private class Ges extends GestureDetector.SimpleOnGestureListener {
int pos;
public Ges(View v) {
pos = (Integer) v.getTag();
}
#Override
public boolean onDown(MotionEvent me) {
//this does get called but none of these methods below
return true;
}
#Override
public boolean onDoubleTap(MotionEvent me) {
new DeleteConfirmationPrompt(c, "board") {
#Override
protected boolean onDeleteConfirmed() {
// delete the visionboard
return deleteBoard(pos);
}
}; // Constructor shows dialog
return false;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
MainBoardGridAdapter.super.flagForUpdate(pos);
if (listener != null) {
listener.onBoardClick(pos, getName(pos));
} else {
Intent intent = new Intent(c, VisionBoardActivity.class);
intent.putExtra(VisionBoardActivity.EXTRA_VISION_BOARD_NAME, getName(pos));
frag.startActivityForResult(intent, MyBoardsFragment.REQUEST_EDIT);
}
return false;
}
}
#Override
public boolean onItemLongClick(AdapterView<?> parent, View v,
final int pos, long id) {
Toast.makeText(c, "Long", Toast.LENGTH_LONG).show();
return false;
}
// called by getView of extended adapter
#Override
public void onImageLoaded(ImageView iv, String data, View root) {
iv.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
(new GestureDetector(c, (new Ges(v)))).onTouchEvent(event);
return false;
}
});
}
}
And in the Activity, gv is my GridView:
gv.setOnItemLongClickListener(gridAdapter);
Also note that I had been using true in the return value in the GestureDetector methods, until trying the current configuration.There was no difference to be seen.
Thank you for your valuable time and help, I hope that someone will be able to point out what I am doing incorrectly.
-Jackson

Categories

Resources