I was following a tutorial about a comic reading app, and while almost everything works, the last part of my code does not work, it says that my RecylcerView has no adapter attached to it, but it does, and because of that my textview cant be set resulting in a app crash
Here is the fragment.xml where the error happens!
public class fragment_discover extends Fragment implements I_ComicLoadDone {
// Database
DatabaseReference comics;
I_ComicLoadDone comicListener;
RecyclerView recycler_comic;
TextView txt_comic;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_discover,container,false);
// Init Database
comics = FirebaseDatabase.getInstance().getReference("Comic");
// Init Listener
comicListener = this;
loadComic();
// Init List things
recycler_comic = (RecyclerView)rootView.findViewById(R.id.RV_Discover);
txt_comic = (TextView)rootView.findViewById(R.id.comic_title);
return rootView;
}
private void loadComic() {
comics.addListenerForSingleValueEvent(new ValueEventListener() {
List<Comic> comic_load = new ArrayList<>();
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot comicSnapShot: dataSnapshot.getChildren())
{
Comic comic = comicSnapShot.getValue(Comic.class);
comic_load.add(comic);
}
comicListener.onComicLoadDoneListener(comic_load);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Toast.makeText(getActivity(), "", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onComicLoadDoneListener(List<Comic> comicList) {
Common.comicList = comicList;
recycler_comic.setAdapter(new MyComicAdapter(getActivity(), comicList));
txt_comic.setText(new StringBuilder("NEW COMIC (")
.append(comicList.size())
.append(")"));
}
}
I didnt put the adapter code in here, because that one works fine, if you need it just ask.
After launching the app I get this error message:
E/RecyclerView: No adapter attached; skipping layout
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
The stacktrace suggests that Your txt_comic TextView is null at the time of calling setText on it. It is caused by the txt_comic not being initialized while the method onComicLoadDoneListener is called after the call to loadComic.
Simply swap the order of txt_comic initialization and the loadComic call:
// Init List things
recycler_comic = (RecyclerView)rootView.findViewById(R.id.RV_Discover);
txt_comic = (TextView)rootView.findViewById(R.id.comic_title);
loadComic();
Second problem is probably the fact, that You are not attaching an Adapter immediately after initializing the RecyclerView. A workaround in Your case would be to pass a null or MyComicAdapter with an empty list:
recycler_comic = (RecyclerView)rootView.findViewById(R.id.RV_Discover);
recycler_comic.setAdapter(new MyComicAdapter(getActivity(), new ArrayList<>())); // or recycler_comic.setAdapter(null);
This should resolve Your problem.
Related
In my android application(Java) I am displaying a list of around 1800 contacts in a recyclerview. While doing a memory profile it was found that when scrolling the recycler view the memory usage was increasing rapidly. So I found this question here which was mentioning the same problem and tried out the solution which was to setIsRecyclable(false) in onBindViewHolder and it worked. The profiling results are given below.
Case 1 : setIsRecyclable(False) not used
Initial memory usage : ~ 40M
[ Java=5.9M Native=5M Graphics=20.3M Stack=0.3M Code=5.3M Others =0.8M ]
Peak memory usage : ~ 345M
[ Java=187.5M Native=39.1M Graphics=101.5M Stack=0.4M Code=11.6M Others =6.5M ]
Also the peak memory usage was found to increase with increase in number of items in the list. After the continuous scrolling is stopped for a while the memory usage does come down but only to around 162 MB.
Case 2 : after adding setIsRecyclable(False) to onBindViewHolder
Initial memory usage : ~ 42M
[ Java=5.8M Native=5.5M Graphics=20.2M Stack=0.3M Code=9.4M Others =0.8M ]
Peak memory usage : ~ 100M
[ Java=43.9M Native=9.7M Graphics=32.6M Stack=0.4M Code=11.7M Others =2.2M ]
Also, in this case, memory usage was not affected significantly by increasing number of items in list. Although peak memory usage is about 100MB the average stays at around 70 MB for most of the time which is even better.
Source Code of fragment containing recyclerView
Note :
* Adapter class is defined as an inner class of Fragment class and ViewHolder class is defined as an inner class of Adapter class.
* 'App.personList' is a static arrayList holding the list of contacts and App is the ViewModel class.
* adapter1 is the only adapter of interest. Please avoid adapter2(handles another small list)
public class FragmentAllContacts extends Fragment
{
public static MainActivity main;
public RecyclerView contactsView, tagsView;
LinearLayoutManager llm, lln;
Button filterCloseButton;
CardView filterView;
Adapter_ContactListView adapter1;
Adapter_TagListView adapter2;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
setHasOptionsMenu(true);
main = (MainActivity) getActivity();
return inflater.inflate(R.layout.fragment_all_contacts, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
adapter1 = new Adapter_ContactListView(App.personList,getContext());
adapter2 = new Adapter_TagListView(App.tagList,getContext());
filterView = getView().findViewById(R.id.cardView7);
FloatingActionButton fab = getView().findViewById(R.id.create_contact_fab);
contactsView = getView().findViewById(R.id.allContacts_recyclerView);
contactsView.setAdapter(adapter1);
llm = new LinearLayoutManager(main.getBaseContext());
contactsView.setLayoutManager(llm);
contactsView.scrollToPosition(App.AllConnections.scrollPosition);
tagsView = getView().findViewById(R.id.allTags_recyclerView);
tagsView.setAdapter(adapter2);
lln = new LinearLayoutManager(main.getBaseContext(), LinearLayoutManager.HORIZONTAL, false);
tagsView.setLayoutManager(lln);
}
class Adapter_ContactListView extends RecyclerView.Adapter<Adapter_ContactListView.ViewHolder> implements Filterable {
List<Person_PersistentData> contactsFiltered;
Context context;
public Adapter_ContactListView(List<Person_PersistentData> list, Context context)
{
this.contactsFiltered = list;
this.context = context;
}
#Override
public Adapter_ContactListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view_list, parent, false);
Adapter_ContactListView.ViewHolder pane = new Adapter_ContactListView.ViewHolder(v);
return pane;
}
#Override
public void onBindViewHolder(Adapter_ContactListView.ViewHolder pane, int position)
{
pane.setIsRecyclable(false);
final Person_PersistentData rec = contactsFiltered.get(position);
pane.nameView.setText(rec.personName + " (" + rec.personID + ")");
Uri imageUri = App.FSManager.getProfilePic(rec.personID);
if (imageUri != null) {pane.imageView.setImageURI(imageUri);}
else {pane.imageView.setImageResource(R.drawable.ico_60px);}
if (App.AllConnections.personSelectionStack.contains(rec.personID)) {pane.cv.setBackgroundColor(context.getResources().getColor(R.color.rgb_000_070_100));}
else
{pane.cv.setBackgroundColor(context.getResources().getColor(R.color.rgb_020_020_020));}
pane.cv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
if(App.AllConnections.selectionMode)
{
App.Person_SelectionInput(rec.personID);
Adapter_ContactListView.this.notifyDataSetChanged();
}
else
{
App.PersonInfo.id = rec.personID;
main.startTask(T.personInfo);
}
}
});
pane.cv.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view)
{
App.Person_SelectionInput(rec.personID);
Adapter_ContactListView.this.notifyDataSetChanged();
return false;
}
});
//animate(holder);
}
#Override
public int getItemCount() {
//returns the number of elements the RecyclerView will display
return contactsFiltered.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public Filter getFilter() { ... }
#Override
public long getItemId(int position)
{
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
class ViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView nameView;
ImageView imageView;
public ViewHolder(#NonNull View itemView)
{
super(itemView);
cv = itemView.findViewById(R.id.cardView);
nameView = itemView.findViewById(R.id.name);
imageView = itemView.findViewById(R.id.imageViewZ);
}
}
}
}
Question
So 'setIsRecyclable(False)' is supposed to prevent recycling of views and this should be causing more memory usage. But instead it is showing the opposite behavior. Also i think the app will surely crash if it has to handle an even larger list without using setIsRecyclable(false). Why is this happening ?
getItemId(int) and getItemViewType(int) should NEVER return position itself, you're violating recyclerview contract by forcing it to create new viewholders for every single position intead of re-using existing views.
This is the cause of your issue - every position has unique itemViewType so they start to fill up recycledViewPool very rapidly since they're only being inserted and never being taken out of it. setIsRecyclable(False) circumvents the issue by not putting them in recyclerViewPool but that doesn't fix the problem of lack of view recycling.
Just delete getItemId and getItemViewType overrides because you're not using them properly.
I have an arraylist (user_word).
I send it to CardAdapter2_2_2 and it shows every item that is in the arraylist.
But user_word.size() returns 0. Why?
private DatabaseReference databaseReference2 = FirebaseDatabase.getInstance().getReference("kullanici_kelime");
private ArrayList<User_word> user_words= new ArrayList<>();
#Override
public void onStart(){
super.onStart();
databaseReference2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
user_words.clear();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
User_word user_word= new User_word();
user_word.setMean(postSnapshot.child("mean").getValue(String.class)) ;
user_word.setName(postSnapshot.child("name").getValue(String.class));
user_word.setKey(postSnapshot.child("key").getValue(String.class));
user_word.setId(postSnapshot.child("id").getValue(String.class));
user_word.setDate(postSnapshot.child("date").getValue(Long.class));
user_word.setLevel(postSnapshot.child("level").getValue(int.class));
user_words.add(user_word); }
adapter.notifyDataSetChanged(); } }); }
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view =inflater.inflate(R.layout.fragment_frag3, container,false);
Timestamp ts=new Timestamp(System.currentTimeMillis());
Date date=new Date(ts.getTime());
Log.e("size", String.valueOf(user_words.size()));// thats my problem
adapter= new CardAdapter2_2_2(getActivity(),user_words);
rv.setAdapter(adapter);
return view;
}
This is because onCreateView is called prior to onStart and prior todatabaseReference2 calling its ValueEventListeners
Make sure dataSnapshot.getChildern() HAS children and it goes into the for loop.
Make sure that onDataChange() gets called.
Make sure that onCreateView() gets called after your array is already
initialized.
Anyway it's hard to tell without having the rest of you code.
I'm having a problem with getter function returning object from a class HttpRequestCurrentWeather extending AsyncTask. In this class I receive data from API, and want to set some TextViews in CurrentWeatherFragment depending on what I received. The problem is I can't return the whole object of DataModel to the Fragment and set the components there nor set TextViews is onPostExecute() method, because I'm getting a NullPointerException ( java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference). What should I do to set those TextViews?
public class CurrentWeatherFragment extends Fragment {
DataModel dataModel = new DataModel();
TextView tv_city;
TextView tv_temperature;
TextView tv_pressure;
public void setComponents() {
this.tv_city = getView().findViewById(R.id.tv_current_city_name);
tv_city.setText(dataModel.getName());
this.tv_temperature = getView().findViewById(R.id.tv_current_temperature);
tv_temperature.setText(dataModel.getTemp());
this.tv_pressure = getView().findViewById(R.id.tv_current_pressure);
tv_pressure.setText(dataModel.getPressure());
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View RootView = inflater.inflate(R.layout.fragment_current_weather_layout, container, false);
HttpRequestCurrentWeather httpRequestCurrentWeather1 = new HttpRequestCurrentWeather();
httpRequestCurrentWeather1.execute("", "", "");
// here I receive null object, but why?
dataModel = httpRequestCurrentWeather1.getDataModel();
// set the texts in components:
setComponents();
return RootView;
}
}
public class HttpRequestCurrentWeather extends AsyncTask<String, Void, DataModel> {
DataModel dataModel;
public HttpRequestCurrentWeather() {
this.dataModel = null;
}
#Override
protected DataModel doInBackground(String... params) {
ApiCurrentWeather apiCurrentWeather1 = new ApiCurrentWeather("London", "uk");
try {
// catch the model with recived data from API:
dataModel = apiCurrentWeather1.getWeather();
} catch (IOException e) {
e.printStackTrace();
}
return dataModel;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(DataModel dataModel) {
super.onPostExecute(dataModel);
CurrentWeatherFragment fragment = new CurrentWeatherFragment();
TextView tv_temperature = (TextView)fragment.getView().findViewById(R.id.tv_current_temperature);
tv_temperature.setText(dataModel.getTemp());
}
// this function returns NULL object ????
public DataModel getDataModel(){
return this.dataModel;
}
}
To be honest, this is not how the appropriate answer should look like, but what I was trying to achieve using AsyncTask seemed far more easier using Retrofit, so I finally gave up this idea and decided to use Retrofit instead. If you have similar problem, forget about AsyncTask - Retrofit is more suitable for this!
I am using a simple java library file for Undo and Redo text as shown in the tutorial and sample android app but for me when I run the app it shows me the following error
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setEnabled(boolean)' on a null object reference
at com.apps.primalnotes.Fragments.EditorFragment.textAction(EditorFragment.java:1023)
at com.apps.primalnotes.Fragments.EditorFragment.onCreateView(EditorFragment.java:84)
Following is the library and method I am following on GitHub enter link description here
And exactly i am doing the following
EditorFragment extends Pantalla implements TextUndoRedo.TextChangeInfo, View.OnClickListener{
private TextUndoRedo TUR;
private ImageView undo, redo;
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.editor, container, false);
getActivity().setTitle("Editor");
setHasOptionsMenu(true);
imm =(InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
base = new DataBase(context, null);
text = (EditText) view.findViewById(R.id.texto);
undo = (ImageView)view.findViewById(R.id.undo);
redo = (ImageView)view.findViewById(R.id.redo);
TUR = new TextUndoRedo(text, this);
textAction(); "Showing error here"
undo.setOnClickListener(this);
redo.setOnClickListener(this);
}
#Override
public void textAction() {
undo.setEnabled(TUR.canUndo()); "Showing error here"
redo.setEnabled(TUR.canRedo());
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.undo:
TUR.exeUndo();
break;
case R.id.redo:
TUR.exeRedo();
break;
Your code doesn't have a "setContentView(R.layout.{file name of layout})".
Could you check it? It should be performed before using findViewById method.
Here the code from the Github source
btn_undo = (Button) findViewById(R.id.btn_undo);
btn_undo.setOnClickListener(this);
btn_redo = (Button) findViewById(R.id.btn_redo);
btn_redo.setOnClickListener(this);
And here the code from your code :
text = (EditText) view.findViewById(R.id.texto);
undo = (ImageView)view.findViewById(R.id.undo);
redo = (ImageView)view.findViewById(R.id.redo);
TUR = new TextUndoRedo(text, this);
The sample using Button for the undo/redo and you using ImageView. Check what is your ImageView ID in xml. Same with other here, your undo/redo ImageView is Null.
Check your XML. Your ImageView for undo & redo should have an id that looks like this,
android:id="#+id/undo
or
android:id="#+id/redo"
Using memberRecList RecyclerView, I'm trying to list every member of a group horizontally, but I get this issue when I set its adapter:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
NOTE: I've included some notes in the comments, scroll to the bottom to see them.
Here is the RecycleView initialization:
// I do InitMemberList() in the OnCreate method of my activity
private void InitMemberList()
{
LinearLayoutManager layoutManager =
new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
memberRecList = (RecyclerView) findViewById(R.id.member_list);
memberRecList.setHasFixedSize(true);
memberRecList.setLayoutManager(layoutManager);
//NOTE 1 (scroll to bottom)
}
My dataset is the ArrayList members. I retrieve it from a worker thread using FirebaseDatabase like so:
//this is also in the OnCreate() method, doesn't matter if it's before
//or after InitMembersList() because it starts a separate thread.
//I'm showing it just in case I have any errors here
private void GetMemberInfo(final String userId)
{
//NOTE 2 (scroll to bottom)
//Getting the reference of the database
DatabaseReference ref = userDB.getReference().getRef();
Query query = ref.child(userId);
//Firebase's method names are confusing. These below just retrieve the
//data from the database ONCE.
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
tempMember = dataSnapshot.getValue(User.class); //Getting member info
members.add(tempMember); //adding member info to the list
memberCount --; //number of members left to retrieve
if(memberCount == 0) {
//NOTE 3 (scroll to bottom)
PopulateMemberList(); //the adapter is set inside here
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
I set members adapter in the PopulateMemberList() method like so:
private void PopulateMemberList()
{
MembersListAdapter adapter = new MembersListAdapter(members);
memberRecList.setAdapter(adapter);
}
Here is my custom MembersListAdapter:
public class MembersListAdapter extends RecyclerView.Adapter<MembersListAdapter.MemberViewHolder> {
private ArrayList<User> users = new ArrayList<>();
public MembersListAdapter(ArrayList<User> users) {
this.users = users;
}
public class MemberViewHolder extends RecyclerView.ViewHolder{
TextView rep_txt;
ImageView memberImageView;
public MemberViewHolder(View itemView) {
super(itemView);
rep_txt = (TextView) itemView.findViewById((R.id.member_rep_text));
memberImageView = (ImageView) itemView.findViewById(R.id.member_profile_image);
}
}
#Override
public int getItemCount() {
return users.size();
}
#Override
public MemberViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//NOTE 4 (scroll to bottom)
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.member_list_item, parent, true);
return new MemberViewHolder(itemView);
}
#Override
public void onBindViewHolder(MemberViewHolder holder, int position) {
final User currentMember = users.get(position);
//changing the text of my textviews
//Loading Member Image
Picasso.with(GroupActivity.this)
.load(currentMember.getProfile_picture_ref())
.into(holder.memberImage);
}
NOTES:
NOTE 1: I tried setting the memberRecList adapter as an empty adapter at first inside InitMemberList() and then setting the new adapter once I got the data from Firebase. Didn't work. Gave me the same error.
NOTE 2: I use the GetMemberInfo() inside a loop where I pass the userId one by one. Firebase gurus will tell me to instead make a foreach loop with dataSnapshot.getChildren() to retrieve users one by one and then filter them, but it's not feasible because of the size of my database. I'd be scanning through a million users just to retrieve 5 of them.
NOTE 3: Using memberCount to find if member list retrieval is finished allows me to start working with the data after it is fully retrieved. Without this, trying to access the data gives Nullpointerexception because most of the it can't be received in time.
NOTE 4: I'm assuming this is where my error lies? I tried getting the itemView parent and then removing **itemView* from it (exactly what the error message says I should do):
#Override
public MemberViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.member_list_item, parent, true);
ViewGroup mParent = (ViewGroup) itemView.getParent();
if(mParent!=null)
mParent.removeView(itemView);
return new MemberViewHolder(itemView);
}
but I get a million instances of the following error:
E/AndroidRuntime: at android.view.ViewGroup.resetResolvedLayoutDirection(ViewGroup.java:6688)
and then a StackOverflow error and the app crashes:
D/Error: ERR: exClass=java.lang.StackOverflowError
D/Error: ERR: exMsg=stack size 8MB
D/Error: ERR: file=ViewGroup.java
D/Error: ERR: class=android.view.ViewGroup
D/Error: ERR: method=resetResolvedLayoutDirection line=6687
D/Error: ERR: stack=java.lang.StackOverflowError: stack size 8MB
at android.view.ViewGroup.resetResolvedLayoutDirection(ViewGroup.java:6687)
Afterwards, the last line is repeated at least 100 times and at the end:
D/Error: ERR: TOTAL BYTES WRITTEN: 41276
EDIT: Forgot to mention, I know FirebaseRecyclerAdapter exists, but I can't use it. If you know of other libraries which could help me, please do tell. Also, would've preferred to use a ListView rather than a RecyclerView, but I need to list the items horizontally (so that I can scroll left/right) and I think that's impossible with ListView.
You get an IllegalStateException because of this:
#Override
public MemberViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//NOTE 4 (scroll to bottom)
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.member_list_item, parent, true);
return new MemberViewHolder(itemView);
}
When you inflate a view for a RecyclerView you shouldn't add it to its parent, android does it for you.
Change the flag to false in the following line to not add view to parent:
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.member_list_item, parent, false);