onSave- / onRestoreInstanceState not working - java

The situation is that I want to create an app, which should be able to create a new TextView in a LinearLayout everytime a button is clicked. A text from and EditText ist assigned to said TextView. I managed to pull this off. However I've been struggling on how to save all of the created TextViews, once the app is closed and started up again. I tried using OnSaveInstanceState and OnRestoreInstance state to save an Array, to which I save every text that is displayed on a TextView. After restored I would check the size of the arraylist and create for a TextView for every value in the arraylist, so that all is restored. However, it doesn't work. I know, that the names are saved to the arraylist, however, nothing is restored, when I restart the app. Here's my Code:
public class MainActivity extends AppCompatActivity{
private ArrayList<String> SubjectArray = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button savebtn = findViewById(R.id.MAINsave);
EditText newname = findViewById(R.id.newname);
CheckBox promo = findViewById(R.id.MAINpromo);
LinearLayout linearlayout = findViewById(R.id.MAINln);
savebtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
newsub();
}
});
}
private void newsub(){
EditText newname = findViewById(R.id.newname);
CheckBox promo = findViewById(R.id.MAINpromo);
LinearLayout MAINln = findViewById(R.id.MAINln);
SubjectArray.add(newname.getText().toString());
Toast.makeText(MainActivity.this, "Added To Array", Toast.LENGTH_SHORT).show();
RelativeLayout.LayoutParams Params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200);
Params.setMargins(0, 10, 0, 10);
TextView newsubject = new TextView(MainActivity.this);
newsubject.setText(newname.getText().toString());
newsubject.setGravity(View.TEXT_ALIGNMENT_CENTER);
newsubject.setBackgroundColor(GRAY);
newsubject.setLayoutParams(Params);
MAINln.addView(newsubject);
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
SubjectArray = savedInstanceState.getStringArrayList("KEY_SUBJECTARRAY");
LinearLayout MAINln = findViewById(R.id.MAINln);
int subjectcount = SubjectArray.size();
for (int i = 0; i <= subjectcount; i++) {
RelativeLayout.LayoutParams Params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200);
Params.setMargins(0, 10, 0, 10);
TextView restoredsubject = new TextView(MainActivity.this);
restoredsubject.setText(SubjectArray.get(i));
restoredsubject.setGravity(View.TEXT_ALIGNMENT_CENTER);
restoredsubject.setBackgroundColor(GRAY);
restoredsubject.setLayoutParams(Params);
MAINln.addView(restoredsubject);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArrayList("KEY_SUBJECTARRAY", SubjectArray);
}
}

you can manage a local database table and every time you are adding the new TextView just insert the Text of that TextView in the table . When you start your application first check the data of that table, if available then get the data in a list .
After getting list
Apply a loop on list size
Add a new textview inside body of loop
Set the current indexed text in Added TextView inside the loop.
you can clear table also when you need.

When you say "restart the app" you mean, a fresh start?
The savedInstanceState is not made for this. You save the state for moments when the instance of your Activity gets recreated.
As an example, when the user is turning the phone to landscape formatting. Then a onConfigurationChanged event happens (depends on how you set up your activity in the manifest) and onCreate gets called with the Bundle you saved in onSaveInstanceState.
To persist your state over app sessions, best approach is either saving it in the SharedPreferences (and loading them in onCreate of your MainActivity oder Application class) or to persist them in a database table.

The onRestoreInstanceState not made this purpose. It has a different purpose like, if your activity was previously destroyed and you going to recreate the activity again, at this you could be able to retrieve the saved data. For more details click here.
In this case, recommend using preferences or lightWeight persistence libraries like Room. So you could retrieve the details any point the app until clear the data of the app.

Related

How to use SharedPreferences in android to get value from PhotoView id/image id

I am creating an android application, I am displaying images from drawable folder based on the numbering, my activity start showing images from number 1 and I am using buttons to show next or previous image.
I am trying to save the value of image user was viewing while pressed back button using shared preferences. my code looks like this
private int currentPage = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_read);
final Button btnNext = findViewById(R.id.btnNext);
final Button btnBack = findViewById(R.id.btnBack);
listOfObjects = getResources().getStringArray(R.array.object_array);
images = getResources().obtainTypedArray(R.array.object_image);
itemImage = (ImageView)findViewById(R.id.imgSpace);
final Spinner spinner = (Spinner)findViewById(R.id.spinner);
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(this,R.layout.my_spinner, listOfObjects);;
spinnerAdapter.setDropDownViewResource(R.layout.my_spinner);
spinner.setAdapter(spinnerAdapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
itemImage.setImageResource(images.getResourceId(spinner.getSelectedItemPosition(), -1));
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
final ImageView img = findViewById(R.id.imgSpace);
PhotoView photoView = (PhotoView) findViewById(R.id.imgSpace);
img.setImageResource(getResources().getIdentifier("page_"+currentPage,"drawable",getPackageName()));
btnNext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(spinner.getSelectedItemPosition() < spinner.getAdapter().getCount()){
spinner.setSelection(spinner.getSelectedItemPosition()+1);
}
}
} );
}
currentPage is the variable which I want to save after pressing back button or to go to any other activity and also if user minimises the application, I want to pass it to main activity in order to show the extra button of resume activity and also to show the exact image the user was seeing before pressing the back button when user clicks on resume activity button.
I am using Chris Banes's photoview to show the image. I know how to pass variables and et them in PHP however android is new for me. ANy help would be appreciated.
SharedPreferences prefs = getSharedPreferences("nameOfSharedPreference", MODE_PRIVATE);
SharedPreferences.Editor editor = getSharedPreferences("nameOfSharedPreference", MODE_PRIVATE).edit();
editor.putInt("image", R.imageID).commit();
#Override
onBackPressed(){
image = prefs.getInt("image", defaultValue) //Default value is taken if the pref doesn't exist yet
}
onBackPressed() is called automatically by android when pressing the phone back button. You can call it manually:
onBackPressed();
I don't understand the question completely but you could write:
SharedPreferences prefs = getSharedPreferences("nameOFPref", MODE_PRIVATE);
private int currentPage = prefs.getInt("image", 1);
The way it works is it saves a file in nameOfPref in the "image" section so you get it back when the program launches. Here we put 1 as default value so the value it will take is 1 the first time you launch the program. You should write:
SharedPreferences.Editor editor = getSharedPreferences("nameOfSharedPreference", MODE_PRIVATE).edit();
editor.putInt("image", idOFImageCurrentlyVIewed).commit();
Later in the program each time the user changes image. So it will change the preference and will make sure that when you launch the program back currentImage will take the right value.
In android studio you can look at the file system of emulated phones. For preferences you need to go in Android/data/com.yourPackageName/preferences and you should see your file there if everything goes fine.

Dynamically Add and Save buttons in Android studio

Currently i managed to create the buttons dynamically on a view
Here is the my code to create the buttons;
public void Add_on(View v) {
AlertDialog.Builder mbuilder = new AlertDialog.Builder(Mb.this);
View mview = getLayoutInflater().inflate(R.layout.activity_mb1, null);
EditText number = (EditText) mview.findViewById(R.id.etnum);
Button Create = (Button) mview.findViewById(R.id.etcreate);
Button Cancel = (Button) mview.findViewById(R.id.etcancel);
Create.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
if (!number.getText().toString().isEmpty())
{
Toast.makeText(Mb.this, "Number can be NULL",Toast.LENGTH_SHORT).show();
LinearLayout yenilayout = new LinearLayout(Mb.this);
int n =1;
for(int i=0; i<n; i++)
{
Button yeniButton = new Button(Mb.this);
yenilayout.addView(yeniButton);
yeniButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(Mb.this, "Button is working",Toast.LENGTH_SHORT).show();
}
});
}
altlayout.addView(yenilayout);
} else {
Toast.makeText(Mb.this, "Number cannot be NULL",Toast.LENGTH_SHORT).show();
}
}
});
But whenever i recall the activity, the buttons are no longer exist. So May i know can i place the button there permanently?
Thank you for suggestions
You can use Bundle to save an activity's state and recreate it in the onCreate() method. This works for a particular instance of Activity, so can be used to save data concerning selection, or user input etc., but not data that you need to be persistent across application launches.
To use the Bundle, override the onSaveInstanceState(Bundle) and onRestoreInstanceState(Bundle) methods in the Activity class. You can use methods from Bundle to save whatever data you like in a map, and get it back in onRestoreInstanceState(Bundle), which is called in onStart().
The default implementations already handle most UI stuff though, and I would have thought this would keep track of your buttons for you, so it may be that your question is actually about associating some persistent data with your application. (this also means that if you do override the above methods, you should make sure to call the super methods in the first line of your implementation).
If you need persistent data across application launches, then the quickest and easiest way would be to use SharedPreferences, see this answer for an example.

Changing background in other activities with a button

I have a problem. I have 3 activities (MainActivity, DetailsActivity, SettingsActivity) and in SettingsActivity I have a Togglebutton "Nightmode". What I want is, when the button is changed, change background of all three activities on gray color.
public class SettingsActivity extends AppCompatActivity {
//This is SettingsActivity(not Main one)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
TextView SettingsTitle = (TextView) findViewById(R.id.SettingsTitle);
TextView NightText = (TextView) findViewById(R.id.NightmodeText);
ToggleButton toggleNightMode = (ToggleButton) findViewById(R.id.toggleNightmode);
final RelativeLayout NightBG = (RelativeLayout) findViewById(R.id.NightBG);
final LinearLayout DetailsBG = (LinearLayout) findViewById(R.id.mainBG);
final LinearLayout HomeBG = (LinearLayout) findViewById(R.id.HomeBG);
toggleNightMode.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NightBG.setBackgroundColor(Color.parseColor("#545657"));
HomeBG.setBackgroundColor(Color.parseColor("#545657"));
DetailsBG.setBackgroundColor(Color.parseColor("#545657"));
}
});
NightBG is in the same activity as that java file (SettingsActivity). But HomeBG is in MainActivity and DetailsBG is in the DetailsActivity. Everytime I start the app, and press on that button, app craches. If I delete HomeBG and DetailsBG from this file, it works just fine with changing current layout's color to gray. Please help me.
One easy way to store little settings like this across multiple activities that may not be open/active at the time of the button click would be to use SharedPreferences.
It might be a little overkill for such a simple piece of code but you can always give it a try if you don't find anything else.
Your code could look something like this:
toggleNightMode.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Set the color of this activity
int color = Color.parseColor("#545657")
View view = SettingsActivity.this.getWindow().getDecorView();
view.setBackgroundColor(color);
// Save color preference
SharedPreferences sharedPref = SettingsActivity.this.getSharedPreferences("bgColorFile",Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("color", color);
editor.apply();
}
});
And then when you open your activities you place something like this in the onStart() or onCreate() method of your activity:
// Get the color preference
SharedPreferences sharedPref = getSharedPreferences("bgColorFile",Context.MODE_PRIVATE);
int colorValue = sharedPref.getInt("color", 0);
View view = this.getWindow().getDecorView();
view.setBackgroundColor(colorValue);
So what you're actually doing is storing the background color as persistent data and fetching it once you reopen/open the activity that you want to have the color on. The benefit of this method is that whenever you close your app the preferred background color will be remembered. I hope this helps.
Change background for current activity in the same activity. Since DetailsActivity is not running, you can't do that, it gives you null pointer. Is kind of you are trying to eat 3 apples and you have just one. After current activity is started, change background.
Update:
You can do that in current activity and just in current activity:
findViewById(android.R.id.content).setBackground(getColor(R.color.your_color));
Don't try to call this in other activities that are not running.
setBackground()
or
setBackgroundColor()
If your other activities are open, you should send a message to the other activities by using an Intent.
How to send string from one activity to another?
When you receive the Intent you could then set the background of the activity.
If your other activities are not open yet, you will not be able to send an Intent to them. In this case you could have each Activity reference a static value in your main activity that could contain the current background color. You would want to reference that value on the other activities on create functions.
Here is an example on how to reference a variable from another activity.
How do I get a variable in another activity?
This might not be the most pretty way to handle it but it should work.
as Ay Rue said you have 2 options: use static variable for that button, and then in onResume of each activity, check the value of the static variable (true or false). or you can save a private variable nightMode and then pass this value in the intent when you need to move to the other two activities.
don't set the background color if you already set before and have an updated background color.

How to dynamically add rows from a table (sqlite) to layout? (Android)

I am trying to take all the rows from my db and add it to the current layout, also, making each row clickable in the layout to take the user to a new screen with the id...
Here is my current code, but stuck on that part... I understand that I can put an onClickListener, but then does it have to be a button?
For a visual representation refer to a notepad app on any device where each note title appears and clicking on it takes you to that note.
public class MainActivity extends Activity implements OnClickListener {
private Button add_new_dictionary;
// Database helper
private DatabaseHelper db;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// db setup
db = new DatabaseHelper(getApplicationContext());
// get all dictionaries
List<db_dictionary> allDictionaries = db.getAllDictioniaries();
for (db_dictionary dictionary_found : allDictionaries) {
// create new view for each dictionary name include id and make it
// dynamic and include onclick to take to dictionary_view screen
Button dictionary_button = new Button(this);
}
add_new_dictionary = (Button) findViewById(R.id.add_new_dictionary);
}
#Override
public void onClick(View v) {
if (v == add_new_dictionary) {
Intent add_new_dictionary_intent = new Intent(MainActivity.this,
add_new_dictionary.class);
startActivity(add_new_dictionary_intent);
}
}
}
To re-iterate the question: How do I go about dynamically taking rows from my db and adding it to my layout dynamically based on how many results are returned from the query? (However, the rows should be able to point to a new screen with the dictionary id)
All views in android can implement the OnClickListener interface. So no, it doesn't HAVE to be a button.
As you've decided to use the activity to handle this then you need to tell your code to pass the event to your implementation wihin your activity.
// create new view for each dictionary name include id and make it
// dynamic and include onclick to take to dictionary_view screen
Button dictionary_button = new Button(this);
dictionary_button.setOnClickListener(this);
A trick I use to store information is the setTag method which would allow you to retrieve the correct reference during your onClick:
dictionary_button.setTag(some_record_id);
Then retrieve it later:
#Override
public void onClick(View v) {
if (v == add_new_dictionary) {
Intent add_new_dictionary_intent = new Intent(MainActivity.this,
add_new_dictionary.class);
startActivity(add_new_dictionary_intent);
}
else (
Object tag = v.getTag();
//now launch the detail activity using the data from the tag
}
}
You should really look into ListAdapters and cursors to do this properly, but this method should get you going for now
If you need to pick data from a db and show it as a list (getting click events) you should probably look into CursorAdapter and ListView
http://developer.android.com/reference/android/widget/CursorAdapter.html
http://developer.android.com/reference/android/widget/ListView.html
You can fins many examples on the web on how to use a cursoradapter and the listview

How to implement a ProgressDialog correctly so that time-consuming methods are called in background?

I use an BaseAdapter to display a list of objects. This objects are fetched from a server. The getView() method of the BaseAdapter is the following:
/* (non-Javadoc)
* #see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
*/
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout content = (LinearLayout) View.inflate(lexs, R.layout.favorite_item, null);
LinearLayout paragraphView = new LinearLayout(lexs);
paragraphView.setBackgroundColor(Color.RED);
paragraphView.setGravity(Gravity.CENTER_VERTICAL);
paragraphView.setOrientation(LinearLayout.HORIZONTAL);
ImageView img = new ImageView(lexs);
img.setImageResource(R.drawable.down);
paragraphView.addView(img);
img.setPadding(0, 0, 5, 0);
img.setOnClickListener(new WorkspaceOnClickListener(position));
TextView text = new TextView(lexs);
text.setTextColor(Color.WHITE);
text.setText(favorites.get(position).getParentChapter().getBook().getName() + ": §" + favorites.get(position).getName());
text.setOnClickListener(new ParagraphOnClickListener(position));
DisplayMetrics metrics = new DisplayMetrics();
lexs.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int maxWidth = metrics.widthPixels - 100;
text.setMaxWidth(maxWidth);
text.setMinWidth(maxWidth);
paragraphView.addView(text);
ImageView imgClose = new ImageView(lexs);
imgClose.setMinimumHeight(30);
imgClose.setMinimumWidth(30);
imgClose.setImageResource(R.drawable.close);
imgClose.setPadding(5, 0, 0, 0);
paragraphView.addView(imgClose);
imgClose.setOnClickListener(new CloseListener(position));
content.addView(paragraphView);
content.setPadding(0, 0, 0, 10);
if (favorites.get(position).isExpanded()) {
LinearLayout textLayer = new LinearLayout(lexs);
textLayer.setBackgroundColor(Color.rgb(214, 214, 214));
LinearLayout left = new LinearLayout(lexs);
left.setOrientation(LinearLayout.VERTICAL);
ImageView moveUp = new ImageView(lexs);
moveUp.setImageResource(R.drawable.move_up);
moveUp.setOnClickListener(new MoveListener(UP, position));
moveUp.setPadding(0, 0, 0, 10);
left.addView(moveUp);
ImageView moveDown = new ImageView(lexs);
moveDown.setImageResource(R.drawable.move_down);
moveDown.setOnClickListener(new MoveListener(DOWN, position));
left.addView(moveDown);
TextView paragraphText = new TextView(lexs);
paragraphText.setText(favorites.get(position).getText());
paragraphText.setTextColor(Color.BLACK);
LinearLayout right = new LinearLayout(lexs);
right.setOrientation(LinearLayout.HORIZONTAL);
right.addView(paragraphText);
textLayer.addView(left);
textLayer.addView(right);
content.addView(textLayer);
img.setImageResource(R.drawable.up);
}
return content;
}
}
So I'd like that this whole method is called in background and during the method is executed, a ProgresDialog is shown. I defined the ProgressDialog the following way:
public class LoadingInformation {
private static ProgressDialog progressDialog;
public static void showProgressInformation(Context view) {
progressDialog = ProgressDialog.show(view, "Please wait...", "Doing Extreme Calculations...", true);
System.out.println("Start Loading Screen");
}
public static void stopShowingProgressInformation() {
Handler handler=new Handler();
handler.post(new Runnable(){public void run(){progressDialog.dismiss();}});
System.out.println("Stop Loading Screen");
}
}
But the problem is, i don't know how to implement the calls correctly. I tried to replace all lines:
adapter.notifyDataSetChanged();
With the following code:
public void updateFavoriteList() {
LoadingInformation.showProgressInformation(lexs);
new Thread() {
public void run() {
lexs.runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
LoadingInformation.stopShowingProgressInformation();
}
});
}
}.start();
}
But unfortunately this doesn't work as expected. Has anybody a hint how to do it betteR?
The getView() method of the adapter isn’t responsible for downloading the content in the list, all it does is build the current row being displayed in the ListView. getView() doesn’t have anything to do with actually downloading the data from a network resource, which is where you wan’t the ProgressDialog.
When you instantiate the adapter (not shown in your code) you pass the data you want your BaseAdapter to populate the ListView with. Either the data has already been downloaded and simply handed off to the adapter, or the adapter itself does in the constructor.
If you wan’t the dataset for your ListView to change, you’re going to have to create some setter method or have a method in the adapter that refreshes the dataset from the network automatically. In any case though, this is where you want the ProgressDialog to show, not while the getView() method is setting up the ListView row.
P.S. One other suggestion I'd give is to make use of the convertView param passed to getView(). It'll improve the performance of your list by a lot if you have a large list. Check out Romain Guy's presentation about ListView in either the Google I/O 2009 or 2010 talks.
EDIT:
So “favorties” is an array (or List, etc.) of data for your ListView. You most likely pass the “favorites” data to the adapter via a constructor argument. At that point the data already exists in the adapter, or you’re passing a network location so the adapter can fetch the data and display it in the ListView.
For simplicity sake, lets say you download some string data into a String array, before passing it to the adapter. Each element contains a string to be displayed in your ListView via a TextView. You pass that array to the adapter and it handles formatting the ListView for you with the logic in the getView() method.
Now while you are downloading the data to populate the ListView with, you want to show the ProgressDialog to let the user know something is happening. Like some people have already said, you would use an AsyncTask to accomplish this. AsyncTask will allow you to display a ProgressDialog via onPreExecute() while you actually download the data in doInBackground() and get rid of the dialog in onPostExecute().
I hope that makes sense.
The time consuming tasks should be handled with an AsyncTask.
Read https://developer.android.com/resources/articles/painless-threading.html
It has been added to the Android framework to help you do these time consuming things and update progress dialogs without having to code the boilerplate of Tasks and Handlers yourself.

Categories