Android Studio Problem: W/RecyclerView: No adapter attached; skipping layout [duplicate] - java

Just implemented RecyclerView in my code, replacing ListView.
Everything works fine. The data is displayed.
But error messages are being logged:
15:25:53.476 E/RecyclerView: No adapter attached; skipping layout
15:25:53.655 E/RecyclerView: No adapter attached; skipping layout
for the following code:
ArtistArrayAdapter adapter = new ArtistArrayAdapter(this, artists);
recyclerView = (RecyclerView) findViewById(R.id.cardList);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
As you can see I have attached an adapter for RecyclerView.
So why do I keep getting this error?
I have read other questions related to the same problem but none of them help.

Can you make sure that you are calling these statements from the "main" thread outside of a delayed asynchronous callback (for example inside the onCreate() method).
As soon as I call the same statements from a "delayed" method. In my case a ResultCallback, I get the same message.
In my Fragment, calling the code below from inside a ResultCallback method produces the same message. After moving the code to the onConnected() method within my app, the message was gone...
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
list.setLayoutManager(llm);
list.setAdapter( adapter );

I was getting the same two error messages until I fixed two things in my code:
(1) By default, when you implement methods in the RecyclerView.Adapter it generates:
#Override
public int getItemCount() {
return 0;
}
Make sure you update your code so it says:
#Override
public int getItemCount() {
return artists.size();
}
Obviously if you have zero items in your items then you will get zero things displayed on the screen.
(2) I was not doing this as shown in the top answer: CardView layout_width="match_parent" does not match parent RecyclerView width
//correct
LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_listitem, parent, false);
//incorrect (what I had)
LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_listitem,null);
(3) EDIT: BONUS:
Also make sure you set up your RecyclerView like this:
<android.support.v7.widget.RecyclerView
android:id="#+id/RecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
NOT like this:
<view
android:id="#+id/RecyclerView"
class="android.support.v7.widget.RecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
I have seen some tutorials using the latter method. While it works I think it generates this error too.

I have the same situation with you, display is ok, but error appear in the locat.
That's my solution:
(1) Initialize the RecyclerView & bind adapter ON CREATE()
RecyclerView mRecycler = (RecyclerView) this.findViewById(R.id.yourid);
mRecycler.setAdapter(adapter);
(2) call notifyDataStateChanged when you get the data
adapter.notifyDataStateChanged();
In the recyclerView's source code, there is other thread to check the state of data.
public RecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mObserver = new RecyclerView.RecyclerViewDataObserver(null);
this.mRecycler = new RecyclerView.Recycler();
this.mUpdateChildViewsRunnable = new Runnable() {
public void run() {
if(RecyclerView.this.mFirstLayoutComplete) {
if(RecyclerView.this.mDataSetHasChangedAfterLayout) {
TraceCompat.beginSection("RV FullInvalidate");
RecyclerView.this.dispatchLayout();
TraceCompat.endSection();
} else if(RecyclerView.this.mAdapterHelper.hasPendingUpdates()) {
TraceCompat.beginSection("RV PartialInvalidate");
RecyclerView.this.eatRequestLayout();
RecyclerView.this.mAdapterHelper.preProcess();
if(!RecyclerView.this.mLayoutRequestEaten) {
RecyclerView.this.rebindUpdatedViewHolders();
}
RecyclerView.this.resumeRequestLayout(true);
TraceCompat.endSection();
}
}
}
};
In the dispatchLayout(), we can find there is the error in it:
void dispatchLayout() {
if(this.mAdapter == null) {
Log.e("RecyclerView", "No adapter attached; skipping layout");
} else if(this.mLayout == null) {
Log.e("RecyclerView", "No layout manager attached; skipping layout");
} else {

i have this problem , a few time problem is recycleView put in ScrollView object
After checking implementation, the reason appears to be the following. If RecyclerView gets put into a ScrollView, then during measure step its height is unspecified (because ScrollView allows any height) and, as a result, gets equal to minimum height (as per implementation) which is apparently zero.
You have couple of options for fixing this:
Set a certain height to RecyclerView
Set ScrollView.fillViewport to true
Or keep RecyclerView outside of ScrollView. In my opinion, this is the best option by far. If RecyclerView height is not limited - which is the case when it's put into ScrollView - then all Adapter's views have enough place vertically and get created all at once. There is no view recycling anymore which kinda breaks the purpose of RecyclerView .
(Can be followed for android.support.v4.widget.NestedScrollView as well)

1) Create ViewHolder that does nothing :)
// SampleHolder.java
public class SampleHolder extends RecyclerView.ViewHolder {
public SampleHolder(View itemView) {
super(itemView);
}
}
2) Again create RecyclerView that does nothing :)
// SampleRecycler.java
public class SampleRecycler extends RecyclerView.Adapter<SampleHolder> {
#Override
public SampleHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
#Override
public void onBindViewHolder(SampleHolder holder, int position) {
}
#Override
public int getItemCount() {
return 0;
}
}
3) Now when your real recycler is not ready just use the sample one like below.
RecyclerView myRecycler = (RecyclerView) findViewById(R.id.recycler_id);
myRecycler.setLayoutManager(new LinearLayoutManager(this));
myRecycler.setAdapter(new SampleRecycler());
This is not best solution though but it works! Hope this is helpful.

It happens when you are not setting the adapter during the creation phase:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
....
}
public void onResume() {
super.onResume();
mRecyclerView.setAdapter(mAdapter);
....
}
Just move setting the adapter into onCreate with an empty data and when you have the data call:
mAdapter.notifyDataSetChanged();

Check if you have missed to call this method in your adapter
#Override
public int getItemCount() {
return list.size();
}

In Kotlin we had this weird illogical issue.
This didn't work:
mBinding.serviceProviderCertificates.apply {
adapter = adapter
layoutManager = LinearLayoutManager(activity)
}
While this worked:
mBinding.serviceProviderCertificates.adapter = adapter
mBinding.serviceProviderCertificates.layoutManager = LinearLayoutManager(activity)
Once I get more after work hours, I will share more insights.

Make sure you set the layout manager for your RecyclerView by:
mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
Instead of LinearLayoutManager, you can use other layout managers too.

ArtistArrayAdapter adapter = new ArtistArrayAdapter(this, artists);
recyclerView = (RecyclerView) findViewById(R.id.cardList);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
Just replace above code with this and it should work. What you did wrong is you called setAdapter(adapter) before calling layout manager.

I had the same error I fixed it doing this if you are waiting for data like me using retrofit or something like that
Put before Oncreate
private ArtistArrayAdapter adapter;
private RecyclerView recyclerView;
Put them in your Oncreate
recyclerView = (RecyclerView) findViewById(R.id.cardList);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new ArtistArrayAdapter( artists , R.layout.list_item ,getApplicationContext());
recyclerView.setAdapter(adapter);
When you receive data put
adapter = new ArtistArrayAdapter( artists , R.layout.list_item ,getApplicationContext());
recyclerView.setAdapter(adapter);
Now go in your ArtistArrayAdapter class and do this what it will do is if your array is empty or is null it will make GetItemCount return 0 if not it will make it the size of artists array
#Override
public int getItemCount() {
int a ;
if(artists != null && !artists.isEmpty()) {
a = artists.size();
}
else {
a = 0;
}
return a;
}

For those who use the RecyclerView within a fragment and inflate it from other views: when inflating the whole fragment view, make sure that you bind the RecyclerView to its root view.
I was connecting and doing everything for the adapter correctly, but I never did the binding.
This answer by #Prateek Agarwal has it all for me, but here is more elaboration.
Kotlin
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView = inflater?.inflate(R.layout.fragment_layout, container, false)
recyclerView = rootView?.findViewById(R.id.recycler_view_id)
// rest of my stuff
recyclerView?.setHasFixedSize(true)
recyclerView?.layoutManager = viewManager
recyclerView?.adapter = viewAdapter
// return the root view
return rootView
}
Java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView= inflater.inflate(R.layout.fragment_layout,container,false);
recyclerview= rootView.findViewById(R.id.recycler_view_id);
return rootView;
}

These Lines must be in OnCreate:
mmAdapter = new Adapter(msgList);
mrecyclerView.setAdapter(mmAdapter);

This happens because the actual inflated layout is different from that which is being referred by you while finding the recyclerView. By default when you create the fragment, the onCreateView method appears as follows:
return inflater.inflate(R.layout.<related layout>,container.false);
Instead of that, separately create the view and use that to refer to recyclerView
View view= inflater.inflate(R.layout.<related layout>,container.false);
recyclerview=view.findViewById(R.id.<recyclerView ID>);
return view;

In my layout xml file, the bottom line with layoutManager was missing. The error disappeared after I added it.
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view_chat"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layoutManager="LinearLayoutManager"/>

First initialize the adapter
public void initializeComments(){
comments = new ArrayList<>();
comments_myRecyclerView = (RecyclerView) findViewById(R.id.comments_recycler);
comments_mLayoutManager = new LinearLayoutManager(myContext);
comments_myRecyclerView.setLayoutManager(comments_mLayoutManager);
updateComments();
getCommentsData();
}
public void updateComments(){
comments_mAdapter = new CommentsAdapter(comments, myContext);
comments_myRecyclerView.setAdapter(comments_mAdapter);
}
When ever there is a change in the dataset set, just call the updateComments method.

I had this error, and I tried to fix for a while until I found the solution.
I made a private method buildRecyclerView, and I called it twice, first on onCreateView and then after my callback (in which I fetch data from an API). This is my method buildRecyclerView in my Fragment:
private void buildRecyclerView(View v) {
mRecyclerView = v.findViewById(R.id.recycler_view_loan);
mLayoutManager = new LinearLayoutManager(getActivity());
((LinearLayoutManager) mLayoutManager).setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new LoanAdapter(mExampleList);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
}
Besides, I have to modify the method get-Item-Count in my adapter, because On on-Create-View the list is null and it through an error. So, my get-Item-Count is the following:
#Override
public int getItemCount() {
try {
return mLoanList.size();
} catch (Exception ex){return 0;}
}

This is really a simple error you are getting, there in no need of doing any codes in this.
This error occurs due to the wrong layout file used by the activity. By IDE i automatically created a layout v21 of a layout which became a default layout of the activity.
all codes I did in the old layout file and new one was only having few xml codes, which led to that error.
Solution: Copy all codes of old layout and paste in layout v 21

In my case, I was setting the adapter inside onLocationChanged() callback AND debugging in the emulator. Since it didn't detected a location change it never fired. When I set them manually in the Extended controls of the emulator it worked as expected.

I have solved this error. You just need to add layout manager
and add the empty adapter.
Like this code:
myRecyclerView.setLayoutManager(...//your layout manager);
myRecyclerView.setAdapter(new RecyclerView.Adapter() {
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
#Override
public int getItemCount() {
return 0;
}
});
//other code's
// and for change you can use if(mrecyclerview.getadapter != speacialadapter){
//replice your adapter
//}

Just add the following to RecyclerView
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
Example:
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</android.support.v7.widget.RecyclerView>

In case you're getting still error while using ViewBinding, make sure you're using the binding to return the inflated view ie
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return fragmentBinding.getRoot();
}

In my situation it was a forgotten component which locates in ViewHolder class but it wasn't located in layout file

I had the same problem and realized I was setting both the LayoutManager and adapter after retrieving the data from my source instead of setting the two in the onCreate method.
salesAdapter = new SalesAdapter(this,ordersList);
salesView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
salesView.setAdapter(salesAdapter);
Then notified the adapter on data change
//get the Orders
Orders orders;
JSONArray ordersArray = jObj.getJSONArray("orders");
for (int i = 0; i < ordersArray.length() ; i++) {
JSONObject orderItem = ordersArray.getJSONObject(i);
//populate the Order model
orders = new Orders(
orderItem.getString("order_id"),
orderItem.getString("customer"),
orderItem.getString("date_added"),
orderItem.getString("total"));
ordersList.add(i,orders);
salesAdapter.notifyDataSetChanged();
}

This issue is because you are not adding any LayoutManager for your RecyclerView.
Another reason is because you are calling this code in a NonUIThread. Make sure to call this call in the UIThread.
The solution is only you have to add a LayoutManager for the RecyclerView before you setAdapter in the UI Thread.
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

Solved by setting the initialized empty list and adapter at the bottom and calling notifyDataSetChanged when results are fetched.
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
recyclerviewItems.setLayoutManager(linearLayoutManager);
someAdapter = new SomeAdapter(getContext(),feedList);
recyclerviewItems.setAdapter(someAdapter);

I lost 16 minutes of my life with this issue, so I'll just admit to this incredibly embarrassing mistake that I was making- I'm using Butterknife and I bind the view in onCreateView in this fragment.
It took a long time to figure out why I had no layoutmanager - but obviously the views are injected so they won't actually be null, so the the recycler will never be null .. whoops!
#BindView(R.id.recycler_view)
RecyclerView recyclerView;
#Override
public View onCreateView(......) {
View v = ...;
ButterKnife.bind(this, v);
setUpRecycler()
}
public void setUpRecycler(Data data)
if (recyclerView == null) {
/*very silly because this will never happen*/
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
//more setup
//...
}
recyclerView.setAdapter(new XAdapter(data));
}
If you are getting an issue like this trace your view and use something like uiautomatorviewer

In my case it happened cause i embedded a RecyclerView in a LinearLayout.
I previously had a layout file only containing one root RecyclerView as follows
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/fragment_products"
android:name="Products.ProductsFragment"
app:layoutManager="LinearLayoutManager"
tools:context=".Products.ProductsFragment"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"/>
I believe the problem is within the 3 lines separated. Anyway, I think its a simple problem, ill be working on it tomorrow; thought i should write what i found before forgetting about this thread.

Adding yet another answer since I came across this thread googling the error. I was trying to initialize a PreferenceFragmentCompat but I forgot to inflate the preference XML in onCreatePreferences like this:
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.fragment_settings, null)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
// Missing this line here:
// setPreferencesFromResource(R.xml.settings, rootKey)
}
}
The error was a mystery until I realized that PreferenceFragmentCompat must be using a RecyclerView internally.

// It happens when you are not setting the adapter during the creation phase: call notifyDataSetChanged() when api response is getting Its Working
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
magazineAdapter = new MagazineAdapter(getContext(), null, this );
newClipRecyclerView.setAdapter(magazineAdapter);
magazineAdapter.notifyDataSetChanged();
APICall();
}
public void APICall() {
if(Response.isSuccessfull()){
mRecyclerView.setAdapter(mAdapter);
}
}
Just move setting the adapter into onCreate with an empty data and when you have the data call:
mAdapter.notifyDataSetChanged();

Related

ViewPager2 not working properly with Fragments and click events

I just wanna know if I'm doing something wrong since I'm kinda new to all this.
If there is anything else that you'll like me to add just let me know.
This is the repo branch where I'm trying to implement the ViewPager if you wanna see all the code.
Context
So I have 4 Categories represented with Fragments, each of this categories holds an ArrayList of items that each has a onItemClickListener that should reproduce some audio.
I'm trying to display the Fragments with a ViewPager but the problem is that when I scroll from a Fragment to another, then come back to the already created Fragment, it doesnt register the touch event, nothing happens, not even an error nor exception.
If I go to a newly created Fragment the touch works just fine.
Also, after switching back to an already created Fragment if I scroll even just a little bit to another Fragment and comeback or through the ArrayList of that Fragment for some reason it starts to recognize the touch in the ArrayList items again.
Similar questions that didn't really help
Fragments in ViewPager2 does not respond to clicks if scroll position is 0
ViewPager2 conflicting with SwipeRefreshLayout
Android ViewPager2 with fragment containing a recyclerview not scrolling
What I've tried
I tried to use a coordinatorlayout wrapping the ViewPager2 but there is no difference
I've been reading some of the official viewPager2 examples that are written in Kotlin but none of them seem to have a similar situation (also it's hard for me to read Kotlin code)
Code Snippets
word_list.xml:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/tan_background" />
activity_main.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"/>
</FrameLayout>
This is one of the Fragments, the other three are basically the same, just the items in the arrayList change and some other minor things:
// ...Skipped some irrelevant code...
public class NumbersFragment extends Fragment {
private ArrayList<Word> mWords;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.word_list, container, false);
mWords = new ArrayList<>();
// ...Add all the items to the list...
// Make the adapter for the word items
WordAdapter adapter = new WordAdapter(getActivity(), mWords, R.color.category_numbers);
// Find the root view of the list
ListView listView = rootView.findViewById(R.id.root_list_view);
// Add adapter to the root list view
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d("NumbersFragment", "CLICKED");
}
}
});
return rootView;
}
#Override
public void onPause() {
super.onPause();
Log.d("NumbersFragment", "Fragment paused");
}
}
This is the Category adapter, it manages the fragments:
public class CategoryAdapter extends FragmentStateAdapter {
private static final int NUM_CATEGORIES = 4;
// Required public constructor
public CategoryAdapter(#NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
#NonNull
#Override
public Fragment createFragment(int position) {
// Depending on which page the user is in,
// create a fragment of the corresponding category
switch (position) {
case 0:
return new NumbersFragment();
case 1:
return new FamilyFragment();
case 2:
return new ColorsFragment();
default:
return new PhrasesFragment();
}
}
#Override
public int getItemCount() {
return NUM_CATEGORIES;
}
}
And this is my MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the content of the activity to use the activity_main.xml layout file
setContentView(R.layout.activity_main);
// Find the view pager that will allow the user to swipe between fragments
ViewPager2 viewPager = findViewById(R.id.viewpager);
// Create an adapter that knows which fragment should be shown on each page
CategoryAdapter adapter = new CategoryAdapter(this);
//or CategoryAdapter adapter = new CategoryAdapter(getSupportFragmentManager(), getLifecycle());
// Set the adapter into the view pager
viewPager.setAdapter(adapter);
}
}
add this in your MainActivity viewPager.setOffscreenPageLimit(3); after creating viewpager
It’s because the ViewPager has a default offscreen limit of 1 ,and ViewPager2 has a default offscreen limit of 0.
In ViewPager2 when you switch tabs the previous tab will be automatically refreshed.
in ViewPager if you have 3 tabs or more when you switch to 3rd tab automatically first one will be destroyed and when you goes to 1st tab it will be recreated.
viewPager.setOffscreenPageLimit(3); from this line when you switch to a tab,the previous 3 tabs will be preloaded and next 3 tabs will be preloaded so nothing will be refreshed.

Is initializing a new RecyclerView Adapter an asynchronous call?

My question is, is the initialization of a new RecyclerView adapter an asynchronous call?
I have an adapter that I am creating:
mRecyclerAdapter = new TestAdapter(mContext, mListImages);
mRecycler.setLayoutManager(mLayoutManager);
mRecycler.setAdapter(mRecyclerAdapter);
After initializing it, I can call .add() directly after these methods without calling .notifyDataSetChanged() and they would still be added to my adapter, and displayed.
mRecyclerAdapter = new TestAdapter(mContext, mListImages);
mRecycler.setLayoutManager(mLayoutManager);
mRecycler.setAdapter(mRecyclerAdapter);
mListImages.add( . . .);
mListImages.add( . . .);
mListImages.add( . . .);
Are RecyclerView adapters automatically initialized on a background thread?
Here is my adapter:
public class SelectBucketAdapter extends RecyclerView.Adapter<SelectBucketAdapter.ViewHolder> {
private static final String TAG = "SelectBucketAdapter";
private Context mContext;
private ArrayList<String> mBucketList;
public SelectBucketAdapter(Context mContext, ArrayList<String> mBucketList,
) {
this.mContext = mContext;
this.mBucketList = mBucketList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.vh_selectbucketmenu_layout, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int i) {
... binding views
}
#Override
public int getItemCount() {
return mBucketList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
#BindView(R.id.vh_selectbucketmenu_name)
TextView vhBucketName;
int mPosition;
public ViewHolder(#NonNull View itemView) {
super(itemView);
}
}
}
Are RecyclerView adapters automatically initialized on a background thread?
No, they are not.
Is the initialization of a new RecyclerView adapter an asynchronous call?
No, it is not.
The layout creation and attachment to window is async.
What this means?
Assume we have following code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
val adapter = MyAdapter()
recyclerView.adapter = adapter
adapter.list.add("1")
}
In this case we will see the "1" being displayed on the screen, because at the point when adapter.list.add("1") was executed RecyclerView hasn't yet passed through its measure-layout-draw cycle.
Now let's consider following code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
val adapter = MyAdapter()
recyclerView.adapter = adapter
Handler().postDelayed({ adapter.list.add("AAA") }, 2000)
}
In this case adapter.list.add("AAA") will be executed in roughly 2 seconds. As long as RecyclerView will already be laid out by that time, then mutating the adapter dataset won't make the RecyclerView show the item, because RecyclerView doesn't know if dataset has suffered a change.
Let's consider following case:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
val adapter = MyAdapter()
recyclerView.adapter = adapter
recyclerView.doOnPreDraw { adapter.list.add("preDraw") }
recyclerView.doOnLayout { adapter.list.add("layout") }
adapter.list.add("onCreate")
}
In this case still only "onCreate" will be displayed on screen.
To sum up: as soon as RecyclerView has passed its measure step (i.e. View#onMeasure) then mutating adapter won't be reflected unless adapter explicitly notifies RecyclerView.
The term 'intiializing' is quite ambiguous. At what point do you consider the adapter 'intiialized'?. To me, an Adapter can be intiialized with 0 items. So you can't really measure whether the Adapter has been intiialized by looking at the contents of the RecyclerView.
Secondly, you're asking if 'intiializing is an asynchronous call'. The 'initialization' of a RecyclerView Adapter is a whole bunch of calls. What you've observed is that the result of these calls is not always immediately visible - which tells you that at least some of what is happening behind the scenes is asynchronous.
I think what you're trying to ask is 'at what point in the lifecycle of a RecyclerView are you required to notify the Adapter of changes'. And it sounds like the answer is 'once the RecyclerView has reached onMeasure() (based on #azizbekian's answer).
If you want to add items to the Adapter without having to call notifyDataSetChanged(), then I would suggest adding them before calling RecyclerView.setAdapter(). Once you've set the Adapter, any further changes you make to the Adapter's dataset should be followed with a notifyDataSetChanged() call (or preferably, one of the more specific notifyX() calls).

Why my RecyclerView updated while I don't notify the adapter?

I have some trouble to understand why my recyclerView get update while I add the data only after setAdapter() and never call notifyDataSetChanged() !
In the same type:
-> I add 3 String to my list
-> Notify the adapter only for the first added (notifyItemInserted)
Result: 3 String displayed.
If anyone can help me understand I will be very grateful to him :)
https://www.noelshack.com/2018-04-5-1516969446-recycler.png
my code:
public class MainActivity extends AppCompatActivity {
List<String> mList = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Call before set adapter so it's normal if show when the adapter is set
mList.add("Before set the adapter");
RecyclerView recyclerView = findViewById(R.id.rcv);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
final Adapter mAdapter = new Adapter(mList);
recyclerView.setAdapter(mAdapter);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mList.add("Button add " +0);
mAdapter.notifyItemInserted(mList.size()-1);
//Again it's showing but never notify
mList.add("Button add " +1);
mList.add("Button add " +2);
}
});
//Called after setAdapter and NEVER notify the adapter but it's still show the data
addMoreData();
}
private void addMoreData() {
for (int i = 0; i < 10; i++) {
mList.add("addMoreData: " + i);
}
}
class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
class ViewHolder extends RecyclerView.ViewHolder {
TextView nameTextView;
ViewHolder(View itemView) {
super(itemView);
nameTextView = (TextView) itemView.findViewById(R.id.textview);
}
}
#Override
public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.raw, parent, false);
return new Adapter.ViewHolder(view);
}
private List<String> mList;
Adapter(List<String> list) {
mList = list;
}
#Override
public void onBindViewHolder(Adapter.ViewHolder holder, int position) {
holder.nameTextView.setText(mList.get(position));
}
#Override
public int getItemCount() {
return mList.size();
}
}
}
I think, because of notifyDataSetChanged's working principle, this is the result.
A Google engineer said ;
When you call notifyDataSetChanged, RecyclerView invalidates the data
but does not update the UI until the next animation frame. This is how
android view system works. When a widget is invalidated (e.g. changing
its data) it requests a layout which means it will be re-measured and
re-laid out in the next view traversal. This is done so that we can
batch all changes until the next time screen will be updated.
That means; nofiyDataSetChanged waits the UI Thread. So, doesn't matter the below lines of the notifyItemInserted from your code.
mList.add("Button add " +0);
mAdapter.notifyItemInserted(mList.size()-1);
//Again it's showing but never notify
mList.add("Button add " +1);
mList.add("Button add " +2);
We mostly use adapter notify calls to keep views that are currently shown in their proper state, it does not prevent adapter from serving the data to RecyclerView component.
You can see it very clearly, if your RecyclerView has scroll - update the list data without notifying it and then scroll to the bottom you will get your new items laid out.
In your case you add item on the end and notify only for it - but LayoutManager determines it can still lay out more views so it does.

Android getListView() in fragment error

I keep having an issue with my android app where it is crashing with the following error when swiping between tabs:
09-16 16:19:27.142 4750-4750/com.khackett.runmate E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.khackett.runmate, PID: 4750
java.lang.IllegalStateException: Content view not yet created
at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
at com.khackett.runmate.ui.MyRunsFragment$1.done(MyRunsFragment.java:167)
at com.khackett.runmate.ui.MyRunsFragment$1.done(MyRunsFragment.java:135)
at com.parse.ParseTaskUtils$2$1.run(ParseTaskUtils.java:115)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
This is the MyRunsFragment:
public class MyRunsFragment extends ListFragment {
protected SwipeRefreshLayout mSwipeRefreshLayout;
// member variable to store the list of routes the user has accepted
protected List<ParseObject> mAcceptedRoutes;
private int MY_STATUS_CODE = 1111;
// Default constructor for MyRunsFragment
public MyRunsFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_my_runs, container, false);
// Set SwipeRefreshLayout component
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeRefreshLayout);
// Set the onRefreshListener
mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
mSwipeRefreshLayout.setColorSchemeResources(
R.color.swipeRefresh1,
R.color.swipeRefresh2,
R.color.swipeRefresh3,
R.color.swipeRefresh4);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Retrieve the accepted routes from the Parse backend
retrieveAcceptedRoutes();
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
// create the message object which is set to the message at the current position
ParseObject route = mAcceptedRoutes.get(position);
// String messageType = message.getString(ParseConstants.KEY_FILE_TYPE);
JSONArray parseList = route.getJSONArray(ParseConstants.KEY_LATLNG_POINTS);
JSONArray parseListBounds = route.getJSONArray(ParseConstants.KEY_LATLNG_BOUNDARY_POINTS);
String objectId = route.getObjectId();
String routeName = route.getString(ParseConstants.KEY_ROUTE_NAME);
// JSONArray ids = route.getJSONArray(ParseConstants.KEY_RECIPIENT_IDS);
// Start a map activity to display the route
Intent intent = new Intent(getActivity(), MapsActivityTrackRun.class);
intent.putExtra("parseLatLngList", parseList.toString());
intent.putExtra("parseLatLngBoundsList", parseListBounds.toString());
intent.putExtra("myRunsObjectId", objectId);
intent.putExtra("myRunsRouteName", routeName);
// Start the MapsActivityDisplayRoute activity
startActivityForResult(intent, MY_STATUS_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
private void retrieveAcceptedRoutes() {
// query the routes class/table in parse
// get messages where the logged in user ID is in the list of the recipient ID's (we only want to retrieve the messages sent to us)
// querying the message class is similar to how we have been querying users
ParseQuery<ParseObject> queryRoute = new ParseQuery<ParseObject>(ParseConstants.CLASS_ROUTES);
// use the 'where' clause to search through the messages to find where our user ID is one of the recipients
queryRoute.whereEqualTo(ParseConstants.KEY_ACCEPTED_RECIPIENT_IDS, ParseUser.getCurrentUser().getObjectId());
// order results so that most recent message are at the top of the inbox
queryRoute.addDescendingOrder(ParseConstants.KEY_CREATED_AT);
// query is ready - run it
queryRoute.findInBackground(new FindCallback<ParseObject>() {
// When the retrieval is done from the Parse query, the done() callback method is called
#Override
public void done(List<ParseObject> routes, ParseException e) {
// dismiss the progress indicator here
// getActivity().setProgressBarIndeterminateVisibility(false);
// End refreshing once routes are retrieved
// done() is called from onResume() and the OnRefreshListener
// Need to check that its called from the the OnRefreshListener before ending it
if (mSwipeRefreshLayout.isRefreshing()) {
mSwipeRefreshLayout.setRefreshing(false);
}
// the list being returned is a list of routes
if (e == null) {
// successful - routes found. They are stored as a list in messages
mAcceptedRoutes = routes;
// adapt this data for the list view, showing the senders name
// create an array of strings to store the usernames and set the size equal to that of the list returned
String[] usernames = new String[mAcceptedRoutes.size()];
// enhanced for loop to go through the list of users and create an array of usernames
int i = 0;
for (ParseObject message : mAcceptedRoutes) {
// get the specific key
usernames[i] = message.getString(ParseConstants.KEY_SENDER_NAME);
i++;
}
// Create the adapter once and update its state on each refresh
if (getListView().getAdapter() == null) {
// the above adapter code is now replaced with the following line
RouteMessageAdapter adapter = new RouteMessageAdapter(getListView().getContext(), mAcceptedRoutes);
// Force a refresh of the list once data has changed
adapter.notifyDataSetChanged();
// need to call setListAdapter for this activity. This method is specifically from the ListActivity class
setListAdapter(adapter);
} else {
// refill the adapter
// cast it to RouteMessageAdapter
((RouteMessageAdapter) getListView().getAdapter()).refill(mAcceptedRoutes);
}
}
}
});
}
protected SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
// When list is swiped down to refresh, retrieve the users runs from the Parse backend
retrieveAcceptedRoutes();
}
};
}
And the fragment_my_runs layout file:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity$PlaceholderFragment">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:clipToPadding="false"
android:paddingBottom="#dimen/inbox_vertical_margin"/>
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/empty_inbox_label"
android:textSize="#dimen/default_text_size"/>
</RelativeLayout>
The TabFragmentContainer
public class TabFragmentContainer extends Fragment {
// Create the FragmentPagerAdapter that will provide and manage tabs for each section.
public static MyFragmentPagerAdapter myFragmentPagerAdapter;
public static TabLayout tabLayout;
// The ViewPager is a layout widget in which each child view is a separate tab in the layout.
// It will host the section contents.
public static ViewPager viewPager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate tab_layout_fragment_container view and setup views for the TabLayout and ViewPager items.
View view = inflater.inflate(R.layout.tab_layout_fragment_container, null);
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
// Set up the ViewPager with the sections adapter.
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
// Instantiate the adapter that will return a fragment for each of the three sections of the main activity
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity(), getChildFragmentManager());
// Set up the adapter for the ViewPager
viewPager.setAdapter(myFragmentPagerAdapter);
// Runnable() method required to implement setupWithViewPager() method
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem(1, false);
// tabLayout.getTabAt(1).select();
}
});
// Return the created View
return view;
}
}
The FragmentPagerAdapter:
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
// The context to be passed in when the adapter is created.
private Context mContext;
// The number of tabs in the layout.
public static int numberOfTabs = 3;
/**
* Default constructor that accepts a FragmentManager parameter to add or remove fragments.
*
* #param context the context from the activity using the adapter.
* #param fragmentManager the FragmentManager for managing Fragments inside of the TabFragmentContainer.
*/
public MyFragmentPagerAdapter(Context context, FragmentManager fragmentManager) {
super(fragmentManager);
mContext = context;
}
/**
* Method to return the relevant fragment for the selected tab.
*/
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new MyRunsFragment();
case 1:
return new InboxRouteFragment();
case 2:
return new FriendsFragment();
}
return null;
}
/**
* Method that gets the number of tabs in the layout.
*
* #return the number of tabs in the layout.
*/
#Override
public int getCount() {
return numberOfTabs;
}
/**
* Method that returns the title of each tab in the layout.
*/
#Override
public CharSequence getPageTitle(int position) {
Locale locale = Locale.getDefault();
switch (position) {
case 0:
return mContext.getString(R.string.title_section1).toUpperCase(locale);
case 1:
return mContext.getString(R.string.title_section2).toUpperCase(locale);
case 2:
return mContext.getString(R.string.title_section3).toUpperCase(locale);
}
return null;
}
}
The tab_layout_fragment_container file that contains the ViewPager widget:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/ColorPrimaryPurple"
app:tabGravity="fill"
app:tabIndicatorColor="#color/ColorPrimaryPurple"
app:tabMode="fixed"
app:tabSelectedTextColor="#color/textColorPrimary"
app:tabTextColor="#color/pressedPurpleButton">
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
</LinearLayout>
The onCreate() method in my MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialise the DrawerLayout and NavigationView views.
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
mNavigationView = (NavigationView) findViewById(R.id.navigationDrawerMenu);
// Inflate the first fragment to be displayed when logged into the app.
mFragmentManager = getSupportFragmentManager();
mFragmentTransaction = mFragmentManager.beginTransaction();
mFragmentTransaction.replace(R.id.containerView, new TabFragmentContainer()).commit();
// Setup click events on the NavigationView items.
// When an item is selected, replace the tab fragment container with the requested fragment.
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
mDrawerLayout.closeDrawers();
if (menuItem.getItemId() == R.id.navItemHome) {
FragmentTransaction tabFragmentContainer = mFragmentManager.beginTransaction();
tabFragmentContainer.replace(R.id.containerView, new TabFragmentContainer()).commit();
}
if (menuItem.getItemId() == R.id.navItemRunHistory) {
FragmentTransaction runHistoryFragment = mFragmentManager.beginTransaction();
runHistoryFragment.replace(R.id.containerView, new RunHistoryFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemSettings) {
FragmentTransaction settingsFragment = mFragmentManager.beginTransaction();
settingsFragment.replace(R.id.containerView, new SettingsFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemHelp) {
FragmentTransaction instructionsFragment = mFragmentManager.beginTransaction();
instructionsFragment.replace(R.id.containerView, new InstructionsFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemMyProfile) {
FragmentTransaction myProfileFragment = mFragmentManager.beginTransaction();
myProfileFragment.replace(R.id.containerView, new MyProfileFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemLogOut) {
// User has selected log out option. Log user out and return to login screen.
ParseUser.logOut();
navigateToLogin();
}
return false;
}
});
// Set up the Toolbar.
setupToolbar();
}
I have followed other answers here and added the getListView() functionality to the onViewCreated() method but the problem still persists... Can anyone point out where I might be going wrong?
Based on these facts:
The exception is thrown because there is no root view yet when done() calls getListView().
done() is called when the query made by retrieveAcceptedRoutes() gets a response.
retrieveAcceptedRoutes is called in multiple places, including the OnRefreshListener mOnRefreshListener, which is registered as the refresh listener in onCreateView() before there is a root view (that is, before onCreateView() returns).
...it is possible for getListView() to be called before there is a root view.
Try moving these 3 statements from onCreateView() to onViewCreated(), so that way the refresh listener can only be called when there is a root view.
// Set SwipeRefreshLayout component
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeRefreshLayout);
// Set the onRefreshListener
mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
mSwipeRefreshLayout.setColorSchemeResources(
R.color.swipeRefresh1,
R.color.swipeRefresh2,
R.color.swipeRefresh3,
R.color.swipeRefresh4);
onViewCreated is called immediately after onCreateView, but the super.onViewCreated call is missing, perhaps this is root cause of your issue.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); // add this line back in
// Retrieve the accepted routes from the Parse backend
retrieveAcceptedRoutes();
}
I read your question again then I guess that:
Your ListFragment is destroyed while your background task keeps running. So when it's done, your callback would like to update the ListView which is no longer alive.
Actually, viewPager.setOffscreenPageLimit(3); may do the trick, but it's not a good practice. It forces your ViewPager to create and store more Fragments in memory which is not necessary. You can solve this without doing so.
What you should do: one of the following two practice should be fine, or both:
Destroy your task in your onPause or whatever lifecycle method, before your onDestroyView.
Exclude the code where you update your ListView inside your done() method. Make it a local method where you will check your ListView carefully, and there, you should ask your update process to run on UI thread to avoid any threading problem. Make sure to check if your getView() is not null (but not your getListView(), since it throws Exception if getView() returns null).
I recommend you to use both of them to make sure: your view is still useable and you don't waste your resource when running task in invisible fragment. Don't forget that by default, once your fragment is invisible, it is considered to be destroyed (not always, for example ViewPager keep reference of 2 fragments, but keep in mind that case).
remove all these imports:
import com.yourName.runmate.R;
Then resync your gradle and rebuild your project.
Also see here:
"cannot resolve symbol R" in Android Studio
edit
Your first obvious mistake in your Main is
mFragmentManager = getSupportFragmentManager();
should be:
mFragmentManager = getFragmentManager();
or change your Main activity to:
MainActivity extends FragmentActivity to make use of the support fragment manager.
You have a lot of unnecessary code in your question, majority of comments can be removed and imports for the purpose of this question.
What I have come up with is there is no activity, being used. The ListFragment needs to be attached to an Activity or you are trying to call that activity view before it is created.
java.lang.IllegalStateException: Content view not yet created
at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
If you are using Main, then you are not pulling them together well, from what I can see.
Firstly:
Take everything out of your onCreate and onCreateView (for all fragments) except the view inflater.
Place all the extra code into either onViewCreated or onActivityCreated. That way no methods can be called on a null view, as these are called after it is created.
Secondly, you need to sort out your activities and with what you're exactly trying to achieve.
You want a page viewer and a fragmentlist. The pageviewer needs to be associated with an activity, or activity fragment, not a fragment. Otherwise there is no view to attach the pageviewer pages to.
Use a FragmentActivity not a Fragment. To be the activity you run your ListFragment from.
public class TabFragmentContainer extends FragmentActivity {
MyPageAdapter pageAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tab_layout_fragment_container); // change to view layout.
// Instantiate the adapter that will return a fragment for each of the three sections of the main activity
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getFragmentManager(), getFragments());
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
// Set up the ViewPager with the sections adapter.
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
// Set up the adapter for the ViewPager
viewPager.setAdapter(myFragmentPagerAdapter);
}
}
I would suggest putting this into your ListFragment, to ensure your activity is created. You will need to move most of your code from your onCreate methods and put them in onViewCreated or onActivityCreated
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ArrayAdapter adapter = ArrayAdapter.createFromResource(getActivity(), R.layout.my_listview)layout, android.R.layout.simple_list_item_1);
setListAdapter(adapter);
getListView().setOnItemClickListener(this);
}
This code is just a guide, you'll need to tweak it.
Let me know if this helps.
These Q&As are excellent.
Content view not yet created
android Illegal state exception content view not yet create?
Fragment same principles applies to viewpager fragments ViewPager
Try to declare:
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
viewPager.setAdapter(myFragmentPagerAdapter);
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity(), getChildFragmentManager());
before:
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem(1, false);
// tabLayout.getTabAt(1).select();
}
});
// Return the created View
return view;

Unknown fatal exception at Runtime when using Fragments and lists

Afternoon all,
I'm currently learning all about fragments and their use in android apps. I'm trying to populate a listView with an array of Strings. This sort of idea worked when I was populating the list without using fragments but at the moment it's throwing a runtime exception "Unable to start activity". A simple point at where to look would be much appreciated as I can't figure out the logcat reading.
The fragment is thus:
public class setupFragment extends Fragment {
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_setup, container, false);
ListView listView = (ListView)getView().findViewById(R.id.listView); //the program seems to at this point
String[] setups = getResources().getStringArray(R.array.setups);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, android.R.id.text1, setups);
listView.setAdapter(arrayAdapter);
return view;
}
}
It's my first time posting java fragments, so any posting errors I apologize for. Cursory breakpoint debugging indicates that the program is breaking on the ListView declaration.
I can post some of the logcat on request, I'm not posting it at the moment due it being long and convoluted.
Replace: ListView listView = (ListView)getView().findViewById(R.id.listView);
with: ListView listView = (ListView) view.findViewById(R.id.listView);
You can't call getView() before returning one from onCreateView().
getView() returns null.
Replace getView() with view, returned object
ListView listView = (ListView)getView().findViewById(R.id.listView);
String[] setups = getResources().getStringArray(R.array.setups);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, android.R.id.text1, setups);
listView.setAdapter(arrayAdapter);
Cut this code and paste it inside onViewCreated method, you cannot get a reference of a view by calling getView() since that view is not attached to that fragment yet.
Also change your getView() to just view.
Your ListView declaration should be as follows :
ListView listView = (ListView) view.findViewById(R.id.listView);
Refer this link to know more about getView() and Fragments. Basically the getView() method returns the root view of the fragment which is nothing but the view returned by onCreateView()

Categories