Lets say I have a few buttons and their click is handled by a common handler is there a way to define the listener once and not defining an onClick attribute for each button?
<Button android:onClick="handler" />
Perhaps parent delegation as in browsers or some interceptor .....imagine you have 50 buttons, you declare on click for each explicitly???
Yes you can do so.
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
List<Button> buttons;
ViewGroup parentView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttons = new ArrayList<>();
parentView = (ViewGroup) findViewById(R.id.cord);
//Replace the above line with the container view i.e.
//Replace parentView = (ViewGroup) findViewById(R.id.cord);
// With parentView = (ViewGroup) findViewById(R.id.<YOUR MAIN VIEW/PARENT CONTAINER>);
//R.id.cord is R.id.<YOUR MAIN VIEW/PARENT CONTAINER> for me because my Relative Layout
// container's name is cord.
for(int i=0; i < parentView.getChildCount(); i++) {
childView = parentView.getChildAt(i);
//The if may change there are other options try them too.
if(childView.getClass().getName().substring(35).equals("Button")){
buttons.add((Button)parentView.getChildAt(i));
}
/*
//Else part optional
//Remove comment to use
else
Log.e("Not","A Button");*/
}
for (int i =0;i<buttons.size();i++){
buttons.get(i).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Do the work here
Log.d("Button Clicked",Integer.toString(view.getId()));
}
});
}
}
}
This is a way that you can solve your problem...
The best way: Just let your activity implement View.OnClickListener, and write your onClick method like this:
public void onClick(View v) {
final int id = v.getId();
switch (id) {
case R.id.button1:
// your code for button1 here
break;
case R.id.button2:
// your code for button2 here
break;
// even more buttons here
}
}
Then, in your XML layout file, you can set the click listeners directly using the attribute android:onClick:
<Button
android:id="#+id/button1"
android:onClick="onClick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 1" />
Yes, it can be achieved at the activity level programmatically. So no need to define the "onClick" attribute in XML.
You define a View.OnClickListener class. For e.g.
View.OnClickListener buttonListener = new View.OnClickListener() {
public void onClick(View v) {
// here you can define different code for different buttons
};
then you attach this listener to all your buttons on one place
final Button button = findViewById(R.id.button_id);
button.setOnClickListener(buttonListener);
Here's what I finally came to as solution it works only with touchables for instance TextView isn't a touchable:
In java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MainActivity activity = this;
/* Any kind of ViewGroup that is
parent of the common listener views. In the example it is the rootView */
View rootView = this.getWindow().getDecorView().getRootView();
// Buttons are touchables
ArrayList<View> touchables = rootView.getTouchables();
// Define the common on click listener for the buttons
View.OnClickListener listener = new View.OnClickListener() {
public final void onClick(View view) {
activity.onBtnClick( (Button) view );
}
};
// Iterate trough touchables and if the View is a button assign the listener
for (View v: touchables) {
// TODO: omit button if that common listener isn't meant for it
if(v instanceof Button) {
v.setOnClickListener(listener);
}
}
}
// The listener callback
protected final void onBtnClick(Button btn) {
String btnStringId = btn.getResources().getResourceEntryName(btn.getId());
}
In Kotlin:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/* Any kind of ViewGroup that is
parent of the common listener views. In the example it is the rootView */
val rootView = window.decorView.rootView
val touchables = rootView.touchables
val listener = View.OnClickListener {
onBtnClick(it as Button)
}
touchables.forEach { if (it is Button) {
it.setOnClickListener(listener)
}}
}
protected fun onBtnClick(btn: Button) {
var btnStringId = btn.getResources().getResourceEntryName(btn.id)
}
Related
Is it possible to write a function that only gets Activity in its arguments and can respond to all buttons pressed in that activity?
I've tried registering something through ViewTreeObserver and maybe use decorView but no success.
Yes, it is possible. You have to find all of your buttons and set their onClickListeners to same listener. Either you can make your activity implement View.OnClickListener or you can create a listener object by implementing it.
Knowing the root layout of your activity, you can access children views by getChildAt(index). Of course, you should check if a child is a Button.
For example in onCreate you can do it by:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// a layout is a ViewGroup
ViewGroup mainLayout = (ViewGroup) findViewById(R.id.main_layout);
for (int i = 0; i < viewGroup.getChildCount(); i++) {
if (view instanceof Button) {
view.setOnClickListener(listener);
}
}
}
The trick here is to find all the buttons even if they are children of sub layouts. For this purpose, you must write a recursive function like this:
private void setListenerToAllButtons(ViewGroup viewGroup, View.OnClickListener listener) {
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View view = viewGroup.getChildAt(i);
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
setListenerToAllButtons(group, listener);
} else if (view instanceof Button) {
view.setOnClickListener(listener);
}
}
}
then use it like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewGroup mainLayout = (ViewGroup) findViewById(R.id.main_layout);
setListenerToAllButtons(mainLayout, this);
}
Yes. Your Activity must
implements OnClicklistener
and have the method
public void onClick(DialogInterface di, int which) {
}
then you can
button.setOnClickListener(this);
The onClick method will be called whenever any button is clicked (if you have set this as listener).
The int which argument is the id of the button pressed.
EDIT: I think I got your question wrong :-/
Yes its possible. The idea is:
You define an interface inside that Activity. For example:
public interface listener{
public void method(Activity activity);
}
In your activity you should expose a register method which registers the listeners.
public void setListener(Listener listener)
{
this.listener=listener;
}
Whenever any Button press/click event occurs, call the interface method from the Activity.
exampleButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
listener.listenToButton(this);
}
Now any Class implements the listener interface has to implement the listenToButton(Activity activity) method and this way they will get the reference to the Activity at any Button press(or click) event
I'm making a program that when I press a button this change the background color but I have a problem with, when I change the screen orientation this change again the color to the predefined and I dont know how to solve this problem... Here is my code.
public class MainActivity extends Activity implements OnClickListener {
LinearLayout myLayout;
LinearLayout myLayout2;
// Declare UI elements
private Button firstButton, secondButton, thirdButton, fourthButton, fifthButton;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); // Our only layout for this app is main.xml
myLayout = (LinearLayout) findViewById(R.id.myLayout);
myLayout2 = (LinearLayout) findViewById(R.id.myLayout2);
// Initialize the UI components
firstButton = (Button) findViewById(R.id.button1);
// When we creating a button and if we expect that to use for event handling we have to set the listener
firstButton.setOnClickListener(this);
secondButton = (Button) findViewById(R.id.button2);
secondButton.setOnClickListener(this);
thirdButton = (Button) findViewById(R.id.button3);
thirdButton.setOnClickListener(this);
fourthButton = (Button) findViewById(R.id.button4);
fourthButton.setOnClickListener(this);
fifthButton = (Button) findViewById(R.id.button5);
fifthButton.setOnClickListener(this);
}
}
The problem is that when the orientation is changed, the activity is restarted. There are multiple ways to fix that.
One way is to make it so that the activity won't restart when the orientation is changed. Here's how to do that:
Add android:configChanges="orientation|screenSize" to your <activity tag, which is in your AndroidManifest.xml, like so:
<activity
android:name="UserIdActivity"
android:label="#string/app_name"
android:configChanges="orientation|screenSize" />
Rastikan's answer did it slightly differently, but his way of doing it won't work for any API after API 12 (source and this too). My way above, you don't actually need to call onConfiguationChanged in your activity class.
Another way of doing it would to use onSaveInstanceState(Bundle savedInstanceState) like Rastikan did.
Another way of doing it would be to let the activity restart itself, but then using the onResume() method to change the background colour, if necessary, when the activity is restarted. Like this:
public boolean changeColor = false;
// set changeColor to be true whenever you change the background colour
public void onResume()
{
if (changeColor) {
// change the background color
}
}
Here is a short/alternate version assuming a view (view1) of some sort.
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Color;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private static final int DEFAULT_COLOR = Color.WHITE;
private int myColor;
// Assuming a 'view' of some sort
private View myView;
// Declare UI elements
private Button firstButton;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // Our only layout for this app
// is main.xml
myView = (View) findViewById( R.id.view1 );
firstButton = (Button) findViewById( R.id.button1 );
Log.i("MainActivity", "onCreate"
+ ((null == savedInstanceState) ? "(null)"
: "(savedInstanceState)"));
if (savedInstanceState != null) {
myColor = savedInstanceState.getInt("COLOR");
} else {
myColor = DEFAULT_COLOR;
}
myView.setBackgroundColor(myColor);
firstButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (myColor == Color.WHITE)
myColor = Color.BLACK;
else if (myColor == Color.BLACK)
myColor = Color.WHITE;
myView.setBackgroundColor(myColor);
}
});
}
#Override
// this method is called before android trashes and recreates your activity
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("COLOR", myColor);
}
}
Alternatively you could do it the cheap-dumb-ass way: Locking the screen. add the 'android:configChanges' attribute to your activity.
<activity
android:name="UserIdActivity"
android:label="#string/app_name"
android:configChanges="orientation" />
And implement the 'onConfigurationChanged' callback to do nothing
public void onConfigurationChanged ( Configuration newConfig)
{
}
I want write code once and use in different activities. I have created a Base Activity class for that . Also the header of all the layouts in different activities are same. I have done that with the help of the <include layout > tag.
Now the Problem is my BaseActivity code is not running. I am trying this first time se don't have much idea about that.
1.)The BaseActivity code is below :
package com.waheguru.app;
import android.R.integer;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public abstract class BaseActivityMenu extends Activity {
//action id
private static final int ID_UP = 1;
private static final int ID_DOWN = 2;
private static final int ID_SEARCH = 3;
private static final int ID_INFO = 4;
private static final int ID_ERASE = 5;
private static final int ID_OK = 6;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.header);
ActionItem nextItem = new ActionItem(ID_DOWN, "Book", getResources().getDrawable(R.drawable.menu_down_arrow));
ActionItem prevItem = new ActionItem(ID_UP, "Bookmark", getResources().getDrawable(R.drawable.menu_up_arrow));
ActionItem searchItem = new ActionItem(ID_SEARCH, "Find", getResources().getDrawable(R.drawable.menu_search));
ActionItem infoItem = new ActionItem(ID_INFO, "Info", getResources().getDrawable(R.drawable.menu_info));
ActionItem eraseItem = new ActionItem(ID_ERASE, "Clear", getResources().getDrawable(R.drawable.menu_eraser));
ActionItem okItem = new ActionItem(ID_OK, "OK", getResources().getDrawable(R.drawable.menu_ok));
//use setSticky(true) to disable QuickAction dialog being dismissed after an item is clicked
prevItem.setSticky(true);
nextItem.setSticky(true);
//create QuickAction. Use QuickAction.VERTICAL or QuickAction.HORIZONTAL param to define layout
//orientation
final QuickAction quickAction = new QuickAction(this, QuickAction.VERTICAL);
//add action items into QuickAction
quickAction.addActionItem(nextItem);
quickAction.addActionItem(prevItem);
quickAction.addActionItem(searchItem);
quickAction.addActionItem(infoItem);
quickAction.addActionItem(eraseItem);
quickAction.addActionItem(okItem);
//Set listener for action item clicked
quickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {
public void onItemClick(QuickAction source, int pos, int actionId) {
ActionItem actionItem = quickAction.getActionItem(pos);
//here we can filter which action item was clicked with pos or actionId parameter
if (actionId == ID_SEARCH) {
Toast.makeText(getApplicationContext(), "Let's do some search action", Toast.LENGTH_SHORT).show();
} else if (actionId == ID_INFO) {
Toast.makeText(getApplicationContext(), "I have no info this time", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), actionItem.getTitle() + " selected", Toast.LENGTH_SHORT).show();
}
}
});
//set listnener for on dismiss event, this listener will be called only if QuickAction dialog was dismissed
//by clicking the area outside the dialog.
quickAction.setOnDismissListener(new QuickAction.OnDismissListener() {
public void onDismiss() {
Toast.makeText(getApplicationContext(), "Dismissed", Toast.LENGTH_SHORT).show();
}
});
Button books=(Button)findViewById(R.id.book);
books.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent=new Intent(ExampleActivity.this,List_of_books.class);
startActivityForResult(intent, 0);
}
});
//show on btn1
Button btn1 = (Button) this.findViewById(R.id.menu);
btn1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
quickAction.show(v);
}
});
}
}
2.) The Activity extended the Base Activity
package com.waheguru.app;
import android.app.Activity;
import android.os.Bundle;
public class ABCActivity extends BaseActivityMenu {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
}
}
So can any one help me where I am doing something wrong.
For this you have to create one header.xml which will be included in each and every layout for your activities as follows
header.xml
<RelativeLayout>
<TextView android:id="#+id/txtHeading"
.... />
</RelativeLayout>
activity_main.xml
<RelativeLayout>
<!-- include your header here -->
<include layout="#layout/header"
... />
<!-- Rest of your views -->
</RelativeLayout>
BaseActivity
abstract class BaseActivity extends Activity {
protected TextView txtHeading;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
protected void setHeading(int resId) {
if(txtHeading == null)
txtHeading = findViewById(R.id.txtHeading);
if(txtHeading != null)
txtHeading.setText(resId);
}
}
MainActivity
class MainActivity extends BaseActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setHeading(R.string.heading_main);
}
}
You can put as many views you want and manage common things in BaseActivity or BaseListActivity.
If you are making inheritance with activities and your base activity calls setContentView and after that the real activity calls setContentView the last call will set the layout for activity. So if you are looking for a solution where all activies have the same header component the are 2 ways.
For each activity layout xml you include that component
-You make function for baseActivity e.g. setContent(int layout_id)
-You call that with your activity always.
-Baseactivity inflates a root view with header and inflates layout_id view to that layout.
-Then calls the actual setContentView with that component.
I think you should achieve it using Fragment, this may helps you.
1 - in main.xml, add:
<fragment
android:id="#+id/header"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
class="com.package.name.HeaderPanel" />
//remaining is same
2 - the BaseActivity which extends FragmentActivity:
public class BaseActivityMenu extends FragmentActivity {
private static final String TAG = Default.class.getName() + " - ";
private int mResLayoutId;
public void onCreate(Bundle savedInstanceState, int resLayout){
super.onCreate(savedInstanceState);
setContentView(resLayout);
mResLayoutId = resLayout;
switch(mResLayoutId){
// here change with your xml file
case R.layout.home:
// set here common control like header textview
break;
default:
break;
}
}
}
3 - Now, you can extend your Activity with the BaseActivity. This will allow the Activity to be extended by FragmentActivity:
public class ABCActivity extends BaseActivityMenu {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState, R.layout.home);
}
}
In code, your base activity is called ExampleActivity, but in your child class you are extending BaseActivityMenu. Don't know where its coming from.
Perhaps change:
public class ABCActivity extends BaseActivityMenu
To this:
public class ABCActivity extends ExampleActivity
Moreover, I would suggest you to define your base activity (ExampleActivity) as an Abstract class.
For example:
public abstract class ExampleActivity extends Activity
Doing so will not define your base class as concrete and will make it easier to debug in case of problems.
I want to display an image on button click, but I have three errors in my code. What's wrong?
class name "SequencerActivity"
The type SequencerActivity must implement the inherited abstract method DialogInterface.OnClickListener.onClick(DialogInterface, int).
next.setOnClickListener(this);
The method setOnClickListener(View.OnClickListener) in the type View is not applicable for the arguments (SequencerActivity).
onClick(View v)
The method onClick(View) of type SequencerActivity must override or implement a supertype method.
Here's the code giving those errors:
public class SequencerActivity extends Activity implements OnClickListener
{
private int imageCounter = 0;
private ImageView imageLoader;
private int[] imageList = {R.drawable.f03, R.drawable.f04, R.drawable.f05, R.drawable.f06};
#Override
public void onCreate(Bundle savedInstanceState)
{
setContentView(R.layout.main);//this one is the common parent layout for all image views
super.onCreate(savedInstanceState);
/*requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);*/
//int image1 = R.drawable.image_w_lbl_0;
imageLoader = (ImageView) findViewById(R.id.imageLoader);
//imageLoader.setImageResource(image1);
Button next = (Button) findViewById(R.id.next);
Button back = (Button) findViewById(R.id.back);
next.setOnClickListener(this);
back.setOnClickListener(this);
back.setEnabled(false);
//show the default image
this.loadImage(imageList[imageCounter]);
}
#Override
public void onClick(View v)
{
int imagePath = 0;
// TODO Auto-generated method stub
switch (v.getId())
{
case R.id.next:
Log.i("Tag","tag");
if(imageCounter < imageList.length)
{
imageCounter++;
imagePath = imageList[imageCounter];
if (imageCounter==(imageList.length)-1)
{
{
ImageButton next=(ImageButton)findViewById(R.id.next);
next.setEnabled(false);
}
}
else
{
ImageButton back=(ImageButton)findViewById(R.id.back);
back.setEnabled(true);
}
}
break;
case R.id.back:
if(imageCounter > 0)
{
imageCounter--;
imagePath = imageList[imageCounter];
if (imageCounter==0)
{
ImageButton back=(ImageButton)findViewById(R.id.back);
back.setEnabled(false);
}
else
{
ImageButton next=(ImageButton)findViewById(R.id.next);
next.setEnabled(true);
}
}
break;
}
this.loadImage(imagePath);
}
private void loadImage(int imagePath)
{
imageLoader.setImageResource(imagePath);
}
}
The OnClickListener that you implement is not correct,
try to implement View.OnClickListener and not DialogInterface.OnClickListener.
You can see that in your import
import View.OnClickListener
instead of
import DialogInterface.OnClickListener
you need to import import android.view.View.OnClickListener;
So your code look like
import android.view.View.OnClickListener;
public class MainActivity extends Activity implements OnClickListener{
// Your oncreate() and rest of all code
}
// you should have method as below
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v.getId() == R.id.editText1) // just for instance
{
.. your onclick handle code
}
}
Still you are not able to resolve the error type import View.OnClickListener; then move the cursor on the View in import View.OnClickListener; It will open up pop-up then choose the Organize imports option.
Use simply import View.OnClickListener; at the top.
The OnClickListener you're implementing is the wrong one. It says it's DialogInterface.OnClickListener, while you probably want View.OnClickListener. You can correct that in the corresponding import statement.
Just do only One thing. Use "import android.view.View.OnClickListener" statement at the top of the program.
Do one thing
remove import android.content.DialogInterface.OnClickListener;
and import
android.View.View.OnClickListener
this will solve the problem
Happy Coding
Implement View.view.onClickListener
This is going to be a bit lame question. I have the following code:
..............
public void onCreate (Bundle bundle)
{
super.onCreate(bundle);
this.setContentView(R.layout.main2);
Button bnt = (Button) this.findViewById(R.id.browser);
bnt.setOnClickListener(new ButtonListener());
}
..............
class ButtonListener implements android.view.View.OnClickListener
{
public void onClick(View v)
{
// I have a TextView in my xml layout file.
// I'd like to get it and change my text when I click this button.
// But I can't get it (the TextView) unless I make it as a value of a static member of this class and pass it to the constructor.
//I believe I am missing a big point here, so i'd be very thankful if you could explain how this is meant to be done ?
}
}
Any help is appreciated.
You could try this:
class ButtonListener implements android.view.View.OnClickListener {
public void onClick(View v) {
View parent = (View)v.getParent();
if (parent != null) {
TextView txtView = parent.findViewById(R.id.mytextview);
txtView.setText(...);
}
}
}
the usage depends on your layout. Its possible, that the parent of your button is not the parent of your textview so be careful...
class ButtonListener implements android.view.View.OnClickListener {
public void onClick(View v) {
View p = (View) view.getRootView();
if (p != null) {
TextView txtView = (TextView) p.findViewById(R.id.mytextview);
txtView.setText(...);
}
}
}
I need to set visible an element from the same parent so I used this code :) and it worked
EDIT
I dont think it will be possible. The view V only has the buttons view in it....
You will know if you typecast the same
class ButtonListener implements android.view.View.OnClickListener
{
public void onClick(View v)
{
Button vg=(Button)v;
Log.e("", "views are "+vg.getText());
//However u can set the test here for ue textview
txt.setText("change text");
}
}
I did not understand your question properly. But if you just want to get the text out from ur TextView you can try like this
TextView txt;
public void onCreate (Bundle bundle)
{
super.onCreate(bundle);
this.setContentView(R.layout.main2);
Button bnt = (Button) this.findViewById(R.id.browser);
txt=(TextView)this.findViewById(R.id.urtextview);
bnt.setOnClickListener(new ButtonListener());
}
..............
class ButtonListener implements android.view.View.OnClickListener
{
public void onClick(View v)
{
// I have a TextView in my xml layout file.
// I'd like to get it and change my text when I click this button.
// But I can't get it (the TextView) unless I make it as a value of a static member of this class and pass it to the constructor.
//I believe I am missing a big point here, so i'd be very thankful if you could explain how this is meant to be done ?
//I think this should get u the string...
System.out.println("text is "+txt.getText().toString());
}
}