I have implemented two fragments with ActionBar tabs. I am using Sherlock fragments. When I start a fragment I create a searchview on the action bar.
The problem is: When I search something on the first fragment and without closing the searchview or deleting the text, I move to the next fragment, I get a collapsed searchview, which is fine. But when I again get back to my first fragment, I see a list sorted like before but the searchview is collapsed. The only way to get back the original list is to click the searchview once and closing it.
How do I reset the searchview on the first fragment when I move to second fragment or how can keep the searchview open when I come to the first fragment?
Any one of the solutions will do.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>
<item android:id="#+id/action_search"
android:title="search"
android:icon="#drawable/search"
android:orderInCategory="100"
android:showAsAction="always|collapseActionView"
android:actionViewClass="com.actionbarsherlock.widget.SearchView" />
</menu>
This is how I am inflating in the fragment
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.main, menu);
SearchView sv = (SearchView)getActivity().findViewById(R.id.action_search);
sv.setOnQueryTextListener(new OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
//...
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
bindingData.resetData();
bindingData.getFilter().filter(newText.toString());
return false;
}
});
}
For those who are still scratching their heads and are not able to find a solution, here it how you do it. It is pretty simple:
Just assign an ID to your searchviews when you are creating it (by creating the ids.xml and reserving the id there and assigning it on runtime).
Now in onTabUnselected() method of the main tab activity, identify the searchviews and use function setIconified(true) twice (one to remove the text and one to collapse the searchview).
Do not forget to add a check for null query string in onQueryTextChange method of your searchview, it created a problem for me.
Android gives you methods like onCreate and onDestroy to work with activity and also for the menus onCreateOptionsMenu and onDestoryOptionsMenu. You can use onDestroyOptionsMenu method to clear your SearchView or to realize your another ideas.
for example:
#Override
public void onDestroyOptionsMenu() {
super.onDestroyOptionsMenu();
if (searchView != null &&
!searchView.getQuery().toString().isEmpty()) {
searchView.setIconified(true);
searchView.setIconified(true);
}
}
It works for me, good Luck.
Related
With Fragment:setRetainInstance(true); the fragment is not re-instantiated on a phones orientation change.
And of course i want my fragments to be kept alive while switching from one fragment to another.
But the Android Studio 4 provides a wizard-template with only
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
From hours of debugging and searching the net if think it would need to inherent from the class FragmentNavigator so i can overwrite FragmentNavigator:naviagte where a new fragment gets created via final Fragment frag = instantiateFragment(.. and then is added with ft.replace(mContainerId, frag);
So i could find my old fragment and use ftNew.show and ftOld.hide instead.
Of course this is a stupid idea, because this navigate method is full of other internal stuff.
And i have no idea where that FrameNavigator is created.
I can retrieve it in the MainActivity:OnCreate with
NavigatorProvider navProvider = navController.getNavigatorProvider ();
Navigator<NavDestination> navigator = navProvider.getNavigator("fragment");
But at that time i could only replace it with my derived version. And there is no replaceNavigtor method but only a addNavigator method, which is called where ?
And anyways this all will be far to complicated and therefore error prone.
Why is there no simple option to keep my fragments alive :-(
In older Wizard-Templates there was the possibility of
#Override
public void onNavigationDrawerItemSelected(int position) {
Fragment fragment;
switch (position) {
case 1:
fragment = fragment1;
break;
case 2:
fragment = fragment2;
break;
case 3:
fragment = fragment3;
break;
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if(mCurrentFragment == null) {
ft.add(R.id.container, fragment).commit();
mCurrentFragment = fragment;
} else if(fragment.isAdded()) {
ft.hide(mCurrentFragment).show(fragment).commit();
} else {
ft.hide(mCurrentFragment).add(R.id.container, fragment).commit();
}
mCurrentFragment = fragment;
}
but i have no idea how to do this with the Android 4.0 template where my MainActivity is only derived as:
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
Ideas welcome :'(
Hi there & sorry for my late answer! I had a similar problem with navigation drawers and navigation component. I tried around a little and found a working solution, which might be helpful for others too.
The key is the usage of a custom FragmentFactory in the FragmentManager of the MainActivity. See the code for this below:
public class StaticFragmentFactory extends FragmentFactory {
private myNavHostFragment1 tripNavHostFragment;
private myNavHostFragment2 settingsNavHostFragment;
#NonNull
#Override
public Fragment instantiate(#NonNull ClassLoader classLoader, #NonNull String className) {
if (MyNavHostFragment1.class.getName().equals(className)) {
if (this.myNavHostFragment1 == null) {
this.myNavHostFragment1 = new MyNavHostFragment1();
}
return this.myNavHostFragment1 ;
} else if (MyNavHostFragment2.class.getName().equals(className)) {
if (this.myNavHostFragment2 == null) {
this.myNavHostFragment2 = new MyNavHostFragment2();
}
return this.myNavHostFragment2;
}
return super.instantiate(classLoader, className);
}
}
The FragmentFactory survives the navigation between different fragments using the NavigationComponent of AndroidX. To keep the fragments alive, the FragmentFactory stores an instance of the fragments which should survive and returns this instance if this is not null. You can find a similar pattern when using a singleton pattern in classes.
You have to register the FragmentFactory in the corresponding activity by calling
this.getSupportFragmentManager().setFragmentFactory(new StaticFragmentFactory())
Please note also that I'm using nesten fragments here, so one toplevel fragment (called NavHostFragmen here) contains multiple child fragments. All fragments are using the same FragmentFactory of their parent fragments. The custom FragmentFactory above returns the result of the super class method, when the fragment to be instantiated is not known to keep alive.
Hello fellow coders of the night,
I am stuck with a moral dilemma (well not moral, but mostly i don't know what to do).
Suppose I have one button that can do several actions, depending on the menu item which is chosen.
Basically, I've imagined this
private void menuButtonActionPerformed(ActionEvent b)
ActionEvent a
if(a.getSource()==menuItem)
if(b.getSource()==button)
do this and that
Is this the correct way to do this? because if it is I'd have to add ActionListeners on the menuItem but I get stuck with some stupid error code somewhere!
Thanks in advance for helping me!
Post Scriptum : #David, I've tried this, however the initial condition isn't verified.
private void buttonValidateActionPerformed(java.awt.event.ActionEvent evt)
ActionListener l = (ActionEvent e) -> {
if(e.getSource()==menuItemAdd)
{
System.out.println("eureka!");
buttonSearch.setEnabled(false);
if (evt.getSource()==buttonValidate)
{
DataTransac dt = new DataTransac();
dt.addCoders("...");
}
}
if(e.getSource()==itemDelete)
{
DataTransac dt = new DataTransac();
dt.deleteCoders("...");
}
};
menuItemAdd.addActionListener(l);
itemDelete.addActionListener(l);
That won't work; your listener will get a different invocation for each time the listener is used -- so the event source will be either a button or a menu item for a single invocation.
You'll need to respond to the menu item with one ActionListener that stores state, and then separately handle the button action. You could do this with one listener, but I wouldn't; I'd do this:
private MenuItem selected;
private class MenuItemListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
// if you really want to have one listener for multiple menu items,
// continue with the .getSource() strategy above, but store some
// state outside the listener
selected = (MenuItem)event.getSource();
// you could alternatively have a different listener for each item
// that manipulates some state
}
}
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
// take conditional action based on selected menu item, as you describe
// in the question
}
}
void setup() {
JMenuItem first = /* ... */;
JMenuItem second = /* ... */;
MenuItemListener listener = new MenuItemListener();
first.addActionListener(listener);
second.addActionListener(listener);
JButton button = /* ... */;
button.addActionListener(buttonListener);
}
Generally speaking this is the preferred approach -- use a different listener for each semantic action, rather than one that introspects the source. Your code will be cleaner, simpler, and easier to understand.
For the same reasons, some people prefer to use anonymous classes for Java event listeners. Here's a Gist that shows several syntaxes: https://gist.github.com/sfcgeorge/83027af0338c7c34adf8. I personally prefer, if you are on Java 8 or higher:
button.addActionListener( event -> {
// handle the button event
} );
I have a TableView with a ComboBoxTableCell, when using the default implementation the user have to click three times to select a value from of the ComboBox's list.
I want when the user clicks on the cell to show the combo box list. I based my solution on this one:
JavaFX editable ComboBox in a table view
The cell does get into edit mode (startEdit() is called) but it takes another click to show the list of values, what am I missing?
table.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) ->
{
if (table.getEditingCell() == null)
{
TablePosition focusedCellPos = table.getFocusModel().getFocusedCell();
table.edit(focusedCellPos.getRow(), focusedCellPos.getTableColumn());
}
});
Thanks.
Interesting problem - bubbling up again after quite a while :)
Looks like the approach of the OP is indeed working (as of fx11, some bugs around its editing seem to be fixed) - with a little help from the combo cell:
start editing in a single click handler on the tableView (from OP)
extend ComboBoxTableCell and override its startEdit to open the dropDown
Code snippet:
// set editable to see the combo
table.setEditable(true);
// keep approach by OP
table.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) -> {
TablePosition<Person, ?> focusedCellPos = table.getFocusModel()
.getFocusedCell();
if (table.getEditingCell() == null) {
table.edit(focusedCellPos.getRow(),
focusedCellPos.getTableColumn());
}
});
// use modified standard combo cell shows its popup on startEdit
firstName.setCellFactory(cb -> new ComboBoxTableCell<>(firstNames) {
#Override
public void startEdit() {
super.startEdit();
if (isEditing() && getGraphic() instanceof ComboBox) {
// needs focus for proper working of esc/enter
getGraphic().requestFocus();
((ComboBox<?>) getGraphic()).show();
}
}
});
Maybe not the cleanest solution to this problem, but I found a workaround to make the ComboBoxTableCells drop down its menu in just 1 click:
column.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell<Person, String> call(TableColumn<Person, String> column) {
ComboBoxTableCell cbtCell = new ComboBoxTableCell<>(cbValues);
cbtCell.setOnMouseEntered(new EventHandler<Event>() {
#Override
public void handle(Event event) {
// Without a Person object, a combobox shouldn't open in that row
if (((Person)((TableRow)cbtCell.getParent()).getItem()) != null) {
Robot r = new Robot();
r.mouseClick(MouseButton.PRIMARY);
r.mouseClick(MouseButton.PRIMARY);
}
}
});
return cbtCell;
}
});
PS: I know that this topic is a bit old, but I also stumbled upon this problem recently and could not find any working solution to it online. As I sad, it's not the cleanest workaround, but at least it does its job. ;)
I have a problem with override Custom Animation on my Fragment. I have on my Home view three buttons (First, Second and Third) and when I'm inside this views I want swipe between those view and I need swipe animation effect from left to right and from right to left etc..
For Example my SecondFragments looks like this:
[MvxFragment(typeof(MainViewModel), Resource.Id.content_frame, true)]
public class SecondFragment : BaseFragment<SecondViewModel>, View.IOnTouchListener
{
private Easter _easter;
protected override int FragmentId => Resource.Layout.fragment_second;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
_easter = new Easter(new KonamiCode());
var easyEgg = new CustomEgg("Easy")
.WatchForSequence(Command.SwipeLeft(), Command.SwipeRight());
_easter = new Easter(easyEgg);=
_easter.CommandDetected += cmd => DoSwipe(cmd.Value);
var coreLayout = view.FindViewById<LinearLayout>(Resource.Id.coreLayout);
coreLayout?.SetOnTouchListener(this);
return view;
}
private void DoSwipe(string swipeText)
{
if (swipeText.Equals("LEFT"))
{
Activity.OverridePendingTransition(Resource.Animator.slide_to_right, Resource.Animator.slide_from_left);
}
if (swipeText.Equals("RIGHT"))
{
Activity.OverridePendingTransition(Resource.Animator.slide_to_left, Resource.Animator.slide_from_right);
}
ViewModel.SwipeView(swipeText);
}
public bool OnTouch(View v, MotionEvent e)
{
_easter.OnTouchEvent(e);
return true;
}
}
Method ViewModel.SwipeView looks like:
public override void SwipeView(string swipeText)
{
if (swipeText.Equals("RIGHT"))
{
Close(this);
UserDialogs.Instance.Toast("RIGHT SWIPE!");
ShowViewModel<FirstViewModel>();
}
if (swipeText.Equals("LEFT"))
{
Close(this);
UserDialogs.Instance.Toast("LEFT SWIPE!");
ShowViewModel<ThirdViewModel>();
}
}
I tried Activity.OverridePendingTransition for this but it doesnt work. I tried something with TransactionManager but still doesnt work. I need just override animations only for these three view no for whole app.
For example my test project is HERE on github.
I tried Activity.OverridePendingTransition for this but it doesnt work.
OverridePendingTransition allows you to specify a custom animation when starting an activity from outside the Context of the current top Activity. That means OverridePendingTransition method only be called immediately after one of the flavors of startActivity(Intent) or finish() to specify an explicit transition animation to perform next. So when you swipe between those Fragment, it has no effect.
Difference between Animator folder and Anim fodler.
An animation resource can define one of two types of animations: Property Animation and View Animation.
Property Animation
File Location:
Resource/animator/filename.xml
SYNTAX:
The file must have a single root element: either <set>, <objectAnimator>, or <valueAnimator>. You can group animation elements together inside the <set> element, including other <set> elements.
View Animation
File Location:
Resource/anim/filename.xml
SYNTAX:
The file must have a single root element: either an <alpha>, <scale>, <translate>, <rotate>, or <set> element that holds a group (or groups) of other animation elements (even nested <set> elements).
Since your animation use translate, I suggest you put these xml file in Resource/anim folder.
I need just override animations only for these three view no for whole app.
Since your base Activity is MvxCachingFragmentCompatActivityas, you can override OnBeforeFragmentChanging method to set a custom transition animation.
public override void OnBeforeFragmentChanging(MvvmCross.Droid.Shared.Caching.IMvxCachedFragmentInfo fragmentInfo, Android.Support.V4.App.FragmentTransaction transaction)
{
//Replace this animation with your own animation.
transaction.SetCustomAnimations(
// Your entrance animation xml reference
Resource.Animation.abc_fade_in,
// Your exit animation xml reference
Resource.Animation.abc_fade_out,
Resource.Animation.abc_fade_in,
Resource.Animation.abc_fade_out);
base.OnBeforeFragmentChanging(fragmentInfo, transaction);
}
SetCustomAnimations set specific animation resources to run for the fragments that are entering and exiting in this transaction. The popEnter and popExit animations will be played for enter/exit operations specifically when popping the back stack.
FragmentTransaction setCustomAnimations (int enter,
int exit,
int popEnter,
int popExit)
EDIT :
Custom animation for every swipe between Fragment, for every Fragment, you could custom animation like this :
[MvxFragment(typeof(MainViewModel), Resource.Id.content_frame, true)]
public class SecondFragment : BaseFragment<SecondViewModel>, View.IOnTouchListener
{
private void DoSwipe(string swipeText)
{
if (swipeText.Equals("LEFT"))
{
FragmentManager.BeginTransaction()
.SetCustomAnimations(Resource.Animation.slide_from_left, Resource.Animation.slide_from_right)
.Replace(Resource.Id.content_frame,ThirdFragment.NewInstance(null))
.Commit();
}
if (swipeText.Equals("RIGHT"))
{
...
}
ViewModel.SwipeView(swipeText);
}
}
They best way you could achieve this is by using a ViewPager that contains your fragments. I implemented the exact thing yesterday, you can look at this example on how to use this with MvvmCross.
Good day, I have a problem when trying to remove tab's from a TabPane in JavaFX.
First thing I do I pass an ArrayList of String's with selected tab names from the list to the method which suppose to delete the corresponding by name tabs from the TabPane:
removeBranch.setOnAction(e->{
ArrayList<String>indexes=new ArrayList(list.getSelectionModel().getSelectedItems());
entryShifts.removeBranch(indexes);
list.getItems().removeAll(indexes);
});
Afterwards I try to delete them like this in the mentioned method:
public void removeBranch(ArrayList<String> branch){
pane.getTabs().removeAll(branch);
}
However it does not work, even if I do something like(Please notice I pass only the first name from the list):
public void removeBranch(ArrayList<String> branch){
pane.getTabs().remove(branch.get(0));
}
But if I do something like this, it works (here I only remove the first name from the ArrayList as well):
public void removeBranch(ArrayList<String> branch){
pane.getTabs().forEach(i -> {
if (i.getText().equals(branch.get(0))) {
Platform.runLater(() -> {
pane.getTabs().remove(i);
});
}
});
}
Why is this happening? Am I doing something wrong?
P.S the way I fixed it by looping through the ArrayList and passing each time a different name to the method:
indexes.forEach(i->{entryShifts.removeBranch(i);});
public void removeBranch(String branch){
pane.getTabs().forEach(i -> {
if (i.getText().equals(branch)) {
Platform.runLater(() -> {
pane.getTabs().remove(i);
});
}
});
}
Your problem is that pane.getTabs() returns a list of Tab, and you are trying to find Strings in it. You need a way to convert the strings (Tab names?) to the actual Tab nodes in the TabPane.
One such way is to have a Map<String, Tab> map which you initialize with the tab names and tabs, and then do something like:
List<Tab> tabs = branch.stream().map(map::get).collect(Collectors.toList());
pane.getTabs().removeAll(tabs);