I'm new to working with floating action button and trying to get a few of the basic things working today. Currently I am stuck on getting the onClick functionality to work. I pulled most of the code from googles FAB basic example, and in there it has an onChecked method which sends a string to a logger to show you have clicked it.
#Override
public void onCheckedChanged(FloatingActionButton fabView, boolean isChecked) {
// When a FAB is toggled, log the action.
switch (fabView.getId()){
case R.id.fab_1:
break;
default:
break;
}
}
I was thinking I'd be able to replace the functionality in there but that had no affect. So I tried to create the onClickListener like you would with any other button but that also had no affect. I am not sure how to continue since neither option worked. my goal is just to produce a dialog when the floating action button is clicked, but for now I am just trying to use a placeholder alert dialog.
This is the FloatingActionButtonFragment class:
public class FloatingActionButtonFragment extends Fragment implements FloatingActionButton.OnCheckedChangeListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fab_layout, container, false);
// Make this {#link Fragment} listen for changes in both FABs.
FloatingActionButton fab1 = (FloatingActionButton) rootView.findViewById(R.id.fab_1);
fab1.setOnCheckedChangeListener(this);
fab1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Are you sure?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// Create the AlertDialog object and return it
AlertDialog dialog = builder.create();
dialog.show();
}
});
return rootView;
}
#Override
public void onCheckedChanged(FloatingActionButton fabView, boolean isChecked) {
// When a FAB is toggled, log the action.
switch (fabView.getId()){
case R.id.fab_1:
break;
default:
break;
}
}
}
And here is the FloatingActionButton class:
public class FloatingActionButton extends FrameLayout implements Checkable {
/**
* Interface definition for a callback to be invoked when the checked state
* of a compound button changes.
*/
public static interface OnCheckedChangeListener {
/**
* Called when the checked state of a FAB has changed.
*
* #param fabView The FAB view whose state has changed.
* #param isChecked The new checked state of buttonView.
*/
void onCheckedChanged(FloatingActionButton fabView, boolean isChecked);
}
/**
* An array of states.
*/
private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_checked
};
private static final String TAG = "FloatingActionButton";
// A boolean that tells if the FAB is checked or not.
private boolean mChecked;
// A listener to communicate that the FAB has changed it's state
private OnCheckedChangeListener mOnCheckedChangeListener;
public FloatingActionButton(Context context) {
this(context, null, 0, 0);
}
public FloatingActionButton(Context context, AttributeSet attrs) {
this(context, attrs, 0, 0);
}
public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr);
setClickable(true);
// Set the outline provider for this view. The provider is given the outline which it can
// then modify as needed. In this case we set the outline to be an oval fitting the height
// and width.
setOutlineProvider(new ViewOutlineProvider() {
#Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, getWidth(), getHeight());
}
});
// Finally, enable clipping to the outline, using the provider we set above
setClipToOutline(true);
}
/**
* Sets the checked/unchecked state of the FAB.
* #param checked
*/
public void setChecked(boolean checked) {
// If trying to set the current state, ignore.
if (checked == mChecked) {
return;
}
mChecked = checked;
// Now refresh the drawable state (so the icon changes)
refreshDrawableState();
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, checked);
}
}
/**
* Register a callback to be invoked when the checked state of this button
* changes.
*
* #param listener the callback to call on checked state change
*/
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void toggle() {
setChecked(!mChecked);
}
/**
* Override performClick() so that we can toggle the checked state when the view is clicked
*/
#Override
public boolean performClick() {
toggle();
return super.performClick();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// As we have changed size, we should invalidate the outline so that is the the
// correct size
invalidateOutline();
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
}
There isn't much to either class at this point, they are mostly husks, but I just want to get this basic functionality down before I continue, and being the noob I am, I don't know why this wouldn't work.
If you are not already heading for deadline, you must change the floating action button to the one provided by google in design library
just follow http://android-developers.blogspot.in/2015/05/android-design-support-library.html
Add to the XML Layout:
<android.support.design.widget.FloatingActionButton
android:id="#+id/myFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/your_icon"
app:elevation="4dp"
... />
Add to the code behind:
FloatingActionButton myFab = (FloatingActionButton) myView.findViewById(R.id.myFAB);
myFab.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
doMyThing();
}
});
For more details follow : FloatingActionButton example with Support Library
Actually now with android support library it was very easy to add FAB and to customize it with click listeners
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// FAB Action goes here
}
});
Reference : http://androidgifts.com/android-material-design-floating-action-button-tutorial/
To use dialog/Alertdialog with the floating action button you're using, try changing your onClick(View v) from this
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
to
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
Related
I am pretty curious about the behavior of the BottomSheetDialog when it is dismissed : when the user draggs it down to hide it, it will remain hidden, even if bottomSheetDialog#show() is called after. This only happens when it is dragged down, not when the user touches outside or when bottomSheetDialog#dismiss() is called programatically.
It is really annoying because I have a pretty big bottomSheetDialog with a recyclerview inside, and I have to create a new one every time I want to show the bottomSheetDialog.
So instead of just doing this :
if(bottomSheetDialog != null){
bottomSheetDialog.show();
else{
createNewBottomSheetDialog();
}
I have to create one every time.
Am I missing something or is it the normal behavior ? (Btw I use appcompat-v7:23.2.1)
So I finally managed to solve this problem by looking directly into the BottomSheetDialog implementation, and I discovered it was nothing but a simple Dialog wrapped into a regular BottomSheet.
The problem was in the BottomSheetCallBack :
#Override
public void onStateChanged(#NonNull View bottomSheet,
#BottomSheetBehavior.State int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
}
}
The problem occurs when the state HIDDEN is reached which happens when the dialog is dismissed by being dragged down. After that the dialog stays hidden even if bottomSheetDialog.show() is called. The most simple fix I found was to remove this state and replace it by the COLLAPSED state.
I create a classCustomBottomSheetDialog, copied the entire BottomSheetDialog class and added a single line to fix the problem :
#Override
public void onStateChanged(#NonNull View bottomSheet,
#BottomSheetBehavior.State int newState) {
if (newState == CustomBottomSheetBehavior.STATE_HIDDEN) {
dismiss();
bottomSheetBehavior.setState(CustomBottomSheetBehavior.STATE_COLLAPSED);
}
}
Here is the final code:
public class CustomBottomSheetDialog extends AppCompatDialog {
public CustomBottomSheetDialog (#NonNull Context context) {
this(context, 0);
}
public CustomBottomSheetDialog (#NonNull Context context, #StyleRes int theme) {
super(context, getThemeResId(context, theme));
// We hide the title bar for any style configuration. Otherwise, there will be a gap
// above the bottom sheet when it is expanded.
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
}
protected CustomBottomSheetDialog (#NonNull Context context, boolean cancelable,
OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
}
#Override
public void setContentView(#LayoutRes int layoutResId) {
super.setContentView(wrapInBottomSheet(layoutResId, null, null));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setLayout(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
#Override
public void setContentView(View view) {
super.setContentView(wrapInBottomSheet(0, view, null));
}
#Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
super.setContentView(wrapInBottomSheet(0, view, params));
}
private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
final CoordinatorLayout coordinator = (CoordinatorLayout) View.inflate(getContext(),
R.layout.design_bottom_sheet_dialog, null);
if (layoutResId != 0 && view == null) {
view = getLayoutInflater().inflate(layoutResId, coordinator, false);
}
FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setBottomSheetCallback(mBottomSheetCallback);
if (params == null) {
bottomSheet.addView(view);
} else {
bottomSheet.addView(view, params);
}
// We treat the CoordinatorLayout as outside the dialog though it is technically inside
if (shouldWindowCloseOnTouchOutside()) {
coordinator.findViewById(R.id.touch_outside).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isShowing()) {
cancel();
}
}
});
}
return coordinator;
}
private boolean shouldWindowCloseOnTouchOutside() {
if (Build.VERSION.SDK_INT < 11) {
return true;
}
TypedValue value = new TypedValue();
//noinspection SimplifiableIfStatement
if (getContext().getTheme()
.resolveAttribute(android.R.attr.windowCloseOnTouchOutside, value, true)) {
return value.data != 0;
}
return false;
}
private static int getThemeResId(Context context, int themeId) {
if (themeId == 0) {
// If the provided theme is 0, then retrieve the dialogTheme from our theme
TypedValue outValue = new TypedValue();
if (context.getTheme().resolveAttribute(
R.attr.bottomSheetDialogTheme, outValue, true)) {
themeId = outValue.resourceId;
} else {
// bottomSheetDialogTheme is not provided; we default to our light theme
themeId = R.style.Theme_Design_Light_BottomSheetDialog;
}
}
return themeId;
}
private BottomSheetBehavior.BottomSheetCallback mBottomSheetCallback
= new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet,
#BottomSheetBehavior.State int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
bottomSheetBehavior.setState(CustomBottomSheetBehavior.STATE_COLLAPSED);
}
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
}
};
}
Update: The problem has been resolved at some version of the support library. I don't really know the exact version that fixes it but in 27.0.2 it is fixed.
Note: The URL does no longer refer to the issue described due to some modification on the URL schema by Google.
A workaround better than copying the whole class just to add a single line
// Fix BottomSheetDialog not showing after getting hidden when the user drags it down
myBottomSheetDialog.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(DialogInterface dialogInterface) {
BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface;
FrameLayout bottomSheet = (FrameLayout) bottomSheetDialog
.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_COLLAPSED);
}
});
see: https://code.google.com/p/android/issues/detail?id=202396#c7
I have sample demo I hope that is useful.
public class BottomListMenu extends BottomSheetDialog {
private List<MenuDTO> menuList;
private OnMenuItemTapped menuTapListener;
public BottomListMenu(#NonNull Context context, List<MenuDTO> menuList, OnMenuItemTapped menuTapListener) {
super(context);
this.menuList = menuList;
this.menuTapListener = menuTapListener;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_menu_list);
RecyclerView rcvList = (RecyclerView) findViewById(R.id.rcv_menu_list);
rcvList.setLayoutManager(new LinearLayoutManager(getContext()));
BottomSheetMenuListAdapter adapter = new BottomSheetMenuListAdapter(getContext(), this, menuList, menuTapListener);
rcvList.setAdapter(adapter);
}
}
--- Use ---
BottomListMenu menu = new BottomListMenu(MainActivity.this, MenuUtils.getListMenu(MainActivity.this), new OnMenuItemTapped() {
#Override
public void onClickMenuItem(MenuDTO menu) {
if (menu.getMenuTitle().equals(getString(R.string.menu_edit))) {
Toast.makeText(MainActivity.this, "Edit Clicked", Toast.LENGTH_SHORT).show();
} else if (menu.getMenuTitle().equals(getString(R.string.menu_delete))) {
Toast.makeText(MainActivity.this, "Delete Clicked", Toast.LENGTH_SHORT).show();
} else if (menu.getMenuTitle().equals(getString(R.string.menu_attach))) {
Toast.makeText(MainActivity.this, "Attach Clicked", Toast.LENGTH_SHORT).show();
}
}
});
menu.show();
-- Full Sample Code Available Here --
https://github.com/bita147/BottomSheetDialog
Even though I'm setting the setOnItemClickListener on the AutoCompleteTextView and performing some custom operations in it, once that method is done, the list dismisses and prints out the object.toString in the editbox.
I want to prevent dismissal of the dropdown on item select and would also like it to not replace the edit box. How can I achieve this ?
I also want to implement the same i used below code to implement it.
Create a custom class and extend AutoCompleteTextView.
Override dismissDropDown() method and remove the super call from it.
Will work for you.
public class CustomAutoComplete extends AutoCompleteTextView {
public NoSelectionAutoComplete(Context context) {
super(context);
}
public NoSelectionAutoComplete(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoSelectionAutoComplete(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void replaceText(CharSequence text) {
}
#Override
public void dismissDropDown() {
}
}
I added an onClickListener to the entire custom row layout that I was using for the dropdown adapter. This way whenever the row is clicked, my row onClickListener is invoked and the default one for the dropdown is not.
First Question - Prevent dropdown dismissal:
Solved below.
Second Question - Prevent text replacement: (For others interested)
You can extend AutoCompleteTextView and override
protected void replaceText(CharSequence text) {}
to do nothing.
As others mentioned, overriding performCompletion() won't help here.
well at least it seems like they are planning to add this in near future.
/**
* Sets whether the drop-down should remain visible as long as there is there is
* {#link #enoughToFilter()}. This is useful if an unknown number of results are expected
* to show up in the adapter sometime in the future.
*
* The drop-down will occupy the entire screen below {#link #getDropDownAnchor} regardless
* of the size or content of the list. {#link #getDropDownBackground()} will fill any space
* that is not used by the list.
*
* #param dropDownAlwaysVisible Whether to keep the drop-down visible.
*
* #hide Pending API council approval
*/
public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
mPopup.setDropDownAlwaysVisible(dropDownAlwaysVisible);
}
edit,new answer:
this worked for me but it closes for a sec,and opens again.
class task extends TimerTask {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
autoComplete.showDropDown();
}
});
}
};
autoComplete.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
new Timer().schedule(new task(),0, 10);
}
});
Overriding replaceText without calling super works fine (prevents entering suggested text into AutoCompleteTextView), but overriding dismissDropDown causes not-dismissing dropdown not only when item clicked, but also when onBackPressed, touched outside dialog etc...
I've ended with NOT using setOnItemClickListener method from AutoCompleteTextView at all. I'm creating custom onClick in my custom ArrayAdapter and set it for all Views returned by getView method
View.OnClickListener onClick=null;
public void setOnItemClickListener(View.OnClickListener onClick) {
this.onClick=onClick;
/*this.onClick=new View.OnClickListener(){
#Override
public void onClick(View v) {
if(v.getTag()==null)
return;
Integer position = (Integer) v.getTag();
Toast.makeText(v.getContext(), "position: "+postion, Toast.LENGTH_SHORT).show();
}
});*/
}
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
... call super/inflate convertView and do your stuff here
setCustomOnClick(convertView, position);
return convertView;
}
private void setCustomOnClick(final View view, final int position){
view.setTag(position);
view.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
if(onClick==null)
return;
// inside called onClick method v.getTag() will return pressed position
onClick.onClick(v);
}
});
}
in fact setting onClick for whole view will cover "original" always-dismissing and always-replacing-text onClick (not called at all then). Remember about adding custom graphic representation, when pressed (ripple/selector are shown when "original" onClick called only)
I've needed also always-visible functionality, because my autocomplete must always show first position (functional), even when there is no suggestions (if present then shown below on positions 1+)
public class AlwaysVisibleAutoCompleteTextView extends AppCompatAutoCompleteTextView {
private boolean showAlways=true;
public AlwaysVisibleAutoCompleteTextView(Context context) {
super(context);
}
public AlwaysVisibleAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AlwaysVisibleAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setShowAlways(boolean showAlways) {
this.showAlways = showAlways;
}
#Override
public boolean enoughToFilter() {
return showAlways || super.enoughToFilter();
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
showDropDownIfFocused();
}
private void showDropDownIfFocused() {
if (enoughToFilter() && isFocused() && getWindowVisibility() == View.VISIBLE)
showDropDown();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
showDropDownIfFocused();
}
}
when AlwaysVisibleAutoCompleteTextView is focused, but dropdown dismissed and user press view again, then dropdown is not showing, because focus state not changing (onFocusChanged not called), so
autoComplete.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP)
autoComplete.showDropDown();
return false;
}
});
If you aren't planning using setOnTouchListener for your AutoCompleteTextView for any other purpose, then OnTouchListener may be set inside AlwaysVisibleAutoCompleteTextView class (in every constructor)
I want to implement search filter in my android application. I have gone through the examples on how to integrate search filter and able to understand how to integrate it in application. But my requirement is to provide scoped search based a filter while doing search. I have tried to search similar implementations but was not able to find any examples. Please check section Scoped Search in this UI Pattern collection, especially Dropbox example for iphone.
As mentioned before I was unable to find similar example in android but by looking at Dictionary.com 's application (snapshot shown below) I came to know that its possible in android also (of course by adding some more efforts in case its not possible with Search Widget itself). Can any one please provide any directions how I can implement similar scoped search in my application ?
Thanks for spending time on this.
I would do the following:
first i create a searchType layout for the alertdialog with the choose: (images, video, etc..)
then i create the activity for the search and implement the widget(like on android guide).
in the activity create a variable:
private String searchType = "";
then
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.YOUR_MENU, menu);
MenuItem menuItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) MenuItemCompat
.getActionView(menuItem);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
//HERE INSERT THE CODE ABOUT THE ALERT DIALOG FOR THE CHOOSE
//THEN INSERT THE aALERT DIALOG RESPONSE INTO THE searchType VARIABLE
}
}
I have created a custom search widget that does not make use of the built in search functionality. Its simple to implement and can provide information to the current activity.
It also uses an Autocomplete textview so you can use Autocomplete, you could alternatively just replace this with a normal EditText.
public class CustomViewSearch extends View {
private CustomAutoCompleteView searchEditText;
private boolean viewShown = false;
private ActionBar actionBar;
private InputMethodManager inputMethodManager;
private OnEditorActionSearchListener onEditorActionSearchListener;
private List<String> dataItems;
private ArrayAdapter<String> arrayAdapter;
public interface OnEditorActionSearchListener {
void onEditorActionSearch(String searchText);
void onTextChangedListener(String text);
List<String> getNewItemsForSuggestions(String text);
}
public void setOnEditorActionSearchListener(OnEditorActionSearchListener l) {
onEditorActionSearchListener = l;
}
// IMPORTANT: Provide your activity as the context
public CustomViewSearch(final Context context, List<String> items) {
super(context);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View searchView = inflater.inflate(R.layout.custom_view_search_widget, null);
actionBar = ((ActionBarActivity) context).getSupportActionBar();
inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setCustomView(searchView);
actionBar.setDisplayShowCustomEnabled(true);
dataItems = items;
searchEditText = (CustomAutoCompleteView) searchView.findViewById(R.id.edit_text_search);
searchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
onEditorActionSearchListener.onEditorActionSearch(searchEditText.getText().toString());
return true;
}
return false;
}
});
searchEditText.requestFocus();
searchEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
onEditorActionSearchListener.onTextChangedListener(s.toString());
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
dataItems = onEditorActionSearchListener.getNewItemsForSuggestions(s.toString());
// update the adapater
arrayAdapter = new ArrayAdapter<String>(context, android.R.layout.simple_dropdown_item_1line, dataItems);
arrayAdapter.notifyDataSetChanged();
searchEditText.setAdapter(arrayAdapter);
}
});
ImageButton closeImageButton = (ImageButton) searchView.findViewById(R.id.image_button_search_close);
closeImageButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (searchEditText.getText().length() > 0) {
searchEditText.setText("");
} else {
hideKeyboard();
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setDisplayShowCustomEnabled(false);
viewShown = false;
}
}
});
viewShown = true;
showKeyboard();
}
public String actionClick() {
if (!viewShown) {
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
searchEditText.requestFocus();
showKeyboard();
viewShown = true;
return null;
} else {
return getSearchText();
}
}
public void showKeyboard() {
inputMethodManager.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT);
}
public void hideKeyboard() {
inputMethodManager.hideSoftInputFromWindow(searchEditText.getWindowToken(), 0);
}
public String getSearchText() {
return searchEditText.getText().toString();
} }
The corresponding xml layout for the search :
<?xml version="1.0" encoding="utf-8"?>
<za.co.android.CustomAutoCompleteView android:id="#+id/edit_text_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/empty_layout"
android:paddingBottom="8dp"
android:textColor="#android:color/white"
android:singleLine="true"
android:imeOptions="actionSearch"
android:textCursorDrawable="#null"
android:paddingRight="36dp"
android:layout_alignParentLeft="true"
android:inputType="text"
android:hint="#string/search"
android:textColorHint="#color/palette_primary_light_grey"/>
<ImageButton
android:id="#+id/image_button_search_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:drawable/ic_menu_close_clear_cancel"
android:layout_toLeftOf="#+id/empty_layout"
android:layout_centerVertical="true"
android:layout_marginRight="4dp"/>
<FrameLayout
android:id="#+id/empty_layout"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_alignParentRight="true">
</FrameLayout>
The custom autocomplete widget code:
public class CustomAutoCompleteView extends AutoCompleteTextView {
public CustomAutoCompleteView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public CustomAutoCompleteView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public CustomAutoCompleteView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
// this is how to disable AutoCompleteTextView filter
#Override
protected void performFiltering(final CharSequence text, final int keyCode) {
String filterText = "";
super.performFiltering(filterText, keyCode);
}
/*
* after a selection we have to capture the new value and append to the existing text
*/
#Override
protected void replaceText(final CharSequence text) {
super.replaceText(text);
}}
Usage of the CustomViewSearch widget:
Add a search icon to the menu.xml for the Activity. Then in the onOptionsItemSelected method - when the menu item is clicked, call the below function:
public void triggerSearch() {
if (customViewSearch == null) {
customViewSearch = new CustomViewSearch(this, null);
customViewSearch.setOnEditorActionSearchListener(new CustomViewSearch.OnEditorActionSearchListener() {
#Override
public void onEditorActionSearch(String searchText) {
// DO SOME STUFF
((AppItemListFragment_) getSupportFragmentManager()
.findFragmentById(R.id.appitem_list)).searchTriggered(searchText);
}
#Override
public void onTextChangedListener(String text) {
((AppItemListFragment_) getSupportFragmentManager()
.findFragmentById(R.id.appitem_list)).searchTriggered(text);
}
#Override
public List<String> getNewItemsForSuggestions(String text) {
return getNewAutoCompleteStrings(text);
}
});
} else {
String searchText = customViewSearch.actionClick();
if (searchText != null) {
// DO SOME STUFF
((AppItemListFragment_) getSupportFragmentManager()
.findFragmentById(R.id.appitem_list)).searchTriggered(searchText);
}
}}
I hope this helps - for me it was much easier to implement a custom search than use the built in one and provides a scoped search for any screen you are on.
How to set null validation in edittextpreference dialog so that if it is null, the user should not be able to click ok and some message should be displayed in the dialog itself. I have been trying to find it for a long time but no success....
edttxtpref = (EditTextPreference) getPreferenceScreen().findPreference(
"edttxtkey");
edttxtpref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(
android.preference.Preference preference, Object newValue) {
if (newValue.toString().trim().equals("")) {
Toast.makeText(getActivity(), "Username can not be empty",
Toast.LENGTH_LONG).show();
return false;
}
return true;
}
});
This way the validation is done and if we want to display the message in dialog itself then a custom dialog has to be created as already told by Phil.
I think what you are looking for is this. You are using the predefined PreferenceDialog (with EditText) and want to check if the Text is null. According to my knowledge, the "text" in this case is the changedPreference, therefore you can go with this:
Simply use an onPreferenceChangedListener for that.
yourPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object changedValue) {
if(changedValue == null) {
// handle it being null
return false;
} else {
return true;
}
}
});
For more advanced requirements, I would recommend that you implement your own Dialog and inside it, do whatever you desire. You can make that happen by defining a Preference list entry in .xml and then spawn the Dialog upon clicking on it.
Preference yourCustomPref = (Preference) findPreference("yourPref");
yourCustomPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
// spawn your dialog here
return true;
}
});
This is how you could implement your custom EditText Dialog:
public Builder buildDialog(final Context c) {
AlertDialog.Builder builder = new AlertDialog.Builder(c);
builder.setTitle("EditText Dialog");
builder.setMessage("Enter text:");
LinearLayout llV = new LinearLayout(c);
llV.setOrientation(1); // 1 = vertical
final EditText patName = new EditText(c);
patName.setHint("Enter text...");
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, 1f);
lp.bottomMargin = 20;
lp.rightMargin = 30;
lp.leftMargin = 15;
patName.setLayoutParams(lp);
llV.addView(patName);
builder.setView(llV);
builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if(patName.getText().toString().length() > 0) {
} else {
}
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder;
}
And then call it like this:
buildDialog(yourcontext).show();
When edittext is null then ok button will be disabled and as soon as the text is entered it will be enabled::
public class CustomEditTextPreference extends EditTextPreference implements
OnClickListener {
public CustomEditTextPreference(Context ctx, AttributeSet attrs, int defStyle)
{
super(ctx, attrs, defStyle);
}
public CustomEditTextPreference(Context ctx, AttributeSet attrs)
{
super(ctx, attrs);
}
private class EditTextWatcher implements TextWatcher
{
#Override
public void onTextChanged(CharSequence s, int start, int before, int count){}
#Override
public void beforeTextChanged(CharSequence s, int start, int before, int count){}
#Override
public void afterTextChanged(Editable s)
{
onEditTextChanged();
}
}
EditTextWatcher m_watcher = new EditTextWatcher();
/**
* Return true in order to enable positive button or false to disable it.
*/
protected boolean onCheckValue(String value)
{
if (value.trim().equals(""))
{
return false;
}
return true;
}
protected void onEditTextChanged()
{
boolean enable = onCheckValue(getEditText().getText().toString());
Dialog dlg = getDialog();
if(dlg instanceof AlertDialog)
{
AlertDialog alertDlg = (AlertDialog)dlg;
Button btn = alertDlg.getButton(AlertDialog.BUTTON_POSITIVE);
btn.setEnabled(enable);
}
}
#Override
protected void showDialog(Bundle state)
{
super.showDialog(state);
getEditText().removeTextChangedListener(m_watcher);
getEditText().addTextChangedListener(m_watcher);
onEditTextChanged();
}
}
MainActivity
public class MainActivity extends Activity {
// Declare our Views, so we can access them later
private CheckUsernameEditText etUsername;
private EditText etPassword;
private EditText etPassword2;
private Button btnRegister;
private Button btnCancel;
private TextView lblUserStatus;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set Activity Layout
setContentView(R.layout.activity_main);
// Get the EditText and Button References
etUsername = (CheckUsernameEditText) findViewById(R.id.username);
etPassword = (EditText) findViewById(R.id.password);
etPassword2 = (EditText) findViewById(R.id.password2);
btnRegister = (Button) findViewById(R.id.register_button);
btnCancel = (Button) findViewById(R.id.cancel_button);
lblUserStatus = (TextView) findViewById(R.id.userstatus);
// Set our new Listener to the Username EditText view
etUsername.setOnUsernameAvailableListener(new OnUsernameAvailableListener() {
#Override
public void onAvailableChecked(String username,
boolean available) {
// Handle the event here
if (!available) {
etUsername.setTextColor(Color.RED);
lblUserStatus
.setText(username
+ " is already taken. Please choose another login name.");
} else {
etUsername.setTextColor(Color.GREEN);
lblUserStatus.setText(username + " is available.");
}
}
});
// Set Click Listener
btnRegister.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// create Account
}
});
btnCancel.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Close the application
finish();
}
});
}
The corresponding XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
*
*
<EditText
android:id="#+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
*
*
</LinearLayout>
CheckUsernameEditText
public class CheckUsernameEditText extends EditText implements OnKeyListener {
OnUsernameAvailableListener onUsernameAvailableListener = null;
final private static String[] registeredUsers = new String[] {
// This is just a fixed List for tutorial purposes
// in a real application you'd check this server sided or inside the
// database
"tseng", "admin", "root", "joedoe", "john" };
public CheckUsernameEditText(Context context) {
super(context);
// Set KeyListener to ourself
this.setOnKeyListener(this);
}
public CheckUsernameEditText(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
// Set KeyListener to ourself
this.setOnKeyListener(this);
}
public CheckUsernameEditText(Context context, AttributeSet attrs) {
super(context, attrs);
// Set KeyListener to ourself
this.setOnKeyListener(this);
}
// Allows the user to set an Listener and react to the event
public void setOnUsernameAvailableListener(
OnUsernameAvailableListener listener) {
onUsernameAvailableListener = listener;
}
// This function is called after the check was complete
private void OnUserChecked(String username, boolean available) {
// Check if the Listener was set, otherwise we'll get an Exception when
// we try to call it
if (onUsernameAvailableListener != null) {
// Only trigger the event, when we have a username
if (!TextUtils.isEmpty(username)) {
onUsernameAvailableListener.onAvailableChecked(username,
available);
}
}
}
#Override
public boolean onKey(View v, int keycode, KeyEvent keyevent) {
// We only want to handle ACTION_UP events, when user releases a key
if (keyevent.getAction() == KeyEvent.ACTION_DOWN)
return false;
boolean available = true;
// Whenever a user press a key, check if the username is available
String username = getText().toString().toLowerCase();
if (!TextUtils.isEmpty(username)) {
// Only perform check, if we have anything inside the EditText box
for (int i = 0; i < registeredUsers.length; i++) {
if (registeredUsers[i].equals(username)) {
available = false;
// Finish the loop, as the name is already taken
break;
}
}
// Trigger the Event and notify the user of our widget
OnUserChecked(username, available);
return false;
}
return false;
}
// Define our custom Listener interface
public interface OnUsernameAvailableListener {
public abstract void onAvailableChecked(String username,
boolean available);
}
}
The problem is that I take a classclastexception. Because I declare on the xml the username as edittext and in the main code i declare it as CheckUsernameEditText. How I can solve this problem?Why the casting isn't working, especially now that the CheckUsernameEditText extends the EditText class?
All CheckUsernameEditText objects are EditText objects,
but not all EditText objects are CheckUsernameEditText objects.
You should use your custom class in the XML:
<your.package.name.CheckUsernameEditText
android:id="#+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
I believe if you have a custom View (in your case CheckUsernameEditText) you have to declare it as such in the XML... Remember that as #Sam points out you can't cast downward into a derived class, you can only cast upward into a parent class, so you can always cast your CheckUsernameEditText View up to an EditText (or just View) but you can't go the other way.