Mark selected user as checked in FriendPicker (Facebook SDK for Android) - android

I've done the tutorials for the Facebook SDK for Android (Especially the "Show Friends"-tutorial).
How can I mark the selected users which i've selected before at the PickerActivity when i click on the "Show Friends"-button again?

I've looked at Facebook SDK's source code and it seems to me that PickerFragment does not give you possibility to reselect previously selected items. Since Facebook SDK is published under Apache License 2.0 and you have access to full source code I guess you could try to modify PickerFragment in such a way that it will have necessary methods.

You have to pass comma separated string with selected facebook user ids in the bundle to the FriendPickerFragment.
Bundle args = new Bundle();
args.putString("com.facebook.android.PickerFragment.Selection", "11111, 2222, 333, already selected facebook ids....");
friendPickerFragment = new FriendPickerFragment(args);
In PickerFragment's onActivityCreated() it will parse the selected ids shows as selected in list. You can see the following code in PickerFragment in FacebookSDK.
selectionStrategy.readSelectionFromBundle(savedInstanceState, SELECTION_BUNDLE_KEY);

Here is a solution which worked for me. I extend a standard FriendPickerFragment.
public class FriendPickerFragment2 extends FriendPickerFragment {
SelectionStrategy selectionStrategy;
String mPreSelectedIDs;
public FriendPickerFragment2(Bundle args)
{
super(args);
}
#Override
SelectionStrategy createSelectionStrategy() {
selectionStrategy = getMultiSelect() ? new MultiSelectionStrategy() : new SingleSelectionStrategy();
return selectionStrategy;
}
public void showInitialSelection()
{
Bundle bundle = new Bundle();
bundle.putString(this.MULTI_SELECT_BUNDLE_KEY, mPreSelectedIDs);
selectionStrategy.readSelectionFromBundle(bundle, this.MULTI_SELECT_BUNDLE_KEY);
adapter.notifyDataSetChanged();
}
public void setInitialSelection(String IDs)
{
mPreSelectedIDs = IDs;
}
}
I use FriendPickerFragment2 as a normal FriendPickerFragment. In OnCreate I do the following:
FragmentManager fm = getSupportFragmentManager();
if (savedInstanceState == null) {
final Bundle args = getIntent().getExtras();
friendPickerFragment = new FriendPickerFragment2(args);
friendPickerFragment.setInitialSelection(pickedUsersString());
fm.beginTransaction()
.add(R.id.friend_picker_fragment, friendPickerFragment)
.commit();
} else {
friendPickerFragment = (FriendPickerFragment2) fm.findFragmentById(R.id.friend_picker_fragment);
}
Here pickedUsersString is a coma separated string of IDs.
The last point is to add one row in the OnStart:
protected void onStart() {
super.onStart();
try {
friendPickerFragment.loadData(false);
friendPickerFragment.showInitialSelection();
} catch (Exception ex) {
onError(ex);
}
}
This solution worked for me.

Good news. In the current SDK version (3.6) the following feature was added:
Added setSelection methods on FriendPickerFragment to allow pre-selection of friends.
Added example use of setSelection API in Friend Picker Sample

FriendPickerFragment has this method:
/**
* Sets the list of friends for pre selection. These friends will be selected by default.
* #param userIds list of friends as ids
*/
public void setSelectionByIds(List<String> userIds) {
preSelectedFriendIds.addAll(userIds);
}
And this method:
public void setSelection(List<GraphUser> graphUsers) {
List<String> userIds = new ArrayList<String>();
for(GraphUser graphUser: graphUsers) {
userIds.add(graphUser.getId());
}
setSelectionByIds(userIds);
}
You should call one of these methods as soon as fragment instantiation:
friendPickerFragment = new FriendPickerFragment(args);
friendPickerFragment.setSelectionByIds(fbIDs);

Related

Activity crashing when setting non-view fragment listener on rotation

I have an Activity that dynamically adds two fragments. One is a hidden (no view) Fragment that has setRetainInstance(true) and handles the interface to my Database Handler. Its purpose is to start the AsyncTask for getting data out of the database and listen for the Database Handler to give its results back. It will then hand the data back to the Activity via another listener. The Activity will then hand the data to the display Fragment which has a ListView within and will display accordingly.
Activity: NOT a FRAGMENT ACTIVITY
import android.app.Activity;
import android.os.Bundle;
public class Workout_Search_Display_Activity extends Activity {
private final String search_string = "SEARCH_STRING";
private final String search_type = "SEARCH_TYPE";
private String Search_String = "";
private String Search_Type = "";
private Workout_Search_Holder_Fragment SearchHolder;
private Workout_Search_Display_Fragment search_display_fragment;
private Workout_Search_Activity_Listener WSAL;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Search_String = getIntent().getStringExtra(search_string);
Search_Type = getIntent().getStringExtra(search_type);
if (getFragmentManager().findFragmentById(android.R.id.content) == null) {
SearchHolder = Workout_Search_Holder_Fragment.newInstance(Search_String, Search_Type);
getFragmentManager().beginTransaction()
.add(android.R.id.content, SearchHolder).commit();
search_display_fragment = Workout_Search_Display_Fragment.newInstance();
getFragmentManager().beginTransaction()
.add(android.R.id.content, search_display_fragment).commit;
} //added too try to fix// else
//added to try to fix// SearchHolder = (Workout_Search_Holder_Fragment) getFragmentManager.findFragmentByTag("search_holder");
WSAL = new Workout_Search_Activity_Listener() {
public void NothingFound() {
search_display_fragment.no_data();
}
public void results_found(ArrayList<Search_Results_Holder> results) {
search_display_fragment.is_data();
search_display_fragment.handover_data(results);
}
};
SearchHolder.setListener(WSAL);
}
}
Fragment:
import android.app.Fragment;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
public class Workout_Search_Holder_Fragment extends Fragment implements DatabaseHelper.Workout_Search_Listener {
private String Search_String = "";
private String Search_Type = "";
private final static String search_string = "SEARCH_STRING";
private final static String search_type = "SEARCH_TYPE";
private Workout_Search_Activity_Listener listener;
protected static Workout_Search_Holder_Fragment newInstance(
String Search_String, String Search_Type) {
Workout_Search_Holder_Fragment f = new Workout_Search_Holder_Fragment();
Bundle args = new Bundle();
args.putString(search_string, Search_String);
args.putString(search_type, Search_Type);
f.setArguments(args);
return f;
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
Search_String = getArguments().getString(search_string, null);
Search_Type = getArguments().getString(search_type, null);
sendSearch();
}
public Workout_Search_Activity_Listener getListener() {
return listener;
}
public void setListener(Workout_Search_Activity_Listener listener) {
this.listener = listener;
}
private void sendSearch() {
DatabaseHelper.getInstance(getActivity()).getSearchResultsAsync(
Search_String, Search_Type, this);
}
static public <T> void executeAsyncTask(AsyncTask<T, ?, ?> task, T... params) {
if (Build.VERSION.SDK_INT > +Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
} else {
task.execute(params);
}
}
#Override
public void return_no_results_found() {
listener.NothingFound();
}
#Override
public void return_search_results(ArrayList<Search_Results_Holder> results) {
Log.v("workout search holder fragment", "results found in fragment, handing off to activity");
listener.results_found(results);
}
}
My issue is: When rotating the screen, my code crashes with a Null Pointer Exception on the SetListener for the Search_Hander in the Activity. If I change the Activity to a FragmentActivity and use the SupportFragmentManager....none of this is an issue...all works correctly with just those quick changes (something about the now-deprecated onRetainNonConfigurationInstance() being overridden by the SupportFragmentManager and it handles everything for you).
In trying to fix this, I kept it as an Activity, but put in SearchHolder = (Workout_Search_Holder_Fragment) getFragmentManager.findFragmentByTag("search_holder") as part of the else if the R.id.Content wasn't null and gave the Fragment a tag when I initially set it up if the R.id.Content WAS null. See the commented out code above. This worked, but created another issue where the onActivityCreated started again and launched my AsyncTask when I didn't want it to. I can't send or check variables in the savedInstanceState bundle to the fragment, as it is null due to the setRetainInstance(true). I know I'm doing something wrong, but can't get around it.
You are using
add(ANDROID.R.id.content, SearchHolder)
while adding your fragment, instead try
add(ContainerID, SearchHolder,"search_holder"), which makes sure that your fragment is added with a tag "search_holder"
then as you did in else part try getting that fragment object by calling
findFragmentByTag()
method
Hope this helps!!!
Answer is two fold:
I needed to manually attach the Fragments, this I knew.
I was executing my AsyncTask in the wrong area. This I didn't know.
It turns out that my AsyncTask was re-executing as OnActivityCreated is called again, even if saveInstanceState(true) is set. BUT, onCreate is not. By just changing the calling method to onCreate, i ensured that the AsyncTask was called only once, as it won't be called again upon Activity recreation. In the Activity, I still needed to reset my holders for both fragments upon testing if R.id.content was NOT null by grabbing those fragments that matched the tags...and this was part of the answer that Dinash gave.
if (getFragmentManager().findFragmentById(android.R.id.content) == null) {
SearchHolder = Workout_Search_Holder_Fragment.newInstance(
Search_String, Search_Type, false);
getFragmentManager().beginTransaction()
.add(android.R.id.content, SearchHolder, "searchholder")
.commit();
search_display_fragment = Workout_Search_Display_Fragment
.newInstance();
getFragmentManager()
.beginTransaction()
.add(android.R.id.content, search_display_fragment,
"displayholder").commit();
} else {
Log.v("activity", "non-null holder");
SearchHolder = (Workout_Search_Holder_Fragment) getFragmentManager()
.findFragmentByTag("searchholder");
search_display_fragment = (Workout_Search_Display_Fragment) getFragmentManager()
.findFragmentByTag("displayholder");
}
FragmentManager required me to test for the content being null in addition to reattaching the Fragments on my own, which is something that the FragmentSupportManager did NOT required. This also has the effect of requireing me to save the ArrayList that I am using for the ListView...again...FragmentSupportManager did all this on its own without prodding from me.
I still am curious as to why this behavior is different for the FragmentActivity as it seems to take care of ALL of this for me and required minimum amount of effort/code to work.

How to pass data from an activity to a fragment on android

I've an activity named ‘Home’; I’m redirected to this activity after a login. This activity implements two fragments (Header & footer) in this way.
View header = inflater.inflate(R.layout.fragment_header_fragement, null);
View footer = inflater.inflate(R.layout.fragment_footer, null);
mDrawerListView.addHeaderView(header);
mDrawerListView.addFooterView(footer);
And I want to display mainly into the header fragment implemented into the 'Home' activity some data transferred from the logging activity. The problem that I’ve searched a little bit, I’ve found that we can just add or replace a fragment in the main activity and not in another fragment. I'm tried to fund a solutions but not yet. This is my code :
#Override
protected void onPostExecute(final String res) {
//LoginTask = null;
//showProgress(false);
try {
jObj = new JSONObject(res);
} catch (JSONException e) {
e.printStackTrace();
}
try {
if (jObj.getString("code").equals("1")) {
infos = new Bundle();
infos.putString("ID",jObj.getString("ID"));
infos.putString("Name",jObj.getString("display_name"));
infos.putString("ImgUrl","http://unchained-network.com/uploads/profilpics/53f5c570b6ac2.png");
android.support.v4.app.FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
HeaderFragement hf = new HeaderFragement();
//Charger les infos dans l'activité
hf.setArguments(infos);
tr.add(R.id.frgmnt,hf);
tr.commit();
Intent myIntent = new Intent(getActivity(), HomesActivity.class);
//Lançer l'activité
startActivityForResult(myIntent, 0);
} else {
//password.setError(getString(R.string.error_incorrect_password));
//password.requestFocus();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Error !");
builder.setMessage("The information entered is incorrect.\nPlease try again!")
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
And this is how i extract the data into the fragment
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
name = (TextView) getView().findViewById(R.id.nameH);
img = (ImageView) getView().findViewById(R.id.imageViewHeader);
Bundle infos = getArguments();
name.setText(infos.getString("Name"));
img.setImageBitmap(getBitmapFromURL(infos.getString("ImgUrl")));
}
So any solution please. Thanks
Why don't you just pass data via intents? You have two activities Login and Home.
Intent myIntent = new Intent(getActivity(), HomesActivity.class);
//Lançer l'activité
startActivityForResult(myIntent, 0);
In the above code, you could simply add an extra information like this
myIntent.putExtra("name", "value");
And then you could retrieve this in Home Activity
String extra = i.getStringExtra("name");
And then pass this extra to your header fragment.
There isn't anything wrong with your code to pass a variable to a Fragment. I've just copied your code and it works fine.
Activity:
Bundle infos = new Bundle();
infos.putString("ID", "1");
infos.putString("Name", "Gonzo");
FragmentMain frag = new FragmentMain();
frag.setArguments(infos);
Fragment:
Bundle infos = getArguments();
Log.i(getTag(), infos.getString("Name"));
I'd suggest you put some logging into the onPostExecute, or step through in debug mode. Ensure your json is returning what you expect it to.
As a general suggestion, infos.getString accepts two params, the second is a default value which may be useful. Also avoid using a hard coded key, like "Name" instead use a public static string and reference this.
Good luck.

update listview from fragment within viewpager from main activity

COMPLETELY EDITED
Ok I will try to be more specific.
I'm developing a small app drawer. Therefore I need a way to let the user choose categories.
This is the main point of the question. If there is another better way than my approach please let me know.
So my try to implement this was to load all apps to an array list and save this to a service to make it available throuout the app. The loading of the apps is done by an asynctask.
MainActivity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
packageManager = getPackageManager();
if ((AppDrawerService.getApps()) == null) {
apps = new ArrayList<ApplistItem>();
loadAppsToService();
} else {
//if Service already holds the data
manageViews();
}
}
private void loadAppsToService() {
LoadApplications loadApps;
loadApps = new LoadApplications(this);
loadApps.setOnLoadApplicationsFinishedListener(this);
loadApps.execute();
}
private void manageViews() {
FragmentManager fragmentManager = getSupportFragmentManager();
myViewPager = (ViewPager) findViewById(R.id.view_pager);
myViewPager.setAdapter(new ViewPagerAdapter(fragmentManager, this));
}
#Override
public void OnLoadApplicationsComplete(ArrayList<ApplistItem> apps) {
manageViews();
}
LoadApplications:
#Override
protected void onPostExecute(Object o) {
AppDrawerService.setApps(apps);
listener.OnLoadApplicationsComplete(apps);
super.onPostExecute(o);
}
ViewPagerAdapter:
#Override
public Fragment getItem(int position) {
Fragment category = null;
category = new AppListFragment()
return category;
}
#Override
public int getCount() {
pages = 5;
return pages;
}
AppListFragment:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if ((AppDrawerService.getApps()) == null) {
apps = new ArrayList<ApplistItem>();
} else {
apps = AppDrawerService.getApps();
}
v = getActivity().findViewById(android.R.id.list);
myApplist = (ListView) v;
applistAdapter = new ApplistAdapter(context, 0, apps);
myApplist.setAdapter(applistAdapter);
myApplist.setOnItemClickListener(this);
}
The problem which I have with this code is, that it alway populates only one Fragment with the applist. On first start the first fragment is populated with the apps but if you swipe two fragments to right and then one back to left, this fragment (the second) is populated. The impression which I have is that the
return new ApplistFragment();
from the ViewPager does actually not create an independent instance of the fragment. (At least I think so)
What I want is to show an undefined amount of Fragments which can all show different categories of the same list. So if possible I want to load the list only once and I want to reuse the Fragment code for every fragment since I don't want to restrict the max amount of categories.
My idea was to use the ApplistAdapter to filter the unwanted apps for every category but I really don't know.
Help is really really appreciated since I have no idea how to go along.
Thanks in advance.
I would change how this works all together. A few suggestions of the top of my head:
Download the data in the Service instead of the Activity and
persist it somewhere for example in a database.
You can use an Intent to tell the Service what you want to download and when to do it.
You can also use an IntentService instead of a Service.
IntentServices already handle each Intent in a separate worker
thread so you don't need an AsyncTask or anything like that in the
IntentService to perform the downloading.
Each Fragment should load the data from the database in onResume(). You
can use local broadcasts to inform the Fragments when the data
changed while they are being displayed.
If you don't give us more information it will be difficult to give you very specific advice.

Why is onLoadFinished called again after fragment resumed?

I have a peculiar issue with Loaders. Currently I am unsure if this is a bug in my code or I misunderstand loaders.
The app
The issue arises with conversations (imagine something similar to Whatsapp).
The loaders I use are implemented based on the AsyncTaskLoader example. I am using the support library.
In OnCreate, I start a loader to retrieve cached messages.
When the CachedMessageLoader finishes, it starts a RefreshLoader to retrieve (online) the newest messages.
Each loader type as a distinct ID (say, offline:1 online:2)
This works very well, with the following exception.
Problem
When I open another fragment (and add the transaction to the backstack) and then use the Back-Key to go back to the conversationFragment, onLoadFinished is called again with both results from before.
This call happens before the fragment has had any chance to start a loader again...
This delivering of "old" results that I obtained before results in duplicated messages.
Question
Why are those results delivered again?
Do I use these loaders wrong?
Can I "invalidate" the results to ensure that I only get them delivered once or do I have to eliminate duplicates myself?
Stack trace of call
MyFragment.onLoadFinished(Loader, Result) line: 369
MyFragment.onLoadFinished(Loader, Object) line: 1
LoaderManagerImpl$LoaderInfo.callOnLoadFinished(Loader, Object) line: 427
LoaderManagerImpl$LoaderInfo.reportStart() line: 307
LoaderManagerImpl.doReportStart() line: 768
MyFragment(Fragment).performStart() line: 1511
FragmentManagerImpl.moveToState(Fragment, int, int, int, boolean) line: 957
FragmentManagerImpl.moveToState(int, int, int, boolean) line: 1104
BackStackRecord.popFromBackStack(boolean) line: 764
...
Update 1
The loaders mentioned here are initiated by the conversation fragment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
Bundle args = getArguments();
m_profileId = args.getString(ArgumentConstants.ARG_USERID);
m_adapter = new MessageAdapter(this);
if (savedInstanceState != null) {
restoreInstanceState(savedInstanceState);
}
if (m_adapter.isEmpty()) {
Bundle bundle = new Bundle();
bundle.putString(ArgumentConstants.ARG_USERID, m_profileId);
getLoaderManager().restartLoader(R.id.loader_message_initial, bundle, this);
} else {
// Omitted: Some arguments passed in Bundle
Bundle b = new Bundle().
getLoaderManager().restartLoader(R.id.loader_message_refresh, b, this);
}
}
#Override
public void onResume() {
super.onResume();
// Omitted: setting up UI state / initiating other loaders that work fine
}
#Override
public AbstractMessageLoader onCreateLoader(final int type, final Bundle bundle) {
final SherlockFragmentActivity context = getSherlockActivity();
context.setProgressBarIndeterminateVisibility(true);
switch (type) {
case R.id.loader_message_empty:
return new EmptyOnlineLoader(context, bundle);
case R.id.loader_message_initial:
return new InitialDBMessageLoader(context, bundle);
case R.id.loader_message_moreoldDB:
return new OlderMessageDBLoader(context, bundle);
case R.id.loader_message_moreoldOnline:
return new OlderMessageOnlineLoader(context, bundle);
case R.id.loader_message_send:
sendPreActions();
return new SendMessageLoader(context, bundle);
case R.id.loader_message_refresh:
return new RefreshMessageLoader(context, bundle);
default:
throw new UnsupportedOperationException("Unknown loader");
}
}
#Override
public void onLoadFinished(Loader<Holder<MessageResult>> loader, Holder<MessageResult> holder) {
if (getSherlockActivity() != null) {
getSherlockActivity().setProgressBarIndeterminateVisibility(false);
}
// Omitted: Error handling of result (can contain exception)
List<PrivateMessage> unreadMessages = res.getUnreadMessages();
switch (type) {
case R.id.loader_message_moreoldDB: {
// Omitted error handling (no data)
if (unreadMessages.isEmpty()) {
m_hasNoMoreCached = true;
// Launch an online loader
Bundle b = new Bundle();
// Arguments omitted
getLoaderManager().restartLoader(R.id.loader_message_moreoldOnline, b, ConversationFragment.this);
}
// Omitted: Inserting results into adapter
}
case R.id.loader_message_empty: { // Online load when nothing in DB
// Omitted: error/result handling handling
break;
}
case R.id.loader_message_initial: { // Latest from DB, when opening
// Omitted: Error/result handling
// If we found nothing, request online
if (unreadMessages.isEmpty()) {
Bundle b = new Bundle();
// Omitted: arguments
getLoaderManager().restartLoader(R.id.loader_message_empty, b, this);
} else {
// Just get new stuff
Bundle b = new Bundle();
// Omitted: Arguments
getLoaderManager().restartLoader(R.id.loader_message_refresh, b, this);
}
break;
}
// Omitted: Loaders that do not start other loaders, but only add returned data to the adapter
default:
throw new IllegalArgumentException("Unknown loader type " + type);
}
// Omitted: Refreshing UI elements
}
#Override
public void onLoaderReset(Loader<Holder<MessageResult>> arg0) { }
Update 2
My MainActivity (which ultimatively hosts all fragments) subclasses SherlockFragmentActivity and basically launches fragments like this:
Fragment f = new ConversationFragment(); // Setup omitted
f.setRetainInstance(false);
// Omitted: Code related to navigation drawer
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fragment_container_frame, f).commit();
The conversation fragment starts the "display profile" fragment like this:
DisplayProfileFragment f = new DisplayProfileFragment();
// Arguments omitted
FragmentManager manager = getSherlockActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container_frame, f).addToBackStack(null).commit();
There are other similar questions such as Android: LoaderCallbacks.OnLoadFinished called twice However the behavior of the loader manager hooks are what they are. You can either destroy the loader after getting the first set of results
public abstract void destroyLoader (int id)
or you can handle the onLoaderReset and tie your UI data more closely to the loader data
public abstract void onLoaderReset (Loader<D> loader)
Called when a previously created loader is being reset, and thus
making its data unavailable. The application should at this point
remove any references it has to the Loader's data.
Personally, I would use a ContentProvider and a CursorLoader for this (each row of data would need to have a unique _ID but for messages that should not be a problem).
use following this in onResume()
#Override
public void onResume() {
super.onResume();
getLoaderManager().initLoader(0, null, this);
}
i solved my problem from this
this is same type Q

How to get data from DialogFragment to a Fragment?

Imagine, I have FragmentA from which I startDialogFragment (there are EditText in box). How to can I get back the value from the EditText to FragmentA? I try to make something like this, and this but I was not successful.
The Fragment.onActivityResult() method is useful in this situation. It takes getTargetRequestCode(), which is a code you set up between fragments so they can be identified. In addition, it takes a request code, normally just 0 if the code worked well, and then an Intent, which you can attach a string too, like so
Intent intent = new Intent();
intent.putExtra("STRING_RESULT", str);
Also, the setTargetFragment(Fragment, requestCode) should be used in the fragment that the result is being sent from to identify it. Overall, you would have code in the requesting fragment that looks like this:
FragmentManager fm = getActivity().getSupportFragmentManager();
DialogFragment dialogFragment = new DialogFragment();
dialogFragment.setTargetFragment(this, REQUEST_CODE);
dialogFragment.show();
The class to send data (the DialogFragment) would use this Fragment we just defined to send the data:
private void sendResult(int REQUEST_CODE) {
Intent intent = new Intent();
intent.putStringExtra(EDIT_TEXT_BUNDLE_KEY, editTextString);
getTargetFragment().onActivityResult(
getTargetRequestCode(), REQUEST_CODE, intent);
}
To receive the data, we use this type of class in the Fragment which initially started the DialogFragment:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Make sure fragment codes match up
if (requestCode == DialogFragment.REQUEST_CODE) {
String editTextString = data.getStringExtra(
DialogFragment.EDIT_TEXT_BUNDLE_KEY);
At this point, you have the string from your EditText from the DialogFragment in the parent fragment. Just use the sendResult(int) method in your TextChangeListener() anonymous class so that the text is sent when you need it.
Assume a situation that you are uploading some file to server , on clicking of upload button a dialog should open,prompting for title and optional tag.And the dialog itself containing 2 buttons say cancel and continue.
make the UI as you wish by using layout xml file.
then create one class that extending DialogFragment. inflate the layout and initialize views inside onCreateView() method.
Inside that class create one interface
public interface uploadDialogInterface
{
public void senddata(String title, String tag);
}
uploadDialogInterface interfaceObj;
String title="";
String tag=" ";
And the important thing is you need to override onAttach() method
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context=context;
interfaceObj= (uploadDialogInterface) getTargetFragment();
}
And in the on Button click call the interface method like
#Override
public void onClick(View v) {
int id=v.getId();
if(id== R.id.vB_fud_cancel)
{
dismiss();
}
else if(id== R.id.vB_fud_upload)
{
title=mVideotitle.getText().toString();
tag=mOptionaltag.getText().toString();
if(mVideotitle.getText().toString().isEmpty()) {
Snackbar.make(mVideotitle,"Please enter the video title", Snackbar.LENGTH_SHORT).show();
}else
{
interfaceObj.senddata(title,tag);
dismiss();
}
}
}
And inside the Fragment or activity from which you are launching the dialog should contain setTargetFragment attribute.
private void callUploadDialog()
{
UploadDialogFragment fragment = new UploadDialogFragment();
fragment.setTargetFragment(this, 0);
FragmentManager manager = getFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_in);
fragment.show(ft, "UploadDialogFragment");
fragment.setCancelable(false);
}
And finally you should implement the interface (that was declared inside the dialog fragment) and override the method
#Override
public void senddata(String title,String optionaltag) {
this.videoTitle=title;
this.optionalTag=optionaltag;
}
I think this post will be helpful for those who are using dialog fragment for the first time . I was struggled to find the solution . And hopefully this will solve someone's problem in the future.
(Sorry for the language)
One of the better and simpler ways to do this is using Android ViewModel.
This helps in easier sharing of data, without the need of sending any data across fragments. You could do this not only for DialogFragments, but also for normal Fragments.
Source: https://developer.android.com/topic/libraries/architecture/viewmodel
Here is what I did
My ViewModel looks as below
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
public class PlayerViewModel extends ViewModel {
private final MutableLiveData<Player> selectedPlayer = new MutableLiveData<>();
public LiveData<Player> getSelectedPlayer() {
return selectedPlayer;
}
public void selectPlayer(Player player) {
selectedPlayer.setValue(player);
}
}
In the Fragment where I select a Player, I use the following code in the onCreate method to bind the ViewModel
playerViewModel = ViewModelProviders.of(getActivity()).get(PlayerViewModel.class);
When a specific Player is selected, use the following (You can use an ArrayAdapter, DialogFragment's selector or anything you want to display list of players)
playerViewModel = ViewModelProviders.of(getActivity()).get(PlayerViewModel.class);
And finally, in the fragment where you need to show the Player information, do the following in the onCreate method
PlayerViewModel model = ViewModelProviders.of(getActivity()).get(PlayerViewModel.class);
model.getSelectedPlayer().observe(this, new Observer<Player>() {
#Override
public void onChanged(#Nullable Player selPlayer) {
if (selPlayer != null)
player = selPlayer;
populateData();
}
});
You need to send the data from the dialog back to the activity via a callback method, then have the activity give that data back to the fragment you want it to go to. Just a quick example:
public void datFromDialog(String data){
MyFragment mf = (MyFragment)getFragmentManager().findFragmentById(r.id.frag);
mf.iWantNewData(data);
}
What you want, according to Android Developers...
This method ensures that the calling fragment implements the onChangeListener of the dialog.
FragmentA (calling fragment):
MyDialogFragment f = new MyDialogFragment();
Bundle args = new Bundle();
args.putString("data", data);
f.setArguments(args);
// Set the calling fragment for this dialog.
f.setTargetFragment(FragmentA.this, 0);
f.show(getActivity().getSupportFragmentManager(), "MyDialogFragment");
MyDialogFragment:
import android.support.v4.app.DialogFragment;
public class MyDialogFragment extends DialogFragment {
public OnChangeListener onChangeListener;
interface OnChangeListener{
void onChange(Data data);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get the calling fragment and ensure that it implements onChangeListener.
try {
onChangeListener = (OnChangeListener) getTargetFragment();
} catch (ClassCastException e) {
throw new ClassCastException(
"The calling Fragment must implement MyDialogFragment.onChangeListener");
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
.....
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// Send the data to the calling fragment.
onChangeListener.onChange(data);
}
});
.....
}
}
dialogFragment.setTargetFragment is deprecated, see : doc
Instead of using a target fragment to pass results, the fragment requesting a result should use FragmentManager.setFragmentResultListener(String, LifecycleOwner, FragmentResultListener) to register a FragmentResultListener with a requestKey using its parent fragment manager. The fragment delivering a result should then call FragmentManager.setFragmentResult(String, Bundle) using the same requestKey. Consider using setArguments to pass the requestKey if you need to support dynamic request keys.
Here is a simple implementation :
Call from host Fragment
val dialog = MockDialog.newInstance(
"requestKey")
dialog.show(
childFragmentManager, MockDialog.TAG
)
In MockDialog (which extends DialogFragment):
dialog.setPositiveButton(R.string.dialog_yes) { _, _ ->
parentFragmentManager.setFragmentResult(
arguments!!.getString(DIALOG_REQUEST_PARAM)!!,// which is "requestKey"
//add data to bundle
bundleOf("result" to "any data")
)
}
Get result on host Fragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
childFragmentManager.setFragmentResultListener(
"requestKey", this
) { requestKey, result ->
// you data here
val data = result.getString("result", null)
}
}
Base-line : you need to pass your "requestKey" and pass it back to host-fragment
Good luck,'.

Categories

Resources