Home > database >  Android RecycleView does not show any data
Android RecycleView does not show any data

Time:05-22

I'm using a template and I replaced the placeholder data with a Retrofit 2 request that parses a JSON file of the data from an URL and puts it into an object list. I ran into a problem where my retrofit call gets executed and returns items = 0 at first, then the code executes further ahead using the value items=0 and after quite a few steps in the debugger, it comes back to the call and shows the correct response (items=12) but the Adapter/RecycleView operations seem to have already been executed and they do not use the late-arrived parsed data.

MainActivity.java

public class MainActivity extends AppCompatActivity {
private View parent_view;
List<Task> items = new List<Task>() {...}
private RecyclerView recyclerView;
private AdapterTasks mAdapter;
private int animation_type = ItemAnimation.BOTTOM_UP;


@Override
protected void onCreate(Bundle savedInstanceState) {
    items = DataGenerator.getTasksData();
    items.addAll(DataGenerator.getTasksData());
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_list_animation);
    parent_view = findViewById(android.R.id.content);
    initComponent();

}
private void initComponent() {

    recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setHasFixedSize(true);
    animation_type = ItemAnimation.FADE_IN;
    setAdapter();
}

private void setAdapter() {
    //set data and list adapter
    mAdapter = new AdapterTasks(this, items, animation_type);
    recyclerView.setAdapter(mAdapter);

    // on item list clicked
    mAdapter.setOnItemClickListener(new AdapterTasks.OnItemClickListener() {
        @Override
        public void onItemClick(View view, Task obj, int position) {
            //open task view
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_list_animation, menu);
    return true;
}

Retrofit request

public static List<Task> getTasksData() {
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

    Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd HH:mm:ss")
            .create();

    Retrofit retrofit= new Retrofit.Builder()
            .baseUrl("http://10.0.2.2/index.php/")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .client(client)
            .build();

    TasksApi taskAPI =retrofit.create(TasksApi.class);
    Call<List<Task>> call = taskAPI.getAllTasks();

    call.enqueue(new Callback<List<Task>>() {
        @Override
        public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
            items = response.body();
        }
        @Override
        public void onFailure(Call<List<Task>> call, Throwable t) {
        }
    });
    return items;
}

Adapter

public class AdapterTasks extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private List<Task> items;
private Context ctx;

private OnItemClickListener mOnItemClickListener;
private int animation_type = 0;

public interface OnItemClickListener {
    void onItemClick(View view, Task obj, int position);
}

public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
    this.mOnItemClickListener = mItemClickListener;
}

public AdapterTasks(Context context, List<Task> items, int animation_type) {
    this.items = DataGenerator.getTasksData();
    this.items.addAll(DataGenerator.getTasksData());
    ctx = context;
    this.animation_type = animation_type;
}

public class OriginalViewHolder extends RecyclerView.ViewHolder {
    public View lyt_parent;
    public TextView laikas;
    public TextView klientas;
    public TextView darbas;
    public TextView komentaras;
    public Button atlikta;


    public OriginalViewHolder(View v) {
        super(v);
        laikas = (TextView) v.findViewById(R.id.laikas);
        klientas = (TextView) v.findViewById(R.id.name);
        lyt_parent = (View) v.findViewById(R.id.lyt_parent);
        darbas = (TextView) v.findViewById(R.id.darbas);
        komentaras = (TextView) v.findViewById(R.id.komentaras);
        atlikta = (Button) v.findViewById(R.id.atlikta);

    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    RecyclerView.ViewHolder vh;
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_task, parent, false);
    vh = new OriginalViewHolder(v);
    return vh;
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
    Log.e("onBindViewHolder", "onBindViewHolder : "   position);
    if (holder instanceof OriginalViewHolder) {
        OriginalViewHolder view = (OriginalViewHolder) holder;

        Task t = items.get(position);
        view.laikas.setText((CharSequence) t.AtlikData);
        view.klientas.setText(t.KlientasID);
        view.darbas.setText(t.VeiksmoID);
        view.komentaras.setText(t.Komentaras);
        view.lyt_parent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mOnItemClickListener != null) {
                    mOnItemClickListener.onItemClick(view, items.get(position), position);
                }
            }
        });
        setAnimation(view.itemView, position);
    }
}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            on_attach = false;
            super.onScrollStateChanged(recyclerView, newState);
        }
    });
    super.onAttachedToRecyclerView(recyclerView);
}

@Override
public int getItemCount() {
    return items.size();
}

private int lastPosition = -1;
private boolean on_attach = true;

private void setAnimation(View view, int position) {
    if (position > lastPosition) {
        ItemAnimation.animate(view, on_attach ? position : -1, animation_type);
        lastPosition = position;
    }
}

}

CodePudding user response:

Your method is asynchronous, so the code in onResponse runs in the future, after your getTaskData method has already returned an empty list. You would need to modify your program flow to accommodate this.

Here is an example of how you could define your own custom callback to handle that, by setting up the RecyclerView empty at first and then adding the data once it arrives.

Step 1: define an interface somewhere (e.g. in the activity or retrofit request class)

interface OnTasksRetrieved {
    void getResult(List<Task> result);
}

Step 2: change your getTasksData method to take a callback implementing that interface instead of returning a list

public static void getTasksData(OnTasksRetrieved callback) {
    //...
    call.enqueue(new Callback<List<Task>>() {
        @Override
        public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
            // Call your callback once you have retrieved the data
            callback.getResult(response.body());
        }
        @Override
        public void onFailure(Call<List<Task>> call, Throwable t) {
        }
    });

    // do not return anything
}

Step 3: when you start the async call in onCreate, create an instance of the callback interface to pass to it to handle the result

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_list_animation);
    parent_view = findViewById(android.R.id.content);
    initComponent();
    
    // start the async fetch here - it won't finish until
    // later in the future, when it will call the callback code
    // below to update the RecyclerView data
    getTasksData(
        new OnTasksRetrieved() {
            @Override
            public void getResult(List<Task> result) {
                // The code in here runs much later in the future - the adapter
                // will already have been set up, but will be empty.
                myAdapter.setItems(result); // add this method to the adapter
            }
        }
    );
}

You will need to modify the adapter so it doesn't take the List as an input, but instead has a setItems method something like this, which will be called once the data is retrieved:

private List<Task> items = new ArrayList<>();

public AdapterTasks(Context context,  int animation_type) {
    ctx = context;
    this.animation_type = animation_type;
}

public void setItems(List<Task> data) {
    items.clear();
    items.addAll(data);
    notifyDataSetChanged();
}
  • Related