当前位置: 首页 > 知识库问答 >
问题:

只有创建视图层次结构的原始线程才能接触其视图。-奇怪的行为

车峻熙
2023-03-14

这是一种非常奇怪的行为,我不知道如何修复它。

我有一个作为演示者的活动(在MVP架构中)。当活动开始时,我附加一个片段作为视图。片段本身非常简单。

public class CurrentSaleFragment extends BaseFragment {

private MainMVP.SalesPresenterOps salesPresenterOps;
private SaleAdapter adapter;
private ListView lv;


@BindView(R.id.btn_sell)
FloatingActionButton btnAdd;

public static CurrentSaleFragment newInstance(){
    CurrentSaleFragment fragment = new CurrentSaleFragment();
    Bundle arguments = new Bundle();
    arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_quick_sale );
    fragment.setArguments(arguments);
    return fragment;
}

@Override
protected void init() {
    super.init();
    lv = (ListView)view.findViewById(R.id.lv_sale);
}

@OnClick(R.id.btn_sell)
public void addToSale(View view){
   mPresenter.moveToFragment(SellProductFragment.newInstance());
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    salesPresenterOps = (MainMVP.SalesPresenterOps)context;
}

@Override
public void onDetach() {
    salesPresenterOps = null;
    super.onDetach();
}
}

此片段扩展的BaseFraank:

public class BaseFragment extends Fragment implements MainMVP.RequiredViewOps, View.OnClickListener,
    LoaderRequiredOps{

protected View view;
protected MainMVP.PresenterOps mPresenter;


protected final static String LAYOUT_RES_ID = "layout_res_id";

@Override
public void showOperationResult(String message, final long rowId) {
    Snackbar.make(view, message, Snackbar.LENGTH_LONG).setAction(
            R.string.see, new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onOperationResultClick(rowId);
                }
            }
    ).show();
}

@Override
public void showSnackBar(String msg) {
    Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show();
}

@Override
public void showAlert(String msg) {}



protected void onOperationResultClick(long rowId){}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    mPresenter = (MainMVP.PresenterOps)context;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    this.view = inflater.inflate(getArguments().getInt(LAYOUT_RES_ID), null);
    init();
    return view;
}

protected void addToClickListener(View ... params){
    for (View v : params){
        v.setOnClickListener(this);
    }
}

protected void init() {
    if (view != null){
        ButterKnife.bind(this, view);
    }
}

@Override
public void onDetach() {
    mPresenter = null;
    Log.d(getClass().getSimpleName(), "Fragment was detached");
    super.onDetach();
}

@Override
public void onClick(View v) {}

@Override
public void onPreLoad() {
    Dialogs.buildLoadingDialog(getContext(), "Loading...").show();
}

@Override
public void onLoad() {}

@Override
public void onDoneLoading() {
    Dialogs.dismiss();
}

}

当我输入“moveToFragment()”方法时,我只是将CurrentSaleFragment替换为一个新片段:

protected void addFragment(BaseFragment fragment){
    mView = fragment;
    getSupportFragmentManager().beginTransaction().replace(R.id.fragment_holder,
            fragment, null).addToBackStack(null).commit();
}

然后附上新的片段:

public class SellProductFragment extends BaseFragment{

private ListView listView;
private ProductListAdapter adapter;
private MainMVP.SalesPresenterOps mSalesPresenter;

public static SellProductFragment newInstance(){
    SellProductFragment fragment = new SellProductFragment();
    Bundle arguments = new Bundle();
    arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_inventory);
    fragment.setArguments(arguments);
    return fragment;
}

private void reload(){
    final Loader loader = new Loader(this);
    loader.execute();
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    mSalesPresenter = (MainMVP.SalesPresenterOps)context;
}

@Override
protected void init() {
    super.init();
    listView = (ListView)view.findViewById(R.id.lv_inventory);
    reload();
    FloatingActionButton button = (FloatingActionButton)view.findViewById(R.id.btn_add);
    addToClickListener(button);
}

@Override
public void onLoad() {
    adapter = new ProductListAdapter(getActivity().getApplicationContext(), R.layout.row_product_item,
            mSalesPresenter.getProducts());
    try{
        updateListView();
    }catch (Exception e){
        Log.w(getClass().getSimpleName(), e.getMessage());
    }
}

private void updateListView(){
    if (adapter != null && listView != null){
        listView.setAdapter(adapter);
    }else{
        throw new RuntimeException();
    }
}
}

请参阅此片段也从BaseFraank扩展并实现Loader必备操作。该接口用于“加载”任何数据。加载完成后,它会添加一个对话框并更新适配器:

public class Loader extends AsyncTask<Void, Void, Void> {

private LoaderRequiredOps presenter;

public Loader(LoaderRequiredOps presenter){
   this.presenter = presenter;
}

@Override
protected void onPreExecute() {
    super.onPreExecute();
    presenter.onPreLoad();
}

@Override
protected Void doInBackground(Void... params) {
    presenter.onLoad();
    return null;
}

@Override
protected void onPostExecute(Void aVoid) {
    super.onPostExecute(aVoid);
    presenter.onDoneLoading();
    presenter = null;
}
}

现在,当我尝试从SellProductFragment执行方法reload()时,我得到了“只有创建视图层次结构的原始线程才能接触其视图”

如果先附加SellProductFragment而不是CurrentSaleFragment,则不会发生这种情况。

这里到底发生了什么?

共有2个答案

鲜于渊
2023-03-14

出于未知原因,未识别的配置允许在doInbackground()方法上执行ListView的适配器设置。将其移动到onPostExecute(),现在它正在工作

金昂熙
2023-03-14

您的Async Loader类在doInbackground()期间从后台线程调用演示者方法onLoad()

我的猜测是在演示者的onLoad()方法中,引用了一个视图。

为了在这一点上更改视图,将视图逻辑作为Runnable发布到UI线程(您说您的演示者是活动,因此这应该可以从onLoad方法)。

@Override
public void onLoad() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            // Your ui code here...
        }
    });

    // Rest of your code here...
}
 类似资料: