بایگانی دسته‌ی: آموزش اندروید

در این قسمت از وب سایت آکادمی برنامه نویسان مشهد به آموزش اندروید و مطالب مرتبط با آن پرداخته می شود

آموزش نحوه تبدیل گفتار به متن در اندروید

تبدیل گفتار به متن در اندروید مانند بسیاری از قابلیت ها و سرویس های اندروید هنوز برای بسیاری از توسعه دهندگان اپلیکیشن های اندروید به صورت دست نخورده باقی مانده است. به عنوان مثال، قابلیت یکپارچه سازی سرویس های تشخیص گفتار به متن در Android SDK یکی از نمونه های بارز در این مورد می باشد. در حال حاضر برنامه های شناخته شده ای ماند Google Keep و any.DO از قابلیت تبدیل گفتار به متن استفاده نموده و سردمداران برنامه نویسی از قبیل مایکروسافت و اپل و گوگل بر روی این قابلیت تمرکز زیادی داشته و نرم افزارهای قدرتمندی از قبیل Google cortana و Siri را توسعه داده اند. آکادمی برنامه نویسان؛ برگزارکننده دوره های آموزش اندروید در مشهد در ادامه به بررسی این موضوع پرداخته و نحوه تبدیل گفتار به متن در برنامه های اندروید را آموزش خواهد داد.

اندروید به صورت ذاتی قابلیت تبدیل گفتار به متن را فراهم نموده و در دسترس قرار داده است، پس چرا از آن استفاده نکنیم؟ در این مطلب ما به شما خواهیم گفت که چگونه Android’s speech و Text API را با هم تلفیق کنید تا از این قابلیت عالی و فوق العاده در برنامه هایتان استفاده نمایید. در حالت کلی، عمل تشخیص صدا و گفتا ر و تبدیل آن به متن به کمک Recognizerintent انجام میشود که استفاده از آن به صورت آفلاین بوده و نیازی به اینترنت در آن وجود ندارد.

پیش نیازها برای تبدیل گفتار به متن در اندروید

برای شروع این کار لازم است به چند موضوع مهم توجه داشته باشید که عبارتند از:

  • ابتدا لازم است Android Studio IDE روی رایانه شخصی یا MAC نصب شده باشد.
  • این برنامه از شبیه ساز پشتیبانی نمی کند . به همین دلیل لازم است گوشی هوشمند یا تبلت اندرویدی در اختیار داشته باشید.
  • دانش ابتدایی درباره لایه بندی برنامه های اندرویدی و چرخه حیات برنامه ها داشته باشید. شناخت و آشنایی ابتدایی با برنامه نویسی اندروید برای استفاده از قابلیت تبدیل گفتار به متن در اندروید و پیاده سازی آن در اپلیکیشن های مورد نظر ضروری می باشد.

پروژه جدید ایجاد کنید

  • Android Studio را باز کنید و یک پروژه جدید به نام speech to text  و دامنه شرکت خود ایجاد کنید. به عنوان مثال، example.com
  • Next را کلیک نموده و Min SDK را انتخاب نمایید. سایر گزینه ها به صورت پیش فرض قرار دهید و نیاز به تغییر در آنها نیست. سپس NEXT را کلیک نموده و Blanck Activity را انتخاب کنید.
  • برای activity مورد نظر نام MainActivity را انتخاب و سپس next را کلیک کنید.
  • سایر موارد را به صورت پیش فرض قرار دهید و نیاز به تغییرات در آنها نیست و در نهایت روی finish کلیک کنید.

تنظیمات activity layout

ابتدا یک imagebutton برای نشان دادن میکروفون و یک textview برای نمایش گفتار تبدیل شده به متن به آن اضافه کنید. برای پشتیبانی از متن های طولانی تر و پیاده سازی قابلیت اسکرول کردن در textview به صورت زیر عمل کنید.

<?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"
    tools:context="com.stacktips.speechtotext.MainActivity">

    <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_above="@+id/btnSpeakContainer"
        android:layout_alignParentTop="true"
        android:layout_marginBottom="20dp"
        android:padding="20dp">

        <TextView
            android:id="@+id/voiceInput"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </ScrollView>

    <LinearLayout
        android:id="@+id/btnSpeakContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#f5f5f5"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:padding="20dp">

        <ImageButton
            android:id="@+id/btnSpeak"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@null"
            android:padding="16dp"
            android:scaleType="fitCenter"
            android:src="@mipmap/ic_microphone_2" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/btnSpeak"
            android:layout_margin="10dp"
            android:text="@string/hint" />
    </LinearLayout>
</RelativeLayout>

پیاده سازی برنامه تشخیص صدا و تبدیل به متن در android activity

قطعه کد زیر را در mainactivity.java اضافه کنید.

package com.stacktips.speechtotext;

import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    private static final int REQ_CODE_SPEECH_INPUT = 100;
    private TextView mVoiceInputTv;
    private ImageButton mSpeakBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mVoiceInputTv = (TextView) findViewById(R.id.voiceInput);
        mSpeakBtn = (ImageButton) findViewById(R.id.btnSpeak);
        mSpeakBtn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                startVoiceInput();
            }
        });
    }

    private void startVoiceInput() {
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Hello, How can I help you?");
        try {
            startActivityForResult(intent, REQ_CODE_SPEECH_INPUT);
        } catch (ActivityNotFoundException a) {

        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            case REQ_CODE_SPEECH_INPUT: {
                if (resultCode == RESULT_OK && null != data) {
                    ArrayList<String> result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
                    mVoiceInputTv.setText(result.get(0));
                }
                break;
            }

        }
    }
}

در قطعه کد بالا، ما یک Intent به نام recognizerintent راه اندازی کردیم که به عنوان ورودی گفتار را دریافت نموده و آن را به speech recognizer ارسال میکند. این کار از طریق ACTION_RECOGNIZE_SPEECH انجام میشود. در نهایت با کد REQ_CODE_SPEECH_INPUT متن مربوطه به خروجی ارسال و در کادر مربوطه نمایش داده میشود.

پس از اتمام این کار، برنامه را اجراکنید. با ضربه زدن روی دکمه mic وارد صفحه دوم میشوید که از شما میخواهد تا صحبت کنید. در نهایت گفتار خود را به صورت متن در کادر مشاهده خواهید کرد.

تبدیل گفتار به متن اندروید

طراحی رابط کاربری اندروید با LinearLayout

برنامه نویس اندروید

در سری آموزش برنامه نویسی اندروید هر بار با مطالب بسیار جذاب و کاربردی ساخت اپلیکیشن های اندرویدی  آشنا خواهید شد . در این فیلم آموزشی مهندس محمد آذرنیوا قصد دارد شما را با نحوه طراحی رابط کاربری اندروید با استفاده از LinearLayout آشنا کند. همچنین می توانید  آموزش linearlayout در اندروید استودیو از لینک پایین دانلود کنید

 

 

1 Star2 Stars3 Stars4 Stars5 Stars (3رای, میانگین: 4,33 از 5 )
Loading...

گزارش دوره آموزش اندروید – جلسه هشتم

گزارش دوره اندروید

جسله هشتم از دوره آموزش اندروید برگزار گردید . مهندس آذرنیوا مدرس دوره به معرفی Intent ها پرداخته و نحوه ارتباط فعالیت ها  با استفاده از Intent ها  را آموزش دادند .

گزارش دوره اندروید

معرفی Intent ها و نحوه استفاده از  آنها در اپلیکیشن در این جلسه  دوره آموزش اندروید  بررسی گردید.

Intent اندروید

همچنین ساخت اپلیکیشن پیام رسان جهت درک بهتر Intent پیاده سازی شد.

آموزش اندروید

 

کلاس های برنامه نویسی در مشهد  آکادمی برنامه نویسان شامل دوره های متخصص PHP  و دوره متخصص اندروید می شود

طراحی رابط کاربری اندروید با Relative Layout

آموزش اندروید

در سری آموزش های « برنامه نویسی اندروید  » هر بار با مطالب بسیار جذاب و کاربردی ساخت اپلیکیشن های اندرویدی  آشنا خواهید شد . در این ویدئو مهندس محمد آذرنیوا قصد دارد شما را با نحوه طراحی رابط کاربری اندروید با استفاده از Relative Layout آشنا کند.

 

 

آموزش Data Binding در اندروید

دوره آموزش اندروید

Data Binding راهی برای اتصال رابط کاربری (UI) با داده ها فراهم می کند و اجازه می دهد که رابط کاربر بطور خودکار بروز شود. همچنین کار ما را راحت کرده و به ما اجازه میدهد که متغیرها و توابع را مستقیما در لایوت مقدار دهی کنیم و مقادیر به view های مربوطه متصل شوند ودیگه نیازی به استفاده از کد تکراری      ()findViewById  درسمت جاوا نیست.که اینکار حجم کد را کمتر و سرعت را بالاتر میبرد. با آموزش Data Binding در اندروید در کلاس های برنامه نویسی در مشهد همراه ما باشید.

Data Bindingیکی از اجزای معماری اندروید است که توسط اندروید پیشنهاد شده است.

فهرست مطالب آموزش Data Binding در اندروید

۱-فعال کردن Data Binding

۲-مثال اصلی آموزش Data Binding در اندروید

۳- کلاسهای Data Binding ایجاد نشده اند

Data Binding -4 در Layout

۵-اتصال Event Handling/ Click Listeners

۶-بروزرسانی رابط کاربر با استفاده از observables

۷-بروزرسانی رابط کاربر با استفاده از observablefield

۸-بارگذاری تصاویر از URL

۹-متصل کردن توابع جاوا

  1- -فعال کردن Data Binding

برای شروع کار ابتدا باید این ویژگی را در پروژه فعال کرد.فایل build.gradle را باز کنید وبا این چند خط کد Data Binding را فعال کنید.پس از فعالسازی پروژه را sync (همگامسازی) کنید.

android {
    dataBinding {
        enabled = true
    }
 
    compileSdkVersion 27
 
    defaultConfig {
        applicationId "info.androidhive.databinding"
        minSdkVersion 16
        // ..
    }
}
 

ما میخواهیم اطلاعات کاربر را از یک کلاس POJO   User نمایش دهیم . در زیر کلاس POJO یک شی ء User با نام و ایمیل را ایجاد میکند.  به منظور اتصال داده  UI به کلاس  model می توان از هر آبجکت ساده ی جاوا استفاده کرد که در اصطلاح POJO میگویند  .

آموزش Data Binding در اندروید

public class User {
    String name;
    String email;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
}

برای فعال کردن Data Binding در یک Layout باید با تگ <layout> به عنوان عنصر ریشه(root)شروع کرد .همراه با آن تگ های <data> و <variable> استفاده میشود.

در زیر ساختاری از لایوت data-binding وجود دارد.

<layout ...>
 
    <data>
         
        <variable
            name="..."
            type="..." />
    </data>
 
    <LinearLayout ...>
       <!-- YOUR LAYOUT HERE -->
    </LinearLayout>
</layout>
  • داخل تگ <layout> کدهای معمول لایوت قرار میگیرد.
  • تگ <data> در زیر تگ<layout> قرار دارد.همه متدها و متغیرهای اتصال باید در تگ <data > وارد شوند.
  • داخل تگ <data> , یک متغیر با تگ <variable> تعریف میشود. تگ <variable> دو خصوصیت name و type دارد.خصوصیت name نام مستعار هست و خصوصیت type باید از کلاس مدل باشد.در اینجا مسیر ما کلاس User می باشد.
  • برای اتصال یک مقدار , از دستور نگارشی{ }@ استفاده میشود. در لایوت زیر name و email کاربربا استفاده از {user.name} @و  {user.email}@به TextView  متصل شده اند.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
 
    <data>
         
        <variable
            name="user"
            type="info.androidhive.databinding.User" />
    </data>
 
    <LinearLayout 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"
        android:orientation="vertical"
        android:padding="@dimen/fab_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".MainActivity"
        tools:showIn="@layout/activity_main">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.email}" />
 
    </LinearLayout>
</layout>
  • وقتی که اتصال داده ها در فایل لایوت انجام شد به منوی Build رفته و پروژه را   Clean Project وRebuild Project میکنیم. اینکار کلاسهای ضروری اتصال را ایجاد میکند.
  • نامگذاری کلاسهای اتصال تولید شده براساس نام فایل لایوت اتصال فعال شده, میباشد .برای لایوت activity_main.xml نام کلاس اتصال تولید شده ActivityMainBinding خواهد بود(پسوند Binding در آخر اضافه میشود)
  • برای متصل کردن داده در رابط کاربر (UI) باید ابتدا لایوت اتصال را با استفاده از کلاسهای اتصال تولید شده  پر کنید. در زیر ابتدا لایوت ActivityMainBinding   را پر کرده و بعد ()binding.setUser لایوت را به شیء User متصل میکند.

میتوانید ملاحظه کنید که از ()findViewByIdدر جایی استفاده نکردیم.

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
 
import info.androidhive.databinding.databinding.ActivityMainBinding;
 
public class MainActivity extends AppCompatActivity {
 
    private User user;
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // setContentView(R.layout.activity_main);
 
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
 
        user = new User();
        user.setName("Ravi Tamada");
        user.setEmail("ravi@androidhive.info");
 
        binding.setUser(user);
    }
}

اگر این برنامه را اجرا کنید میتوانید جزيیات نمایش داده شده کاربر را در TextViews ببینید.

Data Binding در اندروید

۳-کلاسهای DataBinding تولید نشده اند

نسخه رایج اندروید استودیو در اکثر اوقات موفق به تولید کلاسهای اتصال نمیشود. معمولا این مساله را میتوان با , Cleaning & Rebuilding کردن پروژه حل کرد.اگر مشکل همچنان ادامه داشت , به مسیر File Invalidate Caches & Restart برویدو پروژه را دوباره راه اندازی کنید.اگر فایلهای لایوت شما خطایی نداشته باشند احتمالا این مشکل حل شود.

۴- DataBinding در لایوتهای *<include>

* چیدمانی که قرار است چند جا تکرار شود را در یک فایل می نویسند و در جاهای مختلف آن را include می کنند.

 ما  CoordinatorLayout و  AppBarLayout  و سایر عناصر را در مثال بالا نداریم . معمولا  لایوت اصلی(main)  و  محتوا (content)را در دو لایوت مختلف activity_main.xml  و content_main.xml جدا میکنیم .content_main  در لایوت اصلی (main) با استفاده از تگ <include> وجود خواهد داشت . حالا خواهیم دید که چطور وقتی که لایوتها را include میکنیم امکان اتصال داده را فراهم کنیم .

در زیر activity_main.xml با CoordinatorLayout, AppBarLayout و FAB را داریم.

  • تگ <layout> در لایوت activity_main.xml برای فعال کردن اتصال داده استفاده میشود.همچنین تگهای <data> <variable برای اتصال شی ء User استفاده میشوند.
  • برای انتقال user به لایوت content_main در تگ <include> خط کد bind:user=”@{user}” را مینویسیم.بدون این خط کد شیء user در لایوت content_main در دسترس نخواهد بود.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">
 
    <data>
 
        <variable
            name="user"
            type="info.androidhive.databinding.User" />
    </data>
 
    <android.support.design.widget.CoordinatorLayout 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=".MainActivity">
 
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">
 
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />
 
        </android.support.design.widget.AppBarLayout>
 
        <include
            android:id="@+id/content"
            layout="@layout/content_main"
            bind:user="@{user}" />
 
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            app:srcCompat="@android:drawable/ic_dialog_email" />
 
    </android.support.design.widget.CoordinatorLayout>
</layout>
  • content_main.xml تگ <layout> را برای فعال کردن اتصال داده دوباره include میکند. تگهای <layout>, <data> , <variable> برای هردو parent و لایوتهای include شده ضروری هستند.
  • ویژگیهای android:text=”@{user.name}” , android:text=”@{user.email}” برای نمایش داده ها در TextViewها استفاده میشوند.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
 
    <data>
 
        <variable
            name="user"
            type="info.androidhive.databinding.User" />
    </data>
 
    <LinearLayout 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"
        android:orientation="vertical"
        android:padding="@dimen/fab_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".MainActivity"
        tools:showIn="@layout/activity_main">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.email}" />
 
    </LinearLayout>
</layout>
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
 
        setSupportActionBar(binding.toolbar);
 
        User user = new User();
        user.setName("Ravi Tamada");
        user.setEmail("ravi@androidhive.info");
 
        binding.setUser(user);
    }
}

اگر برنامه را اجرا کنید میتوانید داده های نمایش داده شده  را ببینید.

Data Binding

۵اتصال Event Handling/ Click Listeners

نه تنها داده ها ما میتوانیم click  و سایر رویدادها را روی عناصر UI متصل کنیم.

برای اتصال رویداد کلیک ما احتیاج داریم که یک کلاس با متدهای مورد نیاز callback ایجاد کنیم.

در زیر ما یک کلاس داریم که رویداد کلیک FAB* را مدیریت میکند.

* دکمه شناور FAB یا همان Floating Action Button کار این دکمه این هست ، که همیشه دم دسته یعنی حتی اگه یک لیست طولانی در لایوت داشته باشیم و اسکرول کنیم دکمه همیشه ثابت سرجای خودش هست.

public class MyClickHandlers {
 
        public void onFabClicked(View view) {
            Toast.makeText(getApplicationContext(), "FAB clicked!", Toast.LENGTH_SHORT).show();
        }
}

برای اتصال به این رویداد ما دوباره از تگ  <variable>استفاده میکنیم با مسیری برای هندلر کردن کلاس.

در پایین این خط کد android:onClick=”@{handlers::onFabClicked}” کلیک FAB را به متد ()onFabClickedمتصل میکند.

<layout xmlns:bind="http://schemas.android.com/apk/res/android">
 
    <data>
 
        <variable
            name="handlers"
            type="info.androidhive.databinding.MainActivity.MyClickHandlers" />
    </data>
 
    <android.support.design.widget.CoordinatorLayout ...>
 
        <android.support.design.widget.FloatingActionButton
            ...
            android:onClick="@{handlers::onFabClicked}" />
 
    </android.support.design.widget.CoordinatorLayout>
</layout>
  • برای اختصاص دادن رویداد فشار طولانی (long press) ,متد باید نوع Boolean را بجای فضای خالی (void) برگرداند .

()public boolean onButtonLongPressed

  • همچنین میتوانید پارامترهارا در زمان اتصال انتقال بدهید.

public void onButtonClickWithParam(View view, User user) .اتصال شیء, user رااز لایوت UI دریافت می کند.در لایوت پارامترها میتوانند منتقل شوند با استفاده از :

android:onClick=”@{(v) -> handlers.onButtonClickWithParam(v, user)}”

  • برای اتصال رویدادها ,binding.setHandlers(handlers) از اکتیویتی فراخوانده میشود.
package info.androidhive.databinding;
 
import android.content.Context;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
 
import info.androidhive.databinding.databinding.ActivityMainBinding;
 
public class MainActivity extends AppCompatActivity {
 
    private User user;
    private MyClickHandlers handlers;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
 
        setSupportActionBar(binding.toolbar);
 
        user = new User();
        user.setName("Ravi Tamada");
        user.setEmail("ravi@androidhive.info");
 
        binding.setUser(user);
 
        handlers = new MyClickHandlers(this);
        binding.content.setHandlers(handlers);
    }
 
    public class MyClickHandlers {
 
        Context context;
 
        public MyClickHandlers(Context context) {
            this.context = context;
        }
 
        public void onFabClicked(View view) {
            Toast.makeText(getApplicationContext(), "FAB clicked!", Toast.LENGTH_SHORT).show();
        }
 
        public void onButtonClick(View view) {
            Toast.makeText(getApplicationContext(), "Button clicked!", Toast.LENGTH_SHORT).show();
        }
 
        public void onButtonClickWithParam(View view, User user) {
            Toast.makeText(getApplicationContext(), "Button clicked! Name: " + user.name, Toast.LENGTH_SHORT).show();
        }
 
        public boolean onButtonLongPressed(View view) {
            Toast.makeText(getApplicationContext(), "Button long pressed!", Toast.LENGTH_SHORT).show();
            return false;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">
 
    <data>
 
        <variable
            name="user"
            type="info.androidhive.databinding.User" />
 
        <variable
            name="handlers"
            type="info.androidhive.databinding.MainActivity.MyClickHandlers" />
    </data>
 
    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        ...>
 
        <android.support.design.widget.AppBarLayout
            ...>
 
        </android.support.design.widget.AppBarLayout>
 
        <include
            android:id="@+id/content"
            layout="@layout/content_main"
            bind:user="@{user}" />
 
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            android:onClick="@{handlers::onFabClicked}"
            app:srcCompat="@android:drawable/ic_dialog_email" />
 
    </android.support.design.widget.CoordinatorLayout>
</layout>
 
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
 
    <data>
 
        <variable
            name="user"
            type="info.androidhive.databinding.User" />
 
        <variable
            name="handlers"
            type="info.androidhive.databinding.MainActivity.MyClickHandlers" />
    </data>
 
    <LinearLayout 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"
        android:orientation="vertical"
        android:padding="@dimen/fab_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".MainActivity"
        tools:showIn="@layout/activity_main">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.email}" />
 
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{handlers::onButtonClick}"
            android:text="CLICK" />
 
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{(v) -> handlers.onButtonClickWithParam(v, user)}"
            android:text="CLICK WITH PARAM" />
 
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="LONG PRESS"
            android:onLongClick="@{handlers::onButtonLongPressed}" />
 
    </LinearLayout>
</layout>
 

آموزش اندروید

۶-بروز رسانی UI با استفاده از Observables

بدون آنکه متد setter() بطور واضح فراخوانی شود Observables  راهی را برای همگامسازی خودکار UI با داده فراهم میکند .وقتی مقداری از یک ویژگی (property) در یک شیء تغییر کند ,  UIبروز خواهد شد.برای ساختن شیء observable کلاس را گسترش میدهیم (extend) از BaseObservable.

  • برای ساختن یک ویژگی observable از علامت Bindable @روی متد() getter استفاده میکنیم.
  • وقتی که داده ها تغییر میکنند در متد() setter برای بروزکردن رابط کاربر, notifyPropertyChanged(BR.property) فراخوانی میشود
  • وقتی که Data Binding فعال باشد کلاس BR  بطور خودکار تولید خواهد شد.

در پایین کلاس اصلاح شده User گسترش یافته (extend ) از BaseObservable.شما میتوانید متوجه شوید که در اینجاnotifyPropertyChanged بعد از نسبت دادن مقادیر جدید فراخوانی شده.

package info.androidhive.databinding;
 
import android.databinding.BaseObservable;
import android.databinding.Bindable;
 
public class User extends BaseObservable {
    String name;
    String email;
 
    @Bindable
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
 
    @Bindable
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
        notifyPropertyChanged(BR.email);
    }
}

برای تست این , ما اطلاعات کاربر را درon FAB click تغییر دادیم .شما میتوانید ببینید که UI  در on FAB click به درستی بروز شد.

public class MyClickHandlers {
 
    Context context;
 
    public MyClickHandlers(Context context) {
        this.context = context;
    }
 
    public void onFabClicked(View view) {
        user.setName("Ravi");
        user.setEmail("ravi8x@gmail.com");
    }
}

۷-بروزرسانی UI با استفاده از ObservableFields

اگر کلاس شیء شما دارای ویژگیهای کمتری برای بروزرسانی باشد یا اگر شما نمیخواهید که هر فیلد را در شیء مشاهده کنید.میتوانید از ObservableFields برای بروزرسانی رابط کاربر استفاده کنید .شما میتوانید متغیر(variable) را به عنوان ObservableFields اعلام کنید و زمانیکه داده های جدید تنظیم(set) شود رابط کاربر بروز رسانی خواهد شد.

همان کلاس User را میتوان بصورت زیر با استفاده از ObservableFields اصلاح کرد.

package info.androidhive.databinding;
 
import android.databinding.ObservableField;
 
public class User {
    public static ObservableField<String> name = new ObservableField<>();
    public static ObservableField<String> email = new ObservableField<>();
 
    public ObservableField<String> getName() {
        return name;
    }
 
    public ObservableField<String> getEmail() {
        return email;
    }
}

برای بروزرسانی مقادیر باید بجای استفاده از متد() setter مقدار جدید را مستقیما به ویژگی تخصیص داد.

public class MyClickHandlers {
 
    Context context;
 
    public MyClickHandlers(Context context) {
        this.context = context;
    }
 
    public void onFabClicked(View view) {
        user.name.set("Ravi");
        user.email.set("ravi8x@gmail.com");
    }
}

۸-بارگیری تصاویر از (Glide or Picasso)URL

میتوانید یک ImageView را برای بارگیری تصویر به یکURL  متصل کنید.برای اتصال شما میتوانید از علامتBindingAdapter @برای ویژگی(property) شیء استفاده کنید.

در زیر متغیر profileImage به مشخصه android:profileImage متصل شده است .تصویر با استفاده از کتابخانه Glide یا Picasso بارگذاری خواهد شد .

package info.androidhive.databinding;
 
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.databinding.BindingAdapter;
import android.widget.ImageView;
 
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
 
public class User {
    //..
    String profileImage;
 
    public String getProfileImage() {
        return profileImage;
    }
 
    public void setProfileImage(String profileImage) {
        this.profileImage = profileImage;
    }
 
    @BindingAdapter({"android:profileImage"})
    public static void loadImage(ImageView view, String imageUrl) {
        Glide.with(view.getContext())
                .load(imageUrl)
                .into(view);
 
        // If you consider Picasso, follow the below
        // Picasso.with(view.getContext()).load(imageUrl).placeholder(R.drawable.placeholder).into(view);
    }
}

برای بارگذاری تصویر داخل ImageView مشخصه android:profileImage=”@{user.profileImage}” را اضافه کنید.

<ImageView
     android:layout_width="100dp"
     android:layout_height="100dp"
     android:layout_marginTop="@dimen/fab_margin"
     android:profileImage="@{user.profileImage}" />

دقت کنید که اجازه اینترنت را در فایل manifest اضافه کرده باشید.

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

اندروید

۹-اتصال توابع جاوا

میتوانید توابع جاوا را به عناصر UI متصل کنید .حالا شما میخواهید قبل از نمایش UI عملیاتی را روی مقادیر انجام دهید .براحتی شما میتوانید اینکار را با استفاده از تگ <import>انجام دهید.

در اینجا متدی داریم که رشته را به تمام حروف بزرگ تبدیل میکند.

public class BindingUtils {
    public static String capitalize(String text) {
        return text.toUpperCase();
    }
}

برای صدازدن این تابع در لایوت خود ابتدا کلاس را با استفاده از تگ <import> وارد کنید و تابع مربوط به ویژگی را فراخوانی کنید.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
 
    <data>
        <import type="info.androidhive.databinding.BindingUtils" />
    </data>
 
    <LinearLayout ...>
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{BindingUtils.capitalize(user.name)}" />
 
    </LinearLayout>
</layout>

با انجام موارد بالا در آموزش Data Binding در اندروید حالا ما یکسری اطلاعات پایه ای در مورد Data Binding داریم.

اضافه کردن فیلتر جستجو با RecyclerView در اندروید

آموزش اندروید مشهد

در مطالب قبل درباره ی Recyclerview صحبت کردیم و نکاتی را در مورد ترکیب آن با Cardview و اضافه کردن Swipe برای توابع حذف و غیره اشاره کردیم. در این مقاله قصد داریم نحوه ی اضافه کردن قابلیت فیلتر جستجو به recyclerview را آموزش دهیم. ساخت جستجو در اندروید کار بسیار ساده ای می باشد و از ویجت نوار ابزار جستجو برای ورود متن جستجو استفاده میشود. برای نشان دادن این موضوع، یک مثال با لیست تماس و جستجو بر اساس نام و شماره تماس را ارائه خواهیم داد.

 

 

()RecyclerView Search Filter – getFilter

اندروید کلاس های قابل فیلتر شدن برای فیلتر کردن داده ها با استفاده از یک فیلتر یا شرط را فراهم می کند. معمولا متد getfilter()  مجبور است در کلاس آداپتور که شرط فیلتر کردن برای جستجو در آن قرار دارد، اصطلاحا override شود. در قطعه کد زیر یک مثال از متد getfilter() نمایش  داده شده که یک مخاطب را بر اساس نام و شماره تماس از لیست مخاطبان جستجو می کند.

@Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                String charString = charSequence.toString();
                if (charString.isEmpty()) {
                    contactListFiltered = contactList;
                } else {
                    List<Contact> filteredList = new ArrayList<>();
                    for (Contact row : contactList) {

                        // name match condition. this might differ depending on your requirement
                        // here we are looking for name or phone number match
                        if (row.getName().toLowerCase().contains(charString.toLowerCase()) || row.getPhone().contains(charSequence)) {
                            filteredList.add(row);
                        }
                    }

                    contactListFiltered = filteredList;
                }

                FilterResults filterResults = new FilterResults();
                filterResults.values = contactListFiltered;
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
                contactListFiltered = (ArrayList<Contact>) filterResults.values;

                // refresh the list with filtered data
                notifyDataSetChanged();
            }
        };
    }

 

مثال json

برای این مثال ساخت جستجو در اندروید ، قصد داریم تا از json که لینک آن را قرار داده ایم، استفاده کنیم. این جیسون شامل لیستی از مخاطبین می باشد که هر مخاطب نام، شماره تلفن و تصویر پروفایل دارد.

https://acdev.ir/wp-content/uploads/2019/01/json/contacts.json

[{
        "name": "Tom Hardy",
        "image": "https://api.androidhive.info/json/images/tom_hardy.jpg",
        "phone": "(541) 754-3010"
    },
    {
        "name": "Johnny Depp",
        "image": "https://api.androidhive.info/json/images/johnny.jpg",
        "phone": "(452) 839-1210"
    }
]

 

ایجاد پروژه ی جدید

برای شروع کار باید یک پروژه در اندروید استودیو ایجاد کنید و ببینید که از جستجو خروجی مطلوب دریافت نمایید.

۱- یک پروژه ی جدید در اندروید استودیو از مسیر file/new project ایجاد و basic activity را از الگوها انتخاب کنید.

۲- build.gradle را در زیر فولدر app باز کنید و وابستگی های Recyclerview، Glide  و Volley را اضافه کنید.

build.gradle
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    // ...
 
    // recycler view
    implementation 'com.android.support:recyclerview-v7:26.1.0'
 
    // glide image library
    implementation 'com.github.bumptech.glide:glide:4.3.1'
 
    // volley http library
    implementation 'com.android.volley:volley:1.0.0'
    implementation 'com.google.code.gson:gson:2.6.2'
 
}

۳- منابع زیر را به فایل های string.xml، dimens.xml و colors.xml اضافه کنید.

strings.xml
<resources>
    <string name="app_name">RecyclerView Search</string>
    <string name="action_settings">Settings</string>
    <string name="toolbar_title">Contacts</string>
    <string name="action_search">Search</string>
    <string name="search_hint">Type name…</string>
</resources>
dimens.xml
<resources>
    <dimen name="fab_margin">16dp</dimen>
    <dimen name="activity_margin">16dp</dimen>
    <dimen name="thumbnail">40dp</dimen>
    <dimen name="row_padding">10dp</dimen>
    <dimen name="contact_name">15dp</dimen>
    <dimen name="contact_number">12dp</dimen>
</resources>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#111</color>
    <color name="colorPrimaryDark">#FFF</color>
    <color name="colorAccent">#ea3732</color>
    <color name="contact_name">#333333</color>
    <color name="contact_number">#8c8c8c</color>
</resources>

 

۴- فایل  res.zip را دانلود و آیکون های مورد نیاز برای جستجو که در این فولدر قرار دارند را به پروژه اضافه نمایید.

۵- یک کلاس به نام MyApplication.java را ایجاد و کلاس را از Application گسترش دهید. این کلاس در volley مقداردهی اولیه میشود.

MyApplication.java
package info.androidhive.recyclerviewsearch;
 
/**
 * Created by ravi on 16/11/17.
 */
 
import android.app.Application;
import android.text.TextUtils;
 
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
 
public class MyApplication extends Application {
 
    public static final String TAG = MyApplication.class
            .getSimpleName();
 
    private RequestQueue mRequestQueue;
 
    private static MyApplication mInstance;
 
    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }
 
    public static synchronized MyApplication getInstance() {
        return mInstance;
    }
 
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }
 
        return mRequestQueue;
    }
 
    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }
 
    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }
 
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

 

۶- AndroidManifest.xml را باز کنید و Myapplication را به تگ <application> اضافه کنید. علاوه براین، مجوز Intertnet را برای فراخوانی http اضافه نمایید.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.recyclerviewsearch">
 
    <uses-permission android:name="android.permission.INTERNET" />
 
    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
 
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

 

۷- در این مرحله نیاز داریم تا جیسون آنالیز خوانده شود و به یک کلاس pojo برای مرتب کردن جیسون نیاز داریم. یک کلاس به نام Contact.java ایجاد و name، image و phone number را اضافه کنید.

Contact.java
package info.androidhive.recyclerviewsearch;
 
/**
 * Created by ravi on 16/11/17.
 */
 
public class Contact {
    String name;
    String image;
    String phone;
 
    public Contact() {
    }
 
    public String getName() {
        return name;
    }
 
    public String getImage() {
        return image;
    }
 
    public String getPhone() {
        return phone;
    }
}

 

۸- یک کلاس به نام MyDividerItemDecoration.java را ایجاد کنید. این گام کاملا اختیاری بوده اما برای اضافه کردن حاشیه ی بیشتر به RecyclerView divider می باشد. این یک کلاس تقسیم کننده ی اختیاری برای اضافه کردن حاشیه ی چپ به خط تقسیم می باشد.

package info.androidhive.recyclerviewsearch;
 
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;
 
/**
 * Created by ravi on 17/11/17.
 */
 
public class MyDividerItemDecoration  extends RecyclerView.ItemDecoration {
 
    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };
 
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
 
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
 
    private Drawable mDivider;
    private int mOrientation;
    private Context context;
    private int margin;
 
    public MyDividerItemDecoration(Context context, int orientation, int margin) {
        this.context = context;
        this.margin = margin;
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }
 
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }
 
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }
 
    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
 
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left + dpToPx(margin), top, right, bottom);
            mDivider.draw(c);
        }
    }
 
    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();
 
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top + dpToPx(margin), right, bottom - dpToPx(margin));
            mDivider.draw(c);
        }
    }
 
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
 
    private int dpToPx(int dp) {
        Resources r = context.getResources();
        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
    }
}

 

نوشتن کلاس آداپتور با فیلتر

تا این مرحله منابع آماده هستند و باید کلاس آداپتور را بنویسید. نوشتن این کلاس، بخش اصلی کار در این مقاله می باشد، در نتیجه باید تمرکز بیشتری روی آن داشته باشید.

۹- یک لایه به نام user_row_item.xml ایجاد و لایه ی زیر را اضافه کنید. این لایه آیتم مخاطب در لیست را به عنوان خروجی ارائه می دهد. این لایه شامل دو textview برای ارائه ی name،       phone number و یک imageview برای نمایش تصویر پروفایل می باشد.

user_row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/selectableItemBackground"
    android:clickable="true"
    android:paddingBottom="@dimen/row_padding"
    android:paddingLeft="@dimen/activity_margin"
    android:paddingRight="@dimen/activity_margin"
    android:paddingTop="@dimen/row_padding">
 
    <ImageView
        android:id="@+id/thumbnail"
        android:layout_width="@dimen/thumbnail"
        android:layout_height="@dimen/thumbnail"
        android:layout_centerVertical="true"
        android:layout_marginRight="@dimen/row_padding" />
 
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/thumbnail"
        android:fontFamily="sans-serif-medium"
        android:textColor="@color/contact_name"
        android:textSize="@dimen/contact_name" />
 
    <TextView
        android:id="@+id/phone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/name"
        android:layout_toRightOf="@id/thumbnail"
        android:textColor="@color/contact_number"
        android:textSize="@dimen/contact_number" />
 
</RelativeLayout>

 

۱۰- یک کلاس به نام ContactsAdapter.java ایجاد و آن را از کلاسFilterable به صورت implement پیاده سازی کنید تا از شما خواسته شود تا متد getfilter() را override کنید.

ContactsAdapter.java
package info.androidhive.recyclerviewsearch;
 
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
 
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
 
import java.util.ArrayList;
import java.util.List;
 
/**
 * Created by ravi on 16/11/17.
 */
 
public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.MyViewHolder>
        implements Filterable {
    private Context context;
    private List<Contact> contactList;
    private List<Contact> contactListFiltered;
    private ContactsAdapterListener listener;
 
    public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView name, phone;
        public ImageView thumbnail;
 
        public MyViewHolder(View view) {
            super(view);
            name = view.findViewById(R.id.name);
            phone = view.findViewById(R.id.phone);
            thumbnail = view.findViewById(R.id.thumbnail);
 
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // send selected contact in callback
                    listener.onContactSelected(contactListFiltered.get(getAdapterPosition()));
                }
            });
        }
    }
 
 
    public ContactsAdapter(Context context, List<Contact> contactList, ContactsAdapterListener listener) {
        this.context = context;
        this.listener = listener;
        this.contactList = contactList;
        this.contactListFiltered = contactList;
    }
 
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.user_row_item, parent, false);
 
        return new MyViewHolder(itemView);
    }
 
    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        final Contact contact = contactListFiltered.get(position);
        holder.name.setText(contact.getName());
        holder.phone.setText(contact.getPhone());
 
        Glide.with(context)
                .load(contact.getImage())
                .apply(RequestOptions.circleCropTransform())
                .into(holder.thumbnail);
    }
 
    @Override
    public int getItemCount() {
        return contactListFiltered.size();
    }
 
    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                String charString = charSequence.toString();
                if (charString.isEmpty()) {
                    contactListFiltered = contactList;
                } else {
                    List<Contact> filteredList = new ArrayList<>();
                    for (Contact row : contactList) {
 
                        // name match condition. this might differ depending on your requirement
                        // here we are looking for name or phone number match
                        if (row.getName().toLowerCase().contains(charString.toLowerCase()) || row.getPhone().contains(charSequence)) {
                            filteredList.add(row);
                        }
                    }
 
                    contactListFiltered = filteredList;
                }
 
                FilterResults filterResults = new FilterResults();
                filterResults.values = contactListFiltered;
                return filterResults;
            }
 
            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
                contactListFiltered = (ArrayList<Contact>) filterResults.values;
                notifyDataSetChanged();
            }
        };
    }
 
    public interface ContactsAdapterListener {
        void onContactSelected(Contact contact);
    }
}

 

در متد getfilter()، رشته ی جستجو برای متد performfiltering() ارسال میشود. جستجو برای یک مخاطب با نام، شماره تلفن با رشته ی جستجو ارائه می گردد.

شما باید شرایط جستجو را متناسب با منابع برنامه تان تنظیم کنید.

رابط contactsadapterlistner() متد فراخوانی oncontactselected() را هر زمان که یک مخاطب از لیست انتخاب شود، فراهم خواهد کرد.

اضافه کردن ویجت جستجو و لیست فیلتر کردن

در این مرحله نیاز داریم تا Searchview را در نوار ابزار فعال کنیم و از Recyclerview با کمک آنالیز و خواندن جیسون و انتقال متن جستجو به آداپتور خروجی بگیریم.

۱۱- menu_main.xml را ایجاد یا باز کنید که زیر res/meun قرار گرفته و ویجت searchview را اضافه و آنرا برای همیشه قابل نمایش قرار دهید.

menu_main.xml
<menu 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"
    tools:context="info.androidhive.recyclerviewsearch.MainActivity">
    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_search_black_24dp"
        android:orderInCategory="100"
        android:title="@string/action_search"
        app:showAsAction="always"
        app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>

 

۱۲- زیر فولدر res/xml ، یک فایل xml به نام searchable.xml  ایجاد کنید (اگر این فایل از قبل وجود ندارد آنرا ایجاد کنید)

searchable.xml
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:hint="@string/search_hint"
    android:label="@string/app_name" />

 

۱۳- AndroidManifest.xml و configure the search را مانند زیر باز کنید.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.recyclerviewsearch">
 
    <uses-permission android:name="android.permission.INTERNET" />
 
    <application ...>
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
 
            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
 
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>
 
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

 

۱۴- فایل های لایه را از main activity باز کنید که شامل activity_main.xml و content_main.xml می باشد و اجزای recyclerview را اضافه نمایید.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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="info.androidhive.recyclerviewsearch.MainActivity">
 
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">
 
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@android:color/white"
            app:popupTheme="@style/AppTheme.PopupOverlay" />
 
    </android.support.design.widget.AppBarLayout>
 
    <include layout="@layout/content_main" />
 
</android.support.design.widget.CoordinatorLayout>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="info.androidhive.recyclerviewsearch.MainActivity"
    tools:showIn="@layout/activity_main">
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical" />
 
</RelativeLayout>

 

۱۵- در نهایت mainactivity.java را باز و کد های نشان داده شده در زیر را اضافه کنید.

در متد fetchcontacts() یک volley برای بیرون کشیدن جیسون درخواست می کند. بیرون کشیدن یا fetch کردن جیسون با کمک Gson به صورت مرتب انجام شده و تمام مخاطبین به یک لیست اضافه میشوند. فراخونی mAdapter.notifyDataSetChanged() باعث میشود تا از recyclerview خروجی گرفته شود.

در onCreateOptionsMenu() منو inflate شده و searchview نمایش داده میشود.

searchView.setOnQueryTextListener() به تغییر کاراکترها در حین نوشتن در فیلد جستجو گوش میدهد. متن وارد شده برای جستجو تجزیه و تحلیل شده و با کمک mAdapter.getFilter().filter(query) به کلاس آداپتور ارسال میشود و سپس recyclerview با داده های فیلتر شده ی جدید بروز رسانی می گردد.

onContactSelected() زمانیکه یک مخاطب از لیست انتخاب شود، فراخوانی خواهد شد.

MainActivity.java
package info.androidhive.recyclerviewsearch;
 
import android.app.SearchManager;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
 
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
 
import org.json.JSONArray;
 
import java.util.ArrayList;
import java.util.List;
 
public class MainActivity extends AppCompatActivity implements ContactsAdapter.ContactsAdapterListener {
    private static final String TAG = MainActivity.class.getSimpleName();
    private RecyclerView recyclerView;
    private List<Contact> contactList;
    private ContactsAdapter mAdapter;
    private SearchView searchView;
 
    // url to fetch contacts json
    private static final String URL = "https://api.androidhive.info/json/contacts.json";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
 
        // toolbar fancy stuff
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setTitle(R.string.toolbar_title);
 
        recyclerView = findViewById(R.id.recycler_view);
        contactList = new ArrayList<>();
        mAdapter = new ContactsAdapter(this, contactList, this);
 
        // white background notification bar
        whiteNotificationBar(recyclerView);
 
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.addItemDecoration(new MyDividerItemDecoration(this, DividerItemDecoration.VERTICAL, 36));
        recyclerView.setAdapter(mAdapter);
 
        fetchContacts();
    }
 
    /**
     * fetches json by making http calls
     */
    private void fetchContacts() {
        JsonArrayRequest request = new JsonArrayRequest(URL,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        if (response == null) {
                            Toast.makeText(getApplicationContext(), "Couldn't fetch the contacts! Pleas try again.", Toast.LENGTH_LONG).show();
                            return;
                        }
 
                        List<Contact> items = new Gson().fromJson(response.toString(), new TypeToken<List<Contact>>() {
                        }.getType());
 
                        // adding contacts to contacts list
                        contactList.clear();
                        contactList.addAll(items);
 
                        // refreshing recycler view
                        mAdapter.notifyDataSetChanged();
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // error in getting json
                Log.e(TAG, "Error: " + error.getMessage());
                Toast.makeText(getApplicationContext(), "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
 
        MyApplication.getInstance().addToRequestQueue(request);
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
 
        // Associate searchable configuration with the SearchView
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        searchView = (SearchView) menu.findItem(R.id.action_search)
                .getActionView();
        searchView.setSearchableInfo(searchManager
                .getSearchableInfo(getComponentName()));
        searchView.setMaxWidth(Integer.MAX_VALUE);
 
        // listening to search query text change
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                // filter recycler view when query submitted
                mAdapter.getFilter().filter(query);
                return false;
            }
 
            @Override
            public boolean onQueryTextChange(String query) {
                // filter recycler view when text is changed
                mAdapter.getFilter().filter(query);
                return false;
            }
        });
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
 
        //noinspection SimplifiableIfStatement
        if (id == R.id.action_search) {
            return true;
        }
 
        return super.onOptionsItemSelected(item);
    }
 
    @Override
    public void onBackPressed() {
        // close search view on back button pressed
        if (!searchView.isIconified()) {
            searchView.setIconified(true);
            return;
        }
        super.onBackPressed();
    }
 
    private void whiteNotificationBar(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int flags = view.getSystemUiVisibility();
            flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            view.setSystemUiVisibility(flags);
            getWindow().setStatusBarColor(Color.WHITE);
        }
    }
 
    @Override
    public void onContactSelected(Contact contact) {
        Toast.makeText(getApplicationContext(), "Selected: " + contact.getName() + ", " + contact.getPhone(), Toast.LENGTH_LONG).show();
    }
}

 

ساخت جستجو در اندروید

ساخت جستجو در اندروید

آکادمی برنامه نویسان برگزار کننده دوره های آموزش اندروید مشهد و کلاس های برنامه نویسی در مشهد می باشد جهت ثبت نام در این دوره ها با ما در تماس باشید . 

کار با RecyclerView در اندروید

آموزش اندروید

RecyclerView نسخه ی پیشرفته ی ListView می باشد که عملکرد بهتری داشته و مزایای بیشتری نسبت به آن دارد. با استفاده از RecyclerView و Cardview، لیست های نمایش اطلاعات می توانند به آسانی و همچنین زیبا ایجاد شوند. در این مطلب اطلاعات کاملی در مورد RecyclerView ارائه خواهیم داد و نمونه هایی را در این زمینه معرفی می کنیم با آموزش Recyclerview در اندروید با ما همراه باشید.

دانلود پروژه

در این آموزش می آموزید که چطور یک RecyclerView ساده را با یک لایوت سفارشی ایجاد کنید. علاوه براین، یک کلاس آداپتور می نویسیم و list driver یا جدا کننده لیست ها و یک listener را برای کلیک کردن روی ردیف اضافه خواهیم کرد. RecyclerView که ما در این مطلب طراحی می کنیم، یک لیست از فیلم ها را نمایش می دهد که دارای عنوان، ژانر و سال تولید می باشد.

در قطعه کد زیر، ویجت RecyclerView با ویژگی های مورد نیاز را مشاهده می کنید.

<android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

قبل از شروع مطمئن شوید که اندروید استودیو نصب و به آخرین نسخه بروز رسانی شده باشد. در این مطلب از  اندروید استودیو  ۳٫۰ RC 2 استفاده می کنیم.

۱- در اندروید استودیو به مسیر file/new project بروید و تمام جزئیات مورد نیاز برای ایجاد پروژه ی جدید را وارد نمایید و در نهایت Blanck Activity را انتخاب کنید.

۲- build.grade را باز کنید و  وابستگی RecyclerView با کد com.android.support:RecyclerView-v7:26.1.0  را اضافه و پروژه را بازسازی کنید

build.gradle
apply plugin: 'com.android.application'
 
android {
    compileSdkVersion 26
    buildToolsVersion '26.0.2'
    // ..
}
 
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //..
 
    // RecyclerView
    implementation 'com.android.support:recyclerview-v7:26.1.0'
}

۳- با آخرین نسخه ی ابزارهای ساخت، اندروید استودیو دو فایل لایه را برای هر اکتیویتی ایجاد می کند. برای main activity، دو فایل activity_main.xml (شامل CoordinatorLayout و AppBarLayout می باشد) و content_main.xml (برای محتوای واقعی) را ایجاد می کند. فایل content_main.xml و ویجت RecyclerView  را اضافه کنید .

content_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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_main"
    tools:context=".MainActivity">
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical" />
 
</RelativeLayout>

۴- colors.xml که در زیر res/values قرار دارد را باز کنید و رنگ های زیر را به آن اضافه نمایید.

colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
    <color name="year">#999999</color>
    <color name="title">#222222</color>
</resources>

۵- dimens.xml در زیر res/values را باز نموده و dimen های زیر را اضافه کنید.

dimens.xml
<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
    <dimen name="fab_margin">16dp</dimen>
    <dimen name="row_padding_vertical">10dp</dimen>
</resources>

آموزش Recyclerview در اندروید

نوشتن کلاس آداپتور

بعد از اضافه کردن ویجت RecyclerView، حال باید کلاس آداپتور را برای آماده کردن داده ها برای RecyclerView بنویسیم. آداپتور RecyclerView مشابه با ListView بوده اما روش های override کردن متفاوتی دارد.

۶- یک کلاس به نام movie.java را ایجاد و عنوان، ژانر و سال تولید را مشخص کنید. علاوه براین، متدهای getter/setter را برای هر متغیر اضافه کنید.

Movie.java
package info.androidhive.recyclerview;
 
public class Movie {
    private String title, genre, year;
 
    public Movie() {
    }
 
    public Movie(String title, String genre, String year) {
        this.title = title;
        this.genre = genre;
        this.year = year;
    }
 
    public String getTitle() {
        return title;
    }
 
    public void setTitle(String name) {
        this.title = name;
    }
 
    public String getYear() {
        return year;
    }
 
    public void setYear(String year) {
        this.year = year;
    }
 
    public String getGenre() {
        return genre;
    }
 
    public void setGenre(String genre) {
        this.genre = genre;
    }
}

۷- یک لایه xml به نام movie_list_row.xml با کد زیر را ایجاد کنید. این فایل لایه یک سطر از RecyclerView را برای نمایش نام فیلم، ژانر و سال تولید آن به عنوان خروجی ارائه می دهد.

movie_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:clickable="true"
    android:focusable="true"
    android:orientation="vertical"
    android:paddingBottom="@dimen/row_padding_vertical"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/row_padding_vertical">
 
    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:textColor="@color/title"
        android:textSize="16dp"
        android:textStyle="bold" />
 
    <TextView
        android:id="@+id/genre"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/title" />
 
    <TextView
        android:id="@+id/year"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:textColor="@color/year" />
 
</RelativeLayout>

۸- یک کلاس به نام movieAdapter.java را ایجاد و قطعه کد زیر را وارد نمایید. در اینجا متد onCreateViewHolder ()، فایل movie_list_row.xml را اصطلاحا inflates می کند. در متد onBindViewHolder()، اطلاعات مربوط به فیلم از قبیل عنوان، ژانر و سال برای هر سطر در نظر گرفته میشود.

MoviesAdapter.java
package info.androidhive.recyclerview;
 
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
 
import java.util.List;
 
public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MyViewHolder> {
 
    private List<Movie> moviesList;
 
    public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView title, year, genre;
 
        public MyViewHolder(View view) {
            super(view);
            title = (TextView) view.findViewById(R.id.title);
            genre = (TextView) view.findViewById(R.id.genre);
            year = (TextView) view.findViewById(R.id.year);
        }
    }
 
 
    public MoviesAdapter(List<Movie> moviesList) {
        this.moviesList = moviesList;
    }
 
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.movie_list_row, parent, false);
 
        return new MyViewHolder(itemView);
    }
 
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        Movie movie = moviesList.get(position);
        holder.title.setText(movie.getTitle());
        holder.genre.setText(movie.getGenre());
        holder.year.setText(movie.getYear());
    }
 
    @Override
    public int getItemCount() {
        return moviesList.size();
    }
}

۹- در این مرحله باید mainactivity.java را باز کنید و تغییرات زیر را روی آن اعمال نمایید. متد prepareMovieData() در اینجا داده های نمونه را به list view اضافه می کند.

MainActivity.java
package info.androidhive.recyclerview;
 
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
 
import java.util.ArrayList;
import java.util.List;
 
public class MainActivity extends AppCompatActivity {
    private List<Movie> movieList = new ArrayList<>();
    private RecyclerView recyclerView;
    private MoviesAdapter mAdapter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
 
        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
 
        mAdapter = new MoviesAdapter(movieList);
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setAdapter(mAdapter);
 
        prepareMovieData();
    }
 
    private void prepareMovieData() {
        Movie movie = new Movie("Mad Max: Fury Road", "Action & Adventure", "2015");
        movieList.add(movie);
 
        movie = new Movie("Inside Out", "Animation, Kids & Family", "2015");
        movieList.add(movie);
 
        movie = new Movie("Star Wars: Episode VII - The Force Awakens", "Action", "2015");
        movieList.add(movie);
 
        movie = new Movie("Shaun the Sheep", "Animation", "2015");
        movieList.add(movie);
 
        movie = new Movie("The Martian", "Science Fiction & Fantasy", "2015");
        movieList.add(movie);
 
        movie = new Movie("Mission: Impossible Rogue Nation", "Action", "2015");
        movieList.add(movie);
 
        movie = new Movie("Up", "Animation", "2009");
        movieList.add(movie);
 
        movie = new Movie("Star Trek", "Science Fiction", "2009");
        movieList.add(movie);
 
        movie = new Movie("The LEGO Movie", "Animation", "2014");
        movieList.add(movie);
 
        movie = new Movie("Iron Man", "Action & Adventure", "2008");
        movieList.add(movie);
 
        movie = new Movie("Aliens", "Science Fiction", "1986");
        movieList.add(movie);
 
        movie = new Movie("Chicken Run", "Animation", "2000");
        movieList.add(movie);
 
        movie = new Movie("Back to the Future", "Science Fiction", "1985");
        movieList.add(movie);
 
        movie = new Movie("Raiders of the Lost Ark", "Action & Adventure", "1981");
        movieList.add(movie);
 
        movie = new Movie("Goldfinger", "Action & Adventure", "1965");
        movieList.add(movie);
 
        movie = new Movie("Guardians of the Galaxy", "Science Fiction & Fantasy", "2014");
        movieList.add(movie);
 
        mAdapter.notifyDataSetChanged();
    }
}

تا این قسمت از آموزش Recyclerview در اندروید ، اگر برنامه را اجرا کنید، مشاهده خواهید کرد که اطلاعات فیلم ها به صورت لیست نمایش داده میشوند.

آموزش Recyclerview در اندروید

افزودن  Divider/Separator (جدا کننده) به RecyclerView

شما می توانید با استفاده از DividerItemDecoration که توسط کتابخانه پشتیبانی میشود، یک خط جدا کننده بین سطرها ایجاد نمایید. آیتم های زیباسازی را مانند زیر به RecyclerView اضافه کنید.

recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
 
// set the adapter
recyclerView.setAdapter(mAdapter);

با اجرای برنامه متوجه خواهید شد که یک خط جداکننده بین هر سطر قرار گرفته است

ساخت خط تقسیم سفارشی با حاشیه

تقسیم کننده های پیش فرض عرض ردیف را به طور کامل اشغال می کند. در بعضی موارد ممکن است بخواهید که حاشیه ای را به خطوط جداکننده اضافه نمایید. برای این منظور نیاز دارید تا کلاس تقسیم کننده ی سفارشی با رنگ و حاشیه ی مورد نظر  بسازید

۱۰- یک کلاس به نام MyDividerItemDecoration.java را ایجاد و کلاس را از ItemDecoration گسترش دهید و از متد addItemDecoration() برای نمایش تقسیم کننده استفاده کنید.

MyDividerItemDecoration.java
package info.androidhive.recyclerview;
 
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;
 
/**
 * Created by Ravi on 30/10/15.
 * updated by Ravi on 14/11/17
 */
public class MyDividerItemDecoration extends RecyclerView.ItemDecoration {
 
    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };
 
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
 
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
 
    private Drawable mDivider;
    private int mOrientation;
    private Context context;
    private int margin;
 
    public MyDividerItemDecoration(Context context, int orientation, int margin) {
        this.context = context;
        this.margin = margin;
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }
 
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }
 
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }
 
    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
 
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left + dpToPx(margin), top, right - dpToPx(margin), bottom);
            mDivider.draw(c);
        }
    }
 
    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();
 
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top + dpToPx(margin), right, bottom - dpToPx(margin));
            mDivider.draw(c);
        }
    }
 
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
 
    private int dpToPx(int dp) {
        Resources r = context.getResources();
        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
    }
}

۱۱- MainActivity.java را باز کنید و ظاهر مورد نظر را با استفاده از متد addItemDecoration() را قبل از تنظیم کردن آداپتور، تنظیم کنید. در قطعه کد زیر حاشیه را ۱۶ dp در نظر گرفته و به گوشه های چپ و راست خط تقسیم کننده اضافه کرده ایم.

recyclerView.addItemDecoration(new MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16));
 
// set the adapter
recyclerView.setAdapter(mAdapter);

با اجرای برنامه متوجه تغییرات در خط تقسیم کننده در هر ردیف خواهید شد.

آموزش کامل recyclerview

نمایش اسکرول کردن افقی RecyclerView

اگر میخواهید RecyclerView به صورت افقی نمایش داده شود، باید یک خط از قطعه کد زیر را تغییر دهید. تمام مواردی که در مدیریت لایه لازم دارید، مانند LinearLayoutManager.HORIZONTAL در زیر قرار گرفته است. فراموش نکنید که عرض ردیف با WRAP_CONTENT مشخص میشود.

// horizontal RecyclerView
// keep movie_list_row.xml width to `wrap_content`
RecyclerView.LayoutManager mLayoutManager = 
new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false);

اضافه کردن آیتم فراخوانی کلیک در RecyclerView

RecyclerView متد OnItemClickListener را برای تشخیص کلیک کردن ندارد و باید این کلاس را با گسترش RecyclerView.OnItemTouchListener بنویسید.

۱۲- یک کلاس به نام RecyclerTouchListener.java ایجاد کنید و از آن RecyclerView.OnItemTouchListener را گسترش دهید. متوجه خواهید شد که رابط ClickListener به اینجا اضافه خواهد شد.

در نهایت آیتم فراخوانی کلیک را به RecyclerView با کمک کد زیر اضافه کنید.

RecyclerTouchListener.java
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
 
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
 
    private GestureDetector gestureDetector;
    private ClickListener clickListener;
 
    public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
        this.clickListener = clickListener;
        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }
 
            @Override
            public void onLongPress(MotionEvent e) {
                View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if (child != null && clickListener != null) {
                    clickListener.onLongClick(child, recyclerView.getChildPosition(child));
                }
            }
        });
    }
 
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
 
        View child = rv.findChildViewUnder(e.getX(), e.getY());
        if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
            clickListener.onClick(child, rv.getChildPosition(child));
        }
        return false;
    }
 
    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    }
 
    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
 
    }
 
    public interface ClickListener {
        void onClick(View view, int position);
 
        void onLongClick(View view, int position);
    }
}

در نهایت با استفاده از کد زیر  click listener را به  recycler view اضافه کنید.

recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
            @Override
            public void onClick(View view, int position) {
                Movie movie = movieList.get(position);
                Toast.makeText(getApplicationContext(), movie.getTitle() + " is selected!", Toast.LENGTH_SHORT).show();
            }
 
            @Override
            public void onLongClick(View view, int position) {
 
            }
        }));

برنامه را اجرا و آیتم کلیک را تست کنید. شما باید قادر باشید تا بعد از کلیک بر روی یک ردیف، پیام مبنی بر تست را مشاهده نمایید. علاوه براین، نوع حرکت موج گونه را بر روی پس زمینه ی ردیف در زمان تست مشاهده میشود.

کد نهایی آموزش Recyclerview در اندروید

در زیر کد کامل MainActivity.java را مشاهده می کنید.

MainActivity.java
package info.androidhive.recyclerview;
 
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;
 
import java.util.ArrayList;
import java.util.List;
 
public class MainActivity extends AppCompatActivity {
    private List<Movie> movieList = new ArrayList<>();
    private RecyclerView recyclerView;
    private MoviesAdapter mAdapter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
 
        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
 
        mAdapter = new MoviesAdapter(movieList);
 
        recyclerView.setHasFixedSize(true);
 
        // vertical RecyclerView
        // keep movie_list_row.xml width to `match_parent`
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
 
        // horizontal RecyclerView
        // keep movie_list_row.xml width to `wrap_content`
        // RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false);
 
        recyclerView.setLayoutManager(mLayoutManager);
 
        // adding inbuilt divider line
        recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
 
        // adding custom divider line with padding 16dp
        // recyclerView.addItemDecoration(new MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
 
        recyclerView.setAdapter(mAdapter);
 
        // row click listener
        recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
            @Override
            public void onClick(View view, int position) {
                Movie movie = movieList.get(position);
                Toast.makeText(getApplicationContext(), movie.getTitle() + " is selected!", Toast.LENGTH_SHORT).show();
            }
 
            @Override
            public void onLongClick(View view, int position) {
 
            }
        }));
 
        prepareMovieData();
    }
 
    /**
     * Prepares sample data to provide data set to adapter
     */
    private void prepareMovieData() {
        Movie movie = new Movie("Mad Max: Fury Road", "Action & Adventure", "2015");
        movieList.add(movie);
 
        movie = new Movie("Inside Out", "Animation, Kids & Family", "2015");
        movieList.add(movie);
 
        movie = new Movie("Star Wars: Episode VII - The Force Awakens", "Action", "2015");
        movieList.add(movie);
 
        movie = new Movie("Shaun the Sheep", "Animation", "2015");
        movieList.add(movie);
 
        movie = new Movie("The Martian", "Science Fiction & Fantasy", "2015");
        movieList.add(movie);
 
        movie = new Movie("Mission: Impossible Rogue Nation", "Action", "2015");
        movieList.add(movie);
 
        movie = new Movie("Up", "Animation", "2009");
        movieList.add(movie);
 
        movie = new Movie("Star Trek", "Science Fiction", "2009");
        movieList.add(movie);
 
        movie = new Movie("The LEGO Movie", "Animation", "2014");
        movieList.add(movie);
 
        movie = new Movie("Iron Man", "Action & Adventure", "2008");
        movieList.add(movie);
 
        movie = new Movie("Aliens", "Science Fiction", "1986");
        movieList.add(movie);
 
        movie = new Movie("Chicken Run", "Animation", "2000");
        movieList.add(movie);
 
        movie = new Movie("Back to the Future", "Science Fiction", "1985");
        movieList.add(movie);
 
        movie = new Movie("Raiders of the Lost Ark", "Action & Adventure", "1981");
        movieList.add(movie);
 
        movie = new Movie("Goldfinger", "Action & Adventure", "1965");
        movieList.add(movie);
 
        movie = new Movie("Guardians of the Galaxy", "Science Fiction & Fantasy", "2014");
        movieList.add(movie);
 
        // notify adapter about data set changes
        // so that it will render the list with new data
        mAdapter.notifyDataSetChanged();
    }
 
}

دانلود پروژه

قسمت بعدی آموزش ؟

امیدوارم در این مقاله درک پایه ایی نسبت به RecyclerView پیدا کرده باشید در قسمت های بعد مباحث پیشرفته تر RecyclerView با ترکیب آن با CardView خواهیم پرداخت و امکاناتی مانند حذف کردن جستجو و …. به آن اضافه خواهی کرد.

  1. آموزش Recyclerview و Cardview در اندروید

برای یادگیری حرفه ایی برنامه نویسی اندروید شما می توانید در کلاس های برنامه نویسی در مشهد  و یا دوره های غیر حضوری آکادمی برنامه نویسان مشهد ثبت نام کنید

اضافه کردن ( کشیدن و رها کردن ) RecyclerView swipe برای حذف و undo

برنامه نویسی اندروید

در این مطلب قصد داریم با اضافه کردن swipe ( کشیدن و رها کردن ) عملیات هایی مانند حذف و undo را به اپلیکیشن جی میل به recyclerview اضافه کنیم. با ما همراه باشید تا در این آموزش RecyclerView با ساخت یک پروژه ی جدید، این روند را بهتر درک کنید.

RecyclerView Swipe ( کشیدن و رها کردن ) چطور کار می کند؟ با ItemTouchHelper

به کمک کلاس ItemTouchHelper شما می توانید یک swipe را به recycler view اضافه کرده و سپس  عملیات کشیدن و رها کردن را در  پیاده سازی کنید . swipe کردن سطر، باعث میشود تا سطر از recyclerview حذف شود اما داده های موجود را بروزرسانی نمی کند.  شما می توانید یک سطر خالی که با swipe کردن یک سطر نمایش داده میشود را مشاهده کنید. لازم به ذکر است که باید حواستان به بروز رسانی لیست حذف شده ی آیتم ها با پاک شدن از آداپتور مجموعه داده باشد.

ItemTouchHelper.SimpleCallback نوعی روش فراخوانی مانند onmove() ،onswipe و ()onchilddraw برای زمانی می باشد که سطر swipe میشود. نمایش پس زمینه، حذف کردن آیتم ها از آداپتور می توانند با استفاده از متدهای فراخوانی انجام شوند.

در قطعه کد زیر ItemTouchHelper برداشته و به recyclerview اضافه میشود. این کد باید کمی تغییر داده شود تا swipe  و undo به درستی انجام شوند.

RecyclerView recyclerView = findViewById(R.id.recycler_view);
 
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
    @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return false;
    }
 
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        // Row is swiped from recycler view
        // remove it from adapter
    }
 
    @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            // view the background view
        }
    };
         
// attaching the touch helper to recycler view
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);

تعریف جهت دهی های swipe

جهت دهی های swipe می توانند برای ایجاد ()simplecallback تصمیم گیری کنند. در این مقاله ما از جهت دهی های چپ به راست استفاده می کنیم. برای استفاده از سایر جهت دهی ها، باید با اپراتور | ترکیب کنید. با دستور زیر جهت های چپ، راست، بالا و پایین را تعریف می کنیم.

new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT)

طراحی لایه ها

طرح بندی لایه ها برای اضافه کردن پس زمینه ی ردیف ها اهمیت زیادی دارد. بسیاری از افراد پس زمینه را روی یک بوم یا canvas با متد ()onChildDraw ایجاد می کنند. البته اگر پس زمینه کمی پیچیده باشد، این فرآیند بسیار خسته کننده خواهد شد. طراحی آن با  لایه های xml خیلی ساده تر می باشد، بنابراین پس زمینه و پیش زمینه را با روش ساده و با کمک framelayout مشخص می کنیم و لایه ی پیش زمینه را در بالا قرار می دهیم.

پیش زمینه معمولا در recyclerview قابل مشاهده می باشد و زمانیکه swipe بر روی پس زمینه انجام میشود، در یک موقعیت ثابت قابل مشاهده خواهد بود.

آموزش RecyclerView

مثال JSON، منوی رستوران

در این قسمت یک مثال JSON که شامل منوی رستوران و تصویر مناسب و نام می باشد را ارائه می دهیم. در این مثال قصد داریم تا این JSON را استفاده کنیم تا آیتم های غذا در لیست recyclerview نمایش داده شوند.

https://api.androidhive.info/json/menu.json

ایجاد پروژه جدید

برای ادامه ی کار در این آموزش RecyclerView نیاز به اطلاعات کافی در مورد ItemTouchHelper داریم تا یک پروژه ی جدید را شروع کنیم و خروجی مورد نظرمان را بدست آوریم.

۱- یک پروژه ی جدید در اندروید استودیو را از مسیر file/new project ایجاد و در نهایت basic activity را به عنوان قالب انتخاب کنید.

۲- bulid.gradle را که زیر پوشه ی app قرار دارد باز نموده و وابستگی های volley و recyclerview را اضافه کنید.

build.gradle
dependencies {
    implementation 'com.android.support:recyclerview-v7:26.1.0'
  
    // glide image library
    implementation 'com.github.bumptech.glide:glide:3.7.0'
     
    // volley http library
    implementation 'com.android.volley:volley:1.0.0'
    implementation 'com.google.code.gson:gson:2.6.2'
}

۳- منابع زیر را به strings.xml و dimens.xml و colors.xml اضافه کنید.

strings.xml
<resources>
    <string name="app_name">Recycler Swipe</string>
    <string name="action_settings">Settings</string>
    <string name="my_cart">My Cart</string>
    <string name="delete">DELETE</string>
</resources>
dimens.xml
<resources>
    <dimen name="activity_padding_horizontal">16dp</dimen>
    <dimen name="padd_10">10dp</dimen>
    <dimen name="ic_delete">30dp</dimen>
    <dimen name="thumbnail">90dp</dimen>
</resources>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#111</color>
    <color name="colorPrimaryDark">#FFF</color>
    <color name="colorAccent">#ea3732</color>
    <color name="bg_row_background">#fa315b</color>
    <color name="item_name">#535353</color>
    <color name="description">#a9a9a9</color>
</resources>

۴- styles.xml را باز کنید و به زیر استایل ها اضافه کنید تا تم  lighter به پروژه تان اضافه شود. این کار باعث میشود تا متن تیره تر از پیش زمینه ی سفیدی که در تولبار داریم به نظر برسد.

styles.xml
<resources>
 
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
 
    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>
 
    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Light" />
 
    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
 
</resources>

۵- یک کلاس به نام MyApplication.java ایجاد کنید و آنرا از applications گسترش دهید (extend). این یک کلاس تک هست که برای ایجاد کتابخانه ی volley به کار میرود.

MyApplication.java
import android.app.Application;
import android.text.TextUtils;
 
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
 
public class MyApplication extends Application {
 
    public static final String TAG = MyApplication.class
            .getSimpleName();
 
    private RequestQueue mRequestQueue;
 
    private static MyApplication mInstance;
 
    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }
 
    public static synchronized MyApplication getInstance() {
        return mInstance;
    }
 
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }
 
        return mRequestQueue;
    }
 
    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }
 
    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }
 
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

۶-  کلاس AndroidManifest.xml را باز کنید و کلاس MyApplication را به گره application اضافه کنید.مجوزهای internet را درست مانند زمانی که http را فراخوانی می کنیم اضافه کنید.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.recyclerviewswipe">
 
    <uses-permission android:name="android.permission.INTERNET" />
 
    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

تا این مرحله از کار، منابع آماده هستند. حال باید recyclerview را اضافه کنیم و داده ها را با تجزیه و تحلیل جیسون خروجی بگیریم.

۷-  فایل لایه ی MainActivity.java را باز کنید و recyclerview را اضافه نمایید. از قسمت my activity ما به دو فایل لایه ی activity_main.xml و content_main.xml نیاز داریم.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:id="@+id/coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="info.androidhive.recyclerviewswipe.MainActivity">
 
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">
 
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@android:color/white"
            app:popupTheme="@style/AppTheme.PopupOverlay" />
 
    </android.support.design.widget.AppBarLayout>
 
    <include layout="@layout/content_main" />
 
</android.support.design.widget.CoordinatorLayout>
content_main.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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="info.androidhive.recyclerviewswipe.MainActivity"
    tools:showIn="@layout/activity_main">
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical" />
     
</android.support.constraint.ConstraintLayout>

۸-یک کلاس به نام Item.class ایجاد کنید و قطعه کد زیر را اضافه نمایید. کلاس POJO شامل آیتم های منو به نام های name، price، description و آدرس تصویر thumbnail می باشد.

Item.java
/**
 * Created by ravi on 26/09/17.
 */
 
public class Item {
    int id;
    String name;
    String description;
    double price;
    String thumbnail;
 
    public Item() {
    }
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
    public double getPrice() {
        return price;
    }
 
    public void setPrice(double price) {
        this.price = price;
    }
 
    public String getThumbnail() {
        return thumbnail;
    }
 
    public void setThumbnail(String thumbnail) {
        this.thumbnail = thumbnail;
    }
}

۹- یک لایه ی xml به نام cart_list_item.xml  زیر res/layout ایجاد کنید. این لایه هر سطر در recyclerview را با کمک کلاس آداپتور خارج می کند. در اینجا ما یک imageview را برای thumbnail  و چند textviews را برای نمایش آیتم name،  description و price اضافه کرده ایم.

cart_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
 
    <RelativeLayout
        android:id="@+id/view_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/bg_row_background">
 
        <ImageView
            android:id="@+id/delete_icon"
            android:layout_width="@dimen/ic_delete"
            android:layout_height="@dimen/ic_delete"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="@dimen/padd_10"
            android:src="@drawable/ic_delete_white_24dp" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="@dimen/padd_10"
            android:layout_toLeftOf="@id/delete_icon"
            android:text="@string/delete"
            android:textColor="#fff"
            android:textSize="13dp" />
 
    </RelativeLayout>
 
    <RelativeLayout
        android:id="@+id/view_foreground"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:padding="@dimen/padd_10">
 
        <ImageView
            android:id="@+id/thumbnail"
            android:layout_width="@dimen/thumbnail"
            android:layout_height="@dimen/thumbnail"
            android:layout_marginRight="@dimen/activity_padding_horizontal"
            android:scaleType="centerCrop" />
 
        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/thumbnail"
            android:ellipsize="end"
            android:fontFamily="sans-serif-medium"
            android:maxLines="1"
            android:textColor="@color/item_name"
            android:textSize="17dp" />
 
        <TextView
            android:id="@+id/description"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/name"
            android:layout_marginTop="5dp"
            android:layout_toRightOf="@id/thumbnail"
            android:textColor="@color/description"
            android:textSize="12dp" />
 
        <TextView
            android:id="@+id/price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_toRightOf="@id/thumbnail"
            android:textColor="@color/colorAccent"
            android:textStyle="bold" />
 
    </RelativeLayout>
</FrameLayout>

۱۰- یک کلاس به نام CartListAdapter.java را ایجاد کنید. این کلاس آداپتور برای پر کردن لایه ها با داده های مناسب در recyclerview استفاده میشود. ما همچنین دو متد اضافه برای کلاس removeItem() و restoreItem() اضافه کردیم تا عملیات حذف و اضافه کردن را به recyclerview انجام دهند.

CartListAdapter.java
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
 
import com.bumptech.glide.Glide;
 
import java.util.List;
 
public class CartListAdapter extends RecyclerView.Adapter<CartListAdapter.MyViewHolder> {
    private Context context;
    private List<Item> cartList;
 
    public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView name, description, price;
        public ImageView thumbnail;
        public RelativeLayout viewBackground, viewForeground;
 
        public MyViewHolder(View view) {
            super(view);
            name = view.findViewById(R.id.name);
            description = view.findViewById(R.id.description);
            price = view.findViewById(R.id.price);
            thumbnail = view.findViewById(R.id.thumbnail);
            viewBackground = view.findViewById(R.id.view_background);
            viewForeground = view.findViewById(R.id.view_foreground);
        }
    }
 
 
    public CartListAdapter(Context context, List<Item> cartList) {
        this.context = context;
        this.cartList = cartList;
    }
 
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.cart_list_item, parent, false);
 
        return new MyViewHolder(itemView);
    }
 
    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        final Item item = cartList.get(position);
        holder.name.setText(item.getName());
        holder.description.setText(item.getDescription());
        holder.price.setText("₹" + item.getPrice());
 
        Glide.with(context)
                .load(item.getThumbnail())
                .into(holder.thumbnail);
    }
 
    @Override
    public int getItemCount() {
        return cartList.size();
    }
 
    public void removeItem(int position) {
        cartList.remove(position);
        // notify the item removed by position
        // to perform recycler view delete animations
        // NOTE: don't call notifyDataSetChanged()
        notifyItemRemoved(position);
    }
 
    public void restoreItem(Item item, int position) {
        cartList.add(position, item);
        // notify item added by position
        notifyItemInserted(position);
    }
}

۱۱- در این مرحله، قصد داریم  یک توابع swipe را به recyclerview اضافه کنیم. برای این منظور یک کلاس به نام RecyclerItemTouchHelper.java ایجاد کرده و کلاس را از ItemTouchHelper.SimpleCallback گسترش داده و متدهای مورد نیاز را override می کنیم.

()getDefaultUIUtil با ItemTouchHelper استفاده میشود تا زمانیکه UI تغییر می کند، تشخیص داده شود. ما از این تابع برای حفظ پس زمینه در حالت و موقعیت استاتیک و انتقال پیش زمینه استفاده میکنیم.

در ()onChildDrawOver، موقعیت X از پیش زمینه در حالیکه کاربر swipe می کند، تغییر داده میشود

رابط RecyclerItemTouchHelperListener برای این استفاده میشود تا فراخوانی را برای اکتیویتی های پیاده سازی شده ارسال کند.

RecyclerItemTouchHelper.java
import android.graphics.Canvas;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View;
 
/**
 * Created by ravi on 29/09/17.
 */
 
public class RecyclerItemTouchHelper extends ItemTouchHelper.SimpleCallback {
    private RecyclerItemTouchHelperListener listener;
 
    public RecyclerItemTouchHelper(int dragDirs, int swipeDirs, RecyclerItemTouchHelperListener listener) {
        super(dragDirs, swipeDirs);
        this.listener = listener;
    }
 
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return true;
    }
 
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (viewHolder != null) {
            final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground;
 
            getDefaultUIUtil().onSelected(foregroundView);
        }
    }
 
    @Override
    public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
                                RecyclerView.ViewHolder viewHolder, float dX, float dY,
                                int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground;
        getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
                actionState, isCurrentlyActive);
    }
 
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground;
        getDefaultUIUtil().clearView(foregroundView);
    }
 
    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView,
                            RecyclerView.ViewHolder viewHolder, float dX, float dY,
                            int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground;
 
        getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
                actionState, isCurrentlyActive);
    }
 
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
    }
 
    @Override
    public int convertToAbsoluteDirection(int flags, int layoutDirection) {
        return super.convertToAbsoluteDirection(flags, layoutDirection);
    }
 
    public interface RecyclerItemTouchHelperListener {
        void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position);
    }
}

۱۲- در نهایت، MainActivity.java را باز کنید و تغییرات را مانند زیر انجام دهید.

متد () prepareCart، جیسون را از url میگیرد و بعد از تجزیه و تحلیل، آیتم ها را به آداپتور لیست داده اضافه میکند.

RecyclerItemTouchHelper در اینجا ایجاد شده و به recyclerview اختصاص داده شده است. در اینجا تنها جهت چپ را تعریف کرده ایم.

متد onSwiped() زمانی فراخوانی میشود کهswipe  ارائه شود. گام مهم در اینجا حذف آیتم های ردیف می باشد. mAdapter.removeItem برای حذف سطر از recyclerview فراخوانی میشود.

هنگامی که سطر حذف میشود، snackbar برای نمایش یک پیام با گزینه ی undo استفاده میشود. بالای کلیک undo، سطر دوباره با متد mAdapter.restoreItem() به حالت قبل برمیگردد.

متغیرهای deletedItem و deletedIndex برای ذخیره سازی موقت آیتم های حذف شده و ایندکس تا زمانیکه snackbar نمایش داده میشود، کاربرد دارد.

MainActivity.java
import android.graphics.Color;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
 
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
 
import org.json.JSONArray;
 
import java.util.ArrayList;
import java.util.List;
 
public class MainActivity extends AppCompatActivity implements RecyclerItemTouchHelper.RecyclerItemTouchHelperListener {
 
    private static final String TAG = MainActivity.class.getSimpleName();
    private RecyclerView recyclerView;
    private List<Item> cartList;
    private CartListAdapter mAdapter;
    private CoordinatorLayout coordinatorLayout;
 
    // url to fetch menu json
    private static final String URL = "https://api.androidhive.info/json/menu.json";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(getString(R.string.my_cart));
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 
        recyclerView = findViewById(R.id.recycler_view);
        coordinatorLayout = findViewById(R.id.coordinator_layout);
        cartList = new ArrayList<>();
        mAdapter = new CartListAdapter(this, cartList);
 
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
        recyclerView.setAdapter(mAdapter);
 
        // adding item touch helper
        // only ItemTouchHelper.LEFT added to detect Right to Left swipe
        // if you want both Right -> Left and Left -> Right
        // add pass ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT as param
        ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, this);
        new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
 
 
        // making http call and fetching menu json
        prepareCart();
    }
 
    /**
     * method make volley network call and parses json
     */
    private void prepareCart() {
        JsonArrayRequest request = new JsonArrayRequest(URL,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        if (response == null) {
                            Toast.makeText(getApplicationContext(), "Couldn't fetch the menu! Pleas try again.", Toast.LENGTH_LONG).show();
                            return;
                        }
 
                        List<Item> items = new Gson().fromJson(response.toString(), new TypeToken<List<Item>>() {
                        }.getType());
 
                        // adding items to cart list
                        cartList.clear();
                        cartList.addAll(items);
 
                        // refreshing recycler view
                        mAdapter.notifyDataSetChanged();
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // error in getting json
                Log.d(TAG, "Error: " + error.getMessage());
                Toast.makeText(getApplicationContext(), "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
 
        MyApplication.getInstance().addToRequestQueue(request);
    }
 
    /**
     * callback when recycler view is swiped
     * item will be removed on swiped
     * undo option will be provided in snackbar to restore the item
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position) {
        if (viewHolder instanceof CartListAdapter.MyViewHolder) {
            // get the removed item name to display it in snack bar
            String name = cartList.get(viewHolder.getAdapterPosition()).getName();
 
            // backup of removed item for undo purpose
            final Item deletedItem = cartList.get(viewHolder.getAdapterPosition());
            final int deletedIndex = viewHolder.getAdapterPosition();
 
            // remove the item from recycler view
            mAdapter.removeItem(viewHolder.getAdapterPosition());
 
            // showing snack bar with Undo option
            Snackbar snackbar = Snackbar
                    .make(coordinatorLayout, name + " removed from cart!", Snackbar.LENGTH_LONG);
            snackbar.setAction("UNDO", new View.OnClickListener() {
                @Override
                public void onClick(View view) {
 
                    // undo is selected, restore the deleted item
                    mAdapter.restoreItem(deletedItem, deletedIndex);
                }
            });
            snackbar.setActionTextColor(Color.YELLOW);
            snackbar.show();
        }
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds cartList to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
}

در نهایت  برنامه را اجرا کنید و تست های لازم را انجام دهید. مطمئن شوید که دستگاه تلفن شما به اینترنت متصل است. خروجی برنامه را در تصویر زیر مشاهده می کنید.

آموزش RecyclerView

RecyclerView

همچنین شما می توانید برای یادگیری تخصصی اپلیکیشن نویسی اندروید با شرکت کلاس های برنامه نویسی در مشهد و دوره آموزش اندروید مشهد به صورت حرفه ایی در این زمینه شروع به کار نمایید.

آموزش کتابخانه Paging در اندروید

آموزش اندروید

بسیاری از برنامه ها مجموعه بزرگی از داده ها را به کاربران نمایش می دهند . برای مثال برنامه آمازون را در نظر بگیرید این برنامه لیستی از محصولات را نشان می دهد. محصولات زیادی هم دارد اما تمام محصولات را یکباره بارگیری نمی کند ,برخی از محصولات را  نشان می دهد و به محض رسیدن به آخرین مورد از لیست , محصولات بیشتری را بارگیری می کند و به ما نشان می دهد .اینکار صفحه بندی یا پیمایش (Paging)  نامیده می شود. در این مقاله از آموزشگاه های برنامه نویسی در مشهد به معرفی این ویژگی می پردازیم .

چرا از Paging استفاده کنیم؟

  • فرض کنید بیش از ۱۰۰۰ آیتم برای لیست تان دارید که از یک سرور پشتیبان ,می گیرید. کار اشتباهی است که همه آیتم ها را یکباره خوانده و نمایش دهیم .

معایب عدم استفاده از صفحه بندی (Paging) :

  • کاربر همه آیتمها را همزمان نمی بیند اما شما همه آیتم ها را یکدفعه بارگذاری می کنید که در نتیجه پهنای باند بیشتری را بیهوده مصرف خواهید کرد.
  •  همچنین ایجاد یک لیست بزرگ از منابع سیستم بیشتری استفاده می کند که نتیجه آن یک برنامه کند خواهد بود.

مزایای استفاده از پیمایش:

  • شما فقط یک تکه کوچک از مجموع داده های بزرگ را بارگذاری می کنیدکه از پهنای باند کمتری استفاده خواهد شد .
  •  این برنامه از منابع کمتری استفاده می کند که نتیجه آن یک برنامه سریع و مناسب میباشد.

کتابخانه paging

کتابخانه paging اندروید یک جز  از کامپوننت jetpack اندروید هست .به یاد داشته باشید که بطور پیش فرض در دسترس نیست و باید این کتابخانه را به برنامه اضافه کنیم. اینکار به ما کمک می کند که داده هارا به تدریج و به زیبایی در RecyclerView  بارگیری کنیم.

Android Jetpack مجموعه ای از کتابخانه ها، ابزار ها و راهنمایی های معماری است که به شما کمک می کند تا سریع و آسان به ساخت برنامه های کاربردی برای اندروید بپردازید. Android Jetpack زیر ساخت ها ی کد را فراهم می کند، بنابراین شما می توانید بر روی آنچه که در برنامه شما مورد نیاز است ، تمرکز کنید.

پیش نیازها

  • استفاده از Retrofit در اندروید:از کتابخانه Retrofit برای آوردن داده ها از API backend استفاده خواهیم کرد.
  • RecyclerView:بعد از آوردن آیتم ها از سرور آنها را در یک RecyclerView بارگذاری خواهیم کرد.
  • Android ViewModel: یک جزء دیگر از Android Jetpack میباشد و به ما کمک می کند اطلاعات مربوط  به رابط کاربر(UI) را به روش کارآمدتر نمایش دهیم .

Backend API

مهمترین قسمت Backend API هست . اگر چه شما میتوانید داده ها را از پایگاه داده SQLITE با استفاده از کتابخانه Paging بارگذاری کنید اما اغلب , برنامه نیاز به استخراج داده از Bakend API دارد . شما میتوانید با یادگیری یکسری آموزشها یک API را با استفاده از PHP و MYSQL بسازید .

در این آموزش API را خودمان نخواهیم ساخت و از یک API واقعی از Stackoverflow استفاده کنیم.در زیر لینک API هست  :

https://api.stackexchange.com/2.2/answers?page=1&pagesize=50&site=stackoverflow

در نشانی اینترنتی API بالا پارامترهای زیر را داریم:

  • Page: شماره صفحه ای که میخواهیم بیاریم.
  • Pagesize:تعداد کل آیتم هایی که در صفحه میخواهیم.
  • Site:سایتی که میخواهیم داده ها را از آن  بیاوریم.

نشانی اینترنتی بالا پاسخ زیر را خواهد داد:

آموزش کتابخانه Paging در اندروید
آموزش کتابخانه Paging در اندروید

داده ها از Stackoverflow می آیند و این سایت یک مجموعه داده بسیار بزرگی دارد بنابراین ممکنه تعداد نامحدود صفحات داشته باشیم. حال وظیفه ما این است که صفحه ۱ را برداریم و به محض رسیدن کاربر به انتهای فهرست ,صفحه بعدی را بارگذاری کنیم و برای اینکار از کتابخانه paging اندروید استفاده می کنیم.

حالا بیایید وارد کد واقعی شویم.

ایجاد یک پروژه جدید:

  • یک پروژه جدید با عنوان Paging Library Tutorial ایجاد میکنیم.

اضافه کردن وابستگی ها (Dependencies)

  • به فایل build.gradle بروید و وابستگی های زیر را اضافه کنید:
dependencies {
    def paging_version = "1.0.0"
    def view_model_version = "1.1.0"
    def support_version = "27.1.0"
    def glide_version = "4.3.1"
 
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "com.android.support:appcompat-v7:$support_version"
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    
    //adding retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    
    //adding view model
    implementation "android.arch.lifecycle:extensions:$view_model_version"
    implementation "android.arch.lifecycle:viewmodel:$view_model_version"
    
    //adding paging
    implementation "android.arch.paging:runtime:$paging_version"
    
    //adding recyclerview and cardview
    implementation "com.android.support:cardview-v7:$support_version"
    implementation "com.android.support:recyclerview-v7:$support_version"
    
    //adding glide 
    implementation "com.github.bumptech.glide:glide:$glide_version"
    annotationProcessor "com.github.bumptech.glide:compiler:$glide_version"
}

  • بعد از اضافه کردن وابستگی های مورد نیاز پروژه خود را sync (همگام) کنید.

ما وابستگیهای زیر را اضافه کردیم:

  • Retrofit and Gson: برای تجزیه json از URL ( نشانی اینترنتی)
  • ViewModel : کامپوننت اندروید برای ذخیره داده ها
  • Paging:کتابخانه صفحه بندی (پیمایش)
  • RecyclerView and CardView:برای ساختن فهرست
  • Glide:برای بارگذاری تصویر از URL

ایجاد کلاس مدل

ما به این کلاس نیاز داریم تا پاسخ Json را بطور خودکار تجزیه کنیم. در کل به کلاسهای زیادی  هم نیاز داریم تا پاسخ  را بطور خودکار به کلاس جاوا مربوطه متصل کنیم

  • یک فایل به نام StackApiResponse.java ایجاد کنید و کد زیر را بنویسید.
package net.simplifiedcoding.androidpagingexample;

import java.util.List;

class Owner {
    public int reputation;
    public long user_id;
    public String user_type;
    public String profile_image;
    public String display_name;
    public String link;
}

class Item {
    public Owner owner;
    public boolean is_accepted;
    public int score;
    public long last_activity_date;
    public long creation_date;
    public long answer_id;
    public long question_id;
}

public class StackApiResponse {
    public List<Item> items;
    public boolean has_more;
    public int quota_max;
    public int quota_remaining;
}
  • کد بالا بسیار ساده است . و در حد امکان کوتاه نوشته شده است . نام متغیرها با کلیدهای Json مطابقت دارد بنابراین Gson داده ها را بر اساس آن نمایش خواهد داد .
  • فایل بالا فقط یک کلاس عمومی (public) دارد .(در واقع ما میتوانیم فقط یک کلاس عمومی در یک فایل داشته باشیم)

کلاس به نام StackApiResponse.java شامل Json زیر است:

  • در کلاس فوق ما در حال تطبیق دادن داده های  Json بالا هستیم به همین دلیل است که فقط ۴ ویژگی در StackApiResponse.java داریم.
  • اولین مورد در داخل StackApiResponse.java آیتم (items) هست که شامل یک آرایه از آیتم ها می باشد.به همین دلیل است که یک < List<Item داریم.

سپس has_more از نوع boolean  هست و quota_max و quota_remaining  از نوع int هستند.

  • در حال حاضر داخل  Item , ما Json زیر را داریم:

  • درون items ما یک شی ء دیگر به نام Owner  داریم.به همین دلیل است که یک کلاس دیگر به نام owner ایجاد کرده ایم و یک شیء از  نوع owner را داخل کلاس Item تعریف کرده ایم.امیدوارم که فهمیده باشید که چطور کلاس مدل را برای Json مشخص تعریف کنید.

ایجاد کلاس Retrofit

هر بار که میخواهیم داده ها را از یک صفحه جدید دریافت کنیم به شیء Retrofit نیاز داریم. پس ایجاد یک نمونه از Retrofit ایده خوبی هست .

یک کلاس جدید به نام RetrofitClient ایجاد می کنیم.

package net.simplifiedcoding.androidpagingexample;

import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {

    private static final String BASE_URL = "https://api.stackexchange.com/2.2/";
    private static RetrofitClient mInstance;
    private Retrofit retrofit;


    private RetrofitClient() {
        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    public static synchronized RetrofitClient getInstance() {
        if (mInstance == null) {
            mInstance = new RetrofitClient();
        }
        return mInstance;
    }

    public Api getApi() {
        return retrofit.create(Api.class);
    }
}

کد بالا بسیار ساده است اما اگر در درکش مشکل دارید از ابتدا آموزش را دنبال کنید.

ایجاد API

  • حالا ما رابط فراخوان API را ایجاد میکنیم.
  • یک رابط به نام Api ایجاد کرده و کد زیر را می نویسیم.
package net.simplifiedcoding.androidpagingexample;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface Api {

    @GET("answers")
    Call<StackApiResponse> getAnswers(@Query("page") int page, @Query("pagesize") int pagesize, @Query("site") String site);
}

ایجاد RecyclerView

  • همانطور که قبلا گفته بودیم این لیست را در RecyclerView نمایش خواهیم داد . به  activity_main.xml بروید و RecyclerView را در اینجا تعریف کنید.
<?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=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>
  • حالا برای RecyclerView یک فایل لایوت به نام recyclerview_users ایجاد کنید و کد  xml زیر را بنویسید.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">


    <ImageView
        android:id="@+id/imageView"
        android:layout_width="70dp"
        android:layout_height="70dp" />


    <TextView
        android:id="@+id/textViewName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="15dp"
        android:layout_toRightOf="@id/imageView"
        android:text="Belal Khan"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
        android:textColor="@color/colorDefault" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_below="@id/imageView"
        android:background="@color/colorDefault" />

</RelativeLayout>

ایجاد  PagedListAdapter 

برای خواندن صفحه ی داده ها از RecyclerView.Adapter استفاده نخواهیم کرد  و به جای آن از  pageListAdapter استفاده خواهیم کرد. این کلاسی هست که کارهایی مثل شمارش آیتم , صفحه فراخوانی (callback) و غیره را انجام میدهد . و نکته این است که به عنوان رابط برای RecyclerView ما خواهد بود.

  • یک کلاس به نام ItemAdapter ایجاد کنید و کد زیر را بنویسید.
package net.simplifiedcoding.androidpagingexample;

import android.arch.paging.PagedListAdapter;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.bumptech.glide.Glide;

public class ItemAdapter extends PagedListAdapter<Item, ItemAdapter.ItemViewHolder> {

    private Context mCtx;

    ItemAdapter(Context mCtx) {
        super(DIFF_CALLBACK);
        this.mCtx = mCtx;
    }

    @NonNull
    @Override
    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mCtx).inflate(R.layout.recyclerview_users, parent, false);
        return new ItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
        Item item = getItem(position);

        if (item != null) {
            holder.textView.setText(item.owner.display_name);
            Glide.with(mCtx)
                    .load(item.owner.profile_image)
                    .into(holder.imageView);
        }else{
            Toast.makeText(mCtx, "Item is null", Toast.LENGTH_LONG).show();
        }
    }

    private static DiffUtil.ItemCallback<Item> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<Item>() {
                @Override
                public boolean areItemsTheSame(Item oldItem, Item newItem) {
                    return oldItem.question_id == newItem.question_id;
                }

                @Override
                public boolean areContentsTheSame(Item oldItem, Item newItem) {
                    return oldItem.equals(newItem);
                }
            };

    class ItemViewHolder extends RecyclerView.ViewHolder {

        TextView textView;
        ImageView imageView;

        public ItemViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.textViewName);
            imageView = itemView.findViewById(R.id.imageView);
        }
    }
}

آداپتر تقریبا شبیه<> RecyclerView.Adapter است.تنها تغییر اینجا این است که ما  پیاده سازی Diff_CallBack  را داریم که برایsuper()  استفاده میکنیم هستیم . این فراخوانی برای متمایز کردن دو مورد در یک لیست استفاده میشود.

  • برای <> pagedListAdapter ما Item   و  viewholder  را تعریف می کنیم . Item موردی هست که شما میخواهید نمایش داده شود و ما یک کلاس به این نام داریم که حاوی اطلاعاتی است که برای نمایش نیاز داریم.

ایجاد منبع داه Item

منبع داده item هایمان از جایی است که داده های واقعی را قرار دارد خوانده می شود و همانطور که میدانید که ما از StackOverFlow  Api استفاده میکنیم.

برای ایجاد منبع داده گزینه های زیادی مانند :ItemKeyedDataSource, PageKeyedDataSource, PositionalDataSource    داریم.

برای مثال ما قصد داریم ازPageKeyedDataSource استفاده کنیم . و در Api باید برای آوردن صفحه ای که میخواهیم  شماره صفحه را بفرستیم . پس در اینجا شماره صفحه می شود کلید صفحه ی ما .

  • یک کلاس با نام ItemDataSource ایجاد کنید و کد زیر را بنویسید.
package net.simplifiedcoding.androidpagingexample;

import android.arch.paging.PageKeyedDataSource;
import android.support.annotation.NonNull;
import android.util.Log;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class ItemDataSource extends PageKeyedDataSource<Integer, Item> {

    //the size of a page that we want
    public static final int PAGE_SIZE = 50;

    //we will start from the first page which is 1
    private static final int FIRST_PAGE = 1;

    //we need to fetch from stackoverflow
    private static final String SITE_NAME = "stackoverflow";


    //this will be called once to load the initial data
    @Override
    public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull final LoadInitialCallback<Integer, Item> callback) {
        RetrofitClient.getInstance()
                .getApi().getAnswers(FIRST_PAGE, PAGE_SIZE, SITE_NAME)
                .enqueue(new Callback<StackApiResponse>() {
                    @Override
                    public void onResponse(Call<StackApiResponse> call, Response<StackApiResponse> response) {
                        if (response.body() != null) {
                            callback.onResult(response.body().items, null, FIRST_PAGE + 1);
                        }
                    }

                    @Override
                    public void onFailure(Call<StackApiResponse> call, Throwable t) {

                    }
                });
    }

    //this will load the previous page
    @Override
    public void loadBefore(@NonNull final LoadParams<Integer> params, @NonNull final LoadCallback<Integer, Item> callback) {
        RetrofitClient.getInstance()
                .getApi().getAnswers(params.key, PAGE_SIZE, SITE_NAME)
                .enqueue(new Callback<StackApiResponse>() {
                    @Override
                    public void onResponse(Call<StackApiResponse> call, Response<StackApiResponse> response) {

                        //if the current page is greater than one
                        //we are decrementing the page number
                        //else there is no previous page
                        Integer adjacentKey = (params.key > 1) ? params.key - 1 : null;
                        if (response.body() != null) {

                            //passing the loaded data
                            //and the previous page key 
                            callback.onResult(response.body().items, adjacentKey);
                        }
                    }

                    @Override
                    public void onFailure(Call<StackApiResponse> call, Throwable t) {

                    }
                });
    }

    //this will load the next page
    @Override
    public void loadAfter(@NonNull final LoadParams<Integer> params, @NonNull final LoadCallback<Integer, Item> callback) {
        RetrofitClient.getInstance()
                .getApi()
                .getAnswers(params.key, PAGE_SIZE, SITE_NAME)
                .enqueue(new Callback<StackApiResponse>() {
                    @Override
                    public void onResponse(Call<StackApiResponse> call, Response<StackApiResponse> response) {
                        
                        if (response.body() != null) {
                            //if the response has next page
                            //incrementing the next page number
                            Integer key = response.body().has_more ? params.key + 1 : null; 
                            
                            //passing the loaded data and next page value 
                            callback.onResult(response.body().items, key);
                        }
                    }

                    @Override
                    public void onFailure(Call<StackApiResponse> call, Throwable t) {

                    }
                });
    }
}

ممکن است کد بالا مشکل به نظر برسد اما مهمترین بخش پروژه ما هست . پس با هم مرور میکنیم.

  • کلاس بالا   از کلاس <  PageKeyedDataSource<Integer, Item ارث بری دارد . Integer در اینجا کلید صفحه را تعریف می کند که در اینجا  ما عددی صحیح استفاده می کنیم . هر بار که ما یک صفحه جدید از API میخواهیم باید شماره صفحه ای که میخواهیم  که یک عدد صحیح است را بفرستیم . Item موردی است که ما از API بدست خواهیم آورد .از قبل یک کلاس به نام Item داریم.
  • سپس اندازه یک صفحه را تعریف کردیم که ۵۰ هست , شماره صفحه اول که ۱ هست و sitename جایی هست که میخواهیم اطلاعات را بدست آوریم اگر میخواهید این مقادیر را تغییر دهید.
  • ما سه متد را overridden کردیم :

() loadInitials: این متد داده های اولیه را بار خواهد کرد.

() loadBefore:  این متد صفحه قبلی را بار خواهد کرد.

() loadAfter:  این متد صفحه بعد  را بار خواهد کرد.

ایجاد Item Data Source Factory

ما قصد داریم از <> MutableLiveData برای ذخیره  PageKeyedDataSource مان استفاده کنیم و برای اینکار به  DataSource.Factory نیاز داریم.

package net.simplifiedcoding.androidpagingexample;

import android.arch.lifecycle.MutableLiveData;
import android.arch.paging.DataSource;
import android.arch.paging.PageKeyedDataSource;

public class ItemDataSourceFactory extends DataSource.Factory {

    //creating the mutable live data
    private MutableLiveData<PageKeyedDataSource<Integer, Item>> itemLiveDataSource = new MutableLiveData<>();

    @Override
    public DataSource<Integer, Item> create() {
        //getting our data source object
        ItemDataSource itemDataSource = new ItemDataSource();

        //posting the datasource to get the values
        itemLiveDataSource.postValue(itemDataSource);
        
        //returning the datasource
        return itemDataSource;
    }

    
    //getter for itemlivedatasource
    public MutableLiveData<PageKeyedDataSource<Integer, Item>> getItemLiveDataSource() {
        return itemLiveDataSource;
    }
}

ایجاد  ViewModel

  • یک کلاس با نام  ItemViewModel ایجاد کنید و کد زیر را بنویسید:
package net.simplifiedcoding.androidpagingexample;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.paging.LivePagedListBuilder;
import android.arch.paging.PageKeyedDataSource;
import android.arch.paging.PagedList;

public class ItemViewModel extends ViewModel {

    //creating livedata for PagedList  and PagedKeyedDataSource
    LiveData<PagedList<Item>> itemPagedList;
    LiveData<PageKeyedDataSource<Integer, Item>> liveDataSource;

    //constructor
    public ItemViewModel() {
        //getting our data source factory
        ItemDataSourceFactory itemDataSourceFactory = new ItemDataSourceFactory();

        //getting the live data source from data source factory
        liveDataSource = itemDataSourceFactory.getItemLiveDataSource();

        //Getting PagedList config
        PagedList.Config pagedListConfig =
                (new PagedList.Config.Builder())
                        .setEnablePlaceholders(false)
                        .setPageSize(ItemDataSource.PAGE_SIZE).build();

        //Building the paged list
        itemPagedList = (new LivePagedListBuilder(itemDataSourceFactory, pagedListConfig))
                .build();
    }
}

حالا پروژه خود را بازسازی (rebuild) کنید

نمایش Paged List

  • در نهایت به MainActivity.java بروید و کد زیر را بنویسید:
package net.simplifiedcoding.androidpagingexample;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.arch.paging.PagedList;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

    //getting recyclerview
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        
        //setting up recyclerview
        recyclerView = findViewById(R.id.recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setHasFixedSize(true);

        //getting our ItemViewModel
        ItemViewModel itemViewModel = ViewModelProviders.of(this).get(ItemViewModel.class);

        //creating the Adapter
        final ItemAdapter adapter = new ItemAdapter(this);

        
        //observing the itemPagedList from view model
        itemViewModel.itemPagedList.observe(this, new Observer<PagedList<Item>>() {
            @Override
            public void onChanged(@Nullable PagedList<Item> items) {
                
                //in case of any changes 
                //submitting the items to adapter
                adapter.submitList(items);
            }
        });

        //setting the adapter 
        recyclerView.setAdapter(adapter);
    }
}

حالا پروژه خود را اجرا کنید.

توجه: اگر هیچ آیتمی را نمی بینید مطمین شوید که  نشانی اینترنتی API به درستی کار می کند( با باز کردن نشانی اینترنتی در مرورگرتان ).گاهی اوقات  stackoverflow در خواستهای پیوسته به URLs را مسدود می کند.

دانلود پروژه کتابخانه Paging در اندروید

جهت شرکت در دوره های آموزش اندروید مشهد و آموزش طراحی سایت مشهد می توانید از طریق شماره تماس های پایین ما در ارتباط باشید .