My weather app works by searching cities on it's search panel and this works quite well. The only problem is that if i click the next tabs(hourly and daily section) on my bottom navigation view and
click the search button there, it shows the following exception:
java.lang.ClassCastException: com.viz.lightweatherforecast.SecondFragment cannot be cast to com.viz.lightweatherforecast.FirstFragment
at com.viz.lightweatherforecast.Activity.HomeActivity$2.onClick(HomeActivity.java:94)
at android.view.View.performClick(View.java:7044)
at android.view.View.performClickInternal(View.java:7017)
at android.view.View.access$3200(View.java:784)
at android.view.View$PerformClick.run(View.java:26596)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6819)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:497)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:912)
This error clearly indicates that the error is from this line on the activity:
FirstFragment firstFragment = (FirstFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
firstFragment.getWeatherData(textfield.getText().toString().trim());
So I tried adding this:
SecondFragment secondFragment = (SecondFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
secondFragment.getWeatherData(textfield.getText().toString().trim());
So that it can support my secondfragment, but it still gave the error. I have as well searched this site for an answer, but haven't found any solution.
To explain the app further, I have an activity that hosts 3 fragments(Firstfragment, Secondfragment, and Thirdfragment. The 1st is labeled Today, 2nd is Hourly and 3rd is Daily for the weather.
I'm not planning on using the hourly and daily tabs for now until later versions of the app.
My other fragments are currently empty(no code written on them), I just want to stick to only the today tab for now.
But fixing this error is necessary, just in case of users click it.
My Aim is just to ensure that it doesn't give such an error if I click the search button on those other tabs, please help.
My Activity code:
public class HomeActivity extends AppCompatActivity {
// Last update time, click sound, search button, search panel.
TextView time_field;
MediaPlayer player;
ImageView Search;
EditText textfield;
// For scheduling background image change(using constraint layout, start counting from dubai, down to statue of liberty.
ConstraintLayout constraintLayout;
public static int count=0;
int[] drawable =new int[]{R.drawable.dubai,R.drawable.central_bank_of_nigeria,R.drawable.eiffel_tower,R.drawable.hong_kong,R.drawable.statue_of_liberty};
Timer _t;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// use home activity layout.
time_field = findViewById(R.id.textView9);
Search = findViewById(R.id.imageView4);
textfield = findViewById(R.id.textfield);
// find the id's of specific variables.
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
// host 3 fragments along with bottom navigation.
final NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
assert navHostFragment != null;
final NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView, navController);
// For scheduling background image change
constraintLayout = findViewById(R.id.layout);
constraintLayout.setBackgroundResource(R.drawable.dubai);
_t = new Timer();
_t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
// run on ui thread
runOnUiThread(() -> {
if (count < drawable.length) {
constraintLayout.setBackgroundResource(drawable[count]);
count = (count + 1) % drawable.length;
}
});
}
}, 5000, 5000);
Search.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// make click sound when search button is clicked.
player = MediaPlayer.create(HomeActivity.this, R.raw.click);
player.start();
getWeatherData(textfield.getText().toString().trim());
// make use of some fragment's data
FirstFragment firstFragment = (FirstFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
firstFragment.getWeatherData(textfield.getText().toString().trim());
}
private void getWeatherData(String name) {
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<Example> call = apiInterface.getWeatherData(name);
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(#NonNull Call<Example> call, #NonNull Response<Example> response) {
try {
assert response.body() != null;
time_field.setVisibility(View.VISIBLE);
time_field.setText("Last Updated:" + " " + response.body().getDt());
} catch (Exception e) {
time_field.setVisibility(View.GONE);
time_field.setText("Last Updated: Unknown");
Log.e("TAG", "No City found");
Toast.makeText(HomeActivity.this, "No City found", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NotNull Call<Example> call, #NotNull Throwable t) {
t.printStackTrace();
}
});
}
});
}
}
Activity.xml code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:id="#+id/layout"
android:background="#drawable/dubai"
tools:context=".Activity.HomeActivity">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFFFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/bottom_menu" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="409dp"
android:layout_height="599dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:navGraph="#navigation/my_nav"
/>
<EditText
android:id="#+id/textfield"
android:layout_width="250dp"
android:layout_height="35dp"
android:autofillHints="#string/change_city"
android:background="#color/colorPrimary"
android:hint="#string/search_city"
android:inputType="text"
android:labelFor="#id/imageView4"
android:padding="8dp"
android:textColor="#color/colorAccent"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="#+id/imageView4"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/imageView4"
android:layout_width="50dp"
android:layout_height="35dp"
android:layout_marginEnd="1dp"
android:contentDescription="#string/searchbtn"
android:src="#drawable/look"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/time_field"
android:visibility="gone"
android:textColor="#FFFFFF"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textfield" />
</androidx.constraintlayout.widget.ConstraintLayout>
To get a correct reference to the displayed fragment from NavHostFragment you have to check the Fragment Class Type first before you try to downcast it.
Based on your question:
App crashes when I click the search button in my other fragments
You cannot retrieve always the FirstFragment or SecondFragment instance as the visible Fragment when switching between other fragments.
So simply change your code to be like below:
Fragment currentFragment = (Fragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
if(currentFragment instanceof FirstFragment) {
FirstFragment firstFragment = (FirstFragment)currentFragment;
firstFragment.getWeatherData(textfield.getText().toString().trim());
}
else if(currentFragment instanceof SecondFragment) {
SecondFragment secondFragment = (SecondFragment)currentFragment;
secondFragment.getWeatherData(textfield.getText().toString().trim());
}
else if(currentFragment instanceof ThirdFragment) {
ThirdFragment thirdFragment = (ThirdFragment)currentFragment;
thirdFragment.getWeatherData(textfield.getText().toString().trim());
}
Related
This question already has answers here:
findViewByID returns null
(33 answers)
Closed 3 years ago.
I'm trying to change the text of a NavigationView's child. I am using findViewById to get directly to the TextView. It works in the MainActivity, but in every other Activity findViewById returns a null reference.
By the way this is my first submitted question and I hope I respected the guidelines.
I can't seem to figure this out so I don't know where to start.
This is the NavigationView located in the layout of every activity
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:visibility="visible"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer"/>
nav_header_main is the component that has the said TextView, it is referenced in the code above. This is nav_header_main:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_header"
android:theme="#style/ThemeOverlay.AppCompat.Dark">
<TextView
android:id="#+id/nav_header_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="Angheluta Filip"
android:textAppearance="#style/TextAppearance.AppCompat.Body1"
android:textColor="#android:color/white" />
</LinearLayout>
This code is from MainActivity, where finding the said text view doesn't return a null reference
NavigationManager navigationManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Downloads the database and than proceeds to setup the Activity
//onDataChange gets called when the download is finished and dataSnapshot is the downloaded
//data
final AppCompatActivity context = this;
FirebaseHandler.getFirebaseHandler().getReference().addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
FirebaseHandler.getFirebaseHandler().setData(dataSnapshot);
DataSnapshot userSnapshot = dataSnapshot.child("Users").child(FirebaseHandler.getFirebaseHandler().getAuth().getUid());
User.initializeCurrentUser(userSnapshot);
//Add side bar to this activity
navigationManager = new NavigationManager(context);
navigationManager.createNavBar();
//Recycler view setup
RecyclerManager recyclerManager = new RecyclerManager();
recyclerManager.createRecyclerWithLargeElements(context, Voluntariat.getDataSet());
//Sort spinner setup
SortSpinnerManager sortSpinnerManager = new SortSpinnerManager();
sortSpinnerManager.createSortSpinnerManager(context, recyclerManager);
//Search bar setup
SearchBarManager searchBarManager = new SearchBarManager();
searchBarManager.createSearchBar(context, recyclerManager, Voluntariat.getDataSet());
TextView textViewHeader = findViewById(R.id.nav_header_textView);
Log.d("textHeader", textViewHeader.getText().toString());
textViewHeader.setText(User.currentUser.lastName + " " + User.currentUser.firstName);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
But this is from any other Activity in the onCreate method
RecyclerManager recyclerManager;
NavigationManager navigationManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toate_vol);
//Add side bar to this activity
navigationManager = new NavigationManager(this);
navigationManager.createNavBar();
//RECYCLERVIEW SETUP
recyclerManager = new RecyclerManager();
recyclerManager.createRecyclerWithSmallElements(this, Voluntariat.getDataSet());
//Search bar managing
SearchBarManager searchBarManager = new SearchBarManager();
searchBarManager.createSearchBar(this, recyclerManager, Voluntariat.getDataSet());
TextView textViewHeader = findViewById(R.id.nav_header_textView);
Log.d("textHeader", textViewHeader.getText().toString());
textViewHeader.setText(User.currentUser.lastName + " " + User.currentUser.firstName);
}
When I access any activity other than the MainActivity I get NullPointerException on the textViewHeader.
EDIT: I added more code and i changed it so the findViewById is called straight from the Activity file rather than from the NavigationManager file. The error is the same.
EDIT2: Added some more code from activity_toate_vol.xml:
It seems like this layout doesn't have any view with the problematic id, but the id is located in the nav_header_main.xml file which is set in the activity_toate_vol.xml file with the headerLayout attribute:
app:headerLayout="#layout/nav_header_main"
This NavigationView is a direct child of the root layout and the rest of the code is irrelevant, This same navigation view is found in the MainActivity as well.
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:visibility="visible"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:gravity="bottom"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="bottom"
android:orientation="horizontal">
<ImageView
android:id="#+id/imgdeconctare"
android:layout_width="27dp"
android:layout_height="27dp"
android:alpha=".6"
android:src="#drawable/ic_logout"
app:srcCompat="#drawable/ic_logout"
tools:srcCompat="#drawable/ic_logout" />
<TextView
android:id="#+id/logout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="20dp"
android:gravity="center|start"
android:text="Deconectare"
android:textColor="#android:color/black" />
</LinearLayout>
</LinearLayout>
</android.support.design.widget.NavigationView>
EDIT 3: I came out with a workaround and also identified the problem. Turns out I have to somehow wait for that NavigationView to inflate before I update it's text. Can anyone help me with the proper way to do this? Cause I am not happy with this.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
try {
wait(150);
}
catch(Exception e){
}
TextView textViewHeader = findViewById(R.id.nav_header_textView);
Log.d("textHeader", textViewHeader.getText().toString());
textViewHeader.setText(User.currentUser.lastName + " " + User.currentUser.firstName);
}
}, 100);
Welcome! When findViewById returns null, that can mean 2 things.
1) The layout that you pass to 'setContentView' does not have a view with that id...
2) The layout does have that view, but the view itself is not fully inflated yet. To wait until a view is fully inflated, you can use a GlobalLayoutListener
in java:
setContentView(R.layout.activity_other);
final NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
TextView text = navigationView.findViewById(R.id.nav_header_textView);
text.setText("Hello StackOverflow");
navigationView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
and when you switch to kotlin:
setContentView(R.layout.activity_main)
val view = findViewById<NavigationView>(R.id.nav_view)
view?.viewTreeObserver?.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener{
override fun onGlobalLayout() {
val header = findViewById<TextView>(R.id.nav_header_textView)
header.text= "Hallo Main"
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
I have 4 buttons in MainActivity that lead to different activities and by default they are set to have image background.
I click a button in main activity and it gets me to Options activity in where I have a switch.
'
//creates instance of the button and redirects to appropriate activity/class on button click
//options
Button options = findViewById(R.id.options);
options.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intentOptions = new Intent(MainActivity.this, Options.class);
startActivity(intentOptions);
}
});
'
When that switch is checked I want for Options background(for user feedback) to change to a color and so 4 buttons in MainActivity (I don't want to start MainActivity).
However, I obviously get a null object reference error because I try to change smth in another activity.
I read a bunch of similar topics here and I understand that I should use Shared Preference to store smth(what?) that will allow me to change background of the buttons in another activity and then pass it (how?) to Options and pass it back(?) from Options on Switch check(???).
I am a newbie in java and android and I can't grasp how to solve my problem.
Code in Options class:
`
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.options);
//on toggle switch changes options background and activities buttons background in main layout to plain color
final Switch optionPlainColored = findViewById(R.id.switch1);
optionPlainColored.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
ImageView background = findViewById(R.id.background);
Button attractionsAndArchitecture = findViewById(R.id.attractions_and_architecture);
Button barsAndRestaurants = findViewById(R.id.bars_and_restaurants);
Button sportAndRecreations = findViewById(R.id.sports_and_recreation);
Button nightlifeAndCulture = findViewById(R.id.culture_and_nightlife);
//on switch toggle changes background of options activity and 4 buttons in main activity
if(optionPlainColored.isChecked()){
background.setImageResource(R.drawable.setoptionsbackgroundcolor2);
attractionsAndArchitecture.setBackgroundColor(getResources().getColor(R.color.attractionsPlainColor));
barsAndRestaurants.setBackgroundColor(getResources().getColor(R.color.barsPlainColor));
sportAndRecreations.setBackgroundColor(getResources().getColor(R.color.sportsPlainColor));
nightlifeAndCulture.setBackgroundColor(getResources().getColor(R.color.culturePlainColor));
}else{
//when unchecked reverses the change
background.setImageResource(R.drawable.optionsbackground);
attractionsAndArchitecture.setBackground(getResources().getDrawable(R.drawable.placestovisitbackground));
barsAndRestaurants.setBackground(getResources().getDrawable(R.drawable.barandrestuarantsbackground));
sportAndRecreations.setBackground(getResources().getDrawable(R.drawable.sportandrecreationbackground));
nightlifeAndCulture.setBackground(getResources().getDrawable(R.drawable.nightlifeandculturebackground));
}
}
});
//displays toast message about the switch
Toast.makeText(getApplicationContext(), "Toggling the switch will change background color of the Options and Homescreen to plain colored one",
Toast.LENGTH_LONG).show();
}
`
Null object reference on lines where I am changing backgrounds.
XML on MainActivity:
'<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
android:background="#drawable/backgroundinfoadditional"
tools:context=".MainActivity">
<Button
android:id="#+id/attractions_and_architecture"
style="#style/buttonToActivity"
android:layout_width="205dp"
android:layout_height="285dp"
android:layout_marginBottom="8dp"
android:background="#drawable/placestovisitbackground"
android:text="#string/attractions_and_architecture"
app:layout_constraintBottom_toTopOf="#+id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/bars_and_restaurants"
style="#style/buttonToActivity"
android:layout_width="208dp"
android:layout_height="280dp"
android:layout_marginBottom="8dp"
android:background="#drawable/barandrestuarantsbackground"
android:text="#string/bars_and_restaurants"
app:layout_constraintBottom_toTopOf="#+id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<Button
android:id="#+id/sports_and_recreation"
style="#style/buttonToActivity"
android:layout_width="208dp"
android:layout_height="284dp"
android:background="#drawable/sportandrecreationbackground"
android:text="#string/sports_and_recreation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title"
app:layout_constraintVertical_bias="0.0" />
<Button
android:id="#+id/culture_and_nightlife"
style="#style/buttonToActivity"
android:layout_width="204dp"
android:layout_height="282dp"
android:background="#drawable/nightlifeandculturebackground"
android:text="#string/culture_and_nightlife"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title" />
<Button
android:id="#+id/options"
style="#style/options"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.024"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<Button
android:id="#+id/info"
style="#style/info"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.952"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title"
app:layout_constraintVertical_bias="1.0" />
<TextView
android:id="#+id/title"
style="#style/title"
android:layout_width="match_parent"
android:layout_height="39dp"
android:text="#string/moscow_concise_guide"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
'
use handler to pass message to mainactivity. like
if (MainActivity.xhandler != null) {
Message msg = new Message();
msg.what = message;
msg.obj = data1;
MainActivity.xhandler.sendMessage(msg);
}
In MainActivity define handler.
Handler xhandler=new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
return false;
}
});
Got an answer that worked from other source, posting it here (THIS DOES NOT USE SHARED PREFERENCES):
"I think that an alternative to using a separate Activity for the options, is using a DialogFragment. This would allow to show a floating window in the top of your MainActivity layout, and loading the options layout there, including the Switch.
Following this idea, instead of a new Activity, you’d need to create a DialogFragment for the options.
Then, let’s say you have a button in your Activity and you want to change the color. You can refer to the button like this
getActivity().findViewById(R.id.button)
and that’s it :slight_smile:
This way you can access any view in your Activity, change the background color, assign a different image, or whatever you want, without having to deal with preferences or access a different Activity."
OptionsDialogFragment class:
'
import android.app.DialogFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Switch;
public class OptionsDialogFragment extends DialogFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Here we inflate the layout to use with the options dialog
View dialogView = inflater.inflate(R.layout.options, container, false);
final Switch optionPlainColored = dialogView.findViewById(R.id.switch1);
optionPlainColored.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(optionPlainColored.isChecked()){
// we use getActivity() to refer to the views in the Activity layout.
getActivity().findViewById(R.id.attractions_and_architecture).setBackgroundColor(getResources().getColor(R.color.attractionsPlainColor));
getActivity().findViewById(R.id.bars_and_restaurants).setBackgroundColor(getResources().getColor(R.color.barsPlainColor));
getActivity().findViewById(R.id.sports_and_recreation).setBackgroundColor(getResources().getColor(R.color.sportsPlainColor));
getActivity().findViewById(R.id.culture_and_nightlife).setBackgroundColor(getResources().getColor(R.color.culturePlainColor));
} else {
getActivity().findViewById(R.id.attractions_and_architecture).setBackground(getResources().getDrawable(R.drawable.backgroundattractionsandarchitecture));
getActivity().findViewById(R.id.bars_and_restaurants).setBackground(getResources().getDrawable(R.drawable.backgroundbarsandrestaurants));
getActivity().findViewById(R.id.sports_and_recreation).setBackground(getResources().getDrawable(R.drawable.backgroundsportsandrecreation));
getActivity().findViewById(R.id.culture_and_nightlife).setBackground(getResources().getDrawable(R.drawable.backgroundnightlifeandculture));
}
}
});
return dialogView;
}
}'
In MainActivity:
' Button options = findViewById(R.id.options);
options.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OptionsDialogFragment dialogFrag = new OptionsDialogFragment();
FragmentManager fm = getFragmentManager();
dialogFrag.show(fm, "Options");
}
});'
I want a addressCard to scroll down behind another FirstRowCard which are both fragments. I have the addressCard appear behind the other but it doesn't scroll down. I realise I have to use Object Animator and include a View in the arguments.
I was originally using:
View addressCard = (View) getView().findViewById(R.id.years_of_cooking_xp_cardView);
ObjectAnimator addressCard_Animator = ObjectAnimator.ofFloat(addressCard, "translationY", 0f, 258f);
But the addressCard just appears with no animation. General architecture is Main Activity creates a fragment called topRow_fragment. In topRow_fragment I've created an interface so when a User clicks on a button, it creates the addressCard fragment that is displayed underneath the topRow_fragment. When the User clicks the button, the other fragments disappear and this works fine. I've commented out some parts that I intend on actualising but until the initial animation is completed, I'll ignore them.
I think I haven't correctly identified the View associated with the addressCard and consequently no animation is able to be shown. Any help would be great thanks,
Here's the class:
public class topRow_fragment extends Fragment implements View.OnClickListener{
addressCard_fragment addressCard;
onTopLeftClicked mCallback;
int addressCardLaunch = 0;
String NextAddressCardAnimation = "SLIDE DOWN";
View layoutReturn;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
layoutReturn = inflater.inflate(R.layout.firstrow_fragment, container, false);
ImageButton top_row_left_button = (ImageButton) layoutReturn.findViewById(R.id.imageLeft);
top_row_left_button.setOnClickListener(this);
return layoutReturn;
}
#Override
public void onClick(View v) {
addressCard = new addressCard_fragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id. Below_top_row_fragment_container, addressCard);
View addressCard = (View) getView().findViewById(R.id.years_of_cooking_xp_cardView);
ObjectAnimator addressCard_Animator = ObjectAnimator.ofFloat(addressCard, "translationY", 0f, 258f);
//CardView addressCard_View = (CardView) getView().findViewById(R.id.years_of_cooking_xp_cardView);
//ObjectAnimator addressCard_Animator = ObjectAnimator.ofFloat(addressCard_View, "translationY", 0f, 258f);
switch(v.getId()) {
case R.id.imageLeft:
if (addressCardLaunch == 0) {
fragmentTransaction.commit();
moveSecondRowAfterClick(0);
addressCard_Animator.setDuration(250);
addressCard_Animator.start();
moveSecondRowAfterClick(0);
addressCardLaunch = 1;
}
else if (addressCardLaunch == 1) {
switch (NextAddressCardAnimation) {
case "SLIDE DOWN":
NextAddressCardAnimation = "SLIDE UP";
moveSecondRowAfterClick(1);
//addressCard_Animator.setInterpolator(new SlideDownAnimationInterpolator());
//addressCard_Animator.setDuration(250);
//addressCard_Animator.start();
break;
case "SLIDE UP":
NextAddressCardAnimation = "SLIDE DOWN";
moveSecondRowAfterClick(0);
//addressCard_Animator.setDuration(250);
//addressCard_Animator.start();
break;
}
}
}
}
public interface onTopLeftClicked {
void moveSecondRow(int slidingAnimationStatus);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity a;
if (context instanceof Activity) {
a = (Activity) context;
try {
mCallback = (onTopLeftClicked) a;
}
catch (ClassCastException e) {
throw new ClassCastException(a.toString() + " must implement onTopLeftClicked Interface");
}
}
}
#Override
public void onDetach() {
mCallback = null;
super.onDetach();
}
public void moveSecondRowAfterClick(int slidingAnimationStatus) {
mCallback.moveSecondRow(slidingAnimationStatus);
}}
Update01:
I included methods within the fragment class, slideUpAnimation and slideDownAnimation and this has resolved a NullPointerException that I had. Yet to successfully animate the addressCard upon initialisation or subsequent button clicks. This approach is more in line with encapsulation rather than calling the animations within the topRow_fragment class. I'm wondering if this could now relate to view constraints I've emplaced on the CardView in the respective XML file.
Main Activity XML where the fragments are called, both topRow_fragment and addressCard.
<?xml version="1.0" encoding="utf-8"?><FrameLayout 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="com.example.bryanjordan.settingsr11.MainActivity">
<android.support.constraint.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.constraint.ConstraintLayout
android:layout_width="368dp"
android:layout_height="wrap_content"
tools:layout_editor_absoluteY="8dp"
android:id="#+id/constraintLayout"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent">
<RelativeLayout
android:id="#+id/Below_top_row_fragment_container"
android:layout_width="367dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
tools:layout_editor_absoluteY="16dp">
</RelativeLayout>
<RelativeLayout
android:id="#+id/Above_top_row_fragment_container"
android:layout_width="367dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
tools:layout_editor_absoluteY="8dp"
tools:layout_editor_absoluteX="1dp">
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/constraintLayout"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent">
<RelativeLayout
android:id="#+id/second_row_fragment_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
tools:layout_editor_absoluteY="25dp"></RelativeLayout>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
Update02: I've looked at how slideDownAnimation and slideUpAnimation work and these are both being called correctly but the View just isn't moving. This is clearly a problem regarding assignment - I think it stems from the way the fragments are called.
Firstly, topRow_fragment is called then when the User clicks on a button below_topRow_fragmentis created. I've been looking at using:
View layoutTest = layoutReturn.findViewById(R.id.Below_top_row_fragment_container);
View addressTestR2 = (View) layoutTest.findViewById(R.id.years_of_cooking_xp_cardView);
And having NullPointerException errors but this seems to be the right direction as I'd have to call the respective child fragments to get to the one I want before then assigning it.
i have an Activity with 02 tabLayout
i added First Fragment in my first tab, every thing was just fine since i added my Second Fragment in second tab
my Second Fragment works but when i rotate the device it'll be crash, here is my codes and logCat..
i tried to attach my FragmentTwo to my MainActivity in different ways.. can anybody tell me what's wroNg && how can i do this correctly?
Thanks in advancE.
Main Activity:
public class MainActivity extends AppCompatActivity {
private DrawerLayout mDrawerLayout;
private TabLayout tabLayout;
private ViewPager viewPager;
private int[] tabIcons = {
R.drawable.ic_tab_note,
R.drawable.ic_tab_calendar
};
private static final int TIME_DELAY = 2000;
private static long back_pressed;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//////// TOOLBAR
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
actionBar.setDisplayHomeAsUpEnabled(true);
///////// DRAWER
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
NavigationView navigationView =
(NavigationView) findViewById(R.id.navigation_view);
navigationView.setNavigationItemSelectedListener
(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
menuItem.setChecked(true);
mDrawerLayout.closeDrawers();
Toast.makeText(MainActivity.this,
menuItem.getTitle(),
Toast.LENGTH_LONG).show();
return true;
}
});
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tablayout);
tabLayout.setupWithViewPager(viewPager);
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new FragmentOne(), "ONE");
adapter.addFragment(new FragmentTwo(), "TWO");
viewPager.setAdapter(adapter);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
// return mFragmentTitleList.get(position);
return null;
}
}
}
FragmentTwo:
import ir.mirrajabi.persiancalendar.PersianCalendarView;
import ir.mirrajabi.persiancalendar.core.PersianCalendarHandler;
import ir.mirrajabi.persiancalendar.core.interfaces.OnDayClickedListener;
import ir.mirrajabi.persiancalendar.core.interfaces.OnMonthChangedListener;
import ir.mirrajabi.persiancalendar.core.models.CalendarEvent;
import ir.mirrajabi.persiancalendar.core.models.PersianDate;
public class FragmentTwo extends Fragment {
private View view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_two_layout,
container, false);
final PersianCalendarView persianCalendarView = (PersianCalendarView) view.findViewById(R.id.persian_calendar);
final PersianCalendarHandler calendar = persianCalendarView.getCalendar();
final PersianDate today = calendar.getToday();
calendar.addLocalEvent(new CalendarEvent(
today, "Custom event", false
));
calendar.addLocalEvent(new CalendarEvent(
today.clone().rollDay(2, true), "Custom event 2", true
));
calendar.setOnMonthChangedListener(new OnMonthChangedListener() {
#Override
public void onChanged(PersianDate date) {
Toast.makeText(getActivity(), calendar.getMonthName(date), Toast.LENGTH_SHORT).show();
}
});
persianCalendarView.setOnDayClickedListener(new OnDayClickedListener() {
#Override
public void onClick(PersianDate date) {
for (CalendarEvent e : calendar.getAllEventsForDay(date))
Toast.makeText(getActivity(), e.getTitle(), Toast.LENGTH_LONG).show();
calendar.addLocalEvent(new CalendarEvent(
today.clone().rollDay(2, false), "Some event that will be added in runtime", false
));
persianCalendarView.update();
}
});
calendar.setHighlightOfficialEvents(false);
TextView txtDayMonth = (TextView) view.findViewById(R.id.txt_day_month);
TextView txtYear = (TextView) view.findViewById(R.id.txt_year);
String dayAndMonth = calendar.getWeekDayName(today) + calendar.formatNumber(today.getDayOfMonth())
+ calendar.getMonthName(today);
txtDayMonth.setText(dayAndMonth);
txtYear.setText(calendar.formatNumber(today.getYear()));
calendar.setColorBackground(getResources().getColor(android.R.color.holo_blue_dark));
persianCalendarView.update();
return view;
}
}
and here is my activity_main.xml :
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<RelativeLayout
android:id="#+id/base2"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.Dark"/>
<android.support.design.widget.TabLayout
android:id="#+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:tabGravity="fill"
android:theme="#style/ThemeOverlay.AppCompat.Dark"/>
<RelativeLayout
android:id="#+id/relativee"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
layout_weight="1"/>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
<android.support.design.widget.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="#layout/drawer_header"
app:menu="#menu/drawer"/>
</android.support.v4.widget.DrawerLayout>
and here is fragment_two_layout.xml :
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimaryDark"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="24dp">
<ir.mirrajabi.persiancalendar.PersianCalendarView
android:id="#+id/persian_calendar"
android:layout_width="match_parent"
android:layout_height="290sp"
app:pcv_colorBackground="#292929"
app:pcv_colorDayName="#bab6b6"
app:pcv_colorHoliday="#ffd506"
app:pcv_colorHolidaySelected="#f1f2f3"
app:pcv_colorNormalDaySelected="#d9d9d9"
app:pcv_colorNormalDay="#f3f4f5"
app:pcv_eventUnderlineColor="#02f0f0"
app:pcv_fontSize="20sp"
app:pcv_headersFontSize="14sp"/>
</android.support.v7.widget.CardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="8dp">
<TextView
android:id="#+id/txt_day_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/colorAccent"
android:layout_gravity="center_horizontal"
android:textSize="30sp"/>
<TextView
android:id="#+id/txt_year"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="#color/colorAccent"
android:textSize="20sp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<Button
android:id="#+id/change_to_ad"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change to Gregorian Calendar"
android:textSize="14dp"/>
</LinearLayout>
</LinearLayout>
logCat:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.drgnme.listhamrah/com.drgnme.listhamrah.MainActivity}: java.lang.IllegalStateException: Fragment has not been attached yet.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2434)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2494)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4095)
at android.app.ActivityThread.access$1000(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1353)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5451)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.IllegalStateException: Fragment has not been attached yet.
at android.support.v4.app.Fragment.instantiateChildFragmentManager(Fragment.java:2195)
at android.support.v4.app.Fragment.getChildFragmentManager(Fragment.java:745)
at ir.mirrajabi.persiancalendar.core.fragments.CalendarFragment.createViewPagers(CalendarFragment.java:55)
at ir.mirrajabi.persiancalendar.core.fragments.CalendarFragment.access$000(CalendarFragment.java:27)
at ir.mirrajabi.persiancalendar.core.fragments.CalendarFragment$1.update(CalendarFragment.java:46)
at ir.mirrajabi.persiancalendar.PersianCalendarView.update(PersianCalendarView.java:116)
at com.drgnme.listhamrah.FragmentTwo.onCreateView(FragmentTwo.java:87)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2239)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1332)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1574)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1641)
at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:2959)
at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:550)
at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:177)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1238)
at android.app.Activity.performStart(Activity.java:6340)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2397)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2494)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4095)
at android.app.ActivityThread.access$1000(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1353)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5451)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Move all the below code to onActivityCreated():
final PersianCalendarView persianCalendarView = (PersianCalendarView) view.findViewById(R.id.persian_calendar);
final PersianCalendarHandler calendar = persianCalendarView.getCalendar();
final PersianDate today = calendar.getToday();
calendar.addLocalEvent(new CalendarEvent(
today, "Custom event", false
));
calendar.addLocalEvent(new CalendarEvent(
today.clone().rollDay(2, true), "Custom event 2", true
));
calendar.setOnMonthChangedListener(new OnMonthChangedListener() {
#Override
public void onChanged(PersianDate date) {
Toast.makeText(getActivity(), calendar.getMonthName(date), Toast.LENGTH_SHORT).show();
}
});
persianCalendarView.setOnDayClickedListener(new OnDayClickedListener() {
#Override
public void onClick(PersianDate date) {
for (CalendarEvent e : calendar.getAllEventsForDay(date))
Toast.makeText(getActivity(), e.getTitle(), Toast.LENGTH_LONG).show();
calendar.addLocalEvent(new CalendarEvent(
today.clone().rollDay(2, false), "Some event that will be added in runtime", false
));
persianCalendarView.update();
}
});
calendar.setHighlightOfficialEvents(false);
TextView txtDayMonth = (TextView) view.findViewById(R.id.txt_day_month);
TextView txtYear = (TextView) view.findViewById(R.id.txt_year);
String dayAndMonth = calendar.getWeekDayName(today) + calendar.formatNumber(today.getDayOfMonth())
+ calendar.getMonthName(today);
txtDayMonth.setText(dayAndMonth);
txtYear.setText(calendar.formatNumber(today.getYear()));
calendar.setColorBackground(getResources().getColor(android.R.color.holo_blue_dark));
persianCalendarView.update();
It is a best practice not do any works other than inflating the layout inside onCreateView()
Just move the persianCalendarView.update() line inside your FragmentTwo's onAttach and remove it from onCreate() :
#Override
public void onAttachFragment(Fragment childFragment) {
super.onAttachFragment(childFragment);
persianCalendarView.update();
}
Also remove the update() from the inside of persianCalendarView's OnDayClickedListener. I added that line when I was testing and forgot to remove that from the sample app. You don't have to update persianCalendarView unless you need to change the theme or when adding events in runtime.
Problem is not in your app, Problem is in PersianCalendar Lib,
You are inflating PersianCalendarView in your layout in the Fragment class if you check their implementation in PersianCalendarView.java, 1. they are inflating one layout and they are trying to add one Fragment in that layout in that FragmentManager and its working fine. 2. but not only that in that Fragment they are trying to add ViewPager using getChildFragmentManager() Problem comes her only.
You can ask why? In already one FragmentManager added this Fragment that time its working fine but now it's crashing, For that, you can check the difference between that 2 FragmnetManger in this S0 Answer
For this problem, you can do 3 things,
1. You can create ticket to them and wait for the result
2. You can fix that issue
3. Instead of Adding in Layout, Just Create one ViewGroup in Fragment and when onActivtyCreated called you can Add that layout runtime.
EDITED
I tried that sample, And I tried to add it in Fragment, Problem I faced is, Actually the CalendarView fragment is not attached even though our Fragment attached to the Activity, So as I Mentioned in Solution 3 that we can add it in onActivtyCreated that is wrong.
Then how we can solve temporarily?
I solved by adding the view in onStart(I know this is the wrong place this will call multiple time in the life cycle of Fragment), but we can add some logic and we can add this calendar view only once for making it work now till the dev fix in their lib.
My sample :
My fragment Layout:
<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="ir.mirrajabi.pc.sample.BlankFragment">
<LinearLayout
android:id="#+id/calendar_container"
android:layout_width="match_parent"
android:layout_height="290dp"
android:orientation="vertical"/>
<TextView
android:id="#+id/txt_day_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="#f0f2f3"
android:textSize="30sp"/>
<TextView
android:id="#+id/txt_year"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="#c6d9e2"
android:textSize="20sp"/>
</FrameLayout>
Fragment code
public class BlankFragment extends Fragment {
private LinearLayout mLinearLayout;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_blank, container, false);
mLinearLayout= (LinearLayout) view.findViewById(R.id.calendar_container);
return view;
}
#Override
public void onStart() {
super.onStart();
final PersianCalendarView persianCalendarView = new PersianCalendarView(getContext());
// All your remaining PersianCalendarView implementation code here
mLinearLayout.addView(persianCalendarView);
}
}
Note:
If you take above approach, Please add some logic and make sure it's
not adding multiple time when onStart calls in the Fragment.
I am using the Support Library to add a bottom bar similar to the material design one. The bottom bar works great but it seems that if I have the bar displayed, if I try to open any fragment from my custom adapter, the fragment does not open...or maybe it opens behind my main layout? I have no idea how to figure this out. Below is my code.
I've read more posts on SO and around the web and I think this is related to the fragment being properly loaded but below or next to the bottom bar...and that is why it isn't visible? Why does this happen? Is it because the bottom bar has a LinearLayout? I defined it as a menu so I'm not sure if I can control it being a LinearLayout....
Setting up the bottom bar, this method is called from the onCreate of my activity:
public void setupBottomToolbar(Bundle savedInstanceState) {
mBottomBar = BottomBar.attach(MainActivity.this, savedInstanceState);
mBottomBar.setItems(R.menu.bottombar_menu);
mBottomBar.setOnMenuTabClickListener(new OnMenuTabClickListener() {
#Override
public void onMenuTabSelected(#IdRes int menuItemId) {
if (menuItemId == R.id.toolbar_jobs) {
} else if (menuItemId == R.id.toolbar_messages) {
} else if (menuItemId == R.id.toolbar_recentJobs) {
} else if (menuItemId == R.id.toolbar_employerPools) {
}
}
#Override
public void onMenuTabReSelected(#IdRes int menuItemId) {
if (menuItemId == R.id.toolbar_jobs) {
// The user reselected item number one, scroll your content to top.
} else if (menuItemId == R.id.toolbar_messages) {
} else if (menuItemId == R.id.toolbar_employerPools) {
} else if (menuItemId == R.id.toolbar_recentJobs) {
}
}
});
// Setting colors for different tabs when there's more than three of them.
// You can set colors for tabs in three different ways as shown below.
mBottomBar.getBar().setBackgroundColor(getResources().getColor(R.color.laborswipe_darkgray));
mBottomBar.setActiveTabColor(getResources().getColor(R.color.laborswipe_lightgray));
// Make a Badge for the second tab, with red background color and a value of "13".
BottomBarBadge unreadMessages = mBottomBar.makeBadgeForTabAt(1, getResources().getColor(R.color.laborswipe_orange), 5);
// Control the badge's visibility
unreadMessages.show();
//unreadMessages.hide();
// Change the displayed count for this badge.
//unreadMessages.setCount(4);
// Change the show / hide animation duration.
unreadMessages.setAnimationDuration(200);
// If you want the badge be shown always after unselecting the tab that contains it.
unreadMessages.setAutoShowAfterUnSelection(true);
// If you don't want this badge to be hidden after selecting the tab contains it.
unreadMessages.setAutoShowAfterUnSelection(false);
}
In my adapter, I am trying to open the fragment when you click a button, like this:
holder.desc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "Open Description", Toast.LENGTH_SHORT).show();
JobDescFragment firstFragment = new JobDescFragment();
((MainActivity)context).getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
});
If I comment out the call to setupBottomToolbar() in my onCreate of the activity, the fragment opens up fine...but that means I don't have the bottom bar...
What am I missing? There has to be a way to use the bottom bar and also open a fragment?
Thanks!
EDIT:
Here is the top part of my activity.
public class MainActivity extends AppCompatActivity {
private ArrayList<String> swipecardsList;
private ArrayList<Job> jobList = new ArrayList<Job>();
private JobAdapter arrayAdapter; //arrayadapter
private BottomBar mBottomBar;
SharedPreferences settings;
#InjectView(R.id.frame) SwipeFlingAdapterView flingContainer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Remove title bar
//this.requestWindowFeature(Window.FEATURE_NO_TITLE);
//color the notification bar with our company colors
Window window = this.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(this.getResources().getColor(R.color.laborswipe_notificationbar));
//remove title from action bar and add the logo to the top left of the action bar
setupTopToolbar();
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
//set up the bottom toolbar using the roughike library to mimic android material design
setupBottomToolbar(savedInstanceState);
My adapter:
public class JobAdapter extends ArrayAdapter<Job> {
private final Context context;
private final ArrayList<Job> jobs;
private final int layoutResourceId;
private final SwipeFlingAdapterView flingContainer;
private boolean isExpanded = false;
public JobAdapter(Context context, int layoutResourceId, ArrayList<Job> jobs, SwipeFlingAdapterView flingContainer) {
super(context, layoutResourceId, jobs);
this.context = context;
this.jobs = jobs;
this.layoutResourceId = layoutResourceId;
this.flingContainer = flingContainer;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
final ViewHolder holder;
String pay, hrs;
final Bundle fragmentParams = new Bundle();
LayoutInflater inflater = LayoutInflater.from(context);
if (view == null) {
view = inflater.inflate(layoutResourceId, parent, false);
holder = new ViewHolder();
holder.title = (TextView)view.findViewById(R.id.tv_jobTitle);
holder.desc = (TextView) view.findViewById(R.id.tv_JobDesc);
view.setTag(holder);
} else {
holder = (ViewHolder)view.getTag();
}
Job j = jobs.get(position);
holder.title.setText(j.getJobTitle());
holder.desc.setText(j.getDescription());
//when user clicks apply, swipe the card right
holder.apply.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Open up a fragment to display the entire job description
Toast.makeText(context, "Applied", Toast.LENGTH_SHORT).show();
flingContainer.getTopCardListener().selectRight();
}
});
//when user clicks dismiss, swipe the card left
holder.dismiss.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Open up a fragment to display the entire job description
Toast.makeText(context, "Dismissed", Toast.LENGTH_SHORT).show();
flingContainer.getTopCardListener().selectLeft();
}
});
//on click event listener for the job description field - open larger window to read description
holder.desc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Toast.makeText(context, "Open Description", Toast.LENGTH_SHORT).show();
JobDescFragment firstFragment = new JobDescFragment();
Fragment frag = new Fragment();
frag = firstFragment.newJobDescFrag(j.getDescription());
((MainActivity) context).getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, frag)
.addToBackStack("JobDesc").commit();
}
});
return view;
}
public class ViewHolder
{
TextView title;
TextView payrate;
TextView dateRange;
TextView workinghrs;
TextView location;
TextView companyname;
TextView desc;
TextView experience;
TextView equipment;
Button apply, dismiss, expand;
}
}
activity_main.xml:
<merge
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">
<com.lorentzos.flingswipe.SwipeFlingAdapterView
android:id="#+id/frame"
android:background="#color/laborswipe_lightgray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context=".MainActivity"
android:layout_gravity="top" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</merge>
Fragment Layout:
<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=".JobDescFragment">
<LinearLayout
android:id="#+id/outerDescriptionLayout"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_gravity="center_horizontal|top"
android:orientation="vertical"
android:background="#drawable/swipecard_shadow"
android:gravity="top"
android:layout_marginLeft="5dp">
<LinearLayout
android:id="#+id/DescriptionLayout"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_gravity="center_horizontal|top"
android:orientation="vertical"
android:weightSum="1"
android:gravity="top"
android:layout_marginTop="20dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="#00FF00"
android:clickable="true">
<TextView
android:layout_width="200dp"
android:layout_height="200dp"
android:text="Detailed Description:"
android:textColor="#000000"
android:id="#+id/tv_title" />
<TextView
android:layout_width="200dp"
android:layout_height="200dp"
android:text="THIS IS THE FULL DESCRIPTION"
android:textColor="#000000"
android:id="#+id/tv_fullDescription" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
Logcat:
08-07 11:20:47.799 13896-13896/com.lorentzos.swipecards.example I/System.out: DEBUG: job desc fragment loaded!
08-07 11:20:47.855 13896-13941/com.lorentzos.swipecards.example W/EGL_emulation: eglSurfaceAttrib not implemented
08-07 11:20:47.855 13896-13941/com.lorentzos.swipecards.example W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xaaa7f880, error=EGL_SUCCESS
08-07 11:20:48.002 13896-13941/com.lorentzos.swipecards.example V/RenderScript: 0xa1408000 Launching thread(s), CPUs 2
08-07 11:20:49.798 13896-13941/com.lorentzos.swipecards.example E/Surface: getSlotFromBufferLocked: unknown buffer: 0xae433ca0
When I use the bottom bar (not working- no fragment opened but toast displayed):
When I don't use the bottom bar (workin-fragment opened, background is green):
try to link a pic of problem and without problem(no bottombar) and since you are using merge the layout hierarchy will be laid off according to your activity's viewgroup(linear,relative) constraints(we don't know what they are like).
as you said when there is no bottombar ,you fragment displays perfectly though when the bottombar it there ,problem stats ,as per your log in fragment indicating that your fragment is loading perfectly even though when bottombar is visible mean fragment is there but is not visible ,seems like your fragment didn't get the appropriate space to get displayed.
other solution can be adding bottom bar to your fragment instead of activity to avoid any overlapping ,like
mBottomBar.attach(findViewById(R.id.fragmentContainer), savedInstanceState);
OK, I think the solution for this should be simple, from what I can see in your code, you are attaching the BottomBar to your activity, I think this is the problem. If you were to read the readme.md in the roughike/BottomBar github page you'd find this
Why is it overlapping my Navigation Drawer?
All you need to do is instead of attaching the BottomBar to your Activity, attach it to the view that has your content. For example, if your fragments are in a ViewGroup that has the id fragmentContainer, you would do something like this:
mBottomBar.attach(findViewById(R.id.fragmentContainer), savedInstanceState);
So, since navigation drawer works with transition a fragment in and out of view with animation, the same thing is happening when you are adding a new fragment to your activity.
The Solution
From what I can see in your code, your fragment container id is this: fragment_container in your activity layout. So according to the documentation you'd just need to do attach your bottomBar to the fragment_container instead of MainActivity.this
mBottomBar.attach(findViewById(R.id.fragment_container), savedInstanceState);
If the above doesn't work try this
What you'd need to do is add an extra FrameLayout to hold your bottombar, which has a transparent background, but is on top of your fragment.
So change your main_activity layout to
<merge
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">
<com.lorentzos.flingswipe.SwipeFlingAdapterView
android:id="#+id/frame"
android:background="#color/laborswipe_lightgray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context=".MainActivity"
android:layout_gravity="top" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/holder_bottombar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"/>
</merge>
Now in the code instead of attaching the bottom bar to mainactivity, just attach it to the holder like so
mBottomBar.attach(findViewById(R.id.holder_bottombar), savedInstanceState);