I'm working to implement a SearchView on my Fragment's RecyclerView as shown here. When the user taps the search button I want the Menu Item for the SearchView to Override the toolbar and display the area for them to search. If I don't use my custom style for the toolbar it works fine but when I do it get the images below.
Here is what I am currently getting:
This is the class that the EventListActivity inherits from:
public abstract class SingleFragmentActivity extends AppCompatActivity {
protected abstract Fragment createFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.eventcalendar_activity_fragment);
// Manages our fragments. We can call it to add a fragment to an activity in code
FragmentManager fm = getSupportFragmentManager();
// Check if fragment of R.id already exits
// The FragmentManager saves out the list of fragments on rotation destruction or memory reclaim
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
// If the the fragment does not exist, create it
if(fragment == null) {
fragment = createFragment();
// Create a new fragment transaction, include one add operation in it, and then commit it
fm.beginTransaction().add(R.id.fragment_container, fragment).commit();
}
}
}
This creates the fragment that manages and works with the RecyclerView:
public class EventListActivity extends SingleFragmentActivity {
#Override
protected Fragment createFragment() {
// Setting arguments for the new fragment created from the intent from EventFeedResultWrapper
EventListFragment fragment = new EventListFragment();
fragment.setArguments(getIntent().getExtras());
return fragment;
}
}
Fragment that holds the RecyclerViewthat the SearchView will interface with. This is where the search menu button is inflated. It is where I've been trying to modify the toolbar:
public class EventListFragment extends Fragment {
private RecyclerView mEventRecyclerView;
private EventAdapter mAdapter;
// Telling the FragmentManager that it is
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
((AppCompatActivity)getActivity()).getSupportActionBar().setCustomView(R.layout.action_bar_center);
View sabView = ((AppCompatActivity)getActivity()).getSupportActionBar().getCustomView();
TextView titleTxtView = (TextView) sabView.findViewById(R.id.action_bar_title);
titleTxtView.setText("Events Calendar");
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayShowCustomEnabled(true);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.eventcalendar_fragment_event_list, container, false);
mEventRecyclerView = (RecyclerView) view.findViewById(R.id.event_recycler_view);
// RecyclerView requires a layout manager to work, layout manager is in charge of position items on screen
mEventRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
updateUI();
return view;
}
// Populate the menu instance
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.eventcalendar_menu, menu);
}
// When we edit a EventActivity this saves it back to the EventListActivity
// onResume over onStart because we cannot assume the activity will be stopped
// when another activity is in front of it. If the other activity is transparent
// then the activity might just get paused. If it is paused then onStart() will not be called
// but on resume will be called.
// NOTE: In general onResume() is the safest place to take action to update a fragment's view
#Override
public void onResume() {
super.onResume();
updateUI();
}
private void updateUI() {
// Read in the events saved in to EventFeedResultWrapper by the Async task in ParseEventFeedTask
EventFeedResultWrapper wrapper = (EventFeedResultWrapper) getArguments().getSerializable(ParseEventFeedTask.EXTRA_RESULTS_LIST);
// Make sure wrapper is not null, it will NEVER be null
if(wrapper == null){
throw new RuntimeException("Error: The wrapper is null!");
}
// Get all the events from the wrapper/serializable
List<Event> events = wrapper.getEventFeedResults();
// Gets the context that we don't use rofl
EventCal eventCal = EventCal.get(getActivity());
// Add all the events we got from the wrapper to our event manager eventCal
eventCal.addEvents(events);
// Check to see if the EventAdapter is already setup
if(mAdapter == null) {
mAdapter = new EventAdapter(events);
mEventRecyclerView.setAdapter(mAdapter);
} else {
mAdapter.notifyDataSetChanged();
}
}
//Adapter here - removed code since it doesn't do anything with the toolbar
//RecycleView onClickLister - removed code since it doesn't do anything with the toolbar
}
This is the custom XML Style
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/action_bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#ffffff"
android:textSize="24dp"/>
</LinearLayout>
Event Menu XML
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/action_search"
android:title="Search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always"/>
</menu>
Did you missed android:orientation="vertical". The text is displaying vertically. You want horizontal right?
Oh nvm i didn't understood it at first. The problem must be the inflating of the custom view, for some reason it seems it keeps the original view there and inflated it somewhere else. Give me 1s i'll update the answer when i find the issue
Check here: getSupportActionBar().setCustomView(view) does not fill entire actionbar
Similar problem. First and Second answer
EDIT2: Remove the code from EventListFragment onCreate. You don't need to inflate a searchView, you can use the default one.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_main, menu);
super.onCreateOptionsMenu(menu, inflater);
setOptionsMenu(menu);
}
public void setOptionsMenu(Menu menu) {
MenuItem search = menu.findItem(R.id.search);
SearchView searchView;
/**
* Setup the SearchView
*/
SearchManager searchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
final boolean[] modifiedOriginal = {false};
searchView = (SearchView) search.getActionView();
if (searchView != null) {
searchView.setSearchableInfo(searchManager.getSearchableInfo(((Activity) context).getComponentName()));
MenuItemCompat.setOnActionExpandListener(search, new MenuItemCompat.OnActionExpandListener() {
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
...
return true;
}
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
...
return true;
}
});
final EditText et = ButterKnife.findById(searchView, android.support.v7.appcompat.R.id.search_src_text);
et.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
String s = editable.toString();
if (!s.equals("") && !s.equals(" ")) {
...
}
}
});
ButterKnife.findById(searchView, android.support.v7.appcompat.R.id.search_close_btn).setOnClickListener(
view -> {
...
et.setText("");
}
);
}
}
You can leave your menu xml as it is.
Related
I have a activity and fragment ,i want to set a menu to mytoolbar in pre run time the icon has set but in run application it does not show anything.
in my activity:
public class MainActivity extends AppCompatActivity {
private TabLayout mTabLayout;
private TextView mTextViewTabOne;
private TextView mTextViewTabTwo;
private TextView mTextViewTabThree;
private android.support.v7.widget.Toolbar mToolbar;
private TextView mTextViewToolbarTitle;
private AlertDialog mAlertDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setLocale("fa");
getSupportFragmentManager().beginTransaction().replace(R.id.framelayout_mainactivity_fragmentcontainer, new AuthenticationPasswordFragment()).commit();
setToolBar(getString(R.string.addbank_toolbartitle));
}
public void setToolBar(String title) {
mToolbar = findViewById(R.id.toolbar_everywhere_toolbar);
mTextViewToolbarTitle = findViewById(R.id.toolbar_title);
mTextViewToolbarTitle.setText(title);
}
and in my fragment :
public class AuthenticationPasswordFragment extends BaseFragment implements BaseAuthenticationContract.View {
private TextInputEditText mEditTextPassword;
private TextInputLayout mTextInputEditTextPassword;
private View mRoot;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
mRoot=inflater.inflate(R.layout.fragment_authenticationpassword,null);
return mRoot;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.authenticationpassword_menutoolbar,menu);
}
in my menu is:
<item
android:id="#+id/item_authenticationpassword_confirm"
android:title="confirm"
android:icon="#drawable/everywhere_confirm"
app:showAsAction="always"
/>
Replace this setToolbar in activity :
public void setToolBar(String title,int resourceMenu) {
mToolbar = findViewById(R.id.toolbar_everywhere_toolbar);
mTextViewToolbarTitle = findViewById(R.id.toolbar_title);
mTextViewToolbarTitle.setText(title);
mToolbar.inflateMenu(resourceMenu);
}
With this :
public void setToolBar(String title) {
mToolbar = findViewById(R.id.toolbar_everywhere_toolbar);
mTextViewToolbarTitle = findViewById(R.id.toolbar_title);
mTextViewToolbarTitle.setText(title);
}
And add this line to your onCreateView fragment or any fragment that you want add menu toolbar to it :
((MainActivity)getActivity()).setToolBar(getString(R.string.authenticationpassword_titletoolbar),R.menu.authenticationpassword_menutoolbar);
In the activity's onCreate method, set the toolbar with
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Then add this method.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.yourmenufile, menu);
return super.onCreateOptionsMenu(menu);
}
Then add menu click listener,
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 0:
// do whatever
return true;
default:
return super.onOptionsItemSelected(item);
}
}
If you want to change the menu items and listeners while in different fragments, use interfaces to let the activity know which fragment is active and change accordingly.
If you want the method onCreateOptionsMenu inside a Fragment, add the codeline toolbar.setHasOptionsMenu(true) in onCreate of the activity where the Toolbar is set up.
The second option is to move the onCreateOptionsMenu into the Activity.
See this related StackOverflow question.
Report back if this works, I hope it helps you.
I have many fragments they're shown in 2 tab layouts using FragmentStatePagerAdapter in each fragment there is a Recycler view and I want to use a search bar in the app bar to filter results.
First I did this using the onQueryTextChange listener in the onCreateOptionsMenu and it worked fine with a couple fragments but when I added the implementation to the other fragments (just ctrl+c and ctrl+v) it stopped working, no results even on fragments that used to work i then read here that it was better to stick the onQueryTextChange listener in onPrepareOptionsMenu in an attempt to avoid an issue with invalidateOptionsMenu but i decided to give that a try which also worked and then when i added the methods to all my other fragments yet again it fails but it does work on a handful of fragments, oddly these happen to be all attached to the same parent fragment but the code for the 4 parent fragments, and for calling them is identical.
A different way around this I've read is creating an interface and using that from my main activity by getting a reference to the current fragment but then id have to get the currently showing fragment from my viewpager which i dont know is possible can anyone weigh in on this for me,
Many thanks
And thanks for the great edit lol
EDIT
Tried the interface approach to no avail although I'm still a beginner i do need to find the attached fragment and with a fragmentstateviewpager its just not possible without using hacks there must be some reason why it works in some and not in others and sometimes not at all
Edit 2
So im still fiddling with this and ive had almost no replies so lets flesh this out a little so i was adding the menu layout in the main activity like this
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.dashboard, menu);
SearchView searchView =
(SearchView)menu.findItem(R.id.action_search).getActionView();
SearchManager searchManager = (SearchManager)
getSystemService(SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
return true;
}
and then adding a listener on to it from the fragment in onPrepareOptionsMenu like this
#Override
public void onPrepareOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
//in this instance we wont as it will create a duplicate
SearchView searchView = (SearchView)
menu.findItem(R.id.action_search).getActionView();
searchView.setOnQueryTextListener(null);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener(){
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
showResults(newText);
return false;
}
});
super.onPrepareOptionsMenu(menu);
}
and i have called setHasOptionsMenu(true) in onCreateView of the fragment, this works perfectly but i have about 20 fragments to cycle through and when I add this to the other fragments it fails it either does nothing or makes the view blank or in rare occasions works, but that for me says my code is okay and maybe its a lifecycle thing, ive tried calling it from the onCreateOptionsMenu of the fragments and achieved the same results, and ive tried not calling anything from the MainActivity in onCreateOptions except inflating the menu and allow the fragments to call the search activity using
SearchView searchView = (SearchView)menu.findItem
(R.id.action_search).getActionView();
SearchManager searchManager = (SearchManager)
getActivity().getSystemService(getActivity().SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager.getSearchableInfo
(getActivity().getComponentName()));
which again works up to around 8 fragments but my 20 or so just causes it to fall on its sword, is there something im not doing that could help
Edit 3
Okay so by adding some, checking it, adding some more, checking it, it seems to be a problem with my parent fragments so I have main activity 4 fragments that each hold a fragment state pager that each holds 7 or so fragments if I add the methods for the query listener to the first 7 or so fragments in the first parent fragment they all work beautifully, if I then add the methods to the next set of 7 or so fragments in the second parent only the second parents child fragments work, if I then quit my app and open it again only the first parents fragments work, will continue investigating any help appreciated I'll post the code for my parent fragments here soon.
EDIT 4
Just going to add the code i use for the parent fragments and the fragmentstatepager from my main activity
so from my Main activity i set the fragmentstatepager like this
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new
ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new sentenceMakers(), "QUICKS");
adapter.addFrag(new tabFragOne(), "NOUNS");
adapter.addFrag(new tabFragTwo(), "VERBS");
adapter.addFrag(new tabFragThree(), "OBJECTS");
viewPager.setAdapter(adapter);
}
class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
//do nothing here! no call to super.restoreState(arg0, arg1);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFrag(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
and my parent fragments look like this
public class sentenceMakers extends Fragment{
public ImageView imageView;
private ViewPager viewPager;
public static TabLayout tabLayout;
public sentenceMakers() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate
(R.layout.tab_frag_one, container, false);
// Inflate the layout for this fragment
//setRetainInstance(true);
viewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) rootView.findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
setupTabIcons();
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
}
private void setupTabIcons() {
TextView tabZero2 = (TextView)
LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
tabZero2.setText("FAVOURITES");
//tabOne.setGravity(View.TEXT_ALIGNMENT_CENTER);
tabZero2.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.ic_star_white_24dp, 0, 0);
tabLayout.getTabAt(0).setCustomView(tabZero2);
TextView tabZero = (TextView)
LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
tabZero.setText("FREQUENTS");
//tabOne.setGravity(View.TEXT_ALIGNMENT_CENTER);
tabZero.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.ic_people_outline_white_24dp, 0, 0);
tabLayout.getTabAt(1).setCustomView(tabZero);
TextView tabOne = (TextView)
LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
tabOne.setText("PRONOUNS");
//tabOne.setGravity(View.TEXT_ALIGNMENT_CENTER);
tabOne.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.ic_accessibility_white_24dp, 0, 0);
tabLayout.getTabAt(2).setCustomView(tabOne);
TextView tabTwo = (TextView)
LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
tabTwo.setText("CONJUCTIONS");
tabTwo.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.ic_add_white_24dp, 0, 0);
tabLayout.getTabAt(3).setCustomView(tabTwo);
TextView tabThree = (TextView)
LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
tabThree.setText("ADJECTIVES");
tabThree.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.ic_favorite_border_white_24dp, 0, 0);
tabLayout.getTabAt(4).setCustomView(tabThree);
tabLayout.getTabAt(0).getCustomView().setSelected(true);
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new
ViewPagerAdapter(getChildFragmentManager());
adapter.addFrag(new favouriteCards(), "FAVOURITES");
adapter.addFrag(new predictedCards(), "FREQUENTS");
adapter.addFrag(new pronouns(), "PRONOUNS");
adapter.addFrag(new conjuctions(), "CONJUNCTIONS");
adapter.addFrag(new Others(), "ADJECTIVES");
viewPager.setAdapter(adapter);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFrag(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
}
}
still struggling with this and i cannot for the life of me work out why it would work in one lot of fragments untill i add it to the next set, its the same thing with the same code it must just be initializing the last fragments that are asking for it can anyone help me here?
Thanks for any help
Basically, there is a thing that you have to keep in mind when you use fragments: do not keep references to your fragments: just create them and add to your layout, or provide to your ViewPager but then use other methods to retrieve them, like FragmentManager.findFragmentBy...
That said, your code shoud be (let me simplify with just two fragments):
Main activity
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new MyViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
}
class MyViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return new sentenceMakers();
case 1:
return new tabFragOne();
// add here fragments for any other position
}
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch(position) {
case 0:
return "QUICKS";
case 1:
return "NOUNS";
// add here titles for any other position
}
}
}
sentenceMakers
The code you will have to use in your sentenceMakers Fragment is very similar to the previous one, but you will have to use the child fragmentManager (as you did in your code) and again, do not keep your fragments in a List, let the system create them when needed.
ok i fixed it so the problem is that the parent fragmentstatepager loads fragmentA + fragmentB into memory, then the child fragmentstatepagers load there own childfragmentA + childfragmentB so we now have 2 parents and 4 children, onPrepareOptionsMenu is called from the child fragments so the the first is loaded childfragmentA(child of fragmentA) and then childFragmentA(child of fragmentB) is loaded and steals the search view, my 'fix' for this is probably a little crude i'm self taught and i'm not very aware of how to manage memory or if its bad to keep hold of references etc, i digress, in the parent fragment i check which fragment is visible using
setMenuVisibility(final boolean visible)
and in there i set a public static boolean and check for it in my childfragments and it works flawlessly for me as mentioned this is probably a terrible thing to do hopefully someone here can give a better solution
check which parent is visible
public static boolean tabFragOneVisible;
#Override
public void setUserVisibleHint(boolean isVisible){
super.setUserVisibleHint(isVisible);
if(!isVisible){
tabFragOneVisible = false;
}else{
tabFragOneVisible = true;
}
}
check if parent is visible hook up search view
#Override
public void onPrepareOptionsMenu(Menu menu) {
// need to check if fragment is visible
if (tabFragOne.tabFragOneVisible) {
SearchView searchView = (SearchView)
menu.findItem(R.id.action_search).getActionView();
SearchManager searchManager = (SearchManager) getActivity()
.getSystemService(getActivity().SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getActivity().getComponentName()));
// searchView.setOnQueryTextListener(null);
searchView.setOnQueryTextListener(new
SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
showResults(newText);
return false;
}
});
}
else{
}
super.onPrepareOptionsMenu(menu);
}
Maybe, it'll still help someone struggling to make one SearchView work for multiple Fragments. I found that onQueryTextListener would work on Fragment 1 (where the SearchView is initially created) but would not fire on Fragment 2,3, etc.
Fragments 2 worked when onPrepareOptionsMenu is overridden and onViewCreated includes a setHasOptionsMenu(true). For example:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setHasOptionsMenu(true);
...the rest of your code goes here...
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) item.getActionView();
searchView.setOnQueryTextListener(this);
super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
Toast.makeText(getActivity(),"It Worked!!",Toast.LENGTH_SHORT).show();
return true;
}
I'm learning how to create an app and have figured out how to build a login activity and a menu for logging out that returns you to the login layout. However, if I navigate through my different layouts/activities, the menu item for logging out duplicates. I believe it's due to having the menu created on each layout, but I'm not sure how to change it so that it doesn't duplicate.
Here's my fragment.
public class UserFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
return inflater.inflate(R.layout.activity_user_fragment, container, false);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_logout, menu);
}
}
My LoginActivity.class
public class LoginActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
Button b1login = (Button) findViewById(R.id.btlogin);
Button b2login_cancel = (Button) findViewById(R.id.btcancel_login);
assert b1login != null;
b1login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText ed1 = (EditText) findViewById(R.id.etuser_name);
EditText ed2 = (EditText) findViewById(R.id.etpassword);
if (ed1.getText().toString().equals(getText(R.string.user_id)) &&
ed2.getText().toString().equals(getText(R.string.user_password))) {
Toast.makeText(getApplicationContext(), R.string.successful_login,
Toast.LENGTH_SHORT).show();
setContentView(R.layout.activity_clients);
} else {
Toast.makeText(getApplicationContext(), R.string.unsuccessful_login,
Toast.LENGTH_SHORT).show();
}
}
});
assert b2login_cancel != null;
b2login_cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.exit(0);
}
});
}
//Menu option logout return to login screen.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.menu_item_logout) {
Intent i = new Intent(this, LoginActivity.class);
this.startActivity(i);
return true;
}
return super.onOptionsItemSelected(item);
}
public void addClient(View view) {
setContentView(R.layout.activity_new_client);
}
public void submitClient(View view) {
setContentView(R.layout.activity_sessions);
}
public void cancelClient(View view) {
setContentView(R.layout.activity_clients);
}
public void newSession(View view) {
setContentView(R.layout.activity_new_session);
}
public void cancelSessionCompletion(View view) {
setContentView(R.layout.activity_sessions);
}
public void cancelSession(View view) {
setContentView(R.layout.activity_sessions);
}
}
Fragment layout.
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/menu_item_logout"
android:icon="#drawable/ic_logout"
android:title="#string/logout"
app:showAsAction="ifRoom|withText" />
</menu
EDIT:
I deleted the fragment where I initially created the menu as well as the menu code from the UserFragment and hard coded the menu itself into the LoginActivity class, which fixed the duplication issue.
Code adding menu.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
However, the menu now shows on the login screen. How do I prevent it from showing there?
For having different menu items in different activities you need to create different menu xml layouts.
So one file named menu.xml is already there in res folder. You need to create new xml file in same folder.
Then in java code of that activity :- in override Oncreateoptionmenu inflate the menu you want.
Here is what you need to do:
In your LoginActivity class, you want to NOT inflate the menu because you don't want users to do see the logout option.
In your Activity that loads after Login, you actually want to inflate the Logout menu - well, because you want them to see and use that option.
In your code, I see that your fragment uses the method :
setHasOptionsMenu(true);
What you can do here is use an if statement to see if a user has logged in before and if not, set that to true otherwise, do nothing (which is like setting to false).
You can reuse the menu in other activities if you want to enable users to Logout from those activities or just leave them it out!
I hope this helps!
EDIT
According to your sample code above, your first part shows what you would do inside a Fragment class - you setHasOptionsMenu(true). Now, it seems that you have two logout menus, in the activity and in the fragment - which I do not see where you have used your Fragment in your LoginActivity - unless it is somewhere else in a different activity!
Similar to my answer here, https://stackoverflow.com/a/63008005/5916188, a work-around would be to remove the menu each time, if it exists, before adding it.
Note that the existing menu is removed item by item (R.id...) while the inflated one is added by menu (R.menu...) as usual.
This way, you can keep the menu in the fragment. Like this:
#Override public void onCreateOptionsMenu (#NonNull Menu menu, #NonNull MenuInflater
inflater)
{
super.onCreateOptionsMenu (menu, inflater);
removeMenuItemIfPresent (menu, R.id.menu_search_options);
removeMenuItemIfPresent (menu, R.id.menu_sample_filter1);
removeMenuItemIfPresent (menu, R.id.menu_sample_filter2);
inflater.inflate (R.menu.menu_search_menu, menu);
}
private void removeMenuItemIfPresent (#NonNull Menu menu, int resourceIDToRemove)
{
MenuItem menuItemToRemove = menu.findItem (resourceIDToRemove);
if (menuItemToRemove != null) {
menu.removeItem (menuItemToRemove.getItemId ());
}
}
I have Action Bar in my application. I am adding action items using menu.xml. I am using action bar-compat as my support library. I observed a weird issue where my action items are getting duplicated.
I am finding this issue randomly when leave my device idle or work with other applications. Please find the screen shot and my code below:
private LoginWebActivity mContext;
private final String TAG = "LoginFragment";
// for metrics
private String mPageNameSignIn = "signin";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.webview, container, false);
return mView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContext = (LoginWebActivity) getActivity();
initFragment();
}
#Override
public void onResume() {
super.onResume();
}
/**
* Initialises the views and variables of the fragment.
*/
#SuppressLint({ "JavascriptInterface", "SetJavaScriptEnabled" })
protected void initFragment() {
mWebView = (WebView) mView.findViewById(R.id.webView);
Bundle b = mContext.getIntent().getExtras();
if (b != null) {
mUrl = b.getString(Constants.EXTRA_WEB_LOGIN_URL);
}
super.initFragment();
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.signin, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Navigate
switch (item.getItemId()) {
case R.id.menu_item_signup:
mContext.onSignUpClick();
break;
case android.R.id.home:
if (!goBack())
getActivity().finish();
default:
break;
}
return super.onOptionsItemSelected(item);
}
My XML :
<?xml version="1.0" encoding="utf-8"?>
<item
android:id="#+id/menu_item_signup"
allergy:showAsAction="ifRoom"
android:title="#string/sign_up">
</item>
You must clear your menu object before adding items. I had the same problem and this was the best solution that I've found.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.signin, menu);
super.onCreateOptionsMenu(menu, inflater);
}
Pretty late to this party, but for anyone that comes across this via the Google like I did, here's the real problem.
You didn't post your Activity code that's creating the Fragment, but I will venture to guess that it looks something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
Fragment fragment = ...
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
The problem with this is that when the activity goes through its lifecycle (which would happen "when leave my device idle or work with other applications", as you say), the system will save and restore the state of fragments for you. But with this code, you also are adding a new fragment to your Activity, so you end up with multiple fragments running in your activity, each adding an item to the menu.
While the posted workaround will address the duplicate menu entries issue, it would leave these extra fragment instances lying around, which is obviously not what you want.
The correct fix is a simple null check:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
if (savedInstanceState == null) {
Fragment fragment = ...
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
Since the system will indicate the activity is being recreated with a non-null Bundle for the savedInstanceState parameter, you check this to determine whether you should be creating and adding a new fragment.
Hope that helps.
i used Renan Bandeira's great solution and i had some error so i changed it a little bit and worked for me too . then I'm sharing my experience : maybe it become helpful again all credit goes to him for great solution .
#Override
public void onCreateOptionsMenu(Menu menu ) {
menu.clear();
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu."your current activity name ", menu);
return true;
}
I facing the same problem and exactly as state by #Szymon "I add menu option from the fragment, I create multiple fragments?" So my solution was look like below.
onCreate :
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.menu, menu);
menu.findItem(R.id.action_one).setVisible(true);
menu.findItem(R.id.action_two).setVisible(false);
super.onCreateOptionsMenu(menu, inflater);
}
onPrepare :
#Override
public void onPrepareOptionsMenu(Menu menu) {
if (isAdded()
&& !isDetached()
&& isVisible()
&& !isRemoving()
)
{
// show the menu
if (menu.findItem(R.id.action_one).isVisible())
menu.findItem(R.id.action_one).setVisible(true);
// hide the menu
if (menu.findItem(R.id.action_two).isVisible())
menu.findItem(R.id.action_two).setVisible(false);
}
}
You should use the following method instead and you will not see the duplicates anymore (notice that it has only a Menu object as argument)
#Override
public boolean onCreateOptionsMenu( Menu menu )
{
getMenuInflater().inflate( R.menu.main_activity_menu, menu );
return true;
}
I would like to add a badge over ActionBar MenuItem
But the digit icon didn't shows.
Here's what I have done so far
public class Main extends SherlockFragmentActivity
{
private Fragment menuFrag=null;
private MenuItem menuMsg=null;
private BadgeView badge=null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
//Do my stuff...
initUI();
}
private void initUI()
{
FragmentManager fm=getSupportFragmentManager();
FragmentTransaction ft=fm.beginTransaction();
menuFrag=fm.findFragmentByTag("f1");
if(menuFrag==null)
{
menuFrag=new MenuFragment();
ft.add(menuFrag, "f1");
}
ft.commit();
// badge=new BadgeView(Main.this, (View)menuMsg); //Not working
badge=new BadgeView(Main.this, menuMsg.getActionView()); //Not working as well
badge.setBackgroundResource(R.drawable.badge_ifaux);
badge.setTextSize(10);
badge.setBadgeMargin(2);
badge.setText("1");
badge.show();
}
private class MenuFragment extends SherlockFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
menu.add("Cloud").setIcon(R.drawable.icon_cloud).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menu.add("List").setIcon(R.drawable.icon_list).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menuMsg=menu.add("Msg");
menuMsg.setIcon(R.drawable.icon_msg).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
Toast.makeText(Main.this, "Got click: " + item.toString(), Toast.LENGTH_SHORT).show();
return true;
}
}
}
Where do I did wrong ?
RRTW,
The library you are using doesn't support badging Actionbar menu items natively.
https://github.com/jgilfelt/android-viewbadger/commit/e08c3a78cb92c0c8587790b15e73434f972912cf
However, it doesn't mean you can't get it to work.
The setup is going to be as follows (this assumes you already have the viewbager library setup in your project)
(1) onCreateOptionsMenu --> (2) Add a R.menu.your_place_holder_item --> (3) setActionView with custom xml layout --> (4) findViewById of the MenuItem object to get your button/view to set the badge.
1) setup your onCreateOptionsMenu and create a the R.menu.actionbar_menu_messages
R.menu.actionbar_menu_messages:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
>
<item android:showAsAction="ifRoom" android:icon="#drawable/action_bar_pk_content_email"
android:id="#+id/menuMessages" android:title="More"></item>
</menu>
onCreateOptionsMenu:
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getSupportMenuInflater(); //If you are using the support library otherwise use: getMenuInflater();
inflater.inflate(R.menu.actionbar_menu_messages, menu);
this.setupMessagesBadge(menu.findItem(R.id.menuMessages)); //This is part of step 2
return true;
}
2) Defined the common_messages_indicator
R.layout.common_messages_indicator:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="64dp"
android:layout_height="fill_parent"
android:paddingTop="10dp"
android:gravity="center">
<ImageView
android:id="#+id/imgMessagesIcon"
android:layout_width="32dp"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:scaleType="fitCenter"
android:src="#drawable/messages_button"
android:background="#android:color/transparent"
android:focusable="false"
/>
</FrameLayout>
perform setActionView to add your custom xml layout to the ActionView
private void setupMessagesBadge(final MenuItem msgItem) {
msgItem.setActionView(R.layout.common_messages_indicator);
if(msgItem.getActionView().findViewById(R.id.imgMessagesIcon) != null)
{
ImageView imgMessagesIcon = ((ImageView)msgItem.getActionView().findViewById(R.id.imgMessagesIcon));
imgMessagesIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Your click on the action bar item will be captured here
}
});
int badgeCnt = 20;// Add your count here
if(messageCenterBadge == null && badgeCnt > 0)
{
//imgMessagesIcon is the imageview in your custom view, apply the badge to this view.
messageCenterBadge = new BadgeView(this, imgMessagesIcon);
messageCenterBadge.setBadgePosition(BadgeView.POSITION_TOP_RIGHT);
messageCenterBadge.setBadgeMargin(0);
messageCenterBadge.setTextSize(12);
messageCenterBadge.setText(String.valueOf(badgeCnt));
messageCenterBadge.show();
}
else if(messageCenterBadge != null && badgeCnt > 0 )
{
messageCenterBadge.setText(String.valueOf(badgeCnt));
messageCenterBadge.show();
}
else if(messageCenterBadge != null && badgeCnt == 0) {
messageCenterBadge.hide();
}
}
}