0

I am attempting to learn Android Studios using the free Udemy course "Learn Android Application Development." I am on lesson 44 "Fragments Part 1." I am unable to get through the lesson because of an error. I have spent two days and ~8 hours trying to figure it out. Please help me!

The goal of the lesson is to create an app that has a list on one side of the screen and the details on the other. When the user clicks one of the list items, the details are displayed on a textview on the other side of the screen.

The instructor guides us through creating two fragments (ListFrag and DetailFrag) and adding them to the main activity side-by-side. He goes through setting up the list fragment (ListFrag) and passing the clicked list item's index to the main activity. All of that works.

DetailFrag contains a TextView (tvDescription). The instructor sets up the textview and changes the value from the main activity using the index from ListFrag. When running the app, it crashes when you click a list item.

There is one error - '@layout/activity_main' does not contain a declaration with id 'tvDescription'. The issue is in MainActivity.java, tvDescriptions = findViewById(R.id.tvDescription);.

Please help. Thanks in advance for your replies.

XML Files:

activity_main.xml

?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="horizontal"
    tools:context=".MainActivity">


    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/listFrag"
        android:name="com.example.fragmentvideorev2.ListFrag"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        tools:layout="@layout/fragment_list" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/detailFrag"
        android:name="com.example.fragmentvideorev2.DetailFrag"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        tools:layout="@layout/fragment_detail" />
</LinearLayout>

fragment_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"

    tools:context=".ListFrag">


    <ListView
        android:id="@+id/lvList"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

fragment_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/teal_700"
    tools:context=".DetailFrag">


    <TextView
        android:id="@+id/tvDescription"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginRight="20dp"
        android:text="TextView"
        android:textColor="#FFFFFF"
        android:textSize="18sp" />
</LinearLayout>

Java Files:

MainActivity.java

package com.example.fragmentvideorev2;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;

import android.os.Bundle;
import android.widget.TextView;

import java.lang.reflect.Array;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements ListFrag.ItemSelected {

    TextView tvDescriptions;
    ArrayList<String> descriptions;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tvDescriptions = findViewById(R.id.tvDescription);



        ArrayList<String> descriptions = new ArrayList<String>();
        descriptions.add("Description for item 1");
        descriptions.add("Description for item 2");
        descriptions.add("Description for item 3");

          }


    @Override
    public void onItemSelected(int index) {

        tvDescriptions.setText(descriptions.get(index));

    }
}

ListFrag.java

package com.example.fragmentvideorev2;

import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.ListFragment;
import androidx.lifecycle.LifecycleObserver;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;

public class ListFrag extends ListFragment {

    ItemSelected activity;

    public interface ItemSelected
    {
        void onItemSelected(int index);
    }

     public ListFrag() {
        // Required empty public constructor
    }


   //called when frag associated with activity, context is activity
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);

        activity = (ItemSelected) context;
    }

   //called when oncreate is finished
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        ArrayList<String> data = new ArrayList<String>();
        data.add("1. This is one");
        data.add("2. This is two");
        data.add("3. This is three");

        setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_expandable_list_item_1, data));

    }

    //value list get clicked, know posiiton
    @Override
    public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {

        activity.onItemSelected(position);

    }
}

DetailFrag.java - unchanged after creation

package com.example.fragmentvideorev2;

import android.os.Bundle;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.fragment.app.Fragment;

import org.w3c.dom.Text;



public class DetailFrag extends Fragment {

   // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    public DetailFrag() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment DetailFrag.
     */

    // TODO: Rename and change types and number of parameters
    public static DetailFrag newInstance(String param1, String param2) {
        DetailFrag fragment = new DetailFrag();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_detail, container, false);
    }


}

1 Answers1

0

Your code is close. It is because DetailFrag is not yet created when you define your TextView in onCreate() of MainActivity. You can refer Fragment lifecycle with respect to it's activity to know more about the lifecycle of Fragment.

So there are two ways you can change your code:

The first way is to remove definition of your TextView variable and directly call findViewById() in the onItemSelected() callback:

package com.example.fragmentvideorev2;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements ListFrag.ItemSelected {
    ArrayList<String> descriptions;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ArrayList<String> descriptions = new ArrayList<String>();
        descriptions.add("Description for item 1");
        descriptions.add("Description for item 2");
        descriptions.add("Description for item 3");

    }

    @Override
    public void onItemSelected(int index) {
        // Directly retrieve the TextView here
        ((TextView) findViewById(R.id.tvDescription)).setText(descriptions.get(index));
    }
}

The second way is to move the definition of your TextView in onStart() of MainActivity. That guarantees your DetailFrag is created and hence MainActivity can obtain the View from DetailFrag there:

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements ListFrag.ItemSelected {
    ArrayList<String> descriptions;
    TextView tvDescription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ArrayList<String> descriptions = new ArrayList<String>();
        descriptions.add("Description for item 1");
        descriptions.add("Description for item 2");
        descriptions.add("Description for item 3");

    }

    @Override
    protected void onStart() {
        super.onStart();
        // Move the definition here
        tvDescription = findViewById(R.id.tvDescription);
    }

    @Override
    public void onItemSelected(int index) {
        tvDescription.setText(descriptions.get(index));
    }
}

And also you may have to take a look at the following problem:

You have defined a member variable ArrayList<String> descriptions;. However, you are setting ArrayList to a local variable here:

ArrayList<String> descriptions = new ArrayList<String>();
descriptions.add("Description for item 1");
descriptions.add("Description for item 2");
descriptions.add("Description for item 3");

And your onItemSelected() callback can just access your member variable, which you have not assigned any value to it. Therefore, you should remove the ArrayList<String> like this:

// This will directly assign value to member variable
descriptions = new ArrayList<String>();
descriptions.add("Description for item 1");
descriptions.add("Description for item 2");
descriptions.add("Description for item 3");
Enowneb
  • 943
  • 1
  • 2
  • 11
  • 1
    You were completely correct. Moving findViewById() to onItemSelected() or onStart both fixed the declaration issue. The new error was with the array tvDescriptions. Your solution fixed that as well. Thank you so much! You have saved me a lot of time and frustration! – Gregory Maguire Feb 07 '23 at 14:07