I need to update a progress dialog fragment from inside an AsyncTask. I know how to do this using the old ProgressDialog, but now this way of showing dialogs is deprecated and I'm trying to do the same with Fragments.
This is the fragment code:
public static class CustomProgressDialogFragment extends SherlockDialogFragment {
static CustomProgressDialogFragment newInstance(Object... params){
CustomProgressDialogFragment df = new CustomProgressDialogFragment();
Bundle bundle = new Bundle();
// Populate bundle with params (not shown)
df.setArguments(bundle);
return df;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setCancelable(false);
int style = DialogFragment.STYLE_NORMAL, theme = 0;
setStyle(style,theme);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Bundle bundle = this.getArguments();
ProgressDialog dialog = new ProgressDialog(getActivity());
// Get params from bundle and customize dialog accordingly (not shown)
dialog.setCancelable(false);
dialog.setIndeterminate(false);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
return dialog;
}
}
and this is how I show it:
FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
CustomProgressDialogFragment dialogFragment = CustomProgressDialogFragment.newInstance(<My params here>);
ft.disallowAddToBackStack();
ft.add(dialogFragment, stringSet.tag);
ft.commitAllowingStateLoss();
Now I need to call incrementProgressBy over the dialog from the AsyncTask. Is there a way to retrieve the underlying dialog from the FragmentManager? What would be the proper way of doing this? (I don't want to leak references if the dialogs gets recreated).
Thanks in advance.
This can be achieved calling DialogFragment.getDialog(). The DialogFragment can be retrieved as usual, with FragmentManager.findFragmentByTag.
Related
I have two activities. When an item in a RecyclerView is selected, it takes the user to the second activity and fills in the details with the related RecyclerView item.
In the second RecyclerView activity, there is a Spinner. Depending on the item selected in the spinner, different RecyclerViews become visible/invisible to the user on the second activity.
How do I make it work so that the information is sent from the first activity to command the spinner on what to do?This is how Second Activity looks
SecondActivity.java
public class SecondActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private TextView tv_title, tv_description;
private ImageView PartiesThumbnailImg,PartiesCoverImg;
private RecyclerView RvPartyMembers;
private PartyMembersAdapter partyMembersAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_parties_detail);
Spinner spinner = findViewById(R.id.spnConstituencies);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.constituencies, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(this);
//ini views
iniViews();
//Setting up members list
setupRvPartyMembers();
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String text = parent.getItemAtPosition(position).toString();
Toast.makeText(parent.getContext(), text, Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
void iniViews() {
RvPartyMembers = findViewById(R.id.rv_party_members);
String partiesTitle = getIntent().getExtras().getString("title");
String partiesDescription = getIntent().getExtras().getString("description");
int imageResourceId = getIntent().getExtras().getInt("imgURL");
int imagecover = getIntent().getExtras().getInt("imgCover");
PartiesThumbnailImg = findViewById(R.id.detail_members_img);
Glide.with(this).load(imageResourceId).into(PartiesThumbnailImg);
PartiesThumbnailImg.setImageResource(imageResourceId);
PartiesCoverImg = findViewById(R.id.detail_members_cover);
Glide.with(this).load(imagecover).into(PartiesCoverImg);
tv_title = findViewById(R.id.tvPartyTitle);
tv_title.setText(partiesTitle);
tv_description = findViewById(R.id.tvPartyDesc);
tv_description.setText(partiesDescription);
}
void setupRvPartyMembers(){
List<PartyMembers> mdata = new ArrayList<>();
mdata.add(new PartyMembers("name",R.drawable.members_brendangriffin_fg));
mdata.add(new PartyMembers("name",R.drawable.members_brendangriffin_fg));
mdata.add(new PartyMembers("name",R.drawable.members_brendangriffin_fg));
partyMembersAdapter = new PartyMembersAdapter(this,mdata);
RvPartyMembers.setAdapter(partyMembersAdapter);
RvPartyMembers.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));
}
These are the important pieces of code of the FirstActivity.java
#Override
public void onPartiesItemClick(PartiesOireachtas partiesOireachtas, ImageView partiesImageView) {
Intent intent = new Intent(getContext(),PartiesDetailActivity.class);
intent.putExtra("title", partiesOireachtas.getTitle());
intent.putExtra("description",partiesOireachtas.getDescription());
intent.putExtra("imgURL", partiesOireachtas.getThumbnail());
intent.putExtra("imgCover",partiesOireachtas.getCoverPhoto());
and
Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//RecyclerView Setup
//int data
lstPartiesOireachtas = new ArrayList<>();
lstPartiesOireachtas.add(new PartiesOireachtas("Fianna Fáil", "example1", R.drawable.fianna_fail_logo,R.drawable.fianna_fail_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Sinn Féin", "example2", R.drawable.sinn_fein_logo,R.drawable.sinn_fein_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Fine Gael", "example4", R.drawable.fine_gael_logo,R.drawable.fine_gael_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Green Party", "example9", R.drawable.green_party_logo,R.drawable.green_party_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Social Democrats", "example3", R.drawable.soc_dems_logo,R.drawable.soc_dems_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Independent", "example2", R.drawable.independent_party_logo,R.drawable.independent_party_cover));
As you probably noticed an activity doesn't have a default constructor, but uses an onCreate(Bundle savedInstanceState) method. This means you should pass your information using this Bundles, or use something like SharedPreferences / SQLite. Since SharedPreferences / SQLite is a bit overkill for this in my opinion, you can add the object upon creating an intent.
Intent intent = new Intent();
intent.putExtra("name", parcelableObject);
If you want to pass a custom object you have to implement the Parcelable interface, this is pretty straightforward since you can basically auto generate all code for this in Android studio (just hit alt-enter a few times). For more information you can check out the following link.
https://developer.android.com/reference/android/os/Parcelable
In the receiving activity you can do the following:
Bundle bundle = getIntent().getExtras();
Object object = bundle.getObject("name"); //Don't forget to cast!
I don't understand DialogFragment at all. How to create one, how to get the user input out of it, and set it into a TextView.
I would like for the TITLE button, when clicked, to bring up a DialogFragment asking the user to enter the title of their Mood Board. The user enters a title. When they click the PostiveButton "Done", the user's title is set into the top left frame of the mood board, which has a TextView with a hint.
Please! Ask questions, because I don't really understand the dialog setup.
Here is a picture of my main_layout, in my MainActivity. Every element has an "#+id/".
The solution you are looking for is a callback:
Create an interface with a method to use as a callback
Implements the interface on the activity
Create the dialog fragment and in onAttach get the interface
Show the dialog fragment on the activity
On dismiss the dialog fragment pass the text using the instance of the interface
interface Callback {
updateText(String text)
}
class CoolActivity... implements Callback
onCreate {
//find your views
showDialogBtn.setOnClickListener(...
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment prev = getSupportFragmentManager().findFragmentByTag("yourTag");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
DialogFragment dialogFragment = ExampleDialogFragment.newInstance();
dialogFragment.show(ft, "yourTag");
)
}
#Override
updateText(String text) {
youtView.setText(text)
}
class CoolDialogFragment extend DialogFragment {
private Callback callback;
#Override
void onAttach(Context context) {
callback = (Callback) context
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new Dialog(getActivity());
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.dialog_fragment_example, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//find the views in the dialog fragment
closeBtn.clickListener(...
callback.updateText(edittext.getText().toString())
dismiss()
)
}
}
Here is a gist of a dialog fragment
https://gist.github.com/cutiko/7e307efcf7492ea52ef05cd90f9e3203
The problem is you want to connect a dialog fragment with a another component, and you want to do it straigth forward. This is not considered a good practice because yiu create 2 componentes higly attached, so the best would be to use data persistence and some form of react programming
You can make your mood board title textview static then call it to the alertdialog with edittext to set it text (setText)
like this.
final EditText edittext = new EditText(MainActivity.this);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setMessage("Input Title")
.setView(edittext)
.setCancelable(false)
.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
YourCustomDialog.your_title_textviewMoodboard.setText(edittext.getText().toString());
}
})
.setNegativeButton("Back", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
AlertDialog alert = builder.create();
alert.show();
in your custom dialog. declare your textview static globally
public static TextView your_title_textviewMoodboard;
I have a main Activity in which I created a ViewPager that instantiate 3 other Fragments. One of these is a GridView which makes a popup appear when the user click on one item. Then, in this popup, I have a simple button.
What I want to do is: when the user click on this button, I would like to access a method in my main Activity (that should change the current item of my ViewPager) and then dismiss the popup.
I tried everything I could, but I cannot achieve this... I can set up the click event on my popup and dismiss it easily, but I didn't find out how I can access a method (or even a variable) from my popup to my main Activity.
I will put my most relevant code in here so you can understand the structure of my classes (hopefully...).
My main Activity:
public class MainActivity extends FirstActivity{
private ViewPager mViewPager;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
// Set an Adapter on the ViewPager
mViewPager.setAdapter(new MainActivity_Adapter(getSupportFragmentManager()));
public void onSaveInstanceState(Bundle savedInstanceState)
{
menuBar.onSaveInstanceState(savedInstanceState);
}}
My ViewPager activity:
public class MainActivity_Adapter extends FragmentPagerAdapter{
public MainActivity_Adapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
// Set the color background for each page
switch (position)
{
case 0:
return MainActivity_Inventory.newInstance();
case 1:
return MainActivity_Map.newInstance();
default:
return MainActivity_AR.newInstance();
}
}
// The number of Splash Screens to display
#Override
public int getCount()
{
return 3;
}}
My "Inventory" Fragment
public class MainActivity_Inventory extends Fragment implements View.OnClickListener{
public static MainActivity_Inventory newInstance()
{
MainActivity_Inventory frag = new MainActivity_Inventory();
Bundle b = new Bundle();
frag.setArguments(b);
return frag;
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Select the layout
int layout;
layout = R.layout.activity_inventory_01;
// Inflate the layout resource file
View view = getActivity().getLayoutInflater().inflate(layout, container, false);
// Set the grid view
GridView gridview = (GridView) view.findViewById(R.id.inventory_gridView);
gridview.setAdapter(new InventoryImageAdapter(super.getActivity()));
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
public void onItemClick(AdapterView parent, View v, int position, long id)
{
// Create a popup to show item details
createPopup();
}
});
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
}
public void createPopup()
{
DialogFragment newFragment = new PopupActivity_Inventory();
newFragment.show(getFragmentManager(), "itemDetails");
}
#Override
public void onClick(View v)
{
}}
And my popup dialog fragment:
public class PopupActivity_Inventory extends DialogFragment{
#Override
public Dialog onCreateDialog(final Bundle savedInstanceState)
{
// Build the alert dialog
final Dialog dialog = new Dialog(this.getActivity());
// Get the layout inflater
final LayoutInflater inflater = getActivity().getLayoutInflater();
// Set up the dialog box
dialog.setContentView(inflater.inflate(R.layout.activity_inventory_popup_01, null));
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.getWindow().setGravity(Gravity.TOP);
//dialog.getWindow().getAttributes().y = 100;
(dialog.findViewById(R.id.brick_button_01)).setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
// When button is clicked, ACCESS MAIN ACTIVITY!
dialog.dismiss();
}
});
return dialog;
}}
I really hope you can help me with this... I really need to get it working. Thank you very much!
If you need further details or explanation, please just tell me.
The best thing to do is use EventBus library. I have a demo app in which you can add items to RecyclerView from anywhere within the app using EventBus. You can use it as a reference to simply do something else instead of current task. Here is the link to the repo:
https://github.com/code-crusher/android-demos/tree/master/EventBusDemo
And if you want to understand how it works you can refer to my article, it explains how to make communications like this easy:
https://medium.com/#code_crusher/eventbus-for-android
Hope it helps. Happy coding :)
Read this https://developer.android.com/training/basics/fragments/communicating.html
Look for "To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity...."
Another way to achieve it is using an EventBus and post events by Fragments to be caught by Activities.
I have an Activity A with a fragment frag2. Inside the fragment I have a RecyclerView and Adapter to show a list of custom class objects. Adding objects to the adapter is handled programmatically. I have a button inside TwoFragment that opens a FragmentDialog. I'd like to add an object to my Adapter by confirming this dialog, but it seems that the adapter is null when called from the FragmentDialog.
The same adapter is not null, and works if I call it from the fragment OnClick.
Moreover the adapter is null only after screen rotation, it works fine before rotating.
To communicate between the two Fragments I implement a communicator class in activity A.
Activity A
public void respond(String type) {
frag2.addSupport(type);
}
frag2
public RecyclerView rv;
public ArrayList<support> supports;
public myAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supports = new ArrayList<>();
adapter = new myAdapter(supports);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View layout = inflater.inflate( R.layout.fragment_two, container, false);
layout.setId(R.id.frag2);
if (savedInstanceState!=null)
{
supports = savedInstanceState.getParcelableArrayList("supports");
}
rv = (RecyclerView) layout.findViewById(R.id.rv);
adapter = new myAdapter(supports);
rv.setAdapter(myAdapter);
rv.setLayoutManager(new LinearLayoutManager(getActivity()));
rv.setItemAnimator(new DefaultItemAnimator());
#Override
public void onClick(View v) {
int id = v.getId();
switch (id){
case R.id.button1:
addSupport(type); // THIS WORKS ALWAYS, even after screen rotate
break;
case R.id.button2:
showDialog();
break;
}
}
public void showDialog(){
FragmentManager manager = getFragmentManager();
myDialog dialog = new myDialog();
dialog.show(manager, "dialog");
}
public void addSupport(String type){
adapter.addItem(new support(type)); // this line gives null pointer on adapter, but only if called after screen rotate and only if called from the dialog
}
dialog
communicator comm;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog, null);
comm = (myCommunicator) getActivity();
create = (Button) view.findViewById(R.id.button_ok);
create.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
if(v.getId()==R.id.button_ok)
{
// some controls to set type
comm.respond(type)
dismiss();
}
else {
dismiss();
}
myAdapter
public class myAdapter extends RecyclerView.Adapter<myAdapter.VH> {
private LayoutInflater inflater;
private ArrayList<support> data = new ArrayList<>();
// settings for viewholder
public myAdapter (ArrayList<support> data)
{
this.data=data;
}
public void addItem(support dataObj) {
data.add(dataObj);
notifyItemInserted(data.size());
}
}
logcat
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference
I hope there are no mistakes, I shortened the code for better understanding. Keep in mind that everything works if I never rotate the screen.
I'm a beginner with android and I'm stuck with this for several days now. Please, help.
To understand the problem, it's as you say:
.. everything works if I never rotate the screen
So firstly to understand what happens on rotation, this is a quote from the Android Developer website:
Caution: Your activity will be destroyed and recreated each time the user rotates the screen. When the screen changes orientation, the system destroys and recreates the foreground activity because the screen configuration has changed and your activity might need to load alternative resources (such as the layout).
Ok, now to understand the error:
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference
Essentially, in your dialog class, you have created a strong dependency by declaring :
comm = (myCommunicator) getActivity();
because comm references objects which would have been destroyed on rotation, hence the NullPointerException.
To further understand runtime changes, such as orientation changes, I'd recommend going through Handling Runtime Changes.
Update
Thank you for your answer, what would you recommend instead of comm = (myCommunicator) getActivity(); ?
The solution comes in 3 parts:
Make sure the onCreate of Activity A has the following:
#Override
public void onCreate(Bundle savedInstanceState) {
......
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
frag2 = (Frag2) fm.findFragmentByTag(“frag2”);
// create frag2 only for the first time
if (frag2 == null) {
// add the fragment
frag2 = new Frag2();
fm.beginTransaction().add(frag2 , “frag2”).commit();
}
......
}
Add setRetainInstance(true) to the onCreate of frag2.
Remove the implicit referencing i.e. comm = (myCommunicator) getActivity();, and implement something more loosely coupled for dialog.
dialog
public interface Communicator {
void respond(String type);
}
Communicator comm;
....
public void addCommunicator(Communicator communicator) {
comm = communicator;
}
public void removeCommunicator() {
comm = null;
}
#Override
public void onClick(View v) {
if((v.getId()==R.id.button_ok) && (comm!=null))
{
// some controls to set type
comm.respond(type);
}
// Regardless of what button is pressed, the dialog will dismiss
dismiss();
}
This allows you do the following in frag2 (or any other class for that matter):
frag2
<pre><code>
public class Frag2 extends Fragment implements dialog.Communicator {
........
public void showDialog() {
FragmentManager manager = getFragmentManager();
myDialog dialog = new myDialog();
dialog.addCommunicator(this);
dialog.show(manager, "dialog");
}
#Override
public void respond(String type){
adapter.addItem(new support(type));
}
}
I have a dialog fragment with a custom view. The background color of the custom view also changes depending on the passed argument from newInstance() method. To make certain that it's really a different DialogFragment instance, I also passed another argument to the newInstance() method, the dialog title. The background color can also accept a null value, in which case, no background color is set.
Now here is the problem: when I show the dialog fragment with the null value passed for the background color the second time, the inflated view still has the background color of the last shown dialog fragment even though the title is already different. Why is the system reusing the last inflated view? Shouldn't the background color be empty? What am I not understanding properly?
MyDialogFragment.java
public static MyDialogFragment newInstance(String title, Integer bgColor) {
MyDialogFragment df = new MyDialogFragment();
Bundle args = new Bundle();
args.putString(TITLE_ARG, title);
args.putSerializable(BG_ARG, bgColor);
df.setArguments(args);
return df;
}
...
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Bundle args = getArguments();
Integer bgColor = (Integer) args.getSerializable(BG_ARG);
View view = getActivity().getLayoutInflater().inflate(R.layout.my_dialog_fragment, null);
// check if null was passed
if(bgColor != null)
((GradientDrawable) view.getBackground()).setColor(bgColor);
return new AlertDialog.Builder(getActivity())
.setTitle(args.getString(TITLE_ARG))
.setView(view)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, null)
.create();
}
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fm = getSupportFragmentManager();
MyDialogFragment df = (MyDialogFragment) fm.findFragmentByTag(MyDialogFragment.TAG);
if(df == null) {
df = MyDialogFragment.newInstance("title1", Color.GREEN);
df.show(fm, MyDialogFragmentTAG);
}
}
});
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fm = getSupportFragmentManager();
MyDialogFragment df = (MyDialogFragment) fm.findFragmentByTag(MyDialogFragment.TAG);
if(df == null) {
df = MyDialogFragment.newInstance("title2", null);
df.show(fm, MyDialogFragmentTAG);
}
}
});
}
The first click of button1 will create dialog and attach it to the FragmentManager.
The click of button2 (second click) will find your old fragment, so df should not be null, and you should not see any dialog.
Maybe your snippet of code is incomplete.