0

I am trying to adding task in this todo app, it works fine but not showing todo items while items are showing on cloud filestore but not in app.

Here are my Main and ListItemAdapter. It shows the error of skipping layout. I used firebase cloud file for saving my list on int.

Main Activity

public class MainActivity extends AppCompatActivity {

List<ToDo> toDoList = new ArrayList<>();
FirebaseFirestore db;
RecyclerView listItem;
RecyclerView.LayoutManager layoutManager;

FloatingActionButton fab;

public MaterialEditText title, description;

public boolean isUpdate = false;


public  String idUpdate = "" ;

ListItemAdapter adapter;

AlertDialog dialog ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    listItem = (RecyclerView)findViewById(R.id.listTodo);
    listItem.setHasFixedSize(true);
    layoutManager = new LinearLayoutManager(this);
    listItem.setLayoutManager(layoutManager);


    db = FirebaseFirestore.getInstance();   //init firebase

    dialog = new SpotsDialog(MainActivity.this);
    title = (MaterialEditText)findViewById(R.id.title);
    description = (MaterialEditText)findViewById(R.id.description);
    fab = (FloatingActionButton)findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(!isUpdate){
                setData(title.getText().toString(),description.getText().toString());
            }
            else
            {
                updateData(title.getText().toString(),description.getText().toString());
                isUpdate = !isUpdate; //reset  flag
            }
        }
    });

    //recycler view

    loadData();


}

@Override
public boolean onContextItemSelected(MenuItem item) {
    if(item.getTitle().equals("DELETE"))
        deleteItem(item.getOrder());
    return super.onContextItemSelected(item);
}

private void deleteItem(int index) {
    db.collection("ToDoList")
            .document(toDoList.get(index).getId())
            .delete()
            .addOnSuccessListener(new OnSuccessListener<Void>() {
                @Override
                public void onSuccess(Void aVoid) {
                    loadData();
                }
            });
}

private void updateData(String title, String description) {
    db.collection("ToDoList").document(idUpdate)
                    .update("title",title,"description",description)
                    .addOnSuccessListener(new OnSuccessListener<Void>() {
                        @Override
                        public void onSuccess(Void aVoid) {
                        Toast.makeText(MainActivity.this,"Updated !",Toast.LENGTH_SHORT).show();
                        }
                    });


    //Realtime update refresh data
    db.collection("ToDoList").document(idUpdate)
            .addSnapshotListener(new EventListener<DocumentSnapshot>() {
                @Override
                public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {
                    loadData();
                }
            });
}

private void setData(String title, String description) {

    String id = UUID.randomUUID().toString();
    Map<String,Object> todo = new HashMap<>();
    todo.put("id",id);
    todo.put("title",title);
    todo.put("description",description);

    db.collection("ToDoList").document(id).set(todo).addOnSuccessListener(new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void aVoid) {

            loadData();             //refresh data
        }
    });
}

private void loadData() {
    dialog.show();
    if(toDoList.size() > 0 )
        toDoList.clear(); //remove old value
    db.collection("ToDo List")
        .get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    for (DocumentSnapshot doc:task.getResult())
                    {
                        ToDo todo = new ToDo(doc.getString("id"),
                                                doc.getString("title"),
                                                doc.getString("description"));

                            toDoList.add(todo);
                    }
                    adapter = new ListItemAdapter(MainActivity.this,toDoList);
                    listItem.setAdapter(adapter);
                    dialog.dismiss();
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Toast.makeText(MainActivity.this,"" +e.getMessage(),Toast.LENGTH_SHORT).show();
                }
            });

}
}

ListItemAdapter

class ListItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnCreateContextMenuListener
{
ItemClickListener itemClickListener;
TextView item_title, item_description;

public ListItemViewHolder(View itemView) {
    super(itemView);
    itemView.setOnClickListener(this);
    itemView.setOnCreateContextMenuListener(this);

    item_title = (TextView)itemView.findViewById(R.id.item_title);
    item_description = (TextView)itemView.findViewById(R.id.item_description);
}

public void setItemClickListener(ItemClickListener itemClickListener) {
    this.itemClickListener = itemClickListener;
}

@Override
public void onClick(View v) {
    itemClickListener.onClick(v,getAdapterPosition(),false);

    }

@Override
public void onCreateContextMenu(ContextMenu contextMenu, View v, ContextMenu.ContextMenuInfo contextMenuInfo) {

    contextMenu.setHeaderTitle("Select the action");
    contextMenu.add(0,0,getAdapterPosition(),"DELETE");
}
}



public class ListItemAdapter extends    RecyclerView.Adapter<ListItemViewHolder> {

MainActivity mainActivity;
List<ToDo> todoList;

public ListItemAdapter(MainActivity mainActivity, List<ToDo> todoList) {
    this.mainActivity = mainActivity;
    this.todoList = todoList;
}

@Override
public ListItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType)     {
    LayoutInflater inflater = LayoutInflater.from(mainActivity.getBaseContext());
    View view = inflater.inflate(R.layout.list_item,parent,false);

    return new ListItemViewHolder(view);
}

@Override
public void onBindViewHolder(ListItemViewHolder holder, int position) {


    // set data for item

    holder.item_title.setText(todoList.get(position).getTitle());
    holder.item_description.setText(todoList.get(position).getDescription());

    holder.setItemClickListener(new ItemClickListener() {
        @Override
        public void onClick(View view, int position, boolean isLongClick) {
            //When user select item data will auto set for Edit Text
            mainActivity.title.setText(todoList.get(position).getTitle());
            mainActivity.description.setText(todoList.get(position).getDescription());

            mainActivity.isUpdate = true;               //set flag update is true
            mainActivity.idUpdate = todoList.get(position).getId();
        }
    });
}

@Override
public int getItemCount() {

    return todoList.size();
}
}

Error Trace

E/RecyclerView: No adapter attached; skipping layout
W/View: dispatchProvideAutofillStructure(): not laid out, ignoring
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
M. Umer
  • 11
  • 5

2 Answers2

0

You need to call the below statements from the "main" thread, inside the onCreate() method. As soon as you call these statements from a "delayed" method, as in your case is onComplete() method, you get that error message.

To solve this, move the following lines of code:

listItem = (RecyclerView)findViewById(R.id.listTodo);
listItem.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
listItem.setLayoutManager(layoutManager);

Inside onComplete() method and your problem will be solved. Remember, onComplete() method has an asynchronous behaviour.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • before for loop or inside for loop or outside for loop? – M. Umer Jan 23 '18 at 14:48
  • Outside the for loop. – Alex Mamo Jan 23 '18 at 14:49
  • after adding this statement in **onComplete()**, it shows error on **this** keyword and I solved it by using **(MainActivity.this)** but still not working – M. Umer Jan 23 '18 at 15:54
  • W/IInputConnectionWrapper: beginBatchEdit on inactive InputConnection W/IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection W/IInputConnectionWrapper: getTextAfterCursor on inactive InputConnection W/IInputConnectionWrapper: getSelectedText on inactive InputConnection W/IInputConnectionWrapper: endBatchEdit on inactive InputConnection – M. Umer Jan 23 '18 at 15:59
  • This error has nothing to do with the initial issue, which seems to be solved since you are not getting that error anymore. Take a look at [this](https://stackoverflow.com/questions/8122625/getextractedtext-on-inactive-inputconnection-warning-on-android). – Alex Mamo Jan 23 '18 at 16:02
  • Alex i think your missing the big picture, sure your statements are indeed valid, but at the end of the day, he is trying to update the UI asynchronously from listeners, he needs observers and publishers . – Remario Jan 23 '18 at 16:04
0

The main reason why the RecyclerView items are not displayed is due to the fact that you only attach the adaptor instance in the onLoad method, which only gets called when the onComplete listener is triggered. All recycler adaptor instances should be attached after you set the layout manager.

adapter = new ListItemAdapter(MainActivity.this,toDoList);
                listItem.setAdapter(adapter);

The above line should be added after the layout manager attachment. That fixes one issue, however the other issue lies in the way you populate the TodoList List. The logic is still confined to the async listener. When you call the adaptor remember the List is not populated hence empty, thus no items to display to screen.

ToDo todo = new ToDo(doc.getString("id"),doc.getString("title"),
                                            doc.getString("description"))

First Test: Mock some todo items and insert into the List, before the adaptor.

Real test: You need a way to observe the list population and trigger the UI changes. There are many libraries out there that solves this rather elegant.

  1. First, consider Realm, which is a NOSQL database for android, known for its performance and real time updates.
  2. RXAndroid - an observable based reactive library.
  3. LiveData - have not used this one as yet.

To allow scaling and maintainability of your code base going , i suggest putting in the time to research the above solutions and derive your answer. My personal pick would be RxAndroid.

Remario
  • 3,813
  • 2
  • 18
  • 25
  • The on-complete is not synchronous, it blocks basically. – Remario Jan 23 '18 at 14:47
  • get in the habit of initializing your recycler view, attaching manager, attach adaptor then any options after – Remario Jan 23 '18 at 14:48
  • where I have to add this statement? – M. Umer Jan 23 '18 at 15:45
  • after adding this statement next to **listitem.setLayoutManager()**, it still not working. – M. Umer Jan 23 '18 at 15:47
  • Basically what your solving is an Async UI update, thats all – Remario Jan 23 '18 at 16:02
  • after adding, adapter = new ListItemAdapter(MainActivity.this,toDoList); listItem.setAdapter(adapter); dialog.dismiss();, it still not working – M. Umer Jan 23 '18 at 16:15
  • Please give me some time im at work!, in the mean while read up an Rxjava/RxAndroid – Remario Jan 23 '18 at 16:27
  • Before that, have you read upon rxandroid or any of the solutions. – Remario Jan 24 '18 at 13:47
  • I did not find any solution, that's why I am asking you. I also get little bit confused after seen different solutions regarding recycler view. – M. Umer Jan 24 '18 at 14:01
  • I did recall saying Rxjava is the way to go, you should read up on that, the solution requires an architectural change , its not simply code replacement here and there. Your problem is common in android, it requires observables to watch the list and report changes to the relevant UI components in your application . – Remario Jan 24 '18 at 14:06