Android - Interfaces and Inner Class - java

I'm new to Android programming and am trying to figure out how to go about this. I have a fragment that hosts inner tabs, one of them being a ListFragment. On the tabhost fragment I have a button that calls a DialogFragment. When "Yes" is clicked on that DialogFragment I need to refresh that ListFragment if it's currently active in order to show the item added onto the list.
What is the best way to go about this? I am thinking I should put an interface on the DialogFragment and then implement the listener on the Activity which would then call the refresh in the ListFragment. I would need to be able to pull the ListFragment's tag in order to determine if it's active, however and not sure how to do that.
I just started to learn programming a few months ago and this is my first post on this site. I searched for this answer and couldn't find anything. I apologize if my methods or formatting are wrong. Any tips are appreciated, thanks.
TabFragment:
public class Items extends Fragment implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener, View.OnClickListener {
MyPageAdapter pageAdapter;
private ViewPager mViewPager;
private TabHost mTabHost;
static final String ARG_ID = "id";
static final String name = "name";
long id;
String itemName;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.tab_test);
Bundle args = getArguments();
long id = args.getLong(ARG_ID);
String itemName = args.getString(name);
View v = inflater.inflate(R.layout.item_tab, container, false);
mViewPager = (ViewPager) v.findViewById(R.id.pager);
// Tab Initialization
//initialiseTabHost
mTabHost = (TabHost) v.findViewById(android.R.id.tabhost);
mTabHost.setup();
// TODO Put here your Tabs
List<Fragment> fragments = getFragments();
FragmentActivity context = getActivity();
this.AddTab(context, this.mTabHost, this.mTabHost.newTabSpec("ItemList").setIndicator("ItemList"));
mTabHost.setOnTabChangedListener(this);
// Fragments and ViewPager Initialization
pageAdapter = new MyPageAdapter(getChildFragmentManager(), fragments);
mViewPager.setAdapter(pageAdapter);
mViewPager.setOnPageChangeListener(this);
if (savedInstanceState == null) {
}else {
int pos = savedInstanceState.getInt("tab");
mTabHost.setCurrentTab(pos);
}
Button addItemButton = (Button) v.findViewById(R.id.addItem);
addItemButton.setOnClickListener(this);
return v;
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.addItem:
DialogFragment addItem = new AddItemDialogFragment();
Bundle itemArgs = getArguments();
addItem.setArguments(itemArgs);
addItem.show(getChildFragmentManager(), "addItem");
Toast.makeText(getActivity(), "Adding Item", Toast.LENGTH_LONG).show();
break;
}
}
// Method to add a TabHost
private static void AddTab(FragmentActivity activity, TabHost tabHost, FragmentTabHost.TabSpec tabSpec) {
tabSpec.setContent(new MyTabFactory(activity));
tabHost.addTab(tabSpec);
}
// Manages the Tab changes, synchronizing it with Pages
public void onTabChanged(String tag) {
int pos = this.mTabHost.getCurrentTab();
this.mViewPager.setCurrentItem(pos);
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
// Manages the Page changes, synchronizing it with Tabs
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
int pos = this.mViewPager.getCurrentItem();
this.mTabHost.setCurrentTab(pos);
}
#Override
public void onPageSelected(int arg0) {
}
private List<Fragment> getFragments(){
List<Fragment> fList = new ArrayList<Fragment>();
// TODO Put here your Fragments
Bundle args = getArguments();
long id = args.getLong("val");
ItemList f1 = ItemList.newinstance(id);
fList.add(f1);
return fList;
}
public class MyPageAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragments;
public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
}
}
ListFragment within Tab:
public class ItemList extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
String DATABASE_TABLE;
String Query;
String Order;
String name;
MainActivity home;
View view;
public static MyListAdapter mAdapter;
private static Cursor c;
static ItemList newinstance(long rowId) {
ItemList itemList = new ItemList();
// Supply val input as an argument.
Bundle args = new Bundle();
args.putLong("val", rowId);
//args.putString("name", itemName);
itemList.setArguments(args);
return itemList;
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Bundle args = getArguments();
int itemId= (int) args.getLong("val");
mAdapter = new MyListAdapter(getActivity(), R.layout.list_row, c, from, to);
setListAdapter(mAdapter);
setListShown(false);
getLoaderManager().initLoader(itemId, null, this);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// View progressBar = getView().findViewById(R.id.progressbar_loading);
// progressBar.setVisibility(View.VISIBLE);
return new RawCursorLoader(getActivity(), Query + Order);
}
// Called when a previously created loader has finished loading
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
//View progressBar = getView().findViewById(R.id.progressbar_loading);
// progressBar.setVisibility(View.GONE);
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
// Called when a previously created loader is reset, making the data unavailable
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
}
Dialog:
public class AddItemDialogFragment extends DialogFragment {
UpdateItemListener mListener;
public interface UpdateItemsListener {
public void onItemAdded();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mListener = (UpdateItemListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement UpdateItemListener");
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Add " + itemName + "?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
postItem(ItemId);
mListener.onItemAdded();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// Create the AlertDialog object and return it
return builder.create();
}
}

I was able to figure it out, my brain just got stuck for a while.
The first problem was that I was using FragmentStatePagerAdapter which does not set tags when instantiating the fragments. I set it to this since FragmentPagerAdapter was being buggy and another thread recommending extending that class instead. I was able to get it working with FragmentPagerAdapter which sets a tag. I then call getTag() during the onAttach() method of the ItemList fragment and set the variable on the activity. I then have an interface on the AddItemDialogFragment when the item is added and a listener on the activity. The listener then calls:
ItemList itemList= (ItemList)
getSupportFragmentManager().findFragmentByTag("Items")
.getChildFragmentManager().findFragmentByTag("itemListTag");
if(itemList != null) {
itemList.listChange();
}

Related

Get different data via Retrofit for the Fragment in FragmentPagerAdapter for the several tablayout tabs

Conditions:
I have an Activity and I'm creating PagerAdapter extends FragmentPagerAdapter with one Fragment class. TabLayout includes 10 tabs. I have TabLayout.addOnTabSelectedListener and there in onTabSelected() I am updating some custom variable in app's SharedPreferences which depends on current tab name and tab index. Tab's names are not static - there are loading from server too.
My task:
With this value I should make a retrofit2 Call to my server and load values in adapter with recyclerview for my Fragment.
My problem:
I am loading the same data for the different tabs, which should contatin different data! I am understand that this happens because in all cases I have the same SharedPreferences variable's value so I am making retrofit Call with same conditions.
setOffscreenPageLimit() can't be setted to 0 - default is 1 according documentation:
https://developer.android.com/reference/kotlin/androidx/viewpager/widget/ViewPager#setoffscreenpagelimit;
setUserVisibleHint() on SO I've found this like some variant for solving my problem, but it can't be overrided because is deprecated now.
Solution:
I think I should save previous and next tab name in some way and then I can make different parallel retrofit Calls with different conditions. When tab selected I can get current, previous and next tab name. But how, where and when I should make two more retrofit calls?
UPD 08.02.2022: Still thinking about this problem.
Code:
SomeActivity.java
public class SomeActivity extends BaseActivity {
private String TAG="SomeActivity";
private Context context;
private TabLayout tabs;
private ViewPager viewpager;
private ProgressBar PBLoading;
private PagerAdapter_SomePAdapter adapter;
List<String> names = new ArrayList<>();
private String[] tabTitles;
public String previousTabName;
public String nextTabName;
public int counterForPreviousTabName;
public int counterForNextTabName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
context = getApplicationContext();
String categories = AppPreferences.getCategories(context);
tabTitles = categories.split(";");
tabs = findViewById(R.id.tabs);
viewpager = findViewById(R.id.viewpager);
PBLoading = findViewById(R.id.PBLoading);
tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
#Override
public void onTabSelected(TabLayout.Tab tab) {
Log.d(TAG, "onTabSelected");
AppPreferences.setCurrentValueForRetrofit(getApplicationContext(), tab.getPosition());
counterForPreviousTabName = tmpTab.getPosition() - 1;
counterForNextTabName = tmpTab.getPosition() + 1;
//here I can get tab names.
}
});
adapter = new PagerAdapter_SomePAdapter(getSupportFragmentManager(), SomeActivity.this);
viewpager.setAdapter(adapter);
adapter.updateTitleData(tabTitles);
tabs.setupWithViewPager(viewpager);
adapter.notifyDataSetChanged();
}
}
PagerAdapter_SomePAdapter.java:
public class PagerAdapter_SomePAdapter extends FragmentPagerAdapter {
int PAGE_COUNT = 2;
private String[] tabTitles = new String[] { "", ""};
private Context mContext;
Fragment_fragment fragment_fragment;
String TAG = "PagerAdapter_SomePAdapter";
public PagerAdapter_SomePAdapter(FragmentManager fm, Context context) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
mContext = context;
fragment_fragment = new Fragment_fragment();
}
#Override public int getCount() {
return PAGE_COUNT;
}
#Override public Fragment getItem(int position) {
return fragment_fragment.newInstance(position + 1, tabTitles[position]);
}
#Override public CharSequence getPageTitle(int position) {
return tabTitles[position];
}
public void updateTitleData(String[] tabTitlesNew) {
tabTitles = tabTitlesNew;
PAGE_COUNT = tabTitlesNew.length;
notifyDataSetChanged();
}
}
Fragment_fragment.java:
public class Fragment_fragment extends Fragment {
private String TAG = "Fragment_fragment";
private static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
private View rootView;
private RecyclerView RV;
private Adapter_Items adapter;
public static Fragment_fragment newInstance(int page, String tabName) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
Fragment_fragment fragment = new Fragment_fragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mPage = getArguments().getInt(ARG_PAGE);
}
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
mContext=context;
}
#SuppressLint("ClickableViewAccessibility")
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.content_list, container, false);
RV = rootView.findViewById(R.id.recycler_view);
new loadDataAS().execute();
return rootView;
}
private void loadData(){
//retrofit call code;
}
class loadDataAS extends AsyncTask<Void, Void, Void> {
final ProgressDialog progress = new ProgressDialog(mContext);
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... arg0) {
loadData();
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
adapter = new Adapter_Items(arguments, arguments2, arguments3);
RV.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
}
Adapter_Items:
public class Adapter_Items extends RecyclerView.Adapter<Adapter_Items.ViewHolder>{
private LayoutInflater mInflater;
private Context mContext;
private static String TAG = "Adapter_Items";
public Adapter_Items(List<Integer>arguments, List<String>arguments2, List<String>arguments3){
//initializing arguments
}
// inflates the row layout from xml when needed
#NotNull
#Override
public ViewHolder onCreateViewHolder(#NotNull ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recycle_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
//setting view's information
}
#Override
public int getItemCount() {
return arguments.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
}
}
}
First thing,
fragment should contain different data
Saving tab name after tab selected will not work. You should remove this
AppPreferences.setCurrentValueForRetrofit(getApplicationContext(),
tab.getPosition());
You should call retrofit code inside the fragment with the tabName (Not from sharedPref) to get data for that tab.
For this, make the following changes,
You are already passing the tabName to fragment instance
newInstance(int page, String tabName)
use that tabName and pass to your AsyncTask -> loadData() as
loadData(tabName);
Then you can use that in your retrofit call and get the data.
Note:
setOffscreenPageLimit()
This method will load fragments just before they are completely visible. It would be useful and a great user experience to display data directly rather than fetching only when user selects the tab.

How to handle configuration change for fragment with view model

I want to make my fragment wont re-load the data when configuration change with view model.
So, i try to make an App about gitHub User.
My Main Activity contain Search view to search user and show the result with recyclerview.
My Detail Activity is showing the detail of user when one of user in the list clicked, and in my detail activity i use Tab Layout and view pager to show user's followers and following.
My ViewModel for activity works well and can keep my data when orientation change.
But when i do the same to my fragment, my fragment keep re-loading new data when orientation change.
Here my fragment
i use View Model to request data
public class FollowingFragment extends Fragment {
public static final String KEY_FOLLOWING = "key_following";
private RecyclerView recyclerView;
private FollowingViewModel followingViewModel;
private FollowingAdapter followingAdapter;
ShimmerFrameLayout shimmerFrameLayout;
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
ArrayList<FollowingResponse> followingResponse = new ArrayList<>();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_following, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.rv_following);
shimmerFrameLayout = view.findViewById(R.id.shimmer_frame_layout2);
followingViewModel = ViewModelProviders.of(this).get(FollowingViewModel.class);
followingAdapter = new FollowingAdapter();
followingAdapter.setOnItemClickCallback(new FollowingAdapter.OnItemClickCallback() {
#Override
public void onItemClicked(FollowingResponse followingResponse) {
showSelectedItem(followingResponse);
}
});
//this is what i've tried and worked, but my fragment result with double or triple the data.
if (savedInstanceState == null){
}
else {
followingResponse = savedInstanceState.getParcelableArrayList(KEY_FOLLOWING);
}
showRecyclerView();
getData();
}
private void getData() {
followingViewModel.setDataFollowing(DetailUserActivity.clickedUser);
followingViewModel.getDataFollowing().observe(getViewLifecycleOwner(), new Observer<ArrayList<FollowingResponse>>() {
#Override
public void onChanged(ArrayList<FollowingResponse> followingResponses) {
shimmerFrameLayout.setVisibility(View.GONE);
shimmerFrameLayout.stopShimmer();
followingAdapter.setData(followingResponse);
followingResponse.addAll(followingResponses);
recyclerView.setAdapter(followingAdapter);
followingAdapter.notifyDataSetChanged();
}
});
}
private void showRecyclerView() {
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
}
#Override
public void onResume() {
super.onResume();
followingViewModel.setDataFollowing(DetailUserActivity.clickedUser);
}
private void showSelectedItem(FollowingResponse item) {
Intent intent = new Intent(getContext(), DetailUserActivity.class);
intent.putExtra("EXTRA_DATA", item.getLogin());
startActivity(intent);
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
outState.putParcelableArrayList(KEY_FOLLOWING, followingResponse);
super.onSaveInstanceState(outState);
}
}
Detail Activity
public class DetailUserActivity extends AppCompatActivity {
private TextView tvName, tvNickname, tvLocation, tvCompany, tvEmail, tvWebsite;
private TextView tvCountFollowers, tvCountRepository, tvCountFollowing;
private ImageView imgProfile;
ProgressBar progressBar;
public static String clickedUser;
UserViewModel userViewModel;
private ViewPager viewPager;
TabLayout tabLayout;
private final String EXTRA_DATA = "EXTRA_DATA";
private static final String EXTRA_FOLLOW = "extra_follow";
// Fragment followingFragment;
// FragmentTransaction transaction;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detail_user_activity);
// i've tried this, but when the orientation change, my detail activity force close and throw me to my main activity.
// if (savedInstanceState != null){
// followingFragment = getSupportFragmentManager().getFragment(savedInstanceState, KEY_FOLLOWING);
// } else {
// followingFragment = new FollowingFragment();
//
// getSupportFragmentManager().beginTransaction().replace(R.id.tv_view_pager, followingFragment).commit();
// }
tvName = findViewById(R.id.tv_item_name);
tvNickname = findViewById(R.id.tv_item_nickname);
tvLocation = findViewById(R.id.tv_item_location);
tvCompany = findViewById(R.id.tv_item_company);
tvEmail = findViewById(R.id.tv_item_email);
tvWebsite = findViewById(R.id.tv_item_website);
imgProfile = findViewById(R.id.img_item_photo);
tvCountFollowers = findViewById(R.id.tv_count_follower);
tvCountRepository = findViewById(R.id.tv_count_repo);
tvCountFollowing = findViewById(R.id.tv_count_following);
progressBar = findViewById(R.id.progressbar2);
setForUserData();
setForTabLayout();
}
private void setForUserData(){
Intent detailIntent = getIntent();
clickedUser = detailIntent.getStringExtra("EXTRA_DATA");
userViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(UserViewModel.class);
userViewModel.setUserUVM(clickedUser);
userViewModel.getUserUVM().observe(this, new Observer<UserResponse>() {
#Override
public void onChanged(UserResponse userResponse) {
progressBar.setVisibility(View.GONE);
Glide.with(getApplicationContext())
.load(userResponse.getAvatarUrl())
.into(imgProfile);
tvName.setText(userResponse.getLogin());
tvLocation.setText(userResponse.getLocation());
tvNickname.setText(userResponse.getName());
tvCompany.setText(userResponse.getCompany());
tvEmail.setText(userResponse.getEmail());
tvWebsite.setText(userResponse.getBlog());
tvCountRepository.setText(String.valueOf(userResponse.getPublicRepos()));
tvCountFollowers.setText(String.valueOf(userResponse.getFollowers()));
tvCountFollowing.setText(String.valueOf(userResponse.getFollowing()));
}
});
}
private void setForTabLayout(){
tabLayout = findViewById(R.id.tv_tab_layout);
viewPager = findViewById(R.id.tv_view_pager);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount()));
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setTabMode(TabLayout.MODE_FIXED);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
int totalTab;
#SuppressWarnings("deprecation")
ViewPagerAdapter(FragmentManager fragmentManager, int totalTabs) {
super(fragmentManager);
this.totalTab = totalTabs;
}
#SuppressWarnings("ConstantConditions")
#NonNull
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new FollowersFragment();
case 1:
return new FollowingFragment();
default:
return null;
}
}
#Override
public int getCount() {
return totalTab;
}
}
// #Override
// protected void onSaveInstanceState(#NonNull Bundle outState) {
// getSupportFragmentManager().putFragment(outState, KEY_FOLLOWING, followingFragment );
// super.onSaveInstanceState(outState);
// }
}
im trying to solve my FollowingFragment first, then ill do the same on my FollowerFragment.
im really new in programming, and have no one to ask.. can someone help me to solve my problem with keep data in fragment when orientation change?
followingViewModel = ViewModelProviders.of(this).get(FollowingViewModel.class);
In Fragment insted of writing this code in onViewCreated() write it inside OnCreate()
onViewCreated() will get called everytime the orientation changes so it will create new instance of view model everytime the orientation changes making viewmodel to reload the data
If you need to load data only once for the FollowingViewModel a good place to do it should be inside the constructor of the view model in this way you won't need to worry about the orientation change of the fragment.

How to create an custom alertDialog from a tabbed fragment?

Problem:
I'm trying to open a custom dialog after pressing a button in my tabbed fragment.
It's seems like my MainActivity activity is sent to the dialog while i want my tabbed fragment(GroupFragment) to be sent so i can change the editText(for now) in this fragment.
Code:
public class GroupFragment extends Fragment implements AddGroupDialog.AddGroupDialogListener {
private Button addGroupButton;
private TextView textViewNoGroups;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_group, container, false);
addGroupButton = view.findViewById(R.id.addGroupButton);
textViewNoGroups = view.findViewById(R.id.textViewNoGroups);
addGroupButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
addNewGroupDialog();
}
});
scaleAnimation(addGroupButton);
return view;
}
private void scaleAnimation(View v){
Animator scale = ObjectAnimator.ofPropertyValuesHolder(v,
PropertyValuesHolder.ofFloat(View.SCALE_X, 0, 1.2f, 1),
PropertyValuesHolder.ofFloat(View.SCALE_Y, 0, 1.2f, 1)
);
scale.setDuration(600);
scale.start();
}
private void addNewGroupDialog(){
AddGroupDialog dialog = new AddGroupDialog();
assert getFragmentManager() != null;
dialog.show(getFragmentManager(), "add new group dialog");
}
#Override
public void applyString(String groupName) {
textViewNoGroups.setText(groupName);
}
}
public class AddGroupDialog extends AppCompatDialogFragment {
private EditText editTextGroupName;
private AddGroupDialogListener listener;
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.layout_add_new_group_dialog, null);
builder.setView(view)
.setTitle("")
.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
String groupName = editTextGroupName.getText().toString();
listener.applyString(groupName);
}
});
editTextGroupName = view.findViewById(R.id.editTextGroupName);
return builder.create();
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
listener = (AddGroupDialogListener) context;
} catch (ClassCastException e){
throw new ClassCastException(context.toString() +
"Must implement AddGroupDialogListener");
}
}
public interface AddGroupDialogListener{
void applyString(String groupName);
}
}
public class MainActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
}
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
#StringRes
private static final int[] TAB_TITLES = new int[]{R.string.tab_text_1, R.string.tab_text_2, R.string.tab_text_3};
private final Context mContext;
public SectionsPagerAdapter(Context context, FragmentManager fm) {
super(fm);
mContext = context;
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position){
case 0:
fragment = new GroupFragment();
break;
case 1:
fragment = new AttendanceFragment();
break;
case 2:
fragment = new StatisticsFragment();
break;
}
return fragment;
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getString(TAB_TITLES[position]);
}
#Override
public int getCount() {
return TAB_TITLES.length;
}
}
Error:
java.lang.ClassCastException: com.example.attendencetaker.MainActivity#26602bcMust implement AddGroupDialogListener
I hope my explanation of the problem is clear. Thank you!
Make the activity implement your interfaces and so on and then pass all data to the fragment with an method on the fragment. You will need an reference to actual Fragment that is displayed.
In your fragment add a method similar to this:
public void updateData(String data) {
editText.setText(data);
}
And in the override method of the interface in your activity to this:
#Override
public void update(String data) {
fragment.updateData(data);
}
Update
Use this Adapter instead:
public class SectionTabAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> fragments = new ArrayList<>();
private ArrayList<String> titles = new ArrayList<>();
public SectionTabAdapter(#NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
public void addFragment(Fragment fragment, String title) {
fragments.add(fragment);
titles.add(title);
}
#NonNull
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
}
In your activity then do this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SectionsPagerAdapter sectionTabAdapter = new SectionTabAdapter(this, getSupportFragmentManager());
Fragment groupFragment = new GroupFragment();
Fragment attendanceFragment = new AttendanceFragment();
Fragment statisticsFragment = new StatisticsFragment();
sectionTabAdapter.addFragment(groupFragment, context.getString(R.string.title_1);
sectionTabAdapter.addFragment(attendanceFragment, context.getString(R.string.title_2);
sectionTabAdapter.addFragment(statisticsFragment, context.getString(R.string.title_3);
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionTabAdapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
}

How can I pass a ArrayList<String> created in a fragment to the main activity

I have a drawer menu with 3 items (Restaurant, Movie, Food). They are basically 3 to do lists. Each list has its on fragment and in the view there is a way to add items to the list.
The restaurant list, the movie list ad the groceries list.
I need to be able to share that list (via any share-channel) by clicking the share icon that is in the toolbar.
This is my fragment where i populate the items list that i want to use when i click the share icon that is on the toolbar. Basically i need to pass "items" to the MainActivity and use it.
package com.example.mylists;
import android.content.Intent;
public class FoodFragment extends Fragment {
public FoodFragment() {
// Required empty public constructor
}
private ArrayList<String> items;
private ArrayAdapter<String> itemsAdapter;
private ListView lvItems;
private static final String TAG = "FoodFragment";
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
Log.i(TAG, "in on onCreateView ");
View view = inflater.inflate(R.layout.fragment_food, container, false);
lvItems = (ListView) view.findViewById(R.id.lvItems);
items = new ArrayList<String>();
readItems();
itemsAdapter = new ArrayAdapter<String>(view.getContext(),android.R.layout.simple_list_item_1, items);
lvItems.setAdapter(itemsAdapter);
if (items.isEmpty())
items.add("Dummy Item");
final EditText newTask = (EditText) view.findViewById(R.id.etNewItem);
Button btnAdd = (Button) view.findViewById(R.id.btnAddItem);
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String itemText = newTask.getText().toString();
itemsAdapter.add(itemText);
newTask.setText("");
writeItems();
Log.i(TAG, "in on send data ");
}
});
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
Log.i(TAG, "in on onViewCreated ");
super.onViewCreated(view, savedInstanceState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.i(TAG, "in on onActivityCreated ");
super.onActivityCreated(savedInstanceState);
OnItemLongClickListener listener = new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int position, long id) {
//Log.i(TAG, "in on onItemLongClick ");
//Toast.makeText( getActivity().getBaseContext() , "Long Clicked " , Toast.LENGTH_SHORT).show();
items.remove(position);
itemsAdapter.notifyDataSetChanged();
writeItems();
//return true;
return false;
}
};
lvItems.setOnItemLongClickListener(listener);
}
private void readItems() {
File filesDir = getContext().getFilesDir();
File todoFile = new File(filesDir, "todo.txt");
try {
items = new ArrayList<String>(FileUtils.readLines(todoFile));
} catch (IOException e) {
items = new ArrayList<String>();
}
}
private void writeItems() {
File filesDir = getContext().getFilesDir();
File todoFile = new File(filesDir, "todo.txt");
try {
FileUtils.writeLines(todoFile, items);
} catch (IOException e) {
e.printStackTrace();
}
}
}
You should declare ArrayList<String> items in your MainActivity. Then create a public setter method for items.
public void setItems(ArrayList<String> items){
this.items = items;
}
Then you should call setItems() method in your fragment.
((MainActivity) getActivity()).setItems(items);
Interface for Callback
interface MyInterface {
void setItems(ArrayList<String> items);
}
In your activity:
class MyActivity {
ArrayList<String> items;
MyInterface itemsCallback = new MyInterface(){
#Override
void setItems(ArrayList<String> items){
this.items = items;
}
}
myFragment.setItemsCallback(itemsCallback);
}
And then in fragment
class MyFragment {
private MyInterface itemsCallback;
public void setItemsCallback(MyInterface itemsCallback){
this.itemsCallback = itemsCallback;
}
private void readItems(){
...
itemsCallback.setItems(items);
}
}
I guess it's better then public setter in MainActivity. Because, you know, it's like more SOLID code. In this case your Fragment doesnt hold reference to Activity and doesnt even know about it existence.
1.Define an interface , 2.Let your Activity implements the interface 3.use (interface)getActivity() to cast your Activity to that interface,then you can call the method in the interface to send your ArrayList "items"

How do I communicate between ListFragment and DialogFragment where the DialogFragment is invoked via the options menu of list fragment?

I have a class "ListFrag" implementing ListFragment and within that class, I have an nested static DialogFragment class "SortDialogFragment" which is invoked through one of the options in the options menu. The purpose of the dialog is to provide the user with the set of options on how they want the list to be sorted. I am able to display the dialog just fine and the user is able is to choose the option. However, I am not sure how I would refresh the listview without having access to listview's "notifyDataSetChanged" method from the DialogFragment class, once the user has made their choice. I do know about communicating between two fragments through the underlying activity using an interface but I am not clear about how I would apply that technique in this particular case. I am not sure where I would define my interface and who would implement that interface. Please bear with me since I am a newbie Android Developer and also to this site, in terms of posting questing. Below is my modified code, only including relevant code:
public class ListFrag extends SherlockListFragment {
private ListFragListener myListener;
private DatabaseAdapter database;
private ArrayList<MyObject> listItems;
private View layout;
private Button confirmBtn;
private Button cancelBtn;
private boolean isDeleteActive;
private boolean isExportActive;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
layout = inflater.inflate(R.layout.list_frag, container, false);
return layout;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
database = DatabaseAdapter.getInstance(getActivity());
database.open();
listItems = database.getMyObjects();
database.close();
setHasOptionsMenu(true);
registerForContextMenu(this.getListView());
final MyAdapter listAdapter = new MyAdapter(getActivity(), listItems);
setListAdapter(listAdapter);
confirmBtn = (Button) layout.findViewById(R.id.itemDelete);
cancelBtn = (Button) layout.findViewById(R.id.itemCancel);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.listview_omenu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
int itemId = item.getItemId();
if (itemId == R.id.listOMItem3) {
SortDialogFragment sortDialog = SortDialogFragment.newInstance(listItems);
sortDialog.show(getFragmentManager(), "Sort Dialog");
}
return true;
}
public static class SortDialogFragment extends SherlockDialogFragment {
public static SortDialogFragment newInstance(ArrayList<MyObject> objectsToSort) {
SortDialogFragment sortDialog = new SortDialogFragment();
Bundle args = new Bundle();
args.putParcelableArrayList("MyObjects to sort", objectsToSort);
sortDialog.setArguments(args);
return sortDialog;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
final ArrayList<MyObjects> objectsToSort = getArguments().getParcelableArrayList("MyObjects to sort");
builder.setTitle("Sort objects by...");
builder.setSingleChoiceItems(R.array.sortDialogOptions, checkedItem, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
if (which == 0) {
Collections.sort(objectsToSort, new ComparatorOne());
} else if (which == 1) {
Collections.sort(objectsToSort, new ComparatorTwo());
} else {
Collections.sort(objectsToSort, new ComparatorThree());
}
?????????????????????????????????????
// This is where I am trying to refresh the list before dismissing the dialog but I
// I apparently do not have access to listview's adapter to call the "notifyDataSetChanged" method.
dialog.dismiss();
}
});
builder.setPositiveButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder.create();
}
}
}
First to refresh your listview you have to call notifyDataSetChanged on the adapter that you've set for your listview. Start by changing final MyAdapter listAdapter to a field instead of a variable.
Next step is to pass the adapter to the constructor of your dialog and store it in a field. The header of your SortDialogFragment should look like this:
public static class SortDialogFragment extends SherlockDialogFragment {
private MyAdapter mAdapter;
public static SortDialogFragment newInstance(ArrayList<MyObject> objectsToSort, MyAdapter adapter) {
SortDialogFragment sortDialog = new SortDialogFragment();
sortDialog.setAdapter(adapter);
Bundle args = new Bundle();
args.putParcelableArrayList("MyObjects to sort", objectsToSort);
sortDialog.setArguments(args);
return sortDialog;
}
public void setAdapter (MyAdapter adapter){
mAdapter = adapter;
}
(...)
Next pass the adapter when you create your SortDialogFragment. Should look like this:
SortDialogFragment sortDialog = SortDialogFragment.newInstance(listItems, listAdapter);
Finally you just have to call mAdapter.notifyDataSetChanged(); on ????????????????

Categories