Open anand2693 opened 7 years ago
the same as you
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); }
@anand2693 thank bro
@anand2693 and...could u write the reason here to explain why the crash happend?
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
@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;
...
}
});
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;
}
}`
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
@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.
@MindNotion how to add to that code to the library .. it is locked
@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"); }
@MindNotion why you not raise PR for this fix? as this fixed the bug
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;
});
}
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();
}
}
}
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)