I am new to android and am working on a fairly basic android application where users are able to create items that are added to a ListView. On creation of each item I create a instance of the 'clicker' class which keeps track of each items name/tick count/other statistics.
when on of the items in the list are clicked it launches a general activity, used by all of the items. I pass the 'clicker' class object to the activity so that it may construct initialize the textviews.
This is what happens when a list item is clicked (the clicker instances are created in a hashmap named clickers, so I first retrieve the key, then call clickers.get(key))
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
// pass along some ID so that the new activity can retrieve info
String clickedName = clickerListItems.get(arg2);
Intent intent = new Intent(MainActivity.this, ClickerActivity.class);
intent.putExtra("clickerName", clickers.get(clickedName));
startActivity(intent);
}
This is how the general activity receives the info
public class ClickerActivity extends Activity {
protected Clicker currentClicker;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clicker);
Intent i = getIntent();
currentClicker = (Clicker) i.getSerializableExtra("clickerName");
TextView clickerHeader = (TextView) findViewById(R.id.clickerHeader);
clickerHeader.setText(currentClicker.getName());
final TextView countDisplay = (TextView) findViewById(R.id.countText);
countDisplay.setText(Integer.toString(currentClicker.getCount()));
final Button incrementButton = (Button) findViewById(R.id.incrementButton);
final Button resetButton = (Button) findViewById(R.id.resetButton);
(continues)
here's what the general activity looks like to add some perspective. It's fairly plain right now seeing as I am just starting out.
The issue is that say I click on item A of the ListView and increment it's counter (one of the stats) and then press the back button to return to the List Activity. Now if I click on the same listitem to reload the activity none of the data seems to have been saved. Is the clicker class instance not actually being altered? How do I save the info or restore the info? I would do something onRestoreInstanceState but since this is an activity that may be loaded by any item in the list that wouldn't work, right?
If there any clarification is required please let me know, thank you.
You can use shared preferences, to preserve those values.
http://developer.android.com/reference/android/content/SharedPreferences.html
By using this, none of your data will be lost. Just make sure that when you are starting the app again after killing it, you reset the values stored, so that your values are saved for activity relaunch, but not for app relaunch (unless you want it for that too).
EDIT: You can also put all your main code into an Asynctask(), so that processing would be done in the background and it would be kept alive even when you come out of the app.
Related
Please give me a hand with an issue I am having with a ListView and its related data in my android development project.
I have an activity called OrderForm that gets started by an Intent from the activity UserProfile as such:
In UserProfile
Intent intent = new Intent(this, OrderForm.class);
startActivity(intent);
Then in OrderForm there is an EditText and an add button to add String items to an ArrayList, and the UI gets populated accordingly.
When I click the back button (back to UserProfile) and go via the Intent to OrderForm again, the UI does not show the list items, why is that?
I realize I can use Room for persistence and even SharedPreferences, but
I wanted to see if there is cleaner, more efficient method, otherwise the less code the better.
Also, maybe I'm not understanding them correctly, but I tried onSaveInstanceState and onRestoreInstanceState and they don't work for me.
Thanks in advance.
Here is part of the code from OrderForm
public class OrderForm extends AppCompatActivity {
ArrayList<String> list;
ListView itemList;
ArrayAdapter<String> arrayAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_order_form);
itemList = findViewById(R.id.itemList);
itemText = findViewById(R.id.item);
linearLayout = findViewById(R.id.no_items_container);
orderContainer = findViewById(R.id.orderContainer);
list = new ArrayList<>();
arrayAdapter = new ArrayAdapter<String>(this,
R.layout.list_item,R.id.rowItem, list)
{
#Override
public View getView(int position,
View convertView,
ViewGroup parent) {
// some custom stuff here
}
}
public void addItem(View view)
{
String item = itemText.getText().toString().trim().toLowerCase();
if(!item.isEmpty() && list.indexOf(item) == -1) {
arrayAdapter.add(item);
}
}
You need to understand how Activity lifecycles work.
https://developer.android.com/guide/components/activities/activity-lifecycle.html
Your issue is that when pressing the back button, your OrderForm Activity is destroyed and effectively your arraylist/list view is destroyed. To avoid this problem, you'll have to store the values somewhere for example SharedPreferences, create a text file holding your strings and store it, or return the arraylist back to the UserProfile class where you'll store/handle them (to do that use startActivityForResult() instead of startActivity())
When I click the back button (back to UserProfile) and go via the Intent to OrderForm again, the UI does not show the list items, why is that?
In your onCreate() method, you have this:
list = new ArrayList<>();
arrayAdapter = new ArrayAdapter<String>(..., list)...
Unless you persist your data in some way, list is always going to be empty when your activity starts up.
I realize I can use Room for persistence and even SharedPreferences, but I wanted to see if there is cleaner, more efficient method, otherwise the less code the better.
Exactly what you need to store will help define the best way to store it. For a simple list of strings, probably SharedPreferences is the simplest solution.
Also, maybe I'm not understanding them correctly, but I tried onSaveInstanceState and onRestoreInstanceState and they don't work for me.
These methods are used to store data when an activity is destroyed and then recreated, which commonly happens when the user rotates the device (but can also happen for various other reasons). When you exit your activity (by pressing back to UserProfile), these methods aren't triggered.
I have searched through StackOverflow, but have not found a proper answer yet.
I have created a ListView (iteration of a checkbox + itemview) and populated it through my customAdapter (which extends BaseAdapter).
I have a button which takes the values and print it on the screen via a Toast.
So far, so good.
Next step, I still have the button in the MainActivity, but the ListView is now in a child activity that I reach by clicking an image (ImageView placed in the MainActivity). I can still check the checkboxes, but I face two issues:
I am still not able to pass the values to the MainActivity, where they will be printed on screen (or manipulated)
As soon as I press the back button to go back to the MainActivity and I press again the image, every CheckBox that was checked is not checked anymore (they came back to default state)
I don't think that code is needed, as it comes from a standard implementation (ListView - customAdapter with ViewHolder implementation, ...), but in case just let me know.
Thanks a lot in advance!
You can put which checkboxes are checked into sharedpreferences. Then move the listview initialization code to Activity's onResume method.
Sample class to handle sharedpreferences data:
class DataHandler {
private final SharedPreferences dataStore;
DataHandler(Context mContext) {
dataStore = mContext.getSharedPreferences("appname", Context.MODE_PRIVATE);
}
int which() {
return dataStore.getInt("some_key",0);
}
void setCheckedItem(int itemwhat) {
dataStore.edit().putInt("some_key",itemwhat).apply();
}
}
For multiple values, you can put them into an array then convert them to string using toString() method and save. And, to get the values:
String x = "2,3,4,5"; //assume
String[] y = new String[]{x};
int checkablepositions = Integer.parseInt(y[0]); // y[0]....y[y.length-1]
Now, at MainActivity's onResume(), Assume that you have initialized ListView as 'mainList'.
CheckBox x1y2z3 = (CheckBox)mainList.getChildAt(new DataHandler(getBaseContext).which());
x1y2z3.setChecked(true);
And for Saving item,
I would recommend you to show them in an alert-dialog instead of in a Toast. Then set a Positive button to get the values from below code and save them.
Or, if you directly save the values from listview onClick :
mainList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
new DataHandler(getBaseContext()).setCheckedItem(position);
}
});
That's it. I'm really new at programming (as you can see my StackOverFlow rep) but hope it will be able to help you.
The main concept is to : store the value → get the value → parse the value → show it on UI.
I have an app where in the mainActivity the user has about 5 options to choose from. Clicking one of them opens a new activity but essentially all 5 opens up identical activities with different headings. In the newly opened activities, users use multiple rating bars to delegate points to some specified attributes. Using SharedPreference, can I save the entire activity so when I back out, click on the same option everything isn't gone? Or do I need to save let's say, the individual rating bar values using the SharedPreference?
Here is some code for one of the activities that opens from a button click. Something is terribly wrong because it is crashing now. Any suggestions?
public class MageSkillScreen extends AppCompatActivity
{
public float skillPoints = 10;
public float strengthRating;
public float intellectRating;
public float wisdomRating;
public float dexterityRating;
public float totalSkill;
public float mageStrength;
public float mageDexterity;
public float mageIntellect;
public float mageWisdom;
public RatingBar strengthBar;
public RatingBar intellectBar;
public RatingBar wisdomBar;
public RatingBar dexterityBar;
Button submit;
//preferences
SharedPreferences magePref;
boolean rememberRatings = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mage_skill_screen);
strengthBar = (RatingBar) findViewById(R.id.mageStregth);
intellectBar = (RatingBar) findViewById(R.id.mageInt);
wisdomBar = (RatingBar) findViewById(R.id.mageWisdom);
dexterityBar = (RatingBar) findViewById(R.id.mageDext);
submit = (Button) findViewById(R.id.submit);
}
#Override
public void onPause()
{
SharedPreferences.Editor edit = magePref.edit();
edit.putFloat("strengthPts", strengthBar.getRating());
edit.putFloat("dexterityPts", dexterityBar.getRating());
edit.putFloat("intellectPts", intellectBar.getRating());
edit.putFloat("wisdomPts", wisdomBar.getRating());
//edit.putString("pointsLeft", ptsRemaing.getText().toString());
edit.commit();
super.onPause();
}
#Override
public void onResume()
{
super.onResume();
rememberRatings = magePref.getBoolean("mageRatings", true);
mageStrength = magePref.getFloat("strengthPts", 0.0f);
mageDexterity = magePref.getFloat("dexterityPts", 0.0f);
mageIntellect = magePref.getFloat("intellectPts", 0.0f);
mageWisdom = magePref.getFloat("wisdmPts", 0.0f);
}
}
update view activity in function onResume
#Override
protected void onResume() {
super.onResume();
//load SharedPreferences again and update view
}
Okay, let's go step by step.
So I have an app where in the mainActivity the user has about 5
options to choose from.
Okay, sounds good, so you have 5 buttons in MainActivity for users.
Clicking one of them opens a new activity but essentially all 5 opens
up identical activities with different headings.
Can, you please be more specific here, click on one button should launch one single activity not all 5. And this is not possible at a time only one activity exists for the user to view or interact with. Others, goes in the backstack.
Please read about activity lifecycle for the same.
https://developer.android.com/guide/components/activities/activity-lifecycle.html
Using SharedPreference, can I save the entire activity so when I back
out, click on the same option everything isn't gone?
No, you can't save the whole activity inside Sharedpreference. SharedPref is used to store key value pairs, like hashMap and only primitive values, you can store activities and you should never do it.
https://developer.android.com/reference/android/content/SharedPreferences.html
Now, coming back to the solution for the same.
It totally depends on the usecase you are trying to implement, if you are launching different activities on each button click and want to persist some data across other activities, store the primitive values in sharedpref
and then access it in other activities.This also holds, true if you want to persist the same data in app re-launch.
If not, then you can have a singleton object and modify it and access it, across other activities, make sure to make it null to avoid memory leak.
I hope it clears your doubt.
Cheers..!!
I have an Android app that displays a list using a ListView, and there's an action bar button to clear said list. I decided to add a confirmation dialog so people don't accidentally delete all their entries, and I'm running into problems. If I use setListAdapter inside the onclick for the "yes" button within the dialog, it won't compile. If I use it outside that onclick, it'll work but not refresh the list until the user backs out of the activity and goes back into it, which for obvious reasons is not appropriate. Here's my method that gets called when the "clear list" action bar button is pressed, which contains the relevant onclick for the internal buttons.
I have a feeling I shouldn't be using "this" in setListAdapter since with the dialog, this no longer corresponds to the listview activity I think? But I'm not sure what to put instead.
public void clearTrigger(MenuItem item) {
//Set up a dialog with two buttons to verify that the user really wants to delete
everything
confirm = new Dialog(display.this);
confirm.setContentView(R.layout.conf);
confirm.setTitle("Confirm deletion");
yes = (Button)confirm.findViewById(R.id.yes);
//If the user says yes, then delete everything
yes.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Remove everything from Hours.
Hours.clear();
String tempH = " ";
String tempW = " ";
//Then save it again in it's new, empty state so that it doesn't reappear the next time the app is run.
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
SharedPreferences.Editor edit = prefs.edit();
edit.putString("SAVEDATA", TextUtils.join(",", Hours));
edit.remove("totalh");
edit.remove("totalw");
edit.commit();
//And finally... refresh the list view - doesn't work
setListAdapter(new ArrayAdapter<String>(this, R.layout.activity_list, R.id.listText, Hours));
confirm.dismiss();
}
});
confirm.show();
}
The first argument of ArrayAdapter constructor is a Context, so you need to pass the Activity to it, something like new ArrayAdapter<String>(MyActivity.this, ...). Right now you're passing it your instance of OnClickListener which is why it's giving compiler error.
But the best way to update a ListView is to make changes on the ArrayAdapter itself using methods like adapter.add and adapter.remove, and then call adapter.notifyDataSetChanged(). In your case, you would call adapter.clear().
I'm using Eclipse for windows 7 and I am making an informative application(just text and offline content).
In my app I have about 180 buttons. Each button will lead to another screen. I need to know how to make each button lead to a specific screen?
And also, is there a way to like duplicate the code and not spend hours copying and pasting the code 180 times?
Check my code below for the first two screens:
That's for the MainActivity.java:
public void addListenerOnButton() {
final Context context = this;
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(context, MainActivity2.class);
startActivity(intent);
}
});
}
I mean that code is only for one single button. Am I supposed to repeat this for every single button?
and also a question, how many classes should I do? 180 main activities, 180 fragment_main.xml and 180 activity_main.xml?
That's my idea, since your application is just a "informative application" you can create two activities:
Main activity with buttons
"Information page"
To do it i need info about how you get this informations:
In a SQLite Database.
In a string-array
Personally, i prefer a SQLite DB it allows you to improve it without problems in the future.
About the Information activity:
Example: in your layout you have a TextView where will be added the text which this activity should be passed.
To make it dynamic in your case we pass the string to show using Intents, in our onCreate we add something like this:
Intent intent = getIntents();
String stringToDisplay = null;
if (intent != null)
{
stringToDisplay = intent.getStringExtra (EXTRA_STRING_CONTENT);
}
getIntents will get the Intent object which is created and passed to it by our main activity. getStringExtra is a simple method which says to Android: i want to get the string which is saved with the key EXTRA_STRING_CONTENT (it's something like a Map)
EXTRA_STRING_CONTENT is a field which we used to make sure we don't make any error in passing data, since we need to use the same name when we pass it (in MainActivity) and when we read it (InformationActivity)
public static final String EXTRA_STRING_CONTENT = "EXTRA_STRING_CONTENT";
Ok, we are done.
We now only need to set the string to our TextView:
TextView infoTextView = (TextView) findViewById(R.id.infotextview);
infoTextView.setText (stringToDisplay);
Stop it.
Now we should go to our MainActivity code and modify our addListenerOnButton
final Context context = this;
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(context, MainActivity2.class);
startActivity(intent);
}
});
Well, i will focus on this two lines
Intent intent = new Intent(context, MainActivity2.class);
startActivity(intent);
We need to pass the string to display here, how?
Before we used getStringExtra now it's similar (note: it's the same Intent class) but now we need the putExtra method, the compiler will select the correct overload for us so we just need to do
String stringToDisplay = "Hello world";
intent.putExtra(InformativeActivity.EXTRA_STRING_CONTENT, stringToDisplay);
(Note: InformativeActivity.EXTRA_STRING_CONTENT)
With the current code we will always sent Hello world to the second activity but we need something of dynamic based on the button... well now this depends on how you get the data.
If it's a string-array, you can save the string-array in an array and then based on the button (if it's the first sent string index 0, etc.).
An example:
int buttonId = 1; // it will be a general variable, if it's button 1 it will be 0, if 2-1 etc.
String[] informations = getResources().getStringArray(R.array.infos); // in a real code you should move it outside the `onClick` code and put it in a static final field.
intent.putExtra(InformativeActivity.EXTRA_STRING_CONTENT, informations[buttonId]); // it's the same of above
If you understand the concept you will know how to adapt the code based on your needs.
There are some cases where you need more info or what you want to sent is something which is better if managed by the second activity (example: in a sqlite database you could sent only the id of the line and read lines in the second activity based on this id)
Some things which you could change:
Avoid to call it MainActivity2, it's not so helpful as name
You don't need really to save Context, you could just use MainActivity.this
Try to make your addListenerOnButton more general, example take as argument a Button and set the listener to it.. don't read it from XML you will end up with 180 methods for every button.