آموزش خواندن Rss در برنامه نویسی اندروید

سلام دوستان در این سری از آموزش های برنامه نویسی اندروید به آموزش خواندن Rss در برنامه نویسی اندروید می پردازیم این آموزش به در خواست یکی از کاربران ایجاد شده است با ما همراه باشید. باید ابتدا اشاره کنیم ممکن است rss بعضی از سایت ها سفارشی شده باشید و برنامه نتواند آن ها را بخواند ولی به طور معمول rss بیشتر سایت ها شبیه هم هستند.
قبل از هرچیز وارد فایل Build.gradle شده و کتاب خانه های زیر را اضافه کنید.(در بخش  dependencies)

  compile 'com.android.support:appcompat-v7:25.0.1'
    compile 'com.android.support:recyclerview-v7:25.0.1'
    compile 'com.android.support:design:25.0.1'

سپس پروژه رو Sync کنید (علت خطاهای گریدل را برای شما بررسی کردیم کافی است در سایت سرچ کنید )
بخش activity_main.xml ما همانند زیر می شود.

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.sample.foo.simplerssreader.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="@dimen/activity_horizontal_margin"
        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: " />
    <TextView
        android:id="@+id/feedLink"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/feedDescription"
        android:text="Feed Link: " />
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/feedLink"
        android:layout_marginTop="@dimen/activity_vertical_margin">
        <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>

و بخش layout ما شامل یک دکمه سه تا TextView یک input یا همان EditText و یک SwipeRefreshLayout و یک Recycler view (دوست عزیزی که سوال پرسیده بود گفتن در Recyclerview قرار بدهیم به همین علت از آن هم استفاده کردیم.) SwipeRefreshLayout  به کاربر اجازه می دهد تا زمانی که از بالا به پایین بروی اکتیویتی کشید یک progress bar قشنگ به نمایش در می آید و صفحه را تازه سازی (refresh) می کند به طور مثال برای دریافت دیتا صفحه refresh می شود.
قبلتر ما recyclerview را توضیح دادیم. حالا باید برای recycler view ایتم های آن را درست کنیم یعنی مقدارهایی که می خواهد نمایش داده شود.
پس مثل زیر عمل می کنیم.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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" />
<TextView
    android:id="@+id/linkText"
    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>

 
کد بالا هر ایتم از Recycler view را به وجود می آورد یعنی کل کد بالا شبیه به یک لیست در یک ایتم قرار می گیرد.
برای اینکه مقدارها را در هرکدوم از آنها ست بشه باید یک کلاسی درست کنیم که title و description و link را در هر یکی از آیتم های Recycler view قرار دهد.

public class RssFeedModel {
    public String title;
    public String link;
    public String description;
    public RssFeedModel(String title, String link, String description) {
        this.title = title;
        this.link = link;
        this.description = description;
    }
}

حالا برای اینکه مقدار ها در هر item ست بشن (قرار داده شوند) باید برای کلاس بالا یک آداپتور درست کنیم پس مثل زیر عمل می کنیم.

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.util.List;
/**
 * Created by Jeferson on 27/11/2017.
 */
public class RssFeedListAdapter
        extends RecyclerView.Adapter<RssFeedListAdapter.FeedModelViewHolder> {
    private List<RssFeedModel> mRssFeedModels;
    public static 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.item_rss_feed, 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);
        ((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(rssFeedModel.description);
        ((TextView)holder.rssFeedView.findViewById(R.id.linkText)).setText(rssFeedModel.link);
    }
    @Override
    public int getItemCount() {
        return mRssFeedModels.size();
    }
}

و بعد از آن کد های مربوط به MainActivty.java را قرار می دهیم.(در ادامه آنها را توضیح خواهیم داد)

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.TextUtils;
import android.util.Log;
import android.util.Xml;
import android.view.View;
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.HashMap;
import java.util.List;
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private RecyclerView mRecyclerView;
    private EditText mEditText;
    private Button mFetchFeedButton;
    private SwipeRefreshLayout mSwipeLayout;
    private TextView mFeedTitleTextView;
    private TextView mFeedLinkTextView;
    private TextView mFeedDescriptionTextView;
    private List<RssFeedModel> mFeedModelList;
    private String mFeedTitle;
    private String mFeedLink;
    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);
        mFeedLinkTextView = (TextView) findViewById(R.id.feedLink);
        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);
            }
        });
    }
    public List<RssFeedModel> parseFeed(InputStream inputStream) throws XmlPullParserException, IOException {
        String title = null;
        String link = 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("MainActivity", "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("link")) {
                    link = result;
                } else if (name.equalsIgnoreCase("description")) {
                    description = result;
                }
                if (title != null && link != null && description != null) {
                    if(isItem) {
                        RssFeedModel item = new RssFeedModel(title, link, description);
                        items.add(item);
                    }
                    else {
                        mFeedTitle = title;
                        mFeedLink = link;
                        mFeedDescription = description;
                    }
                    title = null;
                    link = null;
                    description = null;
                    isItem = false;
                }
            }
            return items;
        } finally {
            inputStream.close();
        }
    }
    private class FetchFeedTask extends AsyncTask<Void, Void, Boolean> {
        private String urlLink;
        @Override
        protected void onPreExecute() {
            mSwipeLayout.setRefreshing(true);
            mFeedTitle = null;
            mFeedLink = null;
            mFeedDescription = null;
            mFeedTitleTextView.setText("Feed Title: " + mFeedTitle);
            mFeedDescriptionTextView.setText("Feed Description: " + mFeedDescription);
            mFeedLinkTextView.setText("Feed Link: " + mFeedLink);
            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) {
                Log.e(TAG, "Error", e);
            } catch (XmlPullParserException e) {
                Log.e(TAG, "Error", e);
            }
            return false;
        }
        @Override
        protected void onPostExecute(Boolean success) {
            mSwipeLayout.setRefreshing(false);
            if (success) {
                mFeedTitleTextView.setText("Feed Title: " + mFeedTitle);
                mFeedDescriptionTextView.setText("Feed Description: " + mFeedDescription);
                mFeedLinkTextView.setText("Feed Link: " + mFeedLink);
                // Fill RecyclerView
                mRecyclerView.setAdapter(new RssFeedListAdapter(mFeedModelList));
            } else {
                Toast.makeText(MainActivity.this,
                        "Enter a valid Rss feed url",
                        Toast.LENGTH_LONG).show();
            }
        }
    }
}

توضیح کد بالا از حوصله این بخش خارج است اما ما بخش هایی رو که مهم است به طور سریع به آنها اشاره می کنیم ما ابتدا یک کلاس ایجاد کردیم تا rss را parse کند یعنی اطلاعات رو به صورت درست برای نمایش دهد و کلی از بخش ها بررسی می شود به طور مثال اگر کاربر سایت رو بدون http یا https وارد کند ما خودمان به آن http اضافه می کنیم دوست عزیزمون اشاره کرده بود فقط یک مقدار دریافت شود به همین علت در ابتدا برای سه تا TextView داریم که اطلاعات اولی در آن قرار گرفته می شود سپس بقیه آن در recycler view قرار می گیرد برای sync بودن برنامه هم ما از Async task استفاده کردیم. این آموزش به درخواست یکی از دوستان ایجاد شده است و استفاده از این آموزش نیاز به پیش نیازی هایی دارد.
و در اخر شما باید دسترسی استفاده از اینترنت را در AndroidManifest.xml قرار دهید.
 

<uses-permission android:name="android.permission.INTERNET" />

 
این آموزش هم به پایان رسید.
 
موفق باشید.

مطالعه بیشتر