Basically, I have a list of detail fragments and each one represents a list of peers of that phone. When one of these DeviceDetails is tapped on, a selection of buttons and text appears. Which of these buttons and text appears depends on the status of the phone; it is either connected to the phone being used, or it is available for connection.
I currently use the fragment's onCreateView to make 3 buttons appear. 1 of these buttons should appear every time, and the other 2 alternate depending on the connected/available state.
I am trying to figure out which overridden fragment method should handle the changing UI's. It should just be a case of if statements (or maybe switch statements?) but I am not sure where to place these?
Well if let's say you have an Activity and it is hosting all these Fragments.
I assume there is some event that triggers this state to happen, maybe in that Activity
public void onSomeEventThatICareAbout(EventDetails deets) {
Fragment fragment = getFragmentManager().findFragmentById(R.id.my_fragment_with_buttons);
if (fragment != null) {
((MyButtonsFragment)).disableButtons(deets);
}
}
Basically just treat the fragment like any other component and call methods on it based on events like normal, whether it is an onClick(), a AsyncTask callback, or whatever. Just call the function right on the fragment.
Define your own way for your fragment to do what you want,
public void disableButtons(EventDetails deets) {
View view = getView();
view.findViewById(R.id.button1).setEnabled(false);
view.findViewById(R.id.button2).setEnabled(false);
}
Related
I know this is somewhat of a design question but I do have specific questions for it. I'm trying to understand how to handle a situation like this one:
Let's say I have a RecyclerViewFragment which loads a RecyclerView containing a bunch of Toy objects.
In one situation: Maybe this RecyclerViewFragment is part of a ViewPager on main display. There is a FloatingActionButton add-button present over this RecyclerView. You click the + button and you can add a new Toy to the list. Or you can click a Toy from the list directly and a floating menu pops up with Edit/Delete buttons, and pressing Edit lets you edit the Toy's details in a DialogFragment, or clicking Delete removes it from the RecyclerView.
In another situation: Now I am in a separate part of the app where I want to choose toys to use. So I press a button and a DialogFragment appears with a RecyclerView of Toys. I can click a Toy and it'll be added to my cart.
It seems like I should be re-using the same RecyclerView code in both situations, since they both involve a list of the same Toys. The only difference is that in one situation, I can add Toys and edit Toy details, and in the other situation, there is no Add button and clicking on a toy does something different (adding to a cart as opposed to bringing up an Edit/Delete dialog).
Is this the correct way to handle this:
Communication from Fragment to Activity: Interfaces? Have the RecyclerViewFragment, in the onAttach method, assign a listener of my design to the context. Then when a row of the RecyclerView is pressed, the callback is triggered. Now the underlying Activity can decide what to do with that press -- show the Edit/Delete dialog in one situation, add the Toy to a Cart in the other situation. Either way, the click item sends the Toy to the calling Activity so it can decide what to do with it.
Communication from Activity to Fragment: Now what about the situation with the Add button? This Add button would not be intrinsically part of the RecyclerViewFragment, so when I click Add, it would bring up the details dialog box where I can give the Toy details, and then press OK to add it. So somehow I have to transfer this new Toy to the Fragment to have it added to the RecyclerView. Would I simply do something like this:
RecyclerViewFragment recyclerViewFragment = (RecyclerViewFragment ) getSupportFragmentManager().findFragmentByTag("TOY_RECYCLERVIEW");
recyclerViewFragment.getNewToyAndRefreshList(newToy);
and then in the RecyclerViewFragment:
public void getNewToyAndRefreshList(Toy newToy) {
toyList.add(newToy);
Collections.sort(toyList); //Toy has Comparable implemented, sort by name
recyclerViewAdapter.notifyDataSetChanged();
}
Am I on the right track? Is there a different way to fix this situation?
That's certainly a design question, but IMHO there's a very specific issue on it and I believe it's a good question (reason I'm answering), but that also means other developers might have other approaches to solve the issue.
1. that is a totally fair and acceptable approach to it. You let the fragment be simple UI element and let someone else (the activity) implement the click behavior.
For this approach remember to code it only against the interface. That means, don't cast it to your activity. For example:
// do this
toyClickListener.onToyClicked(toy);
// don't do this
((MyActivity)getActivity()).onToyClicked(toy);
That way you keep the "simple UI element" be completely unaware of who is implementing the behavior.
2. IMO for this kind of scenario (specially on RecyclerView.Adapter) the best thing to do is to forget the UI and only focus on the data. And how speciafically you implement this, will vary on what is your data source.
But the base idea is that you have somewhere a data repo (DB?) and anyone using data from there, should subscribe to changes to it.
So you override RecyclerView.Adapter.registerAdapterDataObserver and unregisterAdapterDataObserver add the subscription/listener code, something like that:
#Override registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
super.registerAdapterDataObserver(observer);
db.subscribe(this, toyList);
}
#Override unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
db.unsubscribe(this);
super.unregisterAdapterDataObserver(observer);
}
#Override public void onDbDataUpdate(new Data comes here){
update the data, and call .notifyDataSetChanged();
}
that way once the FAB + and then dialog is clicked the new Toy gets added to the DB and the adapter gets "automatically" notified.
So if this data comes from a SQLite you can call on the cursor registerContentObserver if it's a RealmDB you'll use addChangeListener, even Android databinding libraries have a ObservableList
I have implemented a FAB's setOnClickListener() in MainActivity and it is working without any issue.
I have 8 different Fragments (one shows a WebView, anoother loads a RecyclerView, etc..) based on the selection from the navigation drawer.
The FAB appears and its current action is playing a default song, which is working fine.
Now, from my 8 different Fragments, 3 Fragments show a different song list.
Since the FAB appears as default, I want to use the FAB as a play Button.
So, if the user is in one Fragment and selects a mp3 and clicks the FAB, it should play that song.
That means that I should somehow overwrite MainActivitiy's setOnClickListener with one implemented in the Fragment.
In the same way, I have other 2 Fragments which show some mp3 song list. By selecting an mp3 in that ones and clicking the FAB, the Fragment should listen for that FAB action.
Basically, the FAB should override its action and take inputs from the current Fragment.
One way I can think of is when fab.setOnClickListener is called, I will check the current Fragment and act according to that.
But the problem is to play the song, I am creating an Intent with many parameters and calling startservice.
If I can do something in the Fragment itself, I can create an Intent, bundle the required arguments etc.. on setOnClickListener().
Any suggestions to resolve my confusions is highly appreciated.
I tried below way and it is working fine. Adding it as answer to my question since it is working.
Approach:
Implemented a function with common name playAudio() in all fragments where media suppose to play. In MainActivity, onClick() of floating action button, I am checking the instance of current fragment. If it is among 3 of 8 which needs to play respective audio when fab click, i am calling the fragment's playAudio().
for rest of fragments, doing something else.
Below is code snip:
#Override
onClick(View v) {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container_body);
if (fragment != null && fragment.isMenuVisible()) {
if (fragment instanceof FragmentAudioMessage) {
((FragmentAudioMessage) fragment).initPlay();
} else {
//do something else.
}
}
This way it is working differently for each fragment.
Hope this will help others.
I'm a beginner in Android, so I apologize for the mistakes and I'd appreciate any constructive criticism.
I'm writing a basic application with a ListView of images, and when the user clicks on an item in the list, I want to display that image in a ViewPager, where the user can swipe back and forth to browse the whole list of images. Afterwards when the user presses the back button, I want to switch back to the ListView.
I manage the business logic in the MainActivity, which uses MainActivityFragment for the ListView and ImageHolderFragment for ViewPager.
The simplified code so far is as follows:
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListItems = new ArrayList<>();
mListItemAdapter = new ListItemAdapter(this, R.layout.list_item, R.id.list_item_name, mListItems);
mListView = (ListView) findViewById(R.id.list_view_content);
mListView.setAdapter(mListItemAdapter);
mDeletedListItems = new ArrayList<>();
mViewPager = (ViewPager) getLayoutInflater().inflate(R.layout.image_display, null, true);
mImageAdapter = new ImageAdapter(getSupportFragmentManager(), mListItems);
mViewPager.setAdapter(mImageAdapter);
mViewPager.setOffscreenPageLimit(3);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mViewPager.setCurrentItem(position);
setContentView(mViewPager); // TODO: this is very wrong!
}
});
loadImages();
noContentText = (TextView) findViewById(R.id.no_content_text);
if (mListItems.isEmpty()) {
noContentText.setText(R.string.no_images);
} else {
mImageAdapter.notifyDataSetChanged();
}
}
Although this does work to some extent, meaning that it manages to display the ViewPager when an item in the list is clicked, there are two things about it ringing the alarm bells:
I've read that calling setContentView() for the second time in the same class is pretty much a sin. Nobody explained me why.
The back button doesn't work in this case. When it's pressed, the application is terminated instead of going back to the list view. I believe this is connected to the first point.
I would appreciate any help, explanations if my idea is completely wrong, and if my case is hopeless, I'd like to see a successful combination of ListView and ViewPager with transitions between each other.
Your activity already has R.layout.activity_main set as content view, which rightly displays the list view - that's what the responsibility of this activity is as you defined it. If we want to change what's shown on the screen, we should use a different instance of a building block (activity or fragment) to display the view pager images.
To say the least, imagine if you wanted to change the view to a third piece of functionality or UI, or a fourth... it would be a nightmare to maintain, extend and test as you're not separating functionality into manageable units. Fields that are needed in one view are mixed with those needed in another, your class file would grow larger and larger as each view brings its click listeners, callbacks, etc., you'd also have to override the back button so it does what you want - it's just not how the Android framework was designed to help you. And what if you wanted to re-use UI components in different contexts whilst tapping in to the framework's activity lifecycle callbacks? That's why fragments were introduced.
In your case, the list view could continue to run in your MainActivity and in your click listener, onItemClick you could start a new activity that will hold a viewPager:
Intent i = new Intent(MainActivity.this, MyLargePhotoActivityPager.class);
i.putExtra(KEY_POSITION, position);
// pass the data too
startActivityForResult(i, REQUEST_CODE);
Notice how you could pass the position to this activity as an int extra, in order for that second activity to nicely set the viewPager to the position that the user clicked on. I'll let you discover how to build the second activity and put the ViewPager there. You also get back button functionality assuming your launch modes are set accordingly, if needed. One thing to note is that when you do come back to the list View, you'd probably want to scroll to the position from the view pager, which is why you could supply that back as a result via a request code. The returned position can be supplied back to the list view.
Alternatively, you could use the same activity but have two fragments (see the link further above) and have an equivalent outcome. In fact, one of your fragments could store the list view, and the second fragment could be a fullscreen DialogFragment that stores a viewPager, like a photo gallery (some details here).
Hope this helps.
I've read that calling setContentView() for the second time in the
same class is pretty much a sin. Nobody explained me why.
Well, you kind of get an idea as to why.
When you use setContentView() to display another 'screen' you do no have a proper back stack.
You also keep references to Views (like mListView) that are not visible anymore and are therefore kind of 'useless' after you setContentView() for the second time.
Also keep in mind orientation changes or your app going to the background - you'll have to keep track of the state that your Activity was in which is way more complicated than it has to be if you have one Activity that does two different things.
You won't be arrested for doing things like you do right now, but it's just harder to debug and keep bug free.
I'd suggest using two different Activities for the two different things that you want to do, or use one Activity and two Fragments, swapping them back and forth.
If you insist on having it all in one Activity you need to override onBackPressed() (called when the user presses the back button) and restore the first state of your Activity (setContentView() again, pretty much starting all over).
Just a general question about working with Fragments and Activitys for android development: where does the business end of the functional code go for Fragments loaded into an Activity dynamically? (i.e. a fragment's OnClickListeners, OnCheckedChangedListeners, button logic methods...)
Do they go in the Fragment class, or the Activity class?
All the GUI logic for views attached to a fragment should be contained inside the fragment itself.
Thus a fragment should be as self contained as possible.
You can, though, if necessary do callbacks to your activity based on fragment GUI interaction. This can easily be done like this inside the fragment:
#Override
public void onAttach(Activity activity) {
if (!(activity instanceof SherlockFragmentActivity)) {
throw new IllegalStateException(getClass().getSimpleName()
+ " must be attached to a SherlockFragmentActivity.");
}
mActivity = (SherlockFragmentActivity) activity;
super.onAttach(activity);
}
In this specific case the reason for gaining a reference to SherlockFragmentActivity is to gain access to the support menu inflater mActivity.getSupportMenuInflater(), hence the construction can of course also serve to gain information from the underlying activity.
This probably depends on how much the Fragment's functionalities have in common, and how many, let's say Buttons, have to be handled.
I personally (and it's probably most common practice) handle onClick(...) events separately for each Fragment, meaning that I let each Fragment implement it's own OnClickListener.
Furthermore, when handling everything through the Activity, probably not all the components that react to click-events are in memory at all times and can be reached via findViewById(...), depending on which Fragment is currently displayed and how your user-interface is built up in general.
they always in fragment class because fragment is one type of component in android which we can reuse it. if we put onclick and oncheckchanged in activity then what meaning of reusing that component??
for more information about please go through following step:
Link 1 for basic level of information about fragment and how to handle them
Link 2 for dealing with multi pane fragment
Standard site for fragment
It depends:
If fragment can handle logic which is self sufficient(complete) then that code can be handled by fragment. e.g. on click call phone number.
If fragment have UI whose action is activity specific, then you want to add listener in activity.
e.g. master detail view like email client, on tablet user click on title fragment1 which have list of email titles, then handler on click in activity can show detail fragment2 in activity.
In all you want to keep fragment reusable.
I am new in android. I often use Activity to change from one screen to another screen with other function. Example from Home Page to Popular page. After that, i know about fragment but i never use it before. So, if i have a application with multi tab on a screen, not use TabHost here. Function of every tab very diffrent, ex : tab Home, tab Popular, tab News, tab Profile ... like Instagram App. I must use that
Activity to change Screen to another Screen, it means: i have Home Activity, Popular Activity, ... and change Activity when change Sreen. Each Activity have each layout.
Use fragment within one Activity. We have multi fragment, example HomeFragment, Popular Fragment... chang replace Fragment when change Screen.
What way is better ?
I want to ask when use only phone screen. ( small size screen, not for tablet).
It's important to think of Android devices as more of a spectrum, than clear "phone" vs. "tablet" buckets. There are many instances where you might want to show more information on screen on medium and large screens. Sometimes, this translates to showing two "Activities" at once.
Using Fragments requires little overhead, but adds measurable flexibility, especially when considered early in the development process. If you use Fragments properly, adapting to larger screens is extremely simple. However, there are a few "gotchas" that may make Fragments appear to be more daunting that they actually are:
Fragment classes must always be declared public (if it's a nested class, it must be static).
In the parent Activity (or FragmentActivity), only add the root Fragment if savedInstanceState == null. If you are managing the state of your Fragment properly, everything is handled for you (scroll position, EditText values, etc).
The parent Activity must call through to onSavedInstanceState in order for the Fragment to properly restore it's state.
setRetainInstance(true) should only be used for "headless" Fragments. This is when you use a Fragment that has no UI, and isn't added to the back stack, which is typically used to do life-cycle dependent work.
Fragments declared in XML cannot be used in a FragmentTransaction (and vice-versa).
Think of a Fragment as a modular view, that provides hooks (callbacks) to it's Activity when something important happens. The Activity decides, based on the available space, whether to launch a new Activity, or show a new Fragment.
You can use either way. If you decide to use the Activity solution, create a base activity class that contains all the Tab functionality. You don't want to implement that in every Activity over and over again.
public class BaseActivity extends Activity {
#Override
public void onCreate(...) {
// Init tabs
}
// Methods for tab handling
}
Every Activity (Popular, Profile, Home, ...) extends BaseActivity
public class PopularActivity extends BaseActivity {
#Override
public void onCreate(...) {
super.onCreate(...);
// Init only the popular activity elements here
}
}
This way you implement the tab functionality only once and get it in every activity.