I have an Activity that has a section where a Fragment is shown. A button is clicked on the Fragment and a new Activity launches with some buttons. Depending on which button is clicked, that activity closes and the Fragment should be updated accordingly but I'm having trouble accessing the views on the Fragment to edit them.
It seems that in the onResume() of the Fragment, I'm unable to access the view using getView(). I've also tried passing in the view from the Activity that the Fragment lives in, but that also returned null.
So I'm wondering if Fragments are static after onCreateView() is called?
SecondActivity calling back into MainActivity:
#Override
protected void onResume() {
super.onResume();
rvAdapter.setOnItemClickListener(new RVAdapter.MyClickListener() {
#Override
public void onItemClick(View v, int position) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.putExtra("key", value);
startActivity(intent);
}
});
}
MainActivity handling the call back and calling a function in the Fragment:
#Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
String value = intent.getStringExtra("key");
if (value != null) {
((ExampleFragment) exampleFragment).updateFragment(value);
}
}
Fragment attempting to update itself:
public void updateFragment(String value) {
TextView textView = (TextView) getView().findViewById(R.id.profile_text_view);
if (value != null) {
textView.setText(value);
}
}
And the result would be NPE on getView() call in the Fragment. I've tried passing in the view from MainActivity via: exampleFragment.getView() but also no luck.
Related
I'm making my first android app using android studio. In this APP I have a listview with 12 classes(12 items). After clicking on one class, it goes into a tabbed activity with 10 items of this class. On each tab page I have a rating bar to let people rate the item.
I set an activity for the listview, and 12 independent activities for those 12 tabbed activities. The code from listview to each tabbed activity is like this:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if(i==0){
Intent intent = new Intent(ListViewActivity.this, TabbedActivity1.class);
intent.putExtra("styleName", STYLE_NAMES[i]);
intent.putExtra("styleExample",STYLE_EXAMPLES[i]);
startActivity(intent);
}
else if(i==1){
Intent intent = new Intent(ListViewActivity.this, TabbedActivity2.class);
intent.putExtra("styleName", STYLE_NAMES[i]);
intent.putExtra("styleExample",STYLE_EXAMPLES[i]);
startActivity(intent);
}
...... // skip the other 10 tabbed activities.
}
Now the problem is: after I finish rating on the tabbed activities, I return to the ListView activity and click into each tabbed activity again, the ratings are gone.
I guess the reason is that in my code, each time I click on the item it opens a new tabbed activity, although same layout, the contents are not saved.
So I was wondering whether I should do something on the ListView activity to save the ratings. I have searched for relevant questions, but I found in their scenarios, each list item is just a simple ratingbar. But here, my list item is a tabbed activity with 10 ratingbars.
Therefore, I have no idea how to do it. I have no experience in android studio, so I don't know where to start to solve the problem. Any idea is appreciated! Thanks a lot in advance!!
First of all if all your tab activities are similar you can just create one activity instead of that many in your case 12 and pass the specific content and states via intent.
The basic approach to your question is would be store rating states in your main activity and when you open your tab activity each time you click the list items send the rate of the relevant activity with intent. Then in your tab activity update the rate with it.
To achieve this we are going to use startActivityForResult instead of startActivity because we need tab activity to return last state of rating bars.
You can see the basic example shown below here:
public class ListViewActivity extends AppCompatActivity {
private static final int REQUEST_RATE = 1;
private SparseIntArray rates = new SparseIntArray();
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(ListViewActivity.this, TabActivity.class);
intent.putExtra("styleName", STYLE_NAMES[i]);
intent.putExtra("styleExample", STYLE_EXAMPLES[i]);
intent.putExtra("position", i);
intent.putExtra("rating", rates.get(i, 0));
startActivityForResult(intent, REQUEST_RATE);
}
}
}
#Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_RATE:
if(resultCode == RESULT_OK) {
//retrieve and save rates
Bundle extras = data.getExtras();
int position = extras.getInt("position");
int rating = extras.getInt("rating");
rates.put(position, rating);
}
break;
}
}
}
public class TabActivity extends AppCompatActivity {
private RatingBar ratingBar;
private int position;
#Override protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab);
Bundle extras = getIntent().getExtras();
position = extras.getInt("position");
int rating = extras.getInt("rating");
ratingBar.setRating(rating);
}
#Override protected void onDestroy() {
//send current rating to list activity before we leave
setResult();
super.onDestroy();
}
private void setResult() {
Intent intent = new Intent();
intent.putExtra("position", position);
intent.putExtra("rating", ratingBar.getRating());
setResult(RESULT_OK, intent);
}
}
i've got a "simple" question:
How do i pass a view from an Activity to another?
In my case:
I've got a MainActivity with setContentView(R.layout.activity_main); In this activity_main i've declared a Relativelayout, whose Backgroundcolor I wanna change from another Activity, that gets its setContentView from an .xml file, where you cant find this RelativeLayout. How do i do that?
Thanks for the quick answer!
MainActivity.java
public class MainActivity extends AppCompatActivity{
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
//Save the text of the editText
bringer = savedInstanceState.getString("bringer");
input.setText(bringer);
//Save the Listview
content = savedInstanceState.getStringArrayList("content");
adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, content);
layout = (ListView) findViewById(R.id.layout);
layout.setAdapter(adapter);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArrayList("content", content);
outState.putStringArrayList("list", list);
bringer = input.getText().toString();
outState.putString("bringer", bringer);
}
Second Activity:
public class pop extends Activity {
back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Thats where it fails, because it couldnt get the parent-
view.
finish();
}
});
sure.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
}
The short answer is that you can't pass a View between activities.
If you have a View in ActivityOne that you want to modify based on the user's actions in ActivityTwo, then you should have ActivityOne launch ActivityTwo using startActivityForResult().
ActivityTwo can then call the setResult() method, and ActivityOne can implement onActivityResult() to receive that info.
I have 2 Fragments that both pass an intent to an Activity (through an event listener). How can the Activity know which of these 2 fragments passed the intent? There's a method called getCallingActivity(), I need the equivalent for fragments.
I Attempted to determine which Fragment Called the Activity with the onAttachFragment() method, But it doesn't work:
public class DetailsActivity extends Activity {
static final String POSITION = "position";
private Movie movie;
private int position;
private static final String TAG = "app";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
}
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
Log.v(TAG, "fragment_src");
position = (int) getIntent().getExtras().get(POSITION);
if (fragment instanceof PopularFragment) {
movie = PopularFragment.movieList.get(position);
setData();
} else if (fragment instanceof TopRatedFragment) {
movie = TopRatedFragment.movieList.get(position);
setData();
}
}
public void setData() {
TextView original_title = (TextView) findViewById(R.id.original_title);
original_title.setText(movie.getOriginal_title());
...
}
}
You could send different int values with your intent for both fragment and check in your activity..or you could get your fragment by using
Fragment fragment = getFragmentManager().findFragmentByTag("yourtag");
Why don't you pass some parameters through the Intent itself
For first fragment-
Intent intent=new Intent(getActivity(),DetailsActivity.class);
intent.putExtra("fragName","PopularFragment");
startActivity(intent);
For Second Fragment-
Intent intent=new Intent(getActivity(),DetailsActivity.class);
intent.putExtra("fragName","TopRatedFragment");
startActivity(intent);
In your Activity
String fragName=getIntent().getStringExtra("fragName");
And then just check using if..else.
I have a listfragment in an activity, a dialogfragment will open on pressing on a row for the further options, now I rotate the device and choose a option from dialogfragment, its throws an IllegalStateException .... Fragment is not attached.
choiceDialog
.setOnClickListDialogClickListener(new StandardListDialogFragment.OnClickListener<String>() {
#Override
public void onClick(DialogInterface dialog, int which,
String value) {
Intent intent = new Intent(this.getActivity(),
DutyEditor.class);
startActivityForResult(intent, 0);
dialog.dismiss();
}
});
This happens because the Fragment is destroyed and re-created with a FragmentManager whenever orientation changes.
You should stick to these rules when working with Fragments
Avoid setters when using Fragments.
Never hold a field reference to a Fragment in an Activity.
You can hold a field reference to an Activity on a Fragment after onAttach() and before onDetach(), but I find it better to call getActivity() and check for null every time I need it for short operations.
The best option is to call startActivityForResult() in the DialogFragment itself.
But whenever you really need to deliver the DialogFragment click events to the Activity, use an Interface. For example.
public final class SomeDialogFragment extends DialogFragment {
/**
* Callbacks of {#link SomeDialogFragment}
*/
public interface SomeDialogFragmentCallbacks {
/**
* Called when user pressed some button
*/
void onSomeButtonClick();
}
#Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
// Make sure the Activity can receive callbacks
if (!(activity instanceof SomeDialogFragmentCallbacks)) {
throw new RuntimeException("Should be attached only to SomeDialogFragmentCallbacks");
}
}
// now whenever a button is clicked
#Override
public void onClick(DialogInterface dialog, int which) {
final SomeDialogFragmentCallbacks callbacks = (SomeDialogFragmentCallbacks) getActivity();
if (callbacks != null) {
callbacks.onSomeButtonClick();
}
}
}
And all Activities that use this DialogFragment should implelent the callbacks method.
public final class SomeActivity extends Activity implements SomeDialogFragmentCallbacks {
#Override
public void onSomeButtonClick() {
// Handle some DialogFragment button click here
}
}
I use the same activity for save and edit purpose. I have a list view.
In List fragment I have
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
if(dbHelper != null){
Item item = (Item) this.getListAdapter().getItem(position);
Intent intent = new Intent(getActivity(), SaveEditActivity.class);
intent.putExtra("Update_Item", item);
startActivity(intent);
}
}
Now in SaveEditActivity I have
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(....);
Intent intent = getIntent();
if(intent.hasExtra("Update_Item")){
Item item = (Item) intent.getSerializableExtra("Update_Item");
if(item != null){
this.isEdit = true;
this.editedItem = item;
setItemData(item); // set the data in activity
}
}
}
Now when user clicks on save button I check for isEdit flag
if(isEdit == true){
updateItem();//update
}else{
saveItem(); // add
}
}
In Update method I update the item in database using editedItem Object
private void updateItem(){
if(this.editedItem != null){
dbHelper.updateItem(editedItem);
}
}
But my list view is not refreshed with new data.
I am using customAdapter which extends BaseAdapter.
In ListFragmet I load the data into adapter in onResume() method
#Override
public void onResume() {
super.onResume();
dbHelper = new DatabaseHandler(getActivity());
List<Item> existingItems = dbHelper.getItemData();
adapter = new CustomAdapter(getActivity().getApplicationContext(), existingItems);
if(adapter != null){
this.setListAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
Now when returning from activity this method should be called and adapter should be notified of the change? Isnt that right?
Can anyone help please?
Somehow you have to notify your ListView that the data set changed/updated so that it should refresh itself. In one of my app I used the Observer interface & Observable class to observe data changed or not. This helps me to solve the case.
The idea you can use is on every update/save the data set changed, so after doing so you should call a method named notifyDataSetChanged()
/* don't be confuse with this method's name, Its a custom method. I
used this name. You may use another like myAbcdMethod() :) */
private void notifyDataSetChanged() {
setChanged();
notifyObservers(new Boolean(true));
}
notifyDataSetChanged() is a private method, & you should implement this in your DbHelper. After every edit.save you can call this.
And in your activity you should use the following to refresh the list:
#Override
public void update(Observable observable, Object data) {
isDataChanged = (Boolean) data;
}
#Override
protected void onRestart() {
super.onRestart();
if (isDataChanged) {
adapter.clear();
adapter.refresh(screenNames.toArrayList());
adapter.notifyDataSetChanged();
isDataChanged = false;
}
}
This works in single activity app to multiple activity app.