首页 > 解决方案 > Android Fragments - 只有创建视图层次结构的原始线程才能接触其视图

问题描述

这是我的主要活动:

public class LearnTree extends AppCompatActivity {
    private RulesFragment rulesFragment;
    private TreeFragment treeFragment;
    private PredictionFragment predictionFragment;
    TabLayout tabLayout;
    ViewPager viewPager;
    private Button button;
    private static ObjectOutputStream out;
    private static ObjectInputStream in;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.learn_tree);
        tabLayout=findViewById(R.id.tab_layout);
        viewPager= findViewById((R.id.view_pager));
        final ViewPagerAdapter viewPagerAdapter= new ViewPagerAdapter(getSupportFragmentManager());
        viewPagerAdapter.addFragment(RulesFragment.getInstance(), "TREE RULES");
        viewPagerAdapter.addFragment(TreeFragment.getInstance(), "REGRESSION TREE");
        viewPagerAdapter.addFragment(PredictionFragment.getInstance(), "PREDICTION");
        rulesFragment= (RulesFragment) viewPagerAdapter.getItem(0);
        treeFragment= (TreeFragment) viewPagerAdapter.getItem(1);
        predictionFragment= (PredictionFragment) viewPagerAdapter.getItem(2);
        viewPager.setAdapter(viewPagerAdapter);
        tabLayout.setupWithViewPager(viewPager);

        LearnTree.PrimeThread p=new LearnTree.PrimeThread();
        p.start();
    }

    private class PrimeThread extends Thread {

        public void run() {
            out= SocketObject.getOut();
            in = SocketObject.getIn();
      

                // print rules
                rulesFragment.setText((String)in.readObject());
                //print tree
                treeFragment.setText((String)in.readObject());

         
        }
    }
}

这是我的 3 个片段之一,其他 2 个几乎相同:

    public class RulesFragment extends Fragment {
    // Store instance variables
    private String title;
    private int page;
    private TextView rulesView;


    public static RulesFragment getInstance() {
        RulesFragment rulesFragment = new RulesFragment();
        return rulesFragment;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view= inflater.inflate(R.layout.rules_fragment, container, false);
        rulesView= (TextView) view.findViewById(R.id.textview_treerules);
        rulesView.setMovementMethod(new ScrollingMovementMethod());
        return view;
    }

    public void setText(String text){
        rulesView.setText(text);
    }
}

执行时rulesFragment.setText((String)in.readObject());出现此错误:只有创建视图层次结构的原始线程才能触摸其视图

那是因为我在 onCreateView 中创建了 textview 但我在 setText 中编辑它,对吗?问题是我需要在程序执行期间多次编辑该文本,并且我无法在 onCreateView 中传输部分代码(我猜是让它像一个单独的线程一样运行?)因为我需要按顺序从 Socket 检索输入。

还有另一种方法可以做到这一点吗?

此外,假设我在第三个片段中有一个 Spinner 和一个“发送”按钮。当用户点击发送时,我应该将每个片段中的每个 textview 重置为空,并且我需要在 LearnTree 类的 PrimeThread 中重新启动执行。我怎样才能做到这一点?有没有办法从 mainactivity 检测发送按钮的 onClick 事件?

标签: androidandroid-fragments

解决方案


要回答您的主要问题,正在发生的事情是创建视图的线程是主 UI 线程。出于这个原因,每当您想从不同的线程(在您的情况下为 PrimeThread)更改 UI 上的某些内容时,您应该使用runOnUiThread.

这意味着在你的代码中你应该有:

String rules = (String)in.readObject()
String tree = (String)in.readObject()
requireActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // print rules
                        rulesFragment.setText(rules);
                        //print tree
                        treeFragment.setText(tree);
                    }
                });

对于您关于让按钮单击的侦听器的最后一个问题,您可以这样做:

Button buttonY = (Button)findViewById(R.id.buttonYName);
// Register the onClick listener with the implementation above
buttonY.setOnClickListener(new OnClickListener() {
    public void onClick(View v)
    {
        (LearnTree)getActivity().primeThread.start(); // Assuming this is executed in the context of a Fragment
    } 
});

如果要在 LearnTree Activity 中重启线程,则需要将线程存储在类变量中:

public LearnTree.PrimeThread primeThread;

或者将其声明为私有并拥有一个 getter/setter,这取决于您。


此外,您应该像这样创建 ViewPagerAdapter,否则您将遇到崩溃:

public class ViewPagerAdapter extends FragmentPagerAdapter {
    public ViewPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @Override
    public Fragment getItem(int position) {
        if(position == 0) return new RulesFragment();
        if(position == 1) return new TreeFragment();
        if(position == 2) return new PredictionFragment();

        throw new IllegalStateException("Unexpected position " + position);
    }

    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        if(position == 0) return "TREE RULES";
        if(position == 1) return "REGRESSION TREE";
        if(position == 2) return "PREDICTION";
      
        throw new IllegalStateException("Unexpected position " + position);
    }
}

要获取对 ViewPager 创建的 Fragment 的引用,请使用以下findFragmentByTag方案:

Fragment fragment = supportFragmentManager.findFragmentByTag("android:switcher:" + viewPager.getId() + ":" + fragmentPosition)

推荐阅读