অ্যান্ড্রয়েড 5.0 - একটি পুনর্ব্যবহারযোগ্য ভিউতে শিরোলেখ / পাদচরণ যুক্ত করুন


122

আমি RecyclerViewব্যর্থ হয়ে একটিতে শিরোনাম যুক্ত করার উপায় বের করার চেষ্টা করে এক মুহূর্ত অতিবাহিত করেছি ।

আমি এ পর্যন্ত এটি পেয়েছি:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    layouManager = new LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(layouManager);

    LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    headerPlaceHolder = inflater.inflate(R.layout.view_header_holder_medium, null, false);
    layouManager.addView(headerPlaceHolder, 0);

   ...
}

LayoutManagerবস্তুর স্বভাব হ্যান্ডলিং হবে বলে মনে হয় RecyclerViewআইটেম। যেহেতু আমি কোনও addHeaderView(View view)পদ্ধতি খুঁজে পাচ্ছি না , তাই আমি সিদ্ধান্ত নিয়েছি যে LayoutManagerএর addView(View view, int position)পদ্ধতিটি নিয়ে যাব এবং শিরোনাম হিসাবে কাজ করার জন্য প্রথম অবস্থানে আমার শিরোনামের ভিউ যুক্ত করব।

Aaand এই যেখানে জিনিস খারাপ হয়:

java.lang.NullPointerException: Attempt to read from field 'android.support.v7.widget.RecyclerView$ViewHolder android.support.v7.widget.RecyclerView$LayoutParams.mViewHolder' on a null object reference
    at android.support.v7.widget.RecyclerView.getChildViewHolderInt(RecyclerView.java:2497)
    at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:4807)
    at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:4803)
    at com.mathieumaree.showz.fragments.CategoryFragment.setRecyclerView(CategoryFragment.java:231)
    at com.mathieumaree.showz.fragments.CategoryFragment.access$200(CategoryFragment.java:47)
    at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.java:201)
    at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.java:196)
    at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:41)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5221)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

বিভিন্ন পাওয়ার পর NullPointerExceptionsকল করতে চাইছেন addView(View view)ভ্রমণ সৃষ্টি (এছাড়াও একবার সবকিছু সেট আপ করা হয়েছে এমনকি অ্যাডাপ্টার ডেটা দেখুন যোগ চেষ্টা) এর বিভিন্ন মুহূর্ত সময়ে বুঝলাম আমি কোন ধারণা যদি এই এটা করতে সঠিক উপায় আছে (এবং এটি হতে হবে না)।

PS: এছাড়াও, একটি সমাধান যা GridLayoutManagerসংযোজন ছাড়াও পরিচালনা LinearLayoutManagerকরতে পারে সত্যই প্রশংসা করা হবে!


একটি এই সময়ে কটাক্ষপাত stackoverflow.com/a/26573338/2127203
EC84B4

সমস্যা অ্যাডাপ্টারের কোডে। এর অর্থ, কোনওভাবে আপনি অনক্রিটভিউহোল্ডার ফাংশনে নাল দর্শনার্থীকে ফিরিয়ে দিচ্ছেন।
নিও

সেখানে একটি ভালো উপায় StaggeredGridLayout করার হেডার যোগ করার জন্য কিভাবে stackoverflow.com/questions/42202735/...
Aleksey Timoshchenko

উত্তর:


120

আমাকে আমার একটি ফুটার যুক্ত করতে হয়েছিল RecyclerViewএবং আমি এখানে আমার কোড স্নিপেট ভাগ করেছিলাম কারণ আমি ভেবেছিলাম এটি কার্যকর হতে পারে। সামগ্রিক প্রবাহটি আরও ভালভাবে বোঝার জন্য দয়া করে কোডের ভিতরে থাকা মন্তব্যগুলি দেখুন।

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

public class RecyclerViewWithFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int FOOTER_VIEW = 1;
    private ArrayList<String> data; // Take any list that matches your requirement.
    private Context context;

    // Define a constructor
    public RecyclerViewWithFooterAdapter(Context context, ArrayList<String> data) {
        this.context = context;
        this.data = data;
    }

    // Define a ViewHolder for Footer view
    public class FooterViewHolder extends ViewHolder {
        public FooterViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // Now define the ViewHolder for Normal list item
    public class NormalViewHolder extends ViewHolder {
        public NormalViewHolder(View itemView) {
            super(itemView);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the normal items
                }
            });
        }
    }

    // And now in onCreateViewHolder you have to pass the correct view
    // while populating the list item.

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v;

        if (viewType == FOOTER_VIEW) {
            v = LayoutInflater.from(context).inflate(R.layout.list_item_footer, parent, false);
            FooterViewHolder vh = new FooterViewHolder(v);
            return vh;
        }

        v = LayoutInflater.from(context).inflate(R.layout.list_item_normal, parent, false);

        NormalViewHolder vh = new NormalViewHolder(v);

        return vh;
    }

    // Now bind the ViewHolder in onBindViewHolder
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        try {
            if (holder instanceof NormalViewHolder) {
                NormalViewHolder vh = (NormalViewHolder) holder;

                vh.bindView(position);
            } else if (holder instanceof FooterViewHolder) {
                FooterViewHolder vh = (FooterViewHolder) holder;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Now the critical part. You have return the exact item count of your list
    // I've only one footer. So I returned data.size() + 1
    // If you've multiple headers and footers, you've to return total count
    // like, headers.size() + data.size() + footers.size()

    @Override
    public int getItemCount() {
        if (data == null) {
            return 0;
        }

        if (data.size() == 0) {
            //Return 1 here to show nothing
            return 1;
        }

        // Add extra view to show the footer view
        return data.size() + 1;
    }

    // Now define getItemViewType of your own.

    @Override
    public int getItemViewType(int position) {
        if (position == data.size()) {
            // This is where we'll add footer.
            return FOOTER_VIEW;
        }

        return super.getItemViewType(position);
    }

    // So you're done with adding a footer and its action on onClick.
    // Now set the default ViewHolder for NormalViewHolder

    public class ViewHolder extends RecyclerView.ViewHolder {
        // Define elements of a row here
        public ViewHolder(View itemView) {
            super(itemView);
            // Find view by ID and initialize here
        }

        public void bindView(int position) {
            // bindView() method to implement actions
        }
    }
}

উপরের কোড স্নিপেটটি একটি ফুটার যুক্ত করে RecyclerView। শিরোনাম এবং পাদচরণ উভয়ই সংযোজনের বাস্তবায়ন পরীক্ষা করার জন্য আপনি এই গিটহাব সংগ্রহস্থলটি পরীক্ষা করতে পারেন ।


2
ভাল কাজ করে. এটি সঠিক উত্তর হিসাবে চিহ্নিত করা উচিত।
নাগা মাললেশ মাদদালি

1
আমি ঠিক তাই করেছি। তবে আমি যদি আমার রিসাইক্লারভিউকে স্থির তালিকাটি মানিয়ে নিতে চাই? প্রথম উপাদান (শিরোনাম) পাশাপাশি স্তব্ধ হবে। :(
নিওন ওয়ার্জ

আপনি কীভাবে আপনার RecyclerViewগতিশীলভাবে পপুলেশন করতে পারেন সে সম্পর্কে এটি একটি টিউটোরিয়াল । আপনার প্রতিটি উপাদানের উপর আপনি নিয়ন্ত্রণ রাখতে পারেন RecyclerView। কোনও কার্যনির্বাহী প্রকল্পের জন্য কোড বিভাগটি দেখুন। আশা করি এটি সাহায্য করবে। github.com/comeondude/dynamic-recyclerview/wiki
রিয়াজ মোর্শেদ

2
int getItemViewType (int position)- দেখার পুনর্ব্যবহারের উদ্দেশ্যে অবস্থানটিতে আইটেমের দর্শন প্রকারটি ফিরিয়ে দিন। এই পদ্ধতির ডিফল্ট বাস্তবায়ন 0 প্রদান করে, অ্যাডাপ্টারের জন্য একক দর্শন প্রকারের অনুমান করে। ListViewঅ্যাডাপ্টারগুলির বিপরীতে , প্রকারগুলির সংগত হতে হবে না। আইটেম দর্শন প্রকারটি অনন্যভাবে চিহ্নিত করতে আইডি সংস্থানগুলি ব্যবহার করার বিষয়ে বিবেচনা করুন। - ডকুমেন্টেশন থেকে। বিকাশকারী.অ্যান্ড্রয়েড.
রিজ মুর্শেদ

3
লোকেরা সর্বদা যা করতে চায় তার জন্য এত বেশি ম্যানুয়াল কাজ। আমি এটি বিশ্বাস করতে পারি না ...
JohnyTex

28

সমাধান করা খুব সহজ !!

আমি অ্যাডাপ্টারের অভ্যন্তরে যুক্তিযুক্ত ধারণাটি অন্যরকম ভিউ টাইপ হিসাবে পছন্দ করি না কারণ প্রতিবার ভিউ ফেরানোর আগে ভিউ টাইপের জন্য এটি পরীক্ষা করে। সমাধানের নীচে অতিরিক্ত চেকগুলি এড়ানো হয়।

শুধু ভিতরে LinearLayout (উল্লম্ব) হেডার দৃশ্য + + recyclerview + + পাদচরণ দৃশ্য যোগ android.support.v4.widget.NestedScrollView

এটা দেখ:

 <android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

       <View
            android:id="@+id/header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layoutManager="LinearLayoutManager"/>

        <View
            android:id="@+id/footer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>

মসৃণ স্ক্রোলিংয়ের জন্য এই লাইন কোডটি যুক্ত করুন

RecyclerView v = (RecyclerView) findViewById(...);
v.setNestedScrollingEnabled(false);

এই সব আরভি কর্মক্ষমতা নষ্ট হয়ে যাবে এবং আরভি নির্বিশেষে সব দৃশ্য হোল্ডার খুঁজে রাখা চেষ্টা করবে layout_heightআরভি এর

ছোট আকারের তালিকার জন্য যেমন ন্যাভ ড্রয়ার বা সেটিংস ইত্যাদি ব্যবহার করার জন্য প্রস্তাবিত


1
আমার জন্য মোটামুটি সহজ কাজ করেছেন
হিতেশ সাহু

1
যখন শিরোনাম এবং পাদচরণ তালিকাগুলির মধ্যে পুনরাবৃত্তি না করতে হয় তখন একটি পুনর্ব্যবহারযোগ্য দৃশ্যে শিরোলেখ এবং পাদচরণ যুক্ত করার খুব সহজ উপায়।
ব্যবহারকারী 1841702

34
এটি সমস্ত সুবিধা হারাতে একটি খুব সহজ উপায় RecyclerView নিয়ে আসে - আপনি আসল পুনর্ব্যবহারযোগ্যতা এবং এটি যে অপ্টিমাইজেশন নিয়ে আসে তা হারাবেন।
মার্সিন কোজিস্কি

1
আমি এই কোডটি চেষ্টা করেছি, স্ক্রোলিং সঠিকভাবে কাজ করছে না ... এটি খুব ধীরে স্ক্রোলিং হয়ে গেছে .. plz পরামর্শ দেয় যে এর জন্য কিছু করতে পারত কিনা
নিভা জৈন

2
নেস্টেড স্ক্রোলভিউ ব্যবহার করা পুনর্ব্যবহারযোগ্য সমস্ত ভিউ ক্যাশে করে এবং পুনর্ব্যবহারকারী ভিউয়ের আকারটি আরও বেশি স্ক্রোলিং হয় এবং লোডিংয়ের সময় বৃদ্ধি পাবে। আমি এই কোডটি ব্যবহার না করার পরামর্শ দিচ্ছি
তুষার সাহা

25

ললিপপে আমার একই সমস্যা ছিল এবং Recyclerviewঅ্যাডাপ্টার মোড়ানোর জন্য দুটি পন্থা তৈরি করেছিলাম । একটি ব্যবহার করা বেশ সহজ, তবে এটি পরিবর্তনশীল ডেটাসেটের সাথে কীভাবে আচরণ করবে তা আমি নিশ্চিত নই। কারণ এটি আপনার অ্যাডাপ্টারটি আবৃত notifyDataSetChangedকরে এবং ডান অ্যাডাপ্টার-অবজেক্টের মতো পদ্ধতিগুলি কল করতে আপনার নিজেকে নিশ্চিত করতে হবে ।

অন্যটির মতো সমস্যা হওয়া উচিত নয়। কেবল আপনার নিয়মিত অ্যাডাপ্টারের ক্লাসটি বাড়ানো হোক, বিমূর্ত পদ্ধতিগুলি প্রয়োগ করুন এবং আপনার প্রস্তুত হওয়া উচিত। এবং তারা এখানে:

gists

HeaderRecyclerViewAdapterV1

import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

/**
 * Created by sebnapi on 08.11.14.
 * <p/>
 * This is a Plug-and-Play Approach for adding a Header or Footer to
 * a RecyclerView backed list
 * <p/>
 * Just wrap your regular adapter like this
 * <p/>
 * new HeaderRecyclerViewAdapterV1(new RegularAdapter())
 * <p/>
 * Let RegularAdapter implement HeaderRecyclerView, FooterRecyclerView or both
 * and you are ready to go.
 * <p/>
 * I'm absolutely not sure how this will behave with changes in the dataset.
 * You can always wrap a fresh adapter and make sure to not change the old one or
 * use my other approach.
 * <p/>
 * With the other approach you need to let your Adapter extend HeaderRecyclerViewAdapterV2
 * (and therefore change potentially more code) but possible omit these shortcomings.
 * <p/>
 * TOTALLY UNTESTED - USE WITH CARE - HAVE FUN :)
 */
public class HeaderRecyclerViewAdapterV1 extends RecyclerView.Adapter {
    private static final int TYPE_HEADER = Integer.MIN_VALUE;
    private static final int TYPE_FOOTER = Integer.MIN_VALUE + 1;
    private static final int TYPE_ADAPTEE_OFFSET = 2;

    private final RecyclerView.Adapter mAdaptee;


    public HeaderRecyclerViewAdapterV1(RecyclerView.Adapter adaptee) {
        mAdaptee = adaptee;
    }

    public RecyclerView.Adapter getAdaptee() {
        return mAdaptee;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_HEADER && mAdaptee instanceof HeaderRecyclerView) {
            return ((HeaderRecyclerView) mAdaptee).onCreateHeaderViewHolder(parent, viewType);
        } else if (viewType == TYPE_FOOTER && mAdaptee instanceof FooterRecyclerView) {
            return ((FooterRecyclerView) mAdaptee).onCreateFooterViewHolder(parent, viewType);
        }
        return mAdaptee.onCreateViewHolder(parent, viewType - TYPE_ADAPTEE_OFFSET);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (position == 0 && holder.getItemViewType() == TYPE_HEADER && useHeader()) {
            ((HeaderRecyclerView) mAdaptee).onBindHeaderView(holder, position);
        } else if (position == mAdaptee.getItemCount() && holder.getItemViewType() == TYPE_FOOTER && useFooter()) {
            ((FooterRecyclerView) mAdaptee).onBindFooterView(holder, position);
        } else {
            mAdaptee.onBindViewHolder(holder, position - (useHeader() ? 1 : 0));
        }
    }

    @Override
    public int getItemCount() {
        int itemCount = mAdaptee.getItemCount();
        if (useHeader()) {
            itemCount += 1;
        }
        if (useFooter()) {
            itemCount += 1;
        }
        return itemCount;
    }

    private boolean useHeader() {
        if (mAdaptee instanceof HeaderRecyclerView) {
            return true;
        }
        return false;
    }

    private boolean useFooter() {
        if (mAdaptee instanceof FooterRecyclerView) {
            return true;
        }
        return false;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0 && useHeader()) {
            return TYPE_HEADER;
        }
        if (position == mAdaptee.getItemCount() && useFooter()) {
            return TYPE_FOOTER;
        }
        if (mAdaptee.getItemCount() >= Integer.MAX_VALUE - TYPE_ADAPTEE_OFFSET) {
            new IllegalStateException("HeaderRecyclerViewAdapter offsets your BasicItemType by " + TYPE_ADAPTEE_OFFSET + ".");
        }
        return mAdaptee.getItemViewType(position) + TYPE_ADAPTEE_OFFSET;
    }


    public static interface HeaderRecyclerView {
        public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int viewType);

        public void onBindHeaderView(RecyclerView.ViewHolder holder, int position);
    }

    public static interface FooterRecyclerView {
        public RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType);

        public void onBindFooterView(RecyclerView.ViewHolder holder, int position);
    }

}

HeaderRecyclerViewAdapterV2

import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

/**
 * Created by sebnapi on 08.11.14.
 * <p/>
 * If you extend this Adapter you are able to add a Header, a Footer or both
 * by a similar ViewHolder pattern as in RecyclerView.
 * <p/>
 * If you want to omit changes to your class hierarchy you can try the Plug-and-Play
 * approach HeaderRecyclerViewAdapterV1.
 * <p/>
 * Don't override (Be careful while overriding)
 * - onCreateViewHolder
 * - onBindViewHolder
 * - getItemCount
 * - getItemViewType
 * <p/>
 * You need to override the abstract methods introduced by this class. This class
 * is not using generics as RecyclerView.Adapter make yourself sure to cast right.
 * <p/>
 * TOTALLY UNTESTED - USE WITH CARE - HAVE FUN :)
 */
public abstract class HeaderRecyclerViewAdapterV2 extends RecyclerView.Adapter {
    private static final int TYPE_HEADER = Integer.MIN_VALUE;
    private static final int TYPE_FOOTER = Integer.MIN_VALUE + 1;
    private static final int TYPE_ADAPTEE_OFFSET = 2;

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_HEADER) {
            return onCreateHeaderViewHolder(parent, viewType);
        } else if (viewType == TYPE_FOOTER) {
            return onCreateFooterViewHolder(parent, viewType);
        }
        return onCreateBasicItemViewHolder(parent, viewType - TYPE_ADAPTEE_OFFSET);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (position == 0 && holder.getItemViewType() == TYPE_HEADER) {
            onBindHeaderView(holder, position);
        } else if (position == getBasicItemCount() && holder.getItemViewType() == TYPE_FOOTER) {
            onBindFooterView(holder, position);
        } else {
            onBindBasicItemView(holder, position - (useHeader() ? 1 : 0));
        }
    }

    @Override
    public int getItemCount() {
        int itemCount = getBasicItemCount();
        if (useHeader()) {
            itemCount += 1;
        }
        if (useFooter()) {
            itemCount += 1;
        }
        return itemCount;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0 && useHeader()) {
            return TYPE_HEADER;
        }
        if (position == getBasicItemCount() && useFooter()) {
            return TYPE_FOOTER;
        }
        if (getBasicItemType(position) >= Integer.MAX_VALUE - TYPE_ADAPTEE_OFFSET) {
            new IllegalStateException("HeaderRecyclerViewAdapter offsets your BasicItemType by " + TYPE_ADAPTEE_OFFSET + ".");
        }
        return getBasicItemType(position) + TYPE_ADAPTEE_OFFSET;
    }

    public abstract boolean useHeader();

    public abstract RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int viewType);

    public abstract void onBindHeaderView(RecyclerView.ViewHolder holder, int position);

    public abstract boolean useFooter();

    public abstract RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType);

    public abstract void onBindFooterView(RecyclerView.ViewHolder holder, int position);

    public abstract RecyclerView.ViewHolder onCreateBasicItemViewHolder(ViewGroup parent, int viewType);

    public abstract void onBindBasicItemView(RecyclerView.ViewHolder holder, int position);

    public abstract int getBasicItemCount();

    /**
     * make sure you don't use [Integer.MAX_VALUE-1, Integer.MAX_VALUE] as BasicItemViewType
     *
     * @param position
     * @return
     */
    public abstract int getBasicItemType(int position);

}

মতামত এবং কাঁটাচামচ প্রশংসিত আমি HeaderRecyclerViewAdapterV2নিজের দ্বারা ব্যবহার করব এবং ভবিষ্যতে পরিবর্তনগুলি বিকাশ, পরীক্ষা ও পোস্ট করব।

সম্পাদনা : @ ওভিডিউল্যাটকু হ্যাঁ আমার কিছু সমস্যা ছিল। প্রকৃতপক্ষে আমি শিরোলেখ করেই শিরোলেখটিকে অফসেট করা বন্ধ করে দিয়েছিলাম position - (useHeader() ? 1 : 0)এবং এর পরিবর্তে এর int offsetPosition(int position)জন্য একটি সর্বজনীন পদ্ধতি তৈরি করেছি। কারণ আপনি যদি OnItemTouchListenerরিসাইক্লারভিউতে সেট করেন, আপনি স্পর্শটিকে বাধা দিতে পারবেন, স্পর্শের এক্স, ওয়াই সমন্বয় পেতে পারেন, সেই অনুযায়ী শিশু দৃষ্টিভঙ্গিটি খুঁজে পেতে পারেন এবং তারপরে কল করতে পারেন recyclerView.getChildPosition(...)এবং আপনি সবসময় অ্যাডাপ্টারে অ-অফসেট পজিশন পাবেন! এটি পুনর্ব্যবহারযোগ্য কোডের একটি সংক্ষিপ্তসার, এটি থেকে উত্তরণের কোনও সহজ পদ্ধতি আমি দেখছি না। এই কারণেই আমি এখন আমার নিজের কোড দ্বারা যখন প্রয়োজন তখন অবস্থানগুলি স্পষ্ট করে ফেলি ।


ভাল লাগছে! এটা নিয়ে কোন ঝামেলা হচ্ছে? না আমরা নিরাপদে এটি ব্যবহার করতে পারি? : ডি
ওভিদিউ লাটকু

1
@ ওভিডিউ লাটুকু পোস্টটি দেখুন
সেপ্টেম্বর

এই বাস্তবায়নগুলিতে, আপনি কি শিরোনাম এবং পাদচরণের সংখ্যা প্রতি 1 জন হিসাবে ধরে নিয়েছেন বলে মনে হয়?
রিসভহ্মজন

@seb সংস্করণ 2 কবজ মত কাজ করে !! অনিবার্ড ভিউহোল্ডার এবং আইটেমভিউ টাইপ পদ্ধতি উভয় ক্ষেত্রেই পাদলেখ পাওয়ার জন্য শর্ত হ'ল আমার যা প্রয়োজন তা পরিবর্তন করতে হবে। সমস্যাটি হ'ল আপনি যদি পজিশন == getBasicItemCount () ব্যবহার করে অবস্থানটি পান তবে এটি প্রকৃত শেষ অবস্থানের জন্য সত্য হবে না, তবে শেষ অবস্থান - ১। এটি ফুটারভিউ স্থাপন করে শেষ হয়েছে (নীচে নয়)। আমরা শর্তটি == getBasicItemCount () + 1 এ স্থান পরিবর্তন করে ঠিক করেছি এবং এটি দুর্দান্ত কাজ করেছে!
মর্মার

@ এসইবি সংস্করণ 2 এত দুর্দান্ত কাজ করে। অনেক ধন্যবাদ. আমি এটি ব্যবহার করছি আমার প্রস্তাবিত একটি ছোট জিনিস হ'ল ওভাররাইড ফাংশনটির জন্য 'চূড়ান্ত' কীওয়ার্ড যুক্ত করা।
রায়ানশাও

10

আমি এটি চেষ্টা করে দেখিনি, তবে আমি আপনার অ্যাডাপ্টারে getItemCount দ্বারা ফিরে আসা পূর্ণসংখ্যায় কেবল 1 (বা 2, যদি আপনি উভয় শিরোনাম এবং পাদচরণ চান) যুক্ত করতে চাই। তারপরে আপনি অন্য getItemViewTypeকোনও পূর্ণসংখ্যা ফেরত দিতে আপনার অ্যাডাপ্টারে ওভাররাইড করতে পারেন যখন i==0: https://developer.android.com/references/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)

createViewHolderতারপরে getItemViewTypeআপনি শিরোনাম দর্শনটির জন্য ভিউ হোল্ডারটিকে আলাদাভাবে কনফিগার করতে বা কনফিগার করার অনুমতি দিয়ে আপনি যে পূর্ণসংখ্যার কাছ থেকে ফিরে এসেছিলেন তা পেরিয়ে যায় : https://developer.android.com/references/android/support/v7/widget/RecyclerView.Adapter.html# তৈরি ভিউহোল্ডার (অ্যান্ড্রয়েড.ভিউ.ভিউ গ্রুপ , ইনট)

পূর্ণসংখ্যায় যে অবস্থানটি হয়েছে সেখান থেকে একটিকে বিয়োগ করতে ভুলবেন না bindViewHolder


আমি মনে করি না যে এটি পুনর্ব্যবহারের কাজ। পুনর্ব্যবহারযোগ্য কাজটি কেবলমাত্র দর্শনের পুনর্ব্যবহার করা। একটি শিরোনাম বা পাদলেখ প্রয়োগের সাথে একটি বিন্যাসের ব্যবস্থা লেখার উপায়।
IZI_Shadow_IZI

আপনার জবাবের জন্য @ ইয়ান নিউজনকে ধন্যবাদ। প্রথমত, এই সমাধানটি কাজ করছে বলে মনে হচ্ছে, এমনকি কেবল ব্যবহার করে getItemViewType(int position) { return position == 0 ? 0 : 1; }( RecyclerViewকোনও getViewTypeCount()পদ্ধতি নেই)। অন্যদিকে, আমি @ আইজিআইডিএসএডো_আইজিআইয়ের সাথে একমত নই, লেআউট ম্যানেজারটি এই ধরণের জিনিস পরিচালনা করা উচিত বলে আমার সত্যিই বোধ হয়। অন্য কোন ধারণা?
ম্যাথিউমারি

@ ভিউমা আপনি সম্ভবত দু'জনেই ঠিক আছেন তবে বর্তমানে কীভাবে তা করবেন তা আমি জানি না এবং আমার সমাধানটি কার্যকর হবে তা আমি যথেষ্ট নিশ্চিত ছিলাম। একটি সাবঅপটিমাল সলিউশন কোনও সমাধান ছাড়াই ভাল, যা আপনার আগে ছিল।
ইয়ান নিউজন

@ ভিউমা এছাড়াও, অ্যাডাপ্টারে শিরোনাম এবং পাদচরণ বিমূর্ত করার অর্থ এটি অনুরোধ করা লেআউট উভয় প্রকারের পরিচালনা করা উচিত, আপনার নিজের লেআউট ম্যানেজার লেখার অর্থ উভয় প্রকার বিন্যাসের জন্য পুনরায় বাস্তবায়ন করা।
ইয়ান নিউজন

কেবলমাত্র একটি অ্যাডাপ্টার তৈরি করুন যা কোনও অ্যাডাপ্টারকে জড়িয়ে রাখে এবং সূচক 0 এ শিরোনাম দর্শনটির জন্য সমর্থন যুক্ত করে list
ইজিগিত

9

আপনি এই গিটহাব লাইব্রেরিটি আপনার পুনর্ব্যবহারযোগ্য ভিউতে শিরোনাম এবং / বা পাদচরণ যুক্ত করার অনুমতি দিয়ে ব্যবহার করতে পারেন সবচেয়ে সহজতম উপায়ে ।

আপনার প্রকল্পে আপনাকে HFRecyclerView লাইব্রেরি যুক্ত করতে হবে বা আপনি এটিকে গ্রেডল থেকেও ধরে নিতে পারেন:

compile 'com.mikhaellopez:hfrecyclerview:1.0.0'

এটি চিত্রের ফলাফল:

পূর্বরূপ

সম্পাদনা করুন:

আপনি যদি এই লাইব্রেরির সাথে শীর্ষে এবং / অথবা নীচে কেবল একটি মার্জিন যুক্ত করতে চান: সিম্পল আইটেম সজ্জা :

int offsetPx = 10;
recyclerView.addItemDecoration(new StartOffsetItemDecoration(offsetPx));
recyclerView.addItemDecoration(new EndOffsetItemDecoration(offsetPx));

এই লাইব্রেরিটি লিনিয়ারলআউটআউট ম্যানেজারের শিরোনামে সঠিকভাবে ভিউ যুক্ত করে তবে আমি গ্রিডলাউটম্যানেজারে শিরোনাম হিসাবে ভিউ সেট করতে চাই, যা স্ক্রিনের পুরো প্রস্থে স্থান নেয়। এই লাইব্রেরি দিয়ে কি এটা সম্ভব?
ved

না, এই গ্রন্থাগারটি আপনাকে পুনর্ব্যবহারযোগ্য (রিসাইক্লারভিউ.আডাপ্টার) এ পুনর্ব্যবহারযোগ্য ভিউয়ের প্রথম এবং শেষ উপাদানটি পরিবর্তন করতে দেয়। আপনি কোনও সমস্যা ছাড়াই গ্রিডভিউতে এই অ্যাডাপ্টারটি ব্যবহার করতে পারেন। সুতরাং আমি মনে করি এই গ্রন্থাগারটি আপনি যা চান তা করতে দেয়।
lopez.mikhael

6

অন্য কোনও অ্যাডাপ্টার মোড়ানোর জন্য এবং শিরোনাম এবং পাদচরণ দর্শন যোগ করার পদ্ধতি সরবরাহ করার জন্য আমি আমার নিজের অ্যাডাপ্টারটি বাস্তবায়ন করেছি।

এখানে একটি টুকরো তৈরি করেছেন: HeaderViewRecyclerAdapter.java

প্রধান বৈশিষ্ট্য আমি চেয়েছিলেন একটি ListView একটি অনুরূপ ইন্টারফেসের ছিল, তাই আমি আমার অসম্পূর্ণ অংশ কতবার দেখা হয়েছে ফোলান এবং তাদের যুক্ত করতে সক্ষম হতে চেয়েছিলেন RecyclerViewমধ্যে onCreateView। এটি HeaderViewRecyclerAdapterমোড়ানো হওয়ার জন্য একটি পাসিং অ্যাডাপ্টার তৈরি করে addHeaderViewএবং addFooterViewআপনার স্ফীত দর্শনগুলি কল করে এবং পাস করার মাধ্যমে এটি করা হয়। তারপরে HeaderViewRecyclerAdapterউদাহরণটি অ্যাডাপ্টার হিসাবে সেট করুনRecyclerView

একটি অতিরিক্ত প্রয়োজন হ'ল শিরোনাম এবং পাদটীকাগুলি রাখার সময় আমাকে সহজেই অ্যাডাপ্টারগুলি অদলবদল করতে সক্ষম হওয়া দরকার ছিল, আমি এই শিরোনাম এবং পাদচরণের একাধিক উদাহরণ সহ একাধিক অ্যাডাপ্টার রাখতে চাইনি। সুতরাং আপনি setAdapterশিরোনাম এবং পাদচরণ অক্ষত রেখে মোড়ানো অ্যাডাপ্টার পরিবর্তন করতে কল করতে পারেন , পরিবর্তনের বিষয়ে RecyclerViewঅবহিত হওয়ার পরে।


3

আমার "এটিকে সহজ বোকা রাখুন" উপায় ... এটি কিছু সংস্থান নষ্ট করে, আমি জানি, তবে আমার কোডটি সহজ রাখায় তাই আমি যত্নবান নই ... 1) দৃশ্যমানতার সাথে পাদলেখ যুক্ত করুন আপনার আইটেম_বলে

  <LinearLayout
        android:id="@+id/footer"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="vertical"
        android:visibility="gone">
    </LinearLayout>

2) তারপরে এটি শেষ আইটেমটিতে দৃশ্যমান সেট করুন

public void onBindViewHolder(ChannelAdapter.MyViewHolder holder, int position) {
        boolean last = position==data.size()-1;
        //....
        holder.footer.setVisibility(View.GONE);
        if (last && showFooter){
            holder.footer.setVisibility(View.VISIBLE);
        }
    }

শিরোলেখ জন্য বিপরীত কাজ


1

@ এসইবির সমাধানের ভিত্তিতে, আমি রিসাইক্লারভিউ.এডাপ্টারের একটি সাবক্লাস তৈরি করেছি যা একটি নির্বিচার সংখ্যক শিরোলেখ এবং পাদচরণকে সমর্থন করে।

https://gist.github.com/mheras/0908873267def75dc746

যদিও এটি একটি সমাধান বলে মনে হচ্ছে, আমি এই জিনিসটি লেআউটম্যানেজারের দ্বারা পরিচালিত হওয়া উচিত বলেও মনে করি। দুর্ভাগ্যক্রমে, আমার এখন এটি প্রয়োজন এবং আমার স্ক্র্যাচ থেকে কোনও স্ট্যাগার্ডার্ড গ্রিডলআউটআউট ম্যানেজার কার্যকর করার সময় নেই (এমনকি এটি থেকে প্রসারিতও হবে না)।

আমি এখনও এটি পরীক্ষা করছি, তবে আপনি চাইলে এটি চেষ্টা করে দেখতে পারেন। আপনি যদি এটির সাথে কোনও সমস্যা পান তবে আমাকে জানান।


1

আপনি এই সমস্যাটি সমাধান করতে ভিউ টাইপ ব্যবহার করতে পারেন, এখানে আমার ডেমো: https://github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView

  1. আপনি কিছু পুনর্ব্যক্তিক প্রদর্শন প্রদর্শন মোড সংজ্ঞায়িত করতে পারেন:

    সর্বজনীন স্ট্যাটিক চূড়ান্ত INMODE_DATA = 0, MODE_LOADING = 1, MODE_ERROR = 2, MODE_EMPTY = 3, MODE_HEADER_VIEW = 4, MODE_FOOTER_VIEW = 5;

2. getItemViewType মথডকে ওভাররাইড করুন

 @Override
public int getItemViewType(int position) {
    if (mMode == RecyclerViewMode.MODE_LOADING) {
        return RecyclerViewMode.MODE_LOADING;
    }
    if (mMode == RecyclerViewMode.MODE_ERROR) {
        return RecyclerViewMode.MODE_ERROR;
    }
    if (mMode == RecyclerViewMode.MODE_EMPTY) {
        return RecyclerViewMode.MODE_EMPTY;
    }
    //check what type our position is, based on the assumption that the order is headers > items > footers
    if (position < mHeaders.size()) {
        return RecyclerViewMode.MODE_HEADER_VIEW;
    } else if (position >= mHeaders.size() + mData.size()) {
        return RecyclerViewMode.MODE_FOOTER_VIEW;
    }
    return RecyclerViewMode.MODE_DATA;
}

3. getItemCount পদ্ধতিটি ওভাররাইড করুন

@Override
public int getItemCount() {
    if (mMode == RecyclerViewMode.MODE_DATA) {
        return mData.size() + mHeaders.size() + mFooters.size();
    } else {
        return 1;
    }
}

4.আরক্রিটভিউহোল্ডার পদ্ধতিটি ওভাররাইড করুন। ভিউ টাইপ করে ভিউ ধারক তৈরি করুন

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == RecyclerViewMode.MODE_LOADING) {
        RecyclerView.ViewHolder loadingViewHolder = onCreateLoadingViewHolder(parent);
        loadingViewHolder.itemView.setLayoutParams(
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
        );
        return loadingViewHolder;
    }
    if (viewType == RecyclerViewMode.MODE_ERROR) {
        RecyclerView.ViewHolder errorViewHolder = onCreateErrorViewHolder(parent);
        errorViewHolder.itemView.setLayoutParams(
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
        );
        errorViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (null != mOnErrorViewClickListener) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mOnErrorViewClickListener.onErrorViewClick(v);
                        }
                    }, 200);
                }
            }
        });
        return errorViewHolder;
    }
    if (viewType == RecyclerViewMode.MODE_EMPTY) {
        RecyclerView.ViewHolder emptyViewHolder = onCreateEmptyViewHolder(parent);
        emptyViewHolder.itemView.setLayoutParams(
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
        );
        emptyViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (null != mOnEmptyViewClickListener) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mOnEmptyViewClickListener.onEmptyViewClick(v);
                        }
                    }, 200);
                }
            }
        });
        return emptyViewHolder;
    }
    if (viewType == RecyclerViewMode.MODE_HEADER_VIEW) {
        RecyclerView.ViewHolder headerViewHolder = onCreateHeaderViewHolder(parent);
        headerViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (null != mOnHeaderViewClickListener) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mOnHeaderViewClickListener.onHeaderViewClick(v, v.getTag());
                        }
                    }, 200);
                }
            }
        });
        return headerViewHolder;
    }
    if (viewType == RecyclerViewMode.MODE_FOOTER_VIEW) {
        RecyclerView.ViewHolder footerViewHolder = onCreateFooterViewHolder(parent);
        footerViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (null != mOnFooterViewClickListener) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mOnFooterViewClickListener.onFooterViewClick(v, v.getTag());
                        }
                    }, 200);
                }
            }
        });
        return footerViewHolder;
    }
    RecyclerView.ViewHolder dataViewHolder = onCreateDataViewHolder(parent);
    dataViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            if (null != mOnItemClickListener) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mOnItemClickListener.onItemClick(v, v.getTag());
                    }
                }, 200);
            }
        }
    });
    dataViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(final View v) {
            if (null != mOnItemLongClickListener) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mOnItemLongClickListener.onItemLongClick(v, v.getTag());
                    }
                }, 200);
                return true;
            }
            return false;
        }
    });
    return dataViewHolder;
}

অনবিন্দ ভিউহোল্ডার পদ্ধতিটি ওভাররাইড করুন। ভিউ টাইপ করে ডেটা বাঁধুন

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (mMode == RecyclerViewMode.MODE_LOADING) {
        onBindLoadingViewHolder(holder, position);
    } else if (mMode == RecyclerViewMode.MODE_ERROR) {
        onBindErrorViewHolder(holder, position);
    } else if (mMode == RecyclerViewMode.MODE_EMPTY) {
        onBindEmptyViewHolder(holder, position);
    } else {
        if (position < mHeaders.size()) {
            if (mHeaders.size() > 0) {
                onBindHeaderViewHolder(holder, position);
            }
        } else if (position >= mHeaders.size() + mData.size()) {
            if (mFooters.size() > 0) {
                onBindFooterViewHolder(holder, position - mHeaders.size() - mData.size());
            }
        } else {
            onBindDataViewHolder(holder, position - mHeaders.size());
        }
    }
}

ভবিষ্যতে যদি আপনার লিঙ্কটি ভেঙে যায়?
গোপাল সিং সিরভি


1

আপনার আইটেমগুলিকে বিভাগগুলিতে গোষ্ঠীবদ্ধ করতে এবং নীচের চিত্রের মতো প্রতিটি বিভাগে একটি শিরোনাম যুক্ত করতে আপনি লাইব্রেরিটি সেকশনেড রেসাইক্লার ভিউ অ্যাডাপ্টার ব্যবহার করতে পারেন :

এখানে চিত্র বর্ণনা লিখুন

প্রথমে আপনি আপনার বিভাগ শ্রেণি তৈরি করুন:

class MySection extends StatelessSection {

    String title;
    List<String> list;

    public MySection(String title, List<String> list) {
        // call constructor with layout resources for this Section header, footer and items 
        super(R.layout.section_header, R.layout.section_item);

        this.title = title;
        this.list = list;
    }

    @Override
    public int getContentItemsTotal() {
        return list.size(); // number of items of this section
    }

    @Override
    public RecyclerView.ViewHolder getItemViewHolder(View view) {
        // return a custom instance of ViewHolder for the items of this section
        return new MyItemViewHolder(view);
    }

    @Override
    public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyItemViewHolder itemHolder = (MyItemViewHolder) holder;

        // bind your view here
        itemHolder.tvItem.setText(list.get(position));
    }

    @Override
    public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
        return new SimpleHeaderViewHolder(view);
    }

    @Override
    public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
        MyHeaderViewHolder headerHolder = (MyHeaderViewHolder) holder;

        // bind your header view here
        headerHolder.tvItem.setText(title);
    }
}

তারপরে আপনি আপনার বিভাগগুলি সহ পুনর্ব্যবহারযোগ্য সেট আপ করুন এবং গ্রিডলআউটম্যানেজারের সাহায্যে শিরোনামগুলির স্প্যানসাইজ পরিবর্তন করুন:

// Create an instance of SectionedRecyclerViewAdapter 
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();

// Create your sections with the list of data
MySection section1 = new MySection("My Section 1 title", dataList1);
MySection section2 = new MySection("My Section 2 title", dataList2);

// Add your Sections to the adapter
sectionAdapter.addSection(section1);
sectionAdapter.addSection(section2);

// Set up a GridLayoutManager to change the SpanSize of the header
GridLayoutManager glm = new GridLayoutManager(getContext(), 2);
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override
    public int getSpanSize(int position) {
        switch(sectionAdapter.getSectionItemViewType(position)) {
            case SectionedRecyclerViewAdapter.VIEW_TYPE_HEADER:
                return 2;
            default:
                return 1;
        }
    }
});

// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(glm);
recyclerView.setAdapter(sectionAdapter);

0

আমি জানি আমি দেরি করে এসেছি, তবে কেবলমাত্র আমি অ্যাডাপ্টারে এই জাতীয় "অ্যাডহিডার" প্রয়োগ করতে সক্ষম হয়েছি। আমার ফ্লেক্সিবলএডাপ্টার প্রকল্পে আপনি setHeaderএকটি বিভাগযোগ্য আইটেমে কল করতে পারেন , তারপরে আপনি কল করতে পারেনshowAllHeaders । আপনার যদি কেবল 1 টি হেডার প্রয়োজন হয় তবে প্রথম আইটেমটিতে শিরোনাম থাকা উচিত। আপনি যদি এই আইটেমটি মুছে ফেলেন, তবে শিরোনামটি স্বয়ংক্রিয়ভাবে পরবর্তীটির সাথে লিঙ্ক হবে।

দুর্ভাগ্যক্রমে পাদচরণগুলি কভার করা হয়নি (এখনও)।

ফ্লেক্সিবলএডাপ্টার আপনাকে শিরোনাম / বিভাগ তৈরি করার চেয়ে অনেক বেশি কিছু করতে দেয়। আপনার সত্যিই নজর দেওয়া উচিত: https://github.com/davideas/FlexibleAdapter


0

আমি কেবলমাত্র সেই সমস্ত HeaderRecyclerViewAdapter বাস্তবায়নের একটি বিকল্প যুক্ত করব। CompoundAdapter:

https://github.com/negusoft/CompoundAdapter-android

এটি একটি আরও নমনীয় পদ্ধতি, যেহেতু আপনি অ্যাডাপ্টারগুলির বাইরে একটি অ্যাডাপ্টার গ্রুপ তৈরি করতে পারেন। শিরোনামের উদাহরণের জন্য, আপনার অ্যাডাপ্টারটি যেমন হয় তেমন ব্যবহার করুন, শিরোনামের জন্য একটি আইটেম যুক্ত অ্যাডাপ্টারের সাথে:

AdapterGroup adapterGroup = new AdapterGroup();
adapterGroup.addAdapter(SingleAdapter.create(R.layout.header));
adapterGroup.addAdapter(new MyAdapter(...));

recyclerView.setAdapter(adapterGroup);

এটি মোটামুটি সহজ এবং পঠনযোগ্য। আপনি একই নীতিটি ব্যবহার করে আরও জটিল অ্যাডাপ্টারটি সহজেই প্রয়োগ করতে পারেন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.