Here's the code:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
ImageView button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (ImageView)findViewById(R.id.startButton);
button.setOnClickListener(this);
button.setImageResource(R.drawable.play);
}
public void onClick(View v) {
button.setImageResource(R.drawable.pause);
}
}
Apparently if I put the button.setImageResource(R.drawable.pause);in the onCreate it works and changes images but not in the onClick? I tried debugging it and it ran the onClick code but didn't change the button image. I'm probably making a very stupid mistake but I can't figure it out.
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"
tools:context="com.example.larry.app.MainActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/startButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true" />
</LinearLayout>
Make sure you use
android:src
to set the initial image in the xml.
you had better set ImageView self OnClickListener
Could you try this code and tell me the result then :
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
ImageView button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (ImageView)findViewById(R.id.startButton);
button.setOnClickListener(this);
button.setImageResource(R.drawable.play);
}
public void onClick(View v) {
button.setImageResource(R.drawable.pause);
}
}
and in the 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"
tools:context="com.example.larry.app.MainActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/startButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true" />
</LinearLayout>
In the OnClick there is a switch statement, and when I put the button.setImageResource(R.drawable.pause); before the switch it doesn't always activate even when OnClick activates. When I click one button android.os.SystemClock.sleep is executed which prevents the image from swapping. When the other button is pressed the clock doesn't sleep, so the image can switch successfully.
I stumbled into the same problem Larry faced when using the setImageResource method when changing the source of an imageView. For me this happened when trying to use a switch but for a different scenario. Incase someone would find it helpful, i'll explain how I used a work around for this from the code.
SCENARIO
For me, I had a recyclerView in one activity and based on the selection, it would start another activity using an intent and the widgets in the new activity will be manipulated by the click of the recycler view including an imageView
SOLUTION
Using an If or a Switch inside the new class did not work (Which was the problem). What I did was inside the Custom Adapter for my recycler view, I used an If condition (Again a Switch will not work) as follows,
THE CLASS OF THE ACTIVITY WITH THE RECYCLER VIEW
`#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position)
{
holder.indexTextView.setText(String.valueOf(position + 1));
holder.nameTextView.setText(String.valueOf(name.get(position)));
holder.gradeTextView.setText(String.valueOf(grade.get(position)));
holder.itemView.setOnClickListener(v ->
{
Intent i = new Intent(context, DisplayingProductDetails.class);
if(novaGroup.get(position).equals(1))
i.putExtra("PRODUCT_NOVA_GROUP", R.drawable.nova_grade_1);
else if(novaGroup.get(position).equals(2))
i.putExtra("PRODUCT_NOVA_GROUP", R.drawable.nova_grade_2);
else if(novaGroup.get(position).equals(3))
i.putExtra("PRODUCT_NOVA_GROUP", R.drawable.nova_grade_3);
else if(novaGroup.get(position).equals(4))
i.putExtra("PRODUCT_NOVA_GROUP", R.drawable.nova_grade_4);
activity.startActivityForResult(i, 1);
});
}`
The Important Part here is passing the Resource ID as a String from the putExtra method.
if(novaGroup.get(position).equals(1))
i.putExtra("PRODUCT_NOVA_GROUP", R.drawable.nova_grade_1);
else if(novaGroup.get(position).equals(2))
i.putExtra("PRODUCT_NOVA_GROUP", R.drawable.nova_grade_2);
else if(novaGroup.get(position).equals(3))
i.putExtra("PRODUCT_NOVA_GROUP", R.drawable.nova_grade_3);
else
i.putExtra("PRODUCT_NOVA_GROUP", R.drawable.nova_grade_4);
FROM THE CLASS OF THE NEW ACTIVITY
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_displaying_product_details);
Bundle extras = getIntent().getExtras();
novaGroupImage = findViewById(R.id.novaGroupImage);
novaGroup = extras.getInt("PRODUCT_NOVA_GROUP");
novaGroupImage.setImageResource(novaGroup);
}
The important part here being using the resource ID inside the onCreate Method,
Bundle extras = getIntent().getExtras();
novaGroupImage = findViewById(R.id.novaGroupImage);
Related
I have six activities like this. I tried to make custom activity that hold ImageViews so I won't have to repeat myself in every activity. Should I leave it as it is or can I make it somehow be in one place and let it be used by everyone (like layout is - it's just one and works):
public class ActivityOne extends AppCompatActivity implements View.OnClickListener {
#Bind(R.id.iv1) ImageView iv1;
#Bind(R.id.iv2) ImageView iv2;
#Bind(R.id.iv3) ImageView iv3;
#Bind(R.id.iv4) ImageView iv4;
#Bind(R.id.iv5) ImageView iv5;
#Bind(R.id.iv6) ImageView iv6;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
iv1.setImageDrawable(getResources().getDrawable(R.drawable.c1));
iv2.setImageDrawable(getResources().getDrawable(R.drawable.c2));
iv3.setImageDrawable(getResources().getDrawable(R.drawable.c3));
iv4.setImageDrawable(getResources().getDrawable(R.drawable.c4));
iv5.setImageDrawable(getResources().getDrawable(R.drawable.c5));
iv6.setImageDrawable(getResources().getDrawable(R.drawable.c6));
}
You can create an abstract activity ImageryActivty that needs to override some method like getContentView which provides a layout id:
public abstract class ImageryActivity extends AppCompatActivity {
#Bind(R.id.iv1) ImageView iv1;
#Bind(R.id.iv2) ImageView iv2;
#Bind(R.id.iv3) ImageView iv3;
#Bind(R.id.iv4) ImageView iv4;
#Bind(R.id.iv5) ImageView iv5;
#Bind(R.id.iv6) ImageView iv6;
public abstract int getContentView();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentView());
ButterKnife.bind(this);
iv1.setImageDrawable(getResources().getDrawable(R.drawable.c1));
iv2.setImageDrawable(getResources().getDrawable(R.drawable.c2));
iv3.setImageDrawable(getResources().getDrawable(R.drawable.c3));
iv4.setImageDrawable(getResources().getDrawable(R.drawable.c4));
iv5.setImageDrawable(getResources().getDrawable(R.drawable.c5));
iv6.setImageDrawable(getResources().getDrawable(R.drawable.c6));
}
}
And your child activities must inherit from this one:
public class ActivityOne extends ImageryActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public int getContentView() {
return R.layout.activity_one;
}
}
Of course this layout must contain all the ImageView's with the proper id's. For this I´d recommend you to create a reusable layout imagery_layout and include it in each of your child activities:
<?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="match_parent"
android:orientation="vertical">
<include
layout="#layout/imagery_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!-- And here it comes the content for this particular activity in case there's one -->
</LinearLayout>
You can create one abstract BaseActivity activity that will have all the functionality common to those activities and then you can just extend the other activities with it that needs to have that common functionality
or you can simply use one activity and maintained all the states in it using some sort of switch statements all depends on your requirement
You should be able to do this with one Activity. You can pass arguments to Activities as Intent extras. Define some String constants in the Activity:
public static final String ARG_IN_IMAGE_ONE = "ActivityOne.ImageOne";
public static final String ARG_IN_IMAGE_TWO = "ActivityOne.ImageTwo";
...
Set your Drawable ids on creating the Intent:
intent.putIntExtra(ARG_IN_IMAGE_ONE, R.drawable.c1);
...
And read from the intent in onCreate:
iv1.setImageDrawable(getResources().getDrawable(getIntent().getIntExtra(ARG_IN_IMAGE_ONE)));
...
You can also optionally make a static builder which takes the 6 image ids and returns the Intent.
See the following Activity:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.root);
for (int i = 0; i < 8; i++) {
EditText editText = (EditText) LayoutInflater.from(this).inflate(R.layout.edittextlayout, null);
editText.setText("#" + i);
linearLayout.addView(editText);
}
}
}
The layout R.layout.activity_main:
<?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="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
and the layout R.layout.edittext_layout:
<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
After starting the app it looks like I would expect: every EditText being filled with it's index.
After rotating the device though, the Activity looks like this:
All the EditTexts are there, but they all contain the same text.
What baffles me even more is that this doesn't happen when creating the EditTexts programmatically with
EditText editText = new EditText(this)
instead of inflating it from a layout.
What's happening there?
You can check out my example and try for yourself here.
EDIT: This is not a duplicate of this question as in my case the text in the EditText does not double but get mixed up between different EditTexts.
Try to set some ID for each View.
For example:
view.setId(id);
Or use
onSaveInstanceState() - onRestoreInstanceState()
for saving info.
The problem is your activity recreates on every orientation and saved instance data handled wrong by Android. You can avoid it by setting an id to your dynamically created views or you can
Override onSaveInstanceState method to save your data like text in your edittexts and recover it on onCreate method like so:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check if we're recreating
if (savedInstanceState != null) {
// Restore value of edittext from saved state
editText.setText(savedInstanceState.getString("edittext"));
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putString("edittext", editText.getText().toString());
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
If you encounter any problem similar after doing this try to Override onRestoreInstanceState method to prevent android from doing your work.
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
//do nothing
}
I have the following Activity definition:
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/inspectionMainLayout"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:singleLine="false"
android:id="#+id/breadCrumb"
android:visibility="gone">
</LinearLayout>
<ExpandableListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/expandableListView" />
</LinearLayout>
Now in my code I do add buttons dynamically in breadCrumb LinearLayout:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inspection);
LinearLayout mainLayout = (LinearLayout) findViewById(R.id.inspectionMainLayout);
if (mainLayout != null) {
ExpandableListView list = (ExpandableListView) findViewById(R.id.expandableListView);
list.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i2, long l) {
LinearLayout breadCrumb = (LinearLayout) findViewById(R.id.breadCrumb);
Button filterButton = new Button(InspectionActivity.this);
filterButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onFilterButtonClick((Button) view);
}
});
filterButton.setText(item.getFormattedFilter());
breadCrumb.addView(filterButton);
}
}
}
...
}
This code works well, until I do not change the device orientation and my Activity is recreated. Although all the code is executing correctly, screen seems not being updated. Once I restore the previous orientation, all the items suddenly appear. Any idea why and how to fix it?
Thanks
EDIT:
I do think that I'm running into the same problem as describe in this post:
Android: findViewById gives me wrong pointer?
Any idea on how to solve this?
As requested my onRestoreInstanceState:
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
baseCategories = savedInstanceState.getParcelable(BASE_CATEGORIES_STATE);
currentFilter = savedInstanceState.getParcelable(FILTERS_STATE);
}
and on onSaveInstanceState:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(BASE_CATEGORIES_STATE, baseCategories);
outState.putParcelable(FILTERS_STATE, currentFilter);
}
now both of my classes do implement Parcelable interface.
They are persisted and restored correctly.
Still for some resaon the call to the findViewById get's me pointed to the wrong object (not the one that is recreated).
You add views dynamically (on user click event).
By default, android does not "remember" to keep these dynamic views when re-creating the activity on configuration changes, you have to handle this process yourself.
Some possibilities :
Avoid recreating activity on screen rotation by declaring android:configChanges="keyboardHidden|orientation|screenSize" for your activity in AndroidManifest.xml - This is highly not recommended
"Remember" what views were dynamically added when re-creating activity after rotation (for example using extra flags to detect that new filter button was added and pass it via bundle in onSaveInstanceState, and check in onCreate whether you need to re-create the button), or retain the whole view object as explained here
One extra note : you perhaps want to specify "vertical" orientation for your breadCrumb layout, it is horizontal by default.
I found out why this is happening.
onSave/onRestoreInstanceState I was persisting the currentFilter class which has some custom listeners on it.
As onResume method I was doing the following:
#Override
protected void onResume() {
if (currentFilter == null) {
currentFilter = new FilterItemList();
currentFilter.addListener(new FilterItemListListener() {
#Override
public void filterChanged(FilterChangedEvent e) {
filterCategories(categoryRepository);
}
#Override
public void filterAdded(FilterAddedEvent e) {
FilterItem addedItem = e.getAddedItem();
baseCategories.remove(new BaseCategory("", addedItem.getSectionName()));
}
#Override
public void filterRemoved(FilterRemovedEvent e) {
FilterItem removedItem = e.getRemovedItem();
baseCategories.add(new BaseCategory("", removedItem.getSectionName()));
}
});
}
}
The pointer to the previous instance was persisted. That's why my interface was not behaving correctly.
Now I do re-register listeners even when currentFilter is not null (it is restored) so they can point to the right instance.
Is there any pattern in handling this situations?
Thanks
I am new to android and would like to know as to why thiswont work.If i have a set of images in my res folder and i want to display them based on users choice such that the Mainactivity is like below:-
public class Activity3 extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.drawable.img1);//here i put the image name
}
}
Suppose i use this code snippet
public class Activity3 extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(R.id.text1=='1')
setContentView(R.drawable.img1);
else
setContentView(R.drawable.img2);
}
}
This doesnt work.But i would like to know why,how is the android stuff really working.This does seem to me logically right.
You can't set drawable to setContentView, but you can do this instead:
setContentView(R.layout.MyLayout);
ImageView view = (ImageView)findViewById(R.id.myviewid);
view.setImageResource(R.drawable.img1);
In each click add view.setImageResource(R.drawable.img1); to change the imageView resources.
if you see more carefully the documentation, can see the definition for setContentView method:
public void setContentView (int layoutResID)
if you note, the method receive a layoutResId, the key word is layout, remember that android used the logic (view - controller), an activity is the controller and a layout is the view. You can have visual elements in your view, like an image (in android can be contained in an ImageView).
For example, you can define an xml layout to the next way:
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/selected_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</ImageView>
this xml only contain an ImageView, in this ImageView with the id selected_image you will show the choice image.
In your activity you need put the next code (imagine the xml layout call image_layout.xml):
public class Activity3 extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_layout);
//here you need find the ImageView in this layout
((ImageView) findViewById(R.id.selected_image).setImageResource(R.drawable.img1);
}
}
I have created a custom dialog and a layout xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tap Me"
android:onClick="dialogClicked" />
</LinearLayout>
In the dialog class I've implemented the method "dialogClicked(View v)":
public class TestDialog extends Dialog {
public TestDialog(final Context context)
{
super(context);
}
#Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog);
}
public void dialogClicked(final View view)
{
System.out.println("clicked");
}
}
When I tap the button I get a NoSuchMethodException 'dialogClicked'. Setting the onClick handler in layout xml works fine for activities, but not in dialogs. Any ideas? What I'm doing wrong?
Define the method (dialogClicked) in Activity.
And modify TestDialog like the following code:
public class TestDialog extends Dialog {
Context mContext;
public TestDialog(final Context context)
{
super(context);
mContext=context;
}
#Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
LinearLayout ll=(LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.dialog, null);
setContentView(ll);
}
}
I think it works :)
I think the issue is one of scope. I'm not sure how'd you address this in xml, but essentially the dialogueClicked method in your layout xml doesn't know where to find the method you've defined in the dialog class.
The standard approach i've seen to bind buttons in custom layouts is as follows.
implement the OnClickListener class
Bind the buttons click event to the dialog class
Switch out the buttons in the onClick button based on id. You'd need to add an id to your button.
.
public class TestDialog extends Dialog implements android.view.View.OnClickListener
{
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog);
((Button)findViewById(R.id.dialog_btn_mybutton)).setOnClickListener(this);
}
public void onClick(View view)
{
switch (view.getId())
{
case R.id.dialog_btn_mybutton:
//do stuff
// dismiss();
// cancel etc.
break;
}
}
}
Hope that helps. Would still be interested in knowing if there was a solution to using xml onClick to bind to the method. Perhaps an additional argument in the setContentView? something r'other.
I've found the following code in the View.java source:
public void onClick(View v) {
if (mHandler == null) {
try {
mHandler = getContext().getClass().getMethod(handlerName,
View.class);
...
-> The views uses its context to resolve the onclick handler method.
Noew the following code from Dialog.java source:
public Dialog(Context context, int theme) {
mContext = new ContextThemeWrapper(context, theme == 0 ? com.android.internal.R.style.Theme_Dialog : theme);
...
In the constructor of the dialog an instance of ContextThemeWrapper gets created and set as context. This instance is neither the custom dialog class, nor the calling activity, which can be the place for implementing the handler method. Therefore views are not able to find the onclick handler method.
But I have to use the onclick XML attribut. Any workarounds available?
Dialogs need the signature
dialogClicked(DialogInterface dialog, int id) { ... }
android:onClick="method" is pretty cool, but it doesn't work on Android 1.5 so I am avoiding for some time.
An easy workaround:
Make your Dialog an Activity and use android:theme="#android:style/Theme.Dialog" in you AndroidManifest.
Following on from Jett Hsieh's post, I've implemented my dialogs slightly differently using showDialog and dismissDialog, but the fundamentals of getting the android:onClick working have been the same, my example code is below for future reference.
public class ExampleActivity extends Activity {
static final int DIALOG_DISCLAIMER = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
showDialog(DIALOG_DISCLAIMER);
}
protected Dialog onCreateDialog(int id)
{
Dialog dialog;
switch(id)
{
case DIALOG_DISCLAIMER:
dialog = new Dialog(this);
dialog.setContentView(R.layout.main_disclaimer);
LinearLayout ll = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.main_disclaimer, null);
dialog.setContentView(ll);
break;
default:
dialog = null;
}
return dialog;
}
public void onClick(View v)
{
switch(v.getId())
{
case R.id.maindisclaimer_button_accept:
dismissDialog(DIALOG_DISCLAIMER);
break;
}
}
}
And the layout file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/linearLayout1"
android:padding="10dp"
android:orientation="vertical"
android:background="#drawable/roundedcorners">
<Button
android:id="#+id/maindisclaimer_button_accept"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/string_accept"
android:onClick="onClick" >
</Button>
</LinearLayout>
Try to define that method (dialogClicked) in the activity and not in the dialog.
It might use reflection so if you use different activities just write that method in each activity that might show that dialog
A dialog is always created and displayed as part of an Activity. According to Android References:
If you decide to create a dialog outside of the onCreateDialog() method,
it will not be attached to an Activity. You can, however,
attach it to an Activity with setOwnerActivity(Activity).
Also, are you passing the object returned by getApplicationContext(); to the constructor of TestDialog?
system looks for the method in the where the layout has been inflated from, or in the activity class to which the xml was set as content.