در این قسمت که در ادامه قسمت قبل است یک سرویس API پیچیده با قابلیتهای مهم را به شما آموزش میدهیم.
در قسمت قبل API ما بسیار ساده بود و تنها یک آرایه را از سمت سرور دریافت و به نمایش در میآورد. در این قسمت قصد داریم تعداد زیادی پست را به همراه عکس دریافت و به کاربر نمایش دهیم.
برای نمایش حجم زیادی پست از RecyclerView استفاده میکنیم. اگر با این مبحث آشنا نیستید، آموزش RecyclerView را حتما ببینید.
پیش از شروع کار به Data Model پست مراجعه کرده و آیتم image را از نوع string اضافه کرده و getter و setter آن را ایجاد میکنیم:
private String image;
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
کلاس APIGettingPosts را به شرح زیر ویرایش میکنیم:
public class APIGettingPosts {
private Context context;
public APIGettingPosts(Context context){
this.context= context;
}
public void getPost(final OnPostsReceived onPostsReceived){
JsonArrayRequest request = new JsonArrayRequest(
Request.Method.GET,
"http://192.168.8.8/index.php",
null,
new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
onPostsReceived.onReceived(ParsingPostJSON(response));
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.i("Get_Error: ", error.toString());
onPostsReceived.onReceived(null);
}
});
request.setRetryPolicy(new DefaultRetryPolicy(8000, 1, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
RequestQueue requestQueue= Volley.newRequestQueue(context);
requestQueue.add(request);
}
private List<Posts> ParsingPostJSON(JSONArray response){
try {
List<Posts> posts = new ArrayList<>();
for (int i=0; i < response.length(); i++){
JSONArray postArray= response.getJSONArray(i);
Posts post= new Posts();
post.setTitle(postArray.getString(0));
post.setIntro(postArray.getString(1));
post.setDate(postArray.getString(2));
post.setImage(postArray.getString(3));
posts.add(post);
}
return posts;
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
public interface OnPostsReceived{
void onReceived(List<Posts> posts);
}
}
سطر 32: خروجی متد ParsingPostJSON را List<Posts>
تعیین کردیم تا لیستی از جنس Posts داشته باشیم.
سطر 34 تا 43: یک متغیر از نوع List ایجاد کردیم که در آن Postsها قرار میگیرند.
سطر 44: در این سطر ما لیست آماده شده را return میکنیم.
سطر 53: متد Interface قبلی را به این شکل ویرایش میکنیم تا List در خروجی داشته باشد.
سورس PHP ارسال کننده پستها را مانند زیر ویرایش میکنیم:
<?php
$array= array(
array(
"عنوان اولین مطلب",
"متن خلاصه اولین مطلب را در اینجا میبینید.",
"2015-12-5",
"https://hitos.ir/api/1.jpg"),
array(
"عنوان دومین مطلب",
"متن خلاصه دومین مطلب را در اینجا میبینید.",
"2015-12-5",
"https://hitos.ir/api/2.jpg"),
array(
"عنوان سومین مطلب",
"متن خلاصه سومین مطلب را در اینجا میبینید.",
"2015-12-5",
"https://hitos.ir/api/3.jpg"),
array(
"عنوان چهارمین مطلب",
"متن خلاصه چهارمین مطلب را در اینجا میبینید.",
"2015-12-5",
"https://hitos.ir/api/4.jpg"),
array(
"عنوان پنجمین مطلب",
"متن خلاصه پنجمین مطلب را در اینجا میبینید.",
"2015-12-5",
"https://hitos.ir/api/5.jpg")
);
echo json_encode($array);
?>
در سورس بالا آرایهای از پنج پست را ایجاد کردیم که عنصر صفرم عنوان، عنصر یکم خلاصه، عنصر دوم تاریخ و عنصر سوم لینک عکس مطلب است. اگر با PHP آشنا نیستید حتما از آموزش روان و ساده PHP استفاده کنید.
نکته: تصاویر موجود در آرایهها در یک هاست آپلود شده اند و ما اینجا آدرس آنها را قرار دادیم.
لایه نمایش recycler_view_item را مانند زیر ایجاد میکنیم:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="200dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/title"
android:textColor="#000"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/intro"
android:textColor="#000"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right" />
</LinearLayout>
در بالا یک ImageView برای نمایش تصویر پست و سه TextView برای نمایش عنوان، خلاصه و تاریخ ایجاد کردیم.
پیش از ادامه مبحث باید مقدمهای در مورد دریافت تصاویر از سمت سرور ارائه کنیم. طبیعتا کار معقولی نیست که برنامه در هر بار وصل شدن به سرور تمام تصاویر را دانلود کند چون این فرایند بسیار زمان بر و خسته کننده است.
یک سری کتابخانه وجود دارد که مسئولیت مدیریت کردن فعالیتی نظیر دریافت تصاویر، کش کردن آنها و مدیریت حافظه را بر عهده میگیرند.
کتابخانه Picasso یکی از بهترین گزینهها برای این کار میباشد. برای اضافه کردن این کتابخانه از مسیر File و Project Structure و مسیر app و Dependencies و کلیک کردن روی علامت مثبت و انتخاب Library dependency به جستجوگر کتابخانهها بروید. در باکس جستجو عبارت picasso را تایپ و از لیست کتابخانه com.squareup.picasso:picasso
را انتخاب کنید.
کلاس PostsAdapter که وظیفه آماده سازی RecyclerView دارد را مانند زیر پیاده سازی میکنیم:
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostsViewHolder>{
private Context context;
private List<Posts> posts;
public PostsAdapter(Context context, List<Posts> posts){
this.context = context;
this.posts = posts;
}
@Override
public PostsViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View view= LayoutInflater.from(context).inflate(R.layout.recycler_view_item, parent, false);
return new PostsViewHolder(view);
}
@Override
public void onBindViewHolder(PostsViewHolder holder, int position) {
Posts post= posts.get(position);
holder.title.setText(post.getTitle());
holder.intro.setText(post.getIntro());
holder.date.setText(post.getDate());
Picasso.with(context).load(post.getImage()).into(holder.image);
}
@Override
public int getItemCount() {
return posts.size();
}
public class PostsViewHolder extends RecyclerView.ViewHolder{
private TextView title;
private TextView intro;
private TextView date;
private ImageView image;
public PostsViewHolder(View itemView) {
super(itemView);
title= (TextView)itemView.findViewById(R.id.title);
intro= (TextView)itemView.findViewById(R.id.intro);
date= (TextView)itemView.findViewById(R.id.date);
image= (ImageView)itemView.findViewById(R.id.image);
}
}
}
سطرهای 19 تا 23: در این سطرها تمام آیتمهای دریافت شده از کلاس APIGettingPosts را به لایه نمایش نسبت میدهیم.
سطر 23: در این سطر از کلاس Picasso استفاده کردیم. متد with برای تعیین context، متد load برای دریافت تصویر و متد into مکان قرار گیری عکس را در لایه نمایش مشخص میکند. طبیعتا در سطرهای 35 و 42 نیز بخش نمایش تصویر لایه نمایش را مشخص کرده ایم.
لایه نمایش activity_main را مانند زیر ویرایش کرده و به آن یک RecyclerView اضافه میکنیم:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
متد onCreate کلاس MainActivity را مانند زیر ویرایش میکنیم:
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
APIGettingPosts apiGettingPosts = new APIGettingPosts(this);
apiGettingPosts.getPost(new APIGettingPosts.OnPostsReceived() {
@Override
public void onReceived(List<Posts> posts) {
if(posts == null || posts.isEmpty()){
Toast.makeText(MainActivity.this, "خطا در دریافت اطلاعات", Toast.LENGTH_SHORT).show();
}else {
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);
PostsAdapter postsAdapter = new PostsAdapter(MainActivity.this, posts);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(postsAdapter);
}
}
});
}
سطر 5: یک شی از کلاس APIGettingPosts ایجاد کردیم.
سطرهای 6 تا 18: در این بخش با استفاده از متد getPosts قصد داریم RecyclerView را مقدار دهی و اجرا کنیم.
سطر 9: در این جا ابتدا بررسی میشود که posts برابر null نباشد. چون در کلاس APIGettingPosts گفتیم در صورتی که خطایی در ارتباط با سرور صورت پذیرد باید مقدار null ارسال شود. مقدار بعدیای که در این بخش بررسی میکنیم empty بودن posts هاست، که خالی بودن postsها نیز نشان دهنده پیش آمدن یک مشکل است.
سطرهای 12 تا 15: RecyclerView موجود در لایه نمایش را مقدار دهی میکنیم. در سطر 13 ورودی PostAdapter را context و postsها قرار دادیم.
در صورت درست پیاده سازی کردن کلاسها و لایههای نمایش فوق باید برنامه به شکل زیر اجرا شود:
این آموزش بسیار مهم است و پیشنهاد میشود این آموزش را چندین بار مرور کنید.