Android activity wont load items at first - android

In my application I have empty activity and plus button which opens new activity in which I type some title and few text fields and then save. When i save that item is added to the 1. activity but not shown. I need to go to some other activity and when i come back its then loaded. Same goes for 2nd item after its added but after there are 2 or more items it always loads all the items, im not using aSyncTask, i just load data from database. Anyone knows what could be the problem?
Is there any itemView.function() that reloads all the items in a activity?

You could use LiveData for that and observe it to your Main Activity.
I assume that you are using ROOM library for your database and MVVM architecture. Here's the example from my project.
public LiveData<List<Todo>> getAllTodo() {
return allTodo;
} //This is from my ViewModel class.
and I observe it from my main activity
viewModel = new ViewModelProvider(this).get(ViewModel.class);
viewModel.getAllTodo().observe(this, new Observer<List<Todo>>() {
#Override
public void onChanged(List<Todo> todos) {
todoAdapter.setTodos(todos);
}
});

Problem was that my xml file that containted RecyclerView was set to wrap contnent so i changed
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/card_gallery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:padding="2dp"
tools:listitem="#layout/card_item">
</androidx.recyclerview.widget.RecyclerView>
to
android:layout_width="match_parent"
android:layout_height="match_parent"

Related

Android Fragment in Layout Losing Listener

An interesting issue here. Writing an app in Kotlin (which is awesome btw) and due to customer design restraints we've had to implement a custom navigation drawer(wish we could use the native navigation view with app:menu but we can't).
Embedded fragment
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="false">
<fragment
android:name="com.redacted.app.nav.NavDrawerFragment"
android:id="#+id/nav_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.design.widget.NavigationView>
// our callback
interface NavDrawerListener {
enum class CurrentState {
StateOne,
StateTwo
}
fun onStateOneClicked()
fun onStateTwoClicked()
}
In our case, we've created a Fragment call NavDrawerFragment that contains a RecyclerView with items and a callback interface that get hooked into in the fragment's onAttach(context: Context) method that lets the activity know the item that was clicked and any additional payloads needed to start the next activity. All works as expected, with the new activities using the same base layout and the fragments listener being implemented by the activity until... the back button is pressed. It appears that pressing back on Activity B calls onDetach on the fragment and when ActivityA resumes it the fragment instance's onAttach method never gets called again.
Am I missing something about fragments being embedded into a layout or is this behavior expected? All I need at the end of the day is for ActivityA's implementation of NavDrawerListener to be valid onResume().
I just went through something similar, as in weird fragment behavior. I resolved all the weird issues and errors by calling
SupportFragmentManager
The fragment itself was then still acting weird till I realized that it imported the regular fragment. Once I changed it to v4 fragment import all the weird quirks went away.
This might not help but hope it does.
Ok it turns out I quite misunderstood exactly how variables/values behave inside a companion object in kotlin. They are definitely static in nature. So what it seems actually happened was that when activity B finished and brought activity A back to the foreground that the detach method was setting the global companion object listener value to null so activity A could not access it.
Final Answer: make the listener interface variable a property of the instance vs a companion object member.
Thanks again for all the help and insight!

Using Fragments before they are attached

I am creating a master/detail type application. The Master view is a MasterFragment that shows a list of Master items, while the Detail view is a DetailsFragment that shows a list of Detail items.
When a user clicks a Master item in the list, I create a new DetailsFragment and show it using a transaction.
The details shown in a DetailFragment take some time to load (seconds) so I want to load them in a background thread and show the list once the loading is finished.
I now want to give the user the option to long-click a Master item, which (instead of opening it immediately and letting him wait) will create the DetailsFragment in the background (not visible yet), allowing him to browse the MasterFragment while it's loading. A navigation item is added to the Navigation Drawer so he can navigate to the DetailsFragment after some time when it has finished loading.
Think of it like using a web browser on very slow internet - instead of opening a page and waiting for it to load it is much nicer to open a page in a new tab in the background, browsing the current page some more while it loads, and then going back to the new tab when you think it must be finished loading. That's what I want to do in my app as well except with Fragments.
Now I learned that with creating Fragments it's important to use a static factory method that creates the Fragment, adds any objects as arguments to a Bundle, and then leave only an empty constructor.
public class DetailsFragment : Fragment
{
public DetailsFragment()
{
// Leave empty
}
public static DetailsFragment create(int masterId)
{
DetailsFragment f = new DetailsFragment();
Bundle args = new Bundle();
bundle.putInt("MasterId", masterId);
f.setArguments(args);
return f;
}
#Override
private void onCreate(Bundle bundle)
{
super.onCreate(bundle);
// Get master ID
int masterId = getArguments().getInt("MasterId");
// Load details in background thread
load(masterId);
}
#Background
private void load(int masterId)
{
//... (loading takes a few seconds...)
loadFinished();
}
#UiThread
private void loadFinished()
{
// update view...
}
}
(Note: I am using Android Annotations so that the 'load' method (with the #Background annotation) is run in the background. Just pretend I start it using a runner or AsyncTask or whatever.)
There is a problem here however: onCreate is not called until the Fragment is 'called upon', in other words there is no loading being done until the user opens the details fragment. I have tried onAttach instead of onCreate but the same thing happens. It seems onAttach is the first method called in the lifecycle and that is already too late.
I want the loading to start immediately, even if the Fragment is not shown yet (it may never be shown if the user doesn't navigate to it anymore).
How can I implement this behavior?
This is how I preload a fragment. Not sure it is the best way or the android way but it's the way I figured out how to do it.
In my layout xml I have the following:
<FrameLayout
android:id="#+id/master_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" />
<FrameLayout
android:id="#+id/details_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
Notice that the "details_frame" is invisible. Then when you want to preload the fragment , you replace the invisible details_frame with the fragment and it will remain invisible:
getSupportFragmentManager().beginTransaction()
.replace(R.id.details_frame, <DetailsFragment>, <FragmentName>)
.commit();
Then once you want to display it you change the visibility to visible instead of invisible.
findViewById(R.id.details_frame).setVisibilility(View.VISIBLE);

Using fragments as workers for activity

I'm trying to learn how to use a fragment as a worker for an android activity. I have the following simple xml layout for my main activity:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="#+id/update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press me" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
</LinearLayout>
I define my fragment using the following class definition:
public class UpdateTextFragment extends Fragment {
public static UpdateTextFragment newInstance() {
return new UpdateTextFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void startUpdateText() {
TextView textView = ((TextView) getActivity().findViewById(R.id.text));
textView.setText("I've been pressed!");
}
}
Then from my main activity I simply add the fragment and call the startUpdateText method of the fragment using the button's onClickListener , i.e.,
public void onClick(View arg0) {
UpdateTextFragment fragment = UpdateTextFragment.newInstance();
getFragmentManager().beginTransaction().add(fragment, "updateText").commit();
fragment.startUpdateText();
}
The code compiles and uploads to a tablet with no problems. I would expect it write the text "I've been pressed!" to the text view when the button is pressed, but the app just crashes with the standard "Unfortunately app has stopped working". I haven't implemented a class to catch this uncaught exception yet - I was hoping that it may be something obvious I'm missing or don't understand?
Thanx
Altough this is an old question I'd like to answer it as I was puzzled why your sample code didn't work.
The reason you get a NullPointerException is that you instantiate your Fragment and immediately call a method that requires to have the activity injected into the Fragment. The activity is injected by the FragmentManager / FragmentTransaction#commit method BUT this method does not evaluate the transaction immediately (as from the JavaDocs):
Schedules a commit of this transaction. The commit does
not happen immediately; it will be scheduled as work on the main thread
to be done the next time that thread is ready.
Which means
getFragmentManager().beginTransaction().add(fragment, "updateText").commit();
fragment.startUpdateText();
will yield the NPE in startUpdateText() (as the transaction was not executed yet!).
By adding the method call getFragmentManager().executePendingTransactions(); right after the commit the transaction will be performed immediately and the activity injected into the fragment. getActivity() in the Fragment now returns the Activity it was attached to and your sample works :)
As from the comments below your question: It is true that A Fragment is a re-usable 'UI-element' hosted inside an Activity. (#Stefan de Bruijn) but also that [...] a fragment is not required to be a part of the activity layout; you may also use a fragment without its own UI as an invisible worker for the activity. (as the official Android doc says).
So a Fragment is not necessarily a GUI component (i.e. the view in MVC) but also acts as a controller with its own lifetime/lifecycle.

layout with fragment and framelayout replaced by another fragment and framelayout

EDIT:
So after the comments below, I revisted and realized what was hanging me up.
Imagine my client list and client details activity be started by :
public class ClientsMainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//StudioTabOpenHelper db;
setContentView(R.layout.main_client_activity);
}
}
So this works great, starts up my main_client_Activity (defined in a layout below, and i call this activity when a button on my main screen is clicked):
Intent intent = new Intent(getActivity(), ClientsMainActivity.class);
startActivity(intent);
Easy the issue is, the ClientsMainActivity does not call a OnCreateView or anything, just sets the layout to the layout that defines my Fragment, and my ListFragment. This is fine cause I am not trying to pass anything into the ClientsMainActivity, but if I have a hypothetical activity like:
SessionMainsActivity that is called when they click on the session edit of a client, then I would not be calling the SessionsMainActivity the same way (starts activity that just sets to alayout), i would want that layout set as it defines how my fragments are split up. But I would also want to pass in data to that and then to the subsequent fragments (like which session they clicked on to be editing/mucking with.
So I wonder if the above makes sense, I am sure its a simple thing I just cannot wrap my brain around. I have no issues calling FragmentActivities from other fragments, they take up the whole screen but it works. So I guess the big issue is that ClientsMainActivity is from some example I found online for doing recipes that shows you how to make multiple fragments to a screen. The thing that gets me all that FragmentActivity does is sets the content view, to a layout that does all the work it seems, so that's why I cannot figure out how I would code it to do the same thing but let me pass in values to the fragments the layout defines etc...
END EDIT
So I am using this nice little tutorial here:
http://developer.android.com/guide/topics/fundamentals/fragments.html
It has gotten me a long way and utilizing what they say to do for the main activity, and the fragment_layout.xml, I got a nice client list on the left (Thats a listfragment) and a details fragment on the right.
Then i added the ability to edit session information on a client (or edit client details) both of which were full screen fragments. This worked great.
Now I decided my Session edit ui would best be served splitting the information up into two panes again.
This is not working as I thought, like I said I have a main_Activity that does this in the onCreate:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_client_activity);
}
with the main_client_activity.xml being defined in two layouts but the one for landscape tablets is here:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.viciousbytes.studiotab.subactivities.ClientListView"
android:id="#+id/client_list" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
<FrameLayout android:id="#+id/client_details" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground"/>
</LinearLayout>
This all works great! In which case I handled everything else as a full screen activity that started its own fragment:
EditSessionActivity
EditClientActiivyt both of which use getSupportFragmentManager().beginTransaction and I could pass information into it from the .newInstance call.
I had my session_edit.xml layout defined with buttons, textviews etc..and that was working great. Thats what i loaded in my SessionEdit fragment "launched" by my EditSessionActivity But now since I want to split it apart I ran into a snag. Above I defined a client_list and a client_details id, are these placeholders on my screen? do I reference those when I wanna replace whats there with totally different fragments?
or do i build another fragment layout called something like fragment_session_layout which defines something like:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.viciousbytes.studiotab.subactivities.SessionEdit"
android:id="#+id/session_edit" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
<FrameLayout android:id="#+id/invoice_details" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground" />
</LinearLayout>
Sorry don't know what to title this on the tip of my tongue of what I am asking, basically how to get two panes of fragments twice over. THe demo online shows how to do one (and a simple ListFragment at that).
I have done all the above and I just cannot figure out how to pass into the fragment the data I need, I was using this in my EditSessionActivity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int clientID = getIntent().getIntExtra(THE_SELECTED_CLIENT, -1);
int sessionID = getIntent().getIntExtra(SELECTED_SESSION,-1);
SessionEdit edits = SessionEdit.newInstance(this.getBaseContext(), false, clientID, sessionID);
mUIListener = (OnUpdateUI)edits;
getSupportFragmentManager().beginTransaction().add(android.R.id.content, edits).commit();
}
that worked, but to try to adhere to the earlier fragment example, i assumed my EditSessionActivity was sorta like making another MainActivity (cause it has two panels in it like the main one). so I recoded the onCreate in EditSessionActivity with this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_session_fullview);
}
Which after fighting in my EditSession fragment dealing with the onCreateView, I got this to finally work but couldn't pass in the data cause there is no instantiation of the object using like the previous onCreate which had the edits=SessionEdit.newInstance(...)
So is it good practice to be doing the other fragment layout that has two pains in it and starting that up when the right action is triggered. Or is one supposed to replace the two already created fragments?? from my main_client_activity.xml somehow?
I assume editing clients and editing sessions are two distinct activities. When you switch from "editing clients" to "editing sessions" mode, both the "list" and "details" panes would change?
I would go with two layout files, instead of trying to reuse the same layout and reload fragments in it.
If you tried to reuse the same layout, you would have to:
Change #+id/invoice_details to something like #+id/right_pane. Otherwise it would look confusing to load something related to sessions into "invoice_details" placeholder.
replace fragment definition with another FrameLayout and load either ClientListView or SessionListView (or however it's called) fragment there at runtime.
This would add more complexity than having another layout xml file in my opinion.
So
Take your existing code that works with client list and client details
Duplicate all involved parts, and change what needs to be changed so it's now session list and session details
Remove duplication where it's easy to do (common functions go to utility classes, common layout elements to layout includes). Leave the things that are hard to de-duplicate as-is.
Re-evaluate later, when you have more fragments, more layouts and more experience.
UPDATE, about two different approaches fragments can be embedded in activity
As the Android documentation states, there are two main ways you can get a fragment to show up in your activity:
declare the fragment in layout's XML file (<fragment class=.... />)
put a placeholder FrameLayout in layout's XML file and load fragment at runtime
First approach is fine when fragment doesn't need to receive any arguments. Like, for example, if the logic to retrieve single and only list of clients is hardcoded in fragment's code.
Second approach lets you pass arguments to the fragment, and therefore is appropriate for "details drilldown" type of fragments.
From updated question I understand that,
each client has a separate list of sessions
the components in play are: EditSessionActivity that hosts two fragments, one for displaying list of sessions, another for displaying session details
If that's correct, then indeed you'd need to load both fragments programmatically because both needs parameters to be passed to. So your layout would have two FrameLayouts. The EditSessionActivity would start with getting some parameters from intent ("which list of sessions are we working with?"), and load "list of sessions" fragment with these parameters. When user selects list item in that fragment, the other fragment would be loaded with session details.

Can I update a list view in one activity while im in another activity?

I currently have a tab layout with 2 tabs, one tab with a list view and one with the option make strings so I can add them in the list view. Both tabs have their own activity because this made the code much more structured, and I dont have to repeat my self later.
Lets say im in the tab that offer me to create an string, and i press the update list button, how do I update the list view without startActivity()? If i use startActivity(), it starts List.java, and instead of displaying the list in the list view tab, it takes full screen, which defies the purpose of the tab view. In other words, the startActivity() steals the focus from the tab view of the list, and sends it fulscreen.
Thanks for your time.
Edit: I want to update the activity in my list view tab, without starting a new activity that goes to fullscreen, and doesnt update the one in the tab.
One solution is to have your data model separate from your view (Activity), this is good practice in general, but in this case allows your two tabs to interact with the same model.
You could provide access to the data model in your Application class. Then, when onResume is called on your list activity you can update the view based on the current data.
edit:
public class MyApplication extends Application {
private List<MyObject> myData;
public List<MyObject> getMyData() {
return myData;
}
public void setMyData(List<MyObject> mydata) {
this.myData = myData;
}
}
In your manifest you need to add the application tag to specify that it should use MyApplication as the application class.
Then, in your activity:
public void onResume() {
MyApplication app = (MyApplication) getApplication();
List<MyData> data = app.getMyData();
// update my view with the data
}
I had the same problem when i was making my app and the only solution i found was to add this flag to my intent so every time i go to the tab it would refresh it self
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
I hope it helps.

Categories

Resources