I have 4 buttons on a layout, and for each of the buttons, they have the attribute:
android:onClick="numberChosen"
now, what I had planned on doing was that in my activity (which uses a layout that has the 4 buttons), I have something like:
public class Blah extends Activity{
String fileName;
Button one;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_number_players_selection_screen);
one = (Button) findViewById(R.id.number_players_one);
}
public void numberChosen(View v){
if(v == one){ // <-------- first way
System.out.println("first way");
}
if(v.getId()==R.id.number_players_one){ // <-------- second way
System.out.println("second way");
}
}
}
if you take note of what happens in the method numberChosen, between these two ways, which one is better? or are they both exactly doing the same thing?
on a side note, is doing android:onClick="numberChosen" any better or worse than just setting the View.onClickListener instead?
From my point of view, both ways are correct, they get the job done.
From performance point view, the second way might be better.
In addition, like some suggested, using the switch case might not be possible if you are using ADT 14 or higher, in a library project, because the IDs are not final (constants), so you have to use if statements in that case only.
I am using min SDK 14 and this solution is not working in some cases (work only for first row of my list adapter)
findViewById(R.id.item_icon_favourite) == view
solved by comparing with IDs
R.id.item_icon_favourite == view.getId()
public class Blah extends Activity implements OnClickListener{
String fileName;
Button one;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_number_players_selection_screen);
one = (Button) findViewById(R.id.number_players_one);
one..setOnClickListener(this); // way 1 to do that
// more better way would be customized for whole app
**one..setOnClickListener(new MyClickListener());**
}
/** The click event observer pattern for the different items on the view
*
* #see android.view.View.OnClickListener#onClick(android.view.View)
*/
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.number_players_one:
// best way 1
break;
default :
break;
}
}
}
A more custom implementation you can have a customized listener for different sections in the app as :
public class MyClickListener implements OnClickListener {
private Context mContext;
#Override
public void onClick(View v) {
mContext = v.getContext();
switch (v.getId()) {
case R.id.number_players_one:
// best way 2 and more better among all
break;
default:
break;
}
}
}
I suggest you to use switch case its more readable.Its does n't matter weather your are using android:onClick="numberChosen" or View.onClickListener()
Related
I am trying to have a checkbox on my homepage, which when checked, should take me to an another view, else, displays an error message on the same view.
This works fine the first time only. If unchecked, it displays the error, and if checked, renders the next view.
However, when I return to this homepage using back button, this time the checkbox becomes redundant. Even unchecking it renders the next page correctly.
If I remove all the views of the ViewGroup, it removes them permanently, and the homepage is empty once I return to it.
I believe the checkbox needs to be reset every time I return to the view.
However, I am unable to do the same.
Please find my code below (I am a beginner to Android development):
public class MainActivity extends AppCompatActivity {
public static final String EXTRA_MESSAGE="com.android.AI";
public boolean isCheckBoxClicked=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//isChecked is the onClick attribute for checkbox in the main.xml here
public void isChecked(View view){
boolean isChecked = ((CheckBox) view).isChecked();
if(isChecked){
isCheckBoxClicked = true;
} else {
isCheckBoxClicked = false;
}
}
//Send message is onClick for a submit button
public void sendMessage(View view){
if(isCheckBoxClicked) {
Intent intent = new Intent(this, SeniorTeam.class);
startActivity(intent);
} else {
TextView acceptTerms = new TextView(this);
acceptTerms.setTextSize(15);
acceptTerms.setText("Please accpet terms and conditions before proceeding");
ViewGroup terms = (ViewGroup) findViewById(R.id.activity_main);
terms.addView(acceptTerms);
}
}
}
As far as I see this, you don't need to be able to go back by using the back Button, do you?
In case you don't there are 2 solutions
Easy:
Add this line to your verry last if
this.finish();
so your Activity should look like this:
public class MainActivity extends AppCompatActivity {
public static final String EXTRA_MESSAGE="com.android.AI";
public boolean isCheckBoxClicked=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//isChecked is the onClick attribute for checkbox in the main.xml here
public void isChecked(View view){
boolean isChecked= ((CheckBox) view).isChecked();
if(isChecked){
isCheckBoxClicked=true;
}
else{
isCheckBoxClicked=false;
}
}
//Send message is onClick for a submit button
public void sendMessage(View view){
if(isCheckBoxClicked) {
Intent intent = new Intent(this, SeniorTeam.class);
startActivity(intent);
this.finish();
}
else {
TextView acceptTerms = new TextView(this);
acceptTerms.setTextSize(15);
acceptTerms.setText("Please accpet terms and conditions before proceeding");
ViewGroup terms = (ViewGroup) findViewById(R.id.activity_main);
terms.addView(acceptTerms);
}
}
A little more complicated:
If you use the first method pressing back will actually kick the user out of your App so if you want you can just override the method when back is pressed in your next activity for that you want to add this code int your NEXT activity(SeniorTeam.class)
#Override
public void onBackPressed(){
//custom actions here leave blank to render back useless
}
You should completely remove the isCheckBoxClicked variable, the isChecked function (and remove it from the attributes). In the sendMessage function, just call ((CheckBox)findViewById(id)).isChecked() instead of isCheckBoxClicked.
The way you have it coded, there is too much room for disconnect between the variables and the UI. It is not necessary.
I'm developing a simple application in which youb have different spots placed on google map.
When I click on a spot I get its details which are displayed in a GridViewPager.
For now my application is based on the GridViewPager sample available with the sdk.
Here is my layout for the spot details (nothing fancy)
<android.support.wearable.view.GridViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"/>
My problem now is that I'm not able to detect a Click event on a card.
I've tried this but it doesn't work.
public class DetailsActivity extends Activity implements GridViewPager.OnClickListener {
#Override
public void onClick(View v) {
}
I've also tried View.OnClickListener.
Have any idea ?
There are two issues here. First, if you really want to make the GridViewPager clickable, you need to tell it to listen for click events - just implementing the OnClickListener interface isn't sufficient. So you need to do something like this:
public class DetailsActivity ... {
#Override
public void onCreate(Bundle savedInstanceState) {
...
GridViewPager pager = (GridViewPager)findViewById(R.id.pager);
pager.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// put your onClick logic here
}
});
...
}
}
That being said, however, based on your description it sounds like what you actually want is to set up click handlers on individual pages within the grid, not on the entire grid. If so, then you'll need to do something similar but in each page's Fragment class. For example:
public class MyPageFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View result = inflater.inflate(...);
result.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// put your onClick logic here
}
});
...
return result;
}
}
Note: if you are using CardFragments in your GridViewPager, then you would probably set the OnClickListener in your onCreateContentView override. Otherwise, the above Fragment-based example should apply.
On Android here is for example an excellent code fragment,
showing how to achieve five buttons on a dialog fragment...
android DialogFragment android:onClick="buttonCancel" causes IllegalStateException could not find a method
in your DialogFragment, you have to setOnClickListener(this) for all of your buttons/imageviews etc.
Then you implement View.OnClickListener and have a routine like this...
public void onClick(View v)
{
Utils.Log("Fucking 'A' sort of... ");
switch (v.getId())
{
case R.id.postfragment_send:
break;
etc etc etc
default:
break;
}
}
That's all fantastic. BUT.
Over in my main activity, where I have a ListView. The custom cells have five buttons. Very simply, in the main activity, I have five routines named whatever I like...
public void clickedComments(View v)
{
int position = feed.getPositionForView(v);
...etc etc
}
public void clickedExplosions(View v)
{
int position = feed.getPositionForView(v);
...etc etc
}
public void clickedTanks(View v)
{
int position = feed.getPositionForView(v);
...etc etc
}
Then you just do this which is unbelievably easy ("screw Xcode!") ...
Amazing!
My question, why can't I use the 'onClick system' in dialog fragments?
What am I doing wrong? Can an android expert explain what the fundamental difference is between the two? For the record my projects are 4.1+ only.
Thanks!!
Here I am pasting in a full example of a fragment, using the first method described above.
public class HappyPopupFragment extends DialogFragment implements View.OnClickListener
{
#Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
dialog.getWindow().setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
return dialog;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.happy_popup, container);
_setupButtons(view);
return view;
}
public void onClick(View v)
{
Utils.Log("Fucking 'A' sort of... ");
switch (v.getId())
{
case R.id.button_a:
Utils.Log("tanks !!");
break;
case R.id.button_b:
Utils.Log("bombs !!");
break;
case R.id.button_c:
Utils.Log("guns !!");
break;
case R.id.button_d:
Utils.Log("ammo !!");
break;
default:
break;
}
}
private void _setupButtons(View view)
{
((ImageView)view.findViewById(R.id.button_a)).setOnClickListener(this);
((ImageView)view.findViewById(R.id.button_b)).setOnClickListener(this);
((ImageView)view.findViewById(R.id.button_c)).setOnClickListener(this);
((TextView)view.findViewById(R.id.button_d)).setOnClickListener(this);
}
}
that's actually a pretty simple answer, but you have to start it by remembering that Android 1.0 there were no Fragments.
First let's see what an activity really is:
java.lang.Object
↳ android.content.Context
↳ android.content.ContextWrapper
↳ android.view.ContextThemeWrapper
↳ android.app.Activity
An object that extend Context, that's what it is.
So, when you inflate the XML layout, that method inflate is doing stuff like creating and setting up the views like that:
View v = new View(context) // see the context here ?
then when you set on your XML onClick:commentsClick for example, what is happening when you click is:
getContext().commentsClick(View.this); // look, the context again
so let's analyse that:
The XML onClick tries to call back to the context, meaning, call back to the activity that inflated it. See that the IllegalStateException message says that it cannot find the method? Well, because it's not in the activity. Probably if you put commentsClick in the Activity that is creating the DialogFragment, it will work, but that's just bad O.O., right?
The thing with the XML onClick was a nice facilitator to avoid on the huge switch(int) case, but it is a solution that simply does not scale to other classes that might inflate layouts, such as Fragments.
It's just a fact of Android DialogFragment API. Callback methods defined in a fragment XML layout are called in the Activity which contains this fragment. It`s more simple than you mean because in previous Android API-s such "XML defined" callbacks were called in activities also.
(Transferred from my comments as it strikes me as an answer to the question of topic. Perhaps this will be more convenient to future readers of this topic.)
you can use onClickListener, on each View and its subclasses.
setOnClickListener takes as parameter an instance of the class that implements View.OnclickListener.
If you have an error on setOnClickListener(this) it means that the object this refers is an object of an class that does not implements View.OnClickListener
In other words...
Here's how to make onClickListener work for custom cells in custom list views in custom dialog fragments!
in the ADAPTER class (1) for your list view, you'll have code that sets the values for each cell. (Setting text and os on.) In fact, in that same code set onClickListener for each cell button:
v.nameTV.setText( "User Name" );
v.inviteIV.setOnClickListener( ourBoss ); // like this
the problem is what to set the listener to. In fact you want it to be your dialog fragment. "ourBoss" will be the DialogFragment. So (2) when the dialog fragment creates the adapter, pass it in:
in the dialog fragment creating the adaptor:
fosAdapter = new YourHappyAdapter(
getActivity(), getActivity().getLayoutInflater(),
otherStuff, otherStuff, this);
and in the adapter itself ...
public class YourHappyAdapter extends BaseAdapter
{
YourDialogFragmentClass ourBoss;
public FosAdapter(
Context context, LayoutInflater inflater,
blah, blah,
YourDialogFragmentClass ydfc)
{
blah
blah
ourBoss = ydfc;
}
Finally then (3) in the ordinary way, in YourDialogFragmentClass, you can have the usual onClick code!! Hooray, you're done!!
public void onClick(View v)
{
switch (v.getId())
{
case R.id.submit_button: // from the overall fragment screen
_searchNow();
break;
case R.id.cell_button: // that one's from a cell.
Utils.Log("IT WORKED !!!!!!!!!!!!!!!!! !!");
userWantsANewTank(v);
break;
default:
break;
}
}
It's (essentially) just not realistic to use the "old-fashioned" xml-handy-onClick method, when doing custom tables and custom dialog fragments!
Hope it helps someone!
I'm trying to make multiple OnClickListener methods for 5 buttons in my program, and I've been able to declare them, and I made a switch using the xml id of what was clicked, but I need a parameter for the setOnClickListener method when I call it, and all that will work is null. I have also tried passing in this, so the method has context.
Here's some of the code:
add.setOnClickListener(null);
sub.setOnClickListener(null);
mult.setOnClickListener(null);
div.setOnClickListener(null);
equal.setOnClickListener(null);
The parameter has to be an instance of some object that implements the OnClickListener interface. One way to do it is to use an anonymous inner class:
add.setOnClickListener(new OnClickListener{
public void onClick(View view){
//your event handler code here
}
});
another way is to make your class implement OnClickListener --do that by changing your declaration to look like:
public class MyActivity extends Activity implements OnClickListener{
then define an implementation for the onClick method:
public void onClick(View view){
if(view == add){
//handle add button click
}else if (view == sub){
//handle sub button click
}
//etc
}
then to install the listener you could do:
add.setOnClickListener(this);
You are supposed to pass View.OnClickListener to this function, which is a listener that will get called once the button is clicked.
To do that, you can either:
Declare this listener in the layout XML, with the button, as specified in Button 4 in this site.
Create an instance of View.OnClickListener and pass it to setOnClickListener method as in the example below (Taken from android site which is a great source):
// Create an anonymous implementation of OnClickListener
private OnClickListener mCorkyListener = new OnClickListener() {
public void onClick(View v) {
// do something when the button is clicked
}
};
protected void onCreate(Bundle savedValues) {
...
// Capture our button from layout
Button button = (Button)findViewById(R.id.corky);
// Register the onClick listener with the implementation above
button.setOnClickListener(mCorkyListener);
...
}
Since View.OnClickListener is an interface, your activity may implement it as well, and be itself the listener, in this case, you will pass the activity instance(this) to the setOnClickListener method, but this is just one option, and not that recommended IMHO.
Coming from a .NET background, I struggled with the same thing at first. It's just a different syntax than .NET as java doesn't support properties, or events like I was used to. Here's a simple example of how to do this using a class level click listener variable...
#Override
private void onCreate(Bundle bundle) {
myButton.setOnClickListener(this.genericButtonListener);
}
private OnClickListener genericButtonListener = new OnClickListener() {
#Override
public void onClick(View v) {
//v represents your button
}
};
You need a concrete class here. For example:
imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// your code here
}
});
If you look at documentation, you will notice it takes View.OnClickListener as parameter. If you need five separate listeners, which you are not going to use anywhere else, you can pass an anonymous class implementing onClick(View v), like this
add.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
//do required actions here
}
});
I have a custom TextView which is clickable. It defines its own onClick handler in order to change its appearance based on clicks. However if I then define a second onClick handler in my activity in order to do something based on the button being clicked, only one of the onClick functions is called. onClick is a void function - is there any way to say I didn't process this click, please pass it on to other onClick handlers?
To be more clear here is the code:
Inside MyCheckButton which extends TextView I have:
setOnClickListener( mClickListener );
private OnClickListener mClickListener = new OnClickListener() {
public void onClick(View v) {
toggle();
}
};
However I include MyCheckButton into my Activity, and of course I need to do something when its clicked so I attach another OnClickListener to it:
MyCheckButton button= (MyCheckButtonButton) findViewById(R.id.cb);
button.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// do something in the app
}
});
By calling setOnClickListener twice it appears that I am replacing the original listener so toggle() which changes the appearance is never called. How can I do something in my activity when this button is clicked if it is already using the onClick handler to change its appearance? I thought I would simply see both OnClickListeners getting called.
This is a bit dirty, but the way I would do this if you need multiple listeners is to register one that knows about the other. The first one (the one that's actually registered) will then need to know when to delegate to the other listener based on the conditions of the event. Actually, in reality, there's no real need to have two OnClickListener classes. The second class can implement whatever interface you want. Additionally, there's no need to create a special interface for what you need.
public class MyClickListener implements OnClickListener{
private SomeCustomClass mSecondListener = new SomeCustomClass();
public void onClick(View v){
if (needToForward){
mSecondListener.handleClick(v);
}else{
//handle the click
}
}
}
Then, in your code for your activity, you would do this
MyClickListener lstn = new MyClickListener();
mCheckBox.setOnClickListener(lstn);
Is there a reason this wouldn't work for you?
Alternatively, if you wanted, the second class could also implement the OnClickListener interface.
Additionally, if you need true bubbling, you could define your own interface that supports adding multiple click listeners to an intermediate class that happens to implement the OnClickListener interface. From there, in that class's onClick() method, you would iterate through the registered listeners calling the appropriate method.
A cleaner approach would be to use the CompositeListener pattern.
Taken from:
how can I set up multiple listeners for one event?
You'd have to add this class in your project:
/**
* Aux class to collect multiple click listeners.
*/
class CompositeListener implements OnClickListener {
private List<OnClickListener> registeredListeners = new ArrayList<OnClickListener>();
public void registerListener (OnClickListener listener) {
registeredListeners.add(listener);
}
#Override
public void onClick(View v) {
for(OnClickListener listener:registeredListeners) {
listener.onClick(View v);
}
}
}
Then add this on your MyCheckButton
private CompositeListener clickListener = new CompositeListener();
public MyCheckButton()
{
super.setOnClickListener(clickListener); //multi event listener initialization
}
#Override
public void setOnClickListener(OnClickListener l) {
clickListener.registerListener(l);
}
Both your calls to setOnClickListener would go through this override, get added to the list and get called when the event is fired. Hope it helps.
Since it appears I can only have one onClickListener per View. What I think I have to do is define an interface:
public interface MyOnClickListener {
public void onMyClick(View v);
}
Implement it from my activity and override the onMyClick function to do whatever I want and in the MyCheckButton class I'll need to pass a MyOnClickListener in the constructor save it and call listener.onMyClick inside the onClick handler.
Let me know if theres a better way. I considered using the onTouch handler in either the activity or the MyCheckButton class, but later if I add onTouch or onClick to either one I will get a difficult to notice bug.
My idea doesn't work because I don't know how to get a reference to the activity from my constructor:
public class TVCheckButton extends TextView {
private MyOnClickListener mListener;
public TVCheckButton(Context context, AttributeSet attrs) {
super(context, attrs);
mListener = ???;
}
}
Since only one OnclickListener works on Android 2.1 [I don't know about later versions) make the view private and static and create a static function that can change it e.g.
public class ExampleActivity extends Activity{
private SomeOtherclass someOtherClass;
private static Button b_replay;
public void onCreate(Bundle savedInstanceState){
someOtherClass = new SomeOtherclass();
b_replay = (Button) findViewById(R.id.b_replay);
b_replay.setOnClickListener(someOtherClass);
}
public static void changeReplayText(String text){
b_replay.setText(text);
}
}
A nice generic approach is to use a list of listeners, such as ListenerList and WeakListenerList from the Beryl library.
For some reason I could not use the answers above so here is an alternative:
//I had all of this in one method where I had two buttons, and based on external factors one would be visible and other would not aka 'gone'. So, I had that checked out! Hope it helps someone!!
Button b = (Button) findViewById(R.id.b_reset);
Button breakk = (Button) findViewById(R.id.b_break);
if ((findViewById(R.id.b_reset)).getVisibility() == View.VISIBLE) {
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//some code and methods...
}
});
} else if ((findViewById(R.id.b_break)).getVisibility() == View.VISIBLE) {
breakk.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//some code and methods...
}
});
}
In the first class, define a virtual button:
private static Button myVirtualButton = new Button(context);
... and a public method to register it:
public static void registerMyVirtualButton(Button b) { myVirtualButton = b;}
In the OnClickListener do whatever action is desired, and in the end, softclick the virtual button:
if (myVirtualButton!=null) { myVirtualButton.callOnClick(); }
In the second class, define a button and its OnClickListener with whatever action is additionally desired.
Transmit the button to the first class via registerMyVirtualButton.
Upon clicking the object of the first class, both actions will be executed.
You can attach an OnClick listener to the button in the following way :
Button button= (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// do something
}
});
Similarily, your TextView should have it's on OnClick listener.