英文:
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
您的代码接近正确。这是因为当您在MainActivity的onCreate()方法中定义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的定义移到MainActivity的onStart()方法中。这确保了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<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");
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论