Android - Observing on live data not working from fragment - android

I have 2 fragment in activityty a list fragment and details fragment,details fragment showing selected items details from list fragment and there is button to change list item "status" set order as ready.
I want to move selected item to ready seaction when button Order is ready clicked.
I tried it with observing with shared view model but onchange method not calling when I set value in it.
here is a viewModel:
package com.example.ordermanager.fragments;
import android.database.ContentObserver;
import android.os.Handler;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.example.ordermanager.fragments.orderlist.dummy.DummyContent;
import java.util.List;
public class SharedViewModel extends ViewModel {
private MutableLiveData<DummyContent.DummyItem> item = new MutableLiveData<DummyContent.DummyItem>();
public void setItem(DummyContent.DummyItem value){
item.setValue(value);
}
public MutableLiveData<DummyContent.DummyItem> getItem(){
return item;
};
}
ListFragment:
public class OrderItemFragment extends Fragment {
// TODO: Customize parameter argument names
private static final String ARG_COLUMN_COUNT = "column-count";
// TODO: Customize parameters
private int mColumnCount = 1;
private OnListFragmentInteractionListener mListener;
private SharedViewModel vm;
private RecyclerView recyclerView;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public OrderItemFragment() {
}
// TODO: Customize parameter initialization
#SuppressWarnings("unused")
public static OrderItemFragment newInstance(int columnCount) {
OrderItemFragment fragment = new OrderItemFragment();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
vm = ViewModelProviders.of(this).get(SharedViewModel.class);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_order_item_list, container, false);
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
recyclerView = (RecyclerView) view;
if (mColumnCount <= 1) {
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else {
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
recyclerView.setAdapter(new MyOrderItemRecyclerViewAdapter(DummyContent.ITEMS, mListener));
}
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Observer<DummyItem> itemObserver = new Observer<DummyItem>() {
#Override
public void onChanged(#Nullable DummyItem selectedItem) {
//this never happening
Log.e("hereeeee","dfgdfg");
recyclerView.getAdapter().notifyDataSetChanged();
}
};
vm.getItem().observe(this, itemObserver);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnListFragmentInteractionListener) {
mListener = (OnListFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnListFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p/>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnListFragmentInteractionListener {
// TODO: Update argument type and name
void onListFragmentInteraction(DummyItem item);
}
}
DetailsFragment:
public class OrderDetailFragment extends Fragment {
private SharedViewModel mViewModel;
private DummyContent.DummyItem selectedItem;
private Button ReadyBtn;
public static OrderDetailFragment newInstance() {
return new OrderDetailFragment();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.order_detail_fragment, container, false);
Bundle bundle = getArguments();
if(bundle != null){
selectedItem = (DummyContent.DummyItem)getArguments().getSerializable("item");
TextView tv = (TextView) view.findViewById(R.id.detailid);
tv.setText(selectedItem.content);
}
ReadyBtn = view.findViewById(R.id.readyBtn);
ReadyBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(selectedItem != null){
selectedItem.isReady = true;
mViewModel.getItem().setValue(selectedItem);
}
}
});
return view;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(SharedViewModel.class);
}
}
Observer is in ListFragment OnViewCreated function
Any Ideas?

You should change the data in your adapter before calling notifyDataSetChanged() method. Now you are getting new value in itemObserver but you're not changing the adapter.
UPD. I've solved the problem! The key in your initialization code of SharedViewModel. You should attach activity to the ViewModelProviders class in both cases, but you use this and in reality you have two different instances instead of one which should be attached to the parent activity. So, change the code of initialization to
mViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); and it'll work!

When you declare local variables in functions, they get destroyed when the function call ends. Therefore you need to store your itemObserver in a field.
On a side note...
You don't need a default empty constructor in fragments unless you create a custom one, which isn't recommended.
Regarding recyclerview I would recommend reading this in detail (especially the ListAdapter part).

Related

Android TabLayout with ViewPager duplicates fragment contents when rotating

My app has a tablayout with a view pager. Each page has a fragment. There are 4 different fragments, three of them are basically the same for now (I'm in the development phase right now). One of them has a RecyclerView with a basic list.
I am implementing the Two-pane template in the fragment with the RecyclerView.
Everything seems to be works]ing well. While I move across the tabs the fragments are loaded fine.
But, when I rotate the device and tap on the first tab, and then go back to the one with the recyclerview, I can see the previous intance below. See attached images.
I decided to use static final instances of the fragments in the page adapter and in the recyclerview fragment.
How can I get rid of this problem?
Thanks in advance stackers!
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon1).setText(R.string.dashboard));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon2).setText(R.string.fragment2));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon3).setText(R.string.fragmentDualPane));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon4).setText(R.string.frag4));
final ViewPager viewPager = findViewById(R.id.pager);
final PagerAdapter pageAdapter = new TabPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pageAdapter);
tabLayout.setupWithViewPager(viewPager);
} // protected void onCreate
} // public class MainActivity
TabPagerAdapter has static final intances of the fragments
public class TabPagerAdapter extends FragmentPagerAdapter {
static final Fragment tabs[] = {new DashboardFragment(),
new Fragment2(),
new ExpensesFragment(),
new Fragment4()
};
public TabPagerAdapter(#NonNull FragmentManager fm) {
super(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
} // public TabPagerAdapter
#NonNull
#Override
public Fragment getItem(int position) {
if (position<tabs.length)
return tabs[position];
else
return null;
} // public Fragment getItem
#Override
public int getCount() {
return this.tabs.length;
} // public int getCount
} // class TabPagerAdapter
General fragment template for dashboard, fragment2, and fragment4
public class DashboardFragment extends Fragment {
public DashboardFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_dashboard, container, false);
}
}
This is the code for the fragment with the dual pane. Look that it uses the OnItemSelected implementation of fragments communications.
This fragment loads another fragment with the recyclerview.
public class ExpensesFragment extends Fragment
implements IOnItemSelected {
#Override
public void onAccountSelected(Account item) {
System.out.println("Clicking on " + item.getTitle() + ", and isTwoPane=" + isTwoPane);
} // public void onAccountSelected
public static final String TAG="Expenses Fragment";
private boolean isTwoPane = false; // Let's assume we're on a phone
private FragmentManager fragmentManager;
private View fragmentView = null;
public ExpensesFragment() {
// Required empty public constructor
} // ExpensesFragment()
public static final ExpensesListFragment lListFragment = new ExpensesListFragment();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
fragmentView = inflater.inflate(R.layout.fragment_expenses, container, false);
isTwoPane = fragmentView.findViewById(R.id.expensesDetailContainer) != null;
fragmentManager = getChildFragmentManager();
if (savedInstanceState==null) {
if ( !lListFragment.isAdded() ) {
fragmentManager.
beginTransaction().
add(R.id.expensesListContainer,lListFragment).
commit();
} // if ( !lListFragment.isAdded() )
} // if (savedInstanceState==null)
if ( isTwoPane ) {
fragmentManager.
beginTransaction().
replace(R.id.expensesDetailContainer,new EmptyFragment()).
commit();
} // if ( isTwoPane )
return fragmentView;
} // onCreateView
} // ExpensesFragment
And this is the fragment with the recyclerview:
public class ExpensesListFragment extends Fragment {
private IOnItemSelected mCallback;
private RecyclerView rv;
private RecyclerView.LayoutManager rvlm;
private RecyclerAdapterAccounts rva;
public ExpensesListFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCallback = (IOnItemSelected)getParentFragment();
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.fragment_expenses_list, container, false);
if ( isVisible() ) return fragmentView;
FragmentManager fragmentManager = getChildFragmentManager();
// Setting the recyclerview environment
rv = fragmentView.findViewById(R.id.expensesRV); // recycler view
rvlm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(rvlm);
rva = new RecyclerAdapterAccounts();
rva.setCallBackFunction(mCallback);
rv.setAdapter(rva);
// Setting the floating action button and snackbar
FloatingActionButton fab = fragmentView.findViewById(R.id.fabAdd);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Load a Create Item frag", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
return fragmentView;
} // onCreateView
} // public class ExpensesListFragment
The RecyclerAdapterAccounts creates a generic set of data:
public class RecyclerAdapterAccounts extends
RecyclerView.Adapter<RecyclerAdapterAccounts.ViewHolderAccounts> {
private IOnItemSelected callBackFunction;
public IOnItemSelected getCallBackFunction() {return callBackFunction;}
public void setCallBackFunction(IOnItemSelected callBackFunction) {this.callBackFunction = callBackFunction;}
class ViewHolderAccounts extends RecyclerView.ViewHolder {
ImageView icon, isRepeating, isAlert;
TextView title, total;
public Account getAccount() {return account;}
public void setAccount(Account account) {this.account = account;}
Account account;
public ViewHolderAccounts(View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.list_item_ico_account);
isRepeating = itemView.findViewById(R.id.list_item_isrepeating);
isAlert = itemView.findViewById(R.id.list_item_isalert);
title = itemView.findViewById(R.id.list_item_title_account);
total = itemView.findViewById(R.id.list_item_desc_account);
account = null; // The account needs to be set using the setter/getter method
} // ViewHolderAccounts
} // class ViewHolderAccounts
List<Account> accts = new ArrayList<Account>();
ViewGroup parent;
#NonNull
#Override
public ViewHolderAccounts onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_account,parent,false);
this.parent = parent;
ViewHolderAccounts vh = new ViewHolderAccounts(v);
return vh;
} // onCreateViewHolder
#Override
public void onBindViewHolder(#NonNull ViewHolderAccounts holder, int position) {
// Look into the list the item with id=position
Optional<Account> la = accts.stream()
.filter(ac->ac.getId()==(long)position)
.findFirst();
if ( la.isPresent() ) {
int res = parent.getResources().getIdentifier(la.get().getIcon(), "drawable", "com.almonisolutions.elgddt");
holder.icon.setImageResource(res);
holder.isRepeating.setImageResource(R.drawable.automatic);
holder.isAlert.setImageResource(R.drawable.notifications);
holder.title.setText(la.get().getTitle());
holder.total.setText(la.get().getDescription());
holder.setAccount(la.get());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (holder.getAccount() != null) {
callBackFunction.onAccountSelected(holder.getAccount());
} // if (account != null)
} // public void onClick
});
} // if
} // onBindViewHolder
#Override
public int getItemCount() {
return accts.size();
} // getItemCount
RecyclerAdapterAccounts() {
super();
for(int i=0;i<16;i++) {
Account la = new Account();
la.setId((long) i);
la.setTitle("The item number " + i);
la.setDescription("$" + (1000*i));
switch(i%3) {
case 0: la.setIcon("imaged"); break;
case 1: la.setIcon("person_old"); break;
case 2: la.setIcon("pet"); break;
default: la.setIcon("add");
} // switch
accts.add(la);
} // for
} // RecyclerAdapterAccounts
} // class RecyclerAdapterAccounts
At first, In the ExpensesFragment I was getting an Exception that throw the message "Fragment already added". When I changed the ExpensesListFragment to static final, that error was gone.
Again, to recreate the error, you need to run in portrait mode, move through the tabs. Finish on anyone but the first one. Them rotate the device. Tap on the first tab. Then tap on the 3rd one, the one with the recyclerview. Swipe through the list and you will see it is double.
Any help will be appreciated.
Thanks in advance!!!
So I found the answer. ADM (see comment above) sent me to a previous article where part of the solution was to extend ViewPager and override instantiateItem. But I did not want to extend ViewPager.
However, in the same article was another link to this other article where there was the following explanation:
Blockquote By default, [FragmentPagerAdapter] will only preload one Fragment in front and behind the current position (although it does not destroy them unless you are using FragmentStatePagerAdapter).
So, I made TabPagerAdapter extend FragmentStatePagerAdapter instead of FragmentPageAdapter... and that was it!!
Thanks ADM for pointing to the right series of articles.

How to launch a fragment from a fragment while passing a custom object

Building on a previous question and using fragment based approach, the question now is How to launch a fragment from a fragment while passing a custom data object
public class MainActivity extends AppCompatActivity {
public User user = new User();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_layout, user)
.commit();
}
}
User.java list of users
public class User extends Fragment {
ListView userList;
private ArrayList<UserVO> users;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.user_list, container, false); // Inflate the layout for this fragment
userList = view.findViewById(R.id.userList);
users = getUsers(); // ArrayList
userList.setAdapter(new UserAdapter());
userList.setOnItemClickListener((adapterView, view1, i, l) -> {
Log.d("UserList", "onItemClick: " + i);
// how to launch Form.java Fragment while passing users[i]
});
return view;
}
class UserAdapter extends BaseAdapter {
#Override
public int getCount() {
return users.size();
}
#Override
public Object getItem(int i) {
return null;
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
view = LayoutInflater.from(viewGroup.getContext()).inflate(android.R.layout.simple_list_item_1, viewGroup,false);
((TextView) view.findViewById(android.R.id.text1)).setText(users.get(i).givenName());
return view;
}
}
}
Form.java which is basically to display user data in a form for update purposes.
public class Form extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.form, container, false);
return view;
}
}
Q1. Within User.java I've a placeholder in the comments, asking for how to instantiate Form.java while passing users[i] (a custom object with fields)
Q2. Please see if there's anything wrong with the approach in MainActivity.java, I've seen the other approach with add method and I'm using replace here, not sure which one is right here.
Attempt
I've added following code and not happy from the result, it's overlapping with the list, I want form to have it's own view and place on the stack. Back button should take the user back to the list.
Form form = new Form();
getFragmentManager().beginTransaction()
.add(R.id.frame_layout, form)
.commit();
Edit 2 For review - based on the answer.
public interface IForm { // 1
void update(UserObject userObject);
}
public class User extends Fragments implements IForm { // 2
private Form form = new Form();
void update(UserObject userObject) {
// update user list with updated object.
}
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.user_list, container, false); // Inflate the layout for this fragment
userList = view.findViewById(R.id.userList);
users = getUsers(); // ArrayList
form.delegate = this; // 3
userList.setOnItemClickListener((adapterView, view1, i, l) -> {
Log.d("UserList_archive", "onItemClick: " + i);
form.userData = users[i]; // 4
getFragmentManager().beginTransaction()
.replace(R.id.frame_layout, form)
.addToBackStack(null)
.commit();
});
return view;
}
public class Form {
public IForm delegate; // 5
public UserObject userObject; // see 4 - to display data in the form
}
Here you have to use one CallBack/Interface to plug and play with two fragments.
First, write one interface with a method which will do the job, like below:
public interface ItemClickListener extends Parcelable{
void onItemClick(FormData data);
}
Then initialize this interface in your both Fragments in OnAttach() :
public class UserFragment extends Fragment {
private ItemClickListener onItemClick;
#Override
public void onAttach(Context context) {
super.onAttach(context);
onItemClick= (ItemClickListener) context;
}
Use it inside your onItemClick to pass the value:
userList.setOnItemClickListener((adapterView, view1, i, l) -> {
Log.d("UserList", "onItemClick: " + i);
// how to launch Form.java Fragment while passing users[i]
onItemClick.onItemClick(adapterView.getItemAtPosition(i);
});
}
Then implement this interface in your Activity.
public class MainActivity extends AppCompatActivity implements ItemClickListener{
#override
public void onItemClick(FormData data){
//do your stuff here with form data or whatever your object
//Start Form Fragment and send the data through Bundle
//https://stackoverflow.com/questions/12739909/send-data-from-activity-to-fragment-in-android
}
}

Android- layer beneath the cardviews when scrolling in recyclerview

For a personal project, i'm building an app which shows the current tables of the most popular football (or should i say soccer?) leagues in Europe. The table is a recyclerview, and each team inside it is a cardview. At the begining when my device is on portrait mode, everything shows up fine, but the problem starts when i change to landscape mode and then back to portrait. After that, i see another layer of my cardviews, beneath my current cardviews, and everything gets really ugly.
This is how it looks when everything gets messy
My recyclerview adapter:
public class TeamAdapter extends RecyclerView.Adapter<TeamAdapter.TeamViewHolder> {
private TeamLeagueStandings[] teams;
public TeamAdapter(TeamLeagueStandings[] teams){
this.teams=teams;
}
#Override
public TeamAdapter.TeamViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.
from(parent.getContext()).
inflate(R.layout.card_view_team, parent, false);
return new TeamViewHolder(itemView);
}
#Override
public void onBindViewHolder(TeamAdapter.TeamViewHolder holder, int position) {
TeamLeagueStandings team = teams[position];
holder.teamname.setText(team.getTeamName());
holder.matches.setText(Integer.toString(team.getCurGames()));
holder.wins.setText(Integer.toString(team.getWins()));
holder.draws.setText(Integer.toString(team.getDraws()));
holder.losses.setText(Integer.toString(team.getLosses()));
holder.gd.setText(Integer.toString(team.getGoalDifference()));
holder.points.setText(Integer.toString(team.getPoints()));
}
#Override
public int getItemCount() {
return teams.length;
}}
My view holder (inner class inside the adapter):
public static class TeamViewHolder extends RecyclerView.ViewHolder{
protected TextView teamname;
protected TextView matches;
protected TextView wins;
protected TextView draws;
protected TextView losses;
protected TextView gd;
protected TextView points;
public TeamViewHolder(View itemView) {
super(itemView);
teamname=itemView.findViewById(R.id.teamName_txtview);
matches=itemView.findViewById(R.id.matches_txtview);
wins=itemView.findViewById(R.id.wins_txtview);
draws=itemView.findViewById(R.id.draws_txtview);
losses=itemView.findViewById(R.id.losses_txtview);
gd=itemView.findViewById(R.id.gd_txtview);
points=itemView.findViewById(R.id.points_txtview);
}
}
Finally, my main fragment, where i declare the recyclerview:
public class TableStandingsFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
TeamAdapter adapter;
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public TableStandingsFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment BlankFragment.
*/
// TODO: Rename and change types and number of parameters
public static TableStandingsFragment newInstance(String param1, String param2) {
TableStandingsFragment fragment = new TableStandingsFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
public TeamLeagueStandings [] GetPLTeams() throws IOException, JSONException {
URL urlPL= League_standings.GetPLQuery();
TeamLeagueStandings[] teams=League_standings.LeagueStandingsArray(urlPL);
return teams;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
// Inflate the layout for this fragment
View v= inflater.inflate(R.layout.fragment_table, container, false);
try {
adapter= new TeamAdapter(GetPLTeams());
RecyclerView recyclerView = (RecyclerView) v.findViewById(R.id.recyler_teams);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new VerticalSpaceItemDecorator(30));
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return v;
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
class VerticalSpaceItemDecorator extends RecyclerView.ItemDecoration {
private final int spacer;
public VerticalSpaceItemDecorator(int spacer) {
this.spacer = spacer;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.bottom = spacer;
}
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
Of course, there is more code that i didn't upload, i think that this code that i do upload is enough in order to solve this problem.
Thanks!!
In your Activity try this code for fragment transaction
getSupportFragmentManager().beginTransaction()
.replace(your container id, TableStandingsFragment.newInstance(param1,param2), TableStandingsFragment.class.getSimpleName())
.commitAllowingStateLoss();
Make sure you are importing right Fragment in TableStandingsFragment
import android.support.v4.app.Fragment;
Remove recyclerView.setHasFixedSize(true);from you code and set your cardView height to wrap_content. You have to configure recyclerview before setting adapter.
Your recycler view initialization should be like this.
RecyclerView recyclerView = (RecyclerView) v.findViewById(R.id.recyler_teams);
recyclerView.addItemDecoration(new VerticalSpaceItemDecorator(30));
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
If you want to know more about hasFixedSize(true) look at here.
https://stackoverflow.com/a/40707099/5558150
https://stackoverflow.com/a/33365341/5558150

Populating a recyclerView within a fragment of a Tabbed application

I'm new to android studio and I'm trying to make a Tabbed Application with a Menu Tab in the Middle,
I'm using RecyclerView in my fragment, as explained in this tutorial
I've done everything the way it should be, but at the "Binding the Adapter to the RecyclerView" Step I encountered a problem,
the findViewById(R.id.my_recycler_id); returns null
Here's the Code:
Main activity
public class MainActivity extends AppCompatActivity {
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
ArrayList<DummyItem> dummies;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
RecyclerView rvDummies =(RecyclerView) findViewById(R.id.listdummies);
// Initialize contacts
dummies = DummyItem.createDummiesList(20);
// Create adapter passing in the sample user data
MyItemRecyclerViewAdapter adapter = new MyItemRecyclerViewAdapter(dummies, this);
//Log.d("D",fragment.toString());
// Attach the adapter to the recyclerview to populate items
rvDummies.setAdapter(adapter);
// That's all!
}
... (some other code)
}
My fragement
public class ItemFragment extends Fragment {
// TODO: Customize parameter argument names
private static final String ARG_COLUMN_COUNT = "column-count";
// TODO: Customize parameters
private int mColumnCount = 1;
private OnListFragmentInteractionListener mListener;
RecyclerView fragmentchildRecyclerView;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public ItemFragment() {
}
// TODO: Customize parameter initialization
#SuppressWarnings("unused")
public static ItemFragment newInstance(int columnCount) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item_list, container, false);
fragmentchildRecyclerView = (RecyclerView) view.findViewById(R.id.listdummies);
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(new MyItemRecyclerViewAdapter(DummyContent.ITEMS, context));
}
return view;
}
public RecyclerView getRecyclerView(){
return fragmentchildRecyclerView;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnListFragmentInteractionListener) {
mListener = (OnListFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnListFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p/>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnListFragmentInteractionListener {
// TODO: Update argument type and name
void onListFragmentInteraction(DummyItem item);
}
}
Dummy item
public static class DummyItem {
public final String id;
public final String content;
public final String details;
public DummyItem(String id, String content, String details) {
this.id = id;
this.content = content;
this.details = details;
}
#Override
public String toString() {
return content;
}
public static ArrayList<DummyItem> createDummiesList(int numDummies){
ArrayList<DummyItem> dummies = new ArrayList<DummyItem>();
for (int i = 1; i <= numDummies; i++) {
dummies.add(new DummyItem("id "+numDummies,"title "+numDummies,"description "+numDummies));
}
return dummies;
}
}
I searched a lot and I've found that it may be calling the findViewByID before it's created, and others say that the fragment should have a function that returns the recyclerView so i can access it...
Any suggestions would be helpful.
You cannot reference a component in an activity when the component lies in the Fragment. Instead, add your
RecyclerView rvDummies = (RecyclerView) view.findViewById(R.id.listdummies);
in the Fragment's OnCreateView method and set the adapter there. Here the view object will be the layout your Fragment might be inflating.
Reason to do so: Since the Fragment will have a separate layout, the components included in it must be referenced within the Fragment as child to the view (Layout) inflated by the Fragment.

How to a get a view of fragment in Android

Hello I am using Fragment in my android application. I need to get the view which I can get using.
mNoteEditText = rootView.findViewById(R.id.noteEditText);
This mNoteEditText is require to access in onBackPressed so every view reference I need to make them static variable because of Fragment class is static. I know to make every view to static variable is not good approach. How this I can make such that I dont need to make any static variable of the view.
public class NotesActivity extends Activity {
private int bookId;
private int chapterId;
private static EditText mNoteEditText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notes);
// get data from intent that sent from home activity
bookId = getIntent().getIntExtra("book_id", -1);
chapterId = getIntent().getIntExtra("book_id", -1);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new NoteFragment()).commit();
}
}
/**
* A note fragment containing a note layout.
*/
public static class NoteFragment extends Fragment {
public NoteFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_notes,
container, false);
mNoteEditText = (EditText) rootView.findViewById(R.id.noteEditText);
return rootView;
}
}
#Override
public void onBackPressed() {
super.onBackPressed();
// get database instance
MySQLiteOpenHelper db = MySQLiteOpenHelper.getInstance(this);
Notes note = new Notes();
note.setBookId(bookId);
note.setChapterId(chapterId);
note.setNote(mNoteEditText.getText().toString());
}
}
Please help and thanks in advance.
A Fragment has a Method called getView(). With it you can get the View of the Fragment as long as it is attached to an Activity.
View view = fragment.getView();
But if you are looking for a View inside the Fragment you can also just get it with findViewById() from the Activity. Again the Fragment has to be attached to the Activity for this to work.
BUT you should not do that. Nothing outside the Fragment should have anything to do with something inside the Fragment. Write public methods in the Fragment to interact with it. Try something like this:
public static class NoteFragment extends Fragment {
private EditText noteEditText;
public NoteFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_notes, container, false);
this.noteEditText = (EditText) rootView.findViewById(R.id.noteEditText);
return rootView;
}
// I added the following 3 methods to interact with the Fragment
public boolean isEmpty() {
final String text = this.noteEditText.getText().toString();
return text.isEmpty();
}
public String getText() {
return this.noteEditText.getText().toString();
}
public void setText(String text) {
this.noteEditText.setText(text);
}
}
And now in your Activity you can do this:
public class NotesActivity extends Activity {
private int bookId;
private int chapterId;
private NoteFragment noteFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notes);
// get data from intent that sent from home activity
bookId = getIntent().getIntExtra("book_id", -1);
chapterId = getIntent().getIntExtra("book_id", -1);
if (savedInstanceState == null) {
this.noteFragment = new NoteFragment();
getFragmentManager().beginTransaction().add(R.id.container, this.noteFragment).commit();
}
// Now you can interact with the Fragment
this.noteFragment.setText("some text");
...
if(!this.noteFragment.isEmpty()) {
String note = this.noteFragment.getText();
...
}
}
}

Categories

Resources