I followed a tutorial on how to parse XML(RSS) files on an Android app. Below is my code that is working, but is taking quite long. Am I doing something incorrect or in a way that would slow it down? It takes 7-10 seconds to load 30-35 articles... seemingly way too long, and certainly too long for a user to wait every time they switch sections (which pulls in another feed)
I do have things in place to only pull new data every hour...etc ... but the first time still takes 10 seconds usually, which - by then, the user thinks their app has crashed...etc.
I am doing this in an AsyncTask
I'm not downloading the images at this point
This IS the slow part of the code (not internet, or displaying... etc)
TLDR:
This code is too slow - why?:
package com.mysite.utilities;
import java.util.ArrayList;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
public class RSSHandler extends DefaultHandler
{
// creats a news item class to hold data within loop
public class NewsItem
{
public String title;
public String subhead;
public String link;
public String byline;
public String description;
public String storytext;
public String cmsstoryid;
public String pubDate;
public String keyword;
public String category;
public String subcategory;
public String slotreference;
public String lastModTime;
public ArrayList<PhotoItem> photos = new ArrayList<PhotoItem>();
#Override
public String toString()
{
return title;
}
}
//creates a photo item class to hold image data within loop
public class PhotoItem
{
public String photoid; //tribs id
public String caption;
public String height;
public String width;
public String photo; //full path to photo
public String thumbnail; //full path to thumbnail
public int articleid;
public String photolastmodified;
#Override
public String toString()
{
return photo;
}
}
//creates instances of classes to be used in loop
private StringBuffer buf; //to hold element characters with loop
private ArrayList<NewsItem> feedItems;
private NewsItem item;
private PhotoItem photoitem;
//initialize variables of whether or not it's within an item (or photo item)
private boolean inItem = false;
private boolean inPhotoItem = false;
//
public ArrayList<NewsItem> getParsedItems() {
return feedItems;
}
//Called at the head of each new element
#Override
public void startElement(String uri, String name, String qName, Attributes atts)
{
//creates an array of news items
if ("channel".equals(name))
{
feedItems = new ArrayList<NewsItem>();
}
//creates a news item and toggles "in news item" to on
else if ("item".equals(name))
{
item = new NewsItem();
inItem = true;
}
//creates a photo item and toggles "in photo item" to on
else if ("image".equals(name))
{
photoitem = new PhotoItem();
inPhotoItem = true;
}
//starts a new string buffer if matches an elemnt we want from news item
else if (
("title".equals(name) ||
"subhead".equals(name) ||
"link".equals(name) ||
"byline".equals(name) ||
"description".equals(name) ||
"storytext".equals(name) ||
"keyword".equals(name) ||
"cmsstoryid".equals(name) ||
"pubDate".equals(name) ||
"category".equals(name) ||
"subcategory".equals(name) ||
"lastModTime".equals(name) ||
"slotreference".equals(name)
)
&& inItem)
{
buf = new StringBuffer();
}
//starts an a new string buffer if it matches an element we want from image item
else if(
("caption".equals(name) ||
"photoid".equals(name) ||
"height".equals(name) ||
"width".equals(name) ||
"photo".equals(name) ||
"photolastmodified".equals(name) ||
"thumbnail".equals(name)
)
&& inPhotoItem)
{
buf = new StringBuffer();
}
}
//Called at the tail of each element end
#Override
public void endElement(String uri, String name, String qName)
{
if ("item".equals(name))
{
feedItems.add(item);
inItem = false;
}
else if ("image".equals(name))
{
try {
item.photos.add(photoitem);
} catch (Exception e) {
System.out.println(e);
}
inPhotoItem = false;
}
else if (inItem)
{
if (inPhotoItem)
{
if ("caption".equals(name)) { photoitem.caption = buf.toString(); }
else if ("photoid".equals(name)) { photoitem.photoid = buf.toString(); }
else if ("height".equals(name)) { photoitem.height = buf.toString(); }
else if ("width".equals(name)) { photoitem.width = buf.toString(); }
else if ("photo".equals(name)) { photoitem.photo = buf.toString(); }
else if ("photolastmodified".equals(name)) { photoitem.photolastmodified = buf.toString(); }
else if ("thumbnail".equals(name)) { photoitem.thumbnail = buf.toString(); }
}
else if ("title".equals(name)) { item.title = buf.toString(); }
else if ("subhead".equals(name)) { item.subhead = buf.toString(); }
else if ("link".equals(name)) { item.link = buf.toString(); }
else if ("byline".equals(name)) { item.byline = buf.toString(); }
else if ("description".equals(name)) { item.description = buf.toString(); }
else if ("storytext".equals(name)) { item.storytext = buf.toString(); }
else if ("keyword".equals(name)) { item.keyword = buf.toString(); }
else if ("cmsstoryid".equals(name)) { item.cmsstoryid = buf.toString(); }
else if ("pubDate".equals(name)) { item.pubDate = buf.toString(); }
else if ("category".equals(name)) { item.category = buf.toString(); }
else if ("subcategory".equals(name)) { item.subcategory = buf.toString(); }
else if ("lastModTime".equals(name)) { item.lastModTime = buf.toString(); }
else if ("slotreference".equals(name)) { item.slotreference = buf.toString(); }
}
else
{
buf = null;
}
}
//Called with character data inside elements
#Override
public void characters(char ch[], int start, int length)
{
//Don't bother if buffer isn't initialized
if(buf != null)
{
for (int i=start; i<start+length; i++)
{
buf.append(ch[i]);
}
}
}
}
My updated code thus far (still no speed increase):
package com.sltrib.utilities;
import java.util.ArrayList;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
public class RSSHandler extends DefaultHandler
{
// creats a news item class to hold data within loop
public class NewsItem
{
public String title;
public String subhead;
public String link;
public String byline;
public String description;
public String storytext;
public String cmsstoryid;
public String pubDate;
public String keyword;
public String category;
public String subcategory;
public String slotreference;
public String lastModTime;
public ArrayList<PhotoItem> photos = new ArrayList<PhotoItem>();
#Override
public String toString()
{
return title;
}
}
//creates a photo item class to hold image data within loop
public class PhotoItem
{
public String photoid; //tribs id
public String caption;
public String height;
public String width;
public String photo; //full path to photo
public String thumbnail; //full path to thumbnail
public int articleid;
public String photolastmodified;
#Override
public String toString()
{
return photo;
}
}
//creates instances of classes to be used in loop
private StringBuilder buf; //to hold element characters with loop
private ArrayList<NewsItem> feedItems;
private NewsItem item;
private PhotoItem photoitem;
//initialize variables of whether or not it's within an item (or photo item)
private boolean inItem = false;
private int currentTagId = 0;
//
public ArrayList<NewsItem> getParsedItems() {
return feedItems;
}
#Override
public void startDocument()
{
feedItems = new ArrayList<NewsItem>(); //creates an array to hold feed items
}
//Called at the head of each new element
#Override
public void startElement(String uri, String name, String qName, Attributes atts)
{
//gets the id of the current tag
currentTagId = 0;
String tmpId = atts.getValue("id");
if(tmpId != null)
{
currentTagId = Integer.parseInt(tmpId);
}
//creates a news item
if (currentTagId == 99) // <item>
{
item = new NewsItem();
inItem = true;
}
//creates a photo item
else if (currentTagId == 21) { // <image>
photoitem = new PhotoItem();
}
//creates a string builder if it's a tag within an article or a photo
else if (inItem && currentTagId > 0 && currentTagId < 29)
{
buf = new StringBuilder();
}
}
//Called at the tail of each element end
#Override
public void endElement(String uri, String name, String qName)
{
// it's not getting the tag id - how?
Log.d("XML", "endElement:" + Integer.toString(currentTagId));
if (currentTagId == 99) // </item>
{
Log.d("XML", "Should be adding item: " + item.title);
feedItems.add(item);
inItem=false;
}
else if (currentTagId == 21) // </image>
{
try {
item.photos.add(photoitem);
} catch (Exception e) {
System.out.println(e);
}
}
else
{
switch(currentTagId)
{
case 1: item.title = buf.toString(); break;
case 2: item.subhead = buf.toString(); break;
case 3: item.link = buf.toString(); break;
case 4: item.byline = buf.toString(); break;
case 5: item.description = buf.toString(); break;
case 6: item.storytext = buf.toString(); break;
case 11: item.cmsstoryid = buf.toString(); break;
case 14: item.pubDate = buf.toString(); break;
case 15: item.lastModTime = buf.toString(); break;
case 16: item.keyword = buf.toString(); break;
case 17: item.category = buf.toString(); break;
case 18: item.subcategory = buf.toString(); break;
case 19: item.slotreference = buf.toString(); break;
case 22: photoitem.caption = buf.toString(); break;
case 23: photoitem.photoid = buf.toString(); break;
case 24: photoitem.photolastmodified = buf.toString(); break;
case 25: photoitem.height = buf.toString(); break;
case 26: photoitem.width = buf.toString(); break;
case 27: photoitem.photo = buf.toString(); break;
case 28: photoitem.thumbnail = buf.toString(); break;
default: if(!inItem) buf = null;
}
}
}
//Called with character data inside elements
#Override
public void characters(char ch[], int start, int length)
{
if(buf != null)
{
String chars = new String(ch, start, length); // get all text value inside the element tag
chars = chars.trim(); // remove all white-space characters
buf.append(chars);
}
}
}
Perhaps you are already doing this, but I just wanted to reiterate that this should be done on another thread. The user wont think the app has crashed if you display a progress indicator (spinner) and keep the GUI responsive.
If you are not doing this on a background thread, it could be making this artificially slow because the system is fighting to keep the user interface going.
As to why this is slow, there is a TON of branching. The more if statements you have, and the more elses (etc) you have, the more difficult it is for the branch predictor do do its job.
Also, I can't tell if you are downloading the images here, but if you are, that should be done asynchronously after the text data is retrieved and displayed to the user (to make the user interface more responsive).
Related
Now in detail. I am writing an App for a dictionary. For that I have created a custom Object called Word. This Word contains the term and its' corresponding translation:
public class Word implements Serializable {
private int _id;
private int wordType;
private String engWordSi;
private String okyWordPl;
private String engWordPl;
private String okyWordSi;
private String engEx;
private String okyEx;
/** Creates a custom class, which allows save a term, it's translation, as well as the plural and example in both
* languages
* #param _id saves the Id of the Word in the Database
* #param engWordSi saves the English translation of the word
* #param okyWordSi saves the Oshikwanyama translation of the word
* #param wordType saves the type of word in form of a number:
* 0 = phrase; 1 = noun; 2 = verb; 3 = adjective; 4 = pronoun; 5 = other
*/
public Word(int _id, int wordType, String engWordSi, String okyWordSi){
this._id = _id;
this.wordType = wordType;
this.engWordSi = engWordSi;
this.okyWordSi = okyWordSi;
}public int get_id(){
return _id;
}
public void set_id(int i){
_id = i;
}
public int getWordType(){
return wordType;
}
public void setWordType(int i){
if(i < 0 || i > 6){
return;
}
wordType = i;
}
public String getEngWordSi(){
return engWordSi;
}
public void setEngWordSi(String word){
engWordSi = word;
}
public String getOkyWordSi(){
return okyWordSi;
}
public void setOkyWordSi(String word){
okyWordSi = word;
}
public String getEngWordPl(){
return engWordPl;
}
public void setEngWordPl(String word){
engWordPl = word;
}
public String getOkyWordPl(){
return okyWordPl;
}
public void setOkyWordPl(String word){
okyWordPl = word;
}
public String getEngEx(){
return engEx;
}
public void setEngEx(String word){
engEx = word;
}
public String getOkyEx(){
return okyEx;
}
public void setOkyEx(String word){
okyEx = word;
}
The data is loaded via a php file connected to the database. The ORDER BY statement only allows me to choose one language to order the results by. But what I need is, that the search term, which might be found in words of both languages, defines if the English word or the Oshikwanyama word is the word which is being arranged in the order. I have check which translation to use in each case. My guess would be to do it in onPostExecute(). The data is returned by the PHP file in the JSON format. From there I put them into an ArrayList.
private class Query extends AsyncTask<Void, Void, Void>{
#Override
protected void onPostExecute(Void aVoid) {
try{
JSONArray jsonArray = new JSONArray(result);
for (int i = 0; i < jsonArray.length(); i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
Word word = new Word(jsonObject.getInt("_id"), jsonObject.getInt("type"), jsonObject.getString("eng"), jsonObject.getString("oky") );
arrayList.add(word);
Log.d("JSON", word.toString());
}
}catch (JSONException e){
e.printStackTrace();
}
ProgressBar bar = (ProgressBar) findViewById(R.id.bar);
bar.setVisibility(View.GONE);
Button button = (Button) findViewById(R.id.SearchButton);
button.setVisibility(View.VISIBLE);
super.onPostExecute(aVoid);
if (arrayList.isEmpty()){
Toast.makeText(SearchActivity.this, "Unfortunately there were no results, the missing word was sent to our developers.", Toast.LENGTH_LONG).show();
return;
}
Intent intent = new Intent(SearchActivity.this, ResultActivity.class);
ItemDetailsWrapper wrapper = new ItemDetailsWrapper(arrayList);
intent.putExtra("results", wrapper);
intent.putExtra("term", searchTerm);
startActivity(intent);
}
InputStream inputStream = null;
String result = "";
#Override
protected void onPreExecute() {
if (arrayList != null){
arrayList.clear();}
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
try{
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(uri.toString());
//httpPost.setEntity(new UrlEncodedFormEntity(null));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
inputStream = httpEntity.getContent();
}
catch (UnsupportedEncodingException e1){
e1.printStackTrace();
} catch (ClientProtocolException e2){
e2.printStackTrace();
}catch (IllegalStateException e3){
e3.printStackTrace();
}catch (IOException e4){
e4.printStackTrace();
}try{
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String line = null;
while ((line = bufferedReader.readLine()) != null){
stringBuilder.append(line + "\n");
}
inputStream.close();
result = stringBuilder.toString();
}catch (IOException e){
e.printStackTrace();
}
return null;
}
}}
I already have a place to check whether the term searched for is contained in the English or Oshikwanyama word. There I use it to define the flag used in the layout.
public class WordAdapter extends ArrayAdapter<Word> {
private Context mContext;
private List<Word> wordList = new ArrayList<>();
private int srcCode;
private String term;
/** uses words and puts them into a list
*
* #param context stores the context of the calling activity
* #param list stores the ArrayList that was passed into the constructor, and which contains the
* content
* #param searchTerm stores the term that was searched for in SearchActivity to later compare it
* to the contents of the Word and to arrange the correct flag for the source
* language
*/
public WordAdapter(#NonNull Context context, ArrayList<Word> list, String searchTerm) {
super(context, 0 , list);
mContext = context;
wordList = list;
term = searchTerm;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View listItem = convertView;
if(listItem == null){
listItem = LayoutInflater.from(mContext).inflate(R.layout.list_item,parent,false);}
final Word currentWord = wordList.get(position);
//Add Images for the flags of the countries and the flag
final TextView sourceTerm = (TextView) listItem.findViewById(R.id.searchTerm);
final TextView translationTerm = (TextView) listItem.findViewById(R.id.translationTerm);
ImageView flag = (ImageView) listItem.findViewById(R.id.src_flag);
ImageButton button = (ImageButton) listItem.findViewById(R.id.flag);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), EditorActivity.class);
intent.putExtra("mode", "report");
intent.putExtra("id", currentWord.get_id());
intent.putExtra("source", sourceTerm.getText());
intent.putExtra("trans", translationTerm.getText());
getContext().startActivity(intent);
}
});
if (currentWord.getOkyWordPl() == null){
currentWord.setOkyWordPl("");
}
if (currentWord.getEngWordPl() == null){
currentWord.setEngWordPl("");
}
if (currentWord.getEngWordSi().contains(term) || currentWord.getEngWordPl().contains(term)){
srcCode = 0;
}
if (currentWord.getOkyWordSi().contains(term) || currentWord.getOkyWordPl().contains(term)){
srcCode = 1;
}
if (srcCode == 0){
sourceTerm.setText(currentWord.getEngWordSi());
translationTerm.setText(currentWord.getOkyWordSi());
flag.setImageResource(R.drawable.britain);
}
if (srcCode == 1){
sourceTerm.setText(currentWord.getOkyWordSi());
translationTerm.setText(currentWord.getEngWordSi());
flag.setImageResource(R.drawable.namibia);
}
return listItem;
}}
Thank you in advance. :)
EDIT:
Adding example Words:
1.
EngWordSi: good
OkyWordSi: nawa
wordType: 2
2.
EngWordSi: good morning
OkyWordSi: wa lele po?
wordType: 0
3.
EngWordSi: morning
OkyWordSi: ongula
WordType: 1
what you can do is, sort your object property like below in your adapter,
public WordAdapter(#NonNull Context context, ArrayList<Word> list, String searchTerm) {
super(context, 0 , list);
mContext = context;
wordList = list;
term = searchTerm;
Collections.sort(list, new Comparator<Word>() {
#Override
public int compare(Word word1, word2) {
return word1.getEngWordSi().compareToIgnoreCase(word2.getEngWordSi());
}
});
}
this is helpful to you.
Here is my demo code
public class Main {
static class Word{
String prority1;
#Override
public String toString() {
return "Word{" +
"prority1='" + prority1 + '\'' +
'}';
}
}
public static void main(String[] args) {
ArrayList<Word> words = new ArrayList<>();
Word word;
for (int i = 0; i < 5; i++) {
word =new Word();
word.prority1 = "aaaa"+(5-i);
words.add(word);
System.out.println("wrod "+i+" is "+word);
}
Collections.sort(words, new Comparator<Word>() {
#Override
public int compare(Word o1, Word o2) {
int ret = o1.prority1.compareTo(o2.prority1);
if (ret > 0) {
return 1;
} else if (ret < 0) {
return -1;
}
return 0;
}
});
System.out.println("after sort check words");
for (Word w : words) {
System.out.println("check word:"+w);
}
}
}
and following is console's output:
wrod 0 is Word{prority1='aaaa5'}
wrod 1 is Word{prority1='aaaa4'}
wrod 2 is Word{prority1='aaaa3'}
wrod 3 is Word{prority1='aaaa2'}
wrod 4 is Word{prority1='aaaa1'}
after sort check words
check word:Word{prority1='aaaa1'}
check word:Word{prority1='aaaa2'}
check word:Word{prority1='aaaa3'}
check word:Word{prority1='aaaa4'}
check word:Word{prority1='aaaa5'}
Hope that can help you.
I would like to parse only one specific River data.
From this website: http://www.arso.gov.si/xml/vode/hidro_podatki_zadnji.xml
My code below extract all Rivers:
ParseApplication.java;
public class ParseApplication {
private String xmlData; // XML file we are processing
private ArrayList<Application> applications;
public ParseApplication(String xmlData) {
this.xmlData = xmlData;
this.applications = new ArrayList<Application>();
}
public ArrayList<Application> getApplications() {
return applications;
}
public boolean process()
{
boolean status = true;
Application currentRecord = null;
boolean inEntry = false;
String textValue = "";
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = factory.newPullParser();
parser.setInput(new StringReader(this.xmlData));
int eventType = parser.getEventType();
while(eventType != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
switch (eventType) {
case XmlPullParser.START_TAG:
Log.d("ParseApplications", "Starting tag for " + tagName);
if (tagName.equalsIgnoreCase("postaja")) {
Log.d("ParseApplication", "Create application item");
inEntry = true;
currentRecord = new Application();
}
break;
case XmlPullParser.TEXT:
textValue = parser.getText(); // data itself, used in End_Tag
break;
case XmlPullParser.END_TAG:
Log.d("ParseApplication", "Ending tag for " + tagName);
if (inEntry) {
if (tagName.equalsIgnoreCase("postaja")) {
// Save the record and set entry to false
Log.d("ParseApplication", "Store Application With Name " + currentRecord.getReka());
this.applications.add(currentRecord);
inEntry = false;
} else if (tagName.equalsIgnoreCase("reka")) {
currentRecord.setReka(textValue); // TextValue already updated
} else if (tagName.equalsIgnoreCase("pretok")) {
currentRecord.setPretok(textValue);
} else if (tagName.equalsIgnoreCase("datum")) {
currentRecord.setDatum(textValue);
}
}
break;
default:
break;
}
eventType = parser.next();
}
} catch(Exception e) {
status = false;
e.printStackTrace();
}
return status;
}
}
and
application.java
public class Application {
private String reka;
private String pretok;
private String datum;
public String getReka() {
return reka;
}
public void setReka(String reka) {
this.reka = reka;
}
public String getPretok() {
return pretok;
}
public void setPretok(String pretok) {
this.pretok = pretok;
}
public String getDatum() {
return datum;
}
public void setDatum(String datum) {
this.datum = datum;
}
#Override
public String toString() {
return "Reka: " + this.getReka() + "\n" +
"Pretok: " + this.getPretok() + " m3\n" +
"Datum: " + this.getDatum();
}
}
How do I parse one specific XML River data only?
This question already has an answer here:
java.lang.StackOverflowError trying to parse in a AsyncTask
(1 answer)
Closed 8 years ago.
I have been reading this tutorial and came across the problem that it uses a very low api. I got the NetworkOnMainThreadException. I found this answer on stackoverflow which says I have to use AsyncTask.
I have tried using AsyncTask on an empty project working with another tutorial which worked fine.
My problem is that I need to change this project so that I can use it on higher apis. So the thing is AndroidSaxFeedParser is a subclass and AsyncTask is a super class and the error line is on AndroidSaxFeedParser which extends BaseFeedParser and BaseFeedParser extends FeedParser which is an interface(btw I always thought interfaces had to be implemented instead of extended?).
To be more precise the errors are on these line(indicated with --->) :
AndroidSaxFeedParser.java :
try
{
---> Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
}
catch (Exception e)
{
---> throw new RuntimeException(e);
}
MessageList.java :
private void loadFeed(ParserType type)
{
try
{
Log.i("AndroidNews", "ParserType=" + type.name());
FeedParser parser = FeedParserFactory.getParser(type);
long start = System.currentTimeMillis();
---> messages = parser.parse();
long duration = System.currentTimeMillis() - start;
Log.i("AndroidNews", "Parser duration=" + duration);
String xml = writeXml();
Log.i("AndroidNews", xml);
List<String> titles = new ArrayList<String>(messages.size());
for (Message msg : messages)
{
titles.add(msg.getTitle());
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.row, titles);
this.setListAdapter(adapter);
}
catch (Throwable t)
{
Log.e("AndroidNews", t.getMessage(), t);
}
}
BaseFeedParser.java :
protected InputStream getInputStream()
{
try
{
---> return feedUrl.openConnection().getInputStream();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
So where and how should I use the AsyncTask. (I'm only using the AndroidSaxParser so the other parsers in the tutorial can be ignored).
AndroidSaxFeedParser.java
public class AndroidSaxFeedParser extends BaseFeedParser
{
static final String RSS = "rss";
public AndroidSaxFeedParser(String feedUrl)
{
super(feedUrl);
}
public List<Message> parse()
{
final Message currentMessage = new Message();
RootElement root = new RootElement(RSS);
final List<Message> messages = new ArrayList<Message>();
Element channel = root.getChild(CHANNEL);
Element item = channel.getChild(ITEM);
item.setEndElementListener(new EndElementListener()
{
public void end()
{
messages.add(currentMessage.copy());
}
});
item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener()
{
public void end(String body)
{
currentMessage.setTitle(body);
}
});
item.getChild(LINK).setEndTextElementListener(new EndTextElementListener()
{
public void end(String body)
{
currentMessage.setLink(body);
}
});
item.getChild(DESCRIPTION).setEndTextElementListener(new EndTextElementListener()
{
public void end(String body)
{
currentMessage.setDescription(body);
}
});
item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener()
{
public void end(String body)
{
currentMessage.setDate(body);
}
});
try
{
Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return messages;
}
}
BaseFeedParser.java
public abstract class BaseFeedParser implements FeedParser
{
// names of the XML tags
static final String CHANNEL = "channel";
static final String PUB_DATE = "pubDate";
static final String DESCRIPTION = "description";
static final String LINK = "link";
static final String TITLE = "title";
static final String ITEM = "item";
private final URL feedUrl;
protected BaseFeedParser(String feedUrl)
{
try
{
this.feedUrl = new URL(feedUrl);
}
catch (MalformedURLException e)
{
throw new RuntimeException(e);
}
}
protected InputStream getInputStream()
{
try
{
return feedUrl.openConnection().getInputStream();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
FeedParser.java
public interface FeedParser
{
List<Message> parse();
}
FeedParserFactory.java
public abstract class FeedParserFactory
{
static String feedUrl = "http://example.com/feed/";
public static FeedParser getParser()
{
return getParser(ParserType.ANDROID_SAX);
}
public static FeedParser getParser(ParserType type)
{
switch (type)
{
case SAX:
return new SaxFeedParser(feedUrl);
case DOM:
return new DomFeedParser(feedUrl);
case ANDROID_SAX:
return new AndroidSaxFeedParser(feedUrl);
case XML_PULL:
return new XmlPullFeedParser(feedUrl);
default:
return null;
}
}
}
Message.java
public class Message implements Comparable<Message>
{
static SimpleDateFormat FORMATTER = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss Z", Locale.ENGLISH);
private String title;
private URL link;
private String description;
private Date date;
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title.trim();
}
// getters and setters omitted for brevity
public URL getLink()
{
return link;
}
public void setLink(String link)
{
try
{
this.link = new URL(link);
}
catch (MalformedURLException e)
{
throw new RuntimeException(e);
}
}
public String getDescription()
{
return description;
}
public void setDescription(String description)
{
this.description = description.trim();
}
public String getDate()
{
return FORMATTER.format(this.date);
}
public void setDate(String date)
{
// pad the date if necessary
while (!date.endsWith("00"))
{
date += "0";
}
try
{
this.date = FORMATTER.parse(date.trim());
}
catch (ParseException e)
{
throw new RuntimeException(e);
}
}
public Message copy()
{
Message copy = new Message();
copy.title = title;
copy.link = link;
copy.description = description;
copy.date = date;
return copy;
}
#Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("Title: ");
sb.append(title);
sb.append('\n');
sb.append("Date: ");
sb.append(this.getDate());
sb.append('\n');
sb.append("Link: ");
sb.append(link);
sb.append('\n');
sb.append("Description: ");
sb.append(description);
return sb.toString();
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((date == null) ? 0 : date.hashCode());
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((link == null) ? 0 : link.hashCode());
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Message other = (Message) obj;
if (date == null)
{
if (other.date != null)
return false;
}
else if (!date.equals(other.date))
return false;
if (description == null)
{
if (other.description != null)
return false;
}
else if (!description.equals(other.description))
return false;
if (link == null)
{
if (other.link != null)
return false;
}
else if (!link.equals(other.link))
return false;
if (title == null)
{
if (other.title != null)
return false;
}
else if (!title.equals(other.title))
return false;
return true;
}
public int compareTo(Message another)
{
if (another == null)
return 1;
// sort descending, most recent first
return another.date.compareTo(date);
}
}
MessageList.java
public class MessageList extends ListActivity
{
private List<Message> messages;
#Override
public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
setContentView(R.layout.main);
loadFeed(ParserType.ANDROID_SAX);
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
menu.add(Menu.NONE, ParserType.ANDROID_SAX.ordinal(), ParserType.ANDROID_SAX.ordinal(), R.string.android_sax);
menu.add(Menu.NONE, ParserType.SAX.ordinal(), ParserType.SAX.ordinal(), R.string.sax);
menu.add(Menu.NONE, ParserType.DOM.ordinal(), ParserType.DOM.ordinal(), R.string.dom);
menu.add(Menu.NONE, ParserType.XML_PULL.ordinal(), ParserType.XML_PULL.ordinal(), R.string.pull);
return true;
}
#SuppressWarnings("unchecked")
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item)
{
super.onMenuItemSelected(featureId, item);
ParserType type = ParserType.values()[item.getItemId()];
ArrayAdapter<String> adapter = (ArrayAdapter<String>) this.getListAdapter();
if (adapter.getCount() > 0)
{
adapter.clear();
}
this.loadFeed(type);
return true;
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
Intent viewMessage = new Intent(Intent.ACTION_VIEW, Uri.parse(messages.get(position).getLink().toExternalForm()));
this.startActivity(viewMessage);
}
private void loadFeed(ParserType type)
{
try
{
Log.i("AndroidNews", "ParserType=" + type.name());
FeedParser parser = FeedParserFactory.getParser(type);
long start = System.currentTimeMillis();
messages = parser.parse();
long duration = System.currentTimeMillis() - start;
Log.i("AndroidNews", "Parser duration=" + duration);
String xml = writeXml();
Log.i("AndroidNews", xml);
List<String> titles = new ArrayList<String>(messages.size());
for (Message msg : messages)
{
titles.add(msg.getTitle());
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.row, titles);
this.setListAdapter(adapter);
}
catch (Throwable t)
{
Log.e("AndroidNews", t.getMessage(), t);
}
}
private String writeXml()
{
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try
{
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "messages");
serializer.attribute("", "number", String.valueOf(messages.size()));
for (Message msg : messages)
{
serializer.startTag("", "message");
serializer.attribute("", "date", msg.getDate());
serializer.startTag("", "title");
serializer.text(msg.getTitle());
serializer.endTag("", "title");
serializer.startTag("", "url");
serializer.text(msg.getLink().toExternalForm());
serializer.endTag("", "url");
serializer.startTag("", "body");
serializer.text(msg.getDescription());
serializer.endTag("", "body");
serializer.endTag("", "message");
}
serializer.endTag("", "messages");
serializer.endDocument();
return writer.toString();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
ParserType.java
public enum ParserType
{
SAX, DOM, ANDROID_SAX, XML_PULL;
}
RssHandler.java
public class RssHandler extends DefaultHandler
{
private List<Message> messages;
private Message currentMessage;
private StringBuilder builder;
public List<Message> getMessages()
{
return this.messages;
}
#Override
public void characters(char[] ch, int start, int length) throws SAXException
{
super.characters(ch, start, length);
builder.append(ch, start, length);
}
#Override
public void endElement(String uri, String localName, String name) throws SAXException
{
super.endElement(uri, localName, name);
if (this.currentMessage != null)
{
if (localName.equalsIgnoreCase(TITLE))
{
currentMessage.setTitle(builder.toString());
}
else if (localName.equalsIgnoreCase(LINK))
{
currentMessage.setLink(builder.toString());
}
else if (localName.equalsIgnoreCase(DESCRIPTION))
{
currentMessage.setDescription(builder.toString());
}
else if (localName.equalsIgnoreCase(PUB_DATE))
{
currentMessage.setDate(builder.toString());
}
else if (localName.equalsIgnoreCase(ITEM))
{
messages.add(currentMessage);
}
builder.setLength(0);
}
}
#Override
public void startDocument() throws SAXException
{
super.startDocument();
messages = new ArrayList<Message>();
builder = new StringBuilder();
}
#Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
{
super.startElement(uri, localName, name, attributes);
if (localName.equalsIgnoreCase(ITEM))
{
this.currentMessage = new Message();
}
}
}
public class ParseAsync extends AsyncTask<Url, Void, ArrayList<FeedItem>> {
#Override
protected ArrayList<FeedItem> doInBackground(Url... params) {
//url as parametr, long time operation
return YourParser.parseFeed(params[0])
}
#Override
protected void onPostExecute(ArrayList<FeedItem> result) {
// this we get result of parser in ui thread
}
}
In ui thread
ParseAsync task = new ParseAsync();
task.execute("www.example.ru/feed.rss")
Answer
I fixed my problem if anyone is interested you can read that question.
i think the problem is because of NetworkOnMainThreadException,So what you want to do is that you need to add StrictMode
Where you use the Async task,so just add this linke on preexectue of the async task
int SDK_INT = android.os.Build.VERSION.SDK_INT;
if (SDK_INT>8){
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
Hope this will solve your problem
Hi below is my working code to parse RSS and populate it in ListView, but I want to add CustomAdapter rather than simple Adapter, so that I can add an ImageView in ListView and design my own ListView. Can someone help with code.
SocialFeeds.Java
public class SocialFeeds extends Activity {
// A reference to the local object
private SocialFeeds local;
/**
* This method creates main application view
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set view
setContentView(R.layout.tweet);
// Set reference to this activity
local = this;
GetRSSDataTask task = new GetRSSDataTask();
// Start download RSS task
task.execute("https:url");
// Debug the thread name
Log.d("ITCRssReader", Thread.currentThread().getName());
}
private class GetRSSDataTask extends AsyncTask<String, Void, List<RssItem>> {
private final ProgressDialog dialog = new ProgressDialog(SocialFeeds.this);
#Override
protected List<RssItem> doInBackground(String... urls) {
// Debug the task thread name
Log.d("ITCRssReader", Thread.currentThread().getName());
try {
// Create RSS reader
RssReader rssReader = new RssReader(urls[0]);
// Parse RSS, get items
return rssReader.getItems();
} catch (Exception e) {
Log.e("ITCRssReader", e.getMessage());
}
return null;
}
#Override
protected void onPreExecute() {
dialog.setMessage("Please Wait.");
dialog.setCancelable(true);
dialog.show();
}
#Override
protected void onPostExecute(List<RssItem> result) {
if (dialog.isShowing())
{
dialog.dismiss();
}
// Get a ListView from main view
ListView itcItems = (ListView) findViewById(R.id.list);
// Create a list adapter
ArrayAdapter<RssItem> adapter = new ArrayAdapter<RssItem>(local,android.R.layout.simple_list_item_1, result);
// Set list adapter for the ListView
itcItems.setAdapter(adapter);
// Set list view item click listener
itcItems.setOnItemClickListener(new ListListener(result, local));
}
}
}
ListListener.java
public class ListListener extends Activity implements OnItemClickListener {
// List item's reference
List<RssItem> listItems;
// Calling activity reference
Activity activity;
AlertDialog.Builder alert;
WebView wv;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public ListListener(List<RssItem> aListItems, Activity anActivity) {
listItems = aListItems;
activity = anActivity;
}
/**
* Start a browser with url from the rss item.
*/
public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
/*Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(listItems.get(pos).getLink()));
activity.startActivity(i);*/
}
}
RssParseHandler.java
public class RssParseHandler extends DefaultHandler {
private List<RssItem> rssItems;
// Used to reference item while parsing
private RssItem currentItem;
// Parsing title indicator
private boolean parsingTitle;
// A buffer used to build current title being parsed
private StringBuffer currentTitleSb;
// Parsing pubDate indicator
private boolean parsingpubDate;
// A buffer used to build current pubDate being parsed
private StringBuffer currentpubDateSb;
// Parsing description indicator
private boolean parsingDescription;
// A buffer used to build current description being parsed
private StringBuffer currentDescriptionSb;
// Parsing link indicator
private boolean parsingLink;
public RssParseHandler() {
rssItems = new ArrayList<RssItem>();
}
public List<RssItem> getItems() {
return rssItems;
}
#Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("item".equals(qName)) {
currentItem = new RssItem(0, qName, qName, qName);
} else if ("title".equals(qName)) {
parsingTitle = true;
currentTitleSb = new StringBuffer();
}
else if ("pubDate".equals(qName)) {
parsingpubDate = true;
currentpubDateSb = new StringBuffer();
}
else if ("description".equals(qName)) {
parsingDescription = true;
currentDescriptionSb = new StringBuffer();
}
else if ("link".equals(qName)) {
parsingLink = true;
}
}
#Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("item".equals(qName)) {
rssItems.add(currentItem);
currentItem = null;
}
else if ("title".equals(qName)) {
parsingTitle = false;
// Set item's title when we parse item->title tag not the channel title tag
if (currentItem != null) {
// Set item's title here
currentItem.setTitle(currentTitleSb.toString());
}
}
else if ("pubDate".equals(qName)) {
parsingpubDate = false;
// Set item's pubDate when we parse item->pubDate tag not the channel pubDate tag
if (currentItem != null) {
// Set item's pubDate here
currentItem.setpubDate(currentpubDateSb.toString());
}
}
else if ("description".equals(qName)) {
parsingDescription = false;
// Set item's description when we parse item->description tag not the channel description tag
if (currentItem != null) {
// Set item's description here
currentItem.setDescription(currentDescriptionSb.toString());
}
}
else if ("link".equals(qName)) {
parsingLink = false;
}
}
#Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (parsingTitle) {
if (currentItem != null) {
// Here we append the title to the buffer due to network issues.
// Sometimes this characters method is called multiple times for a tag contents.
currentTitleSb.append(new String(ch, start, length));
}
}
if (parsingpubDate) {
if (currentItem != null) {
// Here we append the pubDate to the buffer due to network issues.
// Sometimes this characters method is called multiple times for a tag contents.
currentpubDateSb.append(new String(ch, start, length));
}
}
if (parsingDescription) {
if (currentItem != null) {
// Here we append the description to the buffer due to network issues.
// Sometimes this characters method is called multiple times for a tag contents.
currentDescriptionSb.append(new String(ch, start, length));
}
}
else if (parsingLink) {
if (currentItem != null) {
currentItem.setLink(new String(ch, start, length));
parsingLink = false;
}
}
}
}
RssReader.java
public class RssParseHandler extends DefaultHandler {
private List<RssItem> rssItems;
// Used to reference item while parsing
private RssItem currentItem;
// Parsing title indicator
private boolean parsingTitle;
// A buffer used to build current title being parsed
private StringBuffer currentTitleSb;
// Parsing pubDate indicator
private boolean parsingpubDate;
// A buffer used to build current pubDate being parsed
private StringBuffer currentpubDateSb;
// Parsing description indicator
private boolean parsingDescription;
// A buffer used to build current description being parsed
private StringBuffer currentDescriptionSb;
// Parsing link indicator
private boolean parsingLink;
public RssParseHandler() {
rssItems = new ArrayList<RssItem>();
}
public List<RssItem> getItems() {
return rssItems;
}
#Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("item".equals(qName)) {
currentItem = new RssItem(0, qName, qName, qName);
} else if ("title".equals(qName)) {
parsingTitle = true;
currentTitleSb = new StringBuffer();
}
else if ("pubDate".equals(qName)) {
parsingpubDate = true;
currentpubDateSb = new StringBuffer();
}
else if ("description".equals(qName)) {
parsingDescription = true;
currentDescriptionSb = new StringBuffer();
}
else if ("link".equals(qName)) {
parsingLink = true;
}
}
#Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("item".equals(qName)) {
rssItems.add(currentItem);
currentItem = null;
}
else if ("title".equals(qName)) {
parsingTitle = false;
// Set item's title when we parse item->title tag not the channel title tag
if (currentItem != null) {
// Set item's title here
currentItem.setTitle(currentTitleSb.toString());
}
}
else if ("pubDate".equals(qName)) {
parsingpubDate = false;
// Set item's pubDate when we parse item->pubDate tag not the channel pubDate tag
if (currentItem != null) {
// Set item's pubDate here
currentItem.setpubDate(currentpubDateSb.toString());
}
}
else if ("description".equals(qName)) {
parsingDescription = false;
// Set item's description when we parse item->description tag not the channel description tag
if (currentItem != null) {
// Set item's description here
currentItem.setDescription(currentDescriptionSb.toString());
}
}
else if ("link".equals(qName)) {
parsingLink = false;
}
}
#Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (parsingTitle) {
if (currentItem != null) {
// Here we append the title to the buffer due to network issues.
// Sometimes this characters method is called multiple times for a tag contents.
currentTitleSb.append(new String(ch, start, length));
}
}
if (parsingpubDate) {
if (currentItem != null) {
// Here we append the pubDate to the buffer due to network issues.
// Sometimes this characters method is called multiple times for a tag contents.
currentpubDateSb.append(new String(ch, start, length));
}
}
if (parsingDescription) {
if (currentItem != null) {
// Here we append the description to the buffer due to network issues.
// Sometimes this characters method is called multiple times for a tag contents.
currentDescriptionSb.append(new String(ch, start, length));
}
}
else if (parsingLink) {
if (currentItem != null) {
currentItem.setLink(new String(ch, start, length));
parsingLink = false;
}
}
}
}
RssItem.java
public class RssItem {
public int imageId;
// item title
public String title;
// item link
public String link;
// item pubDate
public String pubDate;
// item description
public String description;
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getpubDate() {
return pubDate;
}
public void setpubDate(String pubDate) {
this.pubDate = pubDate;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public RssItem(int imageId, String title, String description, String link) {
this.imageId = imageId;
this.title = title;
this.description = description;
this.link = link;
}
#Override
public String toString() {
return title + "\n" + description + "\n" + pubDate;
}
}
Use Below url
http://iamvijayakumar.blogspot.in/2012/06/android-rss-feed-reader-example.html
RSS Parser with CustomAdapter using AsyncTask
I got a listView with and a search field that calls my Adapter's getFilter().filter(keyword) func.
It is working very nice, but i would like to add an other filter that searches in different tags of my listViews's objects.
So i need two filters for my Adapter, whats the best solution for this?
Thanks,
I suppose that you implemented the filter yourself. As you cant get two filters you could have a field in the filter that defines what kind of filtering should by applied ( you could use multiple filters in your filter).
Set you field of the filter to the value you want before using the filter.
Or:
Use the keyword to choose the filter to apply. Add on the beginning of the keyword some characters which define the filter to apply. With String.beginsWith() you can check which type of filtering has to by applied. This has to be done in the Filter itself. The caller of the getFilter.filter(keyword) has to know what characters have to by added in front of the string.
Apply Multiple Filter in Listview and also use the multi sorting in ListView, try this link:
https://github.com/apurv3039/filter_listview/tree/master
I had a similar need and I've written for myself. The filter are combined with AND operator. Keep it as simple as possible. Not claiming it is perfect but it works for me. You can change according to your need.
The ItemModel
public class ItemModel {
public int ID;
public int rating;
public float avg;
public String name;
public String shortDesc;
public boolean read;
}
And the parser.java
/**
* This class is designed to be simple for parsing a filter of the form
* "object field name: variable type: operator: value"
*
* "ID:int:>:20";"name:string:=:20"
* Where ';' means AND, however this is not parsed here.
* If you need different logic, use the results.O
*
* Multiple filters seperated by ';'
*/
public class Parser {
private static final String TAG = "Parser";
public static boolean isPassingTheFiler(String filter, String field, String val) {
String[] mGroups = parseMainGroups(filter);
for (String mGroup : mGroups) {
Log.e(TAG,"Working on the main Group " +mGroup );
String[] subCommand = parseSubCommand(mGroup);
if ( field.equals(subCommand[0])) {
if (!processCommand(subCommand, val)) {
return false;
}
}
}
return true;
}
/**
* parses that data assuming they are all sperated by `;`
*/
public static String[] parseMainGroups(CharSequence commands) {
String buffer = commands.toString();
String parts[] = buffer.split(";");
return parts;
}
public static String[] parseSubCommand(String subCommand) {
//remove the double quotes.
String buffer = removeDoubleQuotes(subCommand.toString());
String parts[] = buffer.split(":");
return parts;
}
public static String removeDoubleQuotes(String quotedString) {
if ((quotedString.charAt(0) == '\"') && (quotedString.charAt(quotedString.length() - 1) == '\"')) {
return quotedString.substring(1, quotedString.length() - 1);
} else {
Log.e(TAG, quotedString + " doesn't contained in double quotes!\nReturned empty string!!!");
return "";
}
}
public static boolean processCommand(String[] subCommand, String val) {
switch (subCommand[1]) {
case "int":
Log.e("TAG","\tint Filer");
return intStatement(subCommand, val);
case "float":
Log.e("TAG","\tfloat Filer");
return floatStatement(subCommand, val);
case "string":
Log.e("TAG","\tString Filer");
return stringStatement(subCommand, val);
default:
return false;
}
}
/**
* Evaluate the Int statement's correctness with the given INT value
*/
public static boolean intStatement(String[] subCommand, String cVal) {
String operString = subCommand[2];
int iVal;
int val;
try {
iVal = Integer.parseInt(subCommand[3]);
val = Integer.parseInt(cVal);
} catch (NumberFormatException e) {
return false;
}
switch (operString) {
case "=":
return val == iVal;
case "<":
return val < iVal;
case ">":
return val > iVal;
case "<=":
return val <= iVal;
case ">=":
return val >= iVal;
case "!=":
return val != iVal;
case "s" :
//digit search as string. We look into string from that we already have
return cVal.contains(subCommand[3]);
default:
Log.e("Parser", "invalid Integer Operation");
return false;
}
}
public static boolean floatStatement(String[] subCommand, String cVal) {
String operString = subCommand[2];
float iVal;
float val;
try {
iVal = Float.parseFloat(subCommand[3]);
val = Float.parseFloat(cVal);
} catch (NumberFormatException e) {
return false;
}
switch (operString) {
case "=":
return val == iVal;
case "<":
return val < iVal;
case ">":
return val > iVal;
case "<=":
return val <= iVal;
case ">=":
return val >= iVal;
case "!=":
return val != iVal;
case "s" :
//digit search as string. We look into string from that we already have
return cVal.contains(subCommand[3]);
default:
Log.e("Parser", "invalid Integer Operation");
return false;
}
}
public static boolean stringStatement(String[] subCommand, String val) {
String operString = subCommand[2];
switch (operString) {
case "=":
//equality
return val.equals(subCommand[3]);
case "<":
//substring
return val.contains(subCommand[3]);
case "sw":
//prefix
return val.startsWith(subCommand[3]);
case "ew":
//postfix
return val.endsWith(subCommand[3]);
default:
Log.e("Parser", "invalid Integer Operation");
return false;
}
}
}
The private filter class in the adapter.
private class ItemFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String charString = constraint.toString();
String[] parts;
FilterResults filterResults = new FilterResults();
if (charString.isEmpty()) {
filterResults.values = new ArrayList<>(itemList);
} else {
//Parse the main group
parts = parseMainGroups(charString);
if (parts.length < 1) {
filterResults.values = new ArrayList<>(itemList);
} else {
List<ItemModel> filteredList = new ArrayList<>();
for (ItemModel row : itemList) {
if ( !isPassingTheFiler(charString,"ID",""+row.ID)) {
continue;
} else {
Log.e("Filter", "passed on ID" + row.ID);
}
if ( !isPassingTheFiler(charString,"name",""+row.name)) {
continue;
} else {
Log.e("Filter", "passed on name" + row.name);
}
// passed every test asked. If empty they returned true!
filteredList.add(row);
}
filterResults.values = new ArrayList<>(filteredList);
}
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
updateList((List<ItemModel>) results.values);
}
}
And the updateList member funtion for the adapter
public void updateList(List<ItemModel> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new ItemDiffUtilCallback(newList, itemListFiltered));
itemListFiltered.clear();
itemListFiltered.addAll(newList);
diffResult.dispatchUpdatesTo(this);
Log.e("TAG", "updated with dispatch");
}
And the difutils that helps the good animation
public class ItemDiffUtilCallback extends DiffUtil.Callback {
private List<ItemModel> oldList;
private List<ItemModel> newList;
public ItemDiffUtilCallback(List<ItemModel> newList, List<ItemModel> oldList) {
this.newList = newList;
this.oldList = oldList;
}
#Override
public int getOldListSize() {
return oldList.size();
}
#Override
public int getNewListSize() {
return newList.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).ID == newList.get(newItemPosition).ID;
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
}
#Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return super.getChangePayload(oldItemPosition, newItemPosition);
}