miteshpithadiya / SearchableSpinner

Spinner with searchable items.
669 stars 238 forks source link

Spinner Crashed!!!! #48

Open anand2693 opened 7 years ago

anand2693 commented 7 years ago

When I click the SearchableSinner throw Exception

java.lang.IllegalStateException: Fragment already added: SearchableListDialog{ad4f6610 #0 TAG} at android.app.FragmentManagerImpl.addFragment(FragmentManager.java:1133) at android.app.BackStackRecord.run(BackStackRecord.java:618) at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1447) at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5017) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) at dalvik.system.NativeStart.main(Native Method)

usherjc commented 7 years ago

the same as you

anand2693 commented 7 years ago

I solved the issue! Just add this lines in your Activity class

@Override public void onAttachFragment(Fragment fragment) { if (fragment.isAdded()) return; super.onAttachFragment(fragment); }

usherjc commented 7 years ago

@anand2693 thank bro

usherjc commented 7 years ago

@anand2693 and...could u write the reason here to explain why the crash happend?

ashwindmk commented 7 years ago

Unfortunately, the above solution by anand2693 did not solve the crash for me. The crash occurs when I double click on the spinner before the dialog opened. So I had to create my own custom searchable spinner which extends searchable spinner:

CustomSearchableSpinner.java:

public class CustomSearchableSpinner extends SearchableSpinner {

     public static boolean isSpinnerDialogOpen = false;

     public CustomSearchableSpinner(Context context) {
         super(context);
     }

     public CustomSearchableSpinner(Context context, AttributeSet attrs) {
         super(context, attrs);
     }

     public CustomSearchableSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }

     @Override
     public boolean onTouch(View v, MotionEvent event) {
         if (event.getAction() == MotionEvent.ACTION_UP) {
             if (!isSpinnerDialogOpen) {
                 isSpinnerDialogOpen = true;
                 return super.onTouch(v, event);
             }
             isSpinnerDialogOpen = false;
         }
         new Handler().postDelayed(new Runnable() {
             @Override
             public void run() {
                 isSpinnerDialogOpen = false;
             }
         }, 500);
         return true;
     }
 }

Now in your activity inside OnItemSelectedListener set the isSpinnerDialogOpen variable to false.

myCustomSearchableSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            myCustomSearchableSpinner.isSpinnerDialogOpen = false;
            ...
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
            myCustomSearchableSpinner.isSpinnerDialogOpen = false;
            ...
        }
    });

You can see the working example of above code in my repository: Searchable Spinner

carloscte commented 7 years ago

@ashwindmk I really enjoyed your solution! By using it, I was able to make some adjustments in which the stability and the operation were further improved. Take a look...

CustomSearchableSpinner.java

public class CustomSearchableSpinner extends SearchableSpinner {
     private boolean isSpinnerDialogOpen = false;
     private Timer waitTimer;

     public CustomSearchableSpinner(Context context) {
         super(context);
     }

     public CustomSearchableSpinner(Context context, AttributeSet attrs) {
         super(context, attrs);
     }

     public CustomSearchableSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }

     public void setIsSpinnerDialogOpen(Boolean value){
         this.isSpinnerDialogOpen = value;
     }

     @Override
     public boolean onTouch(View v, MotionEvent event) {
         if (event.getAction() == MotionEvent.ACTION_UP) {
             if (!isSpinnerDialogOpen) {
                 setIsSpinnerDialogOpen(true);
                 return super.onTouch(v, event);
             }
         }

         if(waitTimer != null){
             waitTimer.cancel();
         } 

         temporizador();
         return true;
     }

     private void temporizador(){
         waitTimer = new Timer();
         waitTimer.schedule(new TimerTask() {
             @Override
             public void run() {
                 setIsSpinnerDialogOpen(false);
             }
         }, 200);
     }
 }

inside OnItemSelectedListener...

myCustomSearchableSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            myCustomSearchableSpinner.isSpinnerDialogOpen = false;
            ...
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
            myCustomSearchableSpinner.isSpinnerDialogOpen = false;
            ...
        }
    });
ZeeOne commented 6 years ago

Thanks guys! This is great! I modified it a bit so that you do not have to set the isSpinnerDialogOpen=false every time (I hope it works well, I'm still a bit of a noob with Android):

`

public class TestSpinner extends SearchableSpinner {

public static boolean isSpinnerDialogOpen = false;

public TestSpinner(Context context) {
    super(context);
}

public TestSpinner(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public TestSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

private static final long MIN_DELAY_MS = 500;

private long mLastClickTime;

@Override
public boolean onTouch(View v, MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_UP) {

        long lastClickTime = mLastClickTime;
        long now = System.currentTimeMillis();
        mLastClickTime = now;
        if (now - lastClickTime < MIN_DELAY_MS) {
            // Too fast: ignore
           return true;
        } else {
            // Register the click
            return super.onTouch(v, event);
        }
    }
    return true;
}

}`

MindNotion commented 6 years ago

On multiple times click on spinner the app crash. In SearchableSpinner.java line number 91 change _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), "TAG"); code to

if(!_searchableListDialog.isAdded()) { _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), "TAG"); }

the issue will resolved

Johnett commented 6 years ago

@MindNotion You saved my day. I tried all the other solutions but if we repeatedly click on the spinner app will crash eventually. But after adding this fix it never crashed. Thanks again. Note to developers:- You should add this fix to the library ASAP.

padmajarani commented 6 years ago

@MindNotion how to add to that code to the library .. it is locked

sousasj commented 5 years ago

@MindNotion First you need to extract the searchablespinnerlibrary-1.3.1-sources.jar file, edit the SearchableSpinner.java file at the line 91 and replace it with this code if (!_searchableListDialog.isAdded()) { _searchableListDialog.dismiss(); _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), "TAG"); } else { _searchableListDialog.dismiss(); _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), "TAG"); }

mohdqasim commented 2 years ago

@MindNotion why you not raise PR for this fix? as this fixed the bug

CodingByDay commented 1 year ago

Here is my solution for our Xamarin.Android application in C#.

using Android.Content; using Android.OS; using Android.Util; using Android.Views; using Com.Toptoche.Searchablespinnerlibrary; using Java.Lang; using Java.Util.Jar; using System; using System.Threading.Tasks; using System.Timers;

public class CustomSearchableSpinner : SearchableSpinner {

public static bool isSpinnerDialogOpen = false;

private Timer timer;

public CustomSearchableSpinner(Context context) : base(context)
{
}
public CustomSearchableSpinner(Context context, IAttributeSet attrs, int defStyleAttr): base(context, attrs, defStyleAttr)
{

}
public CustomSearchableSpinner(Context context, IAttributeSet attrs): base (context, attrs) 
{

}

public override bool OnTouch(View v, MotionEvent motion)
{
    if(isSpinnerDialogOpen) { return true; } 
    else if(motion.Action == MotionEventActions.Up)
    {
        if(!isSpinnerDialogOpen)
        {
            isSpinnerDialogOpen = true;
            PostDelayed(DefaultDialogValue, 500);
            return base.OnTouch(v, motion);
        }
    }
    return true;
}

Action DefaultDialogValue = new Action(() => {

    isSpinnerDialogOpen = false;

});

}

habibTeck commented 3 months ago

use this class and every thing will work fine for you

public class CustomSearchableSpinner extends Spinner implements View.OnTouchListener, SearchableListDialog.SearchableItem {

public static final int NO_ITEM_SELECTED = -1;
private Context _context;
private List _items;
private SearchableListDialog _searchableListDialog;

private boolean _isDirty;
private ArrayAdapter _arrayAdapter;
private String _strHintText;
private boolean _isFromInit;

public CustomSearchableSpinner(Context context) {
    super(context);
    this._context = context;
    init();
}

public CustomSearchableSpinner(Context context, AttributeSet attrs) {
    super(context, attrs);
    this._context = context;
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchableSpinner);
    final int N = a.getIndexCount();
    for (int i = 0; i < N; ++i) {
        int attr = a.getIndex(i);
        if (attr == R.styleable.SearchableSpinner_hintText) {
            _strHintText = a.getString(attr);
        }
    }
    a.recycle();
    init();
}

public CustomSearchableSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this._context = context;
    init();
}

private void init() {
    _items = new ArrayList();
    _searchableListDialog = SearchableListDialog.newInstance
            (_items);
    _searchableListDialog.setOnSearchableItemClickListener(this);
    setOnTouchListener(this);

    _arrayAdapter = (ArrayAdapter) getAdapter();
    if (!TextUtils.isEmpty(_strHintText)) {
        ArrayAdapter arrayAdapter = new ArrayAdapter(_context, android.R.layout
                .simple_list_item_1, new String[]{_strHintText});
        _isFromInit = true;
        setAdapter(arrayAdapter);
    }
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_UP) {

        if (null != _arrayAdapter) {

            // Refresh content #6
            // Change Start
            // Description: The items were only set initially, not reloading the data in the
            // spinner every time it is loaded with items in the adapter.
            _items.clear();
            for (int i = 0; i < _arrayAdapter.getCount(); i++) {
                _items.add(_arrayAdapter.getItem(i));
            }
            // Change end.

            if(!_searchableListDialog.isAdded()) {
                _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), "TAG");
            }
        }
    }
    return true;
}

@Override
public void setAdapter(SpinnerAdapter adapter) {

    if (!_isFromInit) {
        _arrayAdapter = (ArrayAdapter) adapter;
        if (!TextUtils.isEmpty(_strHintText) && !_isDirty) {
            ArrayAdapter arrayAdapter = new ArrayAdapter(_context, android.R.layout
                    .simple_list_item_1, new String[]{_strHintText});
            super.setAdapter(arrayAdapter);
        } else {
            super.setAdapter(adapter);
        }

    } else {
        _isFromInit = false;
        super.setAdapter(adapter);
    }
}

@Override
public void onSearchableItemClicked(Object item, int position) {
    setSelection(_items.indexOf(item));

    if (!_isDirty) {
        _isDirty = true;
        setAdapter(_arrayAdapter);
        setSelection(_items.indexOf(item));
    }
}

public void setTitle(String strTitle) {
    _searchableListDialog.setTitle(strTitle);
}

public void setPositiveButton(String strPositiveButtonText) {
    _searchableListDialog.setPositiveButton(strPositiveButtonText);
}

public void setPositiveButton(String strPositiveButtonText, DialogInterface.OnClickListener onClickListener) {
    _searchableListDialog.setPositiveButton(strPositiveButtonText, onClickListener);
}

public void setOnSearchTextChangedListener(SearchableListDialog.OnSearchTextChanged onSearchTextChanged) {
    _searchableListDialog.setOnSearchTextChangedListener(onSearchTextChanged);
}

private Activity scanForActivity(Context cont) {
    if (cont == null)
        return null;
    else if (cont instanceof Activity)
        return (Activity) cont;
    else if (cont instanceof ContextWrapper)
        return scanForActivity(((ContextWrapper) cont).getBaseContext());

    return null;
}

@Override
public int getSelectedItemPosition() {
    if (!TextUtils.isEmpty(_strHintText) && !_isDirty) {
        return NO_ITEM_SELECTED;
    } else {
        return super.getSelectedItemPosition();
    }
}

@Override
public Object getSelectedItem() {
    if (!TextUtils.isEmpty(_strHintText) && !_isDirty) {
        return null;
    } else {
        return super.getSelectedItem();
    }
}

}