Meaning of "View v" in Android Studio - java

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.

Related

Set "android:text" value from function/method

Theoretically implementation of multi culture stored in remote DB.
In Android we have the following:
<TextView
android:id="#+id/loginlabel"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:textStyle="bold"
android:layout_marginTop="5dp"
android:text="#string/login" />
And the text is using the value of the property login from the file string.xml.
Now my question: in the activity xml in the property android:text is there any way for me to call a function/method (can it even be from a static class) ?
A place that have some business logic and still returns a string.
I know that I can easily use the following type of code in my activity java file:
TextView loginlabel = (TextView) findViewById(R.id.loginlabel);
loginlabel.setText( GetMyText(R.id.loginlabel) );
But I am trying to avoid to write this a "million times" since I have several activities and hundreds of values on my strings.xml.
For adition information, my purpose with this is the following:
On application start I read all the string.xml and connect to my remote server (which has a DB) and verify if all the strings are inserted in that DB.
Insert the ones that are missing, with the default value of that property in string.xml.
Now, in another remote system, the client can dynamically create new cultures and translations of this properties.
back to the android, I load a list of the current cultures that we have available and also load the values (translations) from the DB. The only thing missing it to "inject" them into the place where I am using the "string" value.
This worked for me in the past.
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setText(yourMethodWithBusinessLogic());
}
}
Don't forget to replace your normal textviews with this custom one. Press CTRL+SHFT+R for convenience.
Hope this helps.

onClickListener not working (properly) after ConfigurationChange in fragments

Introduction
Huh, this is a tough one. At least, I think so...
To clear things up:
I did not find any answers to my question after searching the internet (using Google).
Everything I found was about people setting up the onClickListener's for their View's wrong. I guess it is the same problem in my case, but none of the other problems matched mine.
This seems to be the same problem... It has no answers.
Setup
I've got three Fragment's set up together with a ViewPager in my AppCompatActivity.
viewPager = (ViewPager) findViewById(R.id.main_view_pager);
viewPager.setAdapter(new SectionsPagerAdapter(getSupportFragmentManager()));
Handled by the SectionsPagerAdapter.
public class SectionsPagerAdapter extends FragmentPagerAdapter {
SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return MainTextHolder.newInstance(tabIndicatorOnClick);
case 1:
return PostWriter.newInstance(tabIndicatorOnClick);
case 2:
return TopPage.newInstance(tabIndicatorOnClick);
default:
return Error.newInstance();
}
}
#Override
public int getCount() {
return 3;
}
// ... more methods
}
In each of the Fragment's I have some content plus a custom TabIndicator.
(the following xml file is my View's for the indicator)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<View
android:id="#+id/fragment_divider_one"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:tag="one" />
<View
android:id="#+id/fragment_divider_two"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:tag="two" />
<View
android:id="#+id/fragment_divider_three"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:tag="three" />
</LinearLayout>
And then I set an OnClickListener for each of those View's (dividers) in the Fragment's onCreateView(LayoutInflater inflater, ViewGroup Bundle savedInstanceState) method. The OnClickListener is already prepared in my Activity where I also instantiate my ViewPager so that I can change the current Item (Fragment/tab).
private final View.OnClickListener tabIndicatorOnClick = new View.OnClickListener() {
#Override
public void onClick(View view) {
if (view.getTag().toString().equals("one"))
viewPager.setCurrentItem(0, true); // second argument for smooth transition
else if (view.getTag().toString().equals("two"))
viewPager.setCurrentItem(1, true);
else if (view.getTag().toString().equals("three"))
viewPager.setCurrentItem(2, true);
}
};
I pass that OnClickListener to the Fragment's by putting it into my newInstance(View.OnClickListener tabIndicatorOnClick) method.
This is for one of the fragments. It is identical for the others!
public static MainTextHolder newInstance(View.OnClickListener tabIndicatorOnClick) {
MainTextHolder fragment = new MainTextHolder();
Bundle args = new Bundle();
fragment.putIndicatorTabIndicatorOnClick(tabIndicatorOnClick);
fragment.setArguments(args);
fragment.setRetainInstance(true);
return fragment;
}
My putIndicatorTabIndicatorOnClick(View.OnClickListener tabIndicatorOnClick) method is a Void in an Interface. It just applies the OnClickListener to the Class(Fragment).
#Override
public void putIndicatorTabIndicatorOnClick(View.OnClickListener tabIndicatorOnClick) {
this.tabIndicatorOnClick = tabIndicatorOnClick;
}
Does it work
Yes it does. It works perfectly fine... until a ConfigurationChange happens. In my case I tested it with changing the orientation.
The Problem
What happens after that ConfigurationChange is that everything goes normally. The OnClickListener gets applied to all View's in all Fragment's, but the tabIndicatorOnClick OnClickListener is a null object reference in the second and third Fragment.
So in PostWriter and TopPage there isn't even really an OnClickListener on the View's for what ever reason. But it gets better: In MainTextHolder Fragment the tabIndicatorOnClick is not a null object reference, but it does not change the ViewPager's Item anymore. It runs the code, but it does not scroll the Tab.
When turning on "Don't keep activities" ind Developer options of the device and leaving the app, all OnClickListener's are null object references. Even those in MainTextHolder.
Summary
After the ConfigurationChange the OnClickListener gets passed into my first Fragment, but it is no working properly and in the remaining Fragment's it is not even passed / a null object reference.
The issue can be replicated through destroying the Activity and then reloading it.
In the end...
... I have no clue what is going wrong. I hope that I structured my question properly.
May be of interest
minSdkVersion: 14
targetSdkVersion: 25
I already have a SpringIndicator on my ViewPager. This does not feature an OnClickListener so I added my own "overlayed" indicators which have the needed OnClick feature. I also like the look of it, but if you can give me a better solution for my TabIndicator, I would also love to have that as an answer.
I came across the solution to put the method into the xml :onClick attribute multiple times, but for that I would need to create the same OnClickListener for every Fragment which is not really the nice way and it is also not solving my problem, but a way around it. - I would need to pass the ViewPager into my Fragment and then call that method from xml which is, as I said, just another way of doing it and not the way I prefer to do it. Also I don't know if it works. I'll most likely test it.
Other OnClickListener's in the Fragment still work properly. So as I said the problem lays in the OnClickListener itself not working right/being a null object reference.
Firstly, well done for writing a detailed description of the problem. Many questioners on StackOverflow can learn from the ability of this question to articulate the problem.
This error would seem to come from not accounting for Fragment lifecycle changes. While inside a ViewPager, Fragments will pass through various states including hidden and shown when they are temporarily offscreen, and paused and resumed if they are offscreen and the system frees memory, and finally created and destroyed if the Android system decides to save the instance state of your Activity (such as from a configuration change). In the latter case, Android will try and restore the state of your Fragments from saved instance state in your ViewPager. Since your Fragments have no way to save the View.OnClickedListener they were passed in the arguments, they end up with a null pointer when they are restored which is causing your error.
To fix the error, I suggest that you do not pass the View.OnClickedListener as a parameter to the Fragments. Rather, expose the OnClickListener to your Fragments via a public method in your Activity and have the Fragments get it themselves in their onResume(). This way you can guarantee that the Fragments will have a reference to the View.OnClickedListener whenever they are in a resumed state. So your onResume() might look something like this:
#Override
public void onResume() {
OnClickListener onClickListener = ((MyActivity) getActivity).getOnClickListener();
putIndicatorTabIndicatorOnClick(onClickListener);
}
Since you have set it in onResume(), you should remove it in onPause():
#Override
public void onPause() {
//set the onClickListener to null to free up the resource
}
Using onResume() and onPause() like this to free up resources from listeners is the approach recommended in the developer guide.
Of course, you will have to make sure your Activity also handles its own lifecycle by checking for saved state in onCreate(Bundle savedInstanceState) and restoring it appropriately.
Please note that saving instance state and restoring can be triggered in a number of ways:
Configuration state change (such as rotating the phone and causing the Activity to display in landscape rather than portrait
The device becoming low on memory (for example, if a large number of Activities are in recent apps)
Turning on Developer Options / Don't keep activities and using home to put your application in recent apps and then navigating back to your app.
You will be able to use one of these methods to determine if you have correctly handled the lifecycles of your Activities and Fragments.

Clarification regarding findViewById(int id) method in Android

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.

Android Espresso functional tests with fragments

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.

What is the difference between View arg0 and View v?

I wrote an xml file for my code and it has 2 buttons. However, the buttons in java file by default showed this.
BCel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
output=(input-32)*5/9;
}
});
BFah.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
output=1.8*input+32;
}
});
(output=... is obviously written by me)
What I don't understand is why does it show arg0 in the first one and v in the second one.
The other similar questions ask why does it show arg0, arg1, ar2 etc. but I fail to understand this variety.
Will this cause any error in my application ?
The arg0 and v are just variable names. You could choose any valid Java identifier.
What I don't understand is why does it show arg0 in the first one and v in the second one.
If you, in Eclipse, choose the option "Override method in OnClickListener" or let Eclipse fill in the methods in an anonymous class it will automatically select the same variable name as the overridden methods (and argN if the source code is not attached).
Will this cause any error in my application ?
No, as long as you stick with valid Java identifiers it won't cause any errors.
Having different names will not cause an error in your application. They are the names of your parameters for those methods. It is most likely just Eclipse auto-generating a parameter name when you instantiate the anonymous class using new View.OnClickListener() {...}, but you could use any valid Java identifier.
There is no any difference between arg0 and v . both are just identifier
may be you got error due to
this Statement
output=1.8*input+32;
may be casting error etc depending on data type of "input"
You can use any name for variable it does not matter.You are getting error because input is undefined symbol. You have to declare it before using it.

Categories

Resources