You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio Java:如何保存恢复含拖拽元素的Fragment状态?

解决方案:保持拖拽元素位置不重置

一、会话内切换Fragment不丢失状态(无需持久化存储)

当前每次切换到HomeFragment都会创建新实例,导致之前的拖拽状态丢失。只需缓存HomeFragment实例,复用即可解决:

修改MainActivity代码:

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
    private WidgetFragment widgetFragment;
    private HomeFragment homeFragment; // 添加HomeFragment成员变量
    private final String WIDGET_FRAGMENT_TAG = "widgetfragmenttag";
    private final String HOME_FRAGMENT_TAG = "homefragmenttag";
    private DrawerLayout drawerLayout;

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

        // 原有Toolbar、DrawerLayout初始化代码不变
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        
        drawerLayout = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_nav, R.string.close_nav);
        drawerLayout.addDrawerListener(toggle);
        toggle.syncState();

        // 初始化Fragment实例
        if (savedInstanceState != null) {
            widgetFragment = (WidgetFragment) getSupportFragmentManager().findFragmentByTag(WIDGET_FRAGMENT_TAG);
            homeFragment = (HomeFragment) getSupportFragmentManager().findFragmentByTag(HOME_FRAGMENT_TAG);
        } else {
            widgetFragment = new WidgetFragment();
            homeFragment = new HomeFragment();
        }
    }

    @SuppressLint("NonConstantResourceId")
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
            case R.id.nav_home:
                getSupportFragmentManager()
                        .beginTransaction()
                        .replace(R.id.fragment_container, homeFragment, HOME_FRAGMENT_TAG)
                        .commit();
                break;
            // 其他case代码不变
            case R.id.nav_settings:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new SettingsFragment()).commit();
                break;
            case R.id.nav_widget:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,widgetFragment, WIDGET_FRAGMENT_TAG).commit();
                break;
            case R.id.nav_location:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new LocationFragment()).commit();
                break;
            case R.id.fragment_temp_api_demo:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new TempApiDemoFragment()).commit();
                break;
            case R.id.nav_education:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new EducationFragment()).commit();
                break;
            case R.id.nav_shopping:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new ShoppingFragment()).commit();
                break;
            case R.id.nav_logout:
                Toast.makeText(this, "Logout Button Pressed",Toast.LENGTH_SHORT).show();
                break;
        }
        drawerLayout.closeDrawer(GravityCompat.START);
        return true;
    }

    // 原有onBackPressed方法不变
    @Override
    public void onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.START)){
            drawerLayout.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }
}

这样切换Fragment时复用同一个HomeFragment实例,拖拽后的位置会自动保留。

二、跨会话持久化(退出APP再打开仍保持位置)

如果需要退出APP后重新打开依然保留拖拽状态,使用SharedPreferences完全可行——它是轻量级存储,频繁读写的性能开销可以忽略(apply()方法异步写入文件,不会阻塞UI)。

修改HomeFragment代码:

import android.content.Context;
import android.content.SharedPreferences;
// 其他import不变

public class HomeFragment extends Fragment implements View.OnDragListener, View.OnLongClickListener {
    TextView txtVw;
    ImageView imgVw;
    Button btn;
    private SharedPreferences prefs;
    // 定义存储键
    private static final String KEY_TXT_PARENT = "txt_view_parent";
    private static final String KEY_IMG_PARENT = "img_view_parent";
    private static final String KEY_BTN_PARENT = "btn_parent";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        prefs = requireActivity().getSharedPreferences("DragStatePrefs", Context.MODE_PRIVATE);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View viewFragment = inflater.inflate(R.layout.fragment_home, container, false);

        txtVw = viewFragment.findViewById(R.id.lbl);
        txtVw.setTag("DRAGGABLE TEXTVIEW");
        txtVw.setOnLongClickListener(this);

        imgVw = viewFragment.findViewById(R.id.ingvw);
        imgVw.setTag("ANDROID ICON");
        imgVw.setOnLongClickListener(this);

        btn = viewFragment.findViewById(R.id.btnDrag);
        btn.setTag("DRAGGABLE BUTTON");
        btn.setOnLongClickListener(this);

        // 设置拖拽监听
        LinearLayout layout1 = viewFragment.findViewById(R.id.layout1);
        LinearLayout layout2 = viewFragment.findViewById(R.id.layout2);
        LinearLayout layout3 = viewFragment.findViewById(R.id.layout3);
        layout1.setOnDragListener(this);
        layout2.setOnDragListener(this);
        layout3.setOnDragListener(this);

        // 恢复之前的拖拽状态
        restoreDragState(layout1, layout2, layout3);

        return viewFragment;
    }

    // 恢复元素位置
    private void restoreDragState(LinearLayout layout1, LinearLayout layout2, LinearLayout layout3) {
        // 恢复TextView
        int txtParentId = prefs.getInt(KEY_TXT_PARENT, R.id.layout1); // 默认layout1
        moveViewToParent(txtVw, txtParentId, layout1, layout2, layout3);
        // 恢复ImageView
        int imgParentId = prefs.getInt(KEY_IMG_PARENT, R.id.layout1);
        moveViewToParent(imgVw, imgParentId, layout1, layout2, layout3);
        // 恢复Button
        int btnParentId = prefs.getInt(KEY_BTN_PARENT, R.id.layout1);
        moveViewToParent(btn, btnParentId, layout1, layout2, layout3);
    }

    // 将View移动到指定父容器
    private void moveViewToParent(View view, int parentId, LinearLayout... layouts) {
        ViewGroup currentParent = (ViewGroup) view.getParent();
        if (currentParent.getId() != parentId) {
            currentParent.removeView(view);
            for (LinearLayout layout : layouts) {
                if (layout.getId() == parentId) {
                    layout.addView(view);
                    break;
                }
            }
        }
    }

    @Override
    public boolean onDrag(View v, DragEvent event) {
        int action = event.getAction();
        switch (action) {
            // 原有其他case代码不变
            case DragEvent.ACTION_DRAG_STARTED:
                if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
                    return true;
                }
                return false;
            case DragEvent.ACTION_DRAG_ENTERED:
                v.getBackground().setColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN);
                v.invalidate();
                return true;
            case DragEvent.ACTION_DRAG_LOCATION:
                return true;
            case DragEvent.ACTION_DRAG_EXITED:
                v.getBackground().clearColorFilter();
                v.invalidate();
                return true;
            case DragEvent.ACTION_DROP:
                ClipData.Item item = event.getClipData().getItemAt(0);
                String dragData = item.getText().toString();
                Toast.makeText(getActivity(), "Dragged data is " + dragData, Toast.LENGTH_SHORT).show();
                v.getBackground().clearColorFilter();
                v.invalidate();

                View vw = (View) event.getLocalState();
                ViewGroup owner = (ViewGroup) vw.getParent();
                owner.removeView(vw);
                LinearLayout container = (LinearLayout) v;
                container.addView(vw);
                vw.setVisibility(View.VISIBLE);

                // 保存当前元素的父容器ID到SharedPreferences
                saveDragState(vw, container.getId());

                return true;
            case DragEvent.ACTION_DRAG_ENDED:
                v.getBackground().clearColorFilter();
                v.invalidate();
                if (event.getResult())
                    Toast.makeText(getActivity(), "The drop was handled.", Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(getActivity(), "The drop didn't work.", Toast.LENGTH_SHORT).show();
                return true;
            default:
                Log.e("DragDrop Example", "Unknown action type received by OnDragListener.");
                break;
        }
        return false;
    }

    // 保存拖拽状态
    private void saveDragState(View view, int parentId) {
        SharedPreferences.Editor editor = prefs.edit();
        if (view == txtVw) {
            editor.putInt(KEY_TXT_PARENT, parentId);
        } else if (view == imgVw) {
            editor.putInt(KEY_IMG_PARENT, parentId);
        } else if (view == btn) {
            editor.putInt(KEY_BTN_PARENT, parentId);
        }
        editor.apply(); // 异步写入,不阻塞UI
    }

    // 原有onLongClick方法不变
    @Override
    public boolean onLongClick(View v) {
        ClipData.Item item = new ClipData.Item((CharSequence) v.getTag());
        String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
        ClipData data = new ClipData(v.getTag().toString(), mimeTypes, item);
        View.DragShadowBuilder dragshadow = new View.DragShadowBuilder(v);
        v.startDragAndDrop(data, dragshadow, v, 0);
        return true;
    }
}

关于SharedPreferences的适用性

频繁移动元素完全可以使用SharedPreferences,原因:

  • 每次apply()是异步写入磁盘,不会阻塞主线程
  • 存储的数据量极小(仅几个整数ID),性能开销可以忽略
  • 实现简单,无需复杂的存储逻辑

内容的提问来源于stack exchange,提问作者johndo19

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 08:44:55