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位于中心位置。安卓
在使用 FirebaseRecyclerPagingAdapter 时,第二次单击 RecyclerView 中的项目时,片段显示为空