এখানে আমি বাহ্যিক গ্রন্থাগার ছাড়া এটি কীভাবে করব তা ব্যাখ্যা করব। এটি একটি দীর্ঘ পোস্ট হবে, তাই নিজেকে বন্ধনী।
সবার আগে, আমি @ টিম.পেটজকে স্বীকৃতি দিই যার পোস্ট আমাকে ItemDecoration
এস ব্যবহার করে আমার নিজের স্টিকি হেডারগুলি বাস্তবায়নের পথে যাত্রা করতে অনুপ্রাণিত করেছিল । আমি আমার প্রয়োগে তার কোডের কিছু অংশ ধার নিয়েছি।
আপনি ইতিমধ্যে অভিজ্ঞ হয়ে থাকতে পারেন, আপনি যদি নিজে এটি করার চেষ্টা করে থাকেন তবে কৌশলটি দিয়ে বাস্তবে এটি করার জন্য HOW এর একটি ভাল ব্যাখ্যা পাওয়া খুব কঠিন ItemDecoration
। মানে, পদক্ষেপগুলি কী? এর পিছনে যুক্তি কী? আমি কীভাবে তালিকার শীর্ষে হেডারটিকে স্টিক করব? এই প্রশ্নের উত্তর না জেনে হ'ল অন্যরা বাহ্যিক গ্রন্থাগারগুলি ব্যবহার করতে বাধ্য করে, যখন এটির ব্যবহারের সাথে নিজেই এটি ItemDecoration
করা খুব সহজ।
প্রাথমিক শর্তাবলি
- আপনার ডেটাসেটটি
list
বিভিন্ন ধরণের আইটেমের হওয়া উচিত ("জাভা ধরণের" অর্থে নয়, তবে "শিরোনাম / আইটেম" ধরণের অর্থে)।
- আপনার তালিকাটি ইতিমধ্যে বাছাই করা উচিত।
- তালিকার প্রতিটি আইটেম নির্দিষ্ট ধরণের হওয়া উচিত - এর সাথে সম্পর্কিত একটি শিরোনাম আইটেম থাকতে হবে।
- খুব প্রথম আইটেমটি
list
অবশ্যই একটি শিরোনামের আইটেম হতে হবে।
এখানে আমি আমার জন্য পূর্ণ কোড প্রদান RecyclerView.ItemDecoration
নামক HeaderItemDecoration
। তারপরে আমি নেওয়া পদক্ষেপগুলি বিস্তারিতভাবে বর্ণনা করি।
public class HeaderItemDecoration extends RecyclerView.ItemDecoration {
private StickyHeaderInterface mListener;
private int mStickyHeaderHeight;
public HeaderItemDecoration(RecyclerView recyclerView, @NonNull StickyHeaderInterface listener) {
mListener = listener;
// On Sticky Header Click
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
if (motionEvent.getY() <= mStickyHeaderHeight) {
// Handle the clicks on the header here ...
return true;
}
return false;
}
public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
}
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
View topChild = parent.getChildAt(0);
if (Util.isNull(topChild)) {
return;
}
int topChildPosition = parent.getChildAdapterPosition(topChild);
if (topChildPosition == RecyclerView.NO_POSITION) {
return;
}
View currentHeader = getHeaderViewForItem(topChildPosition, parent);
fixLayoutSize(parent, currentHeader);
int contactPoint = currentHeader.getBottom();
View childInContact = getChildInContact(parent, contactPoint);
if (Util.isNull(childInContact)) {
return;
}
if (mListener.isHeader(parent.getChildAdapterPosition(childInContact))) {
moveHeader(c, currentHeader, childInContact);
return;
}
drawHeader(c, currentHeader);
}
private View getHeaderViewForItem(int itemPosition, RecyclerView parent) {
int headerPosition = mListener.getHeaderPositionForItem(itemPosition);
int layoutResId = mListener.getHeaderLayout(headerPosition);
View header = LayoutInflater.from(parent.getContext()).inflate(layoutResId, parent, false);
mListener.bindHeaderData(header, headerPosition);
return header;
}
private void drawHeader(Canvas c, View header) {
c.save();
c.translate(0, 0);
header.draw(c);
c.restore();
}
private void moveHeader(Canvas c, View currentHeader, View nextHeader) {
c.save();
c.translate(0, nextHeader.getTop() - currentHeader.getHeight());
currentHeader.draw(c);
c.restore();
}
private View getChildInContact(RecyclerView parent, int contactPoint) {
View childInContact = null;
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);
if (child.getBottom() > contactPoint) {
if (child.getTop() <= contactPoint) {
// This child overlaps the contactPoint
childInContact = child;
break;
}
}
}
return childInContact;
}
/**
* Properly measures and layouts the top sticky header.
* @param parent ViewGroup: RecyclerView in this case.
*/
private void fixLayoutSize(ViewGroup parent, View view) {
// Specs for parent (RecyclerView)
int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
// Specs for children (headers)
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), view.getLayoutParams().width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), view.getLayoutParams().height);
view.measure(childWidthSpec, childHeightSpec);
view.layout(0, 0, view.getMeasuredWidth(), mStickyHeaderHeight = view.getMeasuredHeight());
}
public interface StickyHeaderInterface {
/**
* This method gets called by {@link HeaderItemDecoration} to fetch the position of the header item in the adapter
* that is used for (represents) item at specified position.
* @param itemPosition int. Adapter's position of the item for which to do the search of the position of the header item.
* @return int. Position of the header item in the adapter.
*/
int getHeaderPositionForItem(int itemPosition);
/**
* This method gets called by {@link HeaderItemDecoration} to get layout resource id for the header item at specified adapter's position.
* @param headerPosition int. Position of the header item in the adapter.
* @return int. Layout resource id.
*/
int getHeaderLayout(int headerPosition);
/**
* This method gets called by {@link HeaderItemDecoration} to setup the header View.
* @param header View. Header to set the data on.
* @param headerPosition int. Position of the header item in the adapter.
*/
void bindHeaderData(View header, int headerPosition);
/**
* This method gets called by {@link HeaderItemDecoration} to verify whether the item represents a header.
* @param itemPosition int.
* @return true, if item at the specified adapter's position represents a header.
*/
boolean isHeader(int itemPosition);
}
}
ব্যবসায়িক যুক্তি
তো, আমি কীভাবে এটি স্টিক করব?
আপনি না। আপনি RecyclerView
নিজের পছন্দের কোনও আইটেমটি কেবল থামাতে এবং উপরে আটকে রাখতে পারবেন না , যদি না আপনি কাস্টম লেআউটের গুরু হন এবং আপনি RecyclerView
হৃদয় দিয়ে 12,000+ কোডের লাইন জানেন না । সুতরাং, যেমন এটি সর্বদা ইউআই ডিজাইনের সাথে চলে আপনি যদি কিছু তৈরি করতে না পারেন তবে এটি জাল করুন। আপনি ব্যবহার করে সমস্ত কিছুর শীর্ষে শিরোনাম আঁকুনCanvas
। এই মুহুর্তে ব্যবহারকারী কোন আইটেম দেখতে পাবে তাও আপনার জানা উচিত। এটি কেবল ঘটে যায়, যা ItemDecoration
আপনাকে Canvas
দৃশ্যমান আইটেমগুলি এবং উভয়ই তথ্য সরবরাহ করতে পারে । এটির সাথে, এখানে প্রাথমিক পদক্ষেপগুলি রয়েছে:
ইন onDrawOver
পদ্ধতি RecyclerView.ItemDecoration
প্রথম (শীর্ষ) আইটেমটি ব্যবহারকারীকে কাছে দৃশ্যমান পেতে।
View topChild = parent.getChildAt(0);
কোন শিরোলেখ এটি উপস্থাপন করে তা নির্ধারণ করুন।
int topChildPosition = parent.getChildAdapterPosition(topChild);
View currentHeader = getHeaderViewForItem(topChildPosition, parent);
drawHeader()
পদ্ধতিটি ব্যবহার করে পুনর্ব্যবহারযোগ্যের শীর্ষে যথাযথ শিরোনাম আঁকুন ।
নতুন আসন্ন শিরোনাম যখন শীর্ষের সাথে মিলিত হয় তখন আমি আচরণটিও বাস্তবায়িত করতে চাই: আসন্ন শিরোনামটি আস্তে আস্তে শীর্ষের বর্তমান শিরোনামটিকে দৃষ্টিতে বাইরে ঠেলে দেয় এবং অবশেষে তার স্থান নেয় takes
"সমস্ত কিছুর উপরে অঙ্কন" করার একই কৌশল এখানে প্রয়োগ হয়।
শীর্ষ "আটকে থাকা" শিরোনামটি নতুন আসন্নটির সাথে মিলবে কিনা তা নির্ধারণ করুন।
View childInContact = getChildInContact(parent, contactPoint);
এই যোগাযোগের বিন্দুটি পান (এটি আপনার স্ট্রিচের শীর্ষের নীচের অংশ এবং আসন্ন শিরোনামের শীর্ষে)।
int contactPoint = currentHeader.getBottom();
তালিকার আইটেমটি যদি এই "যোগাযোগের বিন্দু "টিকে ত্রুটিযুক্ত করে তোলে তবে আপনার স্টিকি হেডারটি আবার আঁকুন যাতে এর নীচের অংশটি দোষী আইটেমের শীর্ষে থাকবে। আপনি translate()
পদ্ধতিতে এটি অর্জন Canvas
। ফলস্বরূপ, শীর্ষ শিরোলেখার শুরুর পয়েন্টটি দৃশ্যমান ক্ষেত্রের বাইরে চলে যাবে এবং এটি "আসন্ন শিরোনাম দ্বারা ধাক্কা মেরে ফেলেছে" বলে মনে হবে। এটি সম্পূর্ণরূপে শেষ হয়ে গেলে, শীর্ষে নতুন শিরোনামটি আঁকুন।
if (childInContact != null) {
if (mListener.isHeader(parent.getChildAdapterPosition(childInContact))) {
moveHeader(c, currentHeader, childInContact);
} else {
drawHeader(c, currentHeader);
}
}
বাকিটি আমার দেওয়া কোডের টুকরোটিতে মন্তব্য এবং পুঙ্খানুপুঙ্খ টিকা দ্বারা ব্যাখ্যা করা হয়েছে।
ব্যবহারটি সরাসরি এগিয়ে রয়েছে:
mRecyclerView.addItemDecoration(new HeaderItemDecoration((HeaderItemDecoration.StickyHeaderInterface) mAdapter));
এটি কাজ করার জন্য আপনার mAdapter
অবশ্যই প্রয়োগ StickyHeaderInterface
করতে হবে। বাস্তবায়ন আপনার থাকা ডেটার উপর নির্ভর করে।
অবশেষে, আমি এখানে অর্ধ-স্বচ্ছ শিরোনাম সহ একটি জিআইএফ সরবরাহ করি, যাতে আপনি ধারণাটি উপলব্ধি করতে পারেন এবং প্রকৃতপক্ষে হুডের নীচে কী চলছে তা দেখতে পারেন।
এখানে "সমস্ত কিছুর উপরে কেবল আঁকুন" ধারণাটির চিত্রণ দেওয়া আছে। আপনি দেখতে পাচ্ছেন যে দুটি শিরোনাম রয়েছে "শিরোনাম 1" - একটি যা আমরা আঁকতে পারি এবং আটকে থাকা অবস্থায় শীর্ষে থাকি এবং অন্যটি যা ডেটাসেট থেকে আসে এবং বাকী সমস্ত আইটেম নিয়ে চলে যায়। ব্যবহারকারী এর অভ্যন্তরীণ কাজগুলি দেখতে পাবে না, কারণ আপনার অর্ধেক স্বচ্ছ শিরোনাম পাবেন না।
এবং এখানে "পুশিং আউট" পর্বে কী ঘটে:
আশা করি এটি সাহায্য করেছে।
সম্পাদন করা
আমার getHeaderPositionForItem()
পুনর্ব্যবহারযোগ্য ভিউয়ের অ্যাডাপ্টারে পদ্ধতিটির আসল বাস্তবায়ন এখানে রয়েছে :
@Override
public int getHeaderPositionForItem(int itemPosition) {
int headerPosition = 0;
do {
if (this.isHeader(itemPosition)) {
headerPosition = itemPosition;
break;
}
itemPosition -= 1;
} while (itemPosition >= 0);
return headerPosition;
}
কোটলিনে কিছুটা ভিন্ন বাস্তবায়ন