-1

this is my main activity:

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());

         
        }
    }
}

And this is one my 3 fragments, the other 2 are almost the same:

    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);
    }
}

When executing rulesFragment.setText((String)in.readObject()); I get this error: Only the original thread that created a view hierarchy can touch its views

That's because I created the textview in onCreateView but I'm editing it in setText right? The problem is that I need to edit that text multiple times during the program execution and I cannot transfer part of my code in onCreateView (to make it run like a separate thread i guess?) because I need to retrieve input from the Socket sequentially.

Is there another way to do this?

Moreover let's say I have a Spinner in the third fragment and a button "Send". When the user hits Send I'm supposed to reset every textview in each fragment to empty and I need to restart the execution in PrimeThread in LearnTree class. How can I do this? Is there a way to detect the onClick event of send button from the mainactivity?

2 Answers2

0

To answer your main issue, what is happening is that the thread that creates the view is the Main UI thread. For this reason, whenever you want to change something on the UI from a different thread (in your case the PrimeThread), you should execute the functions by using runOnUiThread.

This means in your code you should have:

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);
                    }
                });

For your last question about having a listener for a button click, you can do it like this:

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
    } 
});

If you want to restart the thread in the LearnTree Activity, you need to store the thread in a class variable:

public LearnTree.PrimeThread primeThread;

Or declare it as private and have a getter/setter, it's up to you.


Also, you should create your ViewPagerAdapter like this, otherwise you will get crashes:

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);
    }
}

To get a reference to a Fragment created by a ViewPager, use the following findFragmentByTag scheme:

Fragment fragment = supportFragmentManager.findFragmentByTag("android:switcher:" + viewPager.getId() + ":" + fragmentPosition)
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
therock24
  • 89
  • 3
  • Thank you so much, it works! I just had to store the in.readObject value in two different String variables since it gave me networkonmainthreadexception. About the listener, that code is used for the fragment in which I will create my button right? But what if I want to run a method in my mainactivity on the onClick event? I don't know if I should create a new question, if that's the case let me know. Apologies but I'm a new user – Filippo Iacobellis Nov 05 '20 at 22:25
  • I have just updated the answer, covering how you can restart the thread on the LearnTree activity. Hope it helps – therock24 Nov 05 '20 at 22:43
0

The previous answer is very complete. However, you could try by calling the:

        Device.BeginInvokeOnMainThread(
            () => { lblStatus.Text = "Updating..."; }
        );