I am going through Head First Android Development and I am a bit confused with
this method --> findViewById(int id)
I have the below button in the file "activity_find_beer.xml" :
<Button
android:id="#+id/find_beer"
android:text="#string/find_beer"
android:onClick="onClickFindBeer" />
and the following code from the class FindBeerActivity.java which is taking the user selected beer and displaying the same in a textview.
public class FindBeerActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_beer);
}
//Call when the button gets clicked
public void onClickFindBeer(View view) {
//Get a reference to the TextView
TextView brands = (TextView) findViewById(R.id.brands);
//Get a reference to the Spinner
Spinner color = (Spinner) findViewById(R.id.color);
//Get the selected item in the Spinner
String beerType = String.valueOf(color.getSelectedItem());
//Display the selected item
brands.setText(beerType);
}
}
My Question is the method onClickFindBeer(View view) takes a View type of
object as a parameter , but in the xml i have just mentioned
android:onClick="onClickFindBeer" and when the user clicks the
button , the method onClickFindBeer gets invoked...Who is passing the object of
type View to the onClickFindBeer(View view) ...is it something
implicit ?
Second,on developer.android.com I see that the method
findViewById(int id) is both in the Activity class (
https://developer.android.com/reference/android/app/Activity.html
) and also in the View class
https://developer.android.com/reference/android/view/View.html
... It's not clear to me which class (Activity or View)
findViewById(int id) method is invoked when i call findViewById()
from onClickFindBeer(View view){}.
Would be highly obliged if someone could throw light on this.
Regards.
The method takes a View parameter as that is how it is implemented in a superclass of the Button class (It is public class Button extends TextView.). The views you add to XML are actually java classes. When you set a property to such an XML item, that constructs the object from the particular java class accordingly. The onClick method of the View class goes as onClick(View v). By setting an XML you just asked the Button class to look for the entered method but its signature is always with a View as a paramenter, which refers to the view clicked.
findViewById has to be called on a View group. But the Actyvity class implements it to search an item in view assigned to it by the setContentView() method.
It is done somewhat implicitly. When building your app, the XML file is actually converted into Java file. When you click the view, the view is passed into the onClickFindBeer(View view) function.
The findViewById() is being called here by the activity. You can see the method declaration by clicking on findViewByID while pressing Ctrl. For a view, you would have to call it using the view. For example,
view.findViewById();
Its called JAVA Reflection which is used by android
2.
As I know, main difference is that when you used OnClickListener from activity it is connected with partivular object such as Textview,Button
find_beer.setOnClickListener and below code is excuted when someButton is pressed.
While android:onClick = "onClickFindBeer" is used handle click directly in the view's activity without need to implement any interface
You have assigned the method onClickBeer to your button. When the button gets clicked, the object, in this case the button, is passed to the method you assigned to it. A Button is a type of View object, so you have a more generic View object as the parameter, but you are perfectly ok to caste it to a button object.
findViewById is called through a "context", which is a way of getting at system resources. You are asking the system to return to you a specific object, which you can then use. It is worth reading up on contexts.
Hope that answers some of your query.
Base on your sample above the android:onClick method is the one being invoked because when invoking a onclick method in java class, it need to call a onClickListener.
cause on the other question. as far as I know it belong to the view class because it always to reference an object on your design.
Related
Basically i need to call a method, which refreshes the username in my NavigationBar. I try to call it from another activity SettingsActivity.java, where the user changes his name.
SettingsActivity.java:
// ...
MainActivity tempActivity = new MainActivity();
tempActivity.refreshNBName();
// ...
When i do this i get this exception:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
I've tried to do this another way:
((MainActivity)getApplicationContext()).refreshNBName();
But this throws another exception:
java.lang.ClassCastException: com.justnothing.nisser.debbie.GlobalVariables cannot be cast to com.justnothing.nisser.debbie.MainActivity
The method that i'm trying to call here looks like this:
public void refreshNBName(){
NavigationView nV = (NavigationView) findViewById(R.id.nav_view);
View headerView = nV.getHeaderView(0);
TextView local_user = (TextView) headerView.findViewById(R.id.actualUser);
local_user.setText(((GlobalVariables) getApplication()).name + " " + ((GlobalVariables) getApplication()).surname);
}
What should i do here? Any help or advice is appreciated!
You wrote this which is not applicable.
MainActivity tempActivity = new MainActivity();
tempActivity.refreshNBName();
Because this is creating new instance of MainActivity class. not referring to the live instance of MainActivity.
You tried another thing, you can't even do this.
((MainActivity)getApplicationContext()).refreshNBName();
Because getApplicationContext will return you context of application class which is not MainActivity.
I have gone through your question, you need not call any method from another activity. If you want update navigation drawer. You can call refreshNBName() on onResume() of MainActivity.class.
So every time user comes back to MainActivity, navigation view will update automatically.
You can create static method in MainActivity and than call the method like this MainActivity.someMethod() but i don't recommend this ! it can lead to memory leak and lots of exception to handle
if your main activity is not alive and is in the pause,stop,destory state there is no point to refreshing the view in main activity and you can always refresh the view to latest data when activity state changed to resume by overwriting onResume method in activity .
and finally i think best way to communicating with activates and fragment is using callback for more information see this link :
https://developer.android.com/training/basics/fragments/communicating.html
I am trying to implement button click handling in my Android app.
In the XML layout file that includes my button, I added the following line to my Button XML element:
android:onClick="handleClick"
I also defined a method with the following signature in the Activity that uses this layout:
public void handleClick() { /* ... */ }
However, when I ran my app with this code, it crashed. I was able to fix this crash by updating my method signature to:
public void handleClick(View v) { /* ... */ }
but I don't understand why I am required to include this View parameter?
This is because you might want to use your handleClick method for 2 or more Buttons in your XML.
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="handleClick"/>
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="handleClick"/>
In that case it could be unclear which button triggers the callback. The View v helps you identify that e.g.
public void handleClick(View v) {
if (v.getId() == R.id.button1) {
} else if(v.getId() == R.id.button2) {
}
}
The View argument supplied represents the View that received the click event. This may be useful if you reuse the handleClick method for multiple Views (in which case, you could inspect the id of the View passed to the method to determine which View was clicked, as illustrated by Enzokie's answer).
You must include this View parameter when defining your method even if you do not use it in your click logic. This is because reflection is used to locate the method corresponding to the name you supplied in XML, and the method name, parameter count, and parameter types are all required to uniquely define a method in Java. Check out this section of the View source code to see exactly how this reflective lookup works!
The View v is the object of your xml file which is referred in your onCreate method.
To refer any component from xml you have to use v to gets its id of the component.
Condition
You have give id to the component in xml if you want to use onClick in your class file.
I have three activities in my app
A login activity
A main activity
A detail activity
I want to use espresso to test a sequence of events: click the login button on the login activity, which opens the main activity, and then click a list item in main activity, which opens detail activity, and then click another button in the detail activity. I started by creating this simple test, to get a reference to the listview:
public class LoginActivityTest extends ActivityInstrumentationTestCase2<LoginActivity> {
public LoginActivityTest() {
super(LoginActivity.class);
}
#Override
public void setUp() throws Exception {
super.setUp();
getActivity();
}
public void testSequence() throws Exception {
// Login
onView(withId(R.id.button_log_in)).perform(click());
// Check if MainActivity is loaded
onView(withId(R.id.container)).check(matches(isDisplayed()));
// Check if Fragment is loaded
onView(withId(R.id.list)).check(matches(isDisplayed()));
}
}
On the mainActivity onCreate() method I load a fragment like this:
getFragmentManager().beginTransaction()
.add(R.id.container, mListFragment)
.commit();
The ListFragment fragment has a list (R.id.list), but still the test fails with a NoMatchingViewException:
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: com.tests.android.development:id/list
What am I doing wrong?
A note from the documentation for onView:
Note: the view has to be part of the view hierarchy. This may not be
the case if it is rendered as part of an AdapterView (e.g. ListView).
If this is the case, use Espresso.onData to load the view first.
To use onData to load the view, you need to check for instances of whatever your adapter is in the ListView. In other words, if your listview uses a Cursor adapter, you can try this:
onData(allOf(is(instanceOf(Cursor.class)))).check(matches(isDisplayed()));
It is important to note that the above will only pass if your listview contains at least one item. It is a good idea to have one test where an item exists, and one test where an item does not.
For more information on how to check for data that does exist, see here.
For more information on how to check for data that does not exist in an adapter, see here.
In the current version (Espresso 2.2.2) this exception is always appended with a View Hierarchy: clause that lists all the views available to match. Stroll through that and check if you can find your list.
As an alternative: check out android-sdk\tools\uiautomatorviewer.bat (or .sh) which takes a snapshot from the current screen and hierarchy. Put a breakpoint on your list matching line and check with the viewer if the list is there. If you find the list, there may be a timing issue in the test. Maybe it didn't wait enough, check out more about IdlingResources.
I am receiving the following two related errors in eclipse:
1) The method getIntent() is undefined for the type Fragment1
2) The method setDefaultPushCallback(Context, Class<? extends Activity>) in the type PushService is not applicable for the arguments (FragmentActivity, Class<Fragment1>)
Below is the portion of the code where the errors takes place:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1_layout, container,
false);
ParseAnalytics.trackAppOpened(getIntent());
// inform the Parse Cloud that it is ready for notifications
PushService.setDefaultPushCallback(getActivity(), Fragment1.class);
If you need any clarification, let me know.
1)You cannot call getIntent() inside a Fragment as it's not an Activity and so one does not inherit Activity's methods/ You should try using getActivity().getIntent() if you are really sure that this is what you are looking for.
2)This method asks you for a Context and you are passing it an Activity, and then asks you for a class that extends an activity and you are passing it a fragment. For the first argument i would call getActivity().getApplicationContext() and as a second argument getActivity().
Edit: Try casting the getActivity() like: (Activity) getActivity()
ParseAnalytics.trackAppOpened(((Activity)getActivity()).getIntent());
// inform the Parse Cloud that it is ready for notifications
PushService.setDefaultPushCallback(((Activity)getActivity()).getApplicationContext(), ((Activity)getAcitivty()));
The two problems occur because you passed wrong parameters to them. Changes as following should help you get ride of them:
ParseAnalytics.trackAppOpened(getAcitivty().getIntent());
// inform the Parse Cloud that it is ready for notifications
PushService.setDefaultPushCallback(getActivity(), getActivity());
But, whether this is what you want or not still depends on the needs of that two function.
Is it possible to save Stack<Stack<View>> in onSaveInstanceState.
May some another way how to save some specific data to manage Activity state?
You can't save views to bundle. And you shouldn't do it anyway. If activity is recreated it will reinflate layout again (or even inflate another one if configuration has changed) and create a new view hierarchy.
You should separate business data from your UI and store it onSaveInstanceState. After activity recreation you should get that data and update new views hierarchy accordingly.
For example, if you have a TextView, that displays some text that is stored in a field mSuperText, and your activity is going down, you should save it into a bundle in onSaveInstanceState:
#Override
protected void onSaveInstanceState(final Bundle outState) {
outState.putString("supertext", mSuperText);
}
And when your activity is recreated, in your method onCreate you get an argument onCreate(final Bundle savedInstanceState) which will be the bundle you stored previously. So you can get values you need:
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mSuperText = savedInstanceState.getString("superText");
}
}
Is it possible to save Stack> in onSaveInstanceState.
No, because Stack and View doesn't have the Parcelable / Serializable interface, which is necessary to put an Object in a Bundle. A Bundle only takes Arrays/ArrayList, String, primitives and so on.
May some another way how to save some specific data to manage Activity
state?
Which data you wanna save? If you wanna save a whole View/ViewGroup you are probably on the wrong way.
E.g to indicate that a TextView was visible set a boolean to true and put it in the Bundle. Check the boolean in onCreate() and set the View to visible. If the TextView had some text too, save it as String and set the text to the TextView, which you made visible.