Dynamically added views disappear on orientation in Android - android

I have created the views to be added in dynamically to a layout from a button click, but when I rotate the device landscape, the views disappear. Could someone please look at my code and explain how to stop this from happening.
Here is the code:
public class TestContainerActivity extends Activity implements OnClickListener {
LinearLayout containerLayout;
Button testButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_container);
testButton = (Button)findViewById(R.id.testContainerButton1);
testButton.setOnClickListener(this);
containerLayout = (LinearLayout)findViewById(R.id.testContainerLayout);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.test_container, menu);
return true;
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v==testButton){
createNewLayout();
}
}
public void createNewLayout(){
LayoutInflater layoutInflater = (LayoutInflater) getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View addView = layoutInflater.inflate(R.layout.container, null);
TextView textviewTest = (TextView)addView.findViewById(R.id.containerTextView4);
textviewTest.setText("TextView");
containerLayout.addView(addView);
}
}

Add this line in your manifest.xml Tags.
android:configChanges="keyboardHidden|orientation|screenSize"
This will avoid your activity from recreating on orientation change.

It is better to try to recreate the layout in the new orientation, rather than just preventing the orientation change.
In your onCreate check whether there is a saved instance (as a result of the orientation change) e.g.
if (savedInstanceState == null) {
//create new button layout if previously clicked
}
else {
//normal start
}
You might need to retain some values (either in Shared Prefs or onSavedInstanceState).
This approach is more difficult than locking the orientation, but it is a better approach in the long run and is well worth the research effort.

Add the following line to the TestContainerActivity activity
android:ConfigChanges="keyboardHidden|orientation"

Related

Layout change when orientation changes in Android Fragments

please suggest a solution.
When i rotate my fragment it should change in to landscape mode and to display another layout.But screen is not rotating to landscape.
My code blow:
<activity
android:name=".activites.MainActivity"
android:launchMode="singleInstance"
android:configChanges="keyboardHidden|screenLayout|screenSize|orientation"
/>
This is main layout called dashboard and now it is in portrait mode:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view=View.inflate(getContext(), R.frag_dashboard,null);
changeview= (ShimmerTextView)view.findViewById(R.id.changeview);
return view;
}
when i rotate the screen this fragment changed to landscape mode and set another layout, and prayer_times is the new layout.
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(getContext(), "landscape", Toast.LENGTH_SHORT).show();
view=View.inflate(getContext(), R.layout.prayer_times,null);
}
}
and i create layout_land for prayer_times
If your fragment has no issue of reloading when orientation change you can simply reload.
Add two layout with same name in layout and layout-land folders.
This will show correct oriented layout when load, for change layout when device rotate
add following in onConfigarationChanged method inside fragment itself.
#Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE || newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
try {
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (Build.VERSION.SDK_INT >= 26) {
ft.setReorderingAllowed(false);
}
ft.detach(this).attach(this).commit();
} catch (Exception e) {
e.printStackTrace();
}
}
}
If the onCreateView function is called when you rotate the screen, you can do this in it:
if(this.getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE) {
......
} else if(this.getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT) {
.........
}
Late but this will help some one
Try this in V4 Fragment
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (getFragmentManager() != null) {
getFragmentManager()
.beginTransaction()
.detach(this)
.attach(this)
.commit();
}
}
What you're trying to do is rather complicated. Android Fragments are not meant to be rotated.
I had the same problem and found a solution, though. In my case, I wanted to present a Fragment containing different menu pages that would rotate according to orientation.
Just create a Fragment that serves as a base and contains a simple LinearLayout (or any other layout type you want). This LinearLayout will serve as the canvas for our menu:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/llMenuCanvas"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
Next, we want to code the base item fragment as an abstract class, that will be implemented by all menu item fragments:
public abstract class NavMenuItem extends Fragment {
static final String TAG = "yourTag"; // Debug tag
LinearLayout canvas;
View hView; // we'll keep the reference of both views
View vView;
// All we'll need to do is set these up on our fragments
abstract int getVerticalLayoutResource();
abstract int getHorizontalLayoutResource();
abstract void setupUI(); // assigns all UI elements and listeners
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.menu_base, container, false); // sets up the layout for this fragment
// keeping our references to both layout versions
hView = inflater.inflate(getHorizontalLayoutResource(), container, false);
vView = inflater.inflate(getVerticalLayoutResource(), container, false);
canvas = view.findViewById(R.id.llMenuCanvas); // this is the magic part: Our reference to the menu canvas
// returning our first view depending on orientation
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
canvas.addView(hView);
}else{
canvas.addView(vView);
}
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setupUI(); // here we set up our listeners for the first time
}
// Here we update the layout when we rotate the device
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
canvas.removeAllViews();
// Checking screen orientation
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
canvas.addView(hView);
}
else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
canvas.addView(vView);
}
setupUI(); // we always need to rebind our buttons
}
}
And here is an example of a menu item fragment that rotates according to the device's orientation.
public class NavMenuMain extends NavMenuItem{
static final String TAG = "yourTag"; // Debug tag
// Your layout references, as usual
ImageButton btnCloseMenu;
// here we set up the layout resources for this fragment
#Override
int getVerticalLayoutResource() { // vertical layout version
return R.layout.menu_main_port;
}
#Override
int getHorizontalLayoutResource() { // horizontal layout version
return R.layout.menu_main_land;
}
#Override
void setupUI(){
// Setup button listeners and layout interaction here
// REMEMBER: the names of your layout elements must match, both for landscape and portrait layouts. Ex: the "close menu" button must have the same id name in both layout versions
}
}
Hope it helps.
All you need to do is open a new layout-land folder inside your res folder and put there xml with the same name of your fragment's layout, the framework will know to look for that .xml on orientation changed.
Look here for details.
By default, the layouts in /res/layout are applied to both portrait and landscape.
If you have for example
/res/layout/main.xml
you can add a new folder /res/layout-land, copy main.xml into it and make the needed adjustments.
See also http://www.androidpeople.com/android-portrait-amp-landscape-differeent-layouts and http://www.devx.com/wireless/Article/40792/1954 for some more options.
When you change the orientation, your fragment destroyed and recreated again (See this for better understanding). So in onConfigurationChanged, you inflate your new layout but it's useless because when your fragment recreated, the onCreateView is called again; in other words, your old layout is inflated again. So better to do this in this way:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view;
if(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
view = View.inflate(getContext(), R.frag_dashboard,null);
changeview = (ShimmerTextView)view.findViewById(R.id.changeview);
} else(getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
Toast.makeText(getContext(), "landscape", Toast.LENGTH_SHORT).show();
view = View.inflate(getContext(), R.layout.prayer_times,null);
}
return view;
}

Android load multiple menu layout files possible?

I want to have a superclass activity that defines what basic menu items each activity should have in addition to their own.
Here is my code so far:
public abstract class SuperActivity extends FragmentActivity {
protected List<TextView> textView = new ArrayList<TextView>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentResource());
setTextViews(textView);
}
protected abstract void setTextViews(List<TextView> textViews);
protected abstract int getContentResource();
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.menuitemsforallsubclasses, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.basicmenuitem1:
allSubclassActivitiesCanDoThis()
return true;
case R.id.basicmenuitem2:
allSubclassActivitiesCanDoThis2()
return true;
}
}
}
In the subclasses I would then just add something to onCreateOptionsMenu (an additional layout file) and onOptionsItemSelected, is that correct? Just want to make sure I am not on the wrong track here. Would it even be possible to load multiple layout file in the same activity?
FYI: In case you are wondering why I have a TextView array in the activity: In addition to this I want to have all TextViews of each subclass activity in an ArrayList, so that I can add listeners to all of them automatically so I know when something has changed in the activity or to do some other neat stuff.
Is what I am doing possible? Is it possible to load multiple menu layout files?

Setting an onClickListener

I am setting an on click listener and I was wondering if this was an ok way to do it? I see a lot of people define the onClickListener in line with the setOnClickListener but that seems really messy so I was wondering if I would run into any problems doing it this way down the road?
public class Login extends Activity {
protected Button login;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
login = (Button) findViewById(R.id.loginButton);
login.setOnClickListener(myOnClick());
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_login, menu);
return true;
}
protected OnClickListener myOnClick() {
OnClickListener v = new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
//Do stuff
}
};
return v;
}
}
How you define it is your personal coding style choice. You can have the entire class implement the interface, do it inline, do it as you are doing or specify the method to be called via XML. The end result is more or less the same.
If you would like to keep all your onclicklistener methods within one method you could implement the method. For this you do
login.setOnClickListener(this);
And then
extends Activity implements OnClickListener
And finally you will add the unimplemented methods. This will pass all your button clicks to the onclick method where you can use if/else or switch/case to assign whatever method.
Alternatively you can also define it in XML or use the method you've described.
However to go into the benefits and drawbacks: defining the onClick within xml can lead to problems with proguard. Personally I feel the easiest is using a switch and case within the onclicklistener, but if the method is a lot longer then it's nice to give it it's own method so to "hide" it away. If you however need common code to run after any button is pressed (for example a UI refresh) might be better to leave it to a switch and case or if/else
// Just to add for those wanting to use OnClick within xml and proguard
Add this:
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
There are multiple approaches for implementing onClickListner on the views. What you have used is also correct and will not create any problem to you. What i personally prefer is to let the Class implement OnClickListener interface and use switch case scenario inside the override onClick method .
e.g.
public class LoginExampleImplements extends Activity implements OnClickListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
btn1 = (Button)findViewById(R.id.btn1);
btn2 = (Button)findViewById(R.id.btn2);
// Set Click Listener
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.btn1:
// do stuff related btn1 click
break;
case R.id.btn2:
// do stuff related btn2 click
break;
}
}
Depends on your code style, still:
Want to use same method for a lot of buttons: let class implement listener interface, and use a switch on view id to find out which button clicked.
Really complex logic follows click: Have an inner/external class implement that listener.
Few lines, nothing special: do inline, the person reading you code need not go looking for a small piece of code.

Saving state _before_ orientation change when android:configChanges="orientation" is specified

I have an Activity in my Android app that sets different layout XMLs as its view depending on the orientation. I have declared android:configChanges="orientation" in the manifest. Now onConfigurationChanged() is called - but by this time the new orientation has already taken effect.
My goal is to try to hook into the life cycle and try to save some changes before the new orientation is in effect; so that I can restore the state when I return to the current orientation.
I hacked it as follows, but I'm not sure if this is the right way to do this. My procedure involves saving the state in onConfigurationChanged() and then calling setContentView() to set the layout for the new orientation.
public class SwitchOrientationActivity extends Activity {
private View mLandscape, mPortrait;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater li = LayoutInflater.from(this);
mLandscape = li.inflate(R.layout.landscape, null);
mPortrait = li.inflate(R.layout.portrait, null);
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (Configuration.ORIENTATION_LANDSCAPE == newConfig.orientation) {
switchToLandscape();
} else {
switchToPortrait();
}
}
private void switchToPortrait() {
/*
* Use mLandscape.findViewById() to get to the views and save the values
* I'm interested in.
*/
saveLanscapeState();
setContentView(mPortrait);
}
private void switchToLandscape() {
/*
* Use mPortrait.findViewById() to get to the views and save the values
* I'm interested in.
*/
savePortraitState();
setContentView(mLandscape);
}
}
Is there a more elegant way to achieve this?
android:configChanges="orientation" causes your activity to not be restarted on an orientation change, and so skips the regular lifecycle for this. I'd suggest, you take that out, then implement onRetainNonConfigurationInstance() and restore your state in either onCreate or onRestoreInstanceState. See this article for more info

Same Title Bar but different View below it in Android?

In one of my Android Application I need to keep the title bar same but the view that is shown in the rest of the screen changes. So, I have taken different Activity for all the views that I need to show and set the title bar in every Activities onCreate method.
Now, the problem is that I have a button in the title bar and need to perform certain action on its click event. Writing the same event handling code in every Activity class is very cumbersome. Is there any other way out that whenever there is a click event on that button of the title bar then we can have the same functionality without writing the same code in all the Activity classes.
Can we use ViewGroup for that? I don't have much idea about ViewGroup. Is that possible with ViewGroup?
If anyone knows the solution then please let me know.
Thanks & Regards
Sunil
If you are sharing view elements and functionality amongst several classes extending Activity, you might want to consider making a common superclass to handle this overlap.
The best solution is to keep a base activity like this.
public class HeaderBaseActivity extends AppCompatActivity{
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
mAppPreferences = AppUtil.getAppPreferences(this);
item_patients = menu.findItem(R.id.item_patients);
setBatchCountOnMenu(0);
RealmConfiguration realmConfig = new RealmConfiguration.Builder(this).build();
mRealm = Realm.getInstance(realmConfig);
mDotor = new Gson().fromJson(mAppPreferences.getString(Constants.SETTINGS_OBJ_DOCTOR, ""), Doctor.class);
mAppPreferences = AppUtil.getAppPreferences(this);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_logout:
/* DialogUtility.showShortToast(this, " Main manu Action Logout");*/
SharedPreferences.Editor Editor = mAppPreferences.edit();
Editor.putBoolean(Constants.SETTINGS_IS_LOGGED_IN, false);
Editor.apply();
clearRealmDB();
Intent loginIntent = new Intent(HeaderBaseActivity.this, LoginActivity.class);
loginIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(loginIntent);
finish();
break;
case R.id.item_patients:
System.out.println("current activity "+getApplicationContext());
Intent mPatientListIntent = new Intent(HeaderBaseActivity.this, PatientSummaryInfoActivity.class);
mPatientListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(mPatientListIntent);
break;
case R.id.action_doctor_profile:
openDialogOfDoctorProfile();
break;
}
return super.onOptionsItemSelected(item);
}
}
Your other activities can extend the above activity like this:
public class MainActivity extends HeaderBaseActivity{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
}
}

Categories

Resources