改变从活动中的片段TextView。

huangapple go评论67阅读模式
英文:

Changing Fragment TextView from Activity

问题

我正在尝试使用免费的Udemy课程“学习Android应用开发”来学习Android Studio。我正在进行第44课“片段第一部分”。由于出现错误,我无法完成这节课。我已经花了两天和大约8小时来尝试解决它。请帮助我!

这节课的目标是创建一个应用程序,其中屏幕的一侧有一个列表,另一侧有详细信息。当用户单击列表中的项时,详细信息将显示在屏幕的另一侧的TextView上。

讲师指导我们创建两个片段(ListFrag和DetailFrag),并将它们并排添加到主活动中。他介绍了设置列表片段(ListFrag)和将单击的列表项的索引传递给主活动。所有这些都有效。

DetailFrag包含一个TextView(tvDescription)。讲师设置了TextView并使用ListFrag的索引更改了它的值。当运行应用程序时,单击列表项时会崩溃。

存在一个错误 - '@layout/activity_main'中没有具有'id tvDescription'的声明。问题出现在MainActivity.java中的tvDescriptions = findViewById(R.id.tvDescription)。

请帮忙。提前感谢您的回复。

英文:

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

得分: 0

您的代码接近正确。这是因为当您在MainActivityonCreate()方法中定义TextView时,DetailFrag尚未创建。您可以参考Fragment的生命周期与其Activity相关以了解有关Fragment生命周期的更多信息。

所以有两种方法可以更改您的代码:

第一种方法是删除TextView变量的定义,并直接在onItemSelected()回调中调用findViewById()

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("Item 1的描述");
        descriptions.add("Item 2的描述");
        descriptions.add("Item 3的描述");

    }

    @Override
    public void onItemSelected(int index) {
        // 直接在这里检索TextView
        ((TextView) findViewById(R.id.tvDescription)).setText(descriptions.get(index));
    }
}

第二种方法是将TextView的定义移到MainActivityonStart()方法中。这确保了DetailFrag已创建,因此MainActivity可以在那里从DetailFrag获取View:

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("Item 1的描述");
        descriptions add("Item 2的描述");
        descriptions.add("Item 3的描述");

    }

    @Override
    protected void onStart() {
        super.onStart();
        // 将定义移到这里
        tvDescription = findViewById(R.id.tvDescription);
    }

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

此外,您还需要查看以下问题:

您已经定义了一个成员变量ArrayList<String> descriptions;。然而,您在此处将ArrayList分配给一个局部变量:

ArrayList<String> descriptions = new ArrayList<String>();
descriptions.add("Item 1的描述");
descriptions.add("Item 2的描述");
descriptions.add("Item 3的描述");

您的onItemSelected()回调只能访问您的成员变量,而您尚未为其分配任何值。因此,您应该像这样删除ArrayList<String>

// 这将直接为成员变量分配值
descriptions = new ArrayList<String>();
descriptions.add("Item 1的描述");
descriptions.add("Item 2的描述");
descriptions.add("Item 3的描述");
英文:

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&lt;String&gt; descriptions;

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

        ArrayList&lt;String&gt; descriptions = new ArrayList&lt;String&gt;();
        descriptions.add(&quot;Description for item 1&quot;);
        descriptions.add(&quot;Description for item 2&quot;);
        descriptions.add(&quot;Description for item 3&quot;);

    }

    @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&lt;String&gt; descriptions;
    TextView tvDescription;

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

        ArrayList&lt;String&gt; descriptions = new ArrayList&lt;String&gt;();
        descriptions.add(&quot;Description for item 1&quot;);
        descriptions.add(&quot;Description for item 2&quot;);
        descriptions.add(&quot;Description for item 3&quot;);

    }

    @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&lt;String&gt; descriptions;. However, you are setting ArrayList to a local variable here:

ArrayList&lt;String&gt; descriptions = new ArrayList&lt;String&gt;();
descriptions.add(&quot;Description for item 1&quot;);
descriptions.add(&quot;Description for item 2&quot;);
descriptions.add(&quot;Description for item 3&quot;);

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&lt;String&gt; like this:

// This will directly assign value to member variable
descriptions = new ArrayList&lt;String&gt;();
descriptions.add(&quot;Description for item 1&quot;);
descriptions.add(&quot;Description for item 2&quot;);
descriptions.add(&quot;Description for item 3&quot;);

huangapple
  • 本文由 发表于 2023年2月7日 01:07:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/75364421.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定