I'm trying to do something very simple. I have 2 tabs, one showing information about the breed of a dog and the other is showing a list of reviews that people have written describing the breed. I would like to include the number of reviews that were written in the Tab as well so it would look like so:
Breed Info | Reviews(19)
The issue is that I need to download said review data first in order to see the number of reviews there is on the tab.
So what I thought of as a solution was to add the tab first in my BreedProfile.java (ShelockActivity)
ActionBar.Tab reviewTab= ab.newTab();
reviewTab.setText("Reviews");
mTabsAdapter.addTab(reviewTab, DogReviewFragment.class, null);
Then in my BreedProfileReviewFragment.java I would download the reviews then update the reviewTab text by doing so:
getActivity().getActionBar().getTabAt(1).setText("Reviews 10");
However, the above method does not exist according to the LogCat.
03-05 18:15:26.460: E/AndroidRuntime(1286): FATAL EXCEPTION: main
03-05 18:15:26.460: E/AndroidRuntime(1286): java.lang.NoSuchMethodError: android.support.v4.app.FragmentActivity.getActionBar
So my question is, is it possible to access the TAB from the FRAGMENT to change the TEXT property of the TAB after it is added? If it is, how can I achieve that?
NOTES:
I cannot download the reviews together with the breed information because the API's are separate.
The BreedProfile.java Originates from a list of Breeds in a ListActivity BreedList.java.
You can implement an Interface. so you can run the code at the parent activity of the fragment.
create your interface in your fragment like below:.
public interface OnQuestionSelectedListener {
void onQuestionSelected(int Position, boolean isFirstTime);
}
Implement it at your parent activity side like below:
public class YourParentActivity extends Activity implements YourFragment.OnQuestionSelectedListener{
#Override
public void onQuestionSelected(int position, boolean isFirstTime) {
// your code.
}
}
Call it from your fragment like below:
((YourParentActivityName)getActivity()).onQuestionSelected(position, true);
(You can make changes in the code as per your need)
It is a very good practice to transfer all your logic of fragment to its parent because what I believe is fragment use for your UI part and Interface provide us a good way to transfer the fragment message to the parent.Do all the activity at parent and transfer the result to corresponding fragment.
Hope this will help.
Related
I am using Appium for automating my Android app. I have a fragment to enter email and after verifying that second fragment with passwordfield will be loaded. I am able to find all elements in the first fragment; but elements in the second fragment can't be found using any of the methods like By, PageFactory + #AndroidFindBy. Can someone provide a help to sort this issue?
Use ExplicitWait method.
public static void ExplicitWait(MobileElement element){
(new WebDriverWait(driver,30)).until(ExpectedConditions
.elementToBeClickable(element));
}
then before you use element of second fragment call ExplictWait
ExplictWait(passwordField);
passwordField.sendKeys("your password");
I've been practising the MVP pattern in android.
My question is related to how to design the situation where you have a fragment with a custom layout.(see the below layout)
customlayout in fragment
There are 2 cardviews in the customlayout:
- if you click on the friends cardview, a dialogfragment will be displayed showing a customadapter whose data comes from fetching the local DB
- if you click on the others cardview, a dialogfragment will be displayed showing a customadapter whose data comes from a server.
You can check the people in this adapter and if you click OK, the images of the checked people will be displayed in the cardview
I have made the fragment a view as part of an MVP construct but I got stuck.
Is it a good idea to create the customlayout as an MVP construct as well? or not just the layout, but the cardviews, too?
And if yes, then these "nested" MVPs how would they send the data(the friends & the others) back to the host fragment/presenter when I click the Save Button?
Or am I overcomplicating this simple fragment-layout architecture?
Any suggestions are appreciated
The fragment implements the below View:
public interface CreateEventContract {
interface View extends BaseView<Presenter>{
void showStartDateDateDialog();
void showStartDateTimeDialog(LocalDate selectedDate);
void showPlaceMapActivity();
void saveButtonClicked();
boolean isActive();
}
interface Presenter extends BasePresenter {
void startDateDateDialog();
void startDateTimeDialog(LocalDate selectedDate);
void place();
void saveEvent();
}
}
and the CustomLayout:
customLayout gist
In that situation, I think one could be guided by the tablet MVP example on GitHub, the Android Architecture Blueprints.
Transferred to your case, your structure could be something like this
The views are reporting (e.g who's been checked) to a common Presenter, who is manipulating the views through their particular Presenter.
My activity has a spinner and an empty container where fragments should be added when selecting drop down items from spinner.
My plan was to try to make switch construction inside into override method "public void onItemSelected()", where each case represents one drop down item from spinner, and sets correct fragment into container, like this:
String itemSelectedFromSpinner = parent.getSelectedItem().toString();
switch (itemSelectedFromSpinner) {
case "first item": // First and second item put same fragment into container, but do other methods when used
case "second item": // my code
}
My other taught was to put it in if construction like this:
String itemSelectedFromSpinner = parent.getSelectedItem().toString();
if (itemSelectedFromSpinner.equals("first item") || itemSelectedFromSpinner.equals("second item")){
// my code }
Since I've never done something like this, and I believe you can understand from my question what needs to be done, tell me what is the best practice to do that.
Am I doing it right by putting a String itemSelectedFromSpinner into switch construction? Also if user selects one item and first fragment is loaded, when selecting other item will the first fragment disappear and put second fragment into container automatically? (sorry if this sound little silly to you, I have lack of experience with fragments)
Don't use string like "first item" in code directly - move them to string resources.
For components like spinner use Adapter. The concept of Adapters use widely in Android so it's a good idea to be familiar with it. And also it allow you to compare your data by some integer asigned id's, and not by strings (which is unefficient, slow and ugly - correcting string representation everywhere is hard).
To replace or add Fragments dynamically use FragmentManager. See the simple replace() / add() / commit() code
I'm not familiar with FragmentPagerAdapter, so this is going to be one of those questions that we (you) read the description critically.
Structure: I have a FragmentPagerAdapter (code below), that will hold two fragments at a time. The first displays book excerpts, and the second a list of book titles.
Goal: I want to achieve what is described in the title: the user can navigate to the second fragment in the pager, click on a title, and then I want to move the user back to the first fragment and tell the first fragment to update the text. The first fragment has a triggerRefresh method for that.
Code: I believe my problem happens because of the way FragmentPagerAdapter reuses/creates the Fragments (which I don't understand). This is my class:
static class MyFragmentPagerAdapter extends FragmentPagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return new ExcerptsFragment();
case 1:
return new BookListFragment();
default:
throw new IllegalArgumentException("not this many fragments: " + position);
}
}
}
This is how I created the relevant members:
ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
MyFragmentPagerAdapter mFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mFragmentPagerAdapter);
And this is what I've tried elsewhere in my Activity, when I receive the callback from the book titles Fragment with the title selected:
mViewPager.setCurrentItem(0); // back to excerpts screen page. It's OK.
// Here's the problem! How to identify the fragment 0
// to ExcerptsFragment and call its triggerRefresh()?!?
Series of problems:
Calling the adapter's getView() won't work because it will return a new instance of ExcerptsFragment, which is not the one currently attached (as expected, throws the exception).
I've seen many people here (example) just storing fragments in the getView(). Is that right? Because by looking at the official examples, seems like an anti-pattern to me (defeat the automatic reference by holding the items). And that is also the opinion here and here (and looks right to me).
Any suggestions? I wouldn't be surprised if I'm not understanding all of this one bit...
Disclaimer: Although this had worked perfectly fine for me before, you should be aware of the classic pitfalls of depending on internal, private behavior. While I wrote tests that would eventually warn me if the internal implementation changed, I have since moved on to greener pastures. And you should, too. As such, the value of this question and its answer is only historical, in my opinion.
Sorry about that question, I think it was the hour.
To solve that problem, I implemented this solution as is. Seems to work just fine. So, I believe it was just a matter of finding the (currently attached) fragment instance by figuring out how its Id is named. The link above explains how it's made.
I opted to answer my own question instead of deleting it because I believe novices like me on these pagers will benefit from a "real case scenario". Most of the answers I've seen talk most about the theory, which is the right way BTW... but without a real example to work on sometimes people like me get lost.
Anyway, here is the last piece of code that I needed (the commented part above):
int n = 0;
mViewPager.setCurrentItem(n); // in the question I had stopped here.
ExcerptsFragment f = (ExcerptsFragment) ContainerActivity.this
.getSupportFragmentManager().findFragmentByTag(getFragmentTag(n));
f.triggerRefresh();
// ... below the helper method: used the solution from the link.
private String getFragmentTag(int pos){
return "android:switcher:"+R.id.pager+":"+pos;
}
So, I'm having a feeling that this is a robust solution, because I'm not holding any references to fragments (thus risking the references being outdated). I kept my own code at a minimum, therefore minimizing the chances of me doing something stupid.
Of course, if you have something to add, to show us, to tell what is wrong in doing it or what can be improved, I'll be glad to hear from you.
I searched for a solution to this problem a while myself. Your approach in principle works, but it will break your code if ever the code of the fragment tag creation in the Android base class implementation changes. This is a quite nasty dependency!
A more elegant approach would be to turn the problem around and keep an instance of your base activity in your fragment. Implement a setter for the tag in your activity and call that inside the fragment upon creation - the tag there is simply available with getTag().
An example implementation can be found here.
I solved this problem by using WeakReferences to the fragments upon creation. See : https://stackoverflow.com/a/23843743/734151
If you find anything wrong with this approach, please comment.
Everything I've read about Intents talks about using them to push data, or to start one Activity from another Activity. I want to pull data from an Activity that's already running.
The Tab Layout tutorial at http://developer.android.com/resources/tutorials/views/hello-tabwidget.html illustrates what I want to do. (My app is doing some engineering calculations instead, but the tutorial code provides a good analogy to my app.) The tutorial creates an app with three tabs, and each tab hosts a separate activity.
To expand on the example in the tutorial, suppose I select an artist in the Artists tab/activity. I want to be able to select the Albums tab/activity and have it display all the albums featuring that artist.
It seems to me that I need to use an Intent to do this. All of the tutorials I've found assume that I would create a "See albums" Button in the Artists tab/activity, and that pressing the Button would execute an Intent that starts the Albums activity and passes artistName.
I DO NOT want to create that Button. Real estate on the Artists layout is precious, and I have a perfectly good Albums tab, AND the HelloTabWidget activity already contains an intent to create the Albums tab.
Besides, a user will want to skip back and forth between Album and Artist in order to change artist selections, and the tabs are a perfectly good way to do this. There's no need to complicate the UI with another button.
So how can I have the Albums activity PULL artistName from the Artists activity when the Albums tab is selected (or the Albums layout is displayed), rather than have the Artists activity START Albums and PUSH the artistName?
Equivalents I can think of from other programming worlds:
Global variables. Discouraged in Android devt, right? And if they do exist, what are they called?
A getter, like artistName = Artists.getArtistName(); . I get the feeling that it's not that easy.
Writing to, and reading from, a file - that is, mass storage or non-volatile memory. I don't need the artistName value to be permanent. It will be reset to null every time the user launches the application.
So how is it done in the Android world? Do I use an Intent - and if so, how?
Global variables were the right answer.
I thought Java discouraged their use, but a couple of links that appeared in the "Related" links on the right margin of this window mentioned them directly. One was "Android: How to declare global variables?" and the other was "how to pass value betweeen two tab in android". Both pointed to the Application Class as the place to define global variables and methods. Armed with this new knowledge, I found an article called "Android Application Class" on the Xoriant blog that expanded on the StackOverflow answers.
It's best to review those three links first. I need to add some tips to what those authors have said.
Your Application class has to be in its own separate file. (That might be a "duh" to some people, but not to everybody.) Here's a good framework for an example called Something.java:
public class Something extends Application {
// Put application wide (global) variables here
// Constants are final, so they don't have to be private
// But other variables should be declared private;
// use getters/setters to access them
public final boolean FEET = false;
public final boolean METERS = true;
private boolean units = FEET;
#Override
public void onCreate() {
super.onCreate();
// Put any application wide (global) initialization here
}
// Put application wide (global) methods here
public boolean getUnits() {
return units;
}
public void setUnits(boolean whichOne) {
units = whichOne;
}
}
I'm using Eclipse with the ADT plug-in, in Windows XP. Eclipse doesn't always behave properly if you edit XML code directly, so it's best to open AndroidManifest.xml, then select the Application tab and enter your application name in the Name field. You don't need to put a dot or period in front of the name. Just type in the name of your class, like "Globals" or "MyApplication" or whatever. (Note that this is the default application in your Manifest. You don't have to create a separate <application></application> tag.
This step may not be necessary on an actual Android device, but it was necessary for the emulator: you need to use the getApplicationContext() command in every onCreate() and every method that will be accessing the global variables and methods. I tried to put it outside of onCreate() with the rest of my activity wide variables, and it didn't work. Putting it inside every method seems wasteful, but both the emulator and the Android device work fine with it that way. Here's a sample showing how I used it:
public void fooBar() {
// Access to global variables and methods
final Something s = (Something)getApplicationContext();
// ...
// This next line demonstrates both a global method and a global variable
if (s.getUnits() == s.FEET) {
// do something with feet
} else {
// do something with meters instead
}
// ...
}
Those were the only hiccups I encountered. The three references that I have listed, taken together, are otherwise pretty complete.