从另一个片段中的目录更新片段中的列表视图元素

Posted

技术标签:

【中文标题】从另一个片段中的目录更新片段中的列表视图元素【英文标题】:Updating Elements of Listview in a Fragment from a Catalog within another Fragment 【发布时间】:2020-05-15 07:10:01 【问题描述】:

标题可能有点混乱,所以我希望我能正确表达我的问题。所以我正在开发一个简单的锻炼日志应用程序。我有如图 1 所示的片段 ActiveWorkout。当我按下粉红色的添加按钮时,我被定向到另一个片段,其中包含不同的练习作为列表,如图 2 所示。然后我要选择一个练习(使用 onItemClickListener)并且该练习的标题应该被发送回 ActiveWorkoutFragment 并添加到 Active Workout,所以它应该像图 3。所以这里的问题是,我不完全知道如何让我的 Active Workout 保持“活力”,如果我想添加另一个练习并且当我再次按下粉红色的添加按钮时它不是空白的,它应该被更新,所以最后它应该像图 4 中的那样。我正在考虑使用 Bundle 发送数据,我也在代码中进行了尝试,但更困难的部分是在不删除之前添加的练习的情况下更新锻炼列表。顺便说一句,我尝试这样做的原因是因为数据实际上在 Firebase 数据库中,所以从某种意义上说,我正在尝试从 Workout 数据库中检索数据。

图片:

Image 1Image 2Image 3Image 4

这是练习列表或目录:

public class ExercisesFragment extends Fragment 

    private ListView lv;
    private FirebaseListAdapter adapter;
    private ArrayList<Exercise> exerciseList;
    private ArrayList<String> nameList;
    //private Adapter adapter;

    public ExercisesFragment() 
        // Required empty public constructor
    

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) 

        View v = inflater.inflate(R.layout.fragment_exercises, container, false);
        lv = v.findViewById(R.id.lv);
        Query query = FirebaseDatabase.getInstance().getReference().child("exerciseList");
        FirebaseListOptions<ExerciseElement> options = new FirebaseListOptions.Builder<ExerciseElement>()
                .setLayout(R.layout.exercise)
                .setQuery(query, ExerciseElement.class)
                .build();

        adapter = new FirebaseListAdapter(options) 
            @Override
            protected void populateView(@NonNull View v, @NonNull Object model, int position) 
                TextView bodypart = v.findViewById(R.id.bodypart);
                TextView title = v.findViewById(R.id.title);

                ExerciseElement el = (ExerciseElement) model;
                bodypart.setText(el.getBodypart().toString());
                title.setText(el.getTitle().toString());


            
        ;
        lv.setAdapter(adapter);


        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() 
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
                ExerciseElement el = (ExerciseElement) lv.getItemAtPosition(position);
                String item = el.getTitle();

                ActiveWorkoutFragment awf = new ActiveWorkoutFragment();
                Bundle args = new Bundle();
                args.putString("ExerciseTitle", item);
                awf.setArguments(args);

                getFragmentManager().beginTransaction().replace(R.id.nav_host_fragment, awf).commit();
                //Navigation.findNavController(v).navigate(R.id.activeWorkoutFragment);

            
        );
        return v;
    

    @Override
    public void onStart() 
        super.onStart();
        adapter.startListening();
    

    @Override
    public void onStop() 
        super.onStop();
        adapter.stopListening();
    

然后是ActiveWorkoutFragment,它有点长,但上部不是问题。当我点击粉红色的添加按钮时,会调用 addNewExercise() 方法,所以我试图在那里以某种方式检索数据。

public class ActiveWorkoutFragment extends Fragment 

    private Workout workout;
    private TextView emptyRecyclerView;
    private RecyclerView exerciseRecyclerView;
    private ExerciseRecyclerViewAdapter adapter;
    private WorkoutHistory workoutHistory;
    private FloatingActionButton fab;
    private FirebaseAuth mAuth;
    private DatabaseReference databaseWorkouts;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) 



        View v =  inflater.inflate(R.layout.fragment_active_workout, container, false);
        fab = v.findViewById(R.id.fab);
        fab.setOnClickListener(this::addNewExercise);
        setHasOptionsMenu(true);

        workout = new Workout("WORKOUT");

        if(savedInstanceState != null) 
            workout = savedInstanceState.getParcelable("key");
        

        emptyRecyclerView = v.findViewById(R.id.empty_recycler_view);
        //buildRecyclerView(workout);
        exerciseRecyclerView = v.findViewById(R.id.recycler_view_exercise);

        // improves performance if size of RecyclerView content is fixed
        // taken from developer.android.com
        exerciseRecyclerView.setHasFixedSize(true);

        // use a linear layout manager for RecyclerView
        LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext());
        exerciseRecyclerView.setLayoutManager(layoutManager);

        // add divider
        RecyclerView.ItemDecoration itemDecoration = new
                DividerItemDecoration(this.getContext(), DividerItemDecoration.VERTICAL);
        exerciseRecyclerView.addItemDecoration(itemDecoration);

        // Create adapter and set its data set to workout
        adapter = new ExerciseRecyclerViewAdapter(workout, this);

        // Set up swipe to dismiss and ability to move RecyclerView items around

        // Create callback object for ItemTouchHelper
        ItemTouchHelper.Callback callback = new CustomItemTouchHelperCallback(adapter);

        // Implement object created above
        ItemTouchHelper touchHelper = new ItemTouchHelper(callback);

        touchHelper.attachToRecyclerView(exerciseRecyclerView);

        if (adapter.getItemCount() == 0)
        
            showEmptyRecyclerViewText();
        
        else
        
            exerciseRecyclerView.setAdapter(adapter);
        

        return v;
    

    // Adds save button (check mark) to action bar
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) 
        inflater.inflate(R.menu.active_workout_action_bar, menu);
        super.onCreateOptionsMenu(menu, inflater);
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        switch (item.getItemId()) 
            case R.id.action_save: 

                List<Exercise> exercises = workout.getExercises();

                mAuth = FirebaseAuth.getInstance();
                String user_id = mAuth.getCurrentUser().getUid();


                databaseWorkouts = FirebaseDatabase.getInstance().getReference("Workouts").child(user_id);
                String id = databaseWorkouts.push().getKey();

                workout = new Workout("abc", user_id, exercises);
                databaseWorkouts.child(id).setValue(workout);


                Toast.makeText(getContext(), "Workout saved", Toast.LENGTH_SHORT).show();

                return true;
            
            case R.id.action_delete: 
                exerciseRecyclerView.requestFocus();

                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
                builder.setTitle("Confirm deletion").setMessage("Are you sure you want to delete" +
                        " this workout?");

                builder.setPositiveButton(android.R.string.yes, (dialog, which) -> 
                    try 
                        workoutHistory.removeWorkout(workout);
                     catch (IOException e) 
                        e.printStackTrace();
                    

                    Toast.makeText(getContext(), "Workout deleted", Toast.LENGTH_SHORT).show();
                );
                builder.setNegativeButton(android.R.string.no, (dialog, which) -> 
                    dialog.dismiss();
                );

                builder.show();
                return true;
            
            default:
                // unrecognized button pressed
                return super.onOptionsItemSelected(item);
        
    

    public void showEmptyRecyclerViewText()
    
        emptyRecyclerView.setVisibility(View.VISIBLE);
    

    public void addNewExercise(View view) 

        Bundle bundle = getArguments();
        String value = "";
        if(bundle != null) 
            value = bundle.getString("ExerciseTitle");
        
        System.out.println("lala");
        System.out.println(value);

        Exercise newExercise = new Exercise(value, -1, -1);
        if (exerciseRecyclerView.getAdapter() == null) 
            exerciseRecyclerView.setAdapter(adapter);
        


        emptyRecyclerView.setVisibility(View.INVISIBLE);

        workout.addExercise(newExercise);

        adapter.notifyItemInserted(workout.getExercises().size() - 1);
    

    private void hideKeyboard(Context context, View view) 
        InputMethodManager imm = (InputMethodManager) context.getSystemService
                (Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) 
        super.onSaveInstanceState(outState);
        outState.putParcelable("key", workout);
    

【问题讨论】:

所以主要问题是当您从 image2 选择到 image1 时 .. image1 没有更新,换句话说,它重新创建了? 请避免使用 ListView 而使用 RecyclerView 是的,实际上就是这样。我的意思是片段之间的数据传输是这里的一件事,但就像你说的主要问题是当我点击粉红色的添加按钮时,必须更新 image1 而不能一次又一次地重新创建。我也应该在这里使用 Bundle 来传输数据吗?我其实只转Strings,也就是题名。 ListView的缺点是什么,我对这个概念很陌生。 Recyclerview 使用 ViewHolder 模式强制回收视图。所以它增加了性能并被社区推荐,而不是过时的 oof ListView 【参考方案1】:

尝试将此作为您项目的基础。向它添加视图和其他必要的方法,但是它将为您提供模块的流程

//contract to update Framgents
public interface FragUpdater 
    public void updateExerciseFrag(Exercise exercise);




public class ExerciseActiity extends AppCompatActivity implements FragUpdater 
    FragmentManager fm;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_exercise_actiity);
        fab.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                gotoActiveWorkoutFragment();
            
        );
        fm = getSupportFragmentManager();

        gotoExercisesFragment();
    

    private void gotoActiveWorkoutFragment() 
        ActiveWorkoutFragment activeWorkoutFragment = new ActiveWorkoutFragment();
        Bundle bundle = new Bundle();
        activeWorkoutFragment.setArguments(bundle);
        fm.beginTransaction().add(R.id.content_frame, activeWorkoutFragment, "ExerciseSelectionFrag").addToBackStack(null).commit();
    

    private void gotoExercisesFragment() 
        ExercisesFragment exercisesFragment = new ExercisesFragment();
        Bundle bundle = new Bundle();
        exercisesFragment.setArguments(bundle);
        fm.beginTransaction().replace(R.id.content_frame, exercisesFragment, "ExerciseDisplayFrag").commit();
    

    @Override
    public void updateExerciseFrag(Exercise exercise) 
        // Get Fragment ExercisesFragment
        ExercisesFragment frag = (ExercisesFragment)
        fm.findFragmentByTag("ExerciseDisplayFrag");
        if (frag == null) 
            return;
        
        frag.updateList(exercise);
    






public class ExercisesFragment extends Fragment 

    //here update your list in ExerciseFragment
    public void updateList(Exercise exercise) 



public class ActiveWorkoutFragment extends Fragment 
    FragUpdater fragUpdater;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        fragUpdater = (ExerciseActiity) getActivity();
    

    private void selectListItem(Exercise exercise) 
        fragUpdater.updateExerciseFrag(exercise);
        getActivity().getSupportFragmentManager().popBackStack();


    

【讨论】:

以上是关于从另一个片段中的目录更新片段中的列表视图元素的主要内容,如果未能解决你的问题,请参考以下文章

无法从 ViewPager 中的另一个片段刷新/更新片段中的列表视图

从广播接收器更新片段中的列表视图

从另一个片段返回时如何在视图页面中显示片段?

片段中的 notifyDataSetChanged() 不刷新列表视图

在创建视图的片段期间为项目列表中的每个项目设置图像[重复]

片段中的Android ListView - 获取新数据后刷新表的问题