আমি কীভাবে একটি WY_CONTENT একটি পুনর্ব্যবহারযোগ্য ভিউতে কাজ করব


181

আমার একটি DialogFragmentরয়েছে যা একটি RecyclerView(কার্ডের একটি তালিকা) ধারণ করে ।

এর মধ্যে RecyclerViewএক বা একাধিক CardViewsযে কোনও উচ্চতা থাকতে পারে।

এর মধ্যে থাকা DialogFragmentএইগুলির উপর ভিত্তি করে আমি এটি সঠিক উচ্চতা দিতে চাই CardViews

সাধারণত এটি সহজ হবে, আমি এই মত সেট wrap_contentকরা হবে RecyclerView

<android.support.v7.widget.RecyclerView ...
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"   
    android:clickable="true"   
    android:scrollbars="vertical" >

</android.support.v7.widget.RecyclerView>

যেহেতু আমি এটি ব্যবহার করছি RecyclerViewএটি কাজ করে না দেখুন:

https://issuetracker.google.com/issues/37001674

এবং

নেস্টেড রিসাইক্লার দেখার উচ্চতা এর সামগ্রীগুলিকে মোড়া করে না

এই উভয় পৃষ্ঠায় লোকেরা প্রসারিত LinearLayoutManagerএবং ওভাররাইড করার পরামর্শ দেয়onMeasure()

আমি প্রথম লেআউট ম্যানেজারটি ব্যবহার করেছি যা কেউ প্রথম লিঙ্কটিতে সরবরাহ করেছে:

public static class WrappingLayoutManager extends LinearLayoutManager {

        public WrappingLayoutManager(Context context) {
            super(context);
        }

        private int[] mMeasuredDimension = new int[2];

        @Override
        public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                              int widthSpec, int heightSpec) {
            final int widthMode = View.MeasureSpec.getMode(widthSpec);
            final int heightMode = View.MeasureSpec.getMode(heightSpec);
            final int widthSize = View.MeasureSpec.getSize(widthSpec);
            final int heightSize = View.MeasureSpec.getSize(heightSpec);

            measureScrapChild(recycler, 0,
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);

            int width = mMeasuredDimension[0];
            int height = mMeasuredDimension[1];

            switch (widthMode) {
                case View.MeasureSpec.EXACTLY:
                case View.MeasureSpec.AT_MOST:
                    width = widthSize;
                    break;
                case View.MeasureSpec.UNSPECIFIED:
            }

            switch (heightMode) {
                case View.MeasureSpec.EXACTLY:
                case View.MeasureSpec.AT_MOST:
                    height = heightSize;
                    break;
                case View.MeasureSpec.UNSPECIFIED:
            }

            setMeasuredDimension(width, height);
        }

        private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                       int heightSpec, int[] measuredDimension) {
            View view = recycler.getViewForPosition(position);
            if (view != null) {
                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
                int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                        getPaddingLeft() + getPaddingRight(), p.width);
                int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                        getPaddingTop() + getPaddingBottom(), p.height);
                view.measure(childWidthSpec, childHeightSpec);
                measuredDimension[0] = view.getMeasuredWidth();
                measuredDimension[1] = view.getMeasuredHeight();
                recycler.recycleView(view);
            }
        }
    }

তবে এটি কাজ করে না কারণ

heightSize = View.MeasureSpec.getSize(heightSpec);

সম্পর্কিত হতে পারে এমন একটি খুব বড় মান প্রদান করে match_parent

মন্তব্য করে height = heightSize;(দ্বিতীয় স্যুইচ ক্ষেত্রে) আমি উচ্চতার কাজটি পরিচালনা করতে পেরেছি তবে কেবলমাত্র যদি TextViewসেই অভ্যন্তরের কোনও শিশু CardViewতার নিজস্ব পাঠ্য (দীর্ঘ বাক্য) না জড়িয়ে রাখে।

এটি TextViewমোড়ানোর সাথে সাথে এটির নিজস্ব পাঠ্যটি উচ্চতা বাড়ানো উচিত তবে তা হয় না। এটি একটি দীর্ঘ রেখার জন্য উচ্চতা গণনা করেছে একটি একক লাইন হিসাবে, একটি মোড়ানো রেখা (2 বা তার বেশি) নয়।

কিভাবে আমি এই উন্নতি করবে কোন পরামর্শ LayoutManagerতাই আমার RecyclerViewসাথে কাজে WRAP_CONTENT?

সম্পাদনা করুন: এই লেআউট ম্যানেজারটি বেশিরভাগ লোকের পক্ষে কাজ করতে পারে তবে এটি এখনও পাঠ্যদর্শনগুলি মোড়ানো করার জন্য উচ্চতর স্ক্রোলিং এবং গণনা করতে সমস্যা রয়েছে it

public class MyLinearLayoutManager extends LinearLayoutManager {

public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout)    {
    super(context, orientation, reverseLayout);
}

private int[] mMeasuredDimension = new int[2];

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                      int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int width = 0;
    int height = 0;
    for (int i = 0; i < getItemCount(); i++) {
        measureScrapChild(recycler, i,
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);

        if (getOrientation() == HORIZONTAL) {
            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            height = height + mMeasuredDimension[1];
            if (i == 0) {
                width = mMeasuredDimension[0];
            }
        }
    }
    switch (widthMode) {
        case View.MeasureSpec.EXACTLY:
            width = widthSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    switch (heightMode) {
        case View.MeasureSpec.EXACTLY:
            height = heightSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    setMeasuredDimension(width, height);
}

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                   int heightSpec, int[] measuredDimension) {
        View view = recycler.getViewForPosition(position);
        if (view != null) {
            RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
            int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                    getPaddingLeft() + getPaddingRight(), p.width);
            int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom(), p.height);
            view.measure(childWidthSpec, childHeightSpec);
            measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
            measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
            recycler.recycleView(view);
        }
    }
}

4
দেখে মনে হচ্ছে শেষ পর্যন্ত গুগল এটি ঠিক করতে পেরেছে :Jan 22, 2016: This has been merged into the internal tree, should be available with the next version of support library.
মার্সিন ওরোলোস্কি

উত্তর:


187

অ্যান্ড্রয়েড সমর্থন লাইব্রেরি 23.2.1 আপডেট থেকে সমস্ত Wrap_CONTENT টি সঠিকভাবে কাজ করা উচিত।

দয়া করে gradleফাইলটিতে একটি লাইব্রেরির সংস্করণ আপডেট করুন বা আরও:

compile 'com.android.support:recyclerview-v7:23.2.1'

বিভিন্ন পরিমাপ-বিশ্লেষণ পদ্ধতি সম্পর্কিত ফিক্সড বাগের মতো কিছু সমস্যা সমাধান করুন

পরীক্ষা করে দেখুন http://developer.android.com/tools/support-library/features.html#v7-recyclerview

আপনি সমর্থন লাইব্রেরি পুনর্বিবেচনার ইতিহাসটি পরীক্ষা করতে পারেন


আমি একটি লেআউটম্যানেজার ব্যবহার করছিলাম এবং অবিলম্বে আমি এতে আপডেট হয়েছি 23.2.0আমার অ্যাপটিকে ক্রাশ করা শুরু করে। সুতরাং, আমি আপনার উত্তর অনুসরণ করেছি এবং আমার RecyclerViewমোড়ক না। কেন?
আবুবকর ওলাদেজি

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

25
কল করতে ভুলবেন না mRecyclerView.setNestedScrollingEnabled(false);অন্যথায় পুনর্ব্যবহারযোগ্য ভিউ এখনও ইভেন্টগুলি পিতামাতার কাছে প্রেরণের পরিবর্তে নিজেই স্ক্রোলিং পরিচালনা করবে।
উইন্ডারাইডার 17'27

4
পুনর্ব্যবহারযোগ্য ভিউয়ের জন্য মোড়ানো-সামগ্রী এখনও যথেষ্ট সমর্থিত নয়। পরীক্ষা করে দেখুন medium.com/@elye.project/...
Elye

1
এটি প্রদত্ত সমস্যার সমাধান নয়। এমনকি সমর্থন 27 এর সংস্করণে কাজ করছে না। রিলেটিভলয়েটের অভ্যন্তরে রিসাইক্লারভিউটি মোড়ানোর জন্য সমাধানটি অরেঞ্জ01 এর পরবর্তী উত্তরে বলা হয়েছে।
খ্রিস্টান

64

আপডেট 02.07.2020
এই পদ্ধতিটি পুনর্ব্যবহার রোধ করতে পারে এবং বড় ডেটা সেটগুলিতে ব্যবহার করা উচিত নয়

আপডেট 05.07.2019

আপনি ব্যবহার করে থাকেন RecyclerViewএকটি ভিতরে ScrollView, শুধু পরিবর্তন ScrollViewকরতে androidx.core.widget.NestedScrollView। এই ভিউয়ের RecyclerViewভিতরে ক এর ভিতরে প্যাক করার দরকার নেই RelativeLayout

<androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

        <!-- other views -->

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <!-- other views -->

    </LinearLayout>

</androidx.core.widget.NestedScrollView>

অবশেষে এই সমস্যার সমাধান খুঁজে পেয়েছি।

আপনাকে যা করতে হবে প্রয়োজন মোড়ানো হয় RecyclerViewএকটি RelativeLayout। হতে পারে এমন অন্যান্য ভিউ রয়েছে যা কাজ করতে পারে।

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

4
আমি জানি না কেন আপনার সমাধানটি সঠিক। তবে এটি আমার পক্ষে কাজ করে। আপনাকে অনেক ধন্যবাদ <3
আমার উইল

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

2
আমি যখন কনস্ট্রেন্টলয়েটের অভ্যন্তরে পুনর্ব্যবহারযোগ্য ভিউটি ব্যবহার করছিলাম তখন এটি আমার জন্যও কাজ করেছিল।
অ্যালান ভেলোসো

এই পর্যন্ত আমি "match_parent" উভয় উচ্চতা এবং recyclerview জন্য প্রস্থ সেট আমার জন্য কাজ করে নি
darkrider1287

এটি আমার পক্ষে কাজ করেছে, আমি যখন অতীতে এটি ব্যবহার করে দেখেছি তখন আমারও রিমার্জন হয়, আপনাকে ধন্যবাদ @ কমলা # 1 !!
আইভর

53

এখানে শ্রেণীর পরিশ্রুত সংস্করণ যা কাজ করে বলে মনে হচ্ছে এবং অন্যান্য সমাধানের সমস্যার অভাব রয়েছে:

package org.solovyev.android.views.llm;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

/**
 * {@link android.support.v7.widget.LinearLayoutManager} which wraps its content. Note that this class will always
 * wrap the content regardless of {@link android.support.v7.widget.RecyclerView} layout parameters.
 *
 * Now it's impossible to run add/remove animations with child views which have arbitrary dimensions (height for
 * VERTICAL orientation and width for HORIZONTAL). However if child views have fixed dimensions
 * {@link #setChildSize(int)} method might be used to let the layout manager know how big they are going to be.
 * If animations are not used at all then a normal measuring procedure will run and child views will be measured during
 * the measure pass.
 */
public class LinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {

    private static final int CHILD_WIDTH = 0;
    private static final int CHILD_HEIGHT = 1;
    private static final int DEFAULT_CHILD_SIZE = 100;

    private final int[] childDimensions = new int[2];

    private int childSize = DEFAULT_CHILD_SIZE;
    private boolean hasChildSize;

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(Context context) {
        super(context);
    }

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public static int makeUnspecifiedSpec() {
        return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    }

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);

        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
        final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;

        final int unspecified = makeUnspecifiedSpec();

        if (exactWidth && exactHeight) {
            // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
            super.onMeasure(recycler, state, widthSpec, heightSpec);
            return;
        }

        final boolean vertical = getOrientation() == VERTICAL;

        initChildDimensions(widthSize, heightSize, vertical);

        int width = 0;
        int height = 0;

        // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
        // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
        // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
        // called whiles scrolling)
        recycler.clear();

        final int stateItemCount = state.getItemCount();
        final int adapterItemCount = getItemCount();
        // adapter always contains actual data while state might contain old data (f.e. data before the animation is
        // done). As we want to measure the view with actual data we must use data from the adapter and not from  the
        // state
        for (int i = 0; i < adapterItemCount; i++) {
            if (vertical) {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                        // we will use previously calculated dimensions
                        measureChild(recycler, i, widthSpec, unspecified, childDimensions);
                    } else {
                        logMeasureWarning(i);
                    }
                }
                height += childDimensions[CHILD_HEIGHT];
                if (i == 0) {
                    width = childDimensions[CHILD_WIDTH];
                }
                if (height >= heightSize) {
                    break;
                }
            } else {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                        // we will use previously calculated dimensions
                        measureChild(recycler, i, unspecified, heightSpec, childDimensions);
                    } else {
                        logMeasureWarning(i);
                    }
                }
                width += childDimensions[CHILD_WIDTH];
                if (i == 0) {
                    height = childDimensions[CHILD_HEIGHT];
                }
                if (width >= widthSize) {
                    break;
                }
            }
        }

        if ((vertical && height < heightSize) || (!vertical && width < widthSize)) {
            // we really should wrap the contents of the view, let's do it

            if (exactWidth) {
                width = widthSize;
            } else {
                width += getPaddingLeft() + getPaddingRight();
            }

            if (exactHeight) {
                height = heightSize;
            } else {
                height += getPaddingTop() + getPaddingBottom();
            }

            setMeasuredDimension(width, height);
        } else {
            // if calculated height/width exceeds requested height/width let's use default "onMeasure" implementation
            super.onMeasure(recycler, state, widthSpec, heightSpec);
        }
    }

    private void logMeasureWarning(int child) {
        if (BuildConfig.DEBUG) {
            Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                    "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
        }
    }

    private void initChildDimensions(int width, int height, boolean vertical) {
        if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
            // already initialized, skipping
            return;
        }
        if (vertical) {
            childDimensions[CHILD_WIDTH] = width;
            childDimensions[CHILD_HEIGHT] = childSize;
        } else {
            childDimensions[CHILD_WIDTH] = childSize;
            childDimensions[CHILD_HEIGHT] = height;
        }
    }

    @Override
    public void setOrientation(int orientation) {
        // might be called before the constructor of this class is called
        //noinspection ConstantConditions
        if (childDimensions != null) {
            if (getOrientation() != orientation) {
                childDimensions[CHILD_WIDTH] = 0;
                childDimensions[CHILD_HEIGHT] = 0;
            }
        }
        super.setOrientation(orientation);
    }

    public void clearChildSize() {
        hasChildSize = false;
        setChildSize(DEFAULT_CHILD_SIZE);
    }

    public void setChildSize(int childSize) {
        hasChildSize = true;
        if (this.childSize != childSize) {
            this.childSize = childSize;
            requestLayout();
        }
    }

    private void measureChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] dimensions) {
        final View child = recycler.getViewForPosition(position);

        final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();

        final int hPadding = getPaddingLeft() + getPaddingRight();
        final int vPadding = getPaddingTop() + getPaddingBottom();

        final int hMargin = p.leftMargin + p.rightMargin;
        final int vMargin = p.topMargin + p.bottomMargin;

        final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
        final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);

        final int childWidthSpec = getChildMeasureSpec(widthSpec, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
        final int childHeightSpec = getChildMeasureSpec(heightSpec, vPadding + vMargin + vDecoration, p.height, canScrollVertically());

        child.measure(childWidthSpec, childHeightSpec);

        dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
        dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;

        recycler.recycleView(child);
    }
}

এটি একটি গ্রন্থাগার হিসাবেও উপলব্ধ । প্রাসঙ্গিক শ্রেণীর লিঙ্ক ।


আমার জন্য নির্দোষভাবে কাজ করে। ধন্যবাদ.
নটারিও

10
দয়া করে, গিথুব থেকে একটি আপ-টু-ডেট সংস্করণ ব্যবহার করুন কারণ আমি উত্তর পোস্ট করার পরে এটি অনেকটাই পরিবর্তিত হয়েছে।
se.solovyev

আপনার কাজের জন্য ধন্যবাদ. বিভিন্ন উচ্চতার বাচ্চাদের সাথে কাজ করতে আমার সমস্যা হচ্ছে। আমার যদি 10 150 ডিপি বাচ্চা থাকে তবে এটি কাজ করে; যদি এর মধ্যে একটি 300 ডিপি হয় তবে শেষটি লুকিয়ে থাকবে। কোন ধারণা?
নটারিও

3
আরও সুনির্দিষ্টভাবে, আমার মনে হয় এটি বলার আগে শিশুদের পরিমাপ করে onBindViewHolder()। এটি খারাপ কারণ কারণ আমি সেই সময়ে কল করি, উদাহরণস্বরূপ holder.textView.setText(longText), যাতে সেই শিশুটি লম্বা হয় তবে তা পুনর্ব্যবহারযোগ্য উচ্চতায় প্রতিফলিত হয় না। আপনার যদি কোনও ধারণা থাকে (অ্যাডাপ্টারের দ্রুত পরিবর্তনের মতো), আমি কৃতজ্ঞ হব।
নটারিও

ধন্যবাদ। আপনার উত্তর / পাঠাগারটি অনুভূমিক RecyclerViewঅভ্যন্তরের উলম্বেরRecyclerView দৈর্ঘ্যের সমস্যাটি কাটিয়ে উঠতে আমাকে সহায়তা করেছে ।
সুফিয়ান

16

হালনাগাদ

অ্যানড্রয়েড সমর্থন লাইব্রেরি ২৩.২ আপডেটের মাধ্যমে সমস্ত Wrap_CONTENT সঠিকভাবে কাজ করা উচিত।

গ্রেড ফাইলটিতে একটি লাইব্রেরির সংস্করণ আপডেট করুন Please

compile 'com.android.support:recyclerview-v7:23.2.0'

আসল উত্তর

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

    public class MyLinearLayoutManager extends LinearLayoutManager {

public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout)    {
    super(context, orientation, reverseLayout);
}

private int[] mMeasuredDimension = new int[2];

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                      int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int width = 0;
    int height = 0;
    for (int i = 0; i < getItemCount(); i++) {
        measureScrapChild(recycler, i,
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);

        if (getOrientation() == HORIZONTAL) {
            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            height = height + mMeasuredDimension[1];
            if (i == 0) {
                width = mMeasuredDimension[0];
            }
        }
    }

    // If child view is more than screen size, there is no need to make it wrap content. We can use original onMeasure() so we can scroll view.
    if (height < heightSize && width < widthSize) {

        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        setMeasuredDimension(width, height);
    } else {
        super.onMeasure(recycler, state, widthSpec, heightSpec);
    }
}

private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                               int heightSpec, int[] measuredDimension) {

   View view = recycler.getViewForPosition(position);

   // For adding Item Decor Insets to view
   super.measureChildWithMargins(view, 0, 0);
    if (view != null) {
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                    getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view), p.width);
            int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom() + getPaddingBottom() + getDecoratedBottom(view) , p.height);
            view.measure(childWidthSpec, childHeightSpec);

            // Get decorated measurements
            measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin;
            measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin;
            recycler.recycleView(view);
        }
    }
}

আসল উত্তর: https://stackoverflow.com/a/28510031/1577792


2
আমরা স্প্যানের গণনা বিবেচনার জন্য gridlayoutmanagerএবং staggeredgridlayoutmanagerবিবেচনার জন্য কীভাবে তা অর্জন করতে পারি
অমৃত বিদ্রি

10

মনো অ্যান্ড্রয়েডের জন্য এখানে সি # সংস্করণ দেওয়া আছে

/* 
* Ported by Jagadeesh Govindaraj (@jaganjan)
 *Copyright 2015 serso aka se.solovyev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Contact details
 *
 * Email: se.solovyev @gmail.com
 * Site:  http://se.solovyev.org
 */


using Android.Content;
using Android.Graphics;
using Android.Support.V4.View;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Views;
using Java.Lang;
using Java.Lang.Reflect;
using System;
using Math = Java.Lang.Math;

namespace Droid.Helper
{
    public class WrapLayoutManager : LinearLayoutManager
    {
        private const int DefaultChildSize = 100;
        private static readonly Rect TmpRect = new Rect();
        private int _childSize = DefaultChildSize;
        private static bool _canMakeInsetsDirty = true;
        private static readonly int[] ChildDimensions = new int[2];
        private const int ChildHeight = 1;
        private const int ChildWidth = 0;
        private static bool _hasChildSize;
        private static  Field InsetsDirtyField = null;
        private static int _overScrollMode = ViewCompat.OverScrollAlways;
        private static RecyclerView _view;

        public WrapLayoutManager(Context context, int orientation, bool reverseLayout)
            : base(context, orientation, reverseLayout)
        {
            _view = null;
        }

        public WrapLayoutManager(Context context) : base(context)
        {
            _view = null;
        }

        public WrapLayoutManager(RecyclerView view) : base(view.Context)
        {
            _view = view;
            _overScrollMode = ViewCompat.GetOverScrollMode(view);
        }

        public WrapLayoutManager(RecyclerView view, int orientation, bool reverseLayout)
            : base(view.Context, orientation, reverseLayout)
        {
            _view = view;
            _overScrollMode = ViewCompat.GetOverScrollMode(view);
        }

        public void SetOverScrollMode(int overScrollMode)
        {
            if (overScrollMode < ViewCompat.OverScrollAlways || overScrollMode > ViewCompat.OverScrollNever)
                throw new ArgumentException("Unknown overscroll mode: " + overScrollMode);
            if (_view == null) throw new ArgumentNullException(nameof(_view));
            _overScrollMode = overScrollMode;
            ViewCompat.SetOverScrollMode(_view, overScrollMode);
        }

        public static int MakeUnspecifiedSpec()
        {
            return View.MeasureSpec.MakeMeasureSpec(0, MeasureSpecMode.Unspecified);
        }

        public override void OnMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec,
            int heightSpec)
        {
            var widthMode = View.MeasureSpec.GetMode(widthSpec);
            var heightMode = View.MeasureSpec.GetMode(heightSpec);

            var widthSize = View.MeasureSpec.GetSize(widthSpec);
            var heightSize = View.MeasureSpec.GetSize(heightSpec);

            var hasWidthSize = widthMode != MeasureSpecMode.Unspecified;
            var hasHeightSize = heightMode != MeasureSpecMode.Unspecified;

            var exactWidth = widthMode == MeasureSpecMode.Exactly;
            var exactHeight = heightMode == MeasureSpecMode.Exactly;

            var unspecified = MakeUnspecifiedSpec();

            if (exactWidth && exactHeight)
            {
                // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
                base.OnMeasure(recycler, state, widthSpec, heightSpec);
                return;
            }

            var vertical = Orientation == Vertical;

            InitChildDimensions(widthSize, heightSize, vertical);

            var width = 0;
            var height = 0;

            // it's possible to get scrap views in recycler which are bound to old (invalid) adapter
            // entities. This happens because their invalidation happens after "onMeasure" method.
            // As a workaround let's clear the recycler now (it should not cause any performance
            // issues while scrolling as "onMeasure" is never called whiles scrolling)
            recycler.Clear();

            var stateItemCount = state.ItemCount;
            var adapterItemCount = ItemCount;
            // adapter always contains actual data while state might contain old data (f.e. data
            // before the animation is done). As we want to measure the view with actual data we
            // must use data from the adapter and not from the state
            for (var i = 0; i < adapterItemCount; i++)
            {
                if (vertical)
                {
                    if (!_hasChildSize)
                    {
                        if (i < stateItemCount)
                        {
                            // we should not exceed state count, otherwise we'll get
                            // IndexOutOfBoundsException. For such items we will use previously
                            // calculated dimensions
                            MeasureChild(recycler, i, widthSize, unspecified, ChildDimensions);
                        }
                        else
                        {
                            LogMeasureWarning(i);
                        }
                    }
                    height += ChildDimensions[ChildHeight];
                    if (i == 0)
                    {
                        width = ChildDimensions[ChildWidth];
                    }
                    if (hasHeightSize && height >= heightSize)
                    {
                        break;
                    }
                }
                else
                {
                    if (!_hasChildSize)
                    {
                        if (i < stateItemCount)
                        {
                            // we should not exceed state count, otherwise we'll get
                            // IndexOutOfBoundsException. For such items we will use previously
                            // calculated dimensions
                            MeasureChild(recycler, i, unspecified, heightSize, ChildDimensions);
                        }
                        else
                        {
                            LogMeasureWarning(i);
                        }
                    }
                    width += ChildDimensions[ChildWidth];
                    if (i == 0)
                    {
                        height = ChildDimensions[ChildHeight];
                    }
                    if (hasWidthSize && width >= widthSize)
                    {
                        break;
                    }
                }
            }

            if (exactWidth)
            {
                width = widthSize;
            }
            else
            {
                width += PaddingLeft + PaddingRight;
                if (hasWidthSize)
                {
                    width = Math.Min(width, widthSize);
                }
            }

            if (exactHeight)
            {
                height = heightSize;
            }
            else
            {
                height += PaddingTop + PaddingBottom;
                if (hasHeightSize)
                {
                    height = Math.Min(height, heightSize);
                }
            }

            SetMeasuredDimension(width, height);

            if (_view == null || _overScrollMode != ViewCompat.OverScrollIfContentScrolls) return;
            var fit = (vertical && (!hasHeightSize || height < heightSize))
                      || (!vertical && (!hasWidthSize || width < widthSize));

            ViewCompat.SetOverScrollMode(_view, fit ? ViewCompat.OverScrollNever : ViewCompat.OverScrollAlways);
        }

        private void LogMeasureWarning(int child)
        {
#if DEBUG
            Log.WriteLine(LogPriority.Warn, "LinearLayoutManager",
                "Can't measure child #" + child + ", previously used dimensions will be reused." +
                "To remove this message either use #SetChildSize() method or don't run RecyclerView animations");
#endif
        }

        private void InitChildDimensions(int width, int height, bool vertical)
        {
            if (ChildDimensions[ChildWidth] != 0 || ChildDimensions[ChildHeight] != 0)
            {
                // already initialized, skipping
                return;
            }
            if (vertical)
            {
                ChildDimensions[ChildWidth] = width;
                ChildDimensions[ChildHeight] = _childSize;
            }
            else
            {
                ChildDimensions[ChildWidth] = _childSize;
                ChildDimensions[ChildHeight] = height;
            }
        }

        public void ClearChildSize()
        {
            _hasChildSize = false;
            SetChildSize(DefaultChildSize);
        }

        public void SetChildSize(int size)
        {
            _hasChildSize = true;
            if (_childSize == size) return;
            _childSize = size;
            RequestLayout();
        }

        private void MeasureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize,
            int[] dimensions)
        {
            View child = null;
            try
            {
                child = recycler.GetViewForPosition(position);
            }
            catch (IndexOutOfRangeException e)
            {
                Log.WriteLine(LogPriority.Warn, "LinearLayoutManager",
                    "LinearLayoutManager doesn't work well with animations. Consider switching them off", e);
            }

            if (child != null)
            {
                var p = child.LayoutParameters.JavaCast<RecyclerView.LayoutParams>()

                var hPadding = PaddingLeft + PaddingRight;
                var vPadding = PaddingTop + PaddingBottom;

                var hMargin = p.LeftMargin + p.RightMargin;
                var vMargin = p.TopMargin + p.BottomMargin;

                // we must make insets dirty in order calculateItemDecorationsForChild to work
                MakeInsetsDirty(p);
                // this method should be called before any getXxxDecorationXxx() methods
                CalculateItemDecorationsForChild(child, TmpRect);

                var hDecoration = GetRightDecorationWidth(child) + GetLeftDecorationWidth(child);
                var vDecoration = GetTopDecorationHeight(child) + GetBottomDecorationHeight(child);

                var childWidthSpec = GetChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.Width,
                    CanScrollHorizontally());
                var childHeightSpec = GetChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.Height,
                    CanScrollVertically());

                child.Measure(childWidthSpec, childHeightSpec);

                dimensions[ChildWidth] = GetDecoratedMeasuredWidth(child) + p.LeftMargin + p.RightMargin;
                dimensions[ChildHeight] = GetDecoratedMeasuredHeight(child) + p.BottomMargin + p.TopMargin;

                // as view is recycled let's not keep old measured values
                MakeInsetsDirty(p);
            }
            recycler.RecycleView(child);
        }

        private static void MakeInsetsDirty(RecyclerView.LayoutParams p)
        {
            if (!_canMakeInsetsDirty)
            {
                return;
            }
            try
            {
                if (InsetsDirtyField == null)
                {
                   var klass = Java.Lang.Class.FromType (typeof (RecyclerView.LayoutParams));
                    InsetsDirtyField = klass.GetDeclaredField("mInsetsDirty");
                    InsetsDirtyField.Accessible = true;
                }
                InsetsDirtyField.Set(p, true);
            }
            catch (NoSuchFieldException e)
            {
                OnMakeInsertDirtyFailed();
            }
            catch (IllegalAccessException e)
            {
                OnMakeInsertDirtyFailed();
            }
        }

        private static void OnMakeInsertDirtyFailed()
        {
            _canMakeInsetsDirty = false;
#if DEBUG
            Log.Warn("LinearLayoutManager",
                "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
#endif
        }
    }
}

প্রায় ... প্রতিস্থাপন var p = (RecyclerView.LayoutParams) child.LayoutParametersসঙ্গেvar p = child.LayoutParameters.JavaCast<RecyclerView.LayoutParams>()
Roubachof

আপনি কেন সদস্য ভেরিয়েবল স্থির ঘোষণা করবেন?
রচনা

@ ইশকার আমার ধারণা ভেরিয়েবল স্থির পদ্ধতিতে ব্যবহৃত হয়, আপনার যদি সন্দেহ থাকে তবে জাভা সংস্করণটি দেখুন
জগদীশ গোবিন্দরাজ

6

RecyclerViewজন্য অতিরিক্ত সমর্থন wrap_contentমধ্যে 23.2.0যা বগী ছিল, 23.2.1, শুধু স্থিতিশীল ছিল তাই আপনি ব্যবহার করতে পারেন:

compile 'com.android.support:recyclerview-v7:24.2.0'

আপনি এখানে সংশোধন ইতিহাস দেখতে পারেন:

https://developer.android.com/topic/libraries/support-library/revisions.html

বিঃদ্রঃ:

আরও মনে রাখবেন যে সমর্থন লাইব্রেরি আপডেট করার পরে লাইব্রেরিটি যেমন RecyclerViewসম্মান করবে wrap_contentততই match_parentযদি আপনার কোনও RecyclerViewসেটটির আইটেম ভিউ থাকে match_parentতবে একক দর্শন পুরো পর্দাটি পূরণ করবে


1
@ ইয়ভেটে ঠিক আছে
কোল্ডফিউশন


4

স্ক্রোলিং এবং পাঠ্য মোড়ানোর সমস্যাটি এই কোডটি ধরে নিচ্ছে যে প্রস্থ এবং উচ্চতা উভয়ই সেট করা আছে wrap_content। তবে, এটি LayoutManagerজানতে হবে যে অনুভূমিক প্রস্থ সীমাবদ্ধ। সুতরাং widthSpecপ্রতিটি শিশু দেখার জন্য নিজের তৈরি করার পরিবর্তে কেবল আসলটি ব্যবহার করুন widthSpec:

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
    final int widthMode = View.MeasureSpec.getMode(widthSpec);
    final int heightMode = View.MeasureSpec.getMode(heightSpec);
    final int widthSize = View.MeasureSpec.getSize(widthSpec);
    final int heightSize = View.MeasureSpec.getSize(heightSpec);
    int width = 0;
    int height = 0;
    for (int i = 0; i < getItemCount(); i++) {
        measureScrapChild(recycler, i,
                widthSpec,
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);

        if (getOrientation() == HORIZONTAL) {
            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            height = height + mMeasuredDimension[1];
            if (i == 0) {
                width = mMeasuredDimension[0];
            }
        }
    }
    switch (widthMode) {
        case View.MeasureSpec.EXACTLY:
            width = widthSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    switch (heightMode) {
        case View.MeasureSpec.EXACTLY:
            height = heightSize;
        case View.MeasureSpec.AT_MOST:
        case View.MeasureSpec.UNSPECIFIED:
    }

    setMeasuredDimension(width, height);
}

private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,int heightSpec, int[] measuredDimension) {
    View view = recycler.getViewForPosition(position);
    if (view != null) {
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom(), p.height);
        view.measure(widthSpec, childHeightSpec);
        measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
        measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
        recycler.recycleView(view);
    }
}

3

এটি চেষ্টা করুন (এটি একটি বাজে সমাধান তবে এটি কার্যকর হতে পারে): onCreateআপনার পদ্ধতিতে Activityবা onViewCreatedআপনার খণ্ডের পদ্ধতিতে। যখন RecyclerViewপ্রথম রেন্ডার করা হয় তখন ট্রিগার হওয়ার জন্য প্রস্তুত একটি কলব্যাক সেট করুন :

vRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                calculeRecyclerViewFullHeight();
            }
        });

ইন calculeRecyclerViewFullHeightক্যালকুলেট RecyclerViewপূর্ণ উচ্চতা তার সন্তানদের উচ্চতায় ভিত্তি করে।

protected void calculateSwipeRefreshFullHeight() {
        int height = 0;
        for (int idx = 0; idx < getRecyclerView().getChildCount(); idx++ ) {
            View v = getRecyclerView().getChildAt(idx);
            height += v.getHeight();
        }
        SwipeRefreshLayout.LayoutParams params = getSwipeRefresh().getLayoutParams();
        params.height = height;
        getSwipeRefresh().setLayoutParams(params);
    }

আমার ক্ষেত্রে আমার RecyclerViewএকটিকে অন্তর্ভুক্ত করা হয়েছে SwipeRefreshLayoutসেই কারণে আমি উচ্চতাটি সেট করছি SwipeRefreshViewএবং নাও RecyclerViewতবে আপনার যদি কিছু না থাকে SwipeRefreshViewতবে আপনি উচ্চতাটি সেট করতে পারেনRecyclerView পরিবর্তে ।

এটি আপনাকে সহায়তা করেছে কিনা তা আমাকে জানান।


আমি কীভাবে getRecyclerView () পদ্ধতি পাব?
asubanovsky

@ আসুবানোভস্কি এটি এমন একটি পদ্ধতি যা কেবল আপনার RecyclerViewউদাহরণটি দেয়।
4gus71n

2
গ্লোবাল লেআউট ()
ম্যাথু

3

এই পোস্টে উল্লিখিত হিসাবে তারা এখন 23.2 সংস্করণে একটি রিলিজ করেছে এমনই কাজ করে । অফিসিয়াল ব্লগপোস্টের উদ্ধৃতি দিয়েছি

এই প্রকাশটি লেআউটম্যানেজার এপিআইতে একটি আকর্ষণীয় নতুন বৈশিষ্ট্য নিয়ে আসে: স্বতঃ-পরিমাপ! এটি কোনও পুনর্ব্যবহারযোগ্য ভিউকে এর সামগ্রীগুলির আকারের ভিত্তিতে নিজেই আকার দিতে দেয় allows এর অর্থ হ'ল পূর্বে অনুপলব্ধ পরিস্থিতি, যেমন রিসাইক্লার ভিউয়ের মাত্রার জন্য Wrap_CONTENT ব্যবহার করা এখন সম্ভব। লেআউট ম্যানেজারে অন্তর্নির্মিত সমস্ত আপনি এখন স্বয়ংক্রিয়-পরিমাপ সমর্থন করে দেখতে পাবেন।


3

কেবলমাত্র আপনার পুনর্ব্যবহারযোগ্য ভিউটিকে নেস্টডস্ক্রোলভিউয়ের মধ্যে রাখুন। পুরোপুরি কাজ করে

<android.support.v4.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="25dp">
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/kliste"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
            </android.support.v4.widget.NestedScrollView>

2

আমি উপরের কয়েকটি সমাধান ব্যবহার করেছি তবে এটি কার্যকর ছিল widthতবে height

  1. যদি আপনার নির্দিষ্ট compileSdkVersionচেয়ে বড় 23 , আপনি সরাসরি ব্যবহার করতে পারেন RecyclerView recycler দৃশ্য নিজ নিজ সমর্থন গ্রন্থাগারে প্রদান মত 23 এটা হবে 'com.android.support:recyclerview-v7:23.2.1'। এই সমর্থন গ্রন্থাগারগুলিwrap_content প্রস্থ এবং উচ্চতা উভয়ের জন্য বৈশিষ্ট্যগুলি সমর্থন করে ।

আপনাকে এটি আপনার নির্ভরতার সাথে যুক্ত করতে হবে

compile 'com.android.support:recyclerview-v7:23.2.1'
  1. যদি আপনার 23 এরcompileSdkVersion কম হয় , তবে আপনি নীচে উল্লিখিত সমাধানটি ব্যবহার করতে পারেন।

এই সমস্যা সম্পর্কিত আমি এই গুগল থ্রেডটি পেয়েছি । এই থ্রেডে, এমন একটি অবদান রয়েছে যা লিনিয়ারলআউটম্যানেজারের বাস্তবায়নের দিকে পরিচালিত করে ।

আমি এটি উভয় উচ্চতা এবং প্রস্থের জন্য পরীক্ষা করেছি এবং এটি উভয় ক্ষেত্রেই আমার পক্ষে ভাল কাজ করেছে।

/*
 * Copyright 2015 serso aka se.solovyev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Contact details
 *
 * Email: se.solovyev@gmail.com
 * Site:  http://se.solovyev.org
 */

package org.solovyev.android.views.llm;

import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

import java.lang.reflect.Field;

/**
 * {@link android.support.v7.widget.LinearLayoutManager} which wraps its content. Note that this class will always
 * wrap the content regardless of {@link android.support.v7.widget.RecyclerView} layout parameters.
 * <p/>
 * Now it's impossible to run add/remove animations with child views which have arbitrary dimensions (height for
 * VERTICAL orientation and width for HORIZONTAL). However if child views have fixed dimensions
 * {@link #setChildSize(int)} method might be used to let the layout manager know how big they are going to be.
 * If animations are not used at all then a normal measuring procedure will run and child views will be measured during
 * the measure pass.
 */
public class LinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {

    private static boolean canMakeInsetsDirty = true;
    private static Field insetsDirtyField = null;

    private static final int CHILD_WIDTH = 0;
    private static final int CHILD_HEIGHT = 1;
    private static final int DEFAULT_CHILD_SIZE = 100;

    private final int[] childDimensions = new int[2];
    private final RecyclerView view;

    private int childSize = DEFAULT_CHILD_SIZE;
    private boolean hasChildSize;
    private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;
    private final Rect tmpRect = new Rect();

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(Context context) {
        super(context);
        this.view = null;
    }

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
        this.view = null;
    }

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(RecyclerView view) {
        super(view.getContext());
        this.view = view;
        this.overScrollMode = ViewCompat.getOverScrollMode(view);
    }

    @SuppressWarnings("UnusedDeclaration")
    public LinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
        super(view.getContext(), orientation, reverseLayout);
        this.view = view;
        this.overScrollMode = ViewCompat.getOverScrollMode(view);
    }

    public void setOverScrollMode(int overScrollMode) {
        if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
            throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);
        if (this.view == null) throw new IllegalStateException("view == null");
        this.overScrollMode = overScrollMode;
        ViewCompat.setOverScrollMode(view, overScrollMode);
    }

    public static int makeUnspecifiedSpec() {
        return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    }

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);

        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;
        final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;

        final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
        final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;

        final int unspecified = makeUnspecifiedSpec();

        if (exactWidth && exactHeight) {
            // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
            super.onMeasure(recycler, state, widthSpec, heightSpec);
            return;
        }

        final boolean vertical = getOrientation() == VERTICAL;

        initChildDimensions(widthSize, heightSize, vertical);

        int width = 0;
        int height = 0;

        // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
        // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
        // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
        // called whiles scrolling)
        recycler.clear();

        final int stateItemCount = state.getItemCount();
        final int adapterItemCount = getItemCount();
        // adapter always contains actual data while state might contain old data (f.e. data before the animation is
        // done). As we want to measure the view with actual data we must use data from the adapter and not from  the
        // state
        for (int i = 0; i < adapterItemCount; i++) {
            if (vertical) {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                        // we will use previously calculated dimensions
                        measureChild(recycler, i, widthSize, unspecified, childDimensions);
                    } else {
                        logMeasureWarning(i);
                    }
                }
                height += childDimensions[CHILD_HEIGHT];
                if (i == 0) {
                    width = childDimensions[CHILD_WIDTH];
                }
                if (hasHeightSize && height >= heightSize) {
                    break;
                }
            } else {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                        // we will use previously calculated dimensions
                        measureChild(recycler, i, unspecified, heightSize, childDimensions);
                    } else {
                        logMeasureWarning(i);
                    }
                }
                width += childDimensions[CHILD_WIDTH];
                if (i == 0) {
                    height = childDimensions[CHILD_HEIGHT];
                }
                if (hasWidthSize && width >= widthSize) {
                    break;
                }
            }
        }

        if (exactWidth) {
            width = widthSize;
        } else {
            width += getPaddingLeft() + getPaddingRight();
            if (hasWidthSize) {
                width = Math.min(width, widthSize);
            }
        }

        if (exactHeight) {
            height = heightSize;
        } else {
            height += getPaddingTop() + getPaddingBottom();
            if (hasHeightSize) {
                height = Math.min(height, heightSize);
            }
        }

        setMeasuredDimension(width, height);

        if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
            final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
                    || (!vertical && (!hasWidthSize || width < widthSize));

            ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);
        }
    }

    private void logMeasureWarning(int child) {
        if (BuildConfig.DEBUG) {
            Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                    "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
        }
    }

    private void initChildDimensions(int width, int height, boolean vertical) {
        if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
            // already initialized, skipping
            return;
        }
        if (vertical) {
            childDimensions[CHILD_WIDTH] = width;
            childDimensions[CHILD_HEIGHT] = childSize;
        } else {
            childDimensions[CHILD_WIDTH] = childSize;
            childDimensions[CHILD_HEIGHT] = height;
        }
    }

    @Override
    public void setOrientation(int orientation) {
        // might be called before the constructor of this class is called
        //noinspection ConstantConditions
        if (childDimensions != null) {
            if (getOrientation() != orientation) {
                childDimensions[CHILD_WIDTH] = 0;
                childDimensions[CHILD_HEIGHT] = 0;
            }
        }
        super.setOrientation(orientation);
    }

    public void clearChildSize() {
        hasChildSize = false;
        setChildSize(DEFAULT_CHILD_SIZE);
    }

    public void setChildSize(int childSize) {
        hasChildSize = true;
        if (this.childSize != childSize) {
            this.childSize = childSize;
            requestLayout();
        }
    }

    private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
        final View child;
        try {
            child = recycler.getViewForPosition(position);
        } catch (IndexOutOfBoundsException e) {
            if (BuildConfig.DEBUG) {
                Log.w("LinearLayoutManager", "LinearLayoutManager doesn't work well with animations. Consider switching them off", e);
            }
            return;
        }

        final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();

        final int hPadding = getPaddingLeft() + getPaddingRight();
        final int vPadding = getPaddingTop() + getPaddingBottom();

        final int hMargin = p.leftMargin + p.rightMargin;
        final int vMargin = p.topMargin + p.bottomMargin;

        // we must make insets dirty in order calculateItemDecorationsForChild to work
        makeInsetsDirty(p);
        // this method should be called before any getXxxDecorationXxx() methods
        calculateItemDecorationsForChild(child, tmpRect);

        final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
        final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);

        final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
        final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());

        child.measure(childWidthSpec, childHeightSpec);

        dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
        dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;

        // as view is recycled let's not keep old measured values
        makeInsetsDirty(p);
        recycler.recycleView(child);
    }

    private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
        if (!canMakeInsetsDirty) {
            return;
        }
        try {
            if (insetsDirtyField == null) {
                insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");
                insetsDirtyField.setAccessible(true);
            }
            insetsDirtyField.set(p, true);
        } catch (NoSuchFieldException e) {
            onMakeInsertDirtyFailed();
        } catch (IllegalAccessException e) {
            onMakeInsertDirtyFailed();
        }
    }

    private static void onMakeInsertDirtyFailed() {
        canMakeInsetsDirty = false;
        if (BuildConfig.DEBUG) {
            Log.w("LinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
        }
    }
}

1

কোনও লাইব্রেরি ব্যবহার না করে, নতুন সংস্করণ না আসা পর্যন্ত সহজ সমাধান হ'ল b.android.com/74772 খুলুন । আপনি সহজেই তারিখের জানা সবচেয়ে ভাল সমাধান খুঁজে পাবেন।

PS: b.android.com/74772#c50 আমার পক্ষে কাজ করেছে


1

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


0

measureScrapChildকোড অনুসরণ করতে প্রতিস্থাপন করুন :

private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
        int heightSpec, int[] measuredDimension)
    {
        View view = recycler.GetViewForPosition(position);
        if (view != null)
        {
            MeasureChildWithMargins(view, widthSpec, heightSpec);
            measuredDimension[0] = view.MeasuredWidth;
            measuredDimension[1] = view.MeasuredHeight;
            recycler.RecycleView(view);
        }
    }

আমি xamarin ব্যবহার করি, সুতরাং এটি সি # কোড। আমি মনে করি এটি সহজে জাভাতে "অনুবাদ" করা যেতে পারে।


0

অ্যাডাপ্টার ভিউহোল্ডার অনক্রিটভিউহোল্ডার পদ্ধতিতে প্যারেন্ট ভিউগ্রুপের পরিবর্তে নাল মান দিয়ে আপনার দর্শন আপডেট করুন।

@Override
public AdapterItemSku.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = inflator.inflate(R.layout.layout_item, null, false);
    return new MyViewHolder(view);
}

0

আপনাকে অবশ্যই একটি মূল ফ্রেম ফ্রেমলাউট স্থাপন করতে হবে এবং তারপরে স্ক্রোলভিউর সাথে সম্পর্কিত এবং কমপক্ষে আপনার পুনর্ব্যবহারযোগ্য ভিউয়ের সাথে একটি রিলেটিভলয়েটের ভিতরে রাখতে হবে, এটি আমার পক্ষে কাজ করে।

এখানে আসল কৌশলটি আপেক্ষিক লেআউট ...

সাহায্য করতে পারলে খুশি.


-1

আমি আমার উত্তরে কাজ করি নি তবে উপায়টি কীভাবে আমি জানি তা কোনও স্ট্যাগগ্রিডলয়আউট ম্যানেজারের সাথে নেই। গ্রিড 1 আপনার সমস্যার সমাধান করতে পারে কারণ স্ট্যাগগ্রিডলাআউট স্বয়ংক্রিয়ভাবে সামগ্রীর আকারের উচ্চতা এবং প্রস্থ সামঞ্জস্য করবে। যদি এটি কাজ করে তবে এটি সঠিক উত্তর হিসাবে যাচাই করতে ভুলবেন না he চিয়ার্স ..

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