RecyclerView 项目中的片段替换

Posted

技术标签:

【中文标题】RecyclerView 项目中的片段替换【英文标题】:Fragment replacing in RecyclerView item 【发布时间】:2016-09-08 17:52:18 【问题描述】:

在我的 RecyclerView 中,我需要将部分项目替换为我的片段。但在回收站视图中仅替换第一项。我做错了什么?

我的容器(在回收站视图中):

...
<FrameLayout
      android:layout_
      android:layout_
      android:id="@+id/container" />
...

我在 RecyclerView 适配器中的更新代码:

...
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) 

...

MyFragment fragment = MyFragment.newInstance("fragment1");
fragmentManager.beginTransaction().replace(R.id.container, fragment).commit();

...


...

【问题讨论】:

我建议不要在 RecyclerView 项目内使用片段(我假设这是可能的,但也假设这是不好的做法)。我会处理 ViewHolder 中的所有视图。 【参考方案1】:

我终于找到了解决方案。问题是我设置了一个通用的容器 ID。但在回收站视图中需要为每个项目设置唯一容器ID

所以,我的代码现在是这样的:

MyFragment fragment = MyFragment.newInstance("fragment1");
fragmentManager.beginTransaction().replace(UNIQUE_CONTAINER_ID, fragment).commit();

如果有人有用,这是我的完整代码(回收器视图中的实现片段):

public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) 


...

// Delete old fragment
int containerId = holder.mediaContainer.getId();// Get container id
Fragment oldFragment = fragmentManager.findFragmentById(containerId);
if(oldFragment != null) 
    fragmentManager.beginTransaction().remove(oldFragment).commit();


int newContainerId = View.generateViewId();// Generate unique container id
holder.mediaContainer.setId(newContainerId);// Set container id

// Add new fragment
MyFragment fragment = MyFragment.newInstance("fragment1");
fragmentManager.beginTransaction().replace(newContainerId, fragment).commit();

...


更新:不要使用自己的方法生成唯一id,建议使用View.generateViewId()

【讨论】:

你能解释一下什么是GetUniqueID(); 我已经替换了这一行 int newContainerId = GetUniqueID();// 我的方法是 int newContainerId = 111 + (int)(Math.random() * 9999); 伙计,你救了我。我一直在寻找解决方案 3 天。谢谢。 嗨@Mikhail 我无法实现同样的目标。它给了我以相同方式实施后未找到的视图 ID! 这对我不起作用。在onBindViewHolder() 完成后,我得到No view found for id 0xd (unknown) for fragment TitleFragment...请注意,它适用于id 0xd,这是一个有效的id,由View.generateViewId() 生成并根据建议的代码分配给mediaContainer 视图,但是出于某种原因,它在onBindViewHolder() 完成后立即为unknown【参考方案2】:

感谢 Mikhali,我能够为您提供一个完整的运行示例。 特别注意onBindViewHolder()中的cmets

    public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewLedgerAdapter.ViewHolder>
        private final String TAG = RecyclerViewAdapter.class.getSimpleName();

        private final float FINAL_OPACITY = 0.3f;
        private final float START_OPACITY = 1f;

        private final int ANIMATION_TIME = 500;
        private final int TYPE_ITEM = 0;
        private final int TYPE_DATE = 1;
        private final int TYPE_TRANSACTION = 2;
        private final int TYPE_PENDING = 3;

        private HashMap<Integer, Integer> mElementTypes;
        private List<Operation> mObjects;
        private Context mContext;
        private Utils.CURRENCIES mCurrencySelected; // Which currency is already selected
        private boolean mCurrencyFilter; // Defines if a currency is already selected to apply filter
        private Animation mAnimationUp;
        private Animation mAnimationDown;

        public RecyclerViewLedgerAdapter(List<Operation> objects, Context context) 
            mElementTypes = new HashMap<Integer, Integer>();
            mObjects = objects;
            mContext = context;
            mCurrencyFilter = false;
            mCurrencySelected = null;
            mAnimationUp = AnimationUtils.loadAnimation(context, R.anim.slide_up);
            mAnimationDown = AnimationUtils.loadAnimation(context, R.anim.slide_down);
        

        ...
        ...
            Not needed methods
        ...
        ...

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_element_ledger, parent, false);
            return new ViewHolder(view);
        

        @Override
        public void onBindViewHolder(final ViewHolder holder, final int position) 
            Operation operation = mObjects.get(position);
            holder.setAppUserActivity(userActivityOperation);

            // Remember that RecyclerView does not have onClickListener, you should implement it
            holder.getView().setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View view) 
                    // Hide details
                    // iDetailsContainer object could be checked on inner class ViewHolder
                    if(holder.iDetailsContainer.isShown())
                        holder.iDetailsContainer.setVisibility(View.GONE);
                    else
                        // Show details
                        // Get fragment manager inside our fragment
                        FragmentManager fragmentManager = ((UserActivity)mContext).getSupportFragmentManager();

                        // Delete previous added fragment
                        int currentContainerId = holder.iDetailsContainer.getId();
                        // Get the current fragment
                        Fragment oldFragment = fragmentManager.findFragmentById(currentContainerId);
                        if(oldFragment != null) 
                            // Delete fragmet from ui, do not forget commit() otherwise no action
                            // is going to be observed
                            ragmentManager.beginTransaction().remove(oldFragment).commit();
                        

                        // In order to be able of replacing a fragment on a recycler view
                        // the target container should always have a different id ALWAYS
                        int newContainerId = getUniqueId();
                        // Set the new Id to our know fragment container
                        holder.iDetailsContainer.setId(newContainerId);

                        // Just for Testing we are going to create a new fragment according
                        // if the view position is pair one fragment type is created, if not
                        // a different one is used
                        Fragment f;
                        if(position%2 == 0) 
                            f = new FragmentCard();
                        else
                            f=new FragmentChat();
                        

                        // Then just replace the recycler view fragment as usually
                        manager.beginTransaction().replace(newContainerId, f).commit();

                        // Once all fragment replacement is done we can show the hidden container
                        holder.iDetailsContainer.setVisibility(View.VISIBLE);
                    
                

                // Method that could us an unique id
                public int getUniqueId()
                    return (int)SystemClock.currentThreadTimeMillis();
                
            );
        


        public class ViewHolder extends RecyclerView.ViewHolder
            private View iView;
            private LinearLayout iContainer;
            public LinearLayout iDetailsContainer;
            private ImageView iOperationIcon;
            private ImageView iOperationActionImage;
            private TextView iOperation;
            private TextView iAmount;
            private TextView iTimestamp;
            private TextView iStatus;

            private UserActivityOperation mUserActivityOperation;

            public ViewHolder(View itemView) 
                super(itemView);
                iView = itemView;
                iContainer = (LinearLayout) iView.findViewById(R.id.operation_container);
                iDetailsContainer = (LinearLayout) iView.findViewById(R.id.details_container);
                iOperationIcon = (ImageView) iView.findViewById(R.id.ledgerOperationIcon);
                iOperationActionImage = (ImageView) iView.findViewById(R.id.ledgerAction);
                iOperation = (TextView) iView.findViewById(R.id.ledgerOperationDescription);
                iAmount = (TextView) iView.findViewById(R.id.ledgerOperationCurrencyAmount);
                iTimestamp = (TextView) iView.findViewById(R.id.ledgerOperationTimestamp);
                iStatus = (TextView) iView.findViewById(R.id.ledgerOperationStatus);

                // This linear layout status is GONE in order to avoid the view to use space
                // even when it is not seen, when any element selected the Adapter will manage the
                // behavior for showing the layout - container
                iDetailsContainer.setVisibility(View.GONE);
            

            ...
            ...
                Not needed methods
            ...
            ...
        
    

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/operation_container_maximum"
    android:layout_
    android:layout_
    android:layout_marginBottom="11dp"
    android:layout_marginLeft="30dp"
    android:layout_marginRight="30dp"
    android:layout_marginTop="11dp"
    android:orientation="vertical">


    <LinearLayout
        android:id="@+id/operation_container"
        android:layout_
        android:layout_
        android:orientation="horizontal">

        <FrameLayout
            android:layout_
            android:layout_
            android:layout_marginRight="14dp">

            <ImageView
                android:id="@+id/ledgerOperationIcon"
                android:layout_
                android:layout_
                android:src="@drawable/fondear" />

            <ImageView
                android:id="@+id/ledgerAction"
                android:layout_
                android:layout_
                android:layout_gravity="bottom|right"
                android:src="@drawable/operation_trade" />


        </FrameLayout>

        <LinearLayout
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:orientation="vertical"
            android:weightSum="2">

            <LinearLayout
                android:layout_
                android:layout_
                android:layout_gravity="center_vertical"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/ledgerOperationDescription"
                    android:layout_
                    android:layout_
                    android:layout_weight="1"
                    android:text="Descripcion"
                    android:textColor="@color/ledger_desc" />

                <TextView
                    android:id="@+id/ledgerOperationCurrencyAmount"
                    android:layout_
                    android:layout_
                    android:layout_marginRight="2dp"
                    android:text="5000 BTC" />
            </LinearLayout>

            <LinearLayout
                android:layout_
                android:layout_
                android:layout_gravity="center_vertical"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/ledgerOperationTimestamp"
                    android:layout_
                    android:layout_
                    android:layout_weight="1"
                    android:text="Fecha/Hora"
                    android:textColor="@color/ledger_timestamp" />

                <TextView
                    android:id="@+id/ledgerOperationStatus"
                    android:layout_
                    android:layout_
                    android:text="Status" />
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>


    <LinearLayout
        android:id="@+id/details_container"
        android:layout_
        android:layout_
        android:orientation="vertical">



        <TextView
            android:layout_
            android:layout_
            android:layout_gravity="center_horizontal"
            android:text="Something hidden" />

        <ImageView
            android:layout_marginTop="15dp"
            android:layout_
            android:layout_
            android:src="@drawable/user_btc"
            android:layout_gravity="center_horizontal"/>

    </LinearLayout>

</LinearLayout>

片段

    // This is one of the fragments used in the RecyclerViewAdapterCode, and also makes a HTTPRequest to fill the
    // view dynamically, you could laso use any of your fragments.
    public class FragmentCard extends Fragment 

        TextView mTextView;

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) 
            super.onCreate(savedInstanceState);
        

        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) 
            View view = inflater.inflate(R.layout.fragment_card, container, false);

            mTextView = (TextView) view.findViewById(R.id.tv_fragment_two);
            new UserActivityAsyncTask().execute();
            return view;
        

        private UserActivityOperation[] asyncMethodGetPendingWithdrawals()
            BitsoWithdrawal[] userWithdrawals = HttpHandler.getUserWithdrawals(getActivity());
            int totalWithDrawals = userWithdrawals.length;
            UserActivityOperation[] appUserActivities = new UserActivityOperation[totalWithDrawals];
            for(int i=0; i<totalWithDrawals; i++)
                appUserActivities[i] = new UserActivityOperation(userWithdrawals[i], getActivity());
            
            return appUserActivities;
        

        private class UserActivityAsyncTask extends AsyncTask<String, Void, Integer> 
            @Override
            protected void onPreExecute() 
                super.onPreExecute();
            

            @Override
            protected Integer doInBackground(String... strings) 
                // Precess Compound balance
                UserActivityOperation[] compoundBalanceProcessed = asyncMethodGetPendingWithdrawals();
                return compoundBalanceProcessed.length;
            

            @Override
            protected void onPostExecute(Integer result) 
                super.onPostExecute(result);
                mTextView.setText(result.toString());
            
        
    

【讨论】:

嘿!感谢您的惊人解决方案。我能够做我想做的事,但只面临一个问题,即我只在 Recycler 视图中的最后一项上收到“Resources$NotFoundException”,请注意它即将出现在最后一项上。我尝试增加和减少 rc 视图中的项目数,但结果是一样的。你能帮我吗? ? @nimi0112 为了帮助您,我需要知道您何时收到此异常。我认为可能有不止一个地方可以获得它。 1.也许你没有正确地膨胀视图,所以没有找到布局元素。 2. 可能找不到您要设置或查找的资源,请检查您的资源是否可用。为了帮助您,请尽可能提交您的堆栈跟踪,或者请分享代码中发生错误的位置。 看看这里***.com/questions/48202131/… 嘿!兄弟。我已经制作了一个从主项目中分离出来的示例应用程序,你能帮我解决这个问题吗?请提供您的电子邮件ID,以便我可以将示例应用程序的源代码发送给您。 当然 victor.cruz.isc@gmail.com

以上是关于RecyclerView 项目中的片段替换的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AsyncTask 更新 RecyclerView 项目

RecyclerView smoothScroll位于中心位置。安卓

从片段类中的对话框添加项目到recyclerview。

在使用 FirebaseRecyclerPagingAdapter 时,第二次单击 RecyclerView 中的项目时,片段显示为空

更新片段中的 RecyclerView

android studio中片段内的RecyclerView使我的应用程序崩溃