I've tried adding splash screens to my app, and while they do show before the main activity, this screen also always shows no matter what:
Here is the code for MainActivity:
public class MainActivity extends Activity implements WelcomeFragment.StartQuestions, QuestionFragment.QuestionsAnswered {
#Override
protected void onCreate(Bundle savedInstanceState) {
Parse.enableLocalDatastore(this);
Parse.initialize(this, "***************************", "*****************************");
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
loadingIcon = (ProgressBar) findViewById(R.id.loadingIcon);
loadingIcon.setVisibility(View.GONE);
checkInternetConnection();
showWelcomeScreen();
}
}
showWelcomeScreen():
public void showWelcomeScreen(){
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragmentContainer);
if (fragment == null){
fragment = new WelcomeFragment();
getFragmentManager().beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
} else {
fragment = new WelcomeFragment();
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.commit();
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/loadingPanel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" android:id="#+id/loadingIcon"/>
</RelativeLayout>
In MainActivity, I have already specified the removal of the top bar:
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
The app itself doesn't have the bar. Any activity that I place as being default in the manifest (acting as a splash screen) is nevertheless proceeded by the screen shown above when the app is launched.
You can't. Android will always attempt to show something using only the attributes of your theme before your Activity actually loads.
In fact, the correct way to build a splash screen involves taking advantage of that fact and customizing your theme such that what displays during this time is your splash screen.
Related
I am currently trying to create an app having a splash screen, a login screen and a register screen.
The only way to achieve exactly what I need is through fragments. Though when I try to transition from the splash screen to the login fragment the text view which was part of the splash screen remains. The same happens after transitioning from the login fragment to the register fragment as well as from the register fragment back to the login fragment. It is important to note that there is no issue during transitioning between login and register or between register and login - except the fact that the edit text stays there.
My MainActivity.java
public class StartActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
}
#Override
public void onBackPressed(){
Toast.makeText(getApplicationContext(), "Back button is disabled", Toast.LENGTH_SHORT).show();
}
}
My layout for MainActivity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".StartActivity"
android:background="#mipmap/background">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewpagerstart"
android:layout_centerInParent="true"
android:name="com.example.distributedsystemsui.SplashFragment">
</fragment>
</RelativeLayout>
The SplashScreen.java
public class SplashFragment extends Fragment {
LinearLayout linearLayout;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_splash, container, false);
linearLayout = (LinearLayout) view.findViewById(R.id.f_linearsplash);
Animation animation_fadein = AnimationUtils.loadAnimation((StartActivity)getActivity(), R.anim.fade_in);
linearLayout.startAnimation(animation_fadein);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_enter_left, R.anim.slide_exit_left,
R.anim.slide_enter_right, R.anim.slide_exit_right);
LoginFragment loginFragment = LoginFragment.newInstance();
ft.replace(R.id.viewpagerstart, loginFragment);
ft.commit();
}
}, 3000);
return view;
}
}
And the layout for the SplashScreen fragment.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoginFragment"
android:background="#color/Tranparent"
android:id="#+id/frame_splash">
<LinearLayout
android:id="#+id/f_linearsplash"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/f_splashlogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="70dp"
android:background="#FFFFFF"/>
<TextView
android:id="#+id/f_splashtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/app_name"
android:layout_gravity="center_horizontal"
android:layout_marginTop="100dp"
android:background="#FFFFFF"/>
</LinearLayout>
</FrameLayout>
All my search led back to nothing.
I can only assume that either the below contained in the MainActivity.xml creates a confusion (which I tried to change but no luck):
android:name="com.example.distributedsystemsui.SplashFragment"
or that I am doing a mistake in the SplashScreen.java that I cannot locate, even though I tried for more than 5 hours to make it work.
The problem is that you cannot replace fragments added via the <fragment> tag.
Instead, you should either:
1) Switch to FragmentContainerView, which was added in Fragment 1.2.0. It does not suffer from the same issue as the <fragment> tag and lets you do replace operations without issues:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".StartActivity"
android:background="#mipmap/background">
<androidx.fragment.app.FragmentContainerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewpagerstart"
android:layout_centerInParent="true"
android:name="com.example.distributedsystemsui.SplashFragment">
</androidx.fragment.app.FragmentContainerView>
</RelativeLayout>
2) Use a FrameLayout in your layout and manually add the SplashFragment in your activity (this is essentially what FragmentContainerView does for you):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".StartActivity"
android:background="#mipmap/background">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewpagerstart"
android:layout_centerInParent="true">
</FrameLayout>
</RelativeLayout>
which means you need to add the SplashFragment manually:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.viewpagerstart, new SplashFragment())
.commit()
}
}
I think it is better to do the fragment transactions from your activity instead of doing it from your fragment. You might consider having functions in your MainActivity as follows.
public class StartActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
}
public void goToLoginFragment() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_enter_left, R.anim.slide_exit_left,
R.anim.slide_enter_right, R.anim.slide_exit_right);
LoginFragment loginFragment = LoginFragment.newInstance();
ft.replace(R.id.viewpagerstart, loginFragment);
ft.commit();
}
public void goToRegisterFragment() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_enter_left, R.anim.slide_exit_left,
R.anim.slide_enter_right, R.anim.slide_exit_right);
LoginFragment registerFragment = RegisterFragment.newInstance();
ft.replace(R.id.viewpagerstart, registerFragment);
ft.commit();
}
#Override
public void onBackPressed(){
Toast.makeText(getApplicationContext(), "Back button is disabled", Toast.LENGTH_SHORT).show();
}
}
And then from your SplashFragment call the function that is defined in your activity.
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
((MainActivity) getActivity()).goToLoginFragment();
}
}, 3000);
I hope that solves the problem.
I try to implement an activity which can load different fragments into one container view element. I follow the Building a Flexible UI Training. Problem: When replacing a fragment, I can still see the replaced fragment in the background.
I see that my question has already been asked numerous times. However all the answers I found seem to be just a workaround (setting the background-color of fragments) or do not apply.
Here is what I have setup so far:
The activity's XML layout contains one FrameLayout as a container for the fragments:
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
The id fragment_container is used nowhere else in the project.
The complete XML layout of the first fragment:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="First Fragment" />
The complete XML layout of the second fragment:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ff0000"
android:text="Fragment Second" />
The class file of the first fragment looks like this (class for second fragment analogous):
public class FirstFragment extends Fragment {
public FirstFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_first, container, false);
}
}
In the activity's onCreate method I load the first fragment:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// load first fragment
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new FirstFragment())
.commit();
}
}
When selecting a menu item I try to replace the first fragment with the second fragment with this code in the activity's onOptionsItemSelected method:
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new SecondFragment())
.addToBackStack(null) // <-- does not make any difference if left out
.commit();
The result is that the text "Fragment Second" is printed in red above the text "First Fragment".
What is the "right way" (i.e. not just setting a background-color) to fix this problem?
I noticed this in onCreate:
getSupportFragmentManager()
and this in onOptionsItemSelected:
getFragmentManager()
There are actually both versions of the FragmentManager in AppCompatActivity and they know nothing about each other. So when you tell android.app.FragmentManager to replace a fragment that was added by android.support.v4.app.FragmentManager, it doesn't know anything about a FirstFragment so the operation acts more like an add than a replace.
I've made this mistake more than once. You'll need to comb through your code and make sure you are consistent about using the support class vs. the regular class.
If I dynamically add a Fragment (using the FragmentManager) into a container defined in a landscape XML then switch to portrait, that dynamically injected Fragment still exists. It is contributing to the Action Bar even though it is not visible. What is a good way / design to prevent this from happening?
I have tried using isVisible in onCreateOptionsMenu of the Fragment but that causes issues on some Android versions because onCreateOptionsMenu is called before onCreateView which results in false even if the fragment is going to be visible with the current configuration.
Note: I am not handling the configuration myself. I haven't specified configChanges in the manifest and I am not overriding onConfigurationChanged.
Activity:
// inject detail fragment
Fragment detailFragment = getSupportFragmentManager().findFragmentById(R.id.detail_container);
if(detailFragment == null)
getSupportFragmentManager().beginTransaction().replace(R.id.detail_container, DetailFragment.newInstance(id)).commit();
// inject master fragment
if(findViewById(R.id.master_container) != null) {
masterDetail = true;
Fragment listFragment = getSupportFragmentManager().findFragmentById(R.id.master_container);
if(listFragment == null)
getSupportFragmentManager().beginTransaction().replace(R.id.master_container, ListFragment.newInstance(position)).commit();
}
Activity portrait XML
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Activity landscape XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:baselineAligned="false">
<FrameLayout
android:id="#+id/master_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<FrameLayout
android:id="#+id/detail_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"/>
</LinearLayout>
The model I'd follow based on your explanation.
public class MyActivity extends Activity {
boolean mMultiPane = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FrameLayout masterContainer = (FrameLayout) findViewById(R.id.master_container);
if (masterContainer != null)
mMultiPane = true;
...
}
}
From then on in your Activity you can use the mMultiPane variable to change the behaviour including changing how the options menu is set up (just have two different menu.xml files or add / remove menu items depending on which mode you're in).
I have checked the posts. And here is the checklist of what I have tried
Make sure that a TextView by that name exists
That all functions are called after setContentView() in onCreate()
It still continues to baffle me. Your help your be appreciated
Please note that this activity (for which the code is given) is trigger from a parent (as given in the Android Manifest snippet below).
The java code for the activity is: (This code has been changed to simplify the question)
public class MainActivity extends ActionBarActivity {
private TextView tDummy;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tDummy=(TextView)findViewById(R.id.textViewDummy);
tDummy.setText("Testing.");
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
}
...
public void changeMsg(View view){
TextView dummy=(TextView)findViewById(R.id.textViewDummy);
dummy.setText("Changed!");
}
Why am I still getting a null pointer exception?!
It works fine when I click the button (which invokes the changeMsg function), but that just circumvents the problem.
I am using android studio.
XML file
(There are two: activity_main.xml and fragment_main.xml)
fragment_main.xml is as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context="mk.app.MainActivity$PlaceholderFragment">
<TextView
android:text="#string/hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textViewDummy" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textViewDummy"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="42dp"
android:layout_marginTop="26dp"
android:onClick="changeMsg"/>
</RelativeLayout>
main_activity.xml is:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mk.app.MainActivity"
tools:ignore="MergeRootFrame" />
Could it be an issue with having using the fragment layout?
As the content is set to activity_main, but the definitions are in fragment_main.xml??
Just so this question has an answer, you need to make sure that your variables get defined in the layout that you have set in your onCreate method, in this case setContentView(R.layout.activity_main.xml); Since you have set your activity_main.xml as your content view for your Activity, you need to make sure that layout actually contains the views and id's you are trying to use. All you need to do is move the code from your fragment layout to your main layout and vice versa. Reason being, if a particular layout is not set as your content view, the content from that layout hasn't actually been loaded into your Activity to be referenced to.
If you are really intent on using the fragment, simply change this:
tDummy=(TextView)findViewById(R.id.textViewDummy);
tDummy.setText("Testing.");
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
to
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
tDummy=(TextView)findViewById(R.id.textViewDummy);
tDummy.setText("Testing.");
And it should work, since now your fragment has been loaded into your container
I have to implement "standart" fragments navigation in my app (please see link).
The issue is when device is in portrait mode, there should be shown only 1 fragment, and when it is rotated to landscape mode, 2 fragments should be shown.
I tried to do this 2 different ways:
1) I use only 1 activity with different portrait and landscape layouts.
Portrait layout xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/main_frame_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
And here`s landscape layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="horizontal" >
<FrameLayout
android:id="#+id/main_frame_fragment_container_left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="#+id/main_frame_fragment_container_right"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
Activity`s onCreate method:
private static ItemsFragment mItemsFragment;
private static ItemDetailsFragment mItemDetailsFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (mItemsFragment == null) {
mItemsFragment = ItemsFragment.newInstance();
}
if (mItemDetailsFragment == null) {
mItemDetailsFragment = ItemDetailsFragment.newInstance();
}
if (isLandscape()) {
getSupportFragmentManager().beginTransaction().replace(R.id.main_frame_fragment_container_left, mItemsFragment)
.commit();
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_frame_fragment_container_right, mItemDetailsFragment).commit();
} else {
getSupportFragmentManager().beginTransaction().replace(R.id.main_frame_fragment_container, mItemsFragment)
.commit();
}
}
And that`s the way I refresh 2nd fragment:
Bundle bundle = new Bundle();
bundle.putSerializable(BaseFragment.KEY_BUNDLE_ITEM, response.getItem());
mItemDetailsFragment = ItemDetailsFragment.newInstance(bundle);
if (isLandscape()) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_frame_fragment_container_right, mItemDetailsFragment).commit();
} else {
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_frame_fragment_container, mItemDetailsFragment).addToBackStack(null).commit();
}
Also I save and restore fragments` states, so my data does not disappear after rotations. Generally, this code works properly in my case.
2) I use 2 activities and the same layout for 1st Activity portrait and landscape modes.
xml layout is the same as in previous one for landscape:
<FrameLayout
android:id="#+id/main_frame_fragment_container_left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="#+id/main_frame_fragment_container_right"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
onCreate method (note, that fragments entities are not static, as it was in 1st case):
private ItemsFragment mItemsFragment;
private ItemDetailsFragment mItemDetailsFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
mItemsFragment = ItemsFragment.newInstance();
mItemDetailsFragment = ItemDetailsFragment.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.main_frame_fragment_container_left, mItemsFragment)
.commit();
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_frame_fragment_container_right, mItemDetailsFragment).commit();
}
}
And now if the device is in portrait mode, I start new Activity:
if (isLandscape()) {
Bundle bundle = new Bundle();
bundle.putSerializable(BaseFragment.KEY_BUNDLE_ITEM, response.getItem());
mItemDetailsFragment = ItemDetailsFragment.newInstance(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_frame_fragment_container_right, mItemDetailsFragment).commit();
} else {
Intent intent = new Intent(getApplicationContext(), DetailsActivity.class);
intent.putExtra(KEY_ITEM, response.getItem());
startActivity(intent);
}
And, at last, 2nd Activity`s onCreate method:
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.activity_details);
if (isLandscape()) {
finish();
}
Item item = (Item) getIntent().getExtras().getSerializable(KEY_ITEM);
Bundle bundle = new Bundle();
bundle.putSerializable(BaseFragment.KEY_BUNDLE_ITEM, item);
ItemDetailsFragment mItemDetailsFragment = ItemDetailsFragment.newInstance(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_frame_fragment_container, mItemDetailsFragment).commit();
}
When device is rotated to landscape mode, 2nd activity finishes, and I see my 1st activity with 2 fragments (as expected).
Question:
In 1st case I save fragments as static variables, and because of this I don't care if I change 2nd fragment state in portrait or landscape modes (the same fragment is used). But I don't think it's a good idea to save it as static fields.
In 2nd case I don't know how to sync Activity A Fragment B (landscape) and Activity B Fragment B (portrait). If I change something in fragment (I mean, toggle button etc) and rotate device, changes should be applied in another fragment.
Generally, what case is better, and if 2nd, how can I resolve synchronization issue? Or maybe there is another easier way. Thanks for reading, I hope you can help me :)
Just follow this mate http://developer.android.com/guide/components/fragments.html. Dont make fragments static (thats just wierd)