Refreshing data display on a fragment - android

I have two fragments on a view pager.
I once had to move data from fragment B to A and refresh the data displayed on A and I did it with getItemPosition.
For some reason, the same method doesn't work when I try to reset all data..
In my adapter i have :
public void refresh()
{
notifyDataSetChanged();
}
#Override
public int getItemPosition( Object obj )
{
return POSITION_NONE;
}
in fragment where I click 'reset' :
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
notTriedPasswordsList = PagerActivity.mainList;
.....
....
resetButton.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick( View v )
{
PagerActivity.resetPasswords();
PagerActivity.viewPagerAdapter.refresh();
}});
viewPager activity hosting both fragments:
public static void resetPasswords()
{
mainList.addAll( 0, historyList );
historyList.clear();
PagerActivity.viewPagerAdapter.refresh();
}
Main fragment where the pass is displayed :
#Override
public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
{
View view = inflater.inflate(R.layout.fragment_main, container, false);
.....
nextCodeDisplay = ( TextView ) view.findViewById( R.id.passwordDisplayTextView );
nextCodeDisplay.setText( notTriedPasswordsList.get( 0 ).getPasswordString() );
....
nextButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
notTriedPasswordsList.remove( 0 );
if( notTriedPasswordsList.size() > 0 && !(notTriedPasswordsList.get( 0 ).getTried()) )
{
nextCodeDisplay.setText( notTriedPasswordsList.get( 0 ).getPasswordString() );
}
}

PagerActivity is treated like a static class, and you can only access static methods and member data and objects in this way. About code:
notTriedPasswordsList = PagerActivity.mainList;
Note: So now PagerActivity can access static mainList object, or notTriedPasswordsList (sharing the same memory). But this is the only object you can access since your code references static methods.
On code PagerActivity.viewPagerAdapter.refresh(), I am not clear on what data this refreshes since I don't see the enough code, again refresh() must be a static method. With code notifyDataSetChanged(), there must be a direct link between viewPagerAdapter and the data object, probably an ArrayList. Certainly I don't see any direct relation between the two.
Perhaps you want code like:
viewPagerAdapter pagerAdapter = new viewPagerAdapter();
This way you can have the relationship between the adapter and possibly an ArrayList object. The benefit of creating an instance with new is that it saves data and the state inside the class in the form of an object, in my sample that is pagerAdapter.
I could not suggest specific set of codes for now since I don't see sufficient amount of it for me to fix. Perhaps you can fix code first and then we all can contribute.

Your call to PagerActivity.viewPagerAdapter.refresh(); won't cause your fragment to be redrawn. Instead you should access your fragment directly and create a custom refreshUI() method in it.
public void refreshUI(){
nextCodeDisplay.setText( notTriedPasswordsList.get( 0 ).getPasswordString() );
}

I suggest to change your approach. I've uploaded a simple project to my dropbox public folder. Here you can find a reference implementation of how two fragments managed by a ViewPager can share information. The first Fragment - Fragment#1 - simply displays a String that is generated by Fragment#2. Fragment#2 has a button that, when clicked, sends a random String to Fragment#1 through the Activity. No need to refresh viewpager, no need of static methods, simple and working. I guess you can adapt this example to your needs.

As you said that you want to refresh your data, personally i would like to suggest to use swipe refresh layout. It will be very useful for this purpose and stylish as well. Following is the code.
Swipe_Refresh_layout.xml
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView android:id="#+id/listview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
/>
</android.support.v4.widget.SwipeRefreshLayout>
And following is the activity i am using for this layout.
public class LatestNewsFragment extends Fragment implements OnRefreshListener ,OnScrollListener{
SwipeRefreshLayout swipeLayout;
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
{
=
View rootView = inflater.inflate(R.layout.Swipe_Refresh_layout, container, false);
swipeLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_container);
swipeLayout.setOnRefreshListener(this);
swipeLayout.setColorScheme(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
return rootView;
}
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
#Override public void run() {
swipeLayout.setRefreshing(false);
additemstatus();
}
}, 5000);
}
Now in overridden refresh method you can refresh or load your data. I hope this will be very helpful for your support.

Related

Reusing layout created on Fragment.onCreateView() to avoid inflation when showing multiple times

I am using a DialogFragment to display a 'modal' bottom sheet menu (more info here: https://material.io/develop/android/components/bottom-sheet-dialog-fragment/). Since it contains a kind of context menu for the items contained in a RecyclerView, it may be shown multiple times during runtime.
However, always DialogFragment.show() is called, Fragment.onCreateView() is also called, which leads to layout inflation, which can(?) be considered as a 'heavy' task to be computed in the UI thread, which I want to avoid for performance reasons. So to avoid layout inflation every time the DialogFragment is shown, I created a ViewGroup member object pointing to the View being returned Fragment.onCreateView() in order to be reused, like this:
public class BottomMenu extends BottomSheetDialogFragment {
private ViewGroup mLayout;
private TextView mLabel;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (mLayout == null) {
mLayout = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.bottom_sheet, container, false);
mLabel = mLayout.findViewById(R.id.bottom_sheet_label);
}
return mLayout;
}
#Override
public void onDismiss(#NonNull DialogInterface dialog) {
super.onDismiss(dialog);
// The view cannot be reused if it's already attached to the previous parent view
((ViewGroup) mLayout.getParent()).removeView(mLayout);
}
public void setLabel(String label) {
mLabel.setText(label)
}
}
But once used for the first time, such view must be detached from the Fragment container view to be reused (see onDismissed() overriden method on posted snippet), which seems like a nasty workaround
So I post this question to check if anyone knows a better approach to reuse the layout for the same Fragment
More details here:
public class ActivityMain extends AppCompatActivity {
private BottomMenu mBottomMenu;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[...]
mBottomMenu = new BottomMenu();
}
#Override
public boolean onLongClick(View v) {
mBottomSheet.setLabel(label);
// The following calls onCreateView() in Fragment, so try to return
// there the previously inflated layout, if any
mBottomSheet.show(getSupportFragmentManager(), "TAG?");
return true;
}
}
It is already a nice practice as long as you don't surrender to any possible bugs.. However there are one or two things I want to let you know about resuing dialogFragment.
public class BottomMenu extends BottomSheetDialogFragment {
private ViewGroup mLayout;
private TextView mLabel;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (mLayout == null) {
mLayout = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.bottom_sheet, container, false);
mLabel = mLayout.findViewById(R.id.bottom_sheet_label);
} else if(mLayout.getParent()!=null) { // it's not a lot of code. just a few lines……
((ViewGroup)mLayout.getParent()).removeView(mLayout);
}
return mLayout;
}
}
One thing is about nested fragments. When the dialogFragment hold a viewpager and the viewpager have serveral sub-fragments, you must reset the viewpager's adapter on the reusing-call of onCreateView. The reason is that after closing the dialogFragment, the old fragmentManager returned by getChildFragmentManager() is no longer valid, and it should be updated.
... onCreateView(...)
if (mLayout == null) {
...
} else {
...
viewpager.setAdapter(new MyFragmentAdapter(getChildFragmentManager(), fragments));
}
If this step is omitted, you may observe strange behaviours when reusing the dialogFragment, such as recyclerviews in the sub-fragments stop updating in response to NotifyDatasetChanged, but if you scroll it, it will update.
Another thing is that I tend to use WeakRefernce to hold the dialogFragment to be reused. I even have an array of them.
In java applications, if you don't use similar mechanism, you can see rapid surge in memory usage when the user open and close the same dialog again and again. So at least it's not a bad practice to reuse dialogs when it's necessary.

How change a element of fragment.xml from MainActivity.java

I want change an element of a fragment from MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
list = (ListView) findViewById(R.id.list);// => this is the error
...
list.setOnItemClickListener(new Adapter.OnItemClickListener() {//this can't work because list isn't in layout=>activity_main
....
I need manipulate 'lis't from MainActivity, but i can't, because the element "list" is in the fragment container.xml and isn't in activity_main.xml .
I can't move 'list' in activity_main.xml, because i need load into container.xml.
How can i call from MainActivity in the right way?
Why you need to perform action on fragments view in activity?
You can do all this inside your fragment.
Add that fragment to activity.
In fragments onCreateView method get the view and do whatever you want.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View inflatedView = inflater.inflate(R.layout.your_layout, container, false);
ListView list = (ListView) inflatedView.findViewById(R.id.list);// => this is the error
list.setOnItemClickListener(new Adapter.OnItemClickListener() {});
return inflatedView;
}
Although it is a bad idea and it is better that you find a better solution for your requirement, the answer is that you can make your ListView inside fragment public static, and then perform your action on it like:
YourFragment.list.setOnItemClickListener(new Adapter.OnItemClickListener()...

private variable inside fragment are all null, why?

I have of null object inside a fragment. The basic idea is that I have an activity that fetches a database asynchronously. However my recyclerview where I will populate the data lives into a fragment. The pseudo-code is more or less
ACTIVITY:
public void onCreate(Bundle savedInstanceState) {
//kicks off a query to the server
mData = new Gson().fromJson(getIntent().getExtras().getString(Constants.MYDATA), MyData.class);
if (mVenue == null) {
finish();
return;
}
// a bunch of stuff
// create a fragment
mMyFrag = new MyFrag();
}
public void CallBackWhenDone(final List<DataSet> dataset) {
// notify the frag that we are done
mMyFrag.notifyDataSetChanged(messages);
}
FRAGMENT:
private RecyclerView mRV;
private ParentActivity mActivity;
private ActivityAsynchData mAsynchData;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.recycler, container, false);
mRV = (RecyclerView) view.findViewById(R.id.list);
mRV.setLayoutManager(new LinearLayoutManager(getActivity().getApplicationContext(), LinearLayoutManager.VERTICAL, false));
if (null != mActivity) {
mAsynchData = mActivity.GetAsynchData();
}
if (null != mAsynchData) {
mRV.setAdapter(new MyRecyclerAdapter(getActivity(), mAsynchData));
}
}
// mRV is null when the activity "CallBackWhenDone" calls the frag
// all private variables are gone! why?
public void notifyDataSetChanged(final List<Message> messages) {
MyRecyclerAdapter adapter = ((MyRecyclerAdapter) mRV.getAdapter());
adapter.setMessageList(messages);
adapter.notifyDataSetChanged();
}
I ended up hacking my recycler (mRV in this case) view to be static, but this looks super hacked.
any way around? In other words how can I fetch my "mRV" after the fragment has been created and the private vars are all gone.
What i could understand is, that you initialised the fragment and try to access the recycler view in that but its throwing you null. I am not surprised to see it being as null. The way you have called the method is not correct.You need to get the hosted and already running fragment, instance try doing this:
if you are using support fragment use getSupportFragmentManager instead of getFragmentManager.
MyFrag fragment = (MyFrag) getFragmentManager().findFragmentById(R.id.fragmentHolder);
fragment.<specific_function_name>();
Here , the R.id.fragmentHolder is the id of the frame layout or any layout that you are using to host your fragment inside the an activity.
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentHolder"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout>

Accessing parent fragments methods from recyclerview's adapter

Hi guys I'm trying to access few methods and variables of fragment(containing a recycler view) from the recycler views adapter class.. Simplest way is to pass in the fragment reference along with the adapter which creating it. But I dont think passing the full adapter reference which creating the adapter is a good approach.
I'm using RxJava in my project and tried a lot of things with PublishSubject like creating a Subject in adapter, calling its onNext which an event is performed and subscribe to that subject in the fragment but it didnt work out..
So any good approach will be highly appreciated.
TIA...
I'd suggest to introduce EventBus in your app - pretty elegant way of communication between different components of the app.
Then it'd look like:
Fragment:
public class MyFragment extends Fragment {
private EventBus eventBus = EventBus.getDefault();
RecyclerViewAdapter viewAdapter;
#Override
public void onAttach(Context context) {
super.onAttach(context);
eventBus.register(this);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_a, container, false);
if (viewAdapter == null) {
viewAdapter = new RecyclerViewAdapter(eventBus);
}
RecyclerView recyclerView = (RecyclerView)rootView.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(viewAdapter);
return rootView;
}
#SuppressWarnings("unused") // invoked by EventBus
public void onEventMainThread(final DataRefreshedEvent event) {
// Do something!
}
#Override
public void onDetach() {
eventBus.unregister(this);
super.onDetach();
}
}
Adapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
EventBus eventBus;
public RecyclerViewAdapter(EventBus eventBus) {
this.eventBus = eventBus;
}
void sentSomethingToFragment() {
eventBus.post(new DataRefreshedEvent());
}
.....
}
Event: public final class DataRefreshedEvent {}
And just a note - with Dagger, it'd look even better.
I hope, it helps
My suggestion is go with interface approach.
1. Create one interface.
2. Fragment should implement that interface.
3. Pass that interface reference to the adapter.
4. Call the interface method from adapter
So that way you can communicate between fragment and adapter.

setUserVisibleHint with Fragments for android

Im using 3 fragments in my ViewPager adapter. I will be loading data from Parse(parse.com) and displaying them in recycler views. The following code is causing my app to crash. What my understanding is that when my MainActivity loads, this is my first fragment ie, it gets viewed by user immediately so the setUserVisibleHint function gets called immediately and in that v.findViewById code causes a null pointer exception since setContentView hasnt/ may not been called. My proof for this is if i add a 1sec delay to setUserVisisbleHint then my code works properly.
Now I want to add server PULL requests using Parse, add data in a list to an adapter and populate recyclerview AFTER user views the page so
1) Should i add all the code in setUserVisisbleHint and just add a 0.5secc delay so it gets executed after setContentView is called ensuring I dont get a null pointer exception error OR
2) Is there a better way/ other functions I can use to achieve the same?
public class NewsFeed extends Fragment {
LinearLayoutManager mLayoutManager;
boolean _areLecturesLoaded=false;
View v;
ProgressBar bar;
public NewsFeed() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
// Inflate the layout for this fragment
v=inflater.inflate(R.layout.fragment_news_feed, container, false);
RecyclerView mRecyclerView = (RecyclerView)v.findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(v.getContext());
mRecyclerView.setLayoutManager(mLayoutManager);
MyStickyAdapter mAdapter = new MyStickyAdapter(v.getContext());
mRecyclerView.setAdapter(mAdapter);
// mRecyclerView.addItemDecoration(new StickyRecyclerHeadersDecoration(mAdapter));
return v;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && !_areLecturesLoaded ) {
_areLecturesLoaded = true;
v.findViewById(R.id.asd).setVisibility(View.GONE);
}
}
}//Closes Fragment
Im using this library for my recycler view StickyHeaderRecyclerView
I would highly recommend avoiding adding delays especially if you're running this on the main thread. To avoid the NPE, try moving the findViewById(R.id.asd) into your onCreateView method after you inflate the view:
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
// Inflate the layout for this fragment
v=inflater.inflate(R.layout.fragment_news_feed, container, false);
v.findViewById(R.id.asd).setVisibility(View.GONE);
This is assuming that R.id.asd is in R.layout.fragment_news_feed
This post could also provide some insight:
setUserVisibleHint called before onCreateView in Fragment

Categories

Resources