I am completely new to Android Studio and just learned Object-oriented programming. My project requires me to build something on open-source code. I added a new menu item to a menu and want to start another activity once the user clicks the menu item with id: plot. The intent will be sent from if (id == R.id.plot) within onOptionsItemSelected(MenuItem item) of TerminalFragment. For building activity_main2 with a menu, I followed a recipe on the internet to do it. I found out that the page of activity_main2 is blank (all black, no either toolbar or menu) when testing. What is missing with my code?
TerminalFragment
package de.kai_morich.simple_bluetooth_le_terminal; import android.app.Activity; import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.text.Editable; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.method.ScrollingMovementMethod; import android.text.style.ForegroundColorSpan; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import android.widget.EditText; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; public class TerminalFragment extends Fragment implements ServiceConnection, SerialListener { private MenuItem menuItem; private enum Connected { False, Pending, True } private String deviceAddress; private SerialService service; private TextView receiveText; private TextView sendText; private TextUtil.HexWatcher hexWatcher; private Connected connected = Connected.False; private boolean initialStart = true; private boolean hexEnabled = false; private boolean pendingNewline = false; private String newline = TextUtil.newline_crlf; /* * Lifecycle */ @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); //Register with activity // You must inform the system that your app bar fragment is participating in the population of the options menu. // tells the system that your fragment would like to receive menu-related callbacks. setRetainInstance(true); deviceAddress = getArguments().getString("device"); } @Override public void onDestroy() { if (connected != Connected.False) disconnect(); getActivity().stopService(new Intent(getActivity(), SerialService.class)); super.onDestroy(); } @Override public void onStart() { super.onStart(); if(service != null) service.attach(this); else getActivity().startService(new Intent(getActivity(), SerialService.class)); // prevents service destroy on unbind from recreated activity caused by orientation change } @Override public void onStop() { if(service != null && !getActivity().isChangingConfigurations()) service.detach(); super.onStop(); } @SuppressWarnings("deprecation") // onAttach(context) was added with API 23. onAttach(activity) works for all API versions @Override public void onAttach(@NonNull Activity activity) { super.onAttach(activity); getActivity().bindService(new Intent(getActivity(), SerialService.class), this, Context.BIND_AUTO_CREATE); } @Override public void onDetach() { try { getActivity().unbindService(this); } catch(Exception ignored) {} super.onDetach(); } @Override public void onResume() { super.onResume(); if(initialStart && service != null) { initialStart = false; getActivity().runOnUiThread(this::connect); } } @Override public void onServiceConnected(ComponentName name, IBinder binder) { service = ((SerialService.SerialBinder) binder).getService(); service.attach(this); if(initialStart && isResumed()) { initialStart = false; getActivity().runOnUiThread(this::connect); } } @Override public void onServiceDisconnected(ComponentName name) { service = null; } /* * UI */ @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_terminal, container, false); receiveText = view.findViewById(R.id.receive_text); // TextView performance decreases with number of spans receiveText.setTextColor(getResources().getColor(R.color.colorRecieveText)); // set as default color to reduce number of spans receiveText.setMovementMethod(ScrollingMovementMethod.getInstance()); sendText = view.findViewById(R.id.send_text); hexWatcher = new TextUtil.HexWatcher(sendText); hexWatcher.enable(hexEnabled); sendText.addTextChangedListener(hexWatcher); sendText.setHint(hexEnabled ? "HEX mode" : ""); View sendBtn = view.findViewById(R.id.send_btn); sendBtn.setOnClickListener(v -> send(sendText.getText().toString())); return view; } @Override public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.menu_terminal, menu); menu.findItem(R.id.hex).setChecked(hexEnabled); } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.clear) { receiveText.setText(""); return true; } if (id == R.id.plot){ Intent intent = new Intent(getActivity(), MainActivity2.class); startActivity(intent); return true; }else if (id == R.id.newline) { String[] newlineNames = getResources().getStringArray(R.array.newline_names); String[] newlineValues = getResources().getStringArray(R.array.newline_values); int pos = java.util.Arrays.asList(newlineValues).indexOf(newline); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("Newline"); builder.setSingleChoiceItems(newlineNames, pos, (dialog, item1) -> { newline = newlineValues[item1]; dialog.dismiss(); }); builder.create().show(); return true; } else if (id == R.id.hex) { hexEnabled = !hexEnabled; sendText.setText(""); hexWatcher.enable(hexEnabled); sendText.setHint(hexEnabled ? "HEX mode" : ""); item.setChecked(hexEnabled); return true; } else { return super.onOptionsItemSelected(item); } } /* * Serial + UI */ private void connect() { try { BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress); status("connecting..."); connected = Connected.Pending; SerialSocket socket = new SerialSocket(getActivity().getApplicationContext(), device); service.connect(socket); } catch (Exception e) { onSerialConnectError(e); } } private void disconnect() { connected = Connected.False; service.disconnect(); } private void send(String str) { if(connected != Connected.True) { Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show(); return; } try { String msg; byte[] data; if(hexEnabled) { StringBuilder sb = new StringBuilder(); TextUtil.toHexString(sb, TextUtil.fromHexString(str)); TextUtil.toHexString(sb, newline.getBytes()); msg = sb.toString(); data = TextUtil.fromHexString(msg); } else { msg = str; data = (str + newline).getBytes(); } SpannableStringBuilder spn = new SpannableStringBuilder(msg + 'n'); spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorSendText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); receiveText.append(spn); service.write(data); } catch (Exception e) { onSerialIoError(e); } } private void receive(byte[] data) { if(hexEnabled) { receiveText.append("Hello" + TextUtil.toHexString(data) + 'n'); } else { String msg = new String(data); if(newline.equals(TextUtil.newline_crlf) && msg.length() > 0) { // don't show CR as ^M if directly before LF msg = msg.replace(TextUtil.newline_crlf, TextUtil.newline_lf); // special handling if CR and LF come in separate fragments if (pendingNewline && msg.charAt(0) == 'n') { Editable edt = receiveText.getEditableText(); if (edt != null && edt.length() > 1) edt.replace(edt.length() - 2, edt.length(), ""); } pendingNewline = msg.charAt(msg.length() - 1) == 'r'; } receiveText.append(TextUtil.toCaretString(msg, newline.length() != 0)); //print out data } } private void status(String str) { SpannableStringBuilder spn = new SpannableStringBuilder(str + 'n'); spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorStatusText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); receiveText.append(spn); } /* * SerialListener */ @Override public void onSerialConnect() { status("connected"); connected = Connected.True; } @Override public void onSerialConnectError(Exception e) { status("connection failed: " + e.getMessage()); disconnect(); } @Override public void onSerialRead(byte[] data) { receive(data); } @Override public void onSerialIoError(Exception e) { status("connection lost: " + e.getMessage()); disconnect(); } }
MainActivity2.java defines activity_main2.xml
package de.kai_morich.simple_bluetooth_le_terminal; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Toast; public class MainActivity2 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); Toolbar toolbar = findViewById(R.id.toolbar2); setSupportActionBar(toolbar); Intent intent = getIntent(); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_plot,menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.dataplot: Toast.makeText(this, "dataplot", Toast.LENGTH_SHORT).show(); return true; case R.id.fft: Toast.makeText(this, "FFT", Toast.LENGTH_SHORT).show(); return true; case R.id.data: Toast.makeText(this, "DATA", Toast.LENGTH_SHORT).show(); return true; default: return super.onOptionsItemSelected(item); } } }
activity_main2.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity2"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar2" android:layout_width="409dp" android:layout_height="wrap_content" android:layout_marginStart="1dp" android:layout_marginEnd="1dp" android:layout_marginBottom="675dp" android:background="?attr/colorPrimary" android:minHeight="?attr/actionBarSize" android:theme="?attr/actionBarTheme" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
Advertisement
Answer
I think your Toolbar is pushed out of the screen by the layout_marginBottom = 675dp
. If you want to show the Toolbar
at top of the screen I would suggest this:
<androidx.appcompat.widget.Toolbar android:id="@+id/toolbar2" android:layout_width="0dp" android:layout_height="?attr/actionBarSize" android:layout_marginStart="1dp" android:layout_marginEnd="1dp" android:background="?attr/colorPrimary" android:theme="?attr/actionBarTheme" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />