Removing specific items from ListView on Android - android

I'm working on an application for Android that downloads RSS data from given URLs to a ListView where I can sort items in a number of ways (alphabetically, by date, by title etc.).
I'd like to be able to remove all items that were downloaded from a given URL.
Sorting changes original item positions, so I can't simply remove items by position.
Here I explain in detail how it's working:
In fragment A I have an EditText in which you type URLs.
Once you press ENTER:
the URL is passed on through an interface to fragment B where it uses the URL to download XML data and loads "item"s into it's ListView through an ArrayAdapter of 'item' class type.
fragment A has it's own ListView where it stores accepted URLs along with >delete< buttons next to them.
Each RSS item that's loaded into fragment's B ListView has unique title, link, and description, and publish date.
That means there are no common values in the loaded items.
I do download the "channel" part, which is the parent of all "item"s which were downloaded from the given URL, but I am not using it in the fragment's B adapter.
Here is the code for the POJO:
#Root(name = "rss", strict = false)
public class RSS {
#Element
private Channel channel;
#Attribute
private String version;
public Channel getChannel() {
return channel;
}
}
#Root(name = "channel", strict = false)
public class Channel {
#ElementList(name = "item", inline = true)
List<Article> articleList;
#Element
private String title;
#Element
private String link;
#Element
private String description;
// setters and getters
}
#Root(name = "item", strict = false)
public class Article {
#Element
private String title;
#Element
private String description;
#Element
private String link;
#Element(required = false)
private String author;
#Element(required = false)
private String pubDate;
// setters and getters
}
This is the URL class used for storing URLs in fragment A's ListView adapter.
public class URL {
private String url;
URL(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public void setUrl(String title) {
this.url = title;
}
}
Now, fragment B downloads the RSS object (called 'articles' here) and this is how I load the items to the other ListView adapter:
previewList = articles.getChannel().articleList;
adapter.addTop(previewList);
adapter.notifyDataSetChanged();
And finally, the Fragment B's ListView adapter:
public class ArticlePreviewAdapter extends ArrayAdapter<Article> {
List<Article> articlePreviewItems;
public ArticlePreviewAdapter(Activity activity, List<Article> articlePreviewItems) {
super(activity, R.layout.item_article_preview, articlePreviewItems);
this.articlePreviewItems = articlePreviewItems;
}
private static class ViewHolder {
TextView articlePreviewTitle;
TextView articlePreviewLink;
TextView articlePreviewDescription;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
Article articlePreviewItem = getItem(position);
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.item_article_preview, parent, false);
viewHolder.articlePreviewTitle = (TextView) convertView.findViewById(R.id.articleTitle);
viewHolder.articlePreviewLink = (TextView) convertView.findViewById(R.id.articleLink);
viewHolder.articlePreviewDescription = (TextView) convertView.findViewById(R.id.articleDescription);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.articlePreviewTitle.setText(articlePreviewItem.getTitle());
viewHolder.articlePreviewLink.setText(articlePreviewItem.getLink());
viewHolder.articlePreviewDescription.setText(articlePreviewItem.getDescription());
return convertView;
}
public void removeChannelItems(what do) {
what do?
}
public void addTop(List<Article> articles) {
articlePreviewItems.addAll(0, articles);
}
}
Thanks for help in advance.

If your Article object (or RSS, or Channel) can hold references to many URLs, you can implement the association like below (Article taken as example)
public class URL {
private String url;
private ArrayList<String> articleTitles;
public class URL(){
this.articleTitles = new ArrayList<String>();
}
public void setArticleTitles(String articleTitle){
this.articleTitle.add(articleTitle);
}
public ArrayList<String> getAarticleTitles(){
return this.articleTitles;
}
URL(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public void setUrl(String title) {
this.url = title;
}
}
Then you would have your URL reference your object. When you delete the URL, you remove all Articles from the respective adapter, finally calling notifyDataSetChanged(); on the adapter.

Related

How am I getting correct item position in ViewHolder without using getAdapterPosition in Recyclerview?

At first I assigned clickListener in OnBindViewHolder and it was working just fine but as many here on stackoverflow advised that it is not a good practice and it is better if I handle all my click listener logic in ViewHolder. Now in OBVH I used holder's position to get current item' position but in VH I didn't even use any position or getAdapterPosition and still I am getting correct item position.
ViewHolder:
private class NewsHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView mImageView;
private TextView mTextView;
private Results mResults;
private NewsHolder(View itemView) {
super(itemView);
mImageView = itemView.findViewById(R.id.news_image);
mTextView = itemView.findViewById(R.id.news_headlines);
itemView.setOnClickListener(this);
}
public void bind(Results results){
mResults = results;
mTextView.setText(mResults.getWebTitle());
String picture = mResults.getFields().getThumbnail();
if (!picture.isEmpty()) {
Picasso.get()
.load(picture)
.placeholder(R.drawable.display)
.resize(200, 200)
.into(mImageView);
} else {
Picasso.get()
.load(R.drawable.display)
.resize(200, 200)
.into(mImageView);
}
}
#Override
public void onClick(View view){
String itemWebUrl = mResults.getWebUrl();
Uri currentItemUri = Uri.parse(itemWebUrl);
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, currentItemUri);
view.getContext().startActivity(websiteIntent);
}
}
OnBindViewHolder
#Override
public void onBindViewHolder(#NonNull NewsHolder holder, int position) {
try {
Results results = mResults.get(position);
holder.bind(results);
} catch (Throwable throwable) {
Log.d(TAG, " " + throwable);
}
}
Results model class:
public class Results{
#SerializedName("id")
#Expose
public String id;
#SerializedName("sectionId")
#Expose
private String sectionId;
#SerializedName("webTitle")
#Expose
private String webTitle;
#SerializedName("webUrl")
#Expose
private String webUrl;
#SerializedName("fields")
#Expose
private Fields fields;
public String getId(){
return id;
}
public String getSectionId(){
return sectionId;
}
public String getWebTitle(){
return webTitle;
}
public String getWebUrl(){
return webUrl;
}
public Fields getFields(){
return fields;
}
}
Is it possible to get correct item position without getAdapterPosition()?
You are already getting proper Results object using position, which is used in NewsHolder. So nothing has to be modified.
Results results = mResults.get(position);
holder.bind(results);

Cannot Parse JSON using retrofit and use listView

I am try to parse this Json array from this site NewsApi and display it in a listView.When I run the app nothing is displaying.Here is my code.What am doing wrong?.I have tried to debug the problem to no avail.
public class NewsApiClient {
public static String API_BASE_URL = "https://newsapi.org/v1/";
public static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
public static Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create()
);
public static Retrofit retrofit = builder.client(httpClient.build()).build();
NewsApiClient client = retrofit.create(NewsApiClient.class);
}
calling the endpoint?
public interface NewsApiInterface {
//endpoint
#GET("sources")
Call <SourcesResponse> getTechSources(
#Query("language") String language,
#Query("category") String category)
}
I am only using the name,description and category attributes.
public class Source {
#SerializedName("id")
public String id;
#SerializedName("name")
public String name;
#SerializedName("description")
public String description;
#SerializedName("url")
public String url;
#SerializedName("category")
public String category;
#SerializedName("language")
public String language;
#SerializedName("country")
public String country;
}
public class SourcesAdapter extends BaseAdapter {
Context context;
List<Source> sourceList;
public SourcesAdapter( Context context,List<Source> sourceList){
this.context = context;
this.sourceList = sourceList;
}
#Override
public int getCount() {
return sourceList.size();
}
#Override
public Object getItem(int position) {
return sourceList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = LayoutInflater.from(context);
convertView = layoutInflater.inflate(R.layout.sources_list,null);
Source currentsource = sourceList.get(position);
TextView sourcesName = (TextView) convertView.findViewById(R.id.sources_name);
TextView sourcesDescription = (TextView) convertView.findViewById(R.id.sources_description);
TextView sourcesCategory = (TextView) convertView.findViewById(R.id.sources_category);
sourcesName.setText(currentsource.name);
sourcesDescription.setText(currentsource.description);
sourcesCategory.setText(currentsource.category);
return convertView;
}
}
public class SourcesFragment extends Fragment {
ListView listView;
public SourcesFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
//
//instance of the adapter to this listview
View view = inflater.inflate(R.layout.fragment_sources, container, false);
listView = (ListView)view.findViewById(R.id.list_view_sources) ;
getSources();
return view;
}
public void getSources(){
Retrofit retrofit = NewsApiClient.builder.build();
NewsApiInterface newsApiInterface = retrofit.create(NewsApiInterface.class);
Call<SourcesResponse> sourcesCall = newsApiInterface.getTechSources("en", "technology");
sourcesCall.enqueue(new Callback<SourcesResponse>() {
#Override
public void onResponse(Call<SourcesResponse> call, Response<SourcesResponse> response) {
List<Source> sources = response.body().sources;
SourcesAdapter sourcesAdapter = new SourcesAdapter(getContext(),sources);
listView.setAdapter(sourcesAdapter);
}
#Override
public void onFailure(Call<SourcesResponse> call, Throwable t) {
}
});
}
}
public class SourcesResponse {
#SerializedName("status")
public String status;
#SerializedName("sources")
public List<Source> sources;
}
I have created 3 fragments,the sources fragment is one of them.On the sources fragment i only want to display sources with technology.
Thank you in advance!
In your retrofit's onResponse callback method, you are accessing sources list like response.body().sources. Instead of this in the SourcesResponse , add a getter and setter for sources like this
public List<SourcesResponse> getSources() {
return sources;
}
public void setSources(List<SourcesResponse> sources) {
this.sources = sources;
}
Now go to your onResponse callback of retrofit and change
response.body().sources
to
response.body().getSources()

What's wrong with this method of populating RecyclerView with database table?

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.

How to convert downloaded images to int image IDs (for use with an ArrayAdapter)?

I have images available via a movie database, that provides the path to the image online (which I have stored in a Movie object). I want to download these images and display them with a custom ArrayAdapter, but I don't know how to download images to be referenced by an image ID. Here is the code;
Class that represents a movie, containing a title, posterpath for downloading via the internet, the integer for the posterImg downloaded locally (unsure how to find this):
public class Movie {
protected String id;
protected String posterPath;
protected String title;
//TODO pass in image reference
protected int posterImg;
private final String LOG_TAG = Movie.class.getSimpleName();
public Movie(String id, String posterPath, String title){
this.posterPath = posterPath;
this.id = id;
this.title = title;
}
public String getId() {
return id;
}
public String getPosterPath() {
return posterPath;
}
public String getTitle() {
return title;
}
public int getPoster() { return posterImg; }
}
Custom ArrayAdapter with only a few methods:
public class MovieAdapter extends ArrayAdapter<Movie> {
private static final String LOG_TAG = MovieAdapter.class.getSimpleName();
public MovieAdapter(Activity context, List<Movie> movies){
super(context, 0, movies);
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
Movie movie = getItem(position);
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item_movie, parent, false);
}
ImageView iconView = (ImageView) convertView.findViewById(R.id.list_item_icon);
iconView.setImageResource(movie.getPoster());
TextView titleView = (TextView) convertView.findViewById(R.id.list_item_title);
titleView.setText(movie.getTitle());
return convertView;
}
}
How should I import these images and display them, using this method or another? I've looked around at things like the LazyList library, but I couldn't figure out how to import it into my project. I'd also like to know how to do this using this method.
Thanks.
You can use Glide to handle downloading, caching and displaying image all with just one line of code. You just donot need to handle ids.
ImageView targetImageView = (ImageView) findViewById(R.id.imageView);
String internetUrl = "http://i.imgur.com/DvpvklR.png";
Glide
.with(context)
.load(internetUrl)
.into(targetImageView);
Read more about it here
https://futurestud.io/blog/glide-getting-started

Android - sql query for group_by items

I'm creating android app where user enters some Receipts and Logs. Relation is: Receipt has_many Logs.
Here are my receipt and log model.
#Table(name = "Logs")
public class Logs extends Model {
#Column(name="PlateNumber")
String plate_number;
#Column(name="SortID")
String sort_id;
#Column(name="Grade")
String grade;
#Column(name = "Diametar")
double diameter;
#Column(name="Length")
double length;
#Column(name="CreatedAt")
Date createdAt;
#Column(name="Receipt")
Receipt receipt;
#Column(name = "Price")
Price price;
}
#Table(name = "Receipt")
public class Receipt extends Model {
#Column(name="Place")
String place;
#Column(name="ShippingNumber")
String shippingNumber;
#Column(name="Warehouse")
String warehouse;
#Column(name="Carrier")
String carrier;
#Column(name="LicencePlate")
String licencePlate;
#Column(name = "Driver")
String driver;
#Column(name = "Customer")
String customer;
#Column(name= "DestWarehouse")
String destWarehouse;
#Column(name = "Employee")
String employee;
#Column(name = "PriceType")
String priceType;
#Column(name = "PriceCorrection")
Double priceCorrection;
#Column(name = "PriceCorrection2")
Double priceCorrection2;
#Column(name = "Supplier")
String supplier;
#Column(name = "CreatedAt")
Date createdAt;
}
Here is my activity where I'm trying to achieve this but unsuccessfully.
public class LogsRecapitulation extends AppCompatActivity {
private ListView mainListView;
private BaseAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_listview);
mainListView = (ListView) findViewById(R.id.ListViewItem);
//recieve RecepitID in displayLogs.activity
final long forwardedId = (long) getIntent().getExtras().get(String.valueOf("recepitID"));
List<Logs> logsList = new Select().from(Logs.class).where("Receipt = " + forwardedId).groupBy("SortID").execute(); // I grouped Logs by sortId in this line.
listAdapter = new RecapitulationArrayAdapter(logsList);
mainListView.setAdapter(listAdapter);
}
private class RecapitulationArrayAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<Logs> logsList;
public RecapitulationArrayAdapter(List<Logs> logsList) {
inflater = LayoutInflater.from(LogsRecapitulation.this);
this.logsList = logsList;
}
#Override
public int getCount() {
return logsList.size();
}
#Override
public Object getItem(int position) {
return logsList.get(position);
}
#Override
public long getItemId(int position) {
return logsList.get(position).getId();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.logs_recapitulation, parent, false);
}
Logs log = logsList.get(position);
((TextView) convertView.findViewById(R.id.rec_log_sort)).setText(log.sort_id);
((TextView) convertView.findViewById(R.id.rec_log_class)).setText(log.grade);
((TextView) convertView.findViewById(R.id.rec_log_count)).setText(String.valueOf(logsList.size()));
((TextView) convertView.findViewById(R.id.rec_logs_mass)).setText(String.format("%.2f m3", log.getM3()));
if (log.receipt.priceType.equals("Na panju")) {
((TextView) convertView.findViewById(R.id.rec_log_price_default)).setText(String.valueOf(log.price.stumpPrice_kn));
} else {
((TextView) convertView.findViewById(R.id.rec_log_price_default)).setText(String.valueOf(log.price.roadPrice_kn));
}
if (log.receipt.priceType.equals("Na panju")) {
((TextView) convertView.findViewById(R.id.rec_calculated_price)).setText(String.format("%.2f KN", log.price.stumpPrice_kn * log.getM3()));
} else {
((TextView) convertView.findViewById(R.id.rec_calculated_price)).setText(String.format("%.2f KN", log.price.roadPrice_kn * log.getM3()));
}
return convertView;
}
}
}
So, what I've done so far is that I display logs by sortID in this BaseAdapter, but in each sort it show's me only last added log and it's data.
Now I need to display data like it is on image below - part where is REKAPITULAT. There I need group by sortID and then in each sort I need group them by grade.
For example if I have like on image sort 1 with 4 logs, 2 of each grade (A,B) then it need's to display it like on image.
I'm stuck here and I would be very grateful if someone knows how to do it and is willing to help.
Question: How to create sql query or fix this my code to display data like it is on image above?
Here's an example query with GROUP BY and JOIN using ActiveAndroid:
new Select("SUM(Logs.Price)")
.from(Logs.class)
.join(Receipt.class)
.on("Receipt.Id = Logs.Receipt")
.groupBy("Logs.sortID")
.execute();
From this query, you'll get a Log instance, so you'll need to map the SUM(Logs.Price) field to a valid Log field, such as Price. For example:
new Select("SUM(Logs.Price) AS Logs.Price")
The query should be adapted for your use case.

Categories

Resources