I am trying to develop an android app in android studio and I keep writing more and more fragments in the mainActivity class. My question is how to separate these to another file? Probably I am doing it in a wrong way and if so could somebody show me how should I do it?
My code:
public class MainActivity extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
.
.
.
public static class PlaceholderFragment1 extends Fragment {...}
public static class PlaceholderFragment2 extends Fragment {...}
public static class PlaceholderFragment3 extends Fragment {...}
}
Since they are static inner classes, AndroidStudio can easily refactor these for you. Select PlaceholderFragment1 (just put the text cursor on it) and press F6 (or right click the fragment name->refactor->move) and select `Move inner class [fragment name] to upper level', change the name and package if you want and hit refactor.
Having a static inner class for a Fragment is fine (will work technically), but if you want to reuse the fragment in another activity, best to refactor it out. Also, most people like to keep classes as small as possible, and if the function of the fragment is logically separate from the activity there is little reason to keep it as an inner class.
You could without problem write them as separate classes in the same package and then just use them. Otherwise write them in a separate package and import them as any other class.
First you will need to create a new class (new class file)
NOTE: You will ned to create a class like this for every fragment you have to define the fragment logic.
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.article_view, container, false);
}
//logic here
}
Then you can use it in your MainActivity which extends FragmentActivity like:
MyFragment fragment= new MyFragment();
or if exists
fragment= (MyFragment)getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
You can set your fragments to view with:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.frameLayout, fragment, FRAGMENT_TAG);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
Related
I am building an app with 2 fragments. The 1st fragment has an ImageView and 2 TextViews and the 2nd fragment has a set of buttons and EditTexts. In the 2nd fragment, I have a button called "Save". When this button is clicked, I want to download the image inside my 1st fragment to my device folder (The ImageView, the URI, the bitmap and canvas objects are all in the 1st fragment).
Because fragments can't communicate with one another directly, I was thinking of doing this with an interface. What I did was:
I've declared my interface in the 2nd fragment
Applied the logic of the interface's method in the MainActivity (which is the shared activity between the 2 fragments)
Fed the necessary parameters for the method in the 1st fragment.
Didn't work
But I don't think that this was the correct order so it's no surprise that it didn't work. How do I apply the interface in a way that a button click in the 2nd fragment downloads the image in the 1st fragment?
You could try one of these three options:
1.) Using callbacks to communicate via the activity
As shown in this article, you can define an interface in fragment 2 which is then called when the button is clicked. Your activity (which holds fragment 2) provides an implementation for that interface in which the activity calls a method in fragment 1 for downloading the image. For example:
Fragment 1 providing the download method
public class Fragment1 extends Fragment {
OnButtonClickedListener mCallback;
public void startImageDownload() {
// Download the image
}
// ...
}
Fragment 2 defining and calling the interface
public class Fragment2 extends Fragment {
OnButtonClickedListener mCallback;
// Some kind of init method called by onCreate etc.
private void init() {
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Call the listener if present
if(mCallback != null) {
mCallback.onButtonClicked();
}
}
});
}
public void setOnButtonClickedListener(Activity activity) {
mCallback = activity;
}
// Container Activity must implement this interface
public interface OnButtonClickedListener {
public void onButtonClicked();
}
// ...
}
The Activity reacting on the Button click and calling the download method
public static class MainActivity extends Activity implements Fragment2.OnButtonClickedListener {
Fragment1 mFragment1;
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof Fragment2) {
// Register the listener
Fragment2 fragment2 = (Fragment2) fragment;
fragment2.setOnButtonClickedListener(this);
} else if (fragment instanceof Fragment1) {
// Keep a reference to fragment 1 for calling the "download" method
mFragment1 = (Fragment1) fragment;
}
}
#Override
public void onButtonClicked() {
// Handle the button click
mFragment1.startImageDownload();
}
}
This way you avoid linking the fragments together, instead you have the activity beeing a loose "connection" between fragment 1 and fragment 2.
This is just an exmple, i did not had time to test it. But i hope it helps.
2.) Using a local broadcast
I would recommend using the LocalBroadcastManager for sending a broadcast in fragment 1 (that the button was clicked) and receiving it in fragment 2 (downloading the image). Here is a great article about local broadcasts.
3.) Another option is to use ViewModel
The ViewModel was recently introduced by the new Android Jetpack and "is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations." (from ViewModel Overview).
It can also be used to share data between two fragments: Your activity basically holds the ViewModel and the fragments (inside that activity) can get access to it by calling: ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
I think your scenario you could use Observers or some kind of LiveData to react to the button-click via a ViewModel.
Thanks to #Elletlar for helping me improve my answer.
In Android studio, when I create a new Activity with a fragment, it makes a inner static class in my activity.
The problem is that, because it's a static and inner class, I can't do much with it.
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//ArrayList<String> contactList=new ArrayList<String>();
View rootView = inflater.inflate(R.layout.fragment_search, container, false);
//contactList.add("One test);
//ArrayAdapter <String> arrayAdapter= new ArrayAdapter<String> (getActivity(),R.layout.list_item_forecast,
// R.id.list_item_forecast_textview,contactList);
//ListView lv= (ListView)rootView.findViewById(R.id.listview_forecast);
//lv.setAdapter(arrayAdapter);
return rootView;
}
}
The code I commented is what I added, and works.
But I would like to do more: add listener to that ListView items, etc..
If Andoid studio put the placeholderFragment as a static inner class, it means that it's a good and easy way, but I don't know how to continue it.
So, to be more specific, for example, How can a put a listener for the listview items?
Thanks a lot.
The PlaceholderFragment should not be used. It is a placeholder only. If you add your own fragments, you should finally delete the place holder.
You should create a Fragment class of your own (via new->fragment) and add your logic to it. In your activity, implement the methods to be called when an item in your drawer is clicked. Then, implement the logic to activate your fragment.
If you need more information or code examples, please let me know.
I'm following steps on
http://developer.android.com/training/basics/fragments/creating.html#AddInLayout
and I can't seem to figure out why they stated, "One difference when creating a Fragment is that you must use the onCreateView() callback to define the layout." but didn't use onCreateView for the HeadlinesFragment, only the ArticleFragment. They both seem to set a layout so I don't understand why onCreateView is not used. Also, is the fragment_container only the xml file? I'm guessing it's only used in the MainActivity. Code is posted on
Android Fragment Basics Tutorial
except code for articleFragment which I posted below. So far, I think I understand that you have a class that extends fragmentactivity that holds fragment container and methods to switch out other fragments. Then you have the fragments that are classes that extends fragment or listFragment? However, the site,
http://developer.samsung.com/android/technical-docs/Using-Fragments-to-Build-User-Interfaces-in-Android
shows examples of activities that DONT extend fragment activity but rather just activity and it's supposed to hold the other fragment activities.
This site:
http://developer.android.com/reference/android/app/Fragment.html
showed more information on fragments but the lifecycle doesn't clarify when to use OnCreate vs onCreateView, I'm thinking it's something in regards to the layouts but it states that onCreate starts fragment too so I'm not positive on which to use.
THANKS!!!!!
package com.example.android.fragments;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class ArticleFragment extends Fragment {
final static String ARG_POSITION = "position";
int mCurrentPosition = -1;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// If activity recreated (such as from screen rotate), restore
// the previous article selection set by onSaveInstanceState().
// This is primarily necessary when in the two-pane layout.
if (savedInstanceState != null) {
mCurrentPosition = savedInstanceState.getInt(ARG_POSITION);
}
// Inflate the layout for this fragment
return inflater.inflate(R.layout.article_view, container, false);
}
#Override
public void onStart() {
super.onStart();
// During startup, check if there are arguments passed to the fragment.
// onStart is a good place to do this because the layout has already been
// applied to the fragment at this point so we can safely call the method
// below that sets the article text.
Bundle args = getArguments();
if (args != null) {
// Set article based on argument passed in
updateArticleView(args.getInt(ARG_POSITION));
} else if (mCurrentPosition != -1) {
// Set article based on saved instance state defined during onCreateView
updateArticleView(mCurrentPosition);
}
}
public void updateArticleView(int position) {
TextView article = (TextView) getActivity().findViewById(R.id.article_fragment);
article.setText(Ipsum.Articles[position]);
mCurrentPosition = position;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the current article selection in case we need to recreate the fragment
outState.putInt(ARG_POSITION, mCurrentPosition);
}
}
If you look closely at the code you will see a difference between HeadlinesFragment and ArticleFragment:
public class HeadlinesFragment extends ListFragment
public class ArticleFragment extends Fragment
The difference of course is that HeadlinesFragment is a ListFragment rather than just a plain ol' Fragment. ListFragment already implements a default onCreateView() that loads a layout with a ListView. If you want to override the default behavior, you can write your own onCreateView() method to make a more sophisticated layout.
I learned that since the fragment lifecycle is tied to the activity onCreate creates both the activity and the fragment. onCreate is used to set activity layout where onCreateView sets the fragment layout.
the reason headlinesFragment didn't have onCreateView is because since it's a list it has a predefined layout and thus doesn't need to be overridden.
I'm trying to create an activity that adds a dynamic fragment at runtime. From that fragment I want to be able to open six other fragments on button click. [Going to use a case to implement this most likely]
Think of it as a windows 8 UI; with 6 buttons, each one opens a new fragment.
Unfortunately I have no idea how to go about this. I can't seem to get the button to pass data back to the main activity. I've also lost quite a bit of my code due to a git mishap. Here's what I recreated.
If you have any tips on coding style, syntax, java, OO- those are all welcome too. I'm coming from a C background. My end goal would be to create a replaceFragment(Frag) method for some easy syntactic sugar later on. Though I couldn't implement that with any success so far.
Another small question with fragments - I'm trying to add them dynamically at run-time - do I need to create all of them at run time? So each one needs a .add [Drink fragment, Menu fragment] or do I just need to do the .replace
SingleFragmentActivity.java
public abstract class SingleFragmentActivity extends FragmentActivity{
protected abstract Fragment createFragment();
FragmentManager fm = getSupportFragmentManager();
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //Lock screen orientation for app
Fragment frag = fm.findFragmentById(R.id.fragment_container);
fm.beginTransaction()
.add(R.id.fragment_container,frag)
.commit();
}
}
Customer_Activity.java
public class Customer_Activity extends SingleFragmentActivity {
public static Context appContext;
#Override
protected Fragment createFragment() {
return new CustomerSelectionFragment();
}
}
CustomerSelectionFragment
public class CustomerSelectionFragment extends Fragment implements OnClickListener{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.customer_selection_fragment, container, false);
//Buttons should be placed here?
Button btnDrink = (Button) v.findViewById(R.id.Drink);
btnDrink.setOnClickListener(this);
Button btnMenu = (Button) v.findViewById(R.id.Menu);
btnDrink.setOnClickListener(this);
return v;
}
//implement the onClick method here
public void onClick(View v) {
// Perform action on click
switch(v.getId()) {
case R.id.Drink:
//Not sure how to pass "Create Drink Fragment to activity?
break;
case R.id.Menu:
//Pass Create Menu fragment to activity?
break;
}
}
}
Totally ok with people editing my post for good-faith reasons [clarity, etc].
Any communication between fragments should be done via activity . Here is the link to developers site http://developer.android.com/training/basics/fragments/communicating.html , the tutorial is about communicating between fragments and pretty much explains everything.
I'm trying to create a fragment class following from this:
And they give you the code for a fragment class, but I'm getting
ExampleFragments.java
package com.example.learn.fragments;
public static class ExampleFragments extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.blue_pill_frag, container, false);
}
}
But I'm getting an error on line 9, where I declare the name of the class.
Illegal modifier for the class ExampleFragments; only public, abstract & final are permitted
I'm sure it's something basic that I'm not understanding, thanks.
You can't have a static top-level class. Change
public static class ExampleFragments extends Fragment {
to
public class ExampleFragments extends Fragment {
In the example you're following, the static modifier is used because the Fragment is a nested class.
To get rid of the error you can include that fragment into an existing class, or remove the static modifier.