Every once in a while, entering in a new Activity requires updating values in let's say several TextViews. So let's say I got n Strings to write in n TextViews of the starting Activity.
Which one is the best approach to guarantee good performance and providing "clean code"?
Variant 1 (The way I actually apply):
I declare a single TextView variable "tempText" as global variable and assign the TextView to update to this variable (alternatively in a extra method).
Alternatively, a) is doing the whole procedere in the onCreate(), while b) is handle everything in a method called e.g. updateTextViews()
(...)
public class MyActivity extends Activity{
private TextView tempText;
public onCreate(Bundle icicle){
(...)
tempText = (TextView) findViewById(R.id.tv_1);
tempText.setText(string_1);
tempText = (TextView) findViewById(R.id.tv_2);
tempText.setText(string_2);
(...)
tempText = (TextView) findViewById(R.id.tv_n);
tempText.setText(string_n);
}
}
Variant 2 :
I declare a single TextView variable "tempText" as variable in the onCreate() or the respective method and assign the TextView to update to this variable.
The rest is in analogy to Variant 1.
(...)
public class MyActivity extends Activity{
public onCreate(Bundle icicle){
(...)
private TextView tempText;
tempText = (TextView) findViewById(R.id.tv_1);
tempText.setText(string_1);
tempText = (TextView) findViewById(R.id.tv_2);
tempText.setText(string_2);
(...)
tempText = (TextView) findViewById(R.id.tv_n);
tempText.setText(string_n);
}
}
Variant 3:
I declare a global TextView variable for every TextView to update. This, as far as I know, needs more space in the RAM, but I don't know about the impact to velocity. Also here, are there differences between handling it in the onCreate() (a)) or in a seperate method (b))?
(...)
public class MyActivity extends Activity{
private TextView tempText_1;
private TextView tempText_2;
(...)
private TextView tempText_n;
public onCreate(Bundle icicle){
(...)
tempText_1 = (TextView) findViewById(R.id.tv_1);
tempText_1.setText(string_1);
tempText_2 = (TextView) findViewById(R.id.tv_2);
tempText_2.setText(string_2);
(...)
tempText_n = (TextView) findViewById(R.id.tv_n);
tempText_n.setText(string_n);
}
}
Variant 4:
I declare a TextView variable for every TextView to update in the onCreate() or the respective method which handles this. The rest is in analogy to Variant 3?
(...)
public class MyActivity extends Activity{
public onCreate(Bundle icicle){
(...)
private TextView tempText_1;
private TextView tempText_2;
(...)
private TextView tempText_n;
tempText_1 = (TextView) findViewById(R.id.tv_1);
tempText_1.setText(string_1);
tempText_2 = (TextView) findViewById(R.id.tv_2);
tempText_2.setText(string_2);
(...)
tempText_n = (TextView) findViewById(R.id.tv_n);
tempText_n.setText(string_n);
}
}
Which one is the "best" method? Variants 1 and 2 provide reserving only one memory address in the RAM and use this, while according to Robert C. Martins "Clean Code" the variables are really ambiguous. Option 3 and 4 would be the exact opposite. But for the rest I'm not very conscious of other effects.
Personally, I use a Variant 3 if I need to access the TextViews again elsewhere in Activity (i.e. in onResume, which is where I usually put my static UI updates). If the setText is a one-shot thing, I do something like --
((TextView) findViewById( R.id.tv_1 ) ).setText( string_1 );
((TextView) findViewById( R.id.tv_2 ) ).setText( string_2 );
If there's any doubt that the view might not be there (i.e. when you're modifying layouts during dev), I'd use a wrapper such as --
private void safeSetText( int id, String caption ) {
TextView tv = (TextView) findViewById( id );
if( tv != null )
tv.setText( caption );
else
Log.d( "..... " );
}
And then in onCreate:
safeSetText( R.id.tv_1, string_1 );
safeSetText( R.id.tv_2, string_2 );
If you want to use the value through out the this activity, declare it as a member variable in the class as you normally do. There is nothing like you declare inside OnCreate, it is still a member variable. It is as good as declaring in the class body, it will improve the readability.
Also if you are declaring a member variable anywhere, it is going to occupy the same amount of ram. There is nothing you gain/loose there.
If you are sure that a variable is going to be used only locally, declare it locally, it will be allocated in stack instead of heap, and it will vanish as soon as you exit the block.
There is no such android optimized way of declaring variables as such. You can follow normal Object oriented/Java way of doing things.
Related
I was looking at my code and I realized that there are at least 3 ways of getting widget's reference in code:
First one (before onCreate):
private TextView textView= (TextView) findViewById(R.id.textView);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
}
Second one (in onCreate):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
final TextView textView= (TextView) findViewById(R.id.textView);
}
Third one (creating out and setting in onCreate):
private TextView textView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
textView= (TextView) findViewById(R.id.textView);
}
What is the difference between this 3 methods? When should I use them?
You must call setContentView() prior calling findViewById(), therefore on 1st approach will give you null all the times. 2nd and 3rd are the same with the exception for final keyword, but this is Java feature, not Android's.
The first does not guarantee that your widget is actually instantiated, it is not within the onCreate.
Second will be instantiated, but its value can not be changed because it becomes a constant to be the final.
Third, it is a global variable that will be instantiated in onCreate and you can use it in any other part of your code.
If you need to call findViewById() then the call should be anywhere after setContentView. Not before that like in your first option. Your third option creates an instance variable, use it only if the textview will be accessed a lot throughout the class, otherwise just call findViewById wherever you need it.
i am trying to add extra input fields on the fly to my view.
i first read json string from url with an async funct and map this dynamically to an object with a hasmap with GSON.
next i want to iterate the hashmap to create and add the input fields:
public class NewProductActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_product);
TextView view = (TextView) this.findViewById(android.R.id.content);// is this the correct way to get view?
new loadTemplateAttributes(view).execute();// android studio complains about passing view here
.. snip...
class loadTemplateAttributes extends AsyncTask<String, String, HashMap> {
HashMap mymap;
.. snip...
protected void onPostExecute(HashMap mymap) {
pDialog.dismiss();
Iterator it = mymap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
String name = (String) pair.getKey();
TemplateAttribureProperties t = (TemplateAttribureProperties) pair.getValue();
String type = t.getType();
System.out.println("Name=" + name + " type=" + type);
LinearLayout ll = (LinearLayout) findViewById(R.id.linearLayout2);
// add text view
TextView tv = new TextView(this); // 'this' is unknow here
tv.setText(name);
ll.addView(tv);
EditText et = new EditText(view); // 'view' is what i want to pass but dont know how
et.setText();
ll.addView(et);
it.remove();
}
problem is that 'this' is unknown inside onPostExecute function.
i read something about passing the view to the async function but to me it is unclear how to get the view in the firstplace and how to pass it after...
also a lot of options dont seem to work because they are deprecated or are unsafe because the might introduce memory leaks according to the comments.
really lost here.
I don't know what you are doing but You can use Context context; and TextView view; before onCreate() making it global and then you can callTextView tv = new TextView(context); in your method.
public class NewProductActivity extends Activity {
Context context=this;
private TextView view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_product);
view = (TextView) findViewById(android.R.id.content);
}
new loadTemplateAttributes(view).execute();// android studio complains about passing view here
you would have to add constructor to your asynctask, like that:
class loadTemplateAttributes extends AsyncTask<String, String, HashMap> {
View view;
public loadTemplateAttributes(View v) {
view = v;
}
TextView tv = new TextView(this); // 'this' is unknow here
from internal class in java, you refer to parent class using NewProductActivity.this syntax in your case.
EditText et = new EditText(view); // 'view' is what i want to pass but dont know how
et.setText();
you could use aproach with constructor as I described above, or refer directly to activity view: NewProductActivity.this.view. But you would have to make view a class field in your activity.
Advanced: making your activity an internal class instead of static, and also passing views to it, might cause reference leaks and also crashes in case you use view (inside AsyncTask) that was invalidated due to screen rotation. This is especially possible if your AsyncTask is doing network operations. To prevent it always make AsyncTasks static, also if you pass views or activities to them, then wrap those references in WeakReference<>
In short, I set up a tabhost, and since I want the tab's content to have dynamic TextView's (among other things), I try initializing the text and it crashes. I am unsure of why, but commenting out the code that sets the text in the TextView's stopped the crashing.
One reference towards fixing this mentioned using intents to set activities for the tab's content, but apparently this didn't actually fix the crashing, it somehow changed it and then that lead died out without him ever saying how he fixed it, and my attempt at it also failed.
package com.example.main;
//removed imports
#SuppressWarnings("deprecation")
public class MainActivity extends TabActivity {
private boolean atMainMenu;
TextView warframeText;
TextView primaryText;
TextView secondaryText;
TextView meleeText;
TextView sentinelText;
TextView sentinelWeaponText;
Warframe warframe;
PrimaryWeapon primary;
SecondaryWeapon secondary;
MeleeWeapon melee;
Sentinel sentinel;
Weapon sentinelWeapon;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
TabHost mTabHost = getTabHost();
//TabHost
mTabHost.addTab(mTabHost.newTabSpec("tab1").setIndicator("Build").setContent(R.id.build));
mTabHost.addTab(mTabHost.newTabSpec("tab2").setIndicator("Stats").setContent(R.id.stats));
TextView title1 = (TextView)mTabHost.getTabWidget().getChildAt(0).findViewById(android.R.id.title);
title1.setTextColor(Color.parseColor("#30A0F0"));
TextView title2 = (TextView)mTabHost.getTabWidget().getChildAt(1).findViewById(android.R.id.title);
title2.setTextColor(Color.parseColor("#30A0F0"));
mTabHost.setCurrentTab(0);
warframeText = (TextView)findViewById(R.id.warframe);
primaryText = (TextView)findViewById(R.id.primary);
secondaryText = (TextView)findViewById(R.id.secondary);
meleeText = (TextView)findViewById(R.id.melee);
sentinelText = (TextView)findViewById(R.id.sentinel);
sentinelWeaponText = (TextView)findViewById(R.id.sentinelWeapon);
//Change To First Time Setup
warframe = new Excalibur();
primary = new BratonMk1();
secondary = new Lato();
melee = new Skana();
setBuild(); //<--removing this fixed the crashing, the method is included after onCreate, it sets the TextView's text
atMainMenu = true;
}
public void setBuild() {
warframeText.setText(warframe.getName());
primaryText.setText(primary.getName());
secondaryText.setText(secondary.getName());
meleeText.setText(melee.getName());
sentinelText.setText(sentinel.getName());
sentinelWeaponText.setText(sentinelWeapon.getName());
}
}
So what I would like to know, if anyone has the answer, why does editing the Textviews contained in a tab layout cause the app to crash and how can I fix this? :/
(also the code I provided is quite shortened, but I believe it contains all the relevant parts to the problem)
You did not initialize sentinel and sentinelWeapon but you are trying to get name from that
sentinelText.setText(sentinel.getName());
sentinelWeaponText.setText(sentinelWeapon.getName());
It May be a cause to your app get crashed
Trying to get started with Android development, and doing some basic work with TextViews..
For some reason TextView's setText() method is causing huge problems for me.. here's a simplified version of my code to show what I mean:
package com.example.testapp;
import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;
public class MainActivity extends Activity {
TextView text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
text = (TextView) findViewById(R.id.text1);
setContentView(R.layout.activity_main);
text.setText("literally anything");
}
}
This will cause a crash, and I don't understand why.. if I create the TextView within the onCreate it works just fine, but if I create it outside of it, it doesn't.. why is that? Has the line "TextView text;" not been executed yet or something?
Thanks!
You need to call setContentView() before initializing the TextView so that your Activity has access to all the layout components.
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text1);
text.setText("literally anything");
switch these 2 lines
text = (TextView) findViewById(R.id.text1);
setContentView(R.layout.activity_main);
you need to set the content first
From docs:
onCreate(Bundle) is where you initialize your activity. Most
importantly, here you will usually call setContentView(int) with a
layout resource defining your UI, and using findViewById(int) to
retrieve the widgets in that UI that you need to interact with
programmatically.
So this means that if you will reference your views in the layout, you must first set the content view and already then call findViewById method to reference child views of the layout resource defining your activity's UI
text = (TextView) findViewById(R.id.text1);
setContentView(R.layout.activity_main);
text.setText("literally anything");
If "literally anything" is a variable, which often may be the case, be sure that it isn't throwing a NullPointerException. I kept having that problem myself. I fixed it to be:
text = (TextView) findViewById(R.id.text1);
setContentView(R.layout.activity_main);
try {
text.setText("literally anything");
} catch (NullPointerException e) {
// Do something
}
Exceptions can be really useful, so if you're a beginning programmer, I suggest you put exception handling on your list of things to learn soon.
After a click on button1, another layout and another class gets called.
Now I want to change the text of textView out of class 2 which results in an app crash with java.lang.NullPointerException
important parts of Class 1
public static TextView A;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
[button stuff in class 1]
setContentView(R.layout.raten);
final TextView A = (TextView) findViewById(R.id.A); //the textview I wanna chage
max = 10;
Easy easy = new Easy(); // the other class
easy.e();
[now the method in class 1 that should change the text]
public static void Tx(int i)
{
A.setText("adsfasdf");
}
[important parts of Class 2 ("Easy")]
public void e(){
System.out.println("called class easy");
int max = MainActivity.max;
System.out.println(max);
for (int i= 0; i<max; i++){
System.out.println("runde"+i);
MainActivity.Tx(i);
}
I know, some people already asked such questions but I didn't find a working solution. I already understood, that you can't access the UI things outside the UI thread and that the nullpointerexception appears, because he uses the "empty" public static TextView A; and not the final TextView A = (TextView) findViewById(R.id.A).
But how I can make it visible for the other methods?
Sorry if the post looks messed up but I didn't konw how to explain my situation in a better way
Rather than defining a new local variable A, just assign to the static.
change
final TextView A = (TextView) findViewById(R.id.A); //the textview I wanna chage
to
A = (TextView) findViewById(R.id.A); //the textview I wanna chage
remove static from both A and Tx
(of course your MainActivity must be created by the time you call easy.e())