Android RSS Reader отключается после первого абзаца

Я создаю приложение для своей школьной газеты и сталкиваюсь с проблемой при попытке отобразить полную статью. В настоящее время у меня появляется список статей, которые извлекаются из RSS-канала, и при нажатии на них появляется содержимое статьи. Однако в TextView отображается только первый абзац, независимо от его длины. Это наводит меня на мысль, что это как-то связано с HTML-тегами <p></p>. Я не так хорошо знаком с RSS-каналами или синтаксическим анализом XML (я впервые пытаюсь это сделать) и искал способы сделать то, что я пытаюсь сделать.

From: Android TextView обрезается после одного абзаца

Я создаю этот вопрос на основе результатов вышеуказанного вопроса. Я думал, что проблема с моим приложением как-то связана с TextView и его свойствами, но с использованием простого текста, который я жестко запрограммировал, он работает нормально.

Судя по комментариям и тому, что я пробовал, проблема заключается в том, как приложение считывает информацию из RSS-канала. Как я уже говорил, я впервые работаю с RSS-каналами в Android, и я использую пример кода проекта (можно найти в предыдущих вопросах). Ниже приведен код, относящийся к RSS-каналу:

RSSFeed.java:

public class RSSFeed {

    private String title = null;
    private String description = null;
    private String link = null;
    private String pubdate = null;
    private String content = null;
    private List<RSSItem> itemList;

    RSSFeed(){
        itemList = new Vector<RSSItem>(0);
    }

    void addItem(RSSItem item){
        itemList.add(item);
    }

    RSSItem getItem(int location){
        return itemList.get(location);
    }

    List<RSSItem> getList(){
        return itemList;
    }

    void setTitle(String value){
        title = value;
    }
    void setDescription(String value){
        description = value;
    }
    void setLink(String value){
        link = value;
    }
    void setPubdate(String value){
        pubdate = value;
    }
    public void setContent(String value) {
        content=value;
    }

    String getTitle(){
        return title;
    }
    String getDescription(){
        return description;
    }
    String getLink(){
        return link;
    }
    String getPubdate(){
        return pubdate;
    }

    String getContent() {
        return content;
    }



}

RSSHandler.java:

public class RSSHandler extends DefaultHandler {

// Feed and Article objects to use for temporary storage
private Article currentArticle = new Article();
private List<Article> articleList = new ArrayList<Article>();

// Number of articles added so far
private int articlesAdded = 0;

// Number of articles to download
private static final int ARTICLES_LIMIT = 15;

//Current characters being accumulated
StringBuffer chars = new StringBuffer();


/* 
 * This method is called every time a start element is found (an opening XML marker)
 * here we always reset the characters StringBuffer as we are only currently interested
 * in the the text values stored at leaf nodes
 * 
 * (non-Javadoc)
 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
 */
public void startElement(String uri, String localName, String qName, Attributes atts) {
    chars = new StringBuffer();
}



/* 
 * This method is called every time an end element is found (a closing XML marker)
 * here we check what element is being closed, if it is a relevant leaf node that we are
 * checking, such as Title, then we get the characters we have accumulated in the StringBuffer
 * and set the current Article's title to the value
 * 
 * If this is closing the "Item", it means it is the end of the article, so we add that to the list
 * and then reset our Article object for the next one on the stream
 * 
 * 
 * (non-Javadoc)
 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
 */
public void endElement(String uri, String localName, String qName) throws SAXException {

    if (localName.equalsIgnoreCase("title"))
    {
        Log.d("LOGGING RSS XML", "Setting article title: " + chars.toString());
        currentArticle.setTitle(chars.toString());

    }
    else if (localName.equalsIgnoreCase("description"))
    {
        Log.d("LOGGING RSS XML", "Setting article description: " + chars.toString());
        currentArticle.setDescription(chars.toString());
    }
    else if (localName.equalsIgnoreCase("pubDate"))
    {
        Log.d("LOGGING RSS XML", "Setting article published date: " + chars.toString());
        currentArticle.setPubDate(chars.toString());
    }
    else if (localName.equalsIgnoreCase("encoded"))
    {
        Log.d("LOGGING RSS XML", "Setting article content: " + chars.toString());
        currentArticle.setEncodedContent(chars.toString());
    }
    else if (localName.equalsIgnoreCase("item"))
    {

    }
    else if (localName.equalsIgnoreCase("link"))
    {
        try {
            Log.d("LOGGING RSS XML", "Setting article link url: " + chars.toString());
            currentArticle.setUrl(new URL(chars.toString()));
        } catch (MalformedURLException e) {
            Log.e("RSA Error", e.getMessage());
        }

    }




    // Check if looking for article, and if article is complete
    if (localName.equalsIgnoreCase("item")) {

        articleList.add(currentArticle);

        currentArticle = new Article();

        // Lets check if we've hit our limit on number of articles
        articlesAdded++;
        if (articlesAdded >= ARTICLES_LIMIT)
        {
            throw new SAXException();
        }
    }
}





/* 
 * This method is called when characters are found in between XML markers, however, there is no
 * guarantee that this will be called at the end of the node, or that it will be called only once
 * , so we just accumulate these and then deal with them in endElement() to be sure we have all the
 * text
 * 
 * (non-Javadoc)
 * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
 */
public void characters(char ch[], int start, int length) {
    chars.append(new String(ch, start, length));
}





/**
 * This is the entry point to the parser and creates the feed to be parsed
 * 
 * @param feedUrl
 * @return
 */
public List<Article> getLatestArticles(String feedUrl) {
    URL url = null;
    try {

        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = spf.newSAXParser();
        XMLReader xr = sp.getXMLReader();

        url = new URL(feedUrl);

        xr.setContentHandler(this);
        xr.parse(new InputSource(url.openStream()));


    } catch (IOException e) {
        Log.e("RSS Handler IO", e.getMessage() + " >> " + e.toString());
    } catch (SAXException e) {
        Log.e("RSS Handler SAX", e.toString());
    } catch (ParserConfigurationException e) {
        Log.e("RSS Handler Parser Config", e.toString());
    }

    return articleList;
}

}

RSSItem.java:

public class RSSItem {

private String title = null;
private String description = null;
private String link = null;
private String pubdate = null;
private String content = null;

RSSItem(){
}

void setTitle(String value){
    title = value;
}
void setDescription(String value){
    description = value;
}
void setLink(String value){
    link = value;
}
void setPubdate(String value){
    pubdate = value;
}
public void setContent(String value) {
    content=value;
}

String getTitle(){
    return title;
}
String getDescription(){
    return description;
}
String getLink(){
    return link;
}
String getPubdate(){
    return pubdate;
}

public String getContent() {
    return content;
}



@Override
public String toString() {
    // TODO Auto-generated method stub
    return title;
}

}

AllStoriesFragment.java:

public class AllStoriesFragment extends ListFragment {

/*********************************************************************
 * RSS Async Task
 *********************************************************************/
public class RssLoadingTask extends AsyncTask<Void, Void, Void> {

    @Override
    protected void onPostExecute(Void result) {
        // TODO Auto-generated method stub
        displayRss();
    }

    @Override
    protected void onPreExecute() {
        // TODO Auto-generated method stub
        preReadRss();
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        // TODO Auto-generated method stub
        //super.onProgressUpdate(values);
    }

    @Override
    protected Void doInBackground(Void... arg0) {
        // TODO Auto-generated method stub
        readRss();
        return null;
    }

}
/*********************************************************************
 * End RSS Async Task
 *********************************************************************/

private RSSFeed myRssFeed = null;

TextView feedTitle;
TextView feedDescription;

/*********************************************************************
 * Custom Array Adapter 
 *********************************************************************/

public class MyCustomAdapter extends ArrayAdapter<RSSItem> {

        public MyCustomAdapter(Context context, int textViewResourceId, List<RSSItem> list) {
            super(context, textViewResourceId, list);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            //return super.getView(position, convertView, parent);

            View row = convertView;

            if(row==null){
                LayoutInflater inflater=getActivity().getLayoutInflater();
                row=inflater.inflate(R.layout.row, parent, false);
            }
            //Set Item Title
            TextView listTitle=(TextView)row.findViewById(R.id.listtitle);
            listTitle.setText(myRssFeed.getList().get(position).getTitle());

            //Set Item PubDate
            TextView listPubdate=(TextView)row.findViewById(R.id.listpubdate);
            listPubdate.setText(myRssFeed.getList().get(position).getPubdate());

            if (position%2 == 0){
                listTitle.setBackgroundColor(0xff101010);
                listPubdate.setBackgroundColor(0xff101010);
            }
            else{
                listTitle.setBackgroundColor(0xff080808);
                listPubdate.setBackgroundColor(0xff080808);
            }

            return row;
        }
    }
    /*********************************************************************
     * End Custom Array Adapter 
     *********************************************************************/

   /** Called when the fragment is first created. */
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v =  inflater.inflate(R.layout.fragment_allstories, null);
        Log.d("MainActivity", "AllStoriesFragment started.");

        feedTitle = (TextView)v.findViewById(R.id.feedtitle);
        feedDescription = (TextView)v.findViewById(R.id.feeddescription);
        startReadRss();

        return v ;
    }

   private void startReadRss(){
       new RssLoadingTask().execute();
   }

   private void preReadRss(){
       setListAdapter(null);

       Toast.makeText(getActivity(), "Reading RSS, Please wait.", Toast.LENGTH_LONG).show();
   }

   private void readRss(){
       try {
           URL rssUrl = new URL("http://www.campusslate.com/feed/");
           InputSource myInputSource = new InputSource(rssUrl.openStream());

           SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
           SAXParser mySAXParser = mySAXParserFactory.newSAXParser();

           RSSHandler myRSSHandler = new RSSHandler();

           XMLReader myXMLReader = mySAXParser.getXMLReader();

           myXMLReader.setContentHandler(myRSSHandler);
           myXMLReader.parse(myInputSource);

           myRssFeed = myRSSHandler.getFeed();
       } 
       catch (MalformedURLException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       } 
       catch (ParserConfigurationException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       } 
       catch (SAXException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       } 
       catch (IOException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
    }

   private void displayRss(){

       if (myRssFeed!=null){
           MyCustomAdapter adapter = new MyCustomAdapter(getActivity(), R.layout.row, myRssFeed.getList());
           setListAdapter(adapter);
       }
   }


   public void onListItemClick(ListView l, View v, int position, long id) {
       // TODO Auto-generated method stub
       Intent intent = new Intent(getActivity(), ShowDetails.class);
       intent.putExtra("keyPubdate", myRssFeed.getItem(position).getPubdate());
       intent.putExtra("keyLink", myRssFeed.getItem(position).getLink());
       intent.putExtra("keyTitle", myRssFeed.getItem(position).getTitle());
       intent.putExtra("keyContent", myRssFeed.getItem(position).getContent());
       startActivity(intent);
   }

   @Override
   public boolean onOptionsItemSelected(MenuItem item) {
    // TODO Auto-generated method stub
    switch(item.getItemId()){
    case (0): readRss();
      break;
    default:
      break;
    }

    return true;
   }
}

ShowDetails.java размещен в другом вопросе, хотя код может немного отличаться от того, что у меня есть сейчас. Теперь проблема заключается в том, что каждый элемент в списке является последним элементом, прочитанным из RSS-канала.

Разместил бы изображение, если бы у меня было достаточно репутации.


person Yaminick    schedule 29.01.2013    source источник


Ответы (1)


Элемент XML может иметь несколько текстовых узлов. Ваш код предполагает, что существует только один. Пожалуйста, используйте операцию добавления, а не операцию установки в обратном вызове characters().

person CommonsWare    schedule 29.01.2013
comment
Я обновил свой RSSHandler в соответствии с примером, который я видел, который использовал добавление в character() и выполнял всю работу в endElement(). Он запускается, но теперь он устанавливает для всех элементов в списке последний элемент, прочитанный из RSS-канала. Есть идеи, как это исправить? - person Yaminick; 31.01.2013
comment
Я также добавил текущий AllStoriesFragment, я считаю, что моя проблема в данный момент. - person Yaminick; 31.01.2013
comment
Попробуйте этот подход goo.gl/Hwp7x. Это полнофункциональная программа для чтения RSS, которую я использовал для Android Rivers. Базовый синтаксический анализатор XML — это github.com/thebuzzmedia/simple-java-xml-parser (это чертовски хорошо) - person DodyG; 10.02.2013
comment
В итоге я перестроил проект, объединив то, что у меня уже было, с другим найденным примером. После того, как все сказано и сделано, использование решения для добавления и изменение способа работы моего RSSHandler все исправили. Благодарю вас! - person Yaminick; 14.02.2013