I have an activity that has a huge form with lots of EditTexts. If the user rotates the screen from portrait to landscape or vice-versa, all the data in the EditTexts is lost. Is there some way to retain the data so that it is not lost while changing the orientation?
I have created 2 layouts, one each for portrait and landscape. I also have the same IDs for all the Views in both the layouts.
Thanks in advance.
Save your state like this by implementing the following methods in your activity
/* simple method to create a key for a TextView using its id */
private String keyForTextView(TextView txt){
return "textView"+txt.getId();
}
/* go through all your views in your layout and save their values */
private void saveTextViewState(ViewGroup rootView, Bundle bundle){
int children = rootView.getChildCount();
for (int i = 0; i < children; i++) {
View view = rootView.getChildAt(i);
if (view instanceof TextView){
TextView txt = (TextView)view;
if (txt.getText() != null){
bundle.putString(keyForTextView(txt), txt.getText().toString());
}
}else if (view instanceof ViewGroup){
saveTextViewState((ViewGroup)view, bundle);
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
View root = findViewById(R.id.my_root_view); //find your root view of your layout
saveTextViewState(root, outState); //save state
super.onSaveInstanceState(outState);
}
and then retrieve the values in the onCreate method of your activity.
/* go through all your views in your layout and load their saved values */
private void loadTextViewState(ViewGroup rootView, Bundle bundle){
int children = rootView.getChildCount();
for (int i = 0; i < children; i++) {
View view = rootView.getChildAt(i);
if (view instanceof TextView){
TextView txt = (TextView)view;
String saved = bundle.getString(keyForTextView(txt));
txt.setText(saved);
}else if (view instanceof ViewGroup){
loadTextViewState((ViewGroup)view, bundle);
}
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//... inflate your UI here
if (savedInstanceState != null){
View root = findViewById(R.id.my_root_view); //find your root view
loadTextViewState(root, savedInstanceState); //load state
}
}
For this to work all the textboxes must have ids in both landscape and portrait layouts.
Yes. You can get the data of EditTexts using below code:
In onCreate() method,
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int ot = getResources().getConfiguration().orientation;
switch (ot) {
case Configuration.ORIENTATION_LANDSCAPE:
setContentView(R.layout.<your_landscape_layout>);
break;
case Configuration.ORIENTATION_PORTRAIT:
setContentView(R.layout.<your_portrait_layout>);
break;
}
setButtonClickListeners();
}
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int ot = getResources().getConfiguration().orientation;
switch (ot) {
case Configuration.ORIENTATION_LANDSCAPE:
setContentView(R.layout.<your_landscape_layout>);
setButtonOnClickListeners();
initializeViews();
break;
case Configuration.ORIENTATION_PORTRAIT:
setContentView(R.layout.<your_portrait_layout>);
setButtonOnClickListeners();
initializeViews();
break;
}
}
#SuppressWarnings("deprecation")
#Override
public Object onRetainNonConfigurationInstance() {
return super.onRetainNonConfigurationInstance();
}
}
In setButtonOnClickListeners(), you initiatiate all the EditTexts and in initializeViews() get the data of EditTexts and display using setText()
Add android:configChanges="orientation" in your manifest file. Within tag,
<activity
android:name=".YourActivity"
android:configChanges="orientation|keyboardHidden" >
</activity>
Thanks....
Related
I got a problem with a quite large GridView.(about 70 children) The GridView works fine if I start it on onCreate or after resumeing after pressing the home button and then return. But when I resume after coming back from sleep mode, my BaseAdapter starts again and ruin the changes I have done to it during runtime. This also make getChildAt() give a NullPointerException if I am calling it just after restart.
How can I make it just do what regular onPause(home button) does to the GridView, and avoid that the GridView is wiped out everytime I am resumeing from sleep mode?
Edit:
I have tried setting a wakelock for my Activity class that calls the BaseAdpter with no luck
2.Edit: Since I posted this question I have played around with trying to restore the GridView using this code in onPause:
SparseArray<Parcelable> array = new SparseArray<Parcelable>();
gridView.saveHierarchyState(array);
bundle = new Bundle();
bundle.putSparseParcelableArray("state", array);
And this in onResume:
try{
gridView.restoreHierarchyState(bundle.getSparseParcelableArray("state"));
}
catch(Exception e){
//Most likely first start
Log.i("SomeTag", "No GridView state found");
}
}
The strange thing is everything I seems to have jumped from one place to another on the screen and it is still crashing when I try to getChildAt(). It is also failing to get it after sleep mode.
Edit Here is the code from BaseAdapter getView(Note! some of this code is irrelevant)
public View getView (int position, View convertView, ViewGroup parent) {
mParentView = parent;
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
//sets the height for every individual box
int box = width/7*6/10;
ImageCell v = null;
if (convertView == null) {
// If it's not recycled, create a new ImageCell.
v = new ImageCell (mContext);
v.setLayoutParams(new GridView.LayoutParams(box, box));
v.setScaleType(ImageView.ScaleType.CENTER_CROP);
v.setPadding(0, 0, 0, 0);
} else {
v = (ImageCell) convertView;
}
v.mCellNumber = position;
v.mGrid = (GridView) mParentView;
v.mEmpty = true;
int id = 200;
v.setId(++id);
String map = str[position];
int pos = position;
int up = pos-10;
int down = pos+10;
int left = pos-1;
int right = pos+1;
if(up>=0){
above = str[up];
}
else{
//Do nothing
}
if(down<70){
under = str[down];
}
else{
//Do nothing
}
if(left<=-1){
//Do nothing
}
else{
lefte=str[left];
}
if(right>=70){
//Do nothing
}
else{
righte=str[right];
}
//if(left>-1|left!=9|left!=19|left!=29|left!=39|left!=49|left!=59){
// lefte = str[left];
// }
// else{
// Log.i("ImageCellAdapter", "Left is trying to break walls "+left);
//Do nothing
// }
if (map.equals("mountain")) {
//Checks surroundings to find out witch drawable to set
v.setBackgroundResource(R.color.cell_empty);
v.mEmpty = false;
//All
if(above=="mountain"&&under=="mountain"&&lefte=="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_full);
}
//Single
else if(above=="mountain"&&under!="mountain"&&lefte!="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_down);
}
else if(above!="mountain"&&under=="mountain"&&lefte!="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_up);
}
else if(above!="mountain"&&under!="mountain"&&lefte!="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left);
}
else if(above!="mountain"&&under!="mountain"&&lefte=="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_right);
}
//Double
else if(above=="mountain"&&under!="mountain"&&lefte!="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left_down);
}
else if(above!="mountain"&&under=="mountain"&&lefte!="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left_up);
}
else if(above=="mountain"&&under!="mountain"&&lefte=="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_right_down);
}
else if(above!="mountain"&&under=="mountain"&&lefte=="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_up_right);
}
else if(above!="mountain"&&under!="mountain"&&lefte=="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_up_down);
}
else if(above=="mountain"&&under=="mountain"&&lefte!="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_up_down);
}
//Triple
else if(above!="mountain"&&under=="mountain"&&lefte=="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left_right_down);
}
else if(above=="mountain"&&under=="mountain"&&lefte=="mountain"&&righte!="mountain"){
v.setImageResource(R.drawable.mountain_left_up_down);
}
else if(above=="mountain"&&under!="mountain"&&lefte=="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_left_up_right);
}
else if(above=="mountain"&&under=="mountain"&&lefte!="mountain"&&righte=="mountain"){
v.setImageResource(R.drawable.mountain_up_right_down);
}
//None
else{
v.setImageResource(R.drawable.mountain);
}
}
else if(map=="start"){
List<String> posOf = Arrays.asList(str);
startPos=posOf.indexOf("start");
v.mEmpty=false;
v.setBackgroundResource(R.color.cell_empty);
getDur();
BitmapDrawable first = (BitmapDrawable)mContext.getResources().getDrawable(R.drawable.gress);
BitmapDrawable second =(BitmapDrawable)mContext.getResources().getDrawable(R.drawable.ic_launcher);
BitmapDrawable third = (BitmapDrawable)mContext.getResources().getDrawable(R.drawable.gress);
BitmapDrawable fourth = (BitmapDrawable)mContext.getResources().getDrawable(R.drawable.ic_launcher);
final AnimationDrawable ani = new AnimationDrawable();
ani.addFrame(first, duration);
ani.addFrame(second, duration);
ani.addFrame(third, duration);
ani.addFrame(fourth, duration);
ani.setOneShot(true);
v.setImageDrawable(ani);
checkIfAnimationDone(ani);
v.post(new Runnable() {
public void run() {
ani.start();
}
});
}
else if(map=="stop"){
v.mEmpty=false;
v.setBackgroundResource(R.color.cell_empty);
v.setImageResource(R.drawable.ic_launcher);
v.setTag(1);
}
else if(map=="grass"){
v.mEmpty=false;
v.setBackgroundResource(R.drawable.gress);
}
else{
// v.setBackgroundResource (R.color.drop_target_enabled);
v.setBackgroundResource (R.color.cell_empty);
}
//v.mGrid.requestDisallowInterceptTouchEvent (true);
//v.setImageResource (R.drawable.hello);
// Set up to relay events to the activity.
// The activity decides which events trigger drag operations.
// Activities like the Android Launcher require a long click to get a drag operation started.
return v;
}
And defining the GridView in onCreate:
gridView= new BoxView(this);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH){
w.getDefaultDisplay().getSize(size);
Measuredwidth = size.x;
Measuredheight = size.y;
}else{
Display d = w.getDefaultDisplay();
Measuredwidth = d.getWidth();
Measuredheight = d.getHeight();
}
int width = Measuredwidth/7*6;
gridView.setLayoutParams(new GridView.LayoutParams(width,LayoutParams.FILL_PARENT, Gravity.CENTER_HORIZONTAL));
gridView.setNumColumns(columns);
gridView.setVerticalSpacing(0);
gridView.setHorizontalSpacing(0);
gridView.setPadding(0, 0, 0, 0);
gridView.setId(101);
gridView.setSelector(android.R.color.transparent);
gridView.setAdapter (new ImageCellAdapter(this, MAP));
I have noticed one thing in you code:
gridView.setLayoutParams(new GridView.LayoutParams(width,LayoutParams.FILL_PARENT, Gravity.CENTER_HORIZONTAL));
Even if GridView is a ViewGroup, you can't access it's LayoutParams. Just think it trough, if this would be possible that means you could put a GridView inside another GridView.
Fix this before going further because is messing with you.
If you want your GridView to be inside a LinearLayout, for example, try this:
gridView.setLayoutParams(new LinearLayout.LayoutParams(width,LayoutParams.FILL_PARENT, Gravity.CENTER_HORIZONTAL));
Here is the correct implementation of this answer:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*You have to implement what things from the gridView sould be "saved"*/
GridView gridView = new GridView(this) {
#Override
public Parcelable onSaveInstanceState() {
// Create the Parceable object with the things you want to save
Parceable stateOfGrid = ....etc
return stateOfGrid;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
// Restore your grid's parameters that you previously implemented in onSaveInstanceState
super.onRestoreInstanceState(state);
...
}
};
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
Parcelable state = savedInstanceState.getParcelable("state");
if (state != null) {
gridView.onRestoreInstanceState(state);
Log.d(this.getClass().getName(), "state restored!");
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
// Put your grid's Parceable into the bundle
super.onSaveInstanceState(outState);
Parcelable state = gridView.onSaveInstanceState();
outState.putParcelable("state", state);
}
}
Try using this tag:
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
You can use onSaveInstanceState (Bundle outState)
to save state of your gridview
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Parcelable state = gridView.onSaveInstanceState();
outState.putParcelable("state", state);
}
then in onCreate after you seted adapter to grdiview add this code
if (savedInstanceState != null) {
Parcelable state = savedInstanceState.getParcelable("state");
if (state != null) {
gridView.onRestoreInstanceState(state);
Log.d(this.getClass().getName(), "state restored!");
}
}
Is your application running in Landscape Mode?
if yes, then you should consider adding the tag
"android:configChanges="keyboard|keyboardHidden|orientation" for your activity in Manifest.xml, which will prevent android system from killing your activity and restarting it when you unlock the screen.
I have a Login screen which consists of 2 EditTexts for Username and Password. My requirement is that on orientation change , input data(if any) in EditText should remain as it is and a new layout should also be drawn. I have 2 layout xml files- one in layout folder and other in layout-land folder. I am trying to implement following 2 approaches but none of them is perfect:
(1) configChanges:keyboardHidden - In this approach, I don't provide "orientation" in configChanges in manifest file. So I call setContentView() method in both onCreate() and onConfigurationChanged() methods. It fulfills both my requirements. Layout is changed and input data in EditTexts also remains as it is. But it has a big problem :
When user clicks on Login button, a ProgressDialog shows until server-response is received. Now if user rotates the device while ProgressDialog is running, app crashes. It shows an Exception saying "View cannot be attached to Window." I have tried to handle it using onSaveInstanceState (which DOES get called on orientation change) but app still crashes.
(2) configChanges:orientation|keyboardHidden - In this approach, I provide "orientation" in manifest. So now I have 2 scenarios:
(a) If I call setContentView() method in both onCreate() and onConfigurationChanged(), Layout is changed accordingly but EditText data is lost.
(b) If I call setContentView() method in onCreate() , but not in onConfigurationChanged(), then EditText data is not lost but layout also not changes accordingly.
And in this approach, onSaveInstanceState() is not even called.
So I am in a really intimidating situation. Is there any solution to this problem? Please help. Thanx in advance.
By default, Edittext save their own instance when changing orientation.
Be sure that the 2 Edittexts have unique IDs and have the same IDs in both Layouts.
That way, their state should be saved and you can let Android handle the orientation change.
If you are using a fragment, be sure it has a unique ID also and you dont recreate it when recreating the Activity.
A better approach is to let android handle the orientation change. Android will automatically fetch the layout from the correct folder and display it on the screen. All you need to do is to save the input values of the edit texts in the onSaveInsanceState() method and use these saved values to initialize the edit texts in the onCreate() method.
Here is how you can achieve this:
#Override
protected void onCreate (Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.login_screen);
...
...
String userName, password;
if(savedInstanceState!=null)
{
userName = savedInstanceState.getString("user_name");
password= savedInstanceState.getString("password");
}
if(userName != null)
userNameEdtTxt.setText(userName);
if(password != null)
passEdtTxt.setText(password);
}
>
#Override
protected void onSaveInstanceState (Bundle outState)
{
outState.putString("user_name", userNameEdtTxt.getText().toString());
outState.putString("password", passEdtTxt.getText().toString());
}
Give the element an id and Android will manage it for you.
android:id="#id/anything"
in onConfigurationChanged method, first get the data of both the edit texts in global variables and then call setContentView method. Now set the saved data again into the edit texts.
There are many ways to do this. The simplest is 2(b) in your question. Mention android:configChanges="orientation|keyboardHidden|screenSize" in your manifest so that Activity doesn't get destroyed on Orientation changes.
Call setContentView() in onConfigChange(). but before calling setContentView() get the EditText data into a string and set it back after calling setContentView()
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mEditTextData = mEditText.getText().tostring();//mEditTextData is a String
//member variable
setContentView(R.layout.myLayout);
initializeViews();
}
private void initializeViews(){
mEditText = (EditText)findViewById(R.id.edittext1);
mEdiText.setText(mEditTextData);
}
The following should work and is standard to the activities and fragments
#Override
public void onSaveInstanceState (Bundle outState)
{
outState.putString("editTextData1", editText1.getText().toString());
outState.putString("editTextData2", editText2.getText().toString());
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate();
... find references to editText1, editText2
if (savedInstanceState != null)
{
editText1.setText(savedInstanceState.getString("editTextData1");
editText2.setText(savedInstanceState.getString("editTextData2");
}
}
Im restoring instance to restore values and it works fine for me :)
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.addtask2);
if(savedInstanceState!=null)
onRestoreInstanceState(savedInstanceState);
}
Remove android:configChanges attribute from the menifest file and let android handle the orientation change your data in edittext will automatically remain.
Now The problem you mentioned is with the progress dialog force close this is because when the orientation is changed the thread running in backgroud is trying to update the older dialog component whihc was visible. You can handle it by closing the dialog on savedinstancestate method and recalling the proceess you want to perform onRestoreInstanceState method.
Below is a sample hope it helps solving your problem:-
public class MyActivity extends Activity {
private static final String TAG = "com.example.handledataorientationchange.MainActivity";
private static ProgressDialog progressDialog;
private static Thread thread;
private static boolean isTaskRunnig;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(R.layout.main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new EditText.OnClickListener() {
#Override
public void onClick(View v) {
perform();
isTaskRunnig = true;
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void perform() {
Log.d(TAG, "perform");
progressDialog = android.app.ProgressDialog.show(this, null,
"Working, please wait...");
progressDialog
.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
//isTaskRunnig = false;
}
});
thread = new Thread() {
public void run() {
Log.d(TAG, "run");
int result = 0;
try {
// Thread.sleep(5000);
for (int i = 0; i < 20000000; i++) {
}
result = 1;
isTaskRunnig = false;
} catch (Exception e) {
e.printStackTrace();
result = 0;
}
Message msg = new Message();
msg.what = result;
handler.sendMessage(msg);
};
};
thread.start();
}
// handler to update the progress dialgo while the background task is in
// progress
private static Handler handler = new Handler() {
public void handleMessage(Message msg) {
Log.d(TAG, "handleMessage");
int result = msg.what;
if (result == 1) {// if the task is completed successfully
Log.d(TAG, "Task complete");
try {
progressDialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
isTaskRunnig = true;
}
}
}
};
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d(TAG, "onRestoreInstanceState" + isTaskRunnig);
if (isTaskRunnig) {
perform();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState");
if (thread.isAlive()) {
thread.interrupt();
Log.d(TAG, thread.isAlive() + "");
progressDialog.dismiss();
}
}
As pointed out by Yalla T it is important to not recreate the fragment. The EditText will not lose its content if the existing fragment is reused.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_frame);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Display the fragment as the main content.
// Do not do this. It will recreate the fragment on orientation change!
// getSupportFragmentManager().beginTransaction().replace(android.R.id.content, new Fragment_Places()).commit();
// Instead do this
String fragTag = "fragUniqueName";
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = (Fragment) fm.findFragmentByTag(fragTag);
if (fragment == null)
fragment = new Fragment_XXX(); // Here your fragment
FragmentTransaction ft = fm.beginTransaction();
// ft.setCustomAnimations(R.xml.anim_slide_in_from_right, R.xml.anim_slide_out_left,
// R.xml.anim_slide_in_from_left, R.xml.anim_slide_out_right);
ft.replace(android.R.id.content, fragment, fragTag);
// ft.addToBackStack(null); // Depends on what you want to do with your back button
ft.commit();
}
Saving state = Saving (Fragment State + Activity State)
When it comes to saving the state of a Fragment during orientation change, I usually do this way.
1) Fragment State:
Save and Restore EditText value
// Saving State
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("USER_NAME", username.getText().toString());
outState.putString("PASSWORD", password.getText().toString());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.user_name_fragment, parent, false);
username = (EditText) view.findViewById(R.id.username);
password = (EditText) view.findViewById(R.id.password);
// Retriving value
if (savedInstanceState != null) {
username.setText(savedInstanceState.getString("USER_NAME"));
password.setText(savedInstanceState.getString("PASSWORD"));
}
return view;
}
2) Activity State::
Create a new Instance when the activity launches for the first time
else find the old fragment using a TAG and the FragmentManager
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
fragmentManager = getSupportFragmentManager();
if(savedInstanceState==null) {
userFragment = UserNameFragment.newInstance();
fragmentManager.beginTransaction().add(R.id.profile, userFragment, "TAG").commit();
}
else {
userFragment = fragmentManager.findFragmentByTag("TAG");
}
}
You can see the the full working code HERE
Below code is work for me. Need to care two things.
Each Input Field (Edit Text or TextInputEditText) assign unique id.
Manifest activity declaration should have on configuration change attribute with below values.
android:configChanges="orientation|keyboardHidden|screenSize"
Sample activity declaration in manifest.
<activity
android:name=".screens.register.RegisterActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true"
android:label="Registration"
android:theme="#style/AppTheme.NoActionBar" />
Sample declaration of
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/inputLayout"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxCornerRadiusBottomEnd="#dimen/boxCornerRadiusDP"
app:boxCornerRadiusBottomStart="#dimen/boxCornerRadiusDP"
app:boxCornerRadiusTopEnd="#dimen/boxCornerRadiusDP"
app:boxCornerRadiusTopStart="#dimen/boxCornerRadiusDP">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/inputEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:fontFamily="#font/proxima_nova_semi_bold"
android:inputType="textCapWords"
android:lines="1"
android:textColor="#color/colorInputText"
android:textColorHint="#color/colorInputText" />
</com.google.android.material.textfield.TextInputLayout>
this may help you
if your android:targetSdkVersion="12" or less
android:configChanges="orientation|keyboardHidden">
if your android:targetSdkVersion="13" or more
android:configChanges="orientation|keyboardHidden|screenSize">
I extend ViewFlipper widget. When I initializ MyViewFlipper I pass List oа object to it. MyViewFlipper perform it list and add some views to MyViewFlipper.
My activity has android:configChanges="keyboardHidden|orientation", therefor my views hiererchy not recreating when device orientation changed.
Which method I must override in my MyViewFlipper to process orientation changed event to recreate my child views in MyViewFlipper?
How to do it others views?
Methid, that perform List of object in MyViewFlipper:
public void setUsers(List<User> users)
{
removeAllViews();
Display display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int maxWidth = display.getWidth();
screens = new LinearLayout[maxScreens];
int fillingScreen = maxScreens;
while(fillingScreen > 0)
{
LinearLayout linearLayout = new LinearLayout(getContext());
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
linearLayout.setGravity(Gravity.CENTER);
screens[fillingScreen - 1] = linearLayout;
fillingScreen --;
}
int currentUser = 0;
while(currentUser < users.size() && fillingScreen < maxScreens)
{
LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.item_people_category_item, null);
ImageView iv = (ImageView) linearLayout.findViewById(R.id.item_people_category_item_avatar);
screens[fillingScreen].addView(linearLayout);
screens[fillingScreen].measure(0, 0);
if (screens[fillingScreen].getMeasuredWidth() >= maxWidth)
{
screens[fillingScreen].removeView(linearLayout);
addView(screens[fillingScreen]);
fillingScreen ++;
}
else
{
User user = users.get(currentUser);
iv.setOnClickListener(ouUserClicked);
linearLayout.setTag(user);
App._IMAGEMANAGER.setImage(user.getPhoto(), iv, R.drawable.no_photo);
currentUser++;
}
}
if (fillingScreen < maxScreens && screens[fillingScreen].getParent() == null) addView(screens[fillingScreen]);
}
It simpe add some LinwarLayout to ViewFlipper with muximim possiable number of objects.
PS:
#Override
public void onConfigurationChange(Configuration newConf) {
// notify view flopper here
}
Other views in my layout not need to notify. They change representation self. Whih method they use for it? onMeauser()?
You should override
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
this to detect orientation changes...
It is not the method of view, nut method of activity:
#Override
public void onConfigurationChange(Configuration newConf) {
// notify view flopper here
}
New to Android so I'm writing small programs to get familiar with how things work.
What has been giving me a headache so far are views created at runtime in combination with screen rotation.
After having little luck trying to use parcels, I solved the problem by just recreating the views after a rotation.
The program will add the entered text as a TextView to a TableLayout below the EditText.
Is there a better way of solving this? I could not find any "out of the box" methods for doing this.
public class MWEActivity extends Activity {
TableLayout table;
EditText txtInput;
ArrayList<String> savedEntry = new ArrayList<String>();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
table = (TableLayout)this.findViewById(R.id.table);
txtInput = (EditText)this.findViewById(R.id.txtInput);
txtInput.setOnEditorActionListener(new OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN){
TextView newtext = new TextView(getApplicationContext());
newtext.setText(txtInput.getText());
savedEntry.add(newtext.getText().toString());
table.addView(newtext);
txtInput.setText("");
return true;
}
return false;
}
});
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putStringArrayList("entry", savedEntry);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if(savedInstanceState.getStringArrayList("entry") == null){
savedEntry = new ArrayList<String>();
savedEntry.add("Return was NULL");
}else{
savedEntry = savedInstanceState.getStringArrayList("entry");
}
for(String s : savedEntry){
TextView tv = new TextView(getApplicationContext());
tv.setText(s);
table.addView(tv);
}
super.onRestoreInstanceState(savedInstanceState);
}
}
Either you have to disable orientation change like in previous answer, or just rely
on standard facility. It will pause an then recreate your activity. No extra code is necessary. You can also provide different layouts for different orientations:
http://developer.android.com/guide/practices/screens_support.html
You can stop the reloading of the activity by putting
android:configChanges="orientation"
in the manifest.xml for the activity and implementing onConfigurationChanged.
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
If your layout is the same you should not need to do anything else.
I created a game, where I rotate through multiple activitys. Each activity stays just for a few seconds. Now I should add ads to the game. Since it doesn't make sense if the ad refreshes after just a few seconds I have to create a view which stays alive the whole time even if I start a new activity.
Since a view is bind to an activity (?) it might not be possible. So I wonder wether there is another solution to keep the adView alive while the content views are rotating.
Thanks in advance.
Edit:
Here is a simple Activity which is part of the activity cycle:
public class Punish extends ActivityWithSound implements OnClickListener {
#SuppressWarnings("unused")
private final String TAG = "Punish";
private RelativeLayout buttonContainer;
private ImageView bgImg;
private TextView nameTxt;
private TextView questTxt;
private Button mainMenuBtn;
private Button okBtn;
private Bundle bundle;
#Override
protected void onCreate(Bundle savedInstanceState) {
bundle = getIntent().getExtras();
if(bundle == null)
bundle = StbApp.getTempBundle();
setContentView(R.layout.quest);
setupView();
super.onCreate(savedInstanceState);
}
#Override
protected void onResume() {
soundtrack.startFX(R.raw.fx_execution);
super.onResume();
}
#Override
protected void onPause() {
StbApp.getTempBundle().putInt(Victim.VICTIM, bundle.getInt(Victim.VICTIM));
soundtrack.stopAllFX();
super.onPause();
}
#Override
public void onClick(View view) {
if (view == mainMenuBtn){
//TODO Continue Function
Intent intent = new Intent(this, TitleScreen.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
StbApp.setContinueBtn(true);
StbApp.getLastActivity().setClass(this, Punish.class);
startActivity(intent);
}
if (view == okBtn){
if (StbApp.getPenalty() == PenaltyType.LEAVE){
StbApp.getPlayer().remove(bundle.getInt(Victim.VICTIM));
StbApp.setNumberOfPlayer(StbApp.getNumberOfPlayer()-1);
}
if (StbApp.getNumberOfPlayer() == 2 && StbApp.getPenalty() == PenaltyType.LEAVE)
startActivity(new Intent(this, GameOver.class));
else {
startActivity(new Intent(this, Round.class));
}
}
}
#Override
public void onBackPressed() {
return;
}
private void setupView() {
float textSize = (float) getResources().getDimension(R.dimen.standard_text_size)/getResources().getDisplayMetrics().scaledDensity;
buttonContainer = (RelativeLayout) findViewById(R.id.container_button);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) findViewById(R.id.container_button).getLayoutParams();
params.setMargins(0, 0, 0, (int) (StbApp.AdHeight*1.3));
buttonContainer.setLayoutParams(params);
bgImg = (ImageView) findViewById(R.id.imgv_girl);
Log.d(TAG, "PlayerSize " + StbApp.getPlayer().size());
nameTxt = (TextView) findViewById(R.id.text_victim_name);
Log.d(TAG, "PlayerSize " + StbApp.getPlayer().size());
Log.d(TAG, "PlayerIndex bundle.getInt(Victim.VICTIM) " + bundle.getInt(Victim.VICTIM));
nameTxt.setText(StbApp.getPlayer().get(bundle.getInt(Victim.VICTIM)).getName());
nameTxt.setTextSize(textSize);
questTxt = (TextView) findViewById(R.id.text_quest);
switch (StbApp.getPenalty()) {
case LEAVE:
questTxt.setText(getResources().getString(R.string.punish_leave));
break;
case DRNK:
questTxt.setText(getResources().getString(R.string.punish_drink));
break;
case UNDRESS:
questTxt.setText(getResources().getString(R.string.punish_undress));
break;
}
questTxt.setTextSize(textSize);
mainMenuBtn = (Button) findViewById(R.id.button_mainmenu);
mainMenuBtn.setOnClickListener(this);
okBtn = (Button) findViewById(R.id.button_ok);
okBtn.setOnClickListener(this);
}
#Override
protected void onDestroy() {
if (bgImg.getDrawable() != null){
bgImg.getDrawable().setCallback(null);
bgImg.setImageDrawable(null);
}
super.onDestroy();
}
What I need would be an alternative for onDestroy, onPause and onResume.
A solution could be using a ViewFlipper/ViewSwitcher instead of jumping activities and then placing the adsView over or under the ViewFlipper/ViewSwitcher - It will however probably require quite a large re-write of your app.