I'm trying to build a single window app.
I have one layout (one xml file) and two activities. A button on the first activity starts the second activity, which is an infinite quiz. Then the second activity modifies some of the views (displays series of questions).
The button purpose is to finish the second activity if it's up, then start it again.
The problem is when I click the button the second time, the app crashes. Per my understanding, it's because it can't find the onClick method in the second activity code. This is the error message:
java.lang.IllegalStateException: Could not find method startQuiz(View) in a parent or ancestor Context for android:onClick attribute defined on view class android.support.v7.widget.AppCompatButton with id 'buttonStart'
I am looking for a way to bind the onClick of that button to only the main activity.
Is it possible? I Would appreciate any help.
My button xml:
<Button
android:id="#+id/buttonStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="32dp"
android:background="#android:color/holo_orange_dark"
android:onClick="startQuiz"
android:text="#string/start"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/arithMul" />
My main activity:
/** Called when the user taps the Start button */
public void startQuiz(View view) {
finishActivity();
initQuiz();
}
public void initQuiz() {
Intent intent = new Intent(this, QuizActivity.class);
startActivity(intent);
}
public void finishActivity() {
Intent intent = new Intent("finish.quiz");
sendBroadcast(intent);
}
My second activity (QuizActivity):
public class QuizActivity extends AppCompatActivity {
TextView textQuestion = null;
EditText textAnswer = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// register to finish broadcast message
IntentFilter filter = new IntentFilter();
filter.addAction("finish.quiz");
registerReceiver(broadcast_reciever, filter);
// access main activity layout
setContentView(R.layout.activity_main);
// main layout text views
ConstraintLayout layoutChoose = findViewById(R.id.constraintLayoutQuiz);
layoutChoose.setVisibility(View.VISIBLE);
textQuestion = (TextView) findViewById(R.id.textViewQuestion);
textAnswer = (EditText) findViewById(R.id.editTextAnswer);
// start the quiz
startQuizSequence();
}
// register for broadcast to finish this activity
BroadcastReceiver broadcast_reciever = new BroadcastReceiver() {
#Override
public void onReceive(Context arg0, Intent intent) {
//finish the activity
finish();
}
};
private void startQuizSequence() {
...
}
}
you could use findByViewId in both activities to find the button and explicitly set onClickListeners in each activity.
The problem is when I click the button the second time, the app crashes. Per my understanding, it's because it can't find the onClick method in the second activity code.
You're correct. When you're using Button with android:onClick attribute like the following:
<Button
android:id="#+id/buttonStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startQuiz"
...
/>
You're expected to create a method similar with the attribute value like this:
public void startQuiz(View v) {
}
You need to add the same method to another activity using the XML layout.
But, It becomes a problem when you're trying to rename the Button View and rename the android:onClick value to reflect the change. But then you forgot to rename the method in the Activity.
So, you need to decouple the layout from the code. Instead using a android:onClick attribute, you need to set the Button click listener. Update your Button view to something like this:
<!-- I usually use a more readable id for the button -->
<Button
android:id="#+id/start_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
/>
Then, set the click listener:
Button button = (Button) findViewById(R.id.start_btn);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// do something here.
}
});
Related
This question already has answers here:
What's the best way to share data between activities?
(14 answers)
Closed 4 years ago.
So what I am trying to make happen is when you check this checkBoxA, some text will appear in a different TextView in a different Activity that the user will reach later on. The app is kind of like a quiz app so this off the text being displaid like the final score or something.
At first I tried this:
if (checkBoxA.isChecked()){
systemView.setText("Business");
}
But then I got a nullPointerException cause the "systemView" is not in the same activity. The activity is extended to the other activity that the "systemView" is located. So I am not really sure whats wrong anyone know what I should do?
Your issue is that even though you can get the ID of the systemView TextView by using R.id.systemView when you try to find that view using findViewById(R.id.systemView) the view cannot be found as it is not in the current activity's list of ViewGroup. As such null is returned.
Note systemView as the id given to the TextView has been assumed.
That is, You can only successfully use findViewById to find views within the current ViewGroup (e.g. for this.FindyViewById the layout as set by setContentView).
Instead you need to make the value available to the other activity and then retrieve the value in the other activity.
There are various ways that you can make the value available, some options are :-
To pass it to the activity via the Intent that starts the other activity as an intent extra, you could store the value in shared preferences and then retrieve it in the other activity or you could store the value in a database, e.g. SQLite and retrieve it.
Using an IntentExtra is ideal if you are directly starting the other activity with a limited number of values.
using chained Intent Extras is also feasible (that is passing to one activity, then to another and so on).
Shared preferences could suit a situation where there are a limited number of values to be passed and the other activity isn't directly started from the activity.
A database would suit a situation where there is a fair amount of structured data and/or related data (or if you are using a database for other aspects).
An example of using an Intent could be :-
In the Activity that is passing the value
Intent i = new Intent(this, yourOtherActivity.class);
i.putExtra("YOURINTENTEXTRAKEY","Business"); //<<<< 1st parameter is a Key for identification, the 2nd parameter is the value to be passed
startActivity(i);
In the other Activity's onCreate (after you've set the contentView)
TextView mSystemView = this.find(R.id.systemView);
if (this.getIntent().getStringExtra("YOURINTENTEXTRAKEY") != null) {
mSystemView.setText(this.getItent().getStringExtra("YOURINTENTEXTRAKEY"));
} else {
mSystemView.setText("NO VALUE PASSED");
}
You set pass and return multiple IntentExtras see Intent for various options and types of values that can be passed/retrieved.
Simple Working Example
The following is code for a working example. The first activity (MainActivity) has a CheckBox and a Button.
The Button can be clicked or longClicked. If the latter then nothing is passed to the second activity. If the former then depedning upon whether or not the CheckBox is ticked will either pass "Not Checked" or "Business".
The second activity, if passed a value (either "Not Checked" or "Business") will display the passed value, if nothing is passed then it will display "NOTHING PASSED". The button on the second activity will return to the first activity (alternately using the back button will return to the first activity).
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final String INTENTKEY_CHECKBOXA = "checkboxa";
CheckBox checkBoxA;
Button nextActivity;
String valueToPass = "Not Checked";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkBoxA = this.findViewById(R.id.checkBoxA);
checkBoxA.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (checkBoxA.isChecked()) {
valueToPass = "Business";
} else {
valueToPass = "Not Checked";
}
}
});
nextActivity = this.findViewById(R.id.nextActivity);
//Set onlick listener (pass value via intent)
nextActivity.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
callNextActivity(true);
}
});
// Set onlongclick listener (doesn't pass value via intent)
nextActivity.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
callNextActivity(false);
return true;
}
});
}
private void callNextActivity(boolean passvalue) {
Intent i = new Intent(this,NextActivity.class);
if (passvalue) {
i.putExtra(INTENTKEY_CHECKBOXA, valueToPass);
}
startActivity(i);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Button
android:id="#+id/nextActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="NEXT ACTIVITY"/>
<CheckBox
android:id="#+id/checkBoxA"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
NextActivity.java
public class NextActivity extends AppCompatActivity {
Button doneButton;
TextView systemView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_next);
systemView = this.findViewById(R.id.sysstemView);
doneButton = this.findViewById(R.id.done);
if (this.getIntent().getStringExtra(MainActivity.INTENTKEY_CHECKBOXA) != null) {
systemView.setText(this.getIntent().getStringExtra(MainActivity.INTENTKEY_CHECKBOXA));
} else {
systemView.setText("NOTHING PASSED");
}
doneButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
doneWithActivity();
}
});
}
// Return from this activity
private void doneWithActivity() {
this.finish();
}
}
activity_next.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NextActivity">
<Button
android:id="#+id/done"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DONE"/>
<TextView
android:id="#+id/sysstemView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
you can pass your text in the intents of navigations between the activities .. otherwise if you want to save your text to recover it later, even if you close and restart your application, you can save it in a shared preferences file, to retrieve it from this file when you want to display it later.. but you can not set a text to a textview of an activity that is not in foreground.
There are many ways to do this depending on your exact use case one may be better suited than the others.
You can wrap the data in a bundle and pass it to your other activity through an intent if you are opening a second activity
Or you could store the values (in shared prefs or sqlite) and then retrieve them in the next activity.
You could use RxJava to create a stream via a subject (bevahior subject most likely in this case) and write to the stream in the first activity, then subscribe on the stream in the next to get the values.
If you are using an intent to go to the next activity you could put the value in a string and pass the string as an extra to the intent. Take the value in the next activity and set the text view.
//First Activty
String valueToPass = "";
if (checkBoxA.isChecked()){
valueToPass = "Business";
}
startActivity(new Intent(CurrentActivity.this, NextActivity.class).putExtra("value", valueToPass));
//Second Activity
if(getIntent().getString("value") != null)){
systemView.setText(getIntent().getString("value"));
}
Just use SharedPreferences to save the TextView value and then when go to the Activity that contain the TextView get the saved value and set it to the TextView in the onCreate method
For button click listeners code runs fine when click listeners are defined in the activity but facing problem when defining click listener in the xml, no spelling mismatch
following error on logcat appears when listener are defined in xml, why?
java.lang.IllegalStateException: Could not find method addButtonClicked(View) in a parent or ancestor Context for android:onClick attribute defined on view class android.support.v7.widget.AppCompatButton with id 'addButton'
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add"
android:id="#+id/addButton"
android:layout_marginTop="50dp"
android:onClick="addButtonClicked"
android:layout_below="#+id/inputField"
android:layout_alignLeft="#+id/inputField"
android:layout_alignStart="#+id/inputField" />
This is Activity Class
When click listener is defined in xml
public void addButtonClicked()
{
String text = inputField.getText().toString();
Products p = new Products(text);
dbObj.addProduct(p);
printDatabase();
}
When click listener is defined from Activity, This works fine
addButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
String text = inputField.getText().toString();
Products p = new Products(text);
dbObj.addProduct(p);
printDatabase();
}
});
Add this to you activity:
public void addButtonClicked(View view) {
//Your code here
}
Could not find method addButtonClicked(View)
addButtonClicked() method Missing .
Create addButtonClicked() method first .
public void addButtonClicked(View v)
{
// Add your Staff here
}
Clean-Rebuild-Run .
MainActivity shows a viewpager
so there are 3 layout files:activity_main.xml、card1.xml and card2.xml
Now I want to get view from card1.xml and set the listener.
what should I do?
I tried using this:
LayoutInflater layout=this.getLayoutInflater();
View view=layout.inflate(R.layout.card1, null);
Button b=(Button)view.findViewById(R.id.b);
then set OnClickListener:
b.setOnClickListener(new MyClickListener(0));
but useless.
In your card1.xml, you can add a onClick attribute, and just provide the method name as the attribute's value, like this:
<Button android:id="#+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click me!"
android:onClick="myMethod" />
Now in you MainActivity.java file, create a public method with return type as void, and which takes in a View parameter, like this:
public void myMethod(View v) {
// do your thing here
}
You can read more about it here.
public class MyClickListener implements OnClickListener {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.your_btn_id:
// your code
break;
}
}
}
another way is you can add button click listener in your card1.xml fragment class.
So I've been looking around for android tutorials, help questions, etc.. I keep running into questions or tutorials hard for me to understand.
Here's my questions:
When I create an item in the visual designer, piece of code will be created in the .xml.
How can I get the ID of that item to use it in the .java file later?
How can I add callbacks when let's say a button gets clicked?
Here's what I have so far:
.java
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.Button;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public void Button_click_callback() // Where to add the callback in the .xml?
{
// How to get button ID and change the text of it?
//Knowing this will help me A LOT!
}
}
.xml
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="79dp"
android:layout_marginTop="32dp"
android:text="Button" />
When I create an item in the visual designer, piece of code will be created in the .xml. How can I get the ID of that item to use it in the .java file later?
Step #1: Ensure that you have assigned an ID for the widget in the designer (in your XML above, you will see this as android:id="#+id/button1)
Step #2: In Java, you can get at the Java object for that widget by calling findViewById(R.id.button1) at some appropriate time (e.g., from an Activity, sometime after you call setContentView()).
How can I add callbacks when let's say a button gets clicked?
Generally, there is a setter method for this, such as setOnClickListener() that you can call on the Button you retrieved by findViewById().
In the specific case of click events on widgets hosted by activities, there is also an android:onClick attribute you can have in the XML, which supplies the name of a method on your Activity that will get called when the widget is clicked, instead of your having to use the setter.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = (Button)findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do something in response to button click
}
});
}
All of this is extensively documented on the Android Developer site. You should be looking their for this basic stuff.
http://developer.android.com/guide/topics/ui/controls/button.html
You can use findViewById to get view ids from XML in Java, Make sure that you should declare the ids in views, otherwise it might cause exception which results apps force close
If you want call back with xml rather than programatically
you can declare android:onClick attribute on that Views in layout XML
For example, in your case you need add android:onClick="Button_click_callback" in your
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="79dp"
android:layout_marginTop="32dp"
android:text="Button"
android:onClick="Button_click_callback"/>
then you can use Button_click_callback method for call back in your Activity
public void Button_click_callback()
{
}
if you want call back programatically with java,
first you have get the view with findViewById and then you can add click listener to that view
You need to use the onClickListener and override the onClick method.
btn.setOnClickListener.(new View.OnClickListener(){
#Override
public void onClick(View v) {
//TO DO
}
});
OnClickListener is an interface. And thats why you need to override the OnClick method.
//in oncreate method of activity
//take button id like that
Button button1 = (Button)findViewById(R.id.button1);
//then implement on click listener for performing action on button
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do something in response to button click
}
});
}
//you can also implements onclicklistener in activity.its interface;
public class MainActivity extends Activity implements View.OnClickListener
//then generate method
public void onClick(View view) {
}
public class MainActivity extends Activity {
private Button button1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1 = findViewById(R.id.button1);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public void Button_click_callback() {
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do something in response to button click
Log.e("click", "-------------button click");
}
});
}
}
Those two code snippets are equal, just implemented in two different ways.
Code Implementation
Button btn = (Button) findViewById(R.id.mybutton);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myFancyMethod(v);
}
});
// some more code
public void myFancyMethod(View v) {
// does something very interesting
}
Above is a code implementation of an OnClickListener. And this is the XML implementation.
XML Implementation
<?xml version="1.0" encoding="utf-8"?>
<!-- layout elements -->
<Button android:id="#+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click me!"
android:onClick="myFancyMethod" />
1) First Look at your XML file.In code it shows
android:id="#+id/button1.
You can edit it on your own name.Same thing you can do to change it though the GUI appear at right side of your design.
You can get your ID using findViewById(R.id.your id name); function.
For an Example:
My button id is btn1.In my code I can use that button by getting it's Id as follows:
Button btn1=findViewById(R.id.btn1);
2)You can callback after creating your Button which you have created Before as follows;
btn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Create your code
}
});
}
For the call backs - The android component needs to be registered in the calling device . The id of the components has to be unique in the xmls.
Using the unique id the components can be dynamically altered or the call backs can be used.
I have two activities, activity one has buttons that refer to activity two and methods in it. I'm trying to use TextView.SetText to put something on the screen but keep getting NullPointerException.
Activity 2:
public class SomeActivity extends Activity {
TextView textview ;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.some_activity);
textview = (TextView) findViewById( R.id.textview );
spill("Some text");
}
public void spill(String s){
textview.setText(s);
}
public void methodCalledFromActivityOne(){
System.out.println("Works");
spill("Why Doesn't this work?");
}
XML has this:
<TextView
android:id="#+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
tools:context=".SomeActivity" />
I'm new to Android and will REALLY appreciate all/any help.
EDIT: The name of the XML is fine, the error only occurs when I press button 1 in activity 1 which calls methodCalledFromActivityOne().
This is what I get from LogCat:
Caused by: java.lang.NullPointerException at android.app.Activity.findViewById at data.storage.SomeActivity.spill at data.storage.SomeActivity.methodCalledFromActivityOne
at data.storage.ActivityOne.button1clicked
textview in activity 2 will be initialized only when onCreate() is called. And onCreate() of activity 2 will be called only when this activity comes into the phone view. You cannot set the values of views of another activity from your current activity. It is a bad idea.
If you want the values to get to activity 2, then send those in an intent.
If you're calling methodCalledFromActivityOne() method from another activity make sure you've created TextView property at that activity also. Otherwise it'll not work.