在Adapter中回调Fragment来更新UI
今天在工作中遇到这样一个问题:在Adapter中执行了异步任务,在更新UI的时候需要根据Fragment来回调,具体情况如下
首先看我们需要实现什么功能
在Content Subscriptions中我们有一个item1,我们通过异步任务取消item1的订阅:
在我们取消订阅item1后我们理想中应该是这样:也就是把content subscription的这个linearlayout设置为不可见
** 但是其实实际中我们实现的是这样:**
从UI 来看"CONTENT SUBSCRIPTION"这一行字没有按照我们预想的那样消失,也就是在代码中没有把这个LinearLayout设置为不可见。我们必须重新进入这个界面以后"CONTENT SUBSCRIPTION"才会不可见。
问题出现了,我们需要的流程是这样:取消订阅item->content subscription消失,但是实际的流程却是这样:取消订阅item->重新打开界面->content subscription消失。
布局文件是这样的: 代码有删减,我们只关注Content Subscription这一块
<LinearLayout
android:id="@+id/quota_detail_item_area"
android:layout_marginTop="@dimen/margin_big"
style="@style/div.vlist"
android:visibility="gone">
<TextView
style="@style/v3.subtitle"
android:paddingLeft="@dimen/margin_big"
android:paddingRight="@dimen/margin_big"
android:text="Content Subscription" />
<android.support.v7.widget.CardView
style="@style/v3.card.nopadding"
android:layout_marginTop="@dimen/card_margin_top"
android:layout_morginBottom="dimen/card_margin"
android:layout_marginBottom="@dimen/card_margin_bottom"
android:background="@color/white">
<LinearLayout
style="@style/div.vlist">
<android.support.v7.widget.RecyclerView
android:id="@+id/quota_detail_item_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
那么为什么会出现这样的情况呢,我们去代码里看一看:在Adapter中我们通过一个异步任务来执行item的取消订阅操作,在这里就不放完整代码了,只放更新UI的代码。在Adapter.java中,我们在onPostExecute()中进行更新UI的操作(代码有删减,只显示和本文有关的部分):
public class VASAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private Context mContext;
private ArrayList<Item> mList;
public Adapter(Context context, ArrayList<Item> itemSubscriptions) {//Adapter构造函数
mContext = context;
mList = itemSubscriptions;
}...
class UnsubscribeTask extends AsyncTask<Void, Void, Boolean>{
...@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
showSuccessfulDlg();
mList.remove(mPosition); //移除这个item
Adapter.this.notifyDataSetChanged(); // 提示更新mList
} else {
showFailDlg();
}
}
}
}
在Fragment.java中我们这样设置适配器:
public class Fragment extends ToolbarFragment {
private boolean mHideItem;
private ArrayList<Item> mActiveItem;
private RecyclerView mItemList
private LinearLayout mItemArea;
mItemArea = mFragmentView.findViewById(R.id.quota_detail_item_area);//在这个fragment中找到content subscription所在的linearlayout
mItemList = mFragmentView.findViewById(R.id.quota_detail_item_list);
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
...
if (mActiveItem != null && mActiveItem.size() > 0) {// 如果订阅的item个数不是0,那么配置适配器
mItemList.setAdapter(new Adapter(getActivity(), mActiveItem));
hideItemQuota(false);
showLoadErrorView(false);
} else {//如果订阅的item个数是0,那么将linearlayout设置为不可见,但是取消订阅后的第一时间不会执行这一句,必须重新进入这个页面重新判断后才会执行
hideItemQuota(true);
}
}...
...
private void hideItemQuota(boolean hide) {
mHideItem = hide;
mItemArea.setVisibility(hide ? View.GONE : View.VISIBLE);
}
}
hideItemQuota()就是设置content subscription这个linearlayout为不可见的函数。在代码中我们已经进行了对mActiveItem进行了判空操作,如果它mActiveItem为空,那么就把LinearLayout设置为不可见,但是为什么还是会出现而没有按照预想消失呢?
这是因为我们并没有在adapter的onPostExecute()中把Content Subscription即时更新为不可见, 所以导致item1消失后Content Subscription这个linearlayout仍然出现。虽然在fragment中对存放item的ArrayList进行了判空后对linearlayout设置了不可见。但是在取消订阅后的第一时间并不会执行这一句,必须重新进入这个界面,才会判断ArrayList是否为空然后执行else里面的hideItemQuota(true)这一句。那么应该如何解决这个问题呢?就是要把linearlayout设置为不可见加入在更新UI的操作中,但是有一个问题就是我们在adapter中是不能得到fragment的,那么我们应该怎么在adapter中监听fragment呢?答案是用回调函数,也就是adapter中回调fragment。下面介绍具体的代码。
1.在Adapter中添加回调函数:
public interface ContentAdapterCallback {
void onNoItem();
}
2.在Adapter的构造函数和onPostExecute()中加上callback 参数:
public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private Context mContext;
private ArrayList<Item> mList;
private ContentAdapterCallback mItemAdapterCallback;
public interface ContentAdapterCallback {
void onNoItem();
}
public Adapter(Context context, ArrayList<Item> itemSubscriptions, ContentAdapterCallback callback) {
mContext = context;
mList = itemSubscriptions;
mItemAdapterCallback = callback;
}...
class UnsubscribeTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
showSuccessfulDlg();
mList.remove(mPosition);//移除这个item
Adapter.this.notifyDataSetChanged();// 提示更新mList
if ((mList.size() == 0) && mItemAdapterCallback != null) {
mItemAdapterCallback.onNoItem();
}
} else {
showFailDlg();
}
}
}
}
3.在Fragment.java的setAdapter()中添加callback参数:
public class Fragment extends ToolbarFragment {
private RecyclerView mItemList;
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
...
if (mActiveItem != null && mActiveItem.size() > 0) {
mItemList.setAdapter(new Adapter(getActivity(), mActiveItem, new Adapter.ContentAdapterCallback() {
@Override
public void onNoItem() {
hideItemQuota(true);
}
}));
hideItemQuota(false);
showLoadErrorView(false);
}
}
...
private void hideItemQuota(boolean hide) {
mHideItem = hide;
mItemArea.setVisibility(hide ? View.GONE : View.VISIBLE);
}
}
这样的话就可以实现在执行了异步任务后实时更新UI,问题也就解决了。