I'm new to Programming and to Android and got please a question, so I have to build an Android app ( App to buy cars) using MVVM,LiveData, RecyclerView and Fragments.
I can creat an instance of car and then view that in a RecyclerView, but when I want to delete it, then I receive the following error:
Logcat:
java.lang.NullPointerException: **Attempt to invoke virtual method 'void com.example.da.ViewModel.MyViewModel.deleteCar(com.example.da.Model.Car)' on a null object reference**
at com.example.da.Adapter.RecyclerAdapter$MyViewHolder$2.onClick(RecyclerAdapter.java:127)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:174)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
I hope that someone can help me.Thanks a lot!
Dao
<pre><code>
#Dao
public interface CarDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void addCar( Car car);
#Query("SELECT * FROM Car ORDER BY model ASC")
LiveData<List<Car>>getAllCars();
#Delete
public void deleteCar(Car car);
#Query("DELETE FROM Car")
void deleteAll();
#Update
void updateCar(Car car);
//User
#Insert
void registerUser(User user);
}
</pre></code>
Database
<pre><code>
#Database(entities = {Car.class, User.class},version =1, exportSchema = false)
public abstract class RoomDatabase extends androidx.room.RoomDatabase {
private static RoomDatabase roomDatabase;
private static final String databaseName="user";
// Dao including
public abstract CarDao carDao();
public abstract UserDao userDao();
public static synchronized RoomDatabase getUserDatabase(Context context)
{
if (roomDatabase==null)
{
roomDatabase= Room.databaseBuilder(context, RoomDatabase.class,databaseName)
.fallbackToDestructiveMigration()
.build();
}
return roomDatabase;
</pre></code>
Repository
<pre><code>
public class Repository {
private CarDao carDao;
private LiveData<List<Car>>getAllCars;
public Repository(Application application)
{
RoomDatabase db = RoomDatabase.getUserDatabase(application);
carDao=db.carDao();
getAllCars=carDao.getAllCars();
}
//CarDao methods
public void addCar( Car car)
{
new newAsyncTask(carDao).execute(car);
}
private static class newAsyncTask extends AsyncTask<Car, Void, Void>{
private CarDao myAsyncDao;
newAsyncTask(CarDao carDao){
myAsyncDao=carDao;
}
#Override
protected Void doInBackground(Car... cars) {
myAsyncDao.addCar(cars[0]);
return null;
}
}
public void updateCar( Car car)
{
new updateAsyncTask(carDao).execute(car);
}
private static class updateAsyncTask extends AsyncTask<Car, Void, Void>{
private CarDao myAsyncDao;
updateAsyncTask(CarDao carDao){
myAsyncDao=carDao;
}
#Override
protected Void doInBackground(Car... cars) {
myAsyncDao.updateCar(cars[0]);
return null;
}
}
public void deleteCar( Car car)
{
new deleteAsyncTask(carDao).execute(car);
}
private static class deleteAsyncTask extends AsyncTask<Car, Void, Void>{
private CarDao myAsyncDao;
deleteAsyncTask(CarDao carDao){
myAsyncDao=carDao;
}
#Override
protected Void doInBackground(Car... cars) {
myAsyncDao.deleteCar(cars[0]);
return null;
}
}
</pre></code>
RecyclerAdapter
<pre><code>
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private List<Car>list;
Context context;
private MyViewModel myViewModel;
public RecyclerAdapter(List<Car> list) {
this.list = list;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.car_item,parent,false);
MyViewHolder myViewHolder = new MyViewHolder(view);
context=parent.getContext();
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
Car car = list.get(position);
holder.tvModel.setText(car.getCarModel());
holder.tvColor.setText(car.getCarColor());
holder.tvDpl.setText(car.getCarDpl());
holder.tvDescription.setText(car.getCarDescription());
holder.tvPrice.setText(car.getCarPrice());
}
#Override
public int getItemCount() {
if(list !=null)
{
return list.size();
}
else return 0;
}
public void setData(List<Car>list){
this.list=list;
notifyDataSetChanged();
}
// generate ViewHolder
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView tvModel;
TextView tvColor;
TextView tvDpl;
TextView tvDescription;
TextView tvPrice;
Button btnDelete;
// Constructor of ViewHolder
public MyViewHolder(#NonNull View itemView) {
super(itemView);
// Inflate the views
tvModel=itemView.findViewById(R.id.model);
tvColor=itemView.findViewById(R.id.color);
tvDescription=itemView.findViewById(R.id.description);
tvDpl=itemView.findViewById(R.id.dpl);
tvPrice=itemView.findViewById(R.id.price);
btnDelete=itemView.findViewById(R.id.btn_delete);
// Listener for Delete button
btnDelete.setOnClickListener(this);
btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
CreateAlertDialoge();
}
});
}
private void CreateAlertDialoge()
{
AlertDialog.Builder builder =new AlertDialog.Builder(context);
builder.setMessage("Are you sure to delete");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Car car = new Car();
int ID=list.get(getAdapterPosition()).getCarId();
car.setCarId(ID);
// if (car==null){
// return;
// }
//MainActivity.roomDatabase.carDao().deleteCar(car);
myViewModel.deleteCar(car);
MainActivity.fragmentManager.beginTransaction().replace(R.id.container,new BaseFragment(),null).commit();
Toast.makeText(context, "Yes", Toast.LENGTH_SHORT).show();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(context, "no", Toast.LENGTH_SHORT).show();
}
});
builder.create();
builder.show();
}
#Override
public void onClick(View v)
{
Car car = new Car();
int ID=list.get(getAdapterPosition()).getCarId();
car.setCarId(ID);
//MainActivity.roomDatabase.carDao().deleteCar(car);
myViewModel.deleteCar(car);
MainActivity.fragmentManager.beginTransaction().replace(R.id.container,new BaseFragment(),null).commit();
}
}
}
</pre></code>
ViewModel
<pre><code>
public class MyViewModel extends AndroidViewModel {
private Repository repository;
private LiveData<List<Car>>getAllCars;
public MyViewModel(#NonNull Application application) {
super(application);
repository=new Repository(application);
getAllCars=repository.getAllCars();
}
public LiveData<List<Car>>getGetAllCars(){
return getAllCars;
}
public void addCar( Car car)
{
repository.addCar(car);
}
public void updateCar(Car car){
repository.updateCar(car);
}
public void registerUser( User user)
{
repository.registerUser(user);
}
public void deleteCar(Car car)
{
repository.deleteCar(car);
}
public void deleteAll(){
repository.deleteAll();
}
}
</pre></code>
The main problem here is the myViewModel inside your RecyclerAdapter was never set a value. You may pass the instance of your ViewModel when instantiating the RecyclerAdapter. However, this is considered a bad idea.
A better approach would be creating a callback from RecyclerAdapter back to your Fragment. See: https://stackoverflow.com/a/53466188/9656117
Related
I am learning Room DataBase!!
I Know How to Insert and Retrieve Data from Room DataBase to Recycler View!! But In Delete Operation I am getting error of "No Adapter attached Skipped Layout!"
What I want when anyone click on delete button on recycler view . The Task should be deleted
That's why I also Used delete method in Interface and add Interface in Recycler View Adapter which give call back to MainActivity so that we delete and update the recycler view
All of my codes are given below
Here is my Entity Class named as Task
#Entity
public class Task implements Serializable {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "task_name")
private String task_name;
#ColumnInfo
private String task_desc;
#ColumnInfo
private String comment;
#ColumnInfo
private String task_comp_date;
#ColumnInfo
private String activate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTask_name() {
return task_name;
}
public void setTask_name(String task_name) {
this.task_name = task_name;
}
public String getTask_desc() {
return task_desc;
}
public void setTask_desc(String task_desc) {
this.task_desc = task_desc;
}
public String getTask_comp_date() {
return task_comp_date;
}
public void setTask_comp_date(String task_comp_date) {
this.task_comp_date = task_comp_date;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getActivate() {
return activate;
}
public void setActivate(String activate) {
this.activate = activate;
}
}
My Data Accession Object named as TaskDao
#Dao
public interface TaskDao {
#Query("SELECT * FROM task")
List<Task> getAll();
#Insert
void insert(Task task);
#Delete
void delete(Task task);
#Update
void update(Task task);
}
My DataBase
#Database(entities = {Task.class},version = 1)
public abstract class AppDataBase extends RoomDatabase {
public abstract TaskDao taskDao();
}
My DataBaseClient named as DatabaseClient
public class DatabaseClient {
private Context context;
private static DatabaseClient mInstace;
private AppDataBase appDataBase;
public DatabaseClient(Context context) {
this.context = context;
appDataBase = Room.databaseBuilder(context,AppDataBase.class,"MyDailyTask").build();
}
public static synchronized DatabaseClient getInstance(Context context)
{
if(mInstace == null)
{
mInstace = new DatabaseClient(context);
}
return mInstace;
}
public AppDataBase getAppDataBase()
{
return appDataBase;
}
}
My RecyclerView Adapter
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.MyViewHolder> {
private Context context;
private List<Task> taskList;
public interface OnDeleteClickListener
{
void OnDeleteClickListener(Task task);
}
private OnDeleteClickListener onDeleteClickListener;
public TaskAdapter(Context context, List<Task> taskList) {
this.context = context;
this.taskList = taskList;
}
public void setOnDeleteClickListener(OnDeleteClickListener onDeleteClickListener) {
this.onDeleteClickListener = onDeleteClickListener;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view;
view = LayoutInflater.from(context).inflate(R.layout.view_task_list,parent,false);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
holder.setData(taskList.get(position).getTask_name(),taskList.get(position).getTask_desc(),taskList.get(position).getComment(),taskList.get(position).getTask_comp_date(),position);
}
#Override
public int getItemCount() {
return taskList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView t1,t2,t3,t4,t5;
private ImageView view;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
t1= itemView.findViewById(R.id.tvCommnt);
t2=itemView.findViewById(R.id.tvDesc);
t3=itemView.findViewById(R.id.tvName);
t4= itemView.findViewById(R.id.tvStart);
t5 = itemView.findViewById(R.id.tvEnd);
view =itemView.findViewById(R.id.tvdele);
}
public void setData(String t01, String t02, String t03, String t05, final int position)
{
t1.setText("Task Comment "+t03);
t2.setText("Task Description "+t02);
t3.setText("Task Name "+ t01);
t4.setText("Start ");
t5.setText("Ënd "+t05);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onDeleteClickListener!=null)
{
onDeleteClickListener.OnDeleteClickListener(taskList.get(position));
taskList.remove(position);
notifyDataSetChanged();
}
}
});
}
}
}
MainActivity.java :
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.ivAdd);
recyclerView = findViewById(R.id.rvTask);
imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(), AddTaskActivity.class));
}
});
new newTask().execute();
}
class newTask extends AsyncTask<Void,Void, List<Task>> implements TaskAdapter.OnDeleteClickListener {
List<Task> tasks;
#Override
protected List<Task> doInBackground(Void... voids) {
tasks = DatabaseClient.getInstance(getApplicationContext()).getAppDataBase().taskDao().getAll();
return tasks;
}
#Override
protected void onPostExecute(List<Task> tasks) {
super.onPostExecute(tasks);
TaskAdapter taskAdapter = new TaskAdapter(MainActivity.this,tasks);
LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(getApplicationContext(),DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(taskAdapter);
taskAdapter.setOnDeleteClickListener(this);
}
#Override
public void OnDeleteClickListener(final Task task) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
DatabaseClient.getInstance(getApplicationContext()).getAppDataBase().taskDao().delete(task);
}
},1000);
}
}
welcome in stack
you interface is declared but not assign so your will throw null pointer when click on item inside your list, but good work your check null before fire method interface
first add setter for OnDeleteClickListener inside your adapter
public void setOnDeleteClickListener(OnDeleteClickListener listener){
this.onDeleteClickListener=listener;
}
and also add code that remove item from you list when user click item
so inside setData method update this code
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (onDeleteClickListener != null) {
onDeleteClickListener.OnDeleteClickListener(taskList.get(position));
//remove item from list and then notify adapter data is changed
taskList.remove(position);
notifyDataSetChanged();
}
}
});
finally to Triggers onDeleteClickListener inside your AsyncTask
update your code here
#Override
protected void onPostExecute(List < Task > tasks) {
super.onPostExecute(tasks);
TaskAdapter taskAdapter = new TaskAdapter(MainActivity.this, tasks, this);
LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(getApplicationContext(), DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(taskAdapter);
//pass this that refer to my interface
taskAdapter.setOnDeleteClickListener(this);
}
inside your OnDelete just use this code to run in another thread
AsyncTask.execute(new Runnable() {
#Override
public void run() {
DatabaseClient.getInstance(getApplicationContext()).getAppDataBase().taskDao().delete(task);
}
});
Advice : Don't name your variable or arguments like String
t01,String t02..etc ,choice name for like what this variable jop
like String taskComment,String taskName ..etc
i hope this help you
I am using MVVM architecture model to create an app.I have recycler view in MainActivity and on click of a delete button in recycler view item it should be removed from room database.I know item can be removed within adapter class but as I am using MVVM model I want to carry out delete operation in Repository class.
This is my code below:
UserDao.java
#Dao
public interface UserDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void Insert(User... users);
#Query("SELECT * FROM Users")
LiveData<List<User>> getRoomUsers();
#Delete
void Delete(User... user);
}
UserAdapter.java
public class UserAdapter extends
RecyclerView.Adapter<UserAdapter.ViewHolder> {
List<User> userList;
Context context;
public UserAdapter(List<User> userList, Context context) {
this.userList = userList;
this.context = context;
}
#NonNull
#Override
public UserAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_row_layout,parent,false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull UserAdapter.ViewHolder holder, int position) {
final User users = userList.get(position);
holder.row_name.setText(users.getName());
holder.row_age.setText(users.getAge());
holder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
#Override
public int getItemCount() {
return userList.size();
}
public void setUserList(List<User> userList) {
this.userList = userList;
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView row_name,row_age;
ImageView delete;
public ViewHolder(#NonNull View itemView) {
super(itemView);
row_name = itemView.findViewById(R.id.row_name);
row_age = itemView.findViewById(R.id.row_age);
delete = itemView.findViewById(R.id.delete);
}
}
}
UserRepository.java
public class UserRepository {
private Context context;
private UserDb userDb;
private LiveData<List<User>> listLiveData;
public UserRepository(Context context) {
this.context = context;
userDb = UserDb.getInstance(context);
listLiveData = userDb.userDao().getRoomUsers();
}
public void getUserList(){
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<User>> userList = apiService.getUser();
userList.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, final Response<List<User>> response) {
Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
if(response.body() != null) {
List<User> list = response.body();
for (int i = 0; i < list.size(); i++) {
String id = list.get(i).getId();
String names = list.get(i).getName();
String age = list.get(i).getAge();
User user = new User(id,names,age);
userDb.userDao().Insert(user);
}
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
Toast.makeText(context,"Data inserted",Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable e) {
Toast.makeText(context,e.getMessage(),Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Toast.makeText(context,t.getMessage(),Toast.LENGTH_LONG).show();
}
});
}
public LiveData<List<User>> getRoomUsers(){
return listLiveData;
}
}
UserViewModel.java
public class UserViewModel extends AndroidViewModel {
private UserRepository repo;
private LiveData<List<User>> listLiveData;
public UserViewModel(#NonNull Application application) {
super(application);
repo = new UserRepository(application);
listLiveData = repo.getRoomUsers();
}
public LiveData<List<User>> getListLiveData() {
return listLiveData;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
UserRepository userRepository;
RecyclerView recyclerView;
UserViewModel userModel;
List<User> userList;
UserAdapter adapter;
ProgressBar prg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
prg = findViewById(R.id.prg);
userRepository = new UserRepository(this);
userModel = ViewModelProviders.of(this).get(UserViewModel.class);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
userList = new ArrayList<>();
adapter = new UserAdapter(userList,this);
recyclerView.setAdapter(adapter);
userModel.getListLiveData().observe(this, new Observer<List<User>>() {
#Override
public void onChanged(List<User> users) {
prg.setVisibility(View.INVISIBLE);
adapter.setUserList(users);
}
});
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(MainActivity.this,AddUser.class);
startActivity(i);
}
});
userRepository.getUserList();
}
Someone please let me know how do I implement delete operation. Any help would be appreciated.
THANKS
Firstly, initialize UserViewModel in your adapter class like how you did in MainActivity, then call delete function.
holder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
userModel.deleteItem(users);
}
});
Add this function in UserModel class.
public void deleteItem(User user) = repo.deleteItem(user);
In UserRepository class, call Delete function.
public void deleteItem(User user) {
userDb.userDao().Delete(user);
}
Try to observe changes form the database with live data, whenever you perform anything, adding or deleting(in the database), in your observer refresh that data for the recycler view
Set a click listener to your Adapter
public interface OnListInteractionListener {
// TODO: Update argument type and name
void onListInteraction(User user);
}
In Adapter class
private final OnListInteractionListener mListener;
public UserAdapter(List<User> users, OnListInteractionListener listener,Context context) {
mListener = listener;
}
and in view bind holder
holder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mListener.onListInteraction(userList.get(position))
}
});
Implement that listener in your activity/fragment and from that, you can access Viewmodel and repo
I was just playing around with some code, learning new things, when I ran into this problem... I'm trying to pass a variable from my RecylcerViewAdapter to a method in MainActivity, but I just can't seem to accomplish it.
I tried a lot of different thing with interfaces and casting, but nothing did the trick. Since I'm fairly new to all of this, maybe I'm making a trivial mistake somewhere?
My Interface:
public interface AdapterCallback {
void onMethodCallback(int id);
}
This is my adapter class:
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.ViewHolder> {
private List<Post> postList;
private Context context;
private AdapterCallback listener;
public PostAdapter() {
}
public PostAdapter(List<Post> postList, Context context) {
this.postList = postList;
this.context = context;
}
public void setListener(AdapterCallback listener) {
this.listener = listener;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_layout, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder viewHolder, final int position) {
viewHolder.tvTitle.setText(postList.get(position).getTitle());
viewHolder.tvBody.setText(new StringBuilder(postList.get(position).getBody().substring(0, 20)).append("..."));
viewHolder.tvId.setText(String.valueOf(postList.get(position).getUserId()));
viewHolder.parentLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int id = postList.get(position).getId();
if (listener != null) {
listener.onMethodCallback(id);
}
}
});
}
#Override
public int getItemCount() {
return postList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tvTitle;
TextView tvBody;
TextView tvId;
LinearLayout parentLayout;
public ViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tvTitle);
tvBody = itemView.findViewById(R.id.tvBody);
tvId = itemView.findViewById(R.id.tvId);
parentLayout = itemView.findViewById(R.id.parentLayout);
}
}
}
And my MainActivity:
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivityLog";
private CompositeDisposable disposable = new CompositeDisposable();
#BindView(R.id.rvPosts)
RecyclerView rvPosts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
rvPosts.setHasFixedSize(true);
rvPosts.setLayoutManager(new LinearLayoutManager(this));
populateList();
logItems();
}
private void populateList() {
MainViewModel viewModel = ViewModelProviders.of(MainActivity.this).get(MainViewModel.class);
viewModel.makeQuery().observe(MainActivity.this, new Observer<List<Post>>() {
#Override
public void onChanged(#Nullable List<Post> posts) {
PostAdapter adapter = new PostAdapter(posts, getApplicationContext());
rvPosts.setAdapter(adapter);
}
});
}
public void logItems() {
PostAdapter adapter = new PostAdapter();
adapter.setListener(new AdapterCallback() {
#Override
public void onMethodCallback(int id) {
MainViewModel viewModel = ViewModelProviders.of(MainActivity.this).get(MainViewModel.class);
viewModel.makeSingleQuery(id).observe(MainActivity.this, new Observer<Post>() {
#Override
public void onChanged(#Nullable final Post post) {
Log.d(TAG, "onChanged: data response");
Log.d(TAG, "onChanged: " + post);
}
});
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
disposable.clear();
}
}
The populateList() method works just fine, but the logItems() method is the problem.
So when i click on a view in RecyclerView I expect the log to output the title, description and ID of the post that was clicked. nut nothing happens...
So, any help would be appreciated.
Make adapter global variable i.e. a field. Use the same object to set every properties.
private PostAdapter adapter;
Replace your logItems method with this:
public void logItems() {
adapter.setListener(new AdapterCallback() {
#Override
public void onMethodCallback(int id) {
MainViewModel viewModel = ViewModelProviders.of(MainActivity.this).get(MainViewModel.class);
viewModel.makeSingleQuery(id).observe(MainActivity.this, new Observer<Post>() {
#Override
public void onChanged(#Nullable final Post post) {
Log.d(TAG, "onChanged: data response");
Log.d(TAG, "onChanged: " + post);
}
});
}
});
}
And populateList with this:
private void populateList() {
MainViewModel viewModel = ViewModelProviders.of(MainActivity.this).get(MainViewModel.class);
viewModel.makeQuery().observe(MainActivity.this, new Observer<List<Post>>() {
#Override
public void onChanged(#Nullable List<Post> posts) {
adapter = new PostAdapter(posts, getApplicationContext());
rvPosts.setAdapter(adapter);
logItems();
}
});
}
And don't call logItems() from onCreate
This is how I implement with my ListAdapters:
public class FeedbackListAdapter extends RecyclerView.Adapter<FeedbackListAdapter.ViewHolder> {
private final ArrayList<Feedback> feedbacks;
private View.OnClickListener onItemClickListener;
private View.OnLongClickListener onItemLongClickListener;
private final Context context;
public FeedbackListAdapter(ArrayList<Feedback> feedbacks, Context context) {
this.feedbacks = feedbacks;
this.context = context;
}
public void setItemClickListener(View.OnClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public void setOnItemLongClickListener(View.OnLongClickListener onItemLongClickListener){
this.onItemLongClickListener = onItemLongClickListener;
}
public class ViewHolder extends RecyclerView.ViewHolder{
final TextView feedback, created, updated;
final LinearLayout mainLayout;
ViewHolder(View iv) {
super(iv);
/*
* Associate layout elements to Java declarations
* */
mainLayout = iv.findViewById(R.id.main_layout);
feedback = iv.findViewById(R.id.feedback);
created = iv.findViewById(R.id.created_string);
updated = iv.findViewById(R.id.updated_string);
}
}
#Override
public int getItemCount() {
return feedbacks.size();
}
#Override
#NonNull
public FeedbackListAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_feedback_table_row, parent, false);
return new FeedbackListAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(final #NonNull FeedbackListAdapter.ViewHolder holder, final int position) {
/*
* Bind data to layout
* */
try{
Feedback feedback = feedbacks.get(position);
holder.feedback.setText(feedback.getContent());
holder.created.setText(feedback.getCreated());
holder.updated.setText(feedback.getUpdated());
holder.mainLayout.setOnClickListener(this.onItemClickListener);
holder.mainLayout.setOnLongClickListener(this.onItemLongClickListener);
holder.mainLayout.setTag(feedback.getDbID());
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
holder.mainLayout.setBackgroundResource(outValue.resourceId);
}catch(IndexOutOfBoundsException e){
e.printStackTrace();
}
}
}
In onPopulateList you create an adaptor:
PostAdapter adapter = new PostAdapter(posts, getApplicationContext());
rvPosts.setAdapter(adapter);
However in public void logItems() { you used a different adapter
PostAdapter adapter = new PostAdapter();
adapter.setListener(new AdapterCallback() {
#Override
public void onMethodCallback(int id) {
...
}
});
Therefore the list is being populated with 1 adapter, but you are setting the listener on an unused second adapter.
The fix is to use the same adapter for both. If you make the adapater a field, and don't create a new one inside of logItems, but just set your listener it should work.
i.e.
// as a field in your class
private PostAdapter adapter;
then
// in `populateList()`
adapter = new PostAdapter(posts, getApplicationContext());
rvPosts.setAdapter(adapter);
and
// in `logItems()`
adapter.setListener(new AdapterCallback() {
#Override
public void onMethodCallback(int id) {
...
}
});
In Adapter
public class CustomerListAdapter extends RecyclerView.Adapter<CustomerListAdapter.OrderItemViewHolder> {
private Context mCtx;
ProgressDialog progressDialog;
//we are storing all the products in a list
private List<CustomerModel> customeritemList;
public CustomerListAdapter(Context mCtx, List<CustomerModel> orderitemList) {
this.mCtx = mCtx;
this.customeritemList = orderitemList;
progressDialog = new ProgressDialog(mCtx);
}
#NonNull
#Override
public OrderItemViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mCtx);
View view = inflater.inflate(R.layout.activity_customer_list, null);
return new OrderItemViewHolder(view, mCtx, customeritemList);
}
#Override
public void onBindViewHolder(#NonNull OrderItemViewHolder holder, int position) {
CustomerModel customer = customeritemList.get(position);
try {
//holder.textViewPINo.setText("PINo \n"+Integer.toString( order.getPINo()));
holder.c_name.setText(customer.getCustomerName());
holder.c_address.setText(customer.getAddress());
holder.c_contact.setText(customer.getMobile());
holder.i_name.setText(customer.getInteriorName());
holder.i_contact.setText(customer.getInteriorMobile());
holder.i_address.setText(customer.getAddress());
} catch (Exception E) {
E.printStackTrace();
}
}
#Override
public int getItemCount() {
return customeritemList.size();
}
class OrderItemViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener {
AlertDialog.Builder alert;
private Context mCtx;
TextView c_name, c_contact, c_address, i_name, i_contact, i_address;
TextView OrderItemID, MaterialType, Price2, Qty, AQty;
//we are storing all the products in a list
private List<CustomerModel> orderitemList;
public OrderItemViewHolder(View itemView, Context mCtx, List<CustomerModel> orderitemList) {
super(itemView);
this.mCtx = mCtx;
this.orderitemList = orderitemList;
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
// CatelogOrderDetailModel catelogOrderDetailModel = new CatelogOrderDetailModel();
c_name = itemView.findViewById(R.id.customerName);
c_contact = itemView.findViewById(R.id.contact);
c_address = itemView.findViewById(R.id.address);
i_name = itemView.findViewById(R.id.interiorName);
i_address = itemView.findViewById(R.id.interiorAddress);
i_contact = itemView.findViewById(R.id.interiorContact);
}
#Override
public void onClick(View v) {
int position = getAdapterPosition();
CustomerModel orderitem = this.orderitemList.get(position);
}
#Override
public boolean onLongClick(View v) {
int position = getAdapterPosition();
CustomerModel orderitem = this.orderitemList.get(position);
if (v.getId() == itemView.getId()) {
// showUpdateDeleteDialog(order);
try {
} catch (Exception E) {
E.printStackTrace();
}
Toast.makeText(mCtx, "lc: ", Toast.LENGTH_SHORT).show();
}
return true;
}
}
}
I am trying to develop an application that uses Room Persistence Database and displays a list on a RecyclerView, I've followed Google's tutorials and everything but I can seem to see the items on the screen, the app doesn't crash or anything just nothing appearing on the scree:
Here is the Data object class:
#Entity(tableName = "region")
public class Region {
#NonNull
#PrimaryKey(autoGenerate = true)
private int mId;
#ColumnInfo(name = "region_name")
private String mRegionName;
#ColumnInfo(name = "association")
private String mAssociation;
#ColumnInfo(name = "season")
private String mSeason;
public Region(#NonNull int id, String regionName, String association, String season) {
mId = id;
mRegionName = regionName;
mAssociation = association;
mSeason = season;
}
#NonNull
public int getId() {
return mId;
}
public void setId(#NonNull int id) {
mId = id;
}
public String getRegionName() {
return mRegionName;
}
public void setRegionName(String regionName) {
mRegionName = regionName;
}
public String getAssociation() {
return mAssociation;
}
public void setAssociation(String association) {
mAssociation = association;
}
public String getSeason() {
return mSeason;
}
public void setSeason(String season) {
mSeason = season;
}
Here is the Data Dao:
#Dao
public interface RegionDao {
#Query("SELECT * FROM region")
List<Region> getAll();
#Insert
void insert(Region... region);
#Query("DELETE FROM region")
void deleteAll();
Here is the Adapter Class:
public class RegionsAdapter extends RecyclerView.Adapter<RegionsAdapter.RegionViewHolder> {
private List<Region> mRegionList;
private Context mContext;
private int mRowLayout;
private RecyclerViewClickListener mListener;
public static class RegionViewHolder extends RecyclerView.ViewHolder{
LinearLayout regionLayout;
TextView regionName, regionAss, regionSeason;
public RegionViewHolder(View v, RecyclerViewClickListener listener){
super(v);
regionLayout = v.findViewById(R.id.region_layout);
regionName = v.findViewById(R.id.region_name);
regionAss = v.findViewById(R.id.region_ass);
regionSeason = v.findViewById(R.id.region_season);
}
}
public void setClickListener(RecyclerViewClickListener recyclerViewClickListener){
mListener = recyclerViewClickListener;
}
public RegionsAdapter(List<Region> regionList, Context context, int rowLayout, RecyclerViewClickListener listener) {
mRegionList = regionList;
mContext = context;
mRowLayout = rowLayout;
mListener = listener;
}
#Override
public RegionsAdapter.RegionViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(mRowLayout, parent, false);
final RegionsAdapter.RegionViewHolder holder = new RegionsAdapter.RegionViewHolder(view, mListener);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mListener.onClick(v,holder.getLayoutPosition());
}
});
return holder;
}
#Override
public void onBindViewHolder(RegionViewHolder holder, final int position){
holder.regionName.setText(mRegionList.get(position).getRegionName());
holder.regionSeason.setText(mRegionList.get(position).getSeason());
holder.regionAss.setText(mRegionList.get(position).getAssociation());
}
#Override
public int getItemCount() {
return mRegionList.size();
}
And this is the fragment that it is supposed to show the data base on:
public class RegionFragment extends Fragment {
private List<Region> mRegionList;
private RecyclerView mRecyclerView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.region_fragment, container, false);
//whenever the activity is started, it reads data from database and stores it into
// local array list 'mRegionList'
final AppDatabase db = Room.databaseBuilder(getActivity().getApplicationContext(),
AppDatabase.class,"app_database").allowMainThreadQueries().build();
List<Region> regionList = db.getRegionDao().getAll();
// Initializing the RecyclerView
mRecyclerView = view.findViewById(R.id.region_rv);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity().getApplicationContext()));
RegionsAdapter adapter = new RegionsAdapter(regionList, getActivity().
getApplicationContext(), R.id.region_layout, new RecyclerViewClickListener() {
#Override
public void onClick(View view, int position) {
// Handling what happenes when a card is clicked
// TODO handling
}
});
mRecyclerView.setAdapter(adapter);
return view;
}
This is the AppDatabase Class:
#Database(entities = {Region.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract RegionDao getRegionDao();
private static AppDatabase sInstance;
static AppDatabase getDatabase(final Context context) {
if (sInstance == null) {
synchronized (AppDatabase.class){
if (sInstance == null) {
sInstance = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class,
"database").addCallback(sRoomDatabaseCallback).build();
}
}
}
return sInstance;
}
private static RoomDatabase.Callback sRoomDatabaseCallback =
new RoomDatabase.Callback() {
#Override
public void onOpen(#NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
new PopulateDbAsync(sInstance).execute();
}
};
And finally the AsyncClass to pre populate the database:
class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
private final RegionDao mDao;
PopulateDbAsync(AppDatabase db) {
mDao = db.getRegionDao();
}
#Override
protected Void doInBackground(final Void... params) {
mDao.deleteAll();
Region region = new Region(0,"Europe","UEFA","2017/2018");
mDao.insert(region);
region = new Region(1, "Americas","CONCACAF","2017/2018");
mDao.insert(region);
region = new Region(2, "Asia","AFC","2017/2018");
mDao.insert(region);
region = new Region(3, "International","FIFA","2017/2018");
mDao.insert(region);
region = new Region(4, "Other","ConIFA","2017/2018");
mDao.insert(region);
return null;
}
if your id is PrimaryKey(autoGenerate = true), then don't set id in constructor.
update your onCreateView , you are use wrong database.
final AppDatabase db =AppDatabase.getDatabase(getActivity());
List<Region> regionList = db.getRegionDao().getAll();// here you have fire your query in background thread. you can search for that
In a project that is an Activity Model, Presenter and Model, the Activity Theme and Presenter and Presenter Model. When I do #Inject in Presenter to instantiate the Model it is never instantiated.
Do you need a dependency "cascade"?
FATAL EXCEPTION: main Process: fipedaggerrxjava, PID: 22258
java.lang.NullPointerException: Attempt to invoke interface method
'void
fipedaggerrxjava.mvp.SelectMarcaContractMVP$Model.getMarcas(java.lang.String)'
on a null object reference at
fipedaggerrxjava.module.marca.MarcaPresenter.initData(MarcaPresenter.java:35)
at
fipedaggerrxjava.module.marca.MarcaActivity$1.onCheckedChanged(MarcaActivity.java:63)
I already checked in Debug and really the Model that is not being instantiated by Dagger but I can not understand why.
App
public class App extends Application implements HasActivityInjector{
#Inject
public DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().build().inject(App.this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
ActivityBuilder
#Module
public abstract class ActivityBuilder {
#Binds
#IntoMap
#ActivityKey(MarcaActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindMarcaActivity (MarcaComponent.Builder builder);
}
AppComponent
#Component(modules = {ActivityBuilder.class, AndroidInjectionModule.class, AppModule.class})
#Singleton
public interface AppComponent {
void inject(App app);
}
AppModule
#Module(subcomponents = MarcaComponent.class)
public class AppModule {
#Provides
#Singleton
#Named("URL_MARCA")
String provideStringURLBase(){
return "https://fipe.parallelum.com.br/api/v1/";
}
#Provides
#Singleton
Context provideContext(App app){
return app;
}
#Provides
#Singleton
Gson provideGsonRepositorie(){
return new GsonBuilder()
.create();
}
#Singleton
#Provides
OkHttpClient provideOkHttpCliente1(){
return new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();
}
#Singleton
#Provides
RxJavaCallAdapterFactory provideRxJavaCallAdapterFactory(){
return RxJavaCallAdapterFactory.create();
}
#Provides
#Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient, Gson gson, RxJavaCallAdapterFactory rxAdapter, #Named("URL_MARCA") String stringBaseURL){
return new Retrofit.Builder()
.baseUrl(stringBaseURL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(rxAdapter)
.client(okHttpClient)
.build();
}
}
MarcaComponent
#Subcomponent(modules = MarcaModule.class)
#PerMarca
public interface MarcaComponent extends AndroidInjector<MarcaActivity>{
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaActivity> {}
}
MarcaModule
#Module
public class MarcaModule{
#Provides
#PerMarca
APIFIPE provideAPIFIPE(Retrofit retrofit){
return retrofit.create(APIFIPE.class);
}
#Provides
#PerMarca
View provideViewMarca(MarcaActivity activity){
return activity;
}
#Provides
#PerMarca
Presenter providePresenterMarca(){
return new MarcaPresenter();
}
#Provides
#PerMarca
Model provideModelMarca(){
return new MarcaModel();
}
}
AdapterMarca
public class AdapterMarca extends BaseAdapter {
private List<Marca> mListMarca;
#Inject
public Context mContext;
public AdapterMarca(List<Marca> listMarca){
this.mListMarca = listMarca;
}
#Override
public int getCount() {
return mListMarca.size();
}
#Override
public Object getItem(int position) {
return mListMarca.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_list_item, parent, false);
TextView tvNome = view.findViewById(R.id.tv_marca);
tvNome.setText(mListMarca.get(position).getName().toString());
return view;
}
public void addListMarca(List<Marca> marcaList){
mListMarca.clear();
mListMarca.addAll(marcaList);
notifyDataSetChanged();
}
}
MarcaActivity
public class MarcaActivity extends BaseActivity implements HasActivityInjector, View {
private RadioGroup radioGroupMarca;
private String tipoSelect = "";
private List<Marca> mListMarca;
private AdapterMarca mAdapterMarca;
private ListView listViewMarca;
#Inject
public Presenter mMarcaPresenter;
#Inject
protected DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(MarcaActivity.this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listViewMarca = findViewById(R.id.lv_marca);
radioGroupMarca = findViewById(R.id.rg_tipo);
radioGroupMarca.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
int id = group.getCheckedRadioButtonId();
switch (id){
case R.id.rb_carros : tipoSelect = "carros";
mMarcaPresenter.initData(tipoSelect);
break;
case R.id.rb_motos : tipoSelect = "motos";
mMarcaPresenter.initData(tipoSelect);
break;
case R.id.rb_caminhoes : tipoSelect = "caminhoes";
mMarcaPresenter.initData(tipoSelect);
break;
}
}
});
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
#Override
public void onMarcaLoader(List<Marca> listMarcas) {
if(mListMarca==null && listMarcas!=null){
initListView();
}
if(mAdapterMarca!=null){
mListMarca.clear();
mListMarca = listMarcas;
mAdapterMarca.addListMarca(mListMarca);
}
}
private void initListView(){
mAdapterMarca = new AdapterMarca(mListMarca);
listViewMarca.setAdapter(mAdapterMarca);
}
}
MarcaPresenter
#PerMarca
public class MarcaPresenter implements Presenter {
#Inject
View mMarcaView;
#Inject
Model mMarcaModel;
#Inject
public MarcaPresenter(){
}
#Override
public void initData(String tipoMarca) {
mMarcaModel.getMarcas(tipoMarca);
}
#Override
public void getMarcas(List<Marca> listMarcas) {
mMarcaView.onMarcaLoader(listMarcas);
}
#Override
public void onShowDialog(String title, String msg) {
mMarcaView.onShowDialog(title, msg);
}
#Override
public void onHideShowDialog() {
mMarcaView.onHideShowDialog();
}
#Override
public void onShowToast(String s) {
mMarcaView.onShowToast(s);
}
}
MarcaModel
#PerMarca
public class MarcaModel implements Model {
#Inject
APIFIPE mApiFIPE;
#Inject
Presenter mMarcaPresenter;
#Inject
public MarcaModel(){
}
#Override
public void getMarcas(String tipoVeiculo) {
final List<Marca> marcaList = new ArrayList<>();
Observable<List<Marca>> observable = mApiFIPE.getRepositories(tipoVeiculo);
observable.subscribe(new Observer<List<Marca>>() {
#Override
public void onCompleted() {
mMarcaPresenter.getMarcas(marcaList);
}
#Override
public void onError(Throwable e) {
mMarcaPresenter.onShowDialog("Erro", "Falha ao carregar lista de marcas");
}
#Override
public void onNext(List<Marca> marcas) {
marcaList.addAll(marcas);
}
});
}
}
In below lines:
public void onCompleted() {
mMarcaPresenter.getMarcas(marcaList);
}
-You trying to use mMarcePResenter and you inject it but you still need to use the App's component get assigned to mMarcaPresenter.
Initialize the injected value in below constructor:
#Inject
public MarcaModel(){
mMarcaPresenter =
}
You have declared provider methods(in MarcaModule) for your model and presenter classes. Dagger uses those provider methods, ending up not supplying the field injected values.
Therefore, you need to supply your dependencies in your module(s), i.e. via Model and Presenter constructors.
MarcaModel
...
Presenter mMarcaPresenter;
#Inject
public MarcaModel(Presenter presenter){
mMarcaPresenter = presenter
}
...
MarcaModule
#Module
public class MarcaModule{
...
#Provides
#PerMarca
Model provideModelMarca(Presenter presenter){
return new MarcaModel(presenter);
}
}
Reading Dagger user's guide carefully might help you even more;
https://google.github.io/dagger/users-guide