In this android rss reader tutorial we will see how to make demo app that can read rss of a website or blog. So without wasting much time lets begin.
在这个android rss阅读器教程中,我们将了解如何制作可读取网站或博客rss的演示应用程序。 因此,在不浪费大量时间的情况下开始吧。
Typically the rss feed of a website or blog look something like shown below.
通常,网站或博客的rss feed如下所示。
<channel>
<title></title>
<link></link>
<description></description>
<item>
<title></title>
<link></link>
<pubDate></pubDate>
<description></description>
</item>
<item>
<title></title>
<link></link>
<pubDate></pubDate>
<description></description>
</item>
.
.
.
.
</channel>
The data we get is in xml format.
我们获得的数据为xml格式。
Here channel tag is the root or the main element from where rss begins.
这里的channel标签是rss的根或主要元素。
title shows the name of the website.
标题显示网站的名称。
description tells the content of website.
描述说明了网站的内容。
link is the url to the website.
链接是网站的网址。
There are multiple item tags that contain article or post information like post title, link, publish date, etc.
有多个项目标签,其中包含文章或文章信息,例如文章标题,链接,发布日期等。
First of all create a new android project to read the rss in android app.
首先创建一个新的android项目,以读取android应用程序中的rss。
Then we need to give internet permission in manifest to access the rss in application.
然后,我们需要在清单中授予Internet权限以访问应用程序中的rss。
<uses-permission android:name="android.permission.INTERNET"/>
Now we are adding some of the dependency that we need in our project.
现在,我们在项目中添加了一些需要的依赖。
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
We are designing our activity_main.xml file.
我们正在设计activity_main.xml文件。
activity_main.xml
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/fetchFeedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="Fetch" />
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_toStartOf="@id/fetchFeedButton"
android:hint="Rss feed source">
<EditText
android:id="@+id/rssFeedEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/feedTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/textInputLayout"
android:text="Feed Title: " />
<TextView
android:id="@+id/feedDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/feedTitle"
android:text="Feed Description: " />
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/feedDescription"
android:layout_marginTop="20dp">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
Here we are using swipe refresh layout just to refresh the rss content of a website.
在这里,我们使用滑动刷新布局只是为了刷新网站的rss内容。
Now we have to code MainActivity.java for building our functionality of application so first we have to reference xml components in java.
现在我们必须编码MainActivity.java来构建应用程序的功能,因此首先我们必须引用java中的xml组件。
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private EditText mEditText;
private Button mFetchFeedButton;
private SwipeRefreshLayout mSwipeLayout;
private TextView mFeedTitleTextView;
private TextView mFeedDescriptionTextView;
private List<RssFeedModel> mFeedModelList;
private String mFeedTitle;
private String mFeedContent;
private String mFeedDescription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mEditText = (EditText) findViewById(R.id.rssFeedEditText);
mFetchFeedButton = (Button) findViewById(R.id.fetchFeedButton);
mSwipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mFeedTitleTextView = (TextView) findViewById(R.id.feedTitle);
mFeedDescriptionTextView = (TextView) findViewById(R.id.feedDescription);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mFetchFeedButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new FetchFeedTask().execute((Void) null);
}
});
mSwipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new FetchFeedTask().execute((Void) null);
}
});
}
We have to define a java class called FetchFeedTask which is used to get the rss feed from url.
我们必须定义一个名为FetchFeedTask的Java类,该类用于从url获取rss feed。
private class FetchFeedTask extends AsyncTask<Void, Void, Boolean> {
private String urlLink;
@Override
protected void onPreExecute() {
mSwipeLayout.setRefreshing(true);
urlLink = mEditText.getText().toString();
}
@Override
protected Boolean doInBackground(Void... voids) {
if (TextUtils.isEmpty(urlLink))
return false;
try {
if(!urlLink.startsWith("http://") && !urlLink.startsWith("https://"))
urlLink = "http://" + urlLink;
URL url = new URL(urlLink);
InputStream inputStream = url.openConnection().getInputStream();
mFeedModelList = parseFeed(inputStream);
return true;
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
return false;
}
@Override
protected void onPostExecute(Boolean success) {
mSwipeLayout.setRefreshing(false);
if (success) {
mFeedTitleTextView.setText("Feed Title: " + mFeedTitle);
mFeedDescriptionTextView.setText("Feed Description: " + mFeedDescription);
// Fill RecyclerView
mRecyclerView.setAdapter(new RssFeedListAdapter(mFeedModelList));
} else {
Toast.makeText(MainActivity.this,
"Enter a valid Rss feed url",
Toast.LENGTH_LONG).show();
}
}
}
Now we are defining a list and parsing xml data to get the attribute of items from the rss feed we have extracted.
现在,我们定义一个列表并解析xml数据,以从已提取的rss feed中获取项目的属性。
public List<RssFeedModel> parseFeed(InputStream inputStream) throws XmlPullParserException,
IOException {
String title = null;
String content = null;
String description = null;
boolean isItem = false;
List<RssFeedModel> items = new ArrayList<>();
try {
XmlPullParser xmlPullParser = Xml.newPullParser();
xmlPullParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
xmlPullParser.setInput(inputStream, null);
xmlPullParser.nextTag();
while (xmlPullParser.next() != XmlPullParser.END_DOCUMENT) {
int eventType = xmlPullParser.getEventType();
String name = xmlPullParser.getName();
if(name == null)
continue;
if(eventType == XmlPullParser.END_TAG) {
if(name.equalsIgnoreCase("item")) {
isItem = false;
}
continue;
}
if (eventType == XmlPullParser.START_TAG) {
if(name.equalsIgnoreCase("item")) {
isItem = true;
continue;
}
}
Log.d("MyXmlParser", "Parsing name ==> " + name);
String result = "";
if (xmlPullParser.next() == XmlPullParser.TEXT) {
result = xmlPullParser.getText();
xmlPullParser.nextTag();
}
if (name.equalsIgnoreCase("title")) {
title = result;
} else if (name.equalsIgnoreCase("content:encoded")) {
content = result;
} else if (name.equalsIgnoreCase("description")) {
description = result;
}
if (title != null && content != null && description != null) {
if(isItem) {
RssFeedModel item = new RssFeedModel(title, content, description);
items.add(item);
}
else {
mFeedTitle = title;
mFeedContent = content;
mFeedDescription = description;
}
title = null;
content = null;
description = null;
isItem = false;
}
}
return items;
} finally {
inputStream.close();
}
}
Defining RssModel java class which we used to hold the attribute of every post.
定义我们用来保存每个帖子属性的RssModel java类。
public class RssFeedModel {
public String title;
public String content;
public String description;
public RssFeedModel(String title, String content, String description) {
this.title = title;
this.content = content;
this.description = description;
}
}
Defining RecyclerAdapter to attach our list data to recyclerview.
定义RecyclerAdapter以将我们的列表数据附加到recyclerview。
public class RssFeedListAdapter
extends RecyclerView.Adapter<RssFeedListAdapter.FeedModelViewHolder> {
private List<RssFeedModel> mRssFeedModels;
public class FeedModelViewHolder extends RecyclerView.ViewHolder {
private View rssFeedView;
public FeedModelViewHolder(View v) {
super(v);
rssFeedView = v;
}
}
public RssFeedListAdapter(List<RssFeedModel> rssFeedModels) {
mRssFeedModels = rssFeedModels;
}
@Override
public FeedModelViewHolder onCreateViewHolder(ViewGroup parent, int type) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_layout, parent, false);
FeedModelViewHolder holder = new FeedModelViewHolder(v);
return holder;
}
@Override
public void onBindViewHolder(FeedModelViewHolder holder, int position) {
final RssFeedModel rssFeedModel = mRssFeedModels.get(position);
((TextView)holder.rssFeedView.findViewById(R.id.titleText)).setText(rssFeedModel.title);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(Html.fromHtml(rssFeedModel.description,Html.FROM_HTML_MODE_LEGACY));
} else {
((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(Html.fromHtml(rssFeedModel.description)); //we have used Html.fromHtml() in textview to make our textView support html content
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),Final.class);
intent.putExtra("content",rssFeedModel.content);
startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return mRssFeedModels.size();
}
}
Finally the MainActivity.java file will look like.
最终,MainActivity.java文件将如下所示。
MainActivity.java
MainActivity.java
package com.example.anmol.rssdemo;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private EditText mEditText;
private Button mFetchFeedButton;
private SwipeRefreshLayout mSwipeLayout;
private TextView mFeedTitleTextView;
private TextView mFeedDescriptionTextView;
private List<RssFeedModel> mFeedModelList;
private String mFeedTitle;
private String mFeedContent;
private String mFeedDescription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mEditText = (EditText) findViewById(R.id.rssFeedEditText);
mFetchFeedButton = (Button) findViewById(R.id.fetchFeedButton);
mSwipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mFeedTitleTextView = (TextView) findViewById(R.id.feedTitle);
mFeedDescriptionTextView = (TextView) findViewById(R.id.feedDescription);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mFetchFeedButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new FetchFeedTask().execute((Void) null);
}
});
mSwipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new FetchFeedTask().execute((Void) null);
}
});
}
private class FetchFeedTask extends AsyncTask<Void, Void, Boolean> {
private String urlLink;
@Override
protected void onPreExecute() {
mSwipeLayout.setRefreshing(true);
urlLink = mEditText.getText().toString();
}
@Override
protected Boolean doInBackground(Void... voids) {
if (TextUtils.isEmpty(urlLink))
return false;
try {
if(!urlLink.startsWith("http://") && !urlLink.startsWith("https://"))
urlLink = "http://" + urlLink;
URL url = new URL(urlLink);
InputStream inputStream = url.openConnection().getInputStream();
mFeedModelList = parseFeed(inputStream);
return true;
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
return false;
}
@Override
protected void onPostExecute(Boolean success) {
mSwipeLayout.setRefreshing(false);
if (success) {
mFeedTitleTextView.setText("Feed Title: " + mFeedTitle);
mFeedDescriptionTextView.setText("Feed Description: " + mFeedDescription);
// Fill RecyclerView
mRecyclerView.setAdapter(new RssFeedListAdapter(mFeedModelList));
} else {
Toast.makeText(MainActivity.this,
"Enter a valid Rss feed url",
Toast.LENGTH_LONG).show();
}
}
}
public List<RssFeedModel> parseFeed(InputStream inputStream) throws XmlPullParserException,
IOException {
String title = null;
String content = null;
String description = null;
boolean isItem = false;
List<RssFeedModel> items = new ArrayList<>();
try {
XmlPullParser xmlPullParser = Xml.newPullParser();
xmlPullParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
xmlPullParser.setInput(inputStream, null);
xmlPullParser.nextTag();
while (xmlPullParser.next() != XmlPullParser.END_DOCUMENT) {
int eventType = xmlPullParser.getEventType();
String name = xmlPullParser.getName();
if(name == null)
continue;
if(eventType == XmlPullParser.END_TAG) {
if(name.equalsIgnoreCase("item")) {
isItem = false;
}
continue;
}
if (eventType == XmlPullParser.START_TAG) {
if(name.equalsIgnoreCase("item")) {
isItem = true;
continue;
}
}
Log.d("MyXmlParser", "Parsing name ==> " + name);
String result = "";
if (xmlPullParser.next() == XmlPullParser.TEXT) {
result = xmlPullParser.getText();
xmlPullParser.nextTag();
}
if (name.equalsIgnoreCase("title")) {
title = result;
} else if (name.equalsIgnoreCase("content:encoded")) {
content = result;
} else if (name.equalsIgnoreCase("description")) {
description = result;
}
if (title != null && content != null && description != null) {
if(isItem) {
RssFeedModel item = new RssFeedModel(title, content, description);
items.add(item);
}
else {
mFeedTitle = title;
mFeedContent = content;
mFeedDescription = description;
}
title = null;
content = null;
description = null;
isItem = false;
}
}
return items;
} finally {
inputStream.close();
}
}
public class RssFeedModel {
public String title;
public String content;
public String description;
public RssFeedModel(String title, String content, String description) {
this.title = title;
this.content = content;
this.description = description;
}
}
public class RssFeedListAdapter
extends RecyclerView.Adapter<RssFeedListAdapter.FeedModelViewHolder> {
private List<RssFeedModel> mRssFeedModels;
public class FeedModelViewHolder extends RecyclerView.ViewHolder {
private View rssFeedView;
public FeedModelViewHolder(View v) {
super(v);
rssFeedView = v;
}
}
public RssFeedListAdapter(List<RssFeedModel> rssFeedModels) {
mRssFeedModels = rssFeedModels;
}
@Override
public FeedModelViewHolder onCreateViewHolder(ViewGroup parent, int type) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_layout, parent, false);
FeedModelViewHolder holder = new FeedModelViewHolder(v);
return holder;
}
@Override
public void onBindViewHolder(FeedModelViewHolder holder, int position) {
final RssFeedModel rssFeedModel = mRssFeedModels.get(position);
((TextView)holder.rssFeedView.findViewById(R.id.titleText)).setText(rssFeedModel.title);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(Html.fromHtml(rssFeedModel.description,Html.FROM_HTML_MODE_LEGACY));
} else {
((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(Html.fromHtml(rssFeedModel.description));
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),Final.class);
intent.putExtra("content",rssFeedModel.content);
startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return mRssFeedModels.size();
}
}
}
We have to define the layout used in every recyclerview post.
我们必须定义每个recyclerview帖子中使用的布局。
recycler_layout.xml
recycler_layout.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/titleText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold" />
<TextView
android:id="@+id/descriptionText"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorAccent" />
</LinearLayout>
Atlast we have to use our adapter in our onPostExecute method to distribute our list in recyclerAdapter.
最后,我们必须在onPostExecute方法中使用适配器以在recyclerAdapter中分发列表。
mRecyclerView.setAdapter(new RssFeedListAdapter(mFeedModelList));
We have defined another activity which is called when we click on anycard of recyclerView which opens full content of a post in webView.
我们定义了另一个活动,当我们单击recyclerView的任何卡片时会调用该活动,从而在webView中打开帖子的完整内容。
activity_final.xml file
activity_final.xml文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Final">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/web"/>
</android.support.constraint.ConstraintLayout>
And we have to define its java class too.
而且我们也必须定义其java类。
Final.java
Final.java
package com.example.anmol.rssdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class Final extends AppCompatActivity {
WebView webview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_final);
webview=(WebView)findViewById(R.id.web);
webview.getSettings().setJavaScriptEnabled(true);
webview.loadData(getIntent().getStringExtra("content"),"text/html","UTF-8");
webview.setWebViewClient(new WebViewClient());
}
}
![Android RSS Reader Tutorial](https://i-blog.csdnimg.cn/blog_migrate/04d22633931ac53e644fc92fafb50844.png)
That’s all, now you can run your app in your physical device or in your emulator and see your rss feed in list format.
就是这样,现在您可以在物理设备或模拟器中运行应用程序,并以列表格式查看rss feed。
Comment below if you have any queries related to above android rss reader example.
如果您有任何与上述android rss阅读器示例相关的查询,请在下面评论。
翻译自: https://www.thecrazyprogrammer.com/2018/12/android-rss-reader-tutorial.html