Before I wrote the codes below, I searched here on SO on how to populate recyclerview with database table. All the answers I came across were virtually saying the same thing "RecyclerView doesn't have CursorAdapter like listview. Write an adpater that extends CursorRecyclerViewAdapter etc"
However, I found out that by iterating the cursor and putting the data in an array, I was able to populate recyclerview from database like I do with json and html data.
So these are my codes:
BookItem
public class BookItem {
private String title;
private String imageUrl;
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
BookAdapter
public class BookAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
List<BookItem> mBookItems;
public BookAdapter(List<BookItem> bookItems, Context context) {
mBookItems = bookItems;
mContext = context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.book_item, parent, false);
return new BookViewHolder(view);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
BookItem bookItem = mBookItems.get(position);
ImageLoader imageLoader = VolleyRequest.getInstance(mContext).getImageLoader();
imageLoader.get(bookItem.getImageUrl(), ImageLoader.getImageListener(((BookViewHolder) holder).thumbNail,
R.drawable.shop_book_thumb, R.drawable.shop_book_thumb), 120, 120, ImageView.ScaleType.CENTER_INSIDE);
((BookViewHolder) holder).thumbNail.setImageUrl(bookItem.getImageUrl(), imageLoader);
((BookViewHolder) holder).mTitle.setText(bookItem.getTitle());
}
#Override
public int getItemCount() {
return mBookItems.size();
}
public class BookViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.book_item_thumb) NetworkImageView thumbNail;
#BindView(R.id.book_item_title) TextView mTitle;
public BookViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
Part of BookFragment
private void getBookDb () {
Log.d(TAG, "getBookDb called");
mDatabase = mBookHelper.getWritableDatabase();
Cursor cursor = mDatabase.rawQuery("SELECT * FROM " + BookEntry.NAME, null);
if (cursor != null && cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
BookItem bookItem = new BookItem();
String title = cursor.getString(cursor.getColumnIndex(BookEntry.TITLE));
String imageUrl = cursor.getString(cursor.getColumnIndex(BookEntry.IMAGE_URL));
bookItem.setTitle(title);
bookItem.setImageUrl(imageUrl);
mBookItems.add(bookItem);
cursor.moveToNext();
}
} else {
Log.d(TAG, "Database is empty");
emptyDataLayout.setVisibility(View.VISIBLE);
}
if (cursor != null) {
cursor.close();
}
mDatabase.close();
mBookAdapter.notifyItemRangeChanged(0, mBookAdapter.getItemCount());
}
In my eyes, it's working perfectly, but then I am only a beginner.
So, please what is wrong with this implementation and what's the better way of doing this?
what is wrong with this implementation
You are doing database I/O on the main application thread, in your getBookDb() method. Do not do I/O on the main application thread, please.
In general, there is nothing particularly wrong with converting a Cursor into a List of some sort of POJO. Similarly, there is nothing particularly wrong with converting JSON into a List of some sort of POJO. However, ideally, you only do that sort of conversion if you are getting something useful out of it. You don't have to do that conversion just to use a Cursor in a RecyclerView.
Related
everyone, I was trying to make a music app, and for this, I Created a Horizontal RecyclerView in my HomeFragment and my horizontal RecyclerView is getting an image with artist name.
But after clicking I load another Activity. In my other activity, I was trying to load SongsData from firebase in a listView with RecyclerView.
But the problem is I am not getting data from Firebase and it is returning null data. I provided my code below and here is the screenshot of my Firebase database:- ScreenShot
My List Class:-
public class TestUploads
{
private String songName;
private String songImageUri;
private String songUrl;
private String artistName;
public TestUploads() {
}
public String getSongName() {
return songName;
}
public void setSongName(String SongName) {
this.songName = SongName;
}
public String getSongImageUri() {
return songImageUri;
}
public void setSongImageUri(String SongImageUri) {
this.songImageUri = SongImageUri;
}
public String getSongUrl() {
return songUrl;
}
public void setSongUrl(String SongUrl) {
this.songUrl = songUrl;
}
public TestUploads(String SongImageUri, String SongName, String SongUrl ) {
this.songName = SongName;
this.artistName = SongImageUri;
this.songUrl = SongUrl;
}
}
My Adapter Class:-
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.TestViewHolder>{
private Context mContext;
private List<TestUploads> mUploads;
public TestAdapter(Context context , List<TestUploads> uploads) {
mContext = context;
mUploads = uploads;
}
#NonNull
#Override
public TestViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.test_package_layout , parent ,false);
return new TestViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull TestViewHolder holder, int position) {
TestUploads uploadcurrent = mUploads.get(position);
holder.name.setText(uploadcurrent.getSongName());
Glide.with(mContext)
.load(uploadcurrent.getSongImageUri())
.into(holder.image_view);
}
#Override
public int getItemCount() {
return mUploads
.size();
}
public class TestViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public TextView artist_name;
public CircleImageView image_view;
public TestViewHolder(#NonNull View itemView) {
super(itemView);
name = itemView.findViewById(R.id.test_package_song_name);
artist_name = itemView.findViewById(R.id.test_package_artist_name);
image_view = itemView.findViewById(R.id.test_package_image_name);
}
}
}
My Activity:-
public class TestActivity extends AppCompatActivity {
private ValueEventListener listener;
private DatabaseReference reference;
private List<TestUploads> mUploads;
private RecyclerView mRecyclerView;
private TestAdapter adapter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_package_activity);
reference = FirebaseDatabase.getInstance().getReference("ArtistView").child(getIntent().getStringExtra("Artist"))
.child("Songs");
Toast.makeText(this, "" + getIntent().getStringExtra("Artist"), Toast.LENGTH_SHORT).show();
mUploads = new ArrayList<>();
mRecyclerView = findViewById(R.id.test_pacakge_recyclerView);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.smoothScrollToPosition(0);
adapter = new TestAdapter(this , mUploads);
mRecyclerView.setAdapter(adapter);
listener = reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mUploads.clear();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
TestUploads uploads =postSnapshot.getValue(TestUploads.class);
mUploads.add(uploads);
}
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
Sorry for so much code but this is not hard to solve. If you find the solution please reply to me. Thanks for reading this.
The problem in your code lies in the fact that the names of the fields in your TestUploads class are different than the name of the properties in your database. You have in your TestUploads class a field named songName but in your database, I see it as SongName and this is not correct. The names must match. When you are using a getter named getSongName(), Firebase is looking in the database for a field named songName and not SongName. See the lowercase s letter vs. capital letter S?
There are two ways in which you can solve this problem. The first one would be to remove the data in your database and add it again using field names that start with lowercase, as exist in your TestUploads class.
If you are not allowed to use the first solution, then the second approach will be to use annotations. So you should use the PropertyName annotation in front of the getters. So in your TestUploads class, a getter should look like this:
#PropertyName("SongName")
public String getSongName() {
return songName;
}
i receive NPE when i want to show pics in picasso,
pay attention that i've another adapter which i used picasso,
but in my new adapter this npe error for boolean happens,
and my friends used this code and it performed well,
here is the code that error says
public boolean isVideoNews() {
return !image.isEmpty() && !video.isEmpty() ;
}
and the part that is related to this code in my adapter
videoIndicator.setVisibility(news.isVideoNews() ? View.VISIBLE : View.GONE);
my adapter
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.NewsViewHolder> {
private List<News> newsList;
public NewsAdapter(List<News> newsList) {
this.newsList = newsList;
}
#Override
public NewsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new NewsViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news, parent, false));
}
#Override
public void onBindViewHolder(NewsViewHolder holder, int position) {
holder.bindNews(newsList.get(position));
}
#Override
public int getItemCount() {
return newsList.size();
}
class NewsViewHolder extends RecyclerView.ViewHolder {
private ImageView newsImageView;
private TextView titleTextView;
private TextView dateTextView;
private View videoIndicator;
public NewsViewHolder(View itemView) {
super(itemView);
newsImageView = itemView.findViewById(R.id.iv_news_image);
videoIndicator = itemView.findViewById(R.id.iv_news_VideoIndicator);
titleTextView = itemView.findViewById(R.id.tv_news_title);
dateTextView = itemView.findViewById(R.id.tv_news_date);
}
public void bindNews(News news) {
Picasso.get().load(news.getImage()).into(newsImageView);
videoIndicator.setVisibility(news.isVideoNews() ? View.VISIBLE : View.GONE);
titleTextView.setText(news.getTitle());
dateTextView.setText(news.getDate());
}
}
}
and this is my data model
public class News {
private int id;
private String title;
private String content;
private String date;
private String image;
private String video;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getVideo() {
return video;
}
public void setVideo(String video) {
this.video = video;
}
public boolean isVideoNews() {
return !image.isEmpty() && !video.isEmpty() ;
}
}
Error
FATAL EXCEPTION: main
Process: com.example.melal.newsapp, PID: 5958
java.lang.IllegalArgumentException: Path must not be empty.
at com.squareup.picasso.Picasso.load(Picasso.java:332)
at com.example.melal.newsapp.home.NewsAdapter$NewsViewHolder.bindNews(NewsAdapter.java:53)
at com.example.melal.newsapp.home.NewsAdapter.onBindViewHolder(NewsAdapter.java:31)
at com.example.melal.newsapp.home.NewsAdapter.onBindViewHolder(NewsAdapter.java:17)
FATAL EXCEPTION: main
Process: com.example.melal.newsapp, PID: 9523
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.isEmpty()' on a null object reference
at com.example.melal.newsapp.data.News.isVideoNews(News.java:60)
at com.example.melal.newsapp.home.NewsAdapter$NewsViewHolder.bindNews(NewsAdapter.java:53)
at com.example.melal.newsapp.home.NewsAdapter.onBindViewHolder(NewsAdapter.java:30)
at com.example.melal.newsapp.home.NewsAdapter.onBindViewHolder(NewsAdapter.java:16)
I get every data when I debug and my texts and title and every things show on UI but just my image does not show,my banners pic show in my another adapter on UI but main problem is the adapter that i told,
please help me
You have to check here null and empty values for path of Image.
Put following line in if condition like:
if(TextUtils.isEmpty(news.getImage())) {
// Load default image
newsImageView.setImageResource(R.drawable.placeholder);
} else {
Picasso.get().load(news.getImage()).into(newsImageView);
}
Hope it will work.
You need to provide seralized name and expose that field of Model class so that your gson can parse it. Currently you have not set it to any field and thus GSON is not parsing your response accordingly. Below is sample code for how to define it in class. You need to expose all your class fields accordingly.
#SerializedName("foo") //name inside quotes must match with your json field name.
#Expose
private String foo;
I am using recyclerView and Adapter to fetch the data in profileActivity
here is my
public class studentDetailsRecyclerActivity extends AppCompatActivity {
//recyclerview to set the details for UI in the student profile activity
private RecyclerView mRecyclerView;
private storeDetailsAdapter mStoreDetailsAdapter;
private List<storeStudentDetails> studentDetailsList;
private FirebaseFirestore dbReference;
private ProgressBar mProgressBar;
private String TAG = studentDetailsRecyclerActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
dbReference = FirebaseFirestore.getInstance();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_details);
mProgressBar = findViewById(R.id.progressbar);
mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView_products);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
studentDetailsList = new ArrayList<>();
mStoreDetailsAdapter = new storeDetailsAdapter(this,studentDetailsList);
mRecyclerView.setAdapter(mStoreDetailsAdapter);
//to get the "details" this is our collection from firestore so we must fetch them
//by calling the addOnSuccessListener
dbReference.collection("details").get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) { //we must have to hide the progress bar when the data gets loaded
//here queryDocumentsSnapshot will hold all the "details" which is your collection in firestore
if(!queryDocumentSnapshots.isEmpty()){
//we must have to create empty list so that to store all
//details from DocumentsSnapshots
List<DocumentSnapshot> list = queryDocumentSnapshots.getDocuments();
//enhanced for loop because we have to give every index documentSnapShot
for(DocumentSnapshot d: list){
storeStudentDetails sd = d.toObject(storeStudentDetails.class);
studentDetailsList.add(sd);
Log.d(TAG, "onSuccess: " + sd.toString());
}
//to refresh and sync we must have to use notifyDataSetChanged
mStoreDetailsAdapter.notifyDataSetChanged();
}
}
}) .addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Error getting data!!!", Toast.LENGTH_LONG).show();
}
});
}
}
and here is my storeDetailsAdapter
import java.util.List;
public class storeDetailsAdapter extends RecyclerView.Adapter<storeDetailsAdapter.StudentViewHolder>{
private Context context;
private List<storeStudentDetails> studentDetailsList;
public storeDetailsAdapter(Context context, List<storeStudentDetails> studentDetailsList) {
this.context = context;
this.studentDetailsList = studentDetailsList;
}
#NonNull
#Override
public StudentViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new StudentViewHolder(
LayoutInflater.from(context).inflate(R.layout.profile_activity, parent, false)
);
}
#Override
public void onBindViewHolder(#NonNull StudentViewHolder holder, int position) {
storeStudentDetails mStoreDetails = studentDetailsList.get(position);
holder.studName.setText(mStoreDetails.getStudentName());
holder.rollNum.setText(mStoreDetails.getRollNo());
holder.bookName.setText( mStoreDetails.getBook());
holder.fine.setText("Fine:" + mStoreDetails.getFine());
holder.dept.setText(mStoreDetails.getDept());
}
#Override
public int getItemCount() {
return studentDetailsList.size();
}
class StudentViewHolder extends RecyclerView.ViewHolder {
TextView studName,rollNum,bookName,dept,fine;
public StudentViewHolder(View itemView) {
super(itemView);
studName=itemView.findViewById(R.id.studentName_prof);
rollNum = itemView.findViewById(R.id.rollNumber_prof);
bookName = itemView.findViewById(R.id.bookName_prof);
fine = itemView.findViewById(R.id.fineAmt_prof);
dept = itemView.findViewById(R.id.department_prof);
}
}
}
and here is my StoreStudentDetails class:
public class storeStudentDetails implements Serializable {
private String studentName;
private String rollNo;
private String book;
private Double fine;
private String dept;
#Exclude private String id;
public storeStudentDetails() {
}
public storeStudentDetails(String studentName, String rollNo,String book, double fine ,String dept) {
this.studentName = studentName;
this.rollNo = rollNo;
this.book = book;
this.fine = fine;
this.dept = dept;
}
public void setId(String id) {
this.id = id;
}
public String getStudentName() {
return studentName;
}
public String getRollNo() {
return rollNo;
}
public String getBook() {
return book;
}
public Double getFine() {
return fine;
}
public String getDept() {
return dept;
}
public String getId() {
return id;
}
}
To solve this, please move the following lines of code:
mStoreDetailsAdapter = new storeDetailsAdapter(this,studentDetailsList);
mRecyclerView.setAdapter(mStoreDetailsAdapter);
Right before the following line of code:
mStoreDetailsAdapter.notifyDataSetChanged();
And this is because onSuccess() method has an asynchronous behavior and by the time you are setting the adapter outside the callback your list is empty.
As you can see, the easiest solution for this problem is to move those lines of code inside the callback. but if you want to use the value of your studentDetailsList outside the onSuccess() method, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
I have defined a room SQLite database as:
#Database(entities = {PlaceSaved.class},version = 1)
public abstract class PlaceDatabase extends RoomDatabase {
public abstract DatabaseInterface databaseInterface();
#Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
return null;
}
#Override
protected InvalidationTracker createInvalidationTracker() {
return null;
}
}
with definitions at:
#Entity
public class PlaceSaved {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "time")
private String time;
#ColumnInfo(name="title")
private String title;
public PlaceSaved(){
}
public PlaceSaved(String time, String title) {
this.time = time;
this.title = title;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
the corresponding DAO is:
#Dao
public interface DatabaseInterface {
#Query("SELECT * FROM placesaved")
List<PlaceSaved> getAllItems();
#Insert
void insertAll(PlaceSaved... todoListItems);
#Delete
public void delete(PlaceSaved... todoListItems);
#Update
public void update(PlaceSaved...todoListItems);
}
and those data are displayed via a recyclerview with each item layout defined as:
<TextView
android:id="#+id/secondLine"/>
<TextView
android:id="#+id/firstLine"/>
<ImageButton
android:id="#+id/delicon"/>
Now, I want to use this delicon ImageButton to delete the corresponding entry.
So, I tried to put this in my Adapter as (NOTE: Updated, see Updated code at the end):
public class PlacesAdapter extends RecyclerView.Adapter<PlacesAdapter.ViewHolder> {
//PlaceDatabase db;
List<PlaceSaved> items;
public PlacesAdapter(List<PlaceSaved> items) {
this.items = items;
}
#Override
public PlacesAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.places_list_item,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(PlacesAdapter.ViewHolder holder, final int position) {
holder.name.setText(items.get(position).getTitle());
holder.time.setText(items.get(position).getTime());
// holder.delbutton.setClickable(true);
holder.delbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
removeItem(items);
}
});
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView name;
public TextView time;
public ImageButton delbutton;
public ViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.secondLine);
time= itemView.findViewById(R.id.firstLine);
delbutton = itemView.findViewById(R.id.delicon);
}
}
private void removeItem(PlaceSaved infoItem){
PlaceSaved placeSaved = new PlaceSaved();
placeSaved.delete(infoItem);
}
}
and the recyclerview is called as: (**Note: onCreate is updated and posted at the end)
public class PlacesActivity extends AppCompatActivity {
FloatingActionButton fab, fab1, fab2, fab3;
LinearLayout fabLayout1, fabLayout2, fabLayout3;
boolean isFABOpen=false;
View fabBGLayout;
public static RecyclerView recyclerView;
public static RecyclerView.Adapter adapter;
List<PlaceSaved> items;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.places_layout);
//whenever the activity is started, it reads data from database and stores it into
// local array list 'items'
final PlaceDatabase db = Room.databaseBuilder(getApplicationContext(), PlaceDatabase.class, "production")
.build();
//it is very bad practice to pull data from Room on main UI thread,
// that's why we create another thread which we use for getting the data and displaying it
Runnable r = new Runnable() {
#Override
public void run() {
items = db.databaseInterface().getAllItems();
recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplication()));
adapter = new PlacesAdapter(items);
adapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);
}
};
This gives compilation time error:
PlacesAdapter.java
Error:(43, 22) error: incompatible types: List<PlaceSaved> cannot be converted to PlaceSaved
Error:(68, 17) error: cannot find symbol method delete(PlaceSaved)
Kindly help me to solve this problem.
UPDATE
By Vishu's answer, I have updated my adapter as:
public class PlacesAdapter extends RecyclerView.Adapter<PlacesAdapter.ViewHolder> {
private static final String TAG = "MyActivity";
List<PlaceSaved> items;
PlaceDatabase db;
public PlacesAdapter(List<PlaceSaved> items, PlaceDatabase db) {
this.items = items;
this.db = db;
}
#Override
public PlacesAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.places_list_item,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final PlacesAdapter.ViewHolder holder, final int position) {
holder.name.setText(items.get(position).getTitle());
holder.time.setText(items.get(position).getTime());
holder.delbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
removeItem(items.get(holder.getAdapterPosition()));
}
});
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView name;
public TextView time;
public ImageButton delbutton;
public ViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.secondLine);
time= itemView.findViewById(R.id.firstLine);
delbutton = itemView.findViewById(R.id.delicon);
}
}
private void removeItem(PlaceSaved infoItem){
// db.delete(infoItem);
Log.v(TAG, "remove Item called");
}
}
and in PlacesActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.places_layout);
final PlaceDatabase db = Room.databaseBuilder(getApplicationContext(), PlaceDatabase.class, "production")
.build();
Runnable r = new Runnable() {
#Override
public void run() {
items = db.databaseInterface().getAllItems();
recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplication()));
adapter = new PlacesAdapter(items, db);
adapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);
}
};
which is still giving syntax error :
Error:(71, 17) error: cannot find symbol method delete(PlaceSaved)
and 2 warning (not due to Vishu's answer, it was present before):
PlaceSaved.java
Warning:(11, 8) There are multiple good constructors and Room will pick the no-arg constructor. You can use the #Ignore annotation to eliminate unwanted constructors.
PlaceDatabase.java
Warning:(13, 17) Schema export directory is not provided to the annotation processor so we cannot export the schema. You can either provide `room.schemaLocation` annotation processor argument OR set exportSchema to false.
Error: Update:
adding db.databaseInterface().delete(infoItem);
in removeitem gives:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:164)
at android.arch.persistence.room.RoomDatabase.beginTransaction(RoomDatabase.java:211)
at DatabaseInterface_Impl.delete(DatabaseInterface_Impl.java:94)
at PlacesAdapter.removeItem(PlacesAdapter.java:69)
at PlacesAdapter.access$000(PlacesAdapter.java:20)
at PlacesAdapter$1.onClick(PlacesAdapter.java:45)
at android.view.View.performClick(View.java:6294)
why are you passing ArrayList of PlaceSaved in removeItem(). removeItem() accept only PlaceSaved.
So change your onclick to
removeItem(items.get(holder.getAdapterPosition()));
make holder final in method signature final PlacesAdapter.ViewHolder holder else it won't compile.
You have defined delete in PlaceDatabase and you are calling on PlaceSaved that's why you are getting Error:(68, 17) error: cannot find symbol method delete(PlaceSaved)
you can pass db from in PlacesAdapter just like items like PlacesAdapter(items, db)
change
adapter = new PlacesAdapter(items);
to
adapter = new PlacesAdapter(items, db);
Now your PlacesAdapter will have db instance. and you can replce placeSaved.delete(infoItem); with db.delete(infoItem)
Change
List<PlaceSaved> items;
public PlacesAdapter(List<PlaceSaved> items) {
this.items = items;
}
to
List<PlaceSaved> items;
PlaceDatabase db;
public PlacesAdapter(List<PlaceSaved> items, PlaceDatabase db) {
this.items = items;
this.db = db
}
And
Change
placeSaved.delete(infoItem);
to
db.databaseInterface().delete(infoItem);
I process and setup content which is being delivered as a String which includes http links (http://..) and (can include) normal text (info, mirror, ...).
I am trying to set the normal text as headers in my recylcerview.
This all needs to happen dynamically as I do not know upfront if the String includes normal text and if it is included, how many normal text items there are.
So the recylcerview (could) should look like this:
http://www.link1.com
MIRROR
https://www.link2.com
https://www.link3.com
INFO
http://www.link4.com
http://www.link5.com
Now it looks like this:
MIRROR
http://www.link1.com
http://www.link2.com
http://www.link3.com
According the log it looks ok:
/testpackage.com E/ITEM: https://link1.com
/testpackage.com E/ITEM: MIRROR
/testpackage.com E/ITEM: https://link2.com
/testpackage.com E/ITEM: https://link3.com
I am having trouble getting the headers in the right position in the recyclerview.
I already looked up some solutions and I did read about several possibilities like SectionedRecyclerViewAdapter but I wouldn`t know how to implement it for my needs.
I guess the tricky part of my problem is it needs to be setup dynamically.
My code till now:
SectionViewHolder class:
public class SectionViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public SectionViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.sectionHeader);
}
}
Activity where I setup things:
#EActivity(R.layout.downloads_activity)
public class DownloadsActivity extends BaseActivity {
String outPut;
RecyclerView recyclerView;
AdapterSectionRecycler adapterRecycler;
List<SectionHeader> sectionHeaders;
...
#AfterViews
public void init() {
int i = 0;
List<Child> childList = new ArrayList<>();
sectionHeaders = new ArrayList<>();
//initialize RecyclerView
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
//setLayout Manager
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setHasFixedSize(true);
...
// GET RID OF BAD HTML and SPLIT AS OF <br/>
String html = outPut.replaceAll("<br/></a>", "</a>");
String[] lines = html.split("<br/>");
...
// loop and process URL links and normal text
for (String str : Arrays.asList(lines)) {
Jsoup.parse(str).text();
str.split(",");
// function to parse the URL links and normal text from String
String item = unescapeJavaString(String.valueOf(Html.fromHtml(str)));
if (!str.contains("Use following links")) {
// Add URL links to list
if (item.startsWith("http")) {
childList.add(new Child(item));
}
// Add section headers
if (!item.startsWith("http")) {
sectionHeaders.add(new SectionHeader(childList, item, i));
}
}
Log.i("ITEM", item);
}
adapterRecycler = new AdapterSectionRecycler(this, sectionHeaders);
recyclerView.setAdapter(adapterRecycler);
}
SectionHeader class:
public class SectionHeader implements Section<Child>, Comparable<SectionHeader> {
List<Child> childList;
public String sectionText;
int index;
public SectionHeader(List<Child> childList, String sectionText, int index) {
this.childList = childList;
this.sectionText = sectionText;
this.index = index;
}
#Override
public List<Child> getChildItems() {
return childList;
}
public String getSectionText() {
return sectionText;
}
#Override
public int compareTo(SectionHeader another) {
if (this.index > another.index) {
return -1;
} else {
return 1;
}
}
}
ChildViewHolder:
public class ChildViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public ChildViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.child);
}
}
Child class:
public class Child {
String name;
public Child(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
My AdapterSectionRecycler:
public class AdapterSectionRecycler extends SectionRecyclerViewAdapter<SectionHeader, Child, SectionViewHolder, ChildViewHolder> {
Context context;
public AdapterSectionRecycler(Context context, List<SectionHeader> sectionHeaderItemList) {
super(context, sectionHeaderItemList);
this.context = context;
}
#Override
public SectionViewHolder onCreateSectionViewHolder(ViewGroup sectionViewGroup, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.section_item, sectionViewGroup, false);
return new SectionViewHolder(view);
}
#Override
public ChildViewHolder onCreateChildViewHolder(ViewGroup childViewGroup, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.child, childViewGroup, false);
return new ChildViewHolder(view);
}
#Override
public void onBindSectionViewHolder(SectionViewHolder sectionViewHolder, int sectionPosition, SectionHeader sectionHeader) {
sectionViewHolder.name.setText(sectionHeader.sectionText);
}
#Override
public void onBindChildViewHolder(ChildViewHolder childViewHolder, int sectionPosition, int childPosition, Child child) {
childViewHolder.name.setText(child.getName());
}
}