XmlPullParser与自定义类不显示所有的RSS订阅
我有以下的自定义类从几个RSS像纽约时报,BBC等:饲料来获取新闻XmlPullParser与自定义类不显示所有的RSS订阅
class News{
String title;
String link;
String imageURL;
}
这是我的代码用它来解析XML数据:
void getRSSList() {
newsArray = new ArrayList<News>();
// Load each RSS feed URL in a for loop
for (int i = 0; i < catURLsList.size(); i++) {
String feedURL = catURLsList.get(i);
try {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
URL aUrl = new URL(feedURL);
InputStream is = getInputStream(aUrl);
parseRssFeeds(is);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
void parseRssFeeds(InputStream is) {
News n = new News();
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(is, "UTF_8");
boolean insideItem = false;
// Returns the type of current event: START_TAG, END_TAG, etc..
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
// Get LINK
} else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem) {
n.link = xpp.nextText();
Log.i("log-", "LINK: " + n.link);
}
// Get TITLE
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem) {
n.title = xpp.nextText();
Log.i("log-", "TITLE: " + n.title);
}
// Get MEDIA URL
} else if (xpp.getName().equalsIgnoreCase("media:content")) {
if (insideItem)
n.imageURL = xpp.getAttributeValue(null, "url");
Log.i("log-", "MEDIA URL: " + n.imageURL);
}
} else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
insideItem = false;
}
// Add news objects to the newsArray
newsArray.add(n);
eventType = xpp.next(); // move to next element
} // end WHILE loop
} catch(Exception e) { e.printStackTrace(); }
setNewsGridView();
}
setNewsGridView()调用与GridAdapter内,显示标题和RSSfeeds的图像的方法,问题是,我得到了我的logcat的所有标题,链接和媒体的URL ,但我只得到一个新闻提要重复编辑在我的GridView的每个单元格中,可能与newsArray的大小相同。
这是我GridAdapter:
// MARK: - SET NEWS GRID VIEW ---------------------------------------------
void setNewsGridView() {
class GridAdapter extends BaseAdapter {
private Context context;
public GridAdapter(Context context, List<News> objects) {
super();
this.context = context;
}
// CONFIGURE CELL
@Override
public View getView(int position, View cell, ViewGroup parent) {
if (cell == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
cell = inflater.inflate(R.layout.cell_news, null);
}
// Get News object
News n = newsArray.get(position);
// Get Title
TextView titleTxt = (TextView) cell.findViewById(R.id.cnTitleTxt);
titleTxt.setText(n.title);
// Get Image
ImageView newsImg = (ImageView)cell.findViewById(R.id.cnImage);
if (n.imageURL != null) {
Picasso.with(context).load(n.imageURL).into(newsImg);
} else { newsImg.setImageResource(R.drawable.logo); }
return cell;
}
@Override public int getCount() { return newsArray.size(); }
@Override public Object getItem(int position) { return newsArray.get(position); }
@Override public long getItemId(int position) { return position; }
}
// Init GridView and set its adapter
GridView aGrid = (GridView) findViewById(R.id.hNewsGridView);
aGrid.setAdapter(new GridAdapter(Home.this, newsArray));
// Set number of Columns accordingly to the device used
float scalefactor = getResources().getDisplayMetrics().density * 150; // 150 is the cell's width
int number = getWindowManager().getDefaultDisplay().getWidth();
int columns = (int) ((float) number/(float) scalefactor);
aGrid.setNumColumns(columns);
}
我logcat的,当我运行的应用程序:
I/log-: TITLE: Senate Votes Down Broad Obamacare Repeal
I/log-: LINK: https://www.nytimes.com/2017/07/25/us/politics/senate-health-care.html?partner=rss&emc=rss
I/log-: MEDIA URL: https://static01.nyt.com/images/2017/07/26/us/26dc-health-sub1/26dc-health-sub1-moth.jpg
I/log-: TITLE: John McCain to Senate: ‘We’re Getting Nothing Done’
I/log-: LINK: https://www.nytimes.com/video/us/politics/100000005305566/john-mccain-health-bill-vote.html?partner=rss&emc=rss
I/log-: TITLE: McCain Returns to Cast Vote to Help the President Who Derided Him
I/log-: LINK: https://www.nytimes.com/2017/07/25/us/politics/mccain-health-care-brain-cancer
etc...
...
这是输出我得到的设备上:
我在做什么错?
非常感谢!
Inside parseRssFeeds
您在进入主循环之前实例化一个News
对象,然后当您从XML中提取新闻块时,将在此设置值并将其添加到newsArray
。然后,在继续迭代时,每次覆盖News
对象n
中的值。从Java passes objects as references开始,最终会生成一个包含单个实例的多个副本的列表,其值为n
,其值设置为XML中的最后一个条目。
要解决此问题,您需要在循环中移动声明。 (编辑)我在这里第一次尝试的问题是,我认为每个News
对象将在单个循环迭代期间被读取,但情况并非如此。你需要在每次遇到一个新的新闻项目的时候,应该给你的东西像下面实例化一个新News
对象:
// Returns the type of current event: START_TAG, END_TAG, etc..
News currentNewsItem = null;
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
// Get LINK
} else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem) {
// If no item is currently in progress, start one
currentNewsItem = startNewItemIfRequired(currentNewsItem, newsArray);
currentNewsItem.link = xpp.nextText();
Log.i("log-", "LINK: " + currentNewsItem.link);
}
// Get TITLE
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem) {
// Start a new news item, even if one is already in progress
currentNewsItem = startNewItemIfRequired(null, newsArray);
currentNewsItem.title = xpp.nextText();
Log.i("log-", "TITLE: " + currentNewsItem.title);
}
// Get MEDIA URL
} else if (xpp.getName().equalsIgnoreCase("media:content")) {
if (insideItem)
// If no item is currently in progress, start one
currentNewsItem = startNewItemIfRequired(currentNewsItem, newsArray);
currentNewsItem.imageURL = xpp.getAttributeValue(null, "url");
Log.i("log-", "MEDIA URL: " + currentNewsItem.imageURL);
}
} else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
insideItem = false;
}
eventType = xpp.next(); // move to next element
} // end WHILE loop
} catch(Exception e) { e.printStackTrace(); }
有了这个附加功能:
private News startNewItemIfRequired(News currentNewsItem, List<News> newsArray) {
if (currentNewsItem==null){
currentNewsItem = new News();
newsArray.add(currentNewsItem);
}
return currentNewsItem;
}
这将创建一个新的新闻项目遇到“标题”开始标记时,并将其添加到面板,然后继续填充,直到开始新标记。为了安全起见,我添加了一些检查,如果没有“标题”,它将防止空指针。也就是说,这确实会添加一个约束条件,即“title”必须首先出现在您收到的XML中 - 这似乎是您日志中的情况,但是如果您期望这会发生变化,您将需要一些更复杂的东西。
我想补充一点,这种流式传输方式是最简单的方法,但如果您希望将来可以使用这种方法(尤其是防止XML结构和缺失字段的变化),您可能会先阅读XML作为文档,然后提取出您感兴趣的结构。
恐怕我还没有你的问题没有足够的代码让我不用花费一些时间来分解你的依赖关系,所以我一直没有能够真正测试自己的代码,但希望这会给你足够的时间去解决你的问题!
问题是你一次创建了News n = new News();
对象,但每次将该对象添加到ArrayList中,所以它始终显示所有项目中的最新新闻内容。每当有新的标签被调用item
,并且在标签item
的末尾int时,您应该创建对象,您应该插入到ArrayList中。
你应该做这样的,
void parseRssFeeds(InputStream is) {
// News n = new News(); your are creating object only here.
News n = null;
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(is, "UTF_8");
boolean insideItem = false;
// Returns the type of current event: START_TAG, END_TAG, etc..
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
n = new News(); // you should intialize the object every time here
// Get LINK
} else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem) {
n.link = xpp.nextText();
Log.i("log-", "LINK: " + n.link);
}
// Get TITLE
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem) {
n.title = xpp.nextText();
Log.i("log-", "TITLE: " + n.title);
}
// Get MEDIA URL
} else if (xpp.getName().equalsIgnoreCase("media:content")) {
if (insideItem)
n.imageURL = xpp.getAttributeValue(null, "url");
Log.i("log-", "MEDIA URL: " + n.imageURL);
}
} else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
insideItem = false;
// End of the item tag, we should add it into the list
// Add news objects to the newsArray
newsArray.add(n);
}
eventType = xpp.next(); // move to next element
} // end WHILE loop
} catch (Exception e) {
e.printStackTrace();
}
setNewsGridView();
}
完美的作品,谢谢! – cubycode
改造与SimpleXmlConverter
OkHttpClient okHttpClient = new
OkHttpClient.Builder().readTimeout(60,
TimeUnit.SECONDS).connectTimeout(60, TimeUnit.SECONDS)
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException
{
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("Accept", "application/xml");
Request request = requestBuilder.build();
return chain.proceed(request);
}
}).build();
retrofitClient = new Retrofit.Builder()
.baseUrl(RestAPI.baseURL)
.client(okHttpClient)
.addConverterFactory(SimpleXmlConverterFactory.create())
.build();
restAPI = retrofitClient.create(RestAPI.class);
它不工作:(也, “如果(N!= NULL)” 始终是真实的,所以它不是如果我使用“News n = new News()”,它会在我的GridView中给我空单元格,而如果我使用“News n = null”,它根本不会给我任何单元格。 – cubycode
好的,我会稍后再试,我注意到它甚至不打印日志,并且即使我在我的应用程序中根本不使用SQL,它仍然会打印SQL错误... – cubycode
n如果eventType是类型为“item”的START_TAG或非START_TAG,则它将为空,因为它始于空值且始终不会填充。当你说它不起作用时,你会得到什么?可能有很多部分填充的面板,一些带有链接,一些标题和一些URL? – hugh